From f52cc88ae269255428738dbbe7d573d90f4154b6 Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 30 Sep 2021 10:30:43 +0200 Subject: [PATCH 01/24] plugin XEP-0054: fix wrongly used `await`: `_checkAvatarHash` was incorrectly awaited. This method is only called once, the code has been moved directly to `update`. --- sat/plugins/plugin_xep_0054.py | 59 ++++++++++++++-------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/sat/plugins/plugin_xep_0054.py b/sat/plugins/plugin_xep_0054.py index 4c85df12..70e40a5f 100644 --- a/sat/plugins/plugin_xep_0054.py +++ b/sat/plugins/plugin_xep_0054.py @@ -45,10 +45,7 @@ except: "Missing module pillow, please download/install it from https://python-pillow.github.io" ) -try: - from twisted.words.protocols.xmlstream import XMPPHandler -except ImportError: - from wokkel.subprotocols import XMPPHandler +from twisted.words.protocols.jabber.xmlstream import XMPPHandler IMPORT_NAME = "XEP-0054" @@ -109,7 +106,7 @@ class XEP_0054(object): NS_VCARD, client.profile) await client._xep_0054_avatar_hashes.load() - def savePhoto(self, client, photo_elt, entity_jid): + def savePhoto(self, client, photo_elt, entity): """Parse a photo_elt and save the picture""" # XXX: this method is launched in a separate thread try: @@ -131,7 +128,7 @@ class XEP_0054(object): raise Failure(exceptions.NotFound()) if not buf: - log.warning("empty avatar for {jid}".format(jid=entity_jid.full())) + log.warning("empty avatar for {jid}".format(jid=entity.full())) raise Failure(exceptions.NotFound()) log.debug(_("Decoding binary")) @@ -140,7 +137,7 @@ class XEP_0054(object): if mime_type is None: log.debug( - f"no media type found specified for {entity_jid}'s avatar, trying to " + f"no media type found specified for {entity}'s avatar, trying to " f"guess") try: @@ -149,7 +146,7 @@ class XEP_0054(object): log.warning(f"Can't open avatar buffer: {e}") if mime_type is None: - msg = f"Can't find media type for {entity_jid}'s avatar" + msg = f"Can't find media type for {entity}'s avatar" log.warning(msg) raise Failure(exceptions.DataError(msg)) @@ -435,19 +432,6 @@ class XEP_0054_handler(XMPPHandler): def getDiscoItems(self, requestor, target, nodeIdentifier=""): return [] - def _checkAvatarHash(self, client, entity, given_hash): - """Check that hash in cache (i.e. computed hash) is the same as given one""" - # XXX: if they differ, the avatar will be requested on each connection - # TODO: try to avoid re-requesting avatar in this case - computed_hash = client._xep_0054_avatar_hashes[entity.full()] - if computed_hash != given_hash: - log.warning( - "computed hash differs from given hash for {entity}:\n" - "computed: {computed}\ngiven: {given}".format( - entity=entity, computed=computed_hash, given=given_hash - ) - ) - async def update(self, presence): """Called on stanza with vcard data @@ -468,19 +452,19 @@ class XEP_0054_handler(XMPPHandler): except StopIteration: return - new_hash = str(photo_elt).strip() - if new_hash == HASH_SHA1_EMPTY: - new_hash = "" + given_hash = str(photo_elt).strip() + if given_hash == HASH_SHA1_EMPTY: + given_hash = "" hashes_cache = client._xep_0054_avatar_hashes old_hash = hashes_cache.get(entity_jid.full()) - if old_hash == new_hash: + if old_hash == given_hash: # no change, we can return… - if new_hash: + if given_hash: # …but we double check that avatar is in cache - avatar_cache = self.host.common_cache.getMetadata(new_hash) + avatar_cache = self.host.common_cache.getMetadata(given_hash) if avatar_cache is None: log.debug( f"Avatar for [{entity_jid}] is known but not in cache, we get " @@ -492,19 +476,19 @@ class XEP_0054_handler(XMPPHandler): log.debug(f"avatar for {entity_jid} is already in cache") return - if new_hash is None: + if given_hash is None: # XXX: we use empty string to indicate that there is no avatar - new_hash = "" + given_hash = "" - await hashes_cache.aset(entity_jid.full(), new_hash) + await hashes_cache.aset(entity_jid.full(), given_hash) - if not new_hash: + if not given_hash: await self.plugin_parent._i.update( client, IMPORT_NAME, "avatar", None, entity_jid) # the avatar has been removed, no need to go further return - avatar_cache = self.host.common_cache.getMetadata(new_hash) + avatar_cache = self.host.common_cache.getMetadata(given_hash) if avatar_cache is not None: log.debug( f"New avatar found for [{entity_jid}], it's already in cache, we use it" @@ -516,7 +500,7 @@ class XEP_0054_handler(XMPPHandler): 'path': avatar_cache['path'], 'filename': avatar_cache['filename'], 'media_type': avatar_cache['mime_type'], - 'cache_uid': new_hash, + 'cache_uid': given_hash, }, entity_jid ) @@ -528,7 +512,14 @@ class XEP_0054_handler(XMPPHandler): if vcard is None: log.warning(f"Unexpected empty vCard for {entity_jid}") return - await self._checkAvatarHash(client, entity_jid, new_hash) + computed_hash = client._xep_0054_avatar_hashes[entity_jid.full()] + if computed_hash != given_hash: + log.warning( + "computed hash differs from given hash for {entity}:\n" + "computed: {computed}\ngiven: {given}".format( + entity=entity_jid, computed=computed_hash, given=given_hash + ) + ) def _update(self, presence): defer.ensureDeferred(self.update(presence)) From 24ce06eedfe3a55e007f18160b04849c9f919e01 Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 30 Sep 2021 10:32:04 +0200 Subject: [PATCH 02/24] install (setup): set minimum version of `python-dateutil` to `2.8.1`: `dateutil.parser.ParserError` is only available from `2.8.1`, thus this become the minimum required version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1884c5b8..320168c8 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ install_requires = [ 'pygments < 3', 'pygobject < 3.40.1', 'pyopenssl < 21.0.0', - 'python-dateutil < 3', + 'python-dateutil >= 2.8.1, < 3', 'python-potr < 1.1', 'pyxdg < 0.30', 'sat_tmp >= 0.8.0b1, < 0.9', From 5903aaf29b212a93ea6645e99080f593693dab83 Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 30 Sep 2021 10:33:09 +0200 Subject: [PATCH 03/24] misc (.hgignore): exlude e2e tests reports --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index bdc05dd7..bf7ea147 100644 --- a/.hgignore +++ b/.hgignore @@ -7,6 +7,7 @@ tags twistd.log twistd.pid bridge_constructor/generated +tests/e2e/report* _trial_temp/ sat.egg-info *.un~ From 7c31c279e4c93854c5394eb22c9fa859254304db Mon Sep 17 00:00:00 2001 From: Goffi Date: Wed, 20 Oct 2021 17:19:57 +0200 Subject: [PATCH 04/24] docker: fix use of File Sharing component following entry point renaming --- docker/backend-dev-demo/Dockerfile | 2 +- docker/backend-dev-demo/libervia.conf | 2 +- docker/backend-dev-e2e/Dockerfile | 2 +- docker/backend-dev-e2e/libervia.conf | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/backend-dev-demo/Dockerfile b/docker/backend-dev-demo/Dockerfile index fcd82510..6f78fd92 100644 --- a/docker/backend-dev-demo/Dockerfile +++ b/docker/backend-dev-demo/Dockerfile @@ -16,7 +16,7 @@ USER libervia RUN ./entrypoint.sh \ # we create the file sharing component which will autoconnect when backend is started - jp profile create file_sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file_sharing -A && \ + jp profile create file-sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file-sharing -A && \ libervia-backend stop RUN ./entrypoint.sh \ diff --git a/docker/backend-dev-demo/libervia.conf b/docker/backend-dev-demo/libervia.conf index 2c487e25..001e6c89 100644 --- a/docker/backend-dev-demo/libervia.conf +++ b/docker/backend-dev-demo/libervia.conf @@ -4,7 +4,7 @@ email_server = libervia-backend.test email_port = 8025 email_admins_list = admin@server1.test -[component file_sharing] +[component file-sharing] http_upload_public_facing_url = http://localhost:7777 http_upload_connection_type = http http_upload_port = 7777 diff --git a/docker/backend-dev-e2e/Dockerfile b/docker/backend-dev-e2e/Dockerfile index 3866d011..1fe2962c 100644 --- a/docker/backend-dev-e2e/Dockerfile +++ b/docker/backend-dev-e2e/Dockerfile @@ -58,7 +58,7 @@ RUN \ RUN ./entrypoint.sh \ # we create the file sharing component which will autoconnect when backend is started - li profile create file_sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file_sharing -A && \ + li profile create file-sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file-sharing -A && \ libervia-backend stop ENV LIBERVIA_TEST_REPORT_DIR=/reports diff --git a/docker/backend-dev-e2e/libervia.conf b/docker/backend-dev-e2e/libervia.conf index f9d8cec0..90f125a5 100644 --- a/docker/backend-dev-e2e/libervia.conf +++ b/docker/backend-dev-e2e/libervia.conf @@ -4,7 +4,7 @@ email_server = libervia-backend.test email_port = 8025 email_admins_list = admin@server1.test -[component file_sharing] +[component file-sharing] tls_certificate = /usr/share/libervia/certificates/server1.test.pem tls_private_key = /usr/share/libervia/certificates/server1.test-key.pem http_upload_public_facing_url = https://libervia-backend.test:8888 From 60f3f89883068417160631fe7b2e72c0ea6037e7 Mon Sep 17 00:00:00 2001 From: Goffi Date: Wed, 20 Oct 2021 17:21:54 +0200 Subject: [PATCH 05/24] docker: fix use of `REVISION` in `FROM` instruction --- docker/backend-dev-e2e/Dockerfile | 4 ++-- docker/libervia-web-dev-demo/Dockerfile | 4 ++-- docker/libervia-web-dev-e2e/Dockerfile | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/backend-dev-e2e/Dockerfile b/docker/backend-dev-e2e/Dockerfile index 1fe2962c..3e34a7df 100644 --- a/docker/backend-dev-e2e/Dockerfile +++ b/docker/backend-dev-e2e/Dockerfile @@ -1,5 +1,5 @@ -ARG REVISION=dev -FROM libervia/backend:${REVISION} +ARG REVISION +FROM libervia/backend:${REVISION:-dev} LABEL maintainer="Goffi " diff --git a/docker/libervia-web-dev-demo/Dockerfile b/docker/libervia-web-dev-demo/Dockerfile index 6b00e7a9..70751d3b 100644 --- a/docker/libervia-web-dev-demo/Dockerfile +++ b/docker/libervia-web-dev-demo/Dockerfile @@ -1,5 +1,5 @@ -ARG REVISION=dev -FROM libervia/web:${REVISION} +ARG REVISION +FROM libervia/web:${REVISION:-dev} LABEL maintainer="Goffi " diff --git a/docker/libervia-web-dev-e2e/Dockerfile b/docker/libervia-web-dev-e2e/Dockerfile index 20647eb3..8f56f108 100644 --- a/docker/libervia-web-dev-e2e/Dockerfile +++ b/docker/libervia-web-dev-e2e/Dockerfile @@ -1,5 +1,5 @@ -ARG REVISION=dev -FROM libervia/web:${REVISION} +ARG REVISION +FROM libervia/web:${REVISION:-dev} LABEL maintainer="Goffi " From 0ba1709bb54522b0b56648373ae52a52fd271ac2 Mon Sep 17 00:00:00 2001 From: Goffi Date: Wed, 20 Oct 2021 17:23:22 +0200 Subject: [PATCH 06/24] docker: use `@` instead of `tip` for dev images: `tip` links to most recent commit which may be on an other bookmark than the main one. By using `@`, we're sure that the main bookmark is used. --- docker/backend-dev/Dockerfile | 4 ++-- docker/libervia-web-dev/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/backend-dev/Dockerfile b/docker/backend-dev/Dockerfile index 70e88c26..ba429cd4 100644 --- a/docker/backend-dev/Dockerfile +++ b/docker/backend-dev/Dockerfile @@ -36,12 +36,12 @@ RUN python3 -m venv libervia_env && libervia_env/bin/pip install -U pip wheel && # local repos without it them cause troubles hg clone https://repos.goffi.org/urwid-satext && ~/libervia_env/bin/pip install -e urwid-satext && \ mv urwid-satext/urwid_satext.egg-info ~/libervia_env/lib/python3.*/site-packages && \ - hg clone https://repos.goffi.org/sat_tmp -u "${REVISION:-tip}" && ~/libervia_env/bin/pip install -e sat_tmp && \ + hg clone https://repos.goffi.org/sat_tmp -u "${REVISION:-@}" && ~/libervia_env/bin/pip install -e sat_tmp && \ mv sat_tmp/sat_tmp.egg-info ~/libervia_env/lib/python3.*/site-packages && \ hg clone https://repos.goffi.org/libervia-templates && ~/libervia_env/bin/pip install -e libervia-templates && \ mv libervia-templates/libervia_templates.egg-info ~/libervia_env/lib/python3.*/site-packages && \ hg clone https://repos.goffi.org/libervia-media && \ - hg clone https://repos.goffi.org/libervia-backend -u "${REVISION:-tip}" && ~/libervia_env/bin/pip install -e 'libervia-backend[SVG]' && \ + hg clone https://repos.goffi.org/libervia-backend -u "${REVISION:-@}" && ~/libervia_env/bin/pip install -e 'libervia-backend[SVG]' && \ mv libervia-backend/libervia_backend.egg-info ~/libervia_env/lib/python3.*/site-packages && \ mkdir -p /home/libervia/.local/share/libervia diff --git a/docker/libervia-web-dev/Dockerfile b/docker/libervia-web-dev/Dockerfile index 390f9479..8f9ba7b3 100644 --- a/docker/libervia-web-dev/Dockerfile +++ b/docker/libervia-web-dev/Dockerfile @@ -11,7 +11,7 @@ USER root RUN apt-get install -y --no-install-recommends yarnpkg WORKDIR /home/libervia USER libervia -RUN cd /src && hg clone https://repos.goffi.org/libervia-web -u "${REVISION:-tip}" && \ +RUN cd /src && hg clone https://repos.goffi.org/libervia-web -u "${REVISION:-@}" && \ ~/libervia_env/bin/pip install -e libervia-web && \ mv libervia-web/libervia_web.egg-info ~/libervia_env/lib/python3.*/site-packages From 6f0ecc0105e48739ced60d47c88fe7ea8ea6e0fa Mon Sep 17 00:00:00 2001 From: Goffi Date: Wed, 20 Oct 2021 17:24:09 +0200 Subject: [PATCH 07/24] docker (docker-compose-e2e): fix `tmpfs` instruction for `prosody` --- docker/docker-compose-e2e.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose-e2e.yml b/docker/docker-compose-e2e.yml index 2b77fe08..3201c37f 100644 --- a/docker/docker-compose-e2e.yml +++ b/docker/docker-compose-e2e.yml @@ -7,8 +7,9 @@ services: depends_on: # we need to depend on backend to get IP address of the container for conf - backend - tmpfs: /var/lib/prosody - tmpfs: /var/log/prosody + tmpfs: + - /var/lib/prosody + - /var/log/prosody networks: default: aliases: From e529391500247d6309b0d59bcb1ec8b3af85102a Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 21 Oct 2021 11:53:56 +0200 Subject: [PATCH 08/24] docker (web-demo): fix prosody `tmpfs` instruction --- docker/web-demo.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/web-demo.yml b/docker/web-demo.yml index 633d71df..505a0f26 100644 --- a/docker/web-demo.yml +++ b/docker/web-demo.yml @@ -6,8 +6,9 @@ services: depends_on: # we need to depend on backend to get IP address of the container for conf - backend - tmpfs: /var/lib/prosody - tmpfs: /var/log/prosody + tmpfs: + - /var/lib/prosody + - /var/log/prosody networks: default: aliases: From 28b0a91fc7714542bc05f58e693a09b3acd3c336 Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 21 Oct 2021 11:55:39 +0200 Subject: [PATCH 09/24] plugin text syntaxes: remove `id` attributes and whitelist allowed classes: for now, the only allowed classes are the one used for code highlighting. fix 389 --- sat/plugins/plugin_misc_text_syntaxes.py | 43 ++++++++++++++++++------ 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/sat/plugins/plugin_misc_text_syntaxes.py b/sat/plugins/plugin_misc_text_syntaxes.py index 2217b707..d8771d0e 100644 --- a/sat/plugins/plugin_misc_text_syntaxes.py +++ b/sat/plugins/plugin_misc_text_syntaxes.py @@ -17,16 +17,18 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import re -from html import escape from functools import partial -from sat.core.i18n import _, D_ -from sat.core.constants import Const as C -from sat.core.log import getLogger +from html import escape +import re +from typing import Set from twisted.internet import defer from twisted.internet.threads import deferToThread + from sat.core import exceptions +from sat.core.constants import Const as C +from sat.core.i18n import D_, _ +from sat.core.log import getLogger from sat.tools import xml_tools try: @@ -115,7 +117,15 @@ VOID_ELEMENTS = ( "track", "wbr") -SAFE_ATTRS = html.defs.safe_attrs.union(("style", "poster", "controls")) +SAFE_ATTRS = html.defs.safe_attrs.union({"style", "poster", "controls"}) - {"id"} +SAFE_CLASSES = { + # those classes are used for code highlighting + "bp", "c", "ch", "cm", "cp", "cpf", "cs", "dl", "err", "fm", "gd", "ge", "get", "gh", + "gi", "go", "gp", "gr", "gs", "gt", "gu", "highlight", "hll", "il", "k", "kc", "kd", + "kn", "kp", "kr", "kt", "m", "mb", "mf", "mh", "mi", "mo", "na", "nb", "nc", "nd", + "ne", "nf", "ni", "nl", "nn", "no", "nt", "nv", "o", "ow", "s", "sa", "sb", "sc", + "sd", "se", "sh", "si", "sr", "ss", "sx", "vc", "vg", "vi", "vm", "w", "write", +} STYLES_VALUES_REGEX = ( r"^(" + "|".join( @@ -235,7 +245,8 @@ class TextSyntaxes(object): except ImportError: log.warning("markdown or html2text not found, can't use Markdown syntax") log.info( - "You can download/install them from https://pythonhosted.org/Markdown/ and https://github.com/Alir3z4/html2text/" + "You can download/install them from https://pythonhosted.org/Markdown/ " + "and https://github.com/Alir3z4/html2text/" ) host.bridge.addMethod( "syntaxConvert", @@ -286,14 +297,14 @@ class TextSyntaxes(object): ) return failure - def cleanStyle(self, styles): + def cleanStyle(self, styles_raw: str) -> str: """"Clean unsafe CSS styles Remove styles not in the whitelist, or where the value doesn't match the regex - @param styles_raw(unicode): CSS styles - @return (unicode): cleaned styles + @param styles_raw: CSS styles + @return: cleaned styles """ - styles = styles.split(";") + styles: List[str] = styles_raw.split(";") cleaned_styles = [] for style in styles: try: @@ -313,6 +324,14 @@ class TextSyntaxes(object): ["%s: %s" % (key_, value_) for key_, value_ in cleaned_styles] ) + def cleanClasses(self, classes_raw: str) -> str: + """Remove any non whitelisted class + + @param classes_raw: classes set on an element + @return: remaining classes (can be empty string) + """ + return " ".join(SAFE_CLASSES.intersection(classes_raw.split())) + def cleanXHTML(self, xhtml): """Clean XHTML text by removing potentially dangerous/malicious parts @@ -339,6 +358,8 @@ class TextSyntaxes(object): xhtml_elt = cleaner.clean_html(xhtml_elt) for elt in xhtml_elt.xpath("//*[@style]"): elt.set("style", self.cleanStyle(elt.get("style"))) + for elt in xhtml_elt.xpath("//*[@class]"): + elt.set("class", self.cleanClasses(elt.get("class"))) # we remove self-closing elements for non-void elements for element in xhtml_elt.iter(tag=etree.Element): if not element.text: From 770d4e8f354a840baea9e3e24cb13f6a7c4ace87 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 22 Oct 2021 10:39:05 +0200 Subject: [PATCH 10/24] docker (prosody): remove unused `entrypoint.sh` file and `sed` from Dockerfile: The `sed` is not necessary anymore in recent Docker images, since https://github.com/prosody/prosody-docker/pull/65 has been merged. --- docker/prosody-e2e/Dockerfile | 4 ---- docker/prosody-e2e/entrypoint.sh | 15 --------------- 2 files changed, 19 deletions(-) delete mode 100644 docker/prosody-e2e/entrypoint.sh diff --git a/docker/prosody-e2e/Dockerfile b/docker/prosody-e2e/Dockerfile index eae49b22..3f1da9b9 100644 --- a/docker/prosody-e2e/Dockerfile +++ b/docker/prosody-e2e/Dockerfile @@ -16,7 +16,3 @@ COPY --chown=root:prosody prosody.cfg.lua /etc/prosody/prosody.cfg.lua COPY --chown=root:tls-cert certificates/server1.test/cert.pem /usr/share/libervia/certificates/server1.test.pem COPY --chown=root:tls-cert certificates/server1.test/key.pem /usr/share/libervia/certificates/server1.test-key.pem -# we add exec to handle properly signals, this is missing upstream -# FIXME: to be removed when new images are generated with -# https://github.com/prosody/prosody-docker/pull/65 -RUN sed -i "s/^runuser -u prosody/exec \0/" /entrypoint.sh diff --git a/docker/prosody-e2e/entrypoint.sh b/docker/prosody-e2e/entrypoint.sh deleted file mode 100644 index ff4ea606..00000000 --- a/docker/prosody-e2e/entrypoint.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -e - -usermod -u "$(stat -c %u /var/lib/prosody/.)" prosody - -if [[ "$1" != "prosody" ]]; then - exec prosodyctl "$@" - exit 0; -fi - -if [ "$LOCAL" -a "$PASSWORD" -a "$DOMAIN" ] ; then - prosodyctl register "$LOCAL" "$DOMAIN" "$PASSWORD" -fi - -exec runuser -u prosody -- "$@" From 22db0b9880d2491439f5e64890ff0b6aafaa2d57 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 22 Oct 2021 10:40:24 +0200 Subject: [PATCH 11/24] docker (prosody): don't use `tmpfs` for `/var/lib/prosody` (it prevents the container to start) --- docker/docker-compose-e2e.yml | 4 +--- docker/web-demo.yml | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docker/docker-compose-e2e.yml b/docker/docker-compose-e2e.yml index 3201c37f..775295f4 100644 --- a/docker/docker-compose-e2e.yml +++ b/docker/docker-compose-e2e.yml @@ -7,9 +7,7 @@ services: depends_on: # we need to depend on backend to get IP address of the container for conf - backend - tmpfs: - - /var/lib/prosody - - /var/log/prosody + tmpfs: /var/log/prosody networks: default: aliases: diff --git a/docker/web-demo.yml b/docker/web-demo.yml index 505a0f26..4045fce8 100644 --- a/docker/web-demo.yml +++ b/docker/web-demo.yml @@ -7,7 +7,6 @@ services: # we need to depend on backend to get IP address of the container for conf - backend tmpfs: - - /var/lib/prosody - /var/log/prosody networks: default: From bdbf4596da03936277bd1a7a5674fb3eabd53637 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 22 Oct 2021 18:46:51 +0200 Subject: [PATCH 12/24] plugin XEP-0384: fix encryption for user own devices: our own jid is added automatically by OMEMO library, but during the generation of the stanza, our list of jids was used to get the keys (and thus our own jid and devices keys was missing). fix 402 --- sat/plugins/plugin_xep_0384.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sat/plugins/plugin_xep_0384.py b/sat/plugins/plugin_xep_0384.py index 85e11ad2..74545dd6 100644 --- a/sat/plugins/plugin_xep_0384.py +++ b/sat/plugins/plugin_xep_0384.py @@ -1424,13 +1424,12 @@ class OMEMO: header_elt = encrypted_elt.addElement('header') header_elt['sid'] = str(encryption_data['sid']) - for to_jid in to_jids: - bare_jid_s = to_jid.userhost() - - for rid, data in encryption_data['keys'][bare_jid_s].items(): + for key_data in encryption_data['keys'].values(): + for rid, data in key_data.items(): key_elt = header_elt.addElement( 'key', - content=b64enc(data['data'])) + content=b64enc(data['data']) + ) key_elt['rid'] = str(rid) if data['pre_key']: key_elt['prekey'] = 'true' From 3838f04169012787b2df1088219c81c1dc2a7c9c Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 4 Nov 2021 18:13:55 +0100 Subject: [PATCH 13/24] install: requirements update --- requirements.txt | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/requirements.txt b/requirements.txt index 104f9c21..bdc0b99c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,22 +1,18 @@ attrs==21.2.0 Automat==20.2.0 Babel==2.9.1 -cairocffi==1.2.0 -CairoSVG==2.5.2 -certifi==2021.5.30 -cffi==1.14.5 -chardet==4.0.0 +certifi==2021.10.8 +cffi==1.15.0 +charset-normalizer==2.0.7 constantly==15.1.0 -cryptography==3.4.7 -cssselect2==0.4.1 -dbus-python==1.2.16 -defusedxml==0.7.1 +cryptography==3.4.8 +dbus-python==1.2.18 DoubleRatchet==0.7.0 html2text==2020.1.16 hyperlink==21.0.0 -idna==2.10 +idna==3.3 incremental==21.3.0 -Jinja2==3.0.1 +Jinja2==3.0.2 langid==1.1.6 lxml==4.6.3 Markdown==3.3.4 @@ -24,38 +20,36 @@ MarkupSafe==2.0.1 miniupnpc==2.0.2 mutagen==1.45.1 netifaces==0.11.0 -numpy==1.20.3 +numpy==1.21.3 OMEMO==0.12.0 omemo-backend-signal==0.2.6 -Pillow==8.2.0 -progressbar2==3.53.1 -protobuf==3.17.3 +Pillow==8.4.0 +progressbar2==3.53.3 +protobuf==3.19.1 pyasn1==0.4.8 pyasn1-modules==0.2.8 pycairo==1.20.1 pycparser==2.20 pycrypto==2.6.1 -Pygments==2.9.0 -PyGObject==3.40.1 +Pygments==2.10.0 +PyGObject==3.40.0 PyNaCl==1.4.0 pyOpenSSL==20.0.1 -python-dateutil==2.8.1 +python-dateutil==2.8.2 python-potr==1.0.2 python-utils==2.5.6 -pytz==2021.1 +pytz==2021.3 pyxdg==0.27 PyYAML==5.4.1 -requests==2.25.1 +requests==2.26.0 service-identity==21.1.0 -setuptools-scm==6.0.1 shortuuid==1.0.1 six==1.16.0 -tinycss2==1.1.0 treq==21.5.0 Twisted==21.2.0 -urllib3==1.26.5 +typing-extensions==3.10.0.2 +urllib3==1.26.7 urwid==2.1.2 -webencodings==0.5.1 wokkel==18.0.0 X3DH==0.5.9 XEdDSA==0.4.7 From 7c37f32dc4f14d77dd39a099245f114b3ab16081 Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 4 Nov 2021 18:27:36 +0100 Subject: [PATCH 14/24] core: don't callback `self.conn_deferred` if already called: if the connection has been lost (e.g. forced disconnection by server), `self.conn_deferred` may have already been called, thus this is now checked to avoid an exception. --- sat/core/xmpp.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sat/core/xmpp.py b/sat/core/xmpp.py index 46028ffa..1fddf577 100644 --- a/sat/core/xmpp.py +++ b/sat/core/xmpp.py @@ -302,7 +302,11 @@ class SatXMPPEntity: self.streamInitialized() def _finish_connection(self, __): - self.conn_deferred.callback(None) + if self.conn_deferred.called: + # can happen in case of forced disconnection by server + log.debug(f"{self} has already been connected") + else: + self.conn_deferred.callback(None) def streamInitialized(self): """Called after _authd""" From d39a80ee5f051c4ef641b284cebc1894220dfe2c Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 5 Nov 2021 18:08:39 +0100 Subject: [PATCH 15/24] plugin XEP-0264: replace `SIZE_FULL_SCREEN` by `SIZE_BIG`: `SIZE_FULL_SCREEN` may result in large files, and BoB is currently used to transfer them, resulting in potentially large stanza. As a result, Libervia profile can be disconnected from server if stanza max size is reached. As a temporary workaround, `SIZE_BIG` is now the maximum size used. This should be fixed in 0.9 with a proper way to transfer large thumbnails (i.e. not BoB). --- sat/plugins/plugin_xep_0264.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sat/plugins/plugin_xep_0264.py b/sat/plugins/plugin_xep_0264.py index 1ce71243..2c763308 100644 --- a/sat/plugins/plugin_xep_0264.py +++ b/sat/plugins/plugin_xep_0264.py @@ -73,7 +73,10 @@ class XEP_0264(object): SIZE_MEDIUM = (640, 640) SIZE_BIG = (1280, 1280) SIZE_FULL_SCREEN = (2560, 2560) - SIZES = (SIZE_SMALL, SIZE_MEDIUM, SIZE_FULL_SCREEN) + # FIXME: SIZE_FULL_SCREEN is currently discarded as the resulting files are too big + # for BoB + # TODO: use an other mechanism than BoB for bigger files + SIZES = (SIZE_SMALL, SIZE_MEDIUM, SIZE_BIG) def __init__(self, host): log.info(_("Plugin XEP_0264 initialization")) From ecc6f969903b926ff95e633bdb90ba46dcf467d2 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 5 Nov 2021 18:11:18 +0100 Subject: [PATCH 16/24] plugin email invitations: re-use existing invitation for a given email: if an invitation is done on an email which has already received an invitation, it is re-used and no new XMPP account or Libervia profile is created (the name is modified though). This is currently done in an inefficient way due to `LazyPersistentBinaryDict` limitations (all invitations are loaded and checked to find the email). A more efficient check should be easier to implement with storage changes in 0.9 . --- sat/plugins/plugin_misc_email_invitation.py | 167 +++++++++++++------- 1 file changed, 107 insertions(+), 60 deletions(-) diff --git a/sat/plugins/plugin_misc_email_invitation.py b/sat/plugins/plugin_misc_email_invitation.py index 7441f846..cf8cd683 100644 --- a/sat/plugins/plugin_misc_email_invitation.py +++ b/sat/plugins/plugin_misc_email_invitation.py @@ -17,6 +17,7 @@ # along with this program. If not, see . import shortuuid +from typing import Optional from twisted.internet import defer from twisted.words.protocols.jabber import jid from twisted.words.protocols.jabber import error @@ -131,6 +132,97 @@ class InvitationsPlugin(object): kwargs[key] = str(value) return defer.ensureDeferred(self.create(**kwargs)) + async def getExistingInvitation(self, email: Optional[str]) -> Optional[dict]: + """Retrieve existing invitation with given email + + @param email: check if any invitation exist with this email + @return: first found invitation, or None if nothing found + """ + # FIXME: This method is highly inefficient, it get all invitations and check them + # one by one, this is just a temporary way to avoid creating creating new accounts + # for an existing email. A better way will be available with Libervia 0.9. + # TODO: use a better way to check existing invitations + + if email is None: + return None + all_invitations = await self.invitations.all() + for id_, invitation in all_invitations.items(): + if invitation.get("email") == email: + invitation[KEY_ID] = id_ + return invitation + + async def _createAccountAndProfile( + self, + id_: str, + kwargs: dict, + extra: dict + ) -> None: + """Create XMPP account and Libervia profile for guest""" + ## XMPP account creation + password = kwargs.pop('password', None) + if password is None: + password = utils.generatePassword() + assert password + # XXX: password is here saved in clear in database + # it is needed for invitation as the same password is used for profile + # and SàT need to be able to automatically open the profile with the uuid + # FIXME: we could add an extra encryption key which would be used with the + # uuid when the invitee is connecting (e.g. with URL). This key would + # not be saved and could be used to encrypt profile password. + extra[KEY_PASSWORD] = password + + jid_ = kwargs.pop('jid_', None) + if not jid_: + domain = self.host.memory.getConfig(None, 'xmpp_domain') + if not domain: + # TODO: fallback to profile's domain + raise ValueError(_("You need to specify xmpp_domain in sat.conf")) + jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), + domain=domain) + jid_ = jid.JID(jid_) + extra[KEY_JID] = jid_.full() + + if jid_.user: + # we don't register account if there is no user as anonymous login is then + # used + try: + await self.host.plugins['XEP-0077'].registerNewAccount(jid_, password) + except error.StanzaError as e: + prefix = jid_.user + idx = 0 + while e.condition == 'conflict': + if idx >= SUFFIX_MAX: + raise exceptions.ConflictError(_("Can't create XMPP account")) + jid_.user = prefix + '_' + str(idx) + log.info(_("requested jid already exists, trying with {}".format( + jid_.full()))) + try: + await self.host.plugins['XEP-0077'].registerNewAccount( + jid_, + password + ) + except error.StanzaError: + idx += 1 + else: + break + if e.condition != 'conflict': + raise e + + log.info(_("account {jid_} created").format(jid_=jid_.full())) + + ## profile creation + + extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format( + uuid=id_ + ) + # profile creation should not fail as we generate unique name ourselves + await self.host.memory.createProfile(guest_profile, password) + await self.host.memory.startSession(password, guest_profile) + await self.host.memory.setParam("JabberID", jid_.full(), "Connection", + profile_key=guest_profile) + await self.host.memory.setParam("Password", password, "Connection", + profile_key=guest_profile) + async def create(self, **kwargs): r"""Create an invitation @@ -201,6 +293,13 @@ class InvitationsPlugin(object): self.checkExtra(extra) email = kwargs.pop('email', None) + + existing = await self.getExistingInvitation(email) + if existing is not None: + log.info(f"There is already an invitation for {email!r}") + extra.update(existing) + del extra[KEY_ID] + emails_extra = kwargs.pop('emails_extra', []) if not email and emails_extra: raise ValueError( @@ -214,67 +313,18 @@ class InvitationsPlugin(object): ## uuid log.info(_("creating an invitation")) - id_ = str(shortuuid.uuid()) + id_ = existing[KEY_ID] if existing else str(shortuuid.uuid()) - ## XMPP account creation - password = kwargs.pop('password', None) - if password is None: - password = utils.generatePassword() - assert password - # XXX: password is here saved in clear in database - # it is needed for invitation as the same password is used for profile - # and SàT need to be able to automatically open the profile with the uuid - # FIXME: we could add an extra encryption key which would be used with the uuid - # when the invitee is connecting (e.g. with URL). This key would not be - # saved and could be used to encrypt profile password. - extra[KEY_PASSWORD] = password + if existing is None: + await self._createAccountAndProfile(id_, kwargs, extra) - jid_ = kwargs.pop('jid_', None) - if not jid_: - domain = self.host.memory.getConfig(None, 'xmpp_domain') - if not domain: - # TODO: fallback to profile's domain - raise ValueError(_("You need to specify xmpp_domain in sat.conf")) - jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), - domain=domain) - jid_ = jid.JID(jid_) - if jid_.user: - # we don't register account if there is no user as anonymous login is then - # used - try: - await self.host.plugins['XEP-0077'].registerNewAccount(jid_, password) - except error.StanzaError as e: - prefix = jid_.user - idx = 0 - while e.condition == 'conflict': - if idx >= SUFFIX_MAX: - raise exceptions.ConflictError(_("Can't create XMPP account")) - jid_.user = prefix + '_' + str(idx) - log.info(_("requested jid already exists, trying with {}".format( - jid_.full()))) - try: - await self.host.plugins['XEP-0077'].registerNewAccount(jid_, - password) - except error.StanzaError: - idx += 1 - else: - break - if e.condition != 'conflict': - raise e + profile = kwargs.pop('profile', None) + guest_profile = extra[KEY_GUEST_PROFILE] + jid_ = jid.JID(extra[KEY_JID]) - log.info(_("account {jid_} created").format(jid_=jid_.full())) - - ## profile creation - - extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format(uuid=id_) - # profile creation should not fail as we generate unique name ourselves - await self.host.memory.createProfile(guest_profile, password) - await self.host.memory.startSession(password, guest_profile) - await self.host.memory.setParam("JabberID", jid_.full(), "Connection", - profile_key=guest_profile) - await self.host.memory.setParam("Password", password, "Connection", - profile_key=guest_profile) + ## identity name = kwargs.pop('name', None) + password = extra[KEY_PASSWORD] if name is not None: extra['name'] = name try: @@ -306,7 +356,6 @@ class InvitationsPlugin(object): else: format_args['name'] = name - profile = kwargs.pop('profile', None) if profile is None: format_args['profile'] = '' else: @@ -344,8 +393,6 @@ class InvitationsPlugin(object): if kwargs: log.warning(_("Not all arguments have been consumed: {}").format(kwargs)) - extra[KEY_JID] = jid_.full() - ## extra data saving self.invitations[id_] = extra From 38ec47a4da926b9012de14fb06370b8352406a03 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 5 Nov 2021 18:11:24 +0100 Subject: [PATCH 17/24] plugin comp file sharing: don't use Deferred in return value --- sat/plugins/plugin_comp_file_sharing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sat/plugins/plugin_comp_file_sharing.py b/sat/plugins/plugin_comp_file_sharing.py index 27c40de9..0950a1e7 100644 --- a/sat/plugins/plugin_comp_file_sharing.py +++ b/sat/plugins/plugin_comp_file_sharing.py @@ -544,7 +544,7 @@ class FileSharing: self._f.openFileWrite( client, file_tmp_path, transfer_data, file_data, stream_object ) - return False, defer.succeed(True) + return False, True @defer.inlineCallbacks def _retrieveFiles( From b7c1579919a2728b5990c8852a3e6ba249fa1545 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 5 Nov 2021 18:11:24 +0100 Subject: [PATCH 18/24] tests (e2e/run_e2e): fix inappropriate use of `-T` option --- tests/e2e/run_e2e.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/run_e2e.py b/tests/e2e/run_e2e.py index 93eb158c..f5ed7a05 100755 --- a/tests/e2e/run_e2e.py +++ b/tests/e2e/run_e2e.py @@ -226,7 +226,7 @@ def use_e2e_env(): docker_compose.up("-d") p = docker_compose.exec( - "-T", "--workdir", "/src/libervia-backend/tests", "backend", + "--workdir", "/src/libervia-backend/tests", "backend", "pytest", "-o", "cache_dir=/tmp", *sys.argv[1:], color="yes", _in=sys.stdin, _out=live_out, _out_bufsize=0, _err=live_err, _err_bufsize=0, _bg=True From b374f17eb6384a91e1e76f683634629b67acf46c Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 5 Nov 2021 18:11:24 +0100 Subject: [PATCH 19/24] tests (e2e/web): add basic tests for generic lists --- tests/e2e/libervia-web/test_libervia-web.py | 53 ++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/e2e/libervia-web/test_libervia-web.py b/tests/e2e/libervia-web/test_libervia-web.py index 205dbffe..af07e7bb 100644 --- a/tests/e2e/libervia-web/test_libervia-web.py +++ b/tests/e2e/libervia-web/test_libervia-web.py @@ -21,7 +21,7 @@ import re import pytest from helium import ( go_to, write, press, click, drag_file, find_all, wait_until, S, Text, Link, Button, - get_driver, ENTER + select, scroll_down, get_driver, ENTER ) @@ -178,3 +178,54 @@ class TestPhotos: click("test album") wait_until(lambda: not S("#loading_screen").exists()) assert len(find_all(S("img.is-photo-thumbnail"))) == 2 + + +class TestLists: + TEST_GENERIC_LIST_URL = ( + "https://libervia-web.test:8443/lists/view/account1@server1.test/" + "fdp%2Fsubmitted%2Forg.salut-a-toi.tickets%3A0_test-generic-list" + ) + + @pytest.mark.dependency(name="create_generic_list") + def test_user_can_create_generic(self, log_in_account1): + go_to("https://libervia-web.test:8443/lists/view") + click("create a list") + tickets_btn = S("//span[text()='Tickets']") + click(tickets_btn) + write("test-generic-list", "name of the list") + click("create list") + wait_until(Text("Success").exists) + + + @pytest.mark.dependency( + name="create_generic_list_item", depends=["create_generic_list"] + ) + def test_user_can_create_generic_list_item(self): + go_to(self.TEST_GENERIC_LIST_URL) + click("create") + write("test item", "title") + write("label 1, label 2", "labels") + select("type", "feature request") + write("this is a test body", "body") + scroll_down(600) + click("create list item") + wait_until(Text("Success").exists) + assert Link("create").exists() + assert Link("manage invitations").exists() + item = Link("test item") + assert item.exists() + labels_elt = item.web_element.find_element_by_class_name("xmlui_field__labels") + labels = [t.text for t in labels_elt.find_elements_by_tag_name("span")] + assert labels == ["label 1", "label 2"] + + @pytest.mark.dependency(depends=["create_generic_list_item"]) + def test_list_item_can_be_displayed(self): + go_to(self.TEST_GENERIC_LIST_URL) + item = Link("test item") + # FIXME: we can't click on the item created above with Selenium, looks like a + # Selenium bug, to be checked + go_to(item.href) + item_title = Text("test item") + assert item_title.exists() + item_body = Text("this is a test body") + assert item_body.exists() From b1529c1ebf81a1b5f60f18d3f81cbc59c46aa88e Mon Sep 17 00:00:00 2001 From: Goffi Date: Sat, 6 Nov 2021 17:08:50 +0100 Subject: [PATCH 20/24] cli (base): better background detection: - ANSI escape code detection is only used on terminals known to be compatible (currently xterm, konsole, and all VTE based ones) - `COLORFGBG` environment variable is used as fallback - if both methods fail, `dark` is used This should avoid problems with incompatible terminals (e.g. on QTerminal, users had to press `[ENTER]` each time). fix 406 --- sat_frontends/jp/base.py | 80 ++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/sat_frontends/jp/base.py b/sat_frontends/jp/base.py index c6ef0539..e75c8097 100644 --- a/sat_frontends/jp/base.py +++ b/sat_frontends/jp/base.py @@ -119,33 +119,65 @@ class LiberviaCli: return config.getConfig(self.sat_conf, section, name, default=default) def guess_background(self): - if not sys.stdin.isatty() or not sys.stdout.isatty(): - return 'dark' - stdin_fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(stdin_fd) + # cf. https://unix.stackexchange.com/a/245568 (thanks!) try: - tty.setraw(sys.stdin.fileno()) - # we request background color - sys.stdout.write("\033]11;?\a") - sys.stdout.flush() - expected = "\033]11;rgb:" - for c in expected: - ch = sys.stdin.read(1) - if ch != c: - # background id is not supported, we default to "dark" - # TODO: log something? - return 'dark' - red, green, blue = [int(c, 16)/65535 for c in sys.stdin.read(14).split('/')] - # '\a' is the last character - sys.stdin.read(1) - finally: - termios.tcsetattr(stdin_fd, termios.TCSADRAIN, old_settings) + # for VTE based terminals + vte_version = int(os.getenv("VTE_VERSION", 0)) + except ValueError: + vte_version = 0 - lum = utils.per_luminance(red, green, blue) - if lum <= 0.5: - return 'dark' + color_fg_bg = os.getenv("COLORFGBG") + + if ((sys.stdin.isatty() and sys.stdout.isatty() + and ( + # XTerm + os.getenv("TERM", "") in {"xterm"} + # Konsole + or os.getenv("KONSOLE_VERSION") + # All VTE based terminals + or vte_version >= 3502 + ))): + # ANSI escape sequence + stdin_fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(stdin_fd) + try: + tty.setraw(sys.stdin.fileno()) + # we request background color + sys.stdout.write("\033]11;?\a") + sys.stdout.flush() + expected = "\033]11;rgb:" + for c in expected: + ch = sys.stdin.read(1) + if ch != c: + # background id is not supported, we default to "dark" + # TODO: log something? + return 'dark' + red, green, blue = [ + int(c, 16)/65535 for c in sys.stdin.read(14).split('/') + ] + # '\a' is the last character + sys.stdin.read(1) + finally: + termios.tcsetattr(stdin_fd, termios.TCSADRAIN, old_settings) + + lum = utils.per_luminance(red, green, blue) + if lum <= 0.5: + return 'dark' + else: + return 'light' + elif color_fg_bg: + # no luck with ANSI escape sequence, we try COLORFGBG environment variable + try: + bg = int(color_fg_bg.split(";")[-1]) + except ValueError: + return "dark" + if bg in list(range(7)) + [8]: + return "dark" + else: + return "light" else: - return 'light' + # no autodetection method found + return "dark" def set_color_theme(self): background = self.get_config('background', default='auto') From 1419f29df6807708b5b6c714b18de651d69b4458 Mon Sep 17 00:00:00 2001 From: Goffi Date: Sat, 6 Nov 2021 17:09:28 +0100 Subject: [PATCH 21/24] cli (debug/theme): display currently used background --- sat_frontends/jp/base.py | 1 + sat_frontends/jp/cmd_debug.py | 1 + 2 files changed, 2 insertions(+) diff --git a/sat_frontends/jp/base.py b/sat_frontends/jp/base.py index e75c8097..96f2d8f8 100644 --- a/sat_frontends/jp/base.py +++ b/sat_frontends/jp/base.py @@ -189,6 +189,7 @@ class LiberviaCli: 'your settings in libervia.conf').format( background=repr(background) )) + self.background = background if background == 'light': C.A_HEADER = A.FG_MAGENTA C.A_SUBHEADER = A.BOLD + A.FG_RED diff --git a/sat_frontends/jp/cmd_debug.py b/sat_frontends/jp/cmd_debug.py index acd5353a..1dff36a4 100644 --- a/sat_frontends/jp/cmd_debug.py +++ b/sat_frontends/jp/cmd_debug.py @@ -199,6 +199,7 @@ class Theme(base.CommandBase): pass async def start(self): + print(f"background currently used: {A.BOLD}{self.host.background}{A.RESET}\n") for attr in dir(C): if not attr.startswith("A_"): continue From 2cc637cdfed25f6c0e69820bcd062ebf01ae8a79 Mon Sep 17 00:00:00 2001 From: Goffi Date: Sat, 6 Nov 2021 18:53:00 +0100 Subject: [PATCH 22/24] cli (base): use `XTERM_VERSION` to detect XTerm: Using `TERM` is not good as it is used by other terminal emulators which are incompatible with ANSI sequences (seen with `cool-retro-term`). --- sat_frontends/jp/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sat_frontends/jp/base.py b/sat_frontends/jp/base.py index 96f2d8f8..24bb08dc 100644 --- a/sat_frontends/jp/base.py +++ b/sat_frontends/jp/base.py @@ -131,7 +131,7 @@ class LiberviaCli: if ((sys.stdin.isatty() and sys.stdout.isatty() and ( # XTerm - os.getenv("TERM", "") in {"xterm"} + os.getenv("XTERM_VERSION") # Konsole or os.getenv("KONSOLE_VERSION") # All VTE based terminals From 099128918dcd7cf182da25f973e2e38cc8e90db1 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 12 Nov 2021 17:16:40 +0100 Subject: [PATCH 23/24] changelog: minor update following renaming --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index bc6d9748..759412c3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -100,7 +100,7 @@ v 0.8.0 « La Cecília » (NOT RELEASED YET): - bulma theme becomes the default one for default site - "tickets" have been renamed to more generic term "lists" - (lists) common lists can be created from templates - - (photos) album can now be created or deleted from Libervia + - (photos) album can now be created or deleted from Libervia Web - (photos/album) photos or videos can now be uploaded from Libervia - (photos/album) a mobile friendly slideshow can be run - (photos/album) affiliations can be modified with new invitations manager From 0008d3478e80153abbb37ab2455c50c52ebc9bf4 Mon Sep 17 00:00:00 2001 From: Goffi Date: Fri, 12 Nov 2021 17:18:23 +0100 Subject: [PATCH 24/24] plugin events: store `interest_id` in data: ID of the item in list of interest is needed to correctly modify (and notably retract) an event. --- sat/plugins/plugin_exp_events.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sat/plugins/plugin_exp_events.py b/sat/plugins/plugin_exp_events.py index 51553080..a2b53c6e 100644 --- a/sat/plugins/plugin_exp_events.py +++ b/sat/plugins/plugin_exp_events.py @@ -428,6 +428,7 @@ class Events(object): ) else: timestamp, data = self._parseEventElt(event_elt) + data["interest_id"] = item["id"] events.append((timestamp, data)) defer.returnValue(events)