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:
Schimon Jehudah, Adv. 2024-10-22 16:34:39 +03:00
parent 644842ab6d
commit 7dda347ebc
5 changed files with 1958 additions and 268 deletions

View file

@ -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"]

413
fasi.py
View file

@ -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:
@ -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']
if not text:
if condition:
text = 'Could not retrieve node items'
else: else:
result = None text = 'Unknown Error'
except IqTimeout as e: result = {
result = e '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
View 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

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -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>
@ -67,31 +67,32 @@
</span> </span>
</div> </div>
{% endif %} {% endif %}
{% if title == 'DivestOS' %} {% if title == 'Android' %}
<div id="security-notices"> <div id="security-notices">
<span class="notice">
<a href="https://divestos.org">
<img src="/img/divestos.svg" />
</a>
<span>
Due to constant attempts by smartphone manufacturers to
sabotage the functionalities of Android-based XMPP chat
clients; you are advised to install a secure Android ROM such
as <a href="https://divestos.org">DivestOS Mobile</a> to
guarantee a flawless and secure communication experience.
</span>
</span>
<span class="notice"> <span class="notice">
<a href="https://f-droid.org"> <a href="https://f-droid.org">
<img src="/img/fdroid.svg" /> <img src="/img/fdroid.svg" />
</a> </a>
<span> <span>
Due to recent incidents of censoring of XMPP clients from the We advise installing an XMPP client with the
Android store, including of an outrageous banning of the app <a href="https://f-droid.org">F-Droid</a> repository, to
<a href="https://blabber.im/">blabber.im</a> for grotesque and ensure that you are always using the most updated and secure
preposterous alligations, you are further advised to obtain version of your XMPP chat client of choice, with the latest
your XMPP client from the open source store and best that XMPP has to offer.
<a href="https://f-droid.org">F-Droid</a>. <strong>(recommended)</strong>
</span>
</span>
<span class="notice">
<a href="https://customrombay.org">
<img src="/img/plant.svg" />
</a>
<span>
We further suggest using an updated Android system. If your
smartphone manufacturer has ceased to provide updates,
please visit <a href="https://divestos.org">DivestOS
Mobile</a> or <a href="https://customrombay.org">
CustomRomBay</a> to find an updated version for your device.
<strong>(optional)</strong>
</span> </span>
</span> </span>
</div> </div>
@ -124,8 +125,20 @@
<img src="/img/posix.svg" /> <img src="/img/posix.svg" />
</a> </a>
<span> <span>
Please refer to <a href="/download/posix">this page</a>, if Please refer to the section <a href="/download/posix">XMPP
you prefer software with text based (i.e. console) interface. Clients For POSIX</a>, if you prefer software with text
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>