Python : Improve handling of PubSub nodes, modularize code, and restore XMPP error message;
SVG : Add icons of a plant and ReactOS; TOML : Modify references of DivestOS and ReactOS; XHTML : Modify F-Droid and DivestOS notices, and add a notice about ReactOS.
This commit is contained in:
parent
644842ab6d
commit
7dda347ebc
5 changed files with 1958 additions and 268 deletions
50
clients.toml
50
clients.toml
|
@ -6,7 +6,7 @@
|
||||||
##
|
##
|
||||||
#An encrypted instant messaging with video call and GPS features for Divest OS.
|
#An encrypted instant messaging with video call and GPS features for Divest OS.
|
||||||
#"""
|
#"""
|
||||||
#divestos = "https://f-droid.org/packages/org.atalk.android/"
|
#android = "https://f-droid.org/packages/org.atalk.android/"
|
||||||
#features = ["chat", "fdroid", "graphical", "mobile", "omemo", "otr", "zrtp"]
|
#features = ["chat", "fdroid", "graphical", "mobile", "omemo", "otr", "zrtp"]
|
||||||
|
|
||||||
[aparte]
|
[aparte]
|
||||||
|
@ -40,7 +40,7 @@ about = """
|
||||||
blabber.im is a fork of Conversations. The changes aim to improve usability \
|
blabber.im is a fork of Conversations. The changes aim to improve usability \
|
||||||
and ease transition from pre-installed and other widespread messengers.
|
and ease transition from pre-installed and other widespread messengers.
|
||||||
"""
|
"""
|
||||||
divestos = "https://blabber.im"
|
android = "https://blabber.im"
|
||||||
features = ["adhoc", "chat", "graphical", "mobile", "omemo", "openpgp"]
|
features = ["adhoc", "chat", "graphical", "mobile", "omemo", "openpgp"]
|
||||||
|
|
||||||
#[bruno]
|
#[bruno]
|
||||||
|
@ -52,7 +52,7 @@ features = ["adhoc", "chat", "graphical", "mobile", "omemo", "openpgp"]
|
||||||
#
|
#
|
||||||
#You can use Bruno if the other IM apps are just not stylish enough.
|
#You can use Bruno if the other IM apps are just not stylish enough.
|
||||||
#"""
|
#"""
|
||||||
#divestos = "https://yaxim.org/download/"
|
#android = "https://yaxim.org/download/"
|
||||||
#features = ["chat", "graphical", "mobile"]
|
#features = ["chat", "graphical", "mobile"]
|
||||||
|
|
||||||
#[candy]
|
#[candy]
|
||||||
|
@ -104,7 +104,7 @@ The Cheogram Android app allows you to join a worldwide communication network.
|
||||||
It especially focuses on features useful to people who want to contact those \
|
It especially focuses on features useful to people who want to contact those \
|
||||||
on other networks as well, such as SMS-enabled phone numbers.
|
on other networks as well, such as SMS-enabled phone numbers.
|
||||||
"""
|
"""
|
||||||
divestos = "https://f-droid.org/packages/com.cheogram.android"
|
android = "https://f-droid.org/packages/com.cheogram.android"
|
||||||
features = ["adhoc", "chat", "fdroid", "graphical", "mobile", "omemo", "openpgp"]
|
features = ["adhoc", "chat", "fdroid", "graphical", "mobile", "omemo", "openpgp"]
|
||||||
|
|
||||||
[conversations]
|
[conversations]
|
||||||
|
@ -117,7 +117,7 @@ mobile device.
|
||||||
It is easy to use, reliable, battery friendly. With built-in support for \
|
It is easy to use, reliable, battery friendly. With built-in support for \
|
||||||
images, group chats and e2e encryption.
|
images, group chats and e2e encryption.
|
||||||
"""
|
"""
|
||||||
divestos = "https://f-droid.org/packages/eu.siacs.conversations"
|
android = "https://f-droid.org/packages/eu.siacs.conversations"
|
||||||
features = ["chat", "fdroid", "graphical", "mobile", "omemo", "openpgp", "pwa"]
|
features = ["chat", "fdroid", "graphical", "mobile", "omemo", "openpgp", "pwa"]
|
||||||
|
|
||||||
[conversations-classic]
|
[conversations-classic]
|
||||||
|
@ -129,7 +129,7 @@ The very last word in instant messaging.
|
||||||
Conversations is a Jabber/XMPP client for Android 5.0+ smartphones that has \
|
Conversations is a Jabber/XMPP client for Android 5.0+ smartphones that has \
|
||||||
been optimized to provide a unique mobile experience.
|
been optimized to provide a unique mobile experience.
|
||||||
"""
|
"""
|
||||||
divestos = "https://f-droid.org/packages/eu.siacs.conversations.classic"
|
android = "https://f-droid.org/packages/eu.siacs.conversations.classic"
|
||||||
features = ["chat", "fdroid", "graphical", "mobile", "omemo", "openpgp"]
|
features = ["chat", "fdroid", "graphical", "mobile", "omemo", "openpgp"]
|
||||||
|
|
||||||
[conversejs]
|
[conversejs]
|
||||||
|
@ -142,7 +142,7 @@ and desktops.
|
||||||
browser = "https://conversejs.org"
|
browser = "https://conversejs.org"
|
||||||
apple = "https://github.com/conversejs/converse-desktop/releases"
|
apple = "https://github.com/conversejs/converse-desktop/releases"
|
||||||
linux = "https://github.com/conversejs/converse-desktop/releases"
|
linux = "https://github.com/conversejs/converse-desktop/releases"
|
||||||
reactos = "https://github.com/conversejs/converse-desktop/releases"
|
windows = "https://github.com/conversejs/converse-desktop/releases"
|
||||||
features = ["adhoc", "chat", "desktop", "graphical", "omemo"]
|
features = ["adhoc", "chat", "desktop", "graphical", "omemo"]
|
||||||
|
|
||||||
#[coyim]
|
#[coyim]
|
||||||
|
@ -167,7 +167,7 @@ features = ["adhoc", "chat", "desktop", "graphical", "omemo"]
|
||||||
#"""
|
#"""
|
||||||
#apple = "https://coy.im/#download-section"
|
#apple = "https://coy.im/#download-section"
|
||||||
#linux = "https://coy.im/#download-section"
|
#linux = "https://coy.im/#download-section"
|
||||||
#reactos = "https://coy.im/#download-section"
|
#windows = "https://coy.im/#download-section"
|
||||||
#features = ["chat", "desktop", "graphical", "otr"]
|
#features = ["chat", "desktop", "graphical", "otr"]
|
||||||
|
|
||||||
[dino]
|
[dino]
|
||||||
|
@ -202,7 +202,7 @@ on your mobile device.
|
||||||
"""
|
"""
|
||||||
apple = "https://gajim.org/download/#macos"
|
apple = "https://gajim.org/download/#macos"
|
||||||
linux = "https://gajim.org/download/#linux"
|
linux = "https://gajim.org/download/#linux"
|
||||||
reactos = "https://gajim.org/download/#windows"
|
windows = "https://gajim.org/download/#windows"
|
||||||
features = ["adhoc", "admin", "chat", "desktop", "graphical", "omemo", "openpgp"]
|
features = ["adhoc", "admin", "chat", "desktop", "graphical", "omemo", "openpgp"]
|
||||||
|
|
||||||
[irssi]
|
[irssi]
|
||||||
|
@ -225,9 +225,9 @@ jabber.el is an XMPP client for Emacs. XMPP (also known as 'Jabber') is an \
|
||||||
IETF-standard federated instant messaging protocol.
|
IETF-standard federated instant messaging protocol.
|
||||||
"""
|
"""
|
||||||
apple = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
apple = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
||||||
divestos = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
android = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
||||||
posix = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
posix = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
||||||
reactos = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
windows = "https://codeberg.org/emacs-jabber/emacs-jabber#how-to-install"
|
||||||
features = ["admin", "chat", "console", "desktop"]
|
features = ["admin", "chat", "console", "desktop"]
|
||||||
|
|
||||||
[jsxc]
|
[jsxc]
|
||||||
|
@ -253,7 +253,7 @@ It uses the open communication protocol XMPP (Jabber).
|
||||||
Unlike other chat apps, you are not dependent on one specific service \
|
Unlike other chat apps, you are not dependent on one specific service \
|
||||||
provider, and your privacy is gauranteed more than ever before.
|
provider, and your privacy is gauranteed more than ever before.
|
||||||
"""
|
"""
|
||||||
divestos = "https://kaidan.im/download/#android-experimental"
|
android = "https://kaidan.im/download/#android-experimental"
|
||||||
linux = "https://kaidan.im/download/#linux"
|
linux = "https://kaidan.im/download/#linux"
|
||||||
features = ["chat", "desktop", "graphical", "mobile", "omemo"]
|
features = ["chat", "desktop", "graphical", "mobile", "omemo"]
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ client Miranda IM.
|
||||||
|
|
||||||
It is very light on system resources and extremely fast.
|
It is very light on system resources and extremely fast.
|
||||||
"""
|
"""
|
||||||
reactos = "https://miranda-ng.org/downloads/"
|
windows = "https://miranda-ng.org/downloads/"
|
||||||
features = ["adhoc", "chat", "desktop", "graphical", "omemo", "openpgp", "otr"]
|
features = ["adhoc", "chat", "desktop", "graphical", "omemo", "openpgp", "otr"]
|
||||||
|
|
||||||
[monal]
|
[monal]
|
||||||
|
@ -306,7 +306,7 @@ monocles chat is a modern and secure Android XMPP chat client.
|
||||||
It is based on blabber.im and Conversations, yet has a lot of changes and \
|
It is based on blabber.im and Conversations, yet has a lot of changes and \
|
||||||
offers additional features to improve usability and security.
|
offers additional features to improve usability and security.
|
||||||
"""
|
"""
|
||||||
divestos = "https://f-droid.org/packages/de.monocles.chat"
|
android = "https://f-droid.org/packages/de.monocles.chat"
|
||||||
features = ["adhoc", "chat", "fdroid", "graphical", "mobile", "omemo", "openpgp", "otr"]
|
features = ["adhoc", "chat", "fdroid", "graphical", "mobile", "omemo", "openpgp", "otr"]
|
||||||
|
|
||||||
[movim]
|
[movim]
|
||||||
|
@ -327,7 +327,7 @@ desktop computer.
|
||||||
"""
|
"""
|
||||||
apple = "https://join.movim.eu"
|
apple = "https://join.movim.eu"
|
||||||
browser = "https://join.movim.eu"
|
browser = "https://join.movim.eu"
|
||||||
divestos = "https://join.movim.eu"
|
android = "https://join.movim.eu"
|
||||||
features = ["adhoc", "chat", "desktop", "graphical", "mobile", "omemo", "pubsub", "pwa"]
|
features = ["adhoc", "chat", "desktop", "graphical", "mobile", "omemo", "pubsub", "pwa"]
|
||||||
|
|
||||||
[moxxy]
|
[moxxy]
|
||||||
|
@ -339,7 +339,7 @@ Moxxy is an experimental XMPP client that aims to be modern and easy to use.
|
||||||
It is currently alpha software. This means that there will be issues with some \
|
It is currently alpha software. This means that there will be issues with some \
|
||||||
functionalities. Please do not use Moxxy for anything important at this moment.
|
functionalities. Please do not use Moxxy for anything important at this moment.
|
||||||
"""
|
"""
|
||||||
divestos = "https://apt.izzysoft.de/fdroid/index/apk/org.moxxy.moxxyv2"
|
android = "https://apt.izzysoft.de/fdroid/index/apk/org.moxxy.moxxyv2"
|
||||||
features = ["chat", "graphical", "izzyondroid", "mobile", "omemo"]
|
features = ["chat", "graphical", "izzyondroid", "mobile", "omemo"]
|
||||||
|
|
||||||
#[pade]
|
#[pade]
|
||||||
|
@ -408,7 +408,7 @@ supported operating system.
|
||||||
"""
|
"""
|
||||||
apple = "https://psi-im.org"
|
apple = "https://psi-im.org"
|
||||||
linux = "https://psi-im.org"
|
linux = "https://psi-im.org"
|
||||||
reactos = "https://psi-im.org"
|
windows = "https://psi-im.org"
|
||||||
features = ["adhoc", "admin", "chat", "desktop", "graphical", "omemo", "openpgp", "otr"]
|
features = ["adhoc", "admin", "chat", "desktop", "graphical", "omemo", "openpgp", "otr"]
|
||||||
|
|
||||||
[psi-plus]
|
[psi-plus]
|
||||||
|
@ -424,7 +424,7 @@ plugins for transferring them to upstream.
|
||||||
apple = "https://psi-plus.com/wiki/en:downloads#macos"
|
apple = "https://psi-plus.com/wiki/en:downloads#macos"
|
||||||
haiku = "https://depot.haiku-os.org/psi_plus"
|
haiku = "https://depot.haiku-os.org/psi_plus"
|
||||||
linux = "https://psi-plus.com/wiki/en:downloads#linux"
|
linux = "https://psi-plus.com/wiki/en:downloads#linux"
|
||||||
reactos = "https://psi-plus.com/wiki/en:downloads#ms_windows"
|
windows = "https://psi-plus.com/wiki/en:downloads#ms_windows"
|
||||||
features = ["adhoc", "admin", "chat", "desktop", "graphical", "haikudepot", "omemo", "openpgp", "otr"]
|
features = ["adhoc", "admin", "chat", "desktop", "graphical", "haikudepot", "omemo", "openpgp", "otr"]
|
||||||
|
|
||||||
[reeder]
|
[reeder]
|
||||||
|
@ -476,7 +476,7 @@ features = ["chat", "desktop", "graphical", "haikudepot"]
|
||||||
#"""
|
#"""
|
||||||
#apple = "https://igniterealtime.org/projects/spark/"
|
#apple = "https://igniterealtime.org/projects/spark/"
|
||||||
#linux = "https://igniterealtime.org/projects/spark/"
|
#linux = "https://igniterealtime.org/projects/spark/"
|
||||||
#reactos = "https://igniterealtime.org/projects/spark/"
|
#windows = "https://igniterealtime.org/projects/spark/"
|
||||||
#features = ["chat", "desktop", "graphical", "omemo"]
|
#features = ["chat", "desktop", "graphical", "omemo"]
|
||||||
|
|
||||||
#[speeqe]
|
#[speeqe]
|
||||||
|
@ -508,7 +508,7 @@ Stork IM by Tigase, Inc. is a lightweight and powerful XMPP client for Android.
|
||||||
|
|
||||||
It provides an easy way to talk and share moments with your friends.
|
It provides an easy way to talk and share moments with your friends.
|
||||||
"""
|
"""
|
||||||
divestos = "https://stork.im/#about"
|
android = "https://stork.im/#about"
|
||||||
features = ["chat", "graphical", "mobile", "omemo"]
|
features = ["chat", "graphical", "mobile", "omemo"]
|
||||||
|
|
||||||
[swift]
|
[swift]
|
||||||
|
@ -525,7 +525,7 @@ from Isode.
|
||||||
"""
|
"""
|
||||||
apple = "https://swift.im/downloads.html"
|
apple = "https://swift.im/downloads.html"
|
||||||
linux = "https://swift.im/downloads.html"
|
linux = "https://swift.im/downloads.html"
|
||||||
reactos = "https://swift.im/downloads.html"
|
windows = "https://swift.im/downloads.html"
|
||||||
features = ["chat", "desktop", "graphical"]
|
features = ["chat", "desktop", "graphical"]
|
||||||
|
|
||||||
[uwpx]
|
[uwpx]
|
||||||
|
@ -543,7 +543,7 @@ chat markers and experimental OMEMO support.
|
||||||
For a complete list of features, please refer to the project: \
|
For a complete list of features, please refer to the project: \
|
||||||
https://github.com/UWPX/UWPX-Client?tab=readme-ov-file#features
|
https://github.com/UWPX/UWPX-Client?tab=readme-ov-file#features
|
||||||
"""
|
"""
|
||||||
reactos = "https://uwpx.org"
|
windows = "https://uwpx.org"
|
||||||
features = ["chat", "desktop", "graphical", "mobile", "omemo"]
|
features = ["chat", "desktop", "graphical", "mobile", "omemo"]
|
||||||
|
|
||||||
[weechat]
|
[weechat]
|
||||||
|
@ -555,7 +555,7 @@ XMPP for power users and digital masochists.
|
||||||
weechat-xmpp is a WeeChat plugin that extends WeeChat to support XMPP, and it \
|
weechat-xmpp is a WeeChat plugin that extends WeeChat to support XMPP, and it \
|
||||||
currently has a minimal but ideally maximal set of XEPs.
|
currently has a minimal but ideally maximal set of XEPs.
|
||||||
"""
|
"""
|
||||||
divestos = "https://github.com/bqv/weechat-xmpp"
|
android = "https://github.com/bqv/weechat-xmpp"
|
||||||
haiku = "https://depot.haiku-os.org/weechat"
|
haiku = "https://depot.haiku-os.org/weechat"
|
||||||
posix = "https://github.com/bqv/weechat-xmpp"
|
posix = "https://github.com/bqv/weechat-xmpp"
|
||||||
features = ["chat", "console", "desktop", "omemo", "openpgp"]
|
features = ["chat", "console", "desktop", "omemo", "openpgp"]
|
||||||
|
@ -572,7 +572,7 @@ to provide people with a simple, fast and secure messaging, based on \
|
||||||
interoperable open standards.
|
interoperable open standards.
|
||||||
"""
|
"""
|
||||||
browser = "https://xabber.com"
|
browser = "https://xabber.com"
|
||||||
divestos = "https://xabber.com"
|
android = "https://xabber.com"
|
||||||
features = ["chat", "desktop", "graphical", "omemo", "mobile"]
|
features = ["chat", "desktop", "graphical", "omemo", "mobile"]
|
||||||
|
|
||||||
#[xmpp-web]
|
#[xmpp-web]
|
||||||
|
@ -594,5 +594,5 @@ Android.
|
||||||
It aims at usability, low overhead and security, and works on low-end Android \
|
It aims at usability, low overhead and security, and works on low-end Android \
|
||||||
devices starting with Android 4.0.
|
devices starting with Android 4.0.
|
||||||
"""
|
"""
|
||||||
divestos = "https://yaxim.org/download/"
|
android = "https://yaxim.org/download/"
|
||||||
features = ["chat", "graphical", "mobile"]
|
features = ["chat", "graphical", "mobile"]
|
||||||
|
|
427
fasi.py
427
fasi.py
|
@ -118,9 +118,7 @@ class HttpInstance:
|
||||||
jid_details = await FileUtilities.cache_jid_data(
|
jid_details = await FileUtilities.cache_jid_data(
|
||||||
jabber_id, password, jid_bare, node_name, alias=alias)
|
jabber_id, password, jid_bare, node_name, alias=alias)
|
||||||
|
|
||||||
action = jid_details['action']
|
|
||||||
count = jid_details['count']
|
count = jid_details['count']
|
||||||
instance = jid_details['instance']
|
|
||||||
items = jid_details['items']
|
items = jid_details['items']
|
||||||
jid_info = {
|
jid_info = {
|
||||||
'error' : jid_details['error'],
|
'error' : jid_details['error'],
|
||||||
|
@ -131,14 +129,11 @@ class HttpInstance:
|
||||||
'name' : jid_details['name'],
|
'name' : jid_details['name'],
|
||||||
'note' : jid_details['note'],
|
'note' : jid_details['note'],
|
||||||
'type' : jid_details['image_type']}
|
'type' : jid_details['image_type']}
|
||||||
link_href = jid_details['link_href']
|
|
||||||
messages = jid_details['messages']
|
messages = jid_details['messages']
|
||||||
nodes = jid_details['nodes']
|
nodes = jid_details['nodes']
|
||||||
note = jid_details['note']
|
note = jid_details['note']
|
||||||
subject = jid_details['subject']
|
subject = jid_details['subject']
|
||||||
title = jid_details['name']
|
title = jid_details['name']
|
||||||
xmpp_uri = jid_details['uri']
|
|
||||||
view_href = jid_details['view_href']
|
|
||||||
|
|
||||||
# Group chat messages
|
# Group chat messages
|
||||||
# NOTE TODO
|
# NOTE TODO
|
||||||
|
@ -157,9 +152,21 @@ class HttpInstance:
|
||||||
number_of_pages = int(len(messages) / 10)
|
number_of_pages = int(len(messages) / 10)
|
||||||
if number_of_pages < len(messages) / 10: number_of_pages += 1
|
if number_of_pages < len(messages) / 10: number_of_pages += 1
|
||||||
|
|
||||||
|
if jid_kind:
|
||||||
|
# Action and instance type
|
||||||
|
action, instance = XmppUtilities.set_action_instance_type(jid_kind, node_name)
|
||||||
|
else: # jid_info['error']
|
||||||
|
action = 'Contact'
|
||||||
|
instance = view_href = ''
|
||||||
|
message = '{}: {} (XEP-0030)'.format(jid_info['text'], jid_info['condition'])
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
|
||||||
# Query URI links
|
# Query URI links
|
||||||
action, instance, link_href, links, view_href, xmpp_uri = XmppUtilities.set_query_uri_link(
|
print('Query URI links')
|
||||||
jid_bare, jid_info, jid_kind, node_name)
|
links = XmppUtilities.get_query_uri_links(jid_bare, jid_kind, node_name)
|
||||||
|
link_href = XmppUtilities.get_link_href(jid_bare, jid_kind, node_name)
|
||||||
|
view_href = XmppUtilities.get_view_href(jid_bare, jid_kind, node_name)
|
||||||
|
xmpp_uri = XmppUtilities.get_xmpp_uri(jid_bare, jid_kind, node_name)
|
||||||
|
|
||||||
# Graphic files
|
# Graphic files
|
||||||
filename, filepath, filetype, selection = FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
filename, filepath, filetype, selection = FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
||||||
|
@ -221,7 +228,7 @@ class HttpInstance:
|
||||||
if True:
|
if True:
|
||||||
entries = []
|
entries = []
|
||||||
exception = jid_vcard = note = node_note = number_of_pages = \
|
exception = jid_vcard = note = node_note = number_of_pages = \
|
||||||
page_number = previous = selection = services_sorted = None
|
page_number = previous = selection = None
|
||||||
|
|
||||||
filename = 'details/{}.toml'.format(jid_bare)
|
filename = 'details/{}.toml'.format(jid_bare)
|
||||||
if os.path.exists(filename) and os.path.getsize(filename) > 0:
|
if os.path.exists(filename) and os.path.getsize(filename) > 0:
|
||||||
|
@ -230,10 +237,32 @@ class HttpInstance:
|
||||||
jid_details = await FileUtilities.cache_jid_data(
|
jid_details = await FileUtilities.cache_jid_data(
|
||||||
jabber_id, password, jid_bare, node_name, item_id)
|
jabber_id, password, jid_bare, node_name, item_id)
|
||||||
|
|
||||||
action = jid_details['action']
|
xmpp_instance = XmppInstance(jabber_id, password, jid_bare)
|
||||||
count = jid_details['count']
|
xmpp_instance.connect()
|
||||||
instance = jid_details['instance']
|
|
||||||
|
# Node item IDs
|
||||||
|
nodes = jid_details['nodes']
|
||||||
items = jid_details['items']
|
items = jid_details['items']
|
||||||
|
if node_name not in nodes:
|
||||||
|
nodes[node_name] = {}
|
||||||
|
node_item_ids = await XmppXep0060.get_node_item_ids(
|
||||||
|
xmpp_instance, jid_bare, node_name)
|
||||||
|
#node_item_ids = await XmppUtilities.get_item_ids_of_node(
|
||||||
|
# jabber_id, password, jid_bare, node_name, nodes)
|
||||||
|
if isinstance(node_item_ids['iq'], stanza.iq.Iq):
|
||||||
|
iq = node_item_ids['iq']
|
||||||
|
iq_disco_items_items = iq['disco_items']['items']
|
||||||
|
for item in items:
|
||||||
|
if item[1] == node_name:
|
||||||
|
nodes[node_name]['title'] = item[2]
|
||||||
|
break
|
||||||
|
nodes[node_name]['count'] = len(iq_disco_items_items)
|
||||||
|
nodes[node_name]['item_ids'] = []
|
||||||
|
for item in iq_disco_items_items:
|
||||||
|
nodes[node_name]['item_ids'].append(
|
||||||
|
[item[0] or '', item[1] or '', item[2] or ''])
|
||||||
|
|
||||||
|
count = jid_details['count']
|
||||||
jid_info = {
|
jid_info = {
|
||||||
'error' : jid_details['error'],
|
'error' : jid_details['error'],
|
||||||
'text' : jid_details['error_text'],
|
'text' : jid_details['error_text'],
|
||||||
|
@ -243,40 +272,17 @@ class HttpInstance:
|
||||||
'name' : jid_details['name'],
|
'name' : jid_details['name'],
|
||||||
'note' : jid_details['note'],
|
'note' : jid_details['note'],
|
||||||
'type' : jid_details['image_type']}
|
'type' : jid_details['image_type']}
|
||||||
link_href = jid_details['link_href']
|
|
||||||
messages = jid_details['messages']
|
messages = jid_details['messages']
|
||||||
node_title = jid_details['name']
|
node_title = nodes[node_name]['title']
|
||||||
nodes = jid_details['nodes']
|
|
||||||
note = jid_details['note']
|
note = jid_details['note']
|
||||||
#title = nodes[node_name]['title'] if node_name else jid_details['name']
|
#title = nodes[node_name]['title'] if node_name else jid_details['name']
|
||||||
title = nodes[node_name]['title']
|
title = jid_details['name']
|
||||||
xmpp_uri = jid_details['uri']
|
|
||||||
view_href = jid_details['view_href']
|
|
||||||
|
|
||||||
#link_href = 'xmpp:{}?pubsub;node={};action=subscribe'.format(
|
#link_href = 'xmpp:{}?pubsub;node={};action=subscribe'.format(
|
||||||
# jid_bare, node_name)
|
# jid_bare, node_name)
|
||||||
#link_text = 'Subscribe'
|
#link_text = 'Subscribe'
|
||||||
#xmpp_uri = '{}?;node={}'.format(jid_bare, node_name)
|
#xmpp_uri = '{}?;node={}'.format(jid_bare, node_name)
|
||||||
|
|
||||||
xmpp_instance = XmppInstance(jabber_id, password, jid_bare)
|
|
||||||
xmpp_instance.connect()
|
|
||||||
|
|
||||||
# Node item IDs
|
|
||||||
if node_name not in nodes:
|
|
||||||
nodes[node_name] = {}
|
|
||||||
node_item_ids = await XmppXep0060.get_node_item_ids(
|
|
||||||
xmpp_instance, jid_bare, node_name)
|
|
||||||
#node_item_ids = await XmppUtilities.get_item_ids_of_node(
|
|
||||||
# jabber_id, password, jid_bare, node_name, nodes)
|
|
||||||
if isinstance(node_item_ids['iq'], stanza.iq.Iq):
|
|
||||||
iq_disco_items_items = node_item_ids['iq']['disco_items']['items']
|
|
||||||
#nodes[node_name]['title'] =
|
|
||||||
nodes[node_name]['count'] = len(iq_disco_items_items)
|
|
||||||
nodes[node_name]['item_ids'] = []
|
|
||||||
for item in iq_disco_items_items:
|
|
||||||
nodes[node_name]['item_ids'].append(
|
|
||||||
[item[0] or '', item[1] or '', item[2] or ''])
|
|
||||||
|
|
||||||
# Node items
|
# Node items
|
||||||
if item_id:
|
if item_id:
|
||||||
previous = True
|
previous = True
|
||||||
|
@ -304,40 +310,42 @@ class HttpInstance:
|
||||||
if number_of_pages < len(item_ids) / 10: number_of_pages += 1
|
if number_of_pages < len(item_ids) / 10: number_of_pages += 1
|
||||||
node_items = await XmppXep0060.get_node_items(
|
node_items = await XmppXep0060.get_node_items(
|
||||||
xmpp_instance, jid_bare, node_name, item_ids=item_ids_10)
|
xmpp_instance, jid_bare, node_name, item_ids=item_ids_10)
|
||||||
if not node_items:
|
|
||||||
action = 'Warning'
|
if isinstance(node_items['iq'], stanza.iq.Iq):
|
||||||
node_title = jid_info['condition']
|
|
||||||
node_note = jid_info['text']
|
|
||||||
services = services_sorted = None
|
|
||||||
elif isinstance(node_items, IqTimeout):
|
|
||||||
action = 'Warning'
|
|
||||||
node_title = 'Timeout'
|
|
||||||
node_note = 'Timeout error'
|
|
||||||
services = services_sorted = None
|
|
||||||
elif isinstance(node_items, IqError):
|
|
||||||
action = 'Warning'
|
|
||||||
breakpoint()
|
|
||||||
node_title = node_items['condition']
|
|
||||||
node_note = node_items['text']
|
|
||||||
services = services_sorted = None
|
|
||||||
else:
|
|
||||||
#title = title or node_name
|
#title = title or node_name
|
||||||
if not node_title: node_title = node_name
|
if not node_title: node_title = node_name
|
||||||
#node_note = nodes[node_name]['title'] if node_name else jid_details['name']
|
#node_note = nodes[node_name]['title'] if node_name else jid_details['name']
|
||||||
node_note = xmpp_uri # jid_bare
|
#node_note = xmpp_uri # jid_bare
|
||||||
for item in node_items['pubsub']['items']:
|
iq = node_items['iq']
|
||||||
|
for item in iq['pubsub']['items']:
|
||||||
item_payload = item['payload']
|
item_payload = item['payload']
|
||||||
entry = Syndication.extract_items(item_payload)
|
entry = Syndication.extract_items(item_payload)
|
||||||
if entry: entry['id'] = item['id']
|
if entry: entry['id'] = item['id']
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
#if len(entries) > 10: break
|
#if len(entries) > 10: break
|
||||||
|
else:
|
||||||
|
message = '{}: {} (XEP-0060)'.format(node_items['condition'], node_items['text'])
|
||||||
if entries: entries.reverse()
|
if entries: entries.reverse()
|
||||||
|
|
||||||
xmpp_instance.disconnect()
|
xmpp_instance.disconnect()
|
||||||
|
|
||||||
|
if jid_kind:
|
||||||
|
# Action and instance type
|
||||||
|
action, instance = XmppUtilities.set_action_instance_type(jid_kind, node_name)
|
||||||
|
else: # jid_info['error']
|
||||||
|
action = 'Contact'
|
||||||
|
instance = view_href = ''
|
||||||
|
message = '{}: {} (XEP-0030)'.format(jid_info['text'], jid_info['condition'])
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
|
||||||
# Query URI links
|
# Query URI links
|
||||||
action, instance, link_href, links, view_href, xmpp_uri = XmppUtilities.set_query_uri_link(
|
print('Query URI links')
|
||||||
jid_bare, jid_info, jid_kind, node_name, item_id)
|
links = XmppUtilities.get_query_uri_links(jid_bare, jid_kind, node_name, item_id)
|
||||||
|
link_href = XmppUtilities.get_link_href(jid_bare, jid_kind, node_name)
|
||||||
|
view_href = XmppUtilities.get_view_href(jid_bare, jid_kind, node_name)
|
||||||
|
xmpp_uri = XmppUtilities.get_xmpp_uri(jid_bare, jid_kind, node_name)
|
||||||
|
|
||||||
|
node_note = xmpp_uri
|
||||||
|
|
||||||
# Graphic files
|
# Graphic files
|
||||||
filename, filepath, filetype, selection = FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
filename, filepath, filetype, selection = FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
||||||
|
@ -350,7 +358,7 @@ class HttpInstance:
|
||||||
xmpp_uri = note = jid
|
xmpp_uri = note = jid
|
||||||
filename = jid_bare = link_href = link_tex = node_note = \
|
filename = jid_bare = link_href = link_tex = node_note = \
|
||||||
node_title = number_of_pages = page_number = previous = \
|
node_title = number_of_pages = page_number = previous = \
|
||||||
selection = services = services_sorted = url = None
|
selection = url = None
|
||||||
|
|
||||||
#if title == 'remote-server-timeout':
|
#if title == 'remote-server-timeout':
|
||||||
# raise HTTPException(status_code=408, detail='remote-server-timeout')
|
# raise HTTPException(status_code=408, detail='remote-server-timeout')
|
||||||
|
@ -457,9 +465,7 @@ class HttpInstance:
|
||||||
services_sorted = {k: v for k, v in services.items() if k != 'unavailable'}
|
services_sorted = {k: v for k, v in services.items() if k != 'unavailable'}
|
||||||
if 'unavailable' in services: services_sorted['unavailable'] = services['unavailable']
|
if 'unavailable' in services: services_sorted['unavailable'] = services['unavailable']
|
||||||
else:
|
else:
|
||||||
action = 'Warning'
|
message = '{}: {} (XEP-0030)'.format(jid_info['condition'], jid_info['text'])
|
||||||
title = jid_info['condition']
|
|
||||||
note = jid_info['text']
|
|
||||||
services = services_sorted = None
|
services = services_sorted = None
|
||||||
|
|
||||||
xmpp_instance.disconnect()
|
xmpp_instance.disconnect()
|
||||||
|
@ -550,8 +556,6 @@ class HttpInstance:
|
||||||
else:
|
else:
|
||||||
count = nodes[node_name]['count'] if node_name in nodes else jid_details['count']
|
count = nodes[node_name]['count'] if node_name in nodes else jid_details['count']
|
||||||
|
|
||||||
action = jid_details['action']
|
|
||||||
instance = jid_details['instance']
|
|
||||||
items = jid_details['items']
|
items = jid_details['items']
|
||||||
jid_info = {
|
jid_info = {
|
||||||
'error' : jid_details['error'],
|
'error' : jid_details['error'],
|
||||||
|
@ -561,20 +565,52 @@ class HttpInstance:
|
||||||
'name' : jid_details['name'],
|
'name' : jid_details['name'],
|
||||||
'note' : jid_details['note'],
|
'note' : jid_details['note'],
|
||||||
'type' : jid_details['image_type']}
|
'type' : jid_details['image_type']}
|
||||||
link_href = jid_details['link_href']
|
|
||||||
messages = jid_details['messages']
|
messages = jid_details['messages']
|
||||||
note = nodes[node_name]['title'] if node_name else jid_details['note']
|
#note = nodes[node_name]['title'] if node_name in nodes else jid_details['note']
|
||||||
title = jid_details['name']
|
note = jid_details['note']
|
||||||
xmpp_uri = jid_details['uri']
|
|
||||||
view_href = jid_details['view_href']
|
|
||||||
|
|
||||||
|
# TODO Append results to file
|
||||||
|
# Node item IDs
|
||||||
if node_name not in nodes:
|
if node_name not in nodes:
|
||||||
nodes[node_name] = await XmppUtilities.get_item_ids_of_node(
|
nodes[node_name] = await XmppUtilities.get_item_ids_of_node(
|
||||||
jabber_id, password, jid_bare, node_name, nodes)
|
jabber_id, password, jid_bare, node_name, nodes)
|
||||||
|
if isinstance(nodes[node_name]['iq'], stanza.iq.Iq):
|
||||||
|
iq = nodes[node_name]['iq']
|
||||||
|
iq_disco_items = iq['disco_items']
|
||||||
|
if iq_disco_items['items']:
|
||||||
|
count = len(nodes[node_name]['iq']['disco_items']['items'])
|
||||||
|
else:
|
||||||
|
count = 0
|
||||||
|
else:
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
if jid_kind == 'pubsub' and node_name:
|
||||||
|
items = jid_details['items']
|
||||||
|
for item in items:
|
||||||
|
if item[1] == node_name:
|
||||||
|
#nodes[node_name]['title'] = item[2]
|
||||||
|
title = item[2]
|
||||||
|
break
|
||||||
|
if not title: title = node_name
|
||||||
|
else:
|
||||||
|
title = jid_details['name']
|
||||||
|
|
||||||
|
# TODO Consider also the existence of a node /j/pubsub.movim.eu/i2p
|
||||||
|
if jid_kind:
|
||||||
|
# Action and instance type
|
||||||
|
action, instance = XmppUtilities.set_action_instance_type(jid_kind, node_name)
|
||||||
|
link_href = XmppUtilities.get_link_href(jid_bare, jid_kind, node_name)
|
||||||
|
view_href = XmppUtilities.get_view_href(jid_bare, jid_kind, node_name)
|
||||||
|
xmpp_uri = XmppUtilities.get_xmpp_uri(jid_bare, jid_kind, node_name)
|
||||||
|
else: # jid_info['error']
|
||||||
|
action = 'Contact'
|
||||||
|
instance = view_href = ''
|
||||||
|
message = '{}: {} (XEP-0030)'.format(jid_info['text'], jid_info['condition'])
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
|
||||||
# Query URI links
|
# Query URI links
|
||||||
action, instance, link_href, links, view_href, xmpp_uri = XmppUtilities.set_query_uri_link(
|
print('Query URI links')
|
||||||
jid_bare, jid_info, jid_kind, node_name)
|
links = XmppUtilities.get_query_uri_links(jid_bare, jid_kind, node_name)
|
||||||
|
|
||||||
# Graphic files
|
# Graphic files
|
||||||
filename, filepath, filetype, selection = FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
filename, filepath, filetype, selection = FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
||||||
|
@ -589,6 +625,10 @@ class HttpInstance:
|
||||||
count = filename = jid_bare = jid_vcard = jid_kind = links = \
|
count = filename = jid_bare = jid_vcard = jid_kind = links = \
|
||||||
message = selection = url = None
|
message = selection = url = None
|
||||||
|
|
||||||
|
# NOTE Handling of variables "title" and "note" in case of '/j/{jid}/{node_name}' is confusing.
|
||||||
|
# TODO Add new keys that are of 'node' and be utilized for nodes, instead of reusing a variable for several roles.
|
||||||
|
# FIXME If no title be provided to 'node name', use 'node name' itself as title (to be done at FileUtilities.cache_jid_data).
|
||||||
|
|
||||||
template_file = 'jid.xhtml'
|
template_file = 'jid.xhtml'
|
||||||
template_dict = {
|
template_dict = {
|
||||||
'action' : action,
|
'action' : action,
|
||||||
|
@ -603,10 +643,10 @@ class HttpInstance:
|
||||||
'jid_kind' : jid_kind,
|
'jid_kind' : jid_kind,
|
||||||
'links' : links,
|
'links' : links,
|
||||||
'message' : message,
|
'message' : message,
|
||||||
'note' : note,
|
'note' : note, # TODO node_note or title of PubSub JID
|
||||||
'request' : request,
|
'request' : request,
|
||||||
'selection' : selection,
|
'selection' : selection,
|
||||||
'title' : title,
|
'title' : title, # TODO node_title
|
||||||
'url' : request.url._url,
|
'url' : request.url._url,
|
||||||
'view_href' : view_href,
|
'view_href' : view_href,
|
||||||
'xmpp_uri' : xmpp_uri}
|
'xmpp_uri' : xmpp_uri}
|
||||||
|
@ -631,17 +671,14 @@ class HttpInstance:
|
||||||
case _ if 'haiku' in user_agent_lower:
|
case _ if 'haiku' in user_agent_lower:
|
||||||
software = 'haiku'
|
software = 'haiku'
|
||||||
case _ if 'android' in user_agent_lower:
|
case _ if 'android' in user_agent_lower:
|
||||||
software = 'divestos'
|
software = 'android'
|
||||||
case _ if 'reactos' in user_agent_lower or 'windows' in user_agent_lower:
|
case _ if 'reactos' in user_agent_lower or 'windows' in user_agent_lower:
|
||||||
software = 'reactos'
|
software = 'windows'
|
||||||
case _ if 'ios' in user_agent_lower or 'macos' in user_agent_lower:
|
case _ if 'ios' in user_agent_lower or 'macos' in user_agent_lower:
|
||||||
software = 'apple'
|
software = 'apple'
|
||||||
|
|
||||||
name = software.title()
|
name = software.title()
|
||||||
if software == 'posix':
|
if software == 'posix': name = 'POSIX'
|
||||||
name = 'POSIX'
|
|
||||||
elif 'os' in software:
|
|
||||||
name = name.replace('os', 'OS')
|
|
||||||
|
|
||||||
filename_clients = 'clients.toml'
|
filename_clients = 'clients.toml'
|
||||||
clients = Data.open_file_toml(filename_clients)
|
clients = Data.open_file_toml(filename_clients)
|
||||||
|
@ -735,7 +772,7 @@ class FileUtilities:
|
||||||
|
|
||||||
async def cache_jid_data(jabber_id, password, jid_bare, node_name=None, item_id=None, alias=None):
|
async def cache_jid_data(jabber_id, password, jid_bare, node_name=None, item_id=None, alias=None):
|
||||||
|
|
||||||
iq_disco_items_list = iq_disco_items_items_list = node_title = title = ''
|
iq_disco_items_list = iq_disco_items_items_list = node_note = node_title = title = ''
|
||||||
jid_vcard = {
|
jid_vcard = {
|
||||||
'name' : '',
|
'name' : '',
|
||||||
'note' : '',
|
'note' : '',
|
||||||
|
@ -756,11 +793,6 @@ class FileUtilities:
|
||||||
jid_info_iq = jid_info['iq']
|
jid_info_iq = jid_info['iq']
|
||||||
jid_kind = jid_info['kind']
|
jid_kind = jid_info['kind']
|
||||||
|
|
||||||
# Query URI links
|
|
||||||
print('Query URI links')
|
|
||||||
action, instance, link_href, links, view_href, xmpp_uri = XmppUtilities.set_query_uri_link(
|
|
||||||
jid_bare, jid_info, jid_kind, node_name)
|
|
||||||
|
|
||||||
# JID info
|
# JID info
|
||||||
print('JID info')
|
print('JID info')
|
||||||
# NOTE Group chat of Psi+ Project at jabber.ru has a note in its vCard.
|
# NOTE Group chat of Psi+ Project at jabber.ru has a note in its vCard.
|
||||||
|
@ -838,7 +870,8 @@ class FileUtilities:
|
||||||
#if jid_kind in ('mix', 'muc', 'conference', 'server'):
|
#if jid_kind in ('mix', 'muc', 'conference', 'server'):
|
||||||
# jid_items = await XmppXep0030.get_jid_items(xmpp_instance, jid_bare)
|
# jid_items = await XmppXep0030.get_jid_items(xmpp_instance, jid_bare)
|
||||||
# if isinstance(jid_items['iq'], stanza.iq.Iq):
|
# if isinstance(jid_items['iq'], stanza.iq.Iq):
|
||||||
# count = len(jid_items['iq']['disco_items']['items'])
|
# iq = jid_items['iq']
|
||||||
|
# count = len(iq['disco_items']['items'])
|
||||||
#elif jid_kind in ('account', 'pubsub'):
|
#elif jid_kind in ('account', 'pubsub'):
|
||||||
# node_item_ids = await XmppXep0060.get_node_item_ids(xmpp_instance, jid_bare, node_name)
|
# node_item_ids = await XmppXep0060.get_node_item_ids(xmpp_instance, jid_bare, node_name)
|
||||||
# if isinstance(node_item_ids, stanza.iq.Iq):
|
# if isinstance(node_item_ids, stanza.iq.Iq):
|
||||||
|
@ -849,56 +882,41 @@ class FileUtilities:
|
||||||
messages = []
|
messages = []
|
||||||
subject = ''
|
subject = ''
|
||||||
if jid_kind == 'muc':
|
if jid_kind == 'muc':
|
||||||
action = 'Join'
|
#action = 'Join'
|
||||||
# TODO Create configurations for group chat preview
|
# TODO Create configurations for group chat preview
|
||||||
room_info_muc = await XmppXep0045.get_room_information(xmpp_instance, jid_bare, alias, maxstanzas=50)
|
room_info_muc = await XmppXep0045.get_room_information(
|
||||||
if not room_info_muc:
|
xmpp_instance, jid_bare, alias, maxstanzas=50)
|
||||||
action = 'Warning'
|
# NOTE Don not mix error messages with node titles and descriptions etc.
|
||||||
node_title = jid_info['condition']
|
if isinstance(room_info_muc['iq'], stanza.iq.Iq):
|
||||||
node_note = jid_info['text']
|
iq = room_info_muc['iq']
|
||||||
services = services_sorted = None
|
for message in iq[3]:
|
||||||
elif isinstance(room_info_muc['iq'], TimeoutError):
|
|
||||||
action = 'Warning'
|
|
||||||
node_title = 'Timeout'
|
|
||||||
node_note = 'Request timeout has reached'
|
|
||||||
services = services_sorted = None
|
|
||||||
elif isinstance(room_info_muc['iq'], IqTimeout):
|
|
||||||
action = 'Warning'
|
|
||||||
node_title = 'Timeout'
|
|
||||||
node_note = 'Timeout error'
|
|
||||||
services = services_sorted = None
|
|
||||||
elif isinstance(room_info_muc['iq'], IqError):
|
|
||||||
action = 'Warning'
|
|
||||||
breakpoint()
|
|
||||||
node_title = room_info_muc['condition']
|
|
||||||
node_note = room_info_muc['text']
|
|
||||||
services = services_sorted = None
|
|
||||||
else:
|
|
||||||
for message in room_info_muc['iq'][3]:
|
|
||||||
messages.append({
|
messages.append({
|
||||||
'id' : message['id'],
|
'id' : message['id'],
|
||||||
'alias' : message['mucnick'],
|
'alias' : message['mucnick'],
|
||||||
'body' : message['body'],
|
'body' : message['body'],
|
||||||
'timestamp' : message['delay']['stamp'].__str__()})
|
'timestamp' : message['delay']['stamp'].__str__()})
|
||||||
messages.reverse()
|
messages.reverse()
|
||||||
subject = room_info_muc['iq'][1]['subject']
|
subject = iq[1]['subject']
|
||||||
#title = title or node_name
|
#title = title or node_name
|
||||||
if not node_title: node_title = node_name
|
if not node_title: node_title = node_name
|
||||||
node_note = jid_bare
|
node_note = jid_bare
|
||||||
|
else:
|
||||||
|
message = '{}: {} (XEP-0045)'.format(room_info_muc['condition'], room_info_muc['text'])
|
||||||
|
|
||||||
# Node items
|
# Node items
|
||||||
print('Node items')
|
print('Node items')
|
||||||
nodes = {}
|
nodes = {}
|
||||||
#if node_name and node_name in iq_disco_items_set:
|
#if node_name and node_name in iq_disco_items_set:
|
||||||
if iq_disco_items_list and node_name and node_name in iq_disco_items_list:
|
if iq_disco_items_list and node_name and node_name in iq_disco_items_list:
|
||||||
action = 'Browse'
|
#action = 'Browse'
|
||||||
node_item_ids = await XmppXep0060.get_node_item_ids(xmpp_instance, jid_bare, node_name)
|
node_item_ids = await XmppXep0060.get_node_item_ids(xmpp_instance, jid_bare, node_name)
|
||||||
if isinstance(node_item_ids['iq'], stanza.iq.Iq):
|
if isinstance(node_item_ids['iq'], stanza.iq.Iq):
|
||||||
|
iq = node_item_ids['iq']
|
||||||
nodes[node_name] = {}
|
nodes[node_name] = {}
|
||||||
nodes[node_name]['title'] = node_title
|
nodes[node_name]['title'] = node_title
|
||||||
nodes[node_name]['count'] = len(node_item_ids['iq']['disco_items']['items'])
|
nodes[node_name]['count'] = len(iq['disco_items']['items'])
|
||||||
nodes[node_name]['item_ids'] = []
|
nodes[node_name]['item_ids'] = []
|
||||||
for item_id in node_item_ids['iq']['disco_items']['items']:
|
for item_id in iq['disco_items']['items']:
|
||||||
nodes[node_name]['item_ids'].append(
|
nodes[node_name]['item_ids'].append(
|
||||||
[item_id[0] or '', item_id[1] or '', item_id[2] or ''])
|
[item_id[0] or '', item_id[1] or '', item_id[2] or ''])
|
||||||
|
|
||||||
|
@ -915,26 +933,24 @@ class FileUtilities:
|
||||||
# note = jid_vcard['name']
|
# note = jid_vcard['name']
|
||||||
|
|
||||||
jid_details = {
|
jid_details = {
|
||||||
'action' : action or '',
|
|
||||||
'count' : count or '',
|
'count' : count or '',
|
||||||
'error' : jid_info['error'],
|
'error' : jid_info['error'],
|
||||||
'error_text' : jid_info['text'] or '',
|
'error_text' : jid_info['text'] or '',
|
||||||
'error_condition' : jid_info['condition'] or '',
|
'error_condition' : jid_info['condition'] or '',
|
||||||
'image_type' : jid_vcard['type'],
|
'image_type' : jid_vcard['type'],
|
||||||
'instance' : instance or '',
|
|
||||||
'items' : iq_disco_items_items_list,
|
'items' : iq_disco_items_items_list,
|
||||||
'kind' : jid_kind or '',
|
'kind' : jid_kind or '',
|
||||||
'link_href' : link_href,
|
|
||||||
'messages' : messages or '',
|
'messages' : messages or '',
|
||||||
'name' : title,
|
'name' : title,
|
||||||
'nodes' : nodes,
|
'nodes' : nodes,
|
||||||
'note' : note or '',
|
'note' : note or '',
|
||||||
'subject' : subject or '',
|
'subject' : subject or ''}
|
||||||
'uri' : xmpp_uri or '',
|
|
||||||
'view_href' : view_href}
|
|
||||||
|
|
||||||
print(jid_details)
|
print(jid_details)
|
||||||
|
|
||||||
|
# Query URI href
|
||||||
|
link_href = XmppUtilities.get_link_href(jid_bare, jid_kind, node_name)
|
||||||
|
|
||||||
FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
FileUtilities.handle_photo(jid_bare, jid_vcard, link_href)
|
||||||
|
|
||||||
filename = 'details/{}.toml'.format(jid_bare)
|
filename = 'details/{}.toml'.format(jid_bare)
|
||||||
|
@ -1166,75 +1182,108 @@ class XmppUtilities:
|
||||||
xmpp_instance.disconnect()
|
xmpp_instance.disconnect()
|
||||||
return node_item_ids
|
return node_item_ids
|
||||||
|
|
||||||
def set_query_uri_link(jid_bare, jid_info, jid_kind, node_name=None, item_id=None):
|
def set_action_instance_type(jid_kind, node_name=None):
|
||||||
links = []
|
if jid_kind in ('conference', 'server'):
|
||||||
if jid_info['error']:
|
|
||||||
message = '{}: {} (XEP-0030)'.format(jid_info['text'], jid_info['condition'])
|
|
||||||
action = 'Connect with'
|
|
||||||
link_href = 'xmpp:{}'.format(jid_bare)
|
|
||||||
links.append({'name' : 'Connect',
|
|
||||||
'href' : link_href,
|
|
||||||
'iden' : 'connect'})
|
|
||||||
links.append({'name' : 'Add',
|
|
||||||
'href' : 'xmpp:{}?roster'.format(jid_bare),
|
|
||||||
'iden' : 'add'})
|
|
||||||
xmpp_uri = jid_bare
|
|
||||||
instance = view_href = ''
|
|
||||||
elif jid_kind in ('conference', 'server'):
|
|
||||||
action = 'Discover'
|
action = 'Discover'
|
||||||
if jid_kind == 'conference':
|
if jid_kind == 'conference':
|
||||||
instance = 'conferences'
|
instance = 'conferences'
|
||||||
elif jid_kind == 'server':
|
elif jid_kind == 'server':
|
||||||
instance = 'services'
|
instance = 'services'
|
||||||
link_href = 'xmpp:{}?disco;type=get;request=items'.format(jid_bare)
|
|
||||||
links.append({'name' : 'Discover',
|
|
||||||
'href' : link_href,
|
|
||||||
'iden' : 'discover'})
|
|
||||||
view_href = '/d/' + jid_bare
|
|
||||||
xmpp_uri = jid_bare
|
|
||||||
elif jid_kind in ('mix', 'muc'):
|
elif jid_kind in ('mix', 'muc'):
|
||||||
#title = 'Group Chat ' + title
|
|
||||||
# TODO Set group chat subject as description.
|
|
||||||
action = 'Join'
|
action = 'Join'
|
||||||
instance = 'participants'
|
instance = 'occupants'
|
||||||
link_href = 'xmpp:{}?join'.format(jid_bare)
|
|
||||||
links.append({'name' : 'Join',
|
|
||||||
'href' : link_href,
|
|
||||||
'iden' : 'join'})
|
|
||||||
view_href = '/v/' + jid_bare
|
|
||||||
xmpp_uri = jid_bare
|
|
||||||
# room_info = await XmppXep0045.get_room_data(xmpp_instance, jid_bare)
|
|
||||||
# breakpoint()
|
|
||||||
elif jid_kind == 'pubsub':
|
elif jid_kind == 'pubsub':
|
||||||
#node_name = request.query_params.get('node', '')
|
|
||||||
if node_name:
|
if node_name:
|
||||||
action = 'Subscribe'
|
action = 'Subscribe'
|
||||||
instance = 'articles'
|
instance = 'articles'
|
||||||
link_href = 'xmpp:{}?pubsub;node={};action=subscribe'.format(jid_bare, node_name)
|
|
||||||
view_href = '/d/{}/{}'.format(jid_bare, node_name)
|
|
||||||
xmpp_uri = '{}?;node={}'.format(jid_bare, node_name)
|
|
||||||
else:
|
else:
|
||||||
action = 'Browse'
|
action = 'Browse'
|
||||||
instance = 'nodes'
|
instance = 'nodes'
|
||||||
link_href = 'xmpp:{}?disco;type=get;request=items'.format(jid_bare)
|
elif jid_kind == 'account':
|
||||||
links.append({'name' : 'Browse',
|
|
||||||
'href' : link_href,
|
|
||||||
'iden' : 'browse'})
|
|
||||||
view_href = '/d/' + jid_bare
|
|
||||||
xmpp_uri = jid_bare
|
|
||||||
else:
|
|
||||||
action = 'Message'
|
action = 'Message'
|
||||||
instance = 'articles'
|
instance = 'articles'
|
||||||
|
else: # jid_info['error']
|
||||||
|
action = 'Contact'
|
||||||
|
return action, instance
|
||||||
|
|
||||||
|
def get_link_href(jid_bare, jid_kind, node_name=None):
|
||||||
|
if jid_kind in ('conference', 'server'):
|
||||||
|
link_href = 'xmpp:{}?disco;type=get;request=items'.format(jid_bare)
|
||||||
|
elif jid_kind in ('mix', 'muc'):
|
||||||
|
link_href = 'xmpp:{}?join'.format(jid_bare)
|
||||||
|
elif jid_kind == 'pubsub':
|
||||||
|
if node_name:
|
||||||
|
link_href = 'xmpp:{}?pubsub;node={};action=subscribe'.format(jid_bare, node_name)
|
||||||
|
else:
|
||||||
|
link_href = 'xmpp:{}?disco;type=get;request=items'.format(jid_bare)
|
||||||
|
elif jid_kind == 'account':
|
||||||
link_href = 'xmpp:{}?message'.format(jid_bare)
|
link_href = 'xmpp:{}?message'.format(jid_bare)
|
||||||
|
else: # jid_info['error']
|
||||||
|
link_href = 'xmpp:{}'.format(jid_bare)
|
||||||
|
return link_href
|
||||||
|
|
||||||
|
def get_view_href(jid_bare, jid_kind, node_name=None):
|
||||||
|
links = []
|
||||||
|
view_href = None
|
||||||
|
if jid_kind in ('conference', 'server'):
|
||||||
|
view_href = '/d/' + jid_bare
|
||||||
|
elif jid_kind in ('mix', 'muc'):
|
||||||
|
view_href = '/v/' + jid_bare
|
||||||
|
elif jid_kind == 'pubsub':
|
||||||
|
if node_name:
|
||||||
|
view_href = '/d/{}/{}'.format(jid_bare, node_name)
|
||||||
|
else:
|
||||||
|
view_href = '/d/' + jid_bare
|
||||||
|
elif jid_kind == 'account':
|
||||||
|
view_href = '/d/{}/{}'.format(jid_bare, node_name)
|
||||||
|
return view_href
|
||||||
|
|
||||||
|
def get_xmpp_uri(jid_bare, jid_kind, node_name=None):
|
||||||
|
links = []
|
||||||
|
view_href = None
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
if jid_kind in ('conference', 'server'):
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
elif jid_kind in ('mix', 'muc'):
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
elif jid_kind == 'pubsub':
|
||||||
|
if node_name:
|
||||||
|
xmpp_uri = '{}?;node={}'.format(jid_bare, node_name)
|
||||||
|
else:
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
elif jid_kind == 'account':
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
return xmpp_uri
|
||||||
|
|
||||||
|
def get_query_uri_links(jid_bare, jid_kind, node_name=None, item_id=None):
|
||||||
|
links = []
|
||||||
|
if jid_kind in ('conference', 'server'):
|
||||||
|
links.append({'name' : 'Discover',
|
||||||
|
'href' : 'xmpp:{}?disco;type=get;request=items'.format(jid_bare),
|
||||||
|
'iden' : 'discover'})
|
||||||
|
xmpp_uri = jid_bare
|
||||||
|
elif jid_kind in ('mix', 'muc'):
|
||||||
|
links.append({'name' : 'Join',
|
||||||
|
'href' : 'xmpp:{}?join'.format(jid_bare),
|
||||||
|
'iden' : 'join'})
|
||||||
|
elif jid_kind == 'pubsub':
|
||||||
|
links.append({'name' : 'Browse',
|
||||||
|
'href' : 'xmpp:{}?disco;type=get;request=items'.format(jid_bare),
|
||||||
|
'iden' : 'browse'})
|
||||||
|
elif jid_kind == 'account':
|
||||||
links.append({'name' : 'Message',
|
links.append({'name' : 'Message',
|
||||||
'href' : link_href,
|
'href' : 'xmpp:{}?message'.format(jid_bare),
|
||||||
'iden' : 'message'})
|
'iden' : 'message'})
|
||||||
links.append({'name' : 'Add',
|
links.append({'name' : 'Add',
|
||||||
'href' : 'xmpp:{}?roster'.format(jid_bare),
|
'href' : 'xmpp:{}?roster'.format(jid_bare),
|
||||||
'iden' : 'add'})
|
'iden' : 'add'})
|
||||||
#node_name = 'urn:xmpp:microblog:0'
|
else: # jid_info['error']
|
||||||
view_href = '/d/{}/{}'.format(jid_bare, node_name)
|
links.append({'name' : 'Connect',
|
||||||
xmpp_uri = jid_bare
|
'href' : 'xmpp:{}'.format(jid_bare),
|
||||||
|
'iden' : 'connect'})
|
||||||
|
links.append({'name' : 'Add',
|
||||||
|
'href' : 'xmpp:{}?roster'.format(jid_bare),
|
||||||
|
'iden' : 'add'})
|
||||||
if item_id:
|
if item_id:
|
||||||
links.append({'name' : 'Subscribe',
|
links.append({'name' : 'Subscribe',
|
||||||
'href' : 'xmpp:{}?pubsub;node={};item={};action=subscribe'.format(jid_bare, node_name, item_id),
|
'href' : 'xmpp:{}?pubsub;node={};item={};action=subscribe'.format(jid_bare, node_name, item_id),
|
||||||
|
@ -1252,7 +1301,7 @@ class XmppUtilities:
|
||||||
links.append({'name' : 'vCard',
|
links.append({'name' : 'vCard',
|
||||||
'href' : 'xmpp:{}?vcard'.format(jid_bare),
|
'href' : 'xmpp:{}?vcard'.format(jid_bare),
|
||||||
'iden' : 'vcard'})
|
'iden' : 'vcard'})
|
||||||
return action, instance, link_href, links, view_href, xmpp_uri
|
return links
|
||||||
|
|
||||||
class XmppXep0030:
|
class XmppXep0030:
|
||||||
|
|
||||||
|
@ -1298,12 +1347,12 @@ class XmppXep0030:
|
||||||
elif 'urn:xmpp:mix:core:1' in features:
|
elif 'urn:xmpp:mix:core:1' in features:
|
||||||
jid_kind = 'mix'
|
jid_kind = 'mix'
|
||||||
elif ('muc_moderated' in features or
|
elif ('muc_moderated' in features or
|
||||||
'muc_open' in features or
|
'muc_open' in features or
|
||||||
'muc_persistent' in features or
|
'muc_persistent' in features or
|
||||||
'muc_public' in features or
|
'muc_public' in features or
|
||||||
'muc_semianonymous' in features or
|
'muc_semianonymous' in features or
|
||||||
'muc_unmoderated' in features or
|
'muc_unmoderated' in features or
|
||||||
'muc_unsecured' in features):
|
'muc_unsecured' in features):
|
||||||
jid_kind = 'muc'
|
jid_kind = 'muc'
|
||||||
else:
|
else:
|
||||||
for identity in iq_disco_info['identities']:
|
for identity in iq_disco_info['identities']:
|
||||||
|
@ -1437,6 +1486,8 @@ class XmppXep0060:
|
||||||
|
|
||||||
async def get_node_items(self, jid_bare, node_name, item_ids=None, max_items=None):
|
async def get_node_items(self, jid_bare, node_name, item_ids=None, max_items=None):
|
||||||
try:
|
try:
|
||||||
|
error = False
|
||||||
|
condition = text = None
|
||||||
if max_items:
|
if max_items:
|
||||||
iq = await self['xep_0060'].get_items(
|
iq = await self['xep_0060'].get_items(
|
||||||
jid_bare, node_name, timeout=5)
|
jid_bare, node_name, timeout=5)
|
||||||
|
@ -1452,15 +1503,21 @@ class XmppXep0060:
|
||||||
iq = await self['xep_0060'].get_items(
|
iq = await self['xep_0060'].get_items(
|
||||||
jid_bare, node_name, timeout=5, item_ids=item_ids)
|
jid_bare, node_name, timeout=5, item_ids=item_ids)
|
||||||
result = iq
|
result = iq
|
||||||
except IqError as e:
|
except (IqError, IqTimeout) as e:
|
||||||
if e.iq['error']['text'] == 'Node not found':
|
error = True
|
||||||
result = 'Node not found'
|
iq = None
|
||||||
elif e.iq['error']['condition'] == 'item-not-found':
|
condition = e.iq['error']['condition']
|
||||||
result = 'Item not found'
|
text = e.iq['error']['text']
|
||||||
else:
|
if not text:
|
||||||
result = None
|
if condition:
|
||||||
except IqTimeout as e:
|
text = 'Could not retrieve node items'
|
||||||
result = e
|
else:
|
||||||
|
text = 'Unknown Error'
|
||||||
|
result = {
|
||||||
|
'error' : error,
|
||||||
|
'condition' : condition,
|
||||||
|
'text' : text,
|
||||||
|
'iq' : iq}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def get_node_item_ids(self, jid_bare, node_name):
|
async def get_node_item_ids(self, jid_bare, node_name):
|
||||||
|
@ -1474,14 +1531,14 @@ class XmppXep0060:
|
||||||
# jid_bare, node_name, timeout=5)
|
# jid_bare, node_name, timeout=5)
|
||||||
except (IqError, IqTimeout) as e:
|
except (IqError, IqTimeout) as e:
|
||||||
error = True
|
error = True
|
||||||
|
iq = None
|
||||||
condition = e.iq['error']['condition']
|
condition = e.iq['error']['condition']
|
||||||
text = e.iq['error']['text']
|
text = e.iq['error']['text']
|
||||||
if not text:
|
if not text:
|
||||||
if condition:
|
if condition:
|
||||||
text = 'Could not retrieve node items'
|
text = 'Could not retrieve node item IDs'
|
||||||
else:
|
else:
|
||||||
text = 'Unknown Error'
|
text = 'Unknown Error'
|
||||||
iq = None
|
|
||||||
result = {
|
result = {
|
||||||
'error' : error,
|
'error' : error,
|
||||||
'condition' : condition,
|
'condition' : condition,
|
||||||
|
|
1
img/plant.svg
Normal file
1
img/plant.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"><path style="fill:#22bb22;stroke:none" d="M104 132h-1c-5-21-20-42-38-54-3-3-21-10-19-15s10 0 13 1c6 4 33 33 33 12 1-31-31-39-56-43-6-1-25-4-29 2-6 6-1 26 0 33 5 30 21 54 54 49 8-1 15-4 22-7 7 20 14 37 17 58 1 8-2 17 8 18l7-63 9-27c11 2 21 6 33 4 26-3 34-30 37-52 1-6 4-25-1-31-4-5-23-1-29-1-30 3-60 17-54 52 0 5 4 8 8 5 9-6 15-18 23-25 2-1 11-9 13-4s-16 17-19 20c-17 18-28 44-31 68z"/></svg>
|
After Width: | Height: | Size: 457 B |
1619
img/reactos.svg
Normal file
1619
img/reactos.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 70 KiB |
|
@ -31,7 +31,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="action-bar">
|
<div id="action-bar">
|
||||||
<a href="/download/divestos" id="divestos">
|
<a href="/download/android" id="android">
|
||||||
Android
|
Android
|
||||||
</a>
|
</a>
|
||||||
<a href="/download/apple" id="apple">
|
<a href="/download/apple" id="apple">
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
<a href="/download/linux" id="linux">
|
<a href="/download/linux" id="linux">
|
||||||
Linux
|
Linux
|
||||||
</a>
|
</a>
|
||||||
<a href="/download/reactos" id="reactos">
|
<a href="/download/windows" id="windows">
|
||||||
Win
|
Win
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,79 +55,92 @@
|
||||||
<h1>XMPP Clients For {{title}}</h1>
|
<h1>XMPP Clients For {{title}}</h1>
|
||||||
{% if title == 'Apple' %}
|
{% if title == 'Apple' %}
|
||||||
<div id="security-notices">
|
<div id="security-notices">
|
||||||
<span class="notice">
|
<span class="notice">
|
||||||
<a href="/download/haiku">
|
<a href="/download/haiku">
|
||||||
<img src="/img/haiku.svg" />
|
<img src="/img/haiku.svg" />
|
||||||
</a>
|
</a>
|
||||||
<span>
|
<span>
|
||||||
Please refer to the section <a href="/download/haiku">XMPP
|
Please refer to the section <a href="/download/haiku">XMPP
|
||||||
Clients For Haiku</a>, if you are using the Haiku operating
|
Clients For Haiku</a>, if you are using the Haiku operating
|
||||||
system.
|
system.
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if title == 'DivestOS' %}
|
{% if title == 'Android' %}
|
||||||
<div id="security-notices">
|
<div id="security-notices">
|
||||||
<span class="notice">
|
<span class="notice">
|
||||||
<a href="https://divestos.org">
|
<a href="https://f-droid.org">
|
||||||
<img src="/img/divestos.svg" />
|
<img src="/img/fdroid.svg" />
|
||||||
</a>
|
</a>
|
||||||
<span>
|
<span>
|
||||||
Due to constant attempts by smartphone manufacturers to
|
We advise installing an XMPP client with the
|
||||||
sabotage the functionalities of Android-based XMPP chat
|
<a href="https://f-droid.org">F-Droid</a> repository, to
|
||||||
clients; you are advised to install a secure Android ROM such
|
ensure that you are always using the most updated and secure
|
||||||
as <a href="https://divestos.org">DivestOS Mobile</a> to
|
version of your XMPP chat client of choice, with the latest
|
||||||
guarantee a flawless and secure communication experience.
|
and best that XMPP has to offer.
|
||||||
|
<strong>(recommended)</strong>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
<span class="notice">
|
||||||
<span class="notice">
|
<a href="https://customrombay.org">
|
||||||
<a href="https://f-droid.org">
|
<img src="/img/plant.svg" />
|
||||||
<img src="/img/fdroid.svg" />
|
</a>
|
||||||
</a>
|
<span>
|
||||||
<span>
|
We further suggest using an updated Android system. If your
|
||||||
Due to recent incidents of censoring of XMPP clients from the
|
smartphone manufacturer has ceased to provide updates,
|
||||||
Android store, including of an outrageous banning of the app
|
please visit <a href="https://divestos.org">DivestOS
|
||||||
<a href="https://blabber.im/">blabber.im</a> for grotesque and
|
Mobile</a> or <a href="https://customrombay.org">
|
||||||
preposterous alligations, you are further advised to obtain
|
CustomRomBay</a> to find an updated version for your device.
|
||||||
your XMPP client from the open source store
|
<strong>(optional)</strong>
|
||||||
<a href="https://f-droid.org">F-Droid</a>.
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if title == 'Haiku' %}
|
{% if title == 'Haiku' %}
|
||||||
<div id="security-notices">
|
<div id="security-notices">
|
||||||
<span class="notice">
|
<span class="notice">
|
||||||
<img src="/img/haikudepot.svg" />
|
<img src="/img/haikudepot.svg" />
|
||||||
<span>
|
<span>
|
||||||
Most of the software that are listed here should be available
|
Most of the software that are listed here should be available
|
||||||
for you to install directly from desktop with HaikuDepot.
|
for you to install directly from desktop with HaikuDepot.
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if title == 'Linux' %}
|
{% if title == 'Linux' %}
|
||||||
<div id="security-notices">
|
<div id="security-notices">
|
||||||
<span class="notice">
|
<span class="notice">
|
||||||
<img src="/img/tux.svg" />
|
<img src="/img/tux.svg" />
|
||||||
<span>
|
<span>
|
||||||
The listed XMPP clients, herein, are also usable with the
|
The listed XMPP clients, herein, are also usable with the
|
||||||
Linux systems that are designed for mobile devices, e.g.
|
Linux systems that are designed for mobile devices, e.g.
|
||||||
<a href="https://droidian.org">Droidian</a>,
|
<a href="https://droidian.org">Droidian</a>,
|
||||||
<a href="https://mobian.org">Mobian</a> and
|
<a href="https://mobian.org">Mobian</a> and
|
||||||
<a href="https://postmarketos.org">postmarketOS</a>.
|
<a href="https://postmarketos.org">postmarketOS</a>.
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
<span class="notice">
|
||||||
<span class="notice">
|
<a href="/download/posix">
|
||||||
<a href="/download/posix">
|
<img src="/img/posix.svg" />
|
||||||
<img src="/img/posix.svg" />
|
</a>
|
||||||
</a>
|
<span>
|
||||||
<span>
|
Please refer to the section <a href="/download/posix">XMPP
|
||||||
Please refer to <a href="/download/posix">this page</a>, if
|
Clients For POSIX</a>, if you prefer software with text
|
||||||
you prefer software with text based (i.e. console) interface.
|
based (i.e. console) interface.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if title == 'Windows' %}
|
||||||
|
<div id="security-notices">
|
||||||
|
<span class="notice">
|
||||||
|
<img src="/img/reactos.svg" />
|
||||||
|
<span>
|
||||||
|
The listed XMPP clients, herein, are compatible with the
|
||||||
|
<a href="https://reactos.org">ReactOS</a> operating system.
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<dl id="selection">
|
<dl id="selection">
|
||||||
|
|
Loading…
Reference in a new issue