From 35e40455bbc2251e8089803fc781b40bf3207444 Mon Sep 17 00:00:00 2001
From: bashrc
Date: Sun, 5 Jul 2026 10:39:28 +0100
Subject: [PATCH] Add qrcodes for enigma public key and ricochet ID
---
src/daemon_get.py | 7 ++++--
src/daemon_get_images.py | 12 ++++++++++
src/daemon_post_profile.py | 38 +++++++++++++++++++----------
src/enigma.py | 46 +++++++++++++++++++++++++++++++++++-
src/lxmf.py | 2 +-
src/ricochet.py | 46 +++++++++++++++++++++++++++++++++++-
src/webapp_person_options.py | 18 +++++++++++---
src/webapp_profile.py | 20 +++++++++++++---
8 files changed, 165 insertions(+), 24 deletions(-)
diff --git a/src/daemon_get.py b/src/daemon_get.py
index 7fb536376..b84a5c308 100644
--- a/src/daemon_get.py
+++ b/src/daemon_get.py
@@ -2715,7 +2715,8 @@ def daemon_http_get(self) -> None:
# image on login screen or qrcode
if is_image_file(self.path) and \
(string_starts_with(self.path,
- ('/login.', '/qrcode.png', '/qrcode_lxmf.png'))):
+ ('/login.', '/qrcode.png', '/qrcode_lxmf.png',
+ '/qrcode_ricochet.png', '/qrcode_enigma.png'))):
icon_filename = data_dir(self.server.base_dir) + self.path
if is_a_file(icon_filename):
if etag_exists(self, icon_filename):
@@ -2754,7 +2755,9 @@ def daemon_http_get(self) -> None:
# QR code for account handle
if (users_in_path and
(self.path.endswith('/qrcode.png') or
- self.path.endswith('/qrcode_lxmf.png'))):
+ self.path.endswith('/qrcode_lxmf.png') or
+ self.path.endswith('/qrcode_ricochet.png') or
+ self.path.endswith('/qrcode_enigma.png'))):
if show_qrcode(self, calling_domain, self.path,
self.server.base_dir,
self.server.domain,
diff --git a/src/daemon_get_images.py b/src/daemon_get_images.py
index b2745fbca..75aad044b 100644
--- a/src/daemon_get_images.py
+++ b/src/daemon_get_images.py
@@ -29,6 +29,8 @@ from src.daemon_utils import etag_exists
from src.fitnessFunctions import fitness_performance
from src.person import save_person_qrcode
from src.lxmf import save_lxmf_qrcode
+from src.ricochet import save_ricochet_qrcode
+from src.enigma import save_enigma_qrcode
from src.data import load_string
from src.data import load_binary
from src.data import is_a_file
@@ -476,6 +478,16 @@ def show_qrcode(self, calling_domain: str, path: str,
acct_dir(base_dir, nickname, domain) + '/qrcode_lxmf.png'
qrcode_scale = 6
save_lxmf_qrcode(base_dir, nickname, domain, qrcode_scale)
+ elif path.endswith('_ricochet.png'):
+ qr_filename = \
+ acct_dir(base_dir, nickname, domain) + '/qrcode_ricochet.png'
+ qrcode_scale = 6
+ save_ricochet_qrcode(base_dir, nickname, domain, qrcode_scale)
+ elif path.endswith('_enigma.png'):
+ qr_filename = \
+ acct_dir(base_dir, nickname, domain) + '/qrcode_enigma.png'
+ qrcode_scale = 6
+ save_enigma_qrcode(base_dir, nickname, domain, qrcode_scale)
else:
if onion_domain:
qrcode_domain = onion_domain
diff --git a/src/daemon_post_profile.py b/src/daemon_post_profile.py
index 87a09e13e..037009259 100644
--- a/src/daemon_post_profile.py
+++ b/src/daemon_post_profile.py
@@ -1709,19 +1709,24 @@ def _profile_post_pgp_pubkey(actor_json: {}, fields: {},
return actor_changed
-def _profile_post_enigma_pubkey(actor_json: {}, fields: {},
+def _profile_post_enigma_pubkey(base_dir: str, nickname: str, domain: str,
+ actor_json: {}, fields: {},
actor_changed: bool) -> bool:
""" HTTP POST change Enigma public key
"""
- currentenigma_pub_key = get_enigma_pub_key(actor_json)
+ qrcode_scale = 6
+ current_enigma_pub_key = get_enigma_pub_key(actor_json)
if fields.get('enigmapubkey'):
- if fields['enigmapubkey'] != currentenigma_pub_key:
- set_enigma_pub_key(actor_json,
- fields['enigmapubkey'])
+ if fields['enigmapubkey'] != current_enigma_pub_key:
+ set_enigma_pub_key(base_dir, nickname, domain,
+ actor_json,
+ fields['enigmapubkey'],
+ qrcode_scale)
actor_changed = True
else:
- if currentenigma_pub_key:
- set_enigma_pub_key(actor_json, '')
+ if current_enigma_pub_key:
+ set_enigma_pub_key(base_dir, nickname, domain, actor_json,
+ '', qrcode_scale)
actor_changed = True
return actor_changed
@@ -1782,19 +1787,24 @@ def _profile_post_briar_address(fields: {}, actor_json: {},
return actor_changed
-def _profile_post_ricochet_address(fields: {}, actor_json: {},
+def _profile_post_ricochet_address(base_dir: str, nickname: str, domain: str,
+ fields: {}, actor_json: {},
actor_changed: bool) -> bool:
""" HTTP POST change ricochet address
"""
+ qrcode_scale = 6
current_ricochet_address = get_ricochet_address(actor_json)
if fields.get('ricochetAddress'):
if fields['ricochetAddress'] != current_ricochet_address:
- set_ricochet_address(actor_json,
- fields['ricochetAddress'])
+ set_ricochet_address(base_dir, nickname, domain,
+ actor_json,
+ fields['ricochetAddress'],
+ qrcode_scale)
actor_changed = True
else:
if current_ricochet_address:
- set_ricochet_address(actor_json, '')
+ set_ricochet_address(base_dir, nickname, domain,
+ actor_json, '', qrcode_scale)
actor_changed = True
return actor_changed
@@ -3095,7 +3105,8 @@ def profile_edit(self, calling_domain: str, cookie: str,
actor_changed)
actor_changed = \
- _profile_post_ricochet_address(fields, actor_json,
+ _profile_post_ricochet_address(base_dir, nickname, domain,
+ fields, actor_json,
actor_changed)
actor_changed = \
@@ -3107,7 +3118,8 @@ def profile_edit(self, calling_domain: str, cookie: str,
_profile_post_ntfy_topic(base_dir, nickname, domain, fields)
actor_changed = \
- _profile_post_enigma_pubkey(actor_json, fields,
+ _profile_post_enigma_pubkey(base_dir, nickname, domain,
+ actor_json, fields,
actor_changed)
actor_changed = \
diff --git a/src/enigma.py b/src/enigma.py
index 23895c4a8..010b6e600 100644
--- a/src/enigma.py
+++ b/src/enigma.py
@@ -8,8 +8,13 @@ __status__ = "Production"
__module_group__ = "Profile Metadata"
+import pyqrcode
+from src.utils import acct_dir
+from src.utils import load_json
from src.utils import get_attachment_property_value
from src.utils import remove_html
+from src.data import is_a_file
+from src.data import erase_file
def get_enigma_pub_key(actor_json: {}) -> str:
@@ -48,9 +53,47 @@ def get_enigma_pub_key(actor_json: {}) -> str:
return ''
-def set_enigma_pub_key(actor_json: {}, enigma_pub_key: str) -> None:
+def save_enigma_qrcode(base_dir: str,
+ nickname: str, domain: str,
+ scale: int = 6) -> bool:
+ """Saves a qrcode image for the enigma public key
+ This helps to transfer onion or i2p handles to a mobile device
+ """
+ qrcode_filename = \
+ acct_dir(base_dir, nickname, domain) + '/qrcode_enigma.png'
+ if is_a_file(qrcode_filename):
+ return False
+ actor_filename = \
+ acct_dir(base_dir, nickname, domain) + '.json'
+ if not is_a_file(actor_filename):
+ return False
+ actor_json = load_json(actor_filename)
+ if not actor_json:
+ return False
+ enigma_address = get_enigma_pub_key(actor_json)
+ if not enigma_address:
+ return False
+ url = pyqrcode.create(enigma_address)
+ try:
+ url.png(qrcode_filename, scale)
+ return True
+ except ModuleNotFoundError:
+ print('EX: save_enigma_qrcode pyqrcode png module not found')
+ return False
+
+
+def set_enigma_pub_key(base_dir: str, nickname: str, domain: str,
+ actor_json: {}, enigma_pub_key: str,
+ qrcode_scale: int) -> None:
"""Sets a Enigma public key for the given actor
"""
+ if not enigma_pub_key:
+ qrcode_filename = \
+ acct_dir(base_dir, nickname, domain) + '/qrcode_enigma.png'
+ if is_a_file(qrcode_filename):
+ erase_file(qrcode_filename,
+ 'EX: cannot remove enigma qrcode ' + qrcode_filename)
+
remove_key: bool = False
if not enigma_pub_key:
remove_key = True
@@ -118,3 +161,4 @@ def set_enigma_pub_key(actor_json: {}, enigma_pub_key: str) -> None:
"value": enigma_pub_key
}
actor_json['attachment'].append(new_enigma_pub_key)
+ save_enigma_qrcode(base_dir, nickname, domain, qrcode_scale)
diff --git a/src/lxmf.py b/src/lxmf.py
index 2e93f5435..54cdb3092 100644
--- a/src/lxmf.py
+++ b/src/lxmf.py
@@ -34,7 +34,7 @@ def _is_valid_lxmf_address(lxmf_address: str) -> bool:
def save_lxmf_qrcode(base_dir: str,
nickname: str, domain: str,
scale: int = 6) -> bool:
- """Saves a qrcode image for the handle of the person
+ """Saves a qrcode image for the lxmf address of the person
This helps to transfer onion or i2p handles to a mobile device
"""
qrcode_filename = acct_dir(base_dir, nickname, domain) + '/qrcode_lxmf.png'
diff --git a/src/ricochet.py b/src/ricochet.py
index 93a274e3a..97136c711 100644
--- a/src/ricochet.py
+++ b/src/ricochet.py
@@ -8,8 +8,13 @@ __status__ = "Production"
__module_group__ = "Profile Metadata"
+import pyqrcode
+from src.utils import load_json
+from src.utils import acct_dir
from src.utils import get_attachment_property_value
from src.utils import remove_html
+from src.data import is_a_file
+from src.data import erase_file
def get_ricochet_address(actor_json: {}) -> str:
@@ -64,9 +69,47 @@ def get_ricochet_address(actor_json: {}) -> str:
return ''
-def set_ricochet_address(actor_json: {}, ricochet_address: str) -> None:
+def save_ricochet_qrcode(base_dir: str,
+ nickname: str, domain: str,
+ scale: int = 6) -> bool:
+ """Saves a qrcode image for the ricochet address of the person
+ This helps to transfer onion or i2p handles to a mobile device
+ """
+ qrcode_filename = \
+ acct_dir(base_dir, nickname, domain) + '/qrcode_ricochet.png'
+ if is_a_file(qrcode_filename):
+ return False
+ actor_filename = \
+ acct_dir(base_dir, nickname, domain) + '.json'
+ if not is_a_file(actor_filename):
+ return False
+ actor_json = load_json(actor_filename)
+ if not actor_json:
+ return False
+ ricochet_address = get_ricochet_address(actor_json)
+ if not ricochet_address:
+ return False
+ url = pyqrcode.create(ricochet_address)
+ try:
+ url.png(qrcode_filename, scale)
+ return True
+ except ModuleNotFoundError:
+ print('EX: save_ricochet_qrcode pyqrcode png module not found')
+ return False
+
+
+def set_ricochet_address(base_dir: str, nickname: str, domain: str,
+ actor_json: {}, ricochet_address: str,
+ qrcode_scale: int) -> None:
"""Sets an ricochet address for the given actor
"""
+ if not ricochet_address:
+ qrcode_filename = \
+ acct_dir(base_dir, nickname, domain) + '/qrcode_ricochet.png'
+ if is_a_file(qrcode_filename):
+ erase_file(qrcode_filename,
+ 'EX: cannot remove ricochet qrcode ' + qrcode_filename)
+
not_ricochet_address: bool = False
if len(ricochet_address) < 60:
@@ -149,3 +192,4 @@ def set_ricochet_address(actor_json: {}, ricochet_address: str) -> None:
"value": ricochet_address
}
actor_json['attachment'].append(new_ricochet_address)
+ save_ricochet_qrcode(base_dir, nickname, domain, qrcode_scale)
diff --git a/src/webapp_person_options.py b/src/webapp_person_options.py
index e990b28de..7dfcb8eb6 100644
--- a/src/webapp_person_options.py
+++ b/src/webapp_person_options.py
@@ -524,11 +524,17 @@ def html_person_options(default_timeline: str,
if ricochet_address.startswith('ricochet:'):
options_str += \
' ' + \
- remove_html(ricochet_address) + '
\n'
+ remove_html(ricochet_address)
else:
options_str += \
' ricochet:' + \
- remove_html(ricochet_address) + '
\n'
+ remove_html(ricochet_address)
+ options_str += \
+ ' ' + \
+ '![' + translate['QR Code'] + \
+ '](/icons/qrcode.png)
\n'
if cwtch_address:
options_str += \
' Cwtch: ' + \
@@ -536,7 +542,13 @@ def html_person_options(default_timeline: str,
if enigma_pub_key:
options_str += \
'
Enigma: ' + \
- remove_html(enigma_pub_key) + '
\n'
+ remove_html(enigma_pub_key)
+ options_str += \
+ ' ' + \
+ '
\n'
if pgp_fingerprint:
options_str += '' + \
translate['PGP Fingerprint'] + ': ' + \
diff --git a/src/webapp_profile.py b/src/webapp_profile.py
index 89bff1a70..7ec1210ec 100644
--- a/src/webapp_profile.py
+++ b/src/webapp_profile.py
@@ -1511,11 +1511,18 @@ def html_profile(signing_priv_key_pem: str,
if ricochet_address.startswith('ricochet:'):
donate_section += \
'
\n'
+ ricochet_address + ''
else:
donate_section += \
'ricochet:
\n'
+ ricochet_address + ''
+ donate_section += \
+ ' ' + \
+ '
\n'
if cwtch_address:
donate_section += \
'Cwtch: '
+ donate_section += \
+ ' ' + \
+ '![' + translate['QR Code'] + \
+ '](/icons/qrcode.png)
\n'
if pgp_fingerprint:
donate_section += \
'' + translate['PGP Fingerprint'] + ': ' + \