Python : Set OMEMO as an optional dependency;

SVG    : Add a selection of variations of the Slixfeed logo.
This commit is contained in:
Schimon Jehudah, Adv. 2024-09-11 09:48:27 +03:00
parent 3913f740ef
commit 178f49cb86
13 changed files with 408 additions and 135 deletions

View file

@ -57,9 +57,20 @@ It is possible to install Slixfeed using pip and pipx.
``` ```
$ python3 -m venv .venv $ python3 -m venv .venv
$ source .venv/bin/activate $ source .venv/bin/activate
```
##### Install
```
$ pip install git+https://git.xmpp-it.net/sch/Slixfeed $ pip install git+https://git.xmpp-it.net/sch/Slixfeed
``` ```
##### Install (OMEMO)
```
$ pip install git+https://git.xmpp-it.net/sch/Slixfeed[omemo]
```
#### pipx #### pipx
##### Install ##### Install

View file

@ -37,33 +37,36 @@ keywords = [
"xml", "xml",
"xmpp", "xmpp",
] ]
# urls = {Homepage = "https://gitgud.io/sjehuda/slixfeed"} # urls = {Homepage = "https://gitgud.io/sjehuda/slixfeed"}
dependencies = [ dependencies = [
"aiofiles", "aiofiles",
"aiohttp", "aiohttp",
# "daemonize", # "daemonize",
"feedparser", "feedparser",
"lxml", "lxml",
"omemo", # OMEMO
# "pysocks",
"protobuf==3.20.3", # OMEMO
"python-dateutil", "python-dateutil",
"requests", "requests",
"slixmpp", "slixmpp",
"slixmpp-omemo", # OMEMO
"tomli", # Python 3.10 "tomli", # Python 3.10
"tomli_w", "tomli_w",
"X3DH", # OMEMO
"XEdDSA", # OMEMO
] ]
[project.urls] [project.urls]
Homepage = "http://slixfeed.i2p/" Homepage = "https://slixfeed.woodpeckersnest.space"
Repository = "https://gitgud.io/sjehuda/slixfeed" Repository = "https://git.xmpp-it.net/sch/Slixfeed"
Issues = "https://gitgud.io/sjehuda/slixfeed/issues" Issues = "https://gitgud.io/sjehuda/slixfeed/issues"
[project.optional-dependencies] [project.optional-dependencies]
omemo = [
"DoubleRatchet>=0.7.0,<0.8",
"OMEMO>=0.13.0,<0.15",
"protobuf==3.20.3",
"slixmpp-omemo",
"X3DH>=0.5.9,<0.6",
"XEdDSA<0.5,>=0.4.7",
]
proxy = ["pysocks"] proxy = ["pysocks"]
# [project.readme] # [project.readme]

View file

@ -28,9 +28,9 @@ Good luck!
filetypes = "Atom, JSON, RDF, RSS, XML." filetypes = "Atom, JSON, RDF, RSS, XML."
platforms = "XMPP" platforms = "XMPP"
# platforms = "ActivityPub, Briar, DeltaChat, Email, IRC, LXMF, MQTT, Nostr, Session, Tox." # platforms = "ActivityPub, BitMessage, Briar, DeltaChat, Email, IRC, LXMF, MQTT, Nostr, Session, Tox."
comment = "For ideal experience, we recommend using XMPP." # Nostr, Session or DeltaChat comment = "For ideal experience, we recommend using XMPP." # Nostr, Session or DeltaChat
url = "https://gitgud.io/sjehuda/slixfeed" url = "https://git.xmpp-it.net/sch/Slixfeed"
[[about]] [[about]]
name = "slixmpp" name = "slixmpp"
@ -260,7 +260,7 @@ Slixfeed is distributed in the hope that it will be useful, but WITHOUT ANY \
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR \ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR \
A PARTICULAR PURPOSE. See the MIT License for more details. A PARTICULAR PURPOSE. See the MIT License for more details.
"""] """]
link = "https://gitgud.io/sjehuda/slixfeed" link = "https://git.xmpp-it.net/sch/Slixfeed"
[[license]] [[license]]
title = "License" title = "License"

View file

@ -1 +1,48 @@
<svg height="600" width="600" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0zm0 0" style="fill:#ffa000" transform="translate(44 44)"/><path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279zm0 0" style="fill:#ffa000" transform="translate(44 44)"/><path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47Zm0 0" style="fill:#ffa000" transform="translate(44 44)"/></svg> <svg height="600" width="600" xmlns="http://www.w3.org/2000/svg" xml:space="preserve">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" />
<feOffset dx="5" dy="5" result="offsetblur" />
<feFlood flood-color="rgba(0,0,0,0.5)" />
<feComposite in2="offsetblur" operator="in" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<!-- Glass Gradient -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgba(255, 255, 255, 0.3); stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(255, 255, 255, 0.1); stop-opacity:1" />
</linearGradient>
</defs>
<!-- Black shapes with orange margins -->
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:#ffa000; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:#ffa000; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:#ffa000; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<!-- Glass Shadow Effect Layer -->
<g filter="url(#shadow)">
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,48 @@
<svg height="600" width="600" xmlns="http://www.w3.org/2000/svg" xml:space="preserve">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" />
<feOffset dx="5" dy="5" result="offsetblur" />
<feFlood flood-color="rgba(0,0,0,0.5)" />
<feComposite in2="offsetblur" operator="in" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<!-- Glass Gradient -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgba(255, 255, 255, 0.3); stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(255, 255, 255, 0.1); stop-opacity:1" />
</linearGradient>
</defs>
<!-- Black shapes with orange margins -->
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:#000000; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:#000000; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:#000000; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<!-- Glass Shadow Effect Layer -->
<g filter="url(#shadow)">
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,51 @@
<svg height="600" width="600" xmlns="http://www.w3.org/2000/svg" xml:space="preserve">
<defs>
<!-- Gradient for Glass Effect -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgba(255, 255, 255, 0.5); stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(0, 0, 0, 0.2); stop-opacity:1" />
</linearGradient>
<linearGradient id="glassHighlight" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgba(255, 255, 255, 0.8); stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(255, 255, 255, 0); stop-opacity:1" />
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" />
<feOffset dx="5" dy="5" result="offsetblur" />
<feFlood flood-color="rgba(0, 0, 0, 0.3)" />
<feComposite in2="offsetblur" operator="in" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<!-- Shapes with Glass Effect -->
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:url(#glassGradient); stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:url(#glassGradient); stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:url(#glassGradient); stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<!-- Highlights for Shiny Effect -->
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:url(#glassHighlight); opacity:0.6;"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:url(#glassHighlight); opacity:0.6;"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:url(#glassHighlight); opacity:0.6;"
transform="translate(44 44)" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,48 @@
<svg height="600" width="600" xmlns="http://www.w3.org/2000/svg" xml:space="preserve">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" />
<feOffset dx="5" dy="5" result="offsetblur" />
<feFlood flood-color="rgba(0,0,0,0.5)" />
<feComposite in2="offsetblur" operator="in" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<!-- Glass Gradient -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgba(255, 255, 255, 0.3); stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(255, 255, 255, 0.1); stop-opacity:1" />
</linearGradient>
</defs>
<!-- Black shapes with orange margins -->
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:url(#glassGradient); stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:url(#glassGradient); stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:url(#glassGradient); stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<!-- Glass Shadow Effect Layer -->
<g filter="url(#shadow)">
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,48 @@
<svg height="600" width="600" xmlns="http://www.w3.org/2000/svg" xml:space="preserve">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" />
<feOffset dx="5" dy="5" result="offsetblur" />
<feFlood flood-color="rgba(0,0,0,0.5)" />
<feComposite in2="offsetblur" operator="in" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<!-- Glass Gradient -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgba(255, 255, 255, 0.3); stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(255, 255, 255, 0.1); stop-opacity:1" />
</linearGradient>
</defs>
<!-- Black shapes with orange margins -->
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:#ffffff; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:#ffffff; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:#ffffff; stroke:#e15a00; stroke-width:10; filter:url(#shadow);"
transform="translate(44 44)" />
<!-- Glass Shadow Effect Layer -->
<g filter="url(#shadow)">
<path d="M167 406a60 60 0 1 1-120 0 60 60 0 0 1 120 0z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 186v80c110 0 199 89 199 199h80c0-154-125-279-279-279z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
<path d="M47 47v79c187 0 338 152 338 339h80C465 234 277 47 47 47z"
style="fill:url(#glassGradient); stroke:none;"
transform="translate(44 44)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -3032,7 +3032,7 @@ def check_entry_exist(db_file, feed_id, identifier=None, title=None, link=None,
""" """
SELECT id SELECT id
FROM entries_properties FROM entries_properties
WHERE identifier = :identifier and feed_id = :feed_id WHERE identifier = :identifier AND feed_id = :feed_id
""" """
) )
par = { par = {

View file

@ -1,2 +1,2 @@
__version__ = '0.1.94' __version__ = '0.1.95'
__version_info__ = (0, 1, 94) __version_info__ = (0, 1, 95)

View file

@ -36,7 +36,6 @@ import slixfeed.sqlite as sqlite
from slixfeed.syndication import FeedTask from slixfeed.syndication import FeedTask
from slixfeed.utilities import Documentation, Html, MD, Task, Url from slixfeed.utilities import Documentation, Html, MD, Task, Url
from slixfeed.xmpp.commands import XmppCommands from slixfeed.xmpp.commands import XmppCommands
from slixfeed.xmpp.encryption import XmppOmemo
from slixfeed.xmpp.message import XmppMessage from slixfeed.xmpp.message import XmppMessage
from slixfeed.xmpp.presence import XmppPresence from slixfeed.xmpp.presence import XmppPresence
from slixfeed.xmpp.status import XmppStatusTask from slixfeed.xmpp.status import XmppStatusTask
@ -48,6 +47,10 @@ import sys
import time import time
from typing import Optional from typing import Optional
try:
from slixfeed.xmpp.encryption import XmppOmemo
except Exception as e:
print('Encryption of type OMEMO is not enabled. Reason: ' + str(e))
logger = Logger(__name__) logger = Logger(__name__)
@ -150,7 +153,7 @@ class XmppChat:
# await compose.message(self, jid_bare, message) # await compose.message(self, jid_bare, message)
if self['xep_0384'].is_encrypted(message): if self.omemo_present and self['xep_0384'].is_encrypted(message):
allow_untrusted=True # Temporary fix. This should be handled by "retry"" allow_untrusted=True # Temporary fix. This should be handled by "retry""
command, omemo_decrypted, retry = await XmppOmemo.decrypt( command, omemo_decrypted, retry = await XmppOmemo.decrypt(
self, message, allow_untrusted) self, message, allow_untrusted)
@ -229,8 +232,8 @@ class XmppChat:
case _ if command_lowercase in ['greetings', 'hallo', 'hello', case _ if command_lowercase in ['greetings', 'hallo', 'hello',
'hey', 'hi', 'hola', 'holla', 'hey', 'hi', 'hola', 'holla',
'hollo']: 'hollo']:
response = ('Greeting! My name is {}.\n' response = ('Greeting. My name is {}.\n'
'I am an RSS News Bot.\n' 'I am an Atom/RSS News Bot.\n'
'Send "help" for further instructions.\n' 'Send "help" for further instructions.\n'
.format(self.alias)) .format(self.alias))
case _ if command_lowercase.startswith('add'): case _ if command_lowercase.startswith('add'):
@ -358,7 +361,7 @@ class XmppChat:
# XmppMessage.send_oob_reply_message(message, url, response) # XmppMessage.send_oob_reply_message(message, url, response)
if url: if url:
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if encrypted: if self.omemo_present and encrypted:
url_encrypted, omemo_encrypted = await XmppOmemo.encrypt( url_encrypted, omemo_encrypted = await XmppOmemo.encrypt(
self, message_from, url) self, message_from, url)
XmppMessage.send_omemo_oob(self, message_from, url_encrypted, chat_type) XmppMessage.send_omemo_oob(self, message_from, url_encrypted, chat_type)
@ -591,7 +594,7 @@ class XmppChat:
response = XmppCommands.search_items(db_file, query) response = XmppCommands.search_items(db_file, query)
case 'start': case 'start':
status_type = 'available' status_type = 'available'
status_message = '📫️ Welcome back!' status_message = '📫️ Welcome back.'
XmppPresence.send(self, jid_bare, status_message, XmppPresence.send(self, jid_bare, status_message,
status_type=status_type) status_type=status_type)
await asyncio.sleep(5) await asyncio.sleep(5)
@ -627,7 +630,7 @@ class XmppChat:
if response: if response:
encrypt_omemo = Config.get_setting_value(self, jid_bare, 'omemo') encrypt_omemo = Config.get_setting_value(self, jid_bare, 'omemo')
encrypted = True if encrypt_omemo else False encrypted = True if encrypt_omemo else False
if encrypted and self['xep_0384'].is_encrypted(message): if self.omemo_present and encrypted and self['xep_0384'].is_encrypted(message):
response_encrypted, omemo_encrypted = await XmppOmemo.encrypt( response_encrypted, omemo_encrypted = await XmppOmemo.encrypt(
self, message_from, response) self, message_from, response)
if omemo_decrypted and omemo_encrypted: if omemo_decrypted and omemo_encrypted:
@ -732,17 +735,17 @@ class XmppChatAction:
media_url = None media_url = None
if media_url and news_digest: if media_url and news_digest:
if encrypt_omemo: if self.omemo_present and encrypt_omemo:
news_digest_encrypted, omemo_encrypted = await XmppOmemo.encrypt( news_digest_encrypted, omemo_encrypted = await XmppOmemo.encrypt(
self, jid, news_digest) self, jid, news_digest)
if encrypt_omemo and omemo_encrypted: if self.omemo_present and encrypt_omemo and omemo_encrypted:
XmppMessage.send_omemo(self, jid, chat_type, news_digest_encrypted) XmppMessage.send_omemo(self, jid, chat_type, news_digest_encrypted)
else: else:
# Send textual message # Send textual message
XmppMessage.send(self, jid_bare, news_digest, chat_type) XmppMessage.send(self, jid_bare, news_digest, chat_type)
news_digest = '' news_digest = ''
# Send media # Send media
if encrypt_omemo: if self.omemo_present and encrypt_omemo:
cache_dir = config.get_default_cache_directory() cache_dir = config.get_default_cache_directory()
# if not media_url.startswith('data:'): # if not media_url.startswith('data:'):
filename = media_url.split('/').pop().split('?')[0] filename = media_url.split('/').pop().split('?')[0]
@ -796,9 +799,10 @@ class XmppChatAction:
media_url = None media_url = None
if news_digest: if news_digest:
if encrypt_omemo: news_digest_encrypted, omemo_encrypted = await XmppOmemo.encrypt( if self.omemo_present and encrypt_omemo:
news_digest_encrypted, omemo_encrypted = await XmppOmemo.encrypt(
self, jid, news_digest) self, jid, news_digest)
if encrypt_omemo and omemo_encrypted: if self.omemo_present and encrypt_omemo and omemo_encrypted:
XmppMessage.send_omemo(self, jid, chat_type, news_digest_encrypted) XmppMessage.send_omemo(self, jid, chat_type, news_digest_encrypted)
else: else:
XmppMessage.send(self, jid_bare, news_digest, chat_type) XmppMessage.send(self, jid_bare, news_digest, chat_type)

View file

@ -54,7 +54,6 @@ from slixfeed.version import __version__
from slixfeed.xmpp.bookmark import XmppBookmark from slixfeed.xmpp.bookmark import XmppBookmark
from slixfeed.xmpp.chat import XmppChat, XmppChatTask from slixfeed.xmpp.chat import XmppChat, XmppChatTask
from slixfeed.xmpp.connect import XmppConnect, XmppConnectTask from slixfeed.xmpp.connect import XmppConnect, XmppConnectTask
from slixfeed.xmpp.encryption import XmppOmemo
from slixfeed.xmpp.groupchat import XmppGroupchat from slixfeed.xmpp.groupchat import XmppGroupchat
from slixfeed.xmpp.ipc import XmppIpcServer from slixfeed.xmpp.ipc import XmppIpcServer
from slixfeed.xmpp.iq import XmppIQ from slixfeed.xmpp.iq import XmppIQ
@ -69,8 +68,6 @@ from slixfeed.xmpp.status import XmppStatusTask
from slixfeed.xmpp.upload import XmppUpload from slixfeed.xmpp.upload import XmppUpload
from slixfeed.xmpp.utilities import XmppUtilities from slixfeed.xmpp.utilities import XmppUtilities
from slixmpp import JID from slixmpp import JID
import slixmpp_omemo
from slixmpp_omemo import PluginCouldNotLoad
import sys import sys
import time import time
@ -154,6 +151,17 @@ class XmppClient(slixmpp.ClientXMPP):
self.register_plugin('xep_0363') # HTTP File Upload self.register_plugin('xep_0363') # HTTP File Upload
self.register_plugin('xep_0402') # PEP Native Bookmarks self.register_plugin('xep_0402') # PEP Native Bookmarks
self.register_plugin('xep_0444') # Message Reactions self.register_plugin('xep_0444') # Message Reactions
try:
from slixfeed.xmpp.encryption import XmppOmemo
import slixmpp_omemo
from slixmpp_omemo import PluginCouldNotLoad
self.omemo_present = True
except Exception as e:
print('Encryption of type OMEMO is not enabled. Reason: ' + str(e))
self.omemo_present = False
if self.omemo_present:
try: try:
self.register_plugin( self.register_plugin(
'xep_0384', 'xep_0384',
@ -868,7 +876,7 @@ class XmppClient(slixmpp.ClientXMPP):
.format(function_name, jid_full)) .format(function_name, jid_full))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
form = self['xep_0004'].make_form('form', 'PubSub') form = self['xep_0004'].make_form('form', 'PubSub')
form['instructions'] = 'Publish news items to PubSub nodes.' form['instructions'] = 'Publish news items to PubSub nodes.'
options = form.add_field(desc='From which medium source do you ' options = form.add_field(desc='From which medium source do you '
@ -889,16 +897,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['prev'] = None session['prev'] = None
session['payload'] = form session['payload'] = form
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -909,7 +917,7 @@ class XmppClient(slixmpp.ClientXMPP):
.format(function_name, jid_full)) .format(function_name, jid_full))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
values = payload['values'] values = payload['values']
form = self['xep_0004'].make_form('form', 'Publish') form = self['xep_0004'].make_form('form', 'Publish')
form['instructions'] = ('Choose a PubSub Jabber ID and verify ' form['instructions'] = ('Choose a PubSub Jabber ID and verify '
@ -997,16 +1005,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['has_next'] = True session['has_next'] = True
session['prev'] = self._handle_publish session['prev'] = self._handle_publish
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -1413,13 +1421,14 @@ class XmppClient(slixmpp.ClientXMPP):
return session return session
async def _handle_filters(self, iq, session): async def _handle_filters(self, iq, session):
jid = session['from']
jid_full = session['from'].full jid_full = session['from'].full
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_full: {}' logger.debug('{}: jid_full: {}'
.format(function_name, jid_full)) .format(function_name, jid_full))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
jid = session['from'].bare jid = session['from'].bare
db_file = config.get_pathname_to_database(jid_bare) db_file = config.get_pathname_to_database(jid_bare)
form = self['xep_0004'].make_form('form', 'Filters') form = self['xep_0004'].make_form('form', 'Filters')
@ -1458,16 +1467,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['next'] = self._handle_filters_complete session['next'] = self._handle_filters_complete
session['payload'] = form session['payload'] = form
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -1522,13 +1531,14 @@ class XmppClient(slixmpp.ClientXMPP):
async def _handle_subscription_add(self, iq, session): async def _handle_subscription_add(self, iq, session):
jid = session['from']
jid_full = session['from'].full jid_full = session['from'].full
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_full: {}' logger.debug('{}: jid_full: {}'
.format(function_name, jid_full)) .format(function_name, jid_full))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
form = self['xep_0004'].make_form('form', 'Subscribe') form = self['xep_0004'].make_form('form', 'Subscribe')
# form['instructions'] = 'Add a new custom subscription.' # form['instructions'] = 'Add a new custom subscription.'
form.add_field(desc='Enter a URL.', form.add_field(desc='Enter a URL.',
@ -1570,16 +1580,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['prev'] = None session['prev'] = None
session['payload'] = form session['payload'] = form
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -2036,13 +2046,14 @@ class XmppClient(slixmpp.ClientXMPP):
async def _handle_discover(self, iq, session): async def _handle_discover(self, iq, session):
jid = session['from']
jid_full = session['from'].full jid_full = session['from'].full
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_full: {}' logger.debug('{}: jid_full: {}'
.format(function_name, jid_full)) .format(function_name, jid_full))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
form = self['xep_0004'].make_form('form', 'Discover & Search') form = self['xep_0004'].make_form('form', 'Discover & Search')
form['instructions'] = 'Discover news subscriptions of all kinds' form['instructions'] = 'Discover news subscriptions of all kinds'
options = form.add_field(desc='Select type of search.', options = form.add_field(desc='Select type of search.',
@ -2059,16 +2070,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['payload'] = form session['payload'] = form
session['prev'] = None session['prev'] = None
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -2160,13 +2171,14 @@ class XmppClient(slixmpp.ClientXMPP):
async def _handle_subscriptions(self, iq, session): async def _handle_subscriptions(self, iq, session):
jid = session['from']
jid_full = session['from'].full jid_full = session['from'].full
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_full: {}' logger.debug('{}: jid_full: {}'
.format(function_name, jid_full)) .format(function_name, jid_full))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
form = self['xep_0004'].make_form('form', 'Subscriptions') form = self['xep_0004'].make_form('form', 'Subscriptions')
form['instructions'] = ('Browse, view, toggle or remove ' form['instructions'] = ('Browse, view, toggle or remove '
'tags and subscriptions.') 'tags and subscriptions.')
@ -2210,16 +2222,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['next'] = self._handle_subscriptions_result session['next'] = self._handle_subscriptions_result
session['has_next'] = True session['has_next'] = True
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -2520,13 +2532,13 @@ class XmppClient(slixmpp.ClientXMPP):
async def _handle_advanced(self, iq, session): async def _handle_advanced(self, iq, session):
jid_full = session['from'].full jid = session['from']
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_full: {}' logger.debug('{}: jid: {}'
.format(function_name, jid_full)) .format(function_name, jid))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
form = self['xep_0004'].make_form('form', 'Advanced') form = self['xep_0004'].make_form('form', 'Advanced')
form['instructions'] = 'Extended options' form['instructions'] = 'Extended options'
options = form.add_field(ftype='list-single', options = form.add_field(ftype='list-single',
@ -2547,16 +2559,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['next'] = self._handle_advanced_result session['next'] = self._handle_advanced_result
session['prev'] = self._handle_advanced session['prev'] = self._handle_advanced
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -2940,6 +2952,7 @@ class XmppClient(slixmpp.ClientXMPP):
# TODO Attempt to look up for feeds of hostname of JID (i.e. scan # TODO Attempt to look up for feeds of hostname of JID (i.e. scan
# jabber.de for feeds for juliet@jabber.de) # jabber.de for feeds for juliet@jabber.de)
async def _handle_promoted(self, iq, session): async def _handle_promoted(self, iq, session):
jid = session['from']
jid_full = session['from'].full jid_full = session['from'].full
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_full: {}' logger.debug('{}: jid_full: {}'
@ -2947,7 +2960,7 @@ class XmppClient(slixmpp.ClientXMPP):
jid_bare = session['from'].bare jid_bare = session['from'].bare
jid_full = session['from'].full jid_full = session['from'].full
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
form = self['xep_0004'].make_form('form', 'Subscribe') form = self['xep_0004'].make_form('form', 'Subscribe')
# NOTE Refresh button would be of use # NOTE Refresh button would be of use
form['instructions'] = 'Featured subscriptions' form['instructions'] = 'Featured subscriptions'
@ -2998,16 +3011,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['payload'] = form session['payload'] = form
session['prev'] = self._handle_promoted session['prev'] = self._handle_promoted
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session
@ -3646,13 +3659,14 @@ class XmppClient(slixmpp.ClientXMPP):
session. Additional, custom data may be saved session. Additional, custom data may be saved
here to persist across handler callbacks. here to persist across handler callbacks.
""" """
jid = session['from']
jid_full = session['from'].full jid_full = session['from'].full
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_full: {}' logger.debug('{}: jid_full: {}'
.format(function_name, jid_full)) .format(function_name, jid_full))
jid_bare = session['from'].bare jid_bare = session['from'].bare
chat_type = await XmppUtilities.get_chat_type(self, jid_bare) chat_type = await XmppUtilities.get_chat_type(self, jid_bare)
if XmppUtilities.is_access(self, jid_bare, jid_full, chat_type): if XmppUtilities.is_access(self, jid, chat_type):
db_file = config.get_pathname_to_database(jid_bare) db_file = config.get_pathname_to_database(jid_bare)
if jid_bare not in self.settings: if jid_bare not in self.settings:
Config.add_settings_jid(self, jid_bare, db_file) Config.add_settings_jid(self, jid_bare, db_file)
@ -3750,16 +3764,16 @@ class XmppClient(slixmpp.ClientXMPP):
session['next'] = self._handle_settings_complete session['next'] = self._handle_settings_complete
session['payload'] = form session['payload'] = form
else: else:
if not XmppUtilities.is_operator(self, jid_bare): if chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
elif not XmppUtilities.is_operator(self, jid_bare):
text_warn = 'This resource is restricted to operators.' text_warn = 'This resource is restricted to operators.'
elif chat_type == 'groupchat': elif chat_type == 'groupchat':
text_warn = ('This resource is restricted to moderators of {}.' text_warn = ('This resource is restricted to moderators of {}.'
.format(jid_bare)) .format(jid_bare))
elif chat_type == 'error':
text_warn = ('Could not determine chat type of {}.'
.format(jid_bare))
else: else:
text_warn = 'This resource is forbidden.' text_warn = 'This resource is restricted.'
session['notes'] = [['warn', text_warn]] session['notes'] = [['warn', text_warn]]
return session return session

View file

@ -9,10 +9,8 @@ logger = Logger(__name__)
# class XmppChat # class XmppChat
# class XmppUtility: # class XmppUtility:
class XmppUtilities: class XmppUtilities:
async def get_chat_type(self, jid): async def get_chat_type(self, jid):
""" """
Check chat (i.e. JID) type. Check chat (i.e. JID) type.
@ -60,21 +58,18 @@ class XmppUtilities:
return result return result
def is_access(self, jid, chat_type):
def is_access(self, jid_bare, jid_full, chat_type):
"""Determine access privilege""" """Determine access privilege"""
operator = XmppUtilities.is_operator(self, jid_bare) room = jid_bare = jid.bare
if operator: alias = jid.resource
if chat_type == 'groupchat': if chat_type == 'groupchat':
if XmppUtilities.is_moderator(self, jid_bare, jid_full): access = True if XmppUtilities.is_moderator(self, room, alias) else False
access = True if access: print('Access granted to groupchat moderator ' + alias)
else: else:
print('Access granted to chat ' + jid_bare)
access = True access = True
else:
access = False
return access return access
def is_operator(self, jid_bare): def is_operator(self, jid_bare):
"""Check if given JID is an operator""" """Check if given JID is an operator"""
result = False result = False
@ -85,24 +80,28 @@ class XmppUtilities:
break break
return result return result
def is_admin(self, room, alias):
def is_moderator(self, jid_bare, jid_full): """Check if given JID is an administrator"""
"""Check if given JID is a moderator""" affiliation = self.plugin['xep_0045'].get_jid_property(room, alias, 'affiliation')
alias = jid_full[jid_full.index('/')+1:] result = True if affiliation == 'admin' else False
role = self.plugin['xep_0045'].get_jid_property(jid_bare, alias, 'role')
if role == 'moderator':
result = True
else:
result = False
return result return result
def is_owner(self, room, alias):
"""Check if given JID is an owner"""
affiliation = self.plugin['xep_0045'].get_jid_property(room, alias, 'affiliation')
result = True if affiliation == 'owner' else False
return result
def is_moderator(self, room, alias):
"""Check if given JID is a moderator"""
role = self.plugin['xep_0045'].get_jid_property(room, alias, 'role')
result = True if role == 'moderator' else False
return result
# NOTE Would this properly work when Alias and Local differ?
def is_member(self, jid_bare, jid_full): def is_member(self, jid_bare, jid_full):
"""Check if given JID is a member""" """Check if given JID is a member"""
alias = jid_full[jid_full.index('/')+1:] alias = jid_full[jid_full.index('/')+1:]
affiliation = self.plugin['xep_0045'].get_jid_property(jid_bare, alias, 'affiliation') affiliation = self.plugin['xep_0045'].get_jid_property(jid_bare, alias, 'affiliation')
if affiliation == 'member': result = True if affiliation == 'member' else False
result = True
else:
result = False
return result return result