Commit 622a4566 authored by Philipp Hörist's avatar Philipp Hörist

Add OpenPGP (XEP-0373) module

parent 135a5d84
......@@ -63,6 +63,7 @@ from nbxmpp.modules.mood import Mood
from nbxmpp.modules.location import Location
from nbxmpp.modules.user_avatar import UserAvatar
from nbxmpp.modules.bookmarks import Bookmarks
from nbxmpp.modules.openpgp import OpenPGP
from nbxmpp.modules.misc import unwrap_carbon
from nbxmpp.modules.misc import unwrap_mam
from nbxmpp.util import get_properties_struct
......@@ -193,6 +194,7 @@ class XMPPDispatcher(PlugIn):
self._modules['Location'] = Location(self._owner)
self._modules['UserAvatar'] = UserAvatar(self._owner)
self._modules['Bookmarks'] = Bookmarks(self._owner)
self._modules['OpenPGP'] = OpenPGP(self._owner)
for instance in self._modules.values():
for handler in instance.handlers:
......@@ -518,14 +520,19 @@ class XMPPDispatcher(PlugIn):
# Convert simplexml to Protocol object
stanza = self.handlers[xmlns][name]['type'](node=stanza)
own_jid = self._owner.get_bound_jid()
properties = get_properties_struct(name)
if name == 'iq':
if stanza.getFrom() is None and own_jid is not None:
stanza.setFrom(own_jid.getBare())
if name == 'message':
# https://tools.ietf.org/html/rfc6120#section-8.1.1.1
# If the stanza does not include a 'to' address then the client MUST
# treat it as if the 'to' address were included with a value of the
# client's full JID.
own_jid = self._owner.get_bound_jid()
to = stanza.getTo()
if to is None:
stanza.setTo(own_jid)
......
This diff is collapsed.
......@@ -47,16 +47,34 @@ class PubSub:
def _process_pubsub_base(self, _con, stanza, properties):
properties.pubsub = True
event = stanza.getTag('event', namespace=NS_PUBSUB_EVENT)
items = event.getTag('items')
if len(items.getChildren()) != 1:
log.warning('PubSub event with more than one item')
log.warning(stanza)
node = items.getAttr('node')
item = items.getTag('item')
if item is None:
delete = event.getTag('delete')
if delete is not None:
node = delete.getAttr('node')
properties.pubsub_event = PubSubEventData(
node, empty=True, deleted=True)
return
retract = event.getTag('retract')
if retract is not None:
node = retract.getAttr('node')
item = retract.getTag('item')
id_ = item.getAttr('id')
properties.pubsub_event = PubSubEventData(
node, id_, item, retracted=True)
return
id_ = item.getAttr('id')
properties.pubsub_event = PubSubEventData(node, id_, item, None, False)
items = event.getTag('items')
if items is not None:
if len(items.getChildren()) != 1:
log.warning('PubSub event with more than one item')
log.warning(stanza)
node = items.getAttr('node')
item = items.getTag('item')
if item is None:
return
id_ = item.getAttr('id')
properties.pubsub_event = PubSubEventData(node, id_, item)
@call_on_response('_default_response')
def publish(self, jid, node, item, id_=None, options=None):
......
......@@ -189,6 +189,8 @@ NS_HASHES_SHA3_512 = 'urn:xmpp:hash-function-text-names:sha3-512'
NS_HASHES_BLAKE2B_256 = 'urn:xmpp:hash-function-text-names:id-blake2b256'
NS_HASHES_BLAKE2B_512 = 'urn:xmpp:hash-function-text-names:id-blake2b512'
NS_OPENPGP = 'urn:xmpp:openpgp:0'
NS_OPENPGP_PK = 'urn:xmpp:openpgp:0:public-keys'
NS_OPENPGP_SK = 'urn:xmpp:openpgp:0:secret-key'
NS_BOOKMARK_CONVERSION = 'urn:xmpp:bookmarks-conversion:0'
NS_DOMAIN_BASED_NAME = 'urn:xmpp:domain-based-name:1'
......@@ -655,6 +657,9 @@ class InvalidStanza(Exception):
class InvalidJid(Exception):
pass
class StanzaMalformed(Exception):
pass
stream_exceptions = {'bad-format': BadFormat,
'bad-namespace-prefix': BadNamespacePrefix,
'conflict': Conflict,
......@@ -807,6 +812,10 @@ class JID:
"""
return hash(str(self))
def __repr__(self):
return str(self)
class BOSHBody(Node):
"""
<body> tag that wraps usual XMPP stanzas in XMPP over BOSH
......
......@@ -59,7 +59,8 @@ HTTPAuthData.__new__.__defaults__ = (None, None, None, None)
StanzaIDData = namedtuple('StanzaIDData', 'id by')
StanzaIDData.__new__.__defaults__ = (None, None)
PubSubEventData = namedtuple('PubSubEventData', 'node id item data empty')
PubSubEventData = namedtuple('PubSubEventData', 'node id item data empty deleted retracted')
PubSubEventData.__new__.__defaults__ = (None, None, None, False, False, False)
MoodData = namedtuple('MoodData', 'mood text')
......@@ -77,6 +78,17 @@ AvatarData.__new__.__defaults__ = (None,) * len(AvatarData._fields)
BookmarkData = namedtuple('BookmarkData', 'jid name nick autojoin password')
BookmarkData.__new__.__defaults__ = (None, None, None, None)
PGPPublicKey = namedtuple('PGPPublicKey', 'jid key date')
PGPKeyMetadata = namedtuple('PGPKeyMetadata', 'jid fingerprint date')
class CommonError(namedtuple('CommonError', 'type message')):
def __str__(self):
if self.message is not None:
return '%s: %s' % (self.type, self.message)
return self.type
class TuneData(namedtuple('TuneData', 'artist length rating source title track uri')):
......@@ -140,6 +152,16 @@ class MessageProperties:
self.mam = None
self.pubsub = False
self.pubsub_event = None
self.openpgp = None
self.encrypted = None
@property
def is_encrypted(self):
return self.encrypted is not None
@property
def is_openpgp(self):
return self.openpgp is not None
@property
def is_pubsub(self):
......
......@@ -30,6 +30,7 @@ from nbxmpp.structs import Properties
from nbxmpp.structs import IqProperties
from nbxmpp.structs import MessageProperties
from nbxmpp.structs import PresenceProperties
from nbxmpp.structs import CommonError
log = logging.getLogger('nbxmpp.util')
......@@ -137,6 +138,7 @@ def call_on_response(cb):
def response_decorator(func):
@wraps(func)
def func_wrapper(self, *args, **kwargs):
user_data = kwargs.pop('user_data', None)
callback_ = kwargs.pop('callback', None)
stanza = func(self, *args, **kwargs)
......@@ -145,6 +147,9 @@ def call_on_response(cb):
else:
stanza, attrs = stanza, {}
if user_data is not None:
attrs['user_data'] = user_data
if callback_ is not None:
attrs['callback'] = weakref.WeakMethod(callback_)
......@@ -159,11 +164,14 @@ def callback(func):
@wraps(func)
def func_wrapper(self, _con, stanza, **kwargs):
cb = kwargs.pop('callback', None)
user_data = kwargs.pop('user_data', None)
result = func(self, stanza, **kwargs)
if cb is not None and cb() is not None:
cb()(result)
if user_data is not None:
cb()(result, user_data)
else:
cb()(result)
return func_wrapper
......@@ -191,3 +199,18 @@ def to_xs_boolean(value):
raise ValueError(
'Cant convert %s to xs:boolean' % value)
def raise_error(log_method, stanza, type_=None, message=None):
if type_ is None:
type_ = stanza.getError()
message = stanza.getErrorMsg()
error = CommonError(type_, message)
log_method(error)
if log_method.__name__ in ('warning', 'error'):
log_method(stanza)
return error
def is_error_result(result):
return isinstance(result, CommonError)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment