Merge branch 'develop' of https://github.com/fritzy/SleekXMPP into sleek-merge
Conflicts: README.rst examples/IoT_TestDevice.py examples/disco_browser.py setup.py sleekxmpp/jid.py sleekxmpp/plugins/google/auth/stanza.py sleekxmpp/plugins/google/gmail/notifications.py sleekxmpp/plugins/google/nosave/stanza.py sleekxmpp/plugins/google/settings/settings.py sleekxmpp/thirdparty/__init__.py sleekxmpp/thirdparty/socks.py sleekxmpp/thirdparty/statemachine.py sleekxmpp/util/__init__.py sleekxmpp/xmlstream/xmlstream.py slixmpp/basexmpp.py slixmpp/plugins/xep_0004/stanza/form.py slixmpp/plugins/xep_0009/rpc.py slixmpp/plugins/xep_0050/adhoc.py slixmpp/plugins/xep_0065/proxy.py slixmpp/plugins/xep_0084/stanza.py slixmpp/plugins/xep_0202/time.py slixmpp/plugins/xep_0323/sensordata.py slixmpp/plugins/xep_0325/control.py slixmpp/plugins/xep_0325/stanza/control.py slixmpp/roster/single.py slixmpp/stanza/atom.py slixmpp/stanza/rootstanza.py slixmpp/test/slixtest.py slixmpp/util/sasl/mechanisms.py slixmpp/version.py slixmpp/xmlstream/stanzabase.py tests/test_stanza_xep_0323.py tests/test_stanza_xep_0325.py tests/test_stream_xep_0323.py tests/test_stream_xep_0325.py
This commit is contained in:
		@@ -47,6 +47,7 @@ __all__ = [
 | 
			
		||||
    'xep_0108',  # User Activity
 | 
			
		||||
    'xep_0115',  # Entity Capabilities
 | 
			
		||||
    'xep_0118',  # User Tune
 | 
			
		||||
    'xep_0122',  # Data Forms Validation
 | 
			
		||||
    'xep_0128',  # Extended Service Discovery
 | 
			
		||||
    'xep_0131',  # Standard Headers and Internet Metadata
 | 
			
		||||
    'xep_0133',  # Service Administration
 | 
			
		||||
@@ -83,4 +84,5 @@ __all__ = [
 | 
			
		||||
    'xep_0319',  # Last User Interaction in Presence
 | 
			
		||||
    'xep_0323',  # IoT Systems Sensor Data
 | 
			
		||||
    'xep_0325',  # IoT Systems Control
 | 
			
		||||
    'xep_0332',  # HTTP Over XMPP Transport
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								slixmpp/plugins/google/auth/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								slixmpp/plugins/google/auth/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.xmlstream import ElementBase, ET
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GoogleAuth(ElementBase):
 | 
			
		||||
    name = 'auth'
 | 
			
		||||
    namespace = 'http://www.google.com/talk/protocol/auth'
 | 
			
		||||
    plugin_attrib = 'google'
 | 
			
		||||
    interfaces = set(['client_uses_full_bind_result', 'service'])
 | 
			
		||||
 | 
			
		||||
    discovery_attr= '{%s}client-uses-full-bind-result' % namespace
 | 
			
		||||
    service_attr= '{%s}service' % namespace
 | 
			
		||||
 | 
			
		||||
    def setup(self, xml):
 | 
			
		||||
        """Don't create XML for the plugin."""
 | 
			
		||||
        self.xml = ET.Element('')
 | 
			
		||||
 | 
			
		||||
    def get_client_uses_full_bind_result(self):
 | 
			
		||||
        return self.parent()._get_attr(self.discovery_attr) == 'true'
 | 
			
		||||
 | 
			
		||||
    def set_client_uses_full_bind_result(self, value):
 | 
			
		||||
        if value in (True, 'true'):
 | 
			
		||||
            self.parent()._set_attr(self.discovery_attr, 'true')
 | 
			
		||||
        else:
 | 
			
		||||
            self.parent()._del_attr(self.discovery_attr)
 | 
			
		||||
 | 
			
		||||
    def del_client_uses_full_bind_result(self):
 | 
			
		||||
        self.parent()._del_attr(self.discovery_attr)
 | 
			
		||||
 | 
			
		||||
    def get_service(self):
 | 
			
		||||
        return self.parent()._get_attr(self.service_attr, '')
 | 
			
		||||
 | 
			
		||||
    def set_service(self, value):
 | 
			
		||||
        if value:
 | 
			
		||||
            self.parent()._set_attr(self.service_attr, value)
 | 
			
		||||
        else:
 | 
			
		||||
            self.parent()._del_attr(self.service_attr)
 | 
			
		||||
 | 
			
		||||
    def del_service(self):
 | 
			
		||||
        self.parent()._del_attr(self.service_attr)
 | 
			
		||||
							
								
								
									
										90
									
								
								slixmpp/plugins/google/gmail/notifications.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								slixmpp/plugins/google/gmail/notifications.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from slixmpp.stanza import Iq
 | 
			
		||||
from slixmpp.xmlstream.handler import Callback
 | 
			
		||||
from slixmpp.xmlstream.matcher import MatchXPath
 | 
			
		||||
from slixmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from slixmpp.plugins import BasePlugin
 | 
			
		||||
from slixmpp.plugins.google.gmail import stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Gmail(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    Google: Gmail Notifications
 | 
			
		||||
 | 
			
		||||
    Also see <https://developers.google.com/talk/jep_extensions/gmail>.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'gmail'
 | 
			
		||||
    description = 'Google: Gmail Notifications'
 | 
			
		||||
    dependencies = set()
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.GmailQuery)
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.MailBox)
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.NewMail)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
                Callback('Gmail New Mail',
 | 
			
		||||
                    MatchXPath('{%s}iq/{%s}%s' % (
 | 
			
		||||
                        self.xmpp.default_ns,
 | 
			
		||||
                        stanza.NewMail.namespace,
 | 
			
		||||
                        stanza.NewMail.name)),
 | 
			
		||||
                    self._handle_new_mail))
 | 
			
		||||
 | 
			
		||||
        self._last_result_time = None
 | 
			
		||||
        self._last_result_tid = None
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp.remove_handler('Gmail New Mail')
 | 
			
		||||
 | 
			
		||||
    def _handle_new_mail(self, iq):
 | 
			
		||||
        log.info('Gmail: New email!')
 | 
			
		||||
        iq.reply().send()
 | 
			
		||||
        self.xmpp.event('gmail_notification')
 | 
			
		||||
 | 
			
		||||
    def check(self, timeout=None, callback=None):
 | 
			
		||||
        last_time = self._last_result_time
 | 
			
		||||
        last_tid = self._last_result_tid
 | 
			
		||||
 | 
			
		||||
        callback = lambda iq: self._update_last_results(iq, callback)
 | 
			
		||||
 | 
			
		||||
        return self.search(newer_time=last_time,
 | 
			
		||||
                           newer_tid=last_tid,
 | 
			
		||||
                           timeout=timeout,
 | 
			
		||||
                           callback=callback)
 | 
			
		||||
 | 
			
		||||
    def _update_last_results(self, iq, callback=None):
 | 
			
		||||
        self._last_result_time = iq['gmail_messages']['result_time']
 | 
			
		||||
        threads = iq['gmail_messages']['threads']
 | 
			
		||||
        if threads:
 | 
			
		||||
            self._last_result_tid = threads[0]['tid']
 | 
			
		||||
        if callback:
 | 
			
		||||
            callback(iq)
 | 
			
		||||
 | 
			
		||||
    def search(self, query=None, newer_time=None, newer_tid=None,
 | 
			
		||||
                     timeout=None, callback=None):
 | 
			
		||||
        if not query:
 | 
			
		||||
            log.info('Gmail: Checking for new email')
 | 
			
		||||
        else:
 | 
			
		||||
            log.info('Gmail: Searching for emails matching: "%s"', query)
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['to'] = self.xmpp.boundjid.bare
 | 
			
		||||
        iq['gmail']['search'] = query
 | 
			
		||||
        iq['gmail']['newer_than_time'] = newer_time
 | 
			
		||||
        iq['gmail']['newer_than_tid'] = newer_tid
 | 
			
		||||
        return iq.send(timeout=timeout, callback=callback)
 | 
			
		||||
							
								
								
									
										59
									
								
								slixmpp/plugins/google/nosave/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								slixmpp/plugins/google/nosave/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.jid import JID
 | 
			
		||||
from slixmpp.xmlstream import ElementBase, register_stanza_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NoSave(ElementBase):
 | 
			
		||||
    name = 'x'
 | 
			
		||||
    namespace = 'google:nosave'
 | 
			
		||||
    plugin_attrib = 'google_nosave'
 | 
			
		||||
    interfaces = set(['value'])
 | 
			
		||||
 | 
			
		||||
    def get_value(self):
 | 
			
		||||
        return self._get_attr('value', '') == 'enabled'
 | 
			
		||||
 | 
			
		||||
    def set_value(self, value):
 | 
			
		||||
        self._set_attr('value', 'enabled' if value else 'disabled')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NoSaveQuery(ElementBase):
 | 
			
		||||
    name = 'query'
 | 
			
		||||
    namespace = 'google:nosave'
 | 
			
		||||
    plugin_attrib = 'google_nosave'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Item(ElementBase):
 | 
			
		||||
    name = 'item'
 | 
			
		||||
    namespace = 'google:nosave'
 | 
			
		||||
    plugin_attrib = 'item'
 | 
			
		||||
    plugin_multi_attrib = 'items'
 | 
			
		||||
    interfaces = set(['jid', 'source', 'value'])
 | 
			
		||||
 | 
			
		||||
    def get_value(self):
 | 
			
		||||
        return self._get_attr('value', '') == 'enabled'
 | 
			
		||||
 | 
			
		||||
    def set_value(self, value):
 | 
			
		||||
        self._set_attr('value', 'enabled' if value else 'disabled')
 | 
			
		||||
 | 
			
		||||
    def get_jid(self):
 | 
			
		||||
        return JID(self._get_attr('jid', ''))
 | 
			
		||||
 | 
			
		||||
    def set_jid(self, value):
 | 
			
		||||
        self._set_attr('jid', str(value))
 | 
			
		||||
 | 
			
		||||
    def get_source(self):
 | 
			
		||||
        return JID(self._get_attr('source', ''))
 | 
			
		||||
 | 
			
		||||
    def set_source(self, value):
 | 
			
		||||
        self._set_attr('source', str(value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_stanza_plugin(NoSaveQuery, Item)
 | 
			
		||||
							
								
								
									
										63
									
								
								slixmpp/plugins/google/settings/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								slixmpp/plugins/google/settings/settings.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.stanza import Iq
 | 
			
		||||
from slixmpp.xmlstream.handler import Callback
 | 
			
		||||
from slixmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from slixmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from slixmpp.plugins import BasePlugin
 | 
			
		||||
from slixmpp.plugins.google.settings import stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GoogleSettings(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    Google: Gmail Notifications
 | 
			
		||||
 | 
			
		||||
    Also see <https://developers.google.com/talk/jep_extensions/usersettings>.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'google_settings'
 | 
			
		||||
    description = 'Google: User Settings'
 | 
			
		||||
    dependencies = set()
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.UserSettings)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
                Callback('Google Settings',
 | 
			
		||||
                    StanzaPath('iq@type=set/google_settings'),
 | 
			
		||||
                    self._handle_settings_change))
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp.remove_handler('Google Settings')
 | 
			
		||||
 | 
			
		||||
    def get(self, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq.enable('google_settings')
 | 
			
		||||
        return iq.send(timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def update(self, settings, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq.enable('google_settings')
 | 
			
		||||
 | 
			
		||||
        for setting, value in settings.items():
 | 
			
		||||
            iq['google_settings'][setting] = value
 | 
			
		||||
 | 
			
		||||
        return iq.send(timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def _handle_settings_change(self, iq):
 | 
			
		||||
        reply = self.xmpp.Iq()
 | 
			
		||||
        reply['type'] = 'result'
 | 
			
		||||
        reply['id'] = iq['id']
 | 
			
		||||
        reply['to'] = iq['from']
 | 
			
		||||
        reply.send()
 | 
			
		||||
        self.xmpp.event('google_settings_change', iq)
 | 
			
		||||
@@ -13,8 +13,9 @@ class FormField(ElementBase):
 | 
			
		||||
    namespace = 'jabber:x:data'
 | 
			
		||||
    name = 'field'
 | 
			
		||||
    plugin_attrib = 'field'
 | 
			
		||||
    plugin_multi_attrib = 'fields'
 | 
			
		||||
    interfaces = set(('answer', 'desc', 'required', 'value',
 | 
			
		||||
                      'options', 'label', 'type', 'var'))
 | 
			
		||||
                      'label', 'type', 'var'))
 | 
			
		||||
    sub_interfaces = set(('desc',))
 | 
			
		||||
    plugin_tag_map = {}
 | 
			
		||||
    plugin_attrib_map = {}
 | 
			
		||||
@@ -165,6 +166,7 @@ class FieldOption(ElementBase):
 | 
			
		||||
    plugin_attrib = 'option'
 | 
			
		||||
    interfaces = set(('label', 'value'))
 | 
			
		||||
    sub_interfaces = set(('value',))
 | 
			
		||||
    plugin_multi_attrib = 'options'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FormField.addOption = FormField.add_option
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import copy
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
from slixmpp.thirdparty import OrderedSet
 | 
			
		||||
 | 
			
		||||
from slixmpp.xmlstream import ElementBase, ET
 | 
			
		||||
from slixmpp.plugins.xep_0004.stanza import FormField
 | 
			
		||||
@@ -22,8 +23,7 @@ class Form(ElementBase):
 | 
			
		||||
    namespace = 'jabber:x:data'
 | 
			
		||||
    name = 'x'
 | 
			
		||||
    plugin_attrib = 'form'
 | 
			
		||||
    interfaces = set(('fields', 'instructions', 'items',
 | 
			
		||||
                      'reported', 'title', 'type', 'values'))
 | 
			
		||||
    interfaces = OrderedSet(('instructions', 'reported', 'title', 'type', 'items', ))
 | 
			
		||||
    sub_interfaces = set(('title',))
 | 
			
		||||
    form_types = set(('cancel', 'form', 'result', 'submit'))
 | 
			
		||||
 | 
			
		||||
@@ -43,12 +43,12 @@ class Form(ElementBase):
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def field(self):
 | 
			
		||||
        return self['fields']
 | 
			
		||||
        return self.get_fields()
 | 
			
		||||
 | 
			
		||||
    def set_type(self, ftype):
 | 
			
		||||
        self._set_attr('type', ftype)
 | 
			
		||||
        if ftype == 'submit':
 | 
			
		||||
            fields = self['fields']
 | 
			
		||||
            fields = self.get_fields()
 | 
			
		||||
            for var in fields:
 | 
			
		||||
                field = fields[var]
 | 
			
		||||
                del field['type']
 | 
			
		||||
@@ -74,7 +74,8 @@ class Form(ElementBase):
 | 
			
		||||
            field['desc'] = desc
 | 
			
		||||
            field['required'] = required
 | 
			
		||||
            if options is not None:
 | 
			
		||||
                field['options'] = options
 | 
			
		||||
                for option in options:
 | 
			
		||||
                    field.add_option(**option)
 | 
			
		||||
        else:
 | 
			
		||||
            del field['type']
 | 
			
		||||
        self.append(field)
 | 
			
		||||
@@ -151,7 +152,6 @@ class Form(ElementBase):
 | 
			
		||||
        return fields
 | 
			
		||||
 | 
			
		||||
    def get_instructions(self):
 | 
			
		||||
        instructions = ''
 | 
			
		||||
        instsXML = self.xml.findall('{%s}instructions' % self.namespace)
 | 
			
		||||
        return "\n".join([instXML.text for instXML in instsXML])
 | 
			
		||||
 | 
			
		||||
@@ -170,7 +170,7 @@ class Form(ElementBase):
 | 
			
		||||
    def get_reported(self):
 | 
			
		||||
        fields = OrderedDict()
 | 
			
		||||
        xml = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,
 | 
			
		||||
                                     FormField.namespace))
 | 
			
		||||
                                                           FormField.namespace))
 | 
			
		||||
        for field in xml:
 | 
			
		||||
            field = FormField(xml=field)
 | 
			
		||||
            fields[field['var']] = field
 | 
			
		||||
@@ -178,7 +178,7 @@ class Form(ElementBase):
 | 
			
		||||
 | 
			
		||||
    def get_values(self):
 | 
			
		||||
        values = OrderedDict()
 | 
			
		||||
        fields = self['fields']
 | 
			
		||||
        fields = self.get_fields()
 | 
			
		||||
        for var in fields:
 | 
			
		||||
            values[var] = fields[var]['value']
 | 
			
		||||
        return values
 | 
			
		||||
@@ -195,7 +195,14 @@ class Form(ElementBase):
 | 
			
		||||
            fields = fields.items()
 | 
			
		||||
        for var, field in fields:
 | 
			
		||||
            field['var'] = var
 | 
			
		||||
            self.add_field(**field)
 | 
			
		||||
            self.add_field(
 | 
			
		||||
                var=field.get('var'),
 | 
			
		||||
                label=field.get('label'),
 | 
			
		||||
                desc=field.get('desc'),
 | 
			
		||||
                required=field.get('required'),
 | 
			
		||||
                value=field.get('value'),
 | 
			
		||||
                options=field.get('options'),
 | 
			
		||||
                type=field.get('type'))
 | 
			
		||||
 | 
			
		||||
    def set_instructions(self, instructions):
 | 
			
		||||
        del self['instructions']
 | 
			
		||||
@@ -213,17 +220,33 @@ class Form(ElementBase):
 | 
			
		||||
            self.add_item(item)
 | 
			
		||||
 | 
			
		||||
    def set_reported(self, reported):
 | 
			
		||||
        """
 | 
			
		||||
        This either needs a dictionary of dictionaries or a dictionary of form fields.
 | 
			
		||||
        :param reported:
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        for var in reported:
 | 
			
		||||
            field = reported[var]
 | 
			
		||||
            field['var'] = var
 | 
			
		||||
            self.add_reported(var, **field)
 | 
			
		||||
 | 
			
		||||
            if isinstance(field, dict):
 | 
			
		||||
                self.add_reported(**field)
 | 
			
		||||
            else:
 | 
			
		||||
                reported = self.xml.find('{%s}reported' % self.namespace)
 | 
			
		||||
                if reported is None:
 | 
			
		||||
                    reported = ET.Element('{%s}reported' % self.namespace)
 | 
			
		||||
                    self.xml.append(reported)
 | 
			
		||||
 | 
			
		||||
                fieldXML = ET.Element('{%s}field' % FormField.namespace)
 | 
			
		||||
                reported.append(fieldXML)
 | 
			
		||||
                new_field = FormField(xml=fieldXML)
 | 
			
		||||
                new_field.values = field.values
 | 
			
		||||
 | 
			
		||||
    def set_values(self, values):
 | 
			
		||||
        fields = self['fields']
 | 
			
		||||
        fields = self.get_fields()
 | 
			
		||||
        for field in values:
 | 
			
		||||
            if field not in fields:
 | 
			
		||||
            if field not in self.get_fields():
 | 
			
		||||
                fields[field] = self.add_field(var=field)
 | 
			
		||||
            fields[field]['value'] = values[field]
 | 
			
		||||
            self.get_fields()[field]['value'] = values[field]
 | 
			
		||||
 | 
			
		||||
    def merge(self, other):
 | 
			
		||||
        new = copy.copy(self)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from binding import py2xml, xml2py, xml2fault, fault2xml
 | 
			
		||||
from slixmpp.plugins.xep_0009.binding import py2xml, xml2py, xml2fault, fault2xml
 | 
			
		||||
from threading import RLock
 | 
			
		||||
import abc
 | 
			
		||||
import inspect
 | 
			
		||||
@@ -18,6 +18,38 @@ import traceback
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
def _isstr(obj):
 | 
			
		||||
    return isinstance(obj, str)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Class decorator to declare a metaclass to a class in a way compatible with Python 2 and 3.
 | 
			
		||||
# This decorator is copied from 'six' (https://bitbucket.org/gutworth/six):
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2010-2015 Benjamin Peterson
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
# in the Software without restriction, including without limitation the rights
 | 
			
		||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
# copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
# furnished to do so, subject to the following conditions:
 | 
			
		||||
#
 | 
			
		||||
# The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
# copies or substantial portions of the Software.
 | 
			
		||||
def _add_metaclass(metaclass):
 | 
			
		||||
    def wrapper(cls):
 | 
			
		||||
        orig_vars = cls.__dict__.copy()
 | 
			
		||||
        slots = orig_vars.get('__slots__')
 | 
			
		||||
        if slots is not None:
 | 
			
		||||
            if isinstance(slots, str):
 | 
			
		||||
                slots = [slots]
 | 
			
		||||
            for slots_var in slots:
 | 
			
		||||
                orig_vars.pop(slots_var)
 | 
			
		||||
        orig_vars.pop('__dict__', None)
 | 
			
		||||
        orig_vars.pop('__weakref__', None)
 | 
			
		||||
        return metaclass(cls.__name__, cls.__bases__, orig_vars)
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
def _intercept(method, name, public):
 | 
			
		||||
    def _resolver(instance, *args, **kwargs):
 | 
			
		||||
        log.debug("Locally calling %s.%s with arguments %s.", instance.FQN(), method.__name__, args)
 | 
			
		||||
@@ -68,7 +100,7 @@ def remote(function_argument, public = True):
 | 
			
		||||
    if hasattr(function_argument, '__call__'):
 | 
			
		||||
        return _intercept(function_argument, None, public)
 | 
			
		||||
    else:
 | 
			
		||||
        if not isinstance(function_argument, basestring):
 | 
			
		||||
        if not _isstr(function_argument):
 | 
			
		||||
            if not isinstance(function_argument, bool):
 | 
			
		||||
                raise Exception('Expected an RPC method name or visibility modifier!')
 | 
			
		||||
            else:
 | 
			
		||||
@@ -222,12 +254,11 @@ class TimeoutException(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@_add_metaclass(abc.ABCMeta)
 | 
			
		||||
class Callback(object):
 | 
			
		||||
    '''
 | 
			
		||||
    A base class for callback handlers.
 | 
			
		||||
    '''
 | 
			
		||||
    __metaclass__ = abc.ABCMeta
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @abc.abstractproperty
 | 
			
		||||
    def set_value(self, value):
 | 
			
		||||
@@ -291,7 +322,7 @@ class Future(Callback):
 | 
			
		||||
        self._event.set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@_add_metaclass(abc.ABCMeta)
 | 
			
		||||
class Endpoint(object):
 | 
			
		||||
    '''
 | 
			
		||||
    The Endpoint class is an abstract base class for all objects
 | 
			
		||||
@@ -303,8 +334,6 @@ class Endpoint(object):
 | 
			
		||||
    which specifies which object an RPC call refers to. It is the
 | 
			
		||||
    first part in a RPC method name '<fqn>.<method>'.
 | 
			
		||||
    '''
 | 
			
		||||
    __metaclass__ = abc.ABCMeta
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __init__(self, session, target_jid):
 | 
			
		||||
        '''
 | 
			
		||||
@@ -491,7 +520,7 @@ class RemoteSession(object):
 | 
			
		||||
 | 
			
		||||
    def _find_key(self, dict, value):
 | 
			
		||||
        """return the key of dictionary dic given the value"""
 | 
			
		||||
        search = [k for k, v in dict.iteritems() if v == value]
 | 
			
		||||
        search = [k for k, v in dict.items() if v == value]
 | 
			
		||||
        if len(search) == 0:
 | 
			
		||||
            return None
 | 
			
		||||
        else:
 | 
			
		||||
@@ -547,7 +576,7 @@ class RemoteSession(object):
 | 
			
		||||
            result = handler_cls(*args, **kwargs)
 | 
			
		||||
            Endpoint.__init__(result, self, self._client.boundjid.full)
 | 
			
		||||
        method_dict = result.get_methods()
 | 
			
		||||
        for method_name, method in method_dict.iteritems():
 | 
			
		||||
        for method_name, method in method_dict.items():
 | 
			
		||||
            #!!! self._client.plugin['xep_0009'].register_call(result.FQN(), method, method_name)
 | 
			
		||||
            self._register_call(result.FQN(), method, method_name)
 | 
			
		||||
        self._register_acl(result.FQN(), acl)
 | 
			
		||||
@@ -569,11 +598,11 @@ class RemoteSession(object):
 | 
			
		||||
            self._register_callback(pid, callback)
 | 
			
		||||
            iq.send()
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
    def close(self, wait=False):
 | 
			
		||||
        '''
 | 
			
		||||
        Closes this session.
 | 
			
		||||
        '''
 | 
			
		||||
        self._client.disconnect(False)
 | 
			
		||||
        self._client.disconnect(wait=wait)
 | 
			
		||||
        self._session_close_callback()
 | 
			
		||||
 | 
			
		||||
    def _on_jabber_rpc_method_call(self, iq):
 | 
			
		||||
@@ -697,7 +726,8 @@ class Remote(object):
 | 
			
		||||
            if(client.boundjid.bare in cls._sessions):
 | 
			
		||||
                raise RemoteException("There already is a session associated with these credentials!")
 | 
			
		||||
            else:
 | 
			
		||||
                cls._sessions[client.boundjid.bare] = client;
 | 
			
		||||
                cls._sessions[client.boundjid.bare] = client
 | 
			
		||||
 | 
			
		||||
        def _session_close_callback():
 | 
			
		||||
            with Remote._lock:
 | 
			
		||||
                del cls._sessions[client.boundjid.bare]
 | 
			
		||||
 
 | 
			
		||||
@@ -220,3 +220,4 @@ class XEP_0009(BasePlugin):
 | 
			
		||||
    def _extract_method(self, stanza):
 | 
			
		||||
        xml = ET.fromstring("%s" % stanza)
 | 
			
		||||
        return xml.find("./methodCall/methodName").text
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -609,7 +609,7 @@ class XEP_0030(BasePlugin):
 | 
			
		||||
        """
 | 
			
		||||
        self.api['del_features'](jid, node, None, kwargs)
 | 
			
		||||
 | 
			
		||||
    def _run_node_handler(self, htype, jid, node=None, ifrom=None, data={}):
 | 
			
		||||
    def _run_node_handler(self, htype, jid, node=None, ifrom=None, data=None):
 | 
			
		||||
        """
 | 
			
		||||
        Execute the most specific node handler for the given
 | 
			
		||||
        JID/node combination.
 | 
			
		||||
@@ -620,6 +620,9 @@ class XEP_0030(BasePlugin):
 | 
			
		||||
            node  -- The node requested.
 | 
			
		||||
            data  -- Optional, custom data to pass to the handler.
 | 
			
		||||
        """
 | 
			
		||||
        if not data:
 | 
			
		||||
            data = {}
 | 
			
		||||
 | 
			
		||||
        return self.api[htype](jid, node, ifrom, data)
 | 
			
		||||
 | 
			
		||||
    def _handle_disco_info(self, iq):
 | 
			
		||||
 
 | 
			
		||||
@@ -403,6 +403,16 @@ class XEP_0045(BasePlugin):
 | 
			
		||||
            return None
 | 
			
		||||
        return self.rooms[room].keys()
 | 
			
		||||
 | 
			
		||||
    def getUsersByAffiliation(cls, room, affiliation='member', ifrom=None):
 | 
			
		||||
        if affiliation not in ('outcast', 'member', 'admin', 'owner', 'none'):
 | 
			
		||||
            raise TypeError
 | 
			
		||||
        query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
 | 
			
		||||
        item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {'affiliation': affiliation})
 | 
			
		||||
        query.append(item)
 | 
			
		||||
        iq = cls.xmpp.Iq(sto=room, sfrom=ifrom, stype='get')
 | 
			
		||||
        iq.append(query)
 | 
			
		||||
        return iq.send()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
xep_0045 = XEP_0045
 | 
			
		||||
register_plugin(XEP_0045)
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ class XEP_0050(BasePlugin):
 | 
			
		||||
                         self._handle_command))
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(Iq, Command)
 | 
			
		||||
        register_stanza_plugin(Command, Form)
 | 
			
		||||
        register_stanza_plugin(Command, Form, iterable=True)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.add_event_handler('command_execute',
 | 
			
		||||
                                    self._handle_command_start)
 | 
			
		||||
@@ -415,12 +415,26 @@ class XEP_0050(BasePlugin):
 | 
			
		||||
 | 
			
		||||
            del self.sessions[sessionid]
 | 
			
		||||
 | 
			
		||||
            payload = session['payload']
 | 
			
		||||
            if payload is None:
 | 
			
		||||
                payload = []
 | 
			
		||||
            if not isinstance(payload, list):
 | 
			
		||||
                payload = [payload]
 | 
			
		||||
 | 
			
		||||
            for item in payload:
 | 
			
		||||
                register_stanza_plugin(Command, item.__class__, iterable=True)
 | 
			
		||||
 | 
			
		||||
            iq = iq.reply()
 | 
			
		||||
 | 
			
		||||
            iq['command']['node'] = node
 | 
			
		||||
            iq['command']['sessionid'] = sessionid
 | 
			
		||||
            iq['command']['actions'] = []
 | 
			
		||||
            iq['command']['status'] = 'completed'
 | 
			
		||||
            iq['command']['notes'] = session['notes']
 | 
			
		||||
 | 
			
		||||
            for item in payload:
 | 
			
		||||
                iq['command'].append(item)
 | 
			
		||||
 | 
			
		||||
            iq.send()
 | 
			
		||||
        else:
 | 
			
		||||
            raise XMPPError('item-not-found')
 | 
			
		||||
 
 | 
			
		||||
@@ -128,7 +128,8 @@ class Telephone(ElementBase):
 | 
			
		||||
 | 
			
		||||
    def setup(self, xml=None):
 | 
			
		||||
        super(Telephone, self).setup(xml=xml)
 | 
			
		||||
        self._set_sub_text('NUMBER', '', keep=True)
 | 
			
		||||
        ## this blanks out numbers received from server
 | 
			
		||||
        ##self._set_sub_text('NUMBER', '', keep=True)
 | 
			
		||||
 | 
			
		||||
    def set_number(self, value):
 | 
			
		||||
        self._set_sub_text('NUMBER', value, keep=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -251,7 +251,6 @@ class XEP_0065(BasePlugin):
 | 
			
		||||
        host : The hostname or the IP of the proxy. <str>
 | 
			
		||||
        port : The port of the proxy. <str> or <int>
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        factory = lambda: Socks5Protocol(dest, 0, self.xmpp.event)
 | 
			
		||||
        return self.xmpp.loop.create_connection(factory, proxy, proxy_port)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ class XEP_0096(BasePlugin):
 | 
			
		||||
        data['size'] = size
 | 
			
		||||
        data['date'] = date
 | 
			
		||||
        data['desc'] = desc
 | 
			
		||||
        data['hash'] = hash
 | 
			
		||||
        if allow_ranged:
 | 
			
		||||
            data.enable('range')
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								slixmpp/plugins/xep_0122/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								slixmpp/plugins/xep_0122/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
 | 
			
		||||
from slixmpp.plugins.base import register_plugin
 | 
			
		||||
from slixmpp.plugins.xep_0122.stanza import FormValidation
 | 
			
		||||
from slixmpp.plugins.xep_0122.data_validation import XEP_0122
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0122)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Retain some backwards compatibility
 | 
			
		||||
xep_0122 = XEP_0122
 | 
			
		||||
							
								
								
									
										19
									
								
								slixmpp/plugins/xep_0122/data_validation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								slixmpp/plugins/xep_0122/data_validation.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
from slixmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from slixmpp.plugins import BasePlugin
 | 
			
		||||
from slixmpp.plugins.xep_0004 import stanza
 | 
			
		||||
from slixmpp.plugins.xep_0004.stanza import FormField
 | 
			
		||||
from slixmpp.plugins.xep_0122.stanza import FormValidation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0122(BasePlugin):
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0122: Data Forms
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0122'
 | 
			
		||||
    description = 'XEP-0122: Data Forms Validation'
 | 
			
		||||
    dependencies = set(['xep_0004'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(FormField, FormValidation)
 | 
			
		||||
							
								
								
									
										93
									
								
								slixmpp/plugins/xep_0122/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								slixmpp/plugins/xep_0122/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
from slixmpp.xmlstream import ElementBase, ET
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FormValidation(ElementBase):
 | 
			
		||||
    """
 | 
			
		||||
    Validation values for form fields.
 | 
			
		||||
 | 
			
		||||
    Example:
 | 
			
		||||
 | 
			
		||||
    <field var='evt.date' type='text-single' label='Event Date/Time'>
 | 
			
		||||
      <validate xmlns='http://jabber.org/protocol/xdata-validate'
 | 
			
		||||
                datatype='xs:dateTime'/>
 | 
			
		||||
      <value>2003-10-06T11:22:00-07:00</value>
 | 
			
		||||
    </field>
 | 
			
		||||
 | 
			
		||||
    Questions:
 | 
			
		||||
      Should this look at the datatype value and convert the range values as appropriate?
 | 
			
		||||
      Should this stanza provide a pass/fail for a value from the field, or convert field value to datatype?
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    namespace = 'http://jabber.org/protocol/xdata-validate'
 | 
			
		||||
    name = 'validate'
 | 
			
		||||
    plugin_attrib = 'validate'
 | 
			
		||||
    interfaces = {'datatype', 'basic', 'open', 'range', 'regex', }
 | 
			
		||||
    sub_interfaces = {'basic', 'open', 'range', 'regex', }
 | 
			
		||||
    plugin_attrib_map = {}
 | 
			
		||||
    plugin_tag_map = {}
 | 
			
		||||
 | 
			
		||||
    def _add_field(self, name):
 | 
			
		||||
        self.remove_all()
 | 
			
		||||
        item_xml = ET.Element('{%s}%s' % (self.namespace, name))
 | 
			
		||||
        self.xml.append(item_xml)
 | 
			
		||||
        return item_xml
 | 
			
		||||
 | 
			
		||||
    def set_basic(self, value):
 | 
			
		||||
        if value:
 | 
			
		||||
            self._add_field('basic')
 | 
			
		||||
        else:
 | 
			
		||||
            del self['basic']
 | 
			
		||||
 | 
			
		||||
    def set_open(self, value):
 | 
			
		||||
        if value:
 | 
			
		||||
            self._add_field('open')
 | 
			
		||||
        else:
 | 
			
		||||
            del self['open']
 | 
			
		||||
 | 
			
		||||
    def set_regex(self, regex):
 | 
			
		||||
        if regex:
 | 
			
		||||
            _regex = self._add_field('regex')
 | 
			
		||||
            _regex.text = regex
 | 
			
		||||
        else:
 | 
			
		||||
            del self['regex']
 | 
			
		||||
 | 
			
		||||
    def set_range(self, value, minimum=None, maximum=None):
 | 
			
		||||
        if value:
 | 
			
		||||
            _range = self._add_field('range')
 | 
			
		||||
            _range.attrib['min'] = str(minimum)
 | 
			
		||||
            _range.attrib['max'] = str(maximum)
 | 
			
		||||
        else:
 | 
			
		||||
            del self['range']
 | 
			
		||||
 | 
			
		||||
    def remove_all(self, except_tag=None):
 | 
			
		||||
        for a in self.sub_interfaces:
 | 
			
		||||
            if a != except_tag:
 | 
			
		||||
                del self[a]
 | 
			
		||||
 | 
			
		||||
    def get_basic(self):
 | 
			
		||||
        present = self.xml.find('{%s}basic' % self.namespace)
 | 
			
		||||
        return present is not None
 | 
			
		||||
 | 
			
		||||
    def get_open(self):
 | 
			
		||||
        present = self.xml.find('{%s}open' % self.namespace)
 | 
			
		||||
        return present is not None
 | 
			
		||||
 | 
			
		||||
    def get_regex(self):
 | 
			
		||||
        present = self.xml.find('{%s}regex' % self.namespace)
 | 
			
		||||
        if present is not None:
 | 
			
		||||
            return present.text
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def get_range(self):
 | 
			
		||||
        present = self.xml.find('{%s}range' % self.namespace)
 | 
			
		||||
        if present is not None:
 | 
			
		||||
            attributes = present.attrib
 | 
			
		||||
            return_value = dict()
 | 
			
		||||
            if 'min' in attributes:
 | 
			
		||||
                return_value['minimum'] = attributes['min']
 | 
			
		||||
            if 'max' in attributes:
 | 
			
		||||
                return_value['maximum'] = attributes['max']
 | 
			
		||||
            return return_value
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
							
								
								
									
										145
									
								
								slixmpp/plugins/xep_0138.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								slixmpp/plugins/xep_0138.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
"""
 | 
			
		||||
    slixmpp: The Slick XMPP Library
 | 
			
		||||
    Copyright (C) 2011  Nathanael C. Fritz
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import zlib
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from slixmpp.stanza import StreamFeatures
 | 
			
		||||
from slixmpp.xmlstream import RestartStream, register_stanza_plugin, ElementBase, StanzaBase
 | 
			
		||||
from slixmpp.xmlstream.matcher import *
 | 
			
		||||
from slixmpp.xmlstream.handler import *
 | 
			
		||||
from slixmpp.plugins import BasePlugin, register_plugin
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Compression(ElementBase):
 | 
			
		||||
    name = 'compression'
 | 
			
		||||
    namespace = 'http://jabber.org/features/compress'
 | 
			
		||||
    interfaces = set(('methods',))
 | 
			
		||||
    plugin_attrib = 'compression'
 | 
			
		||||
    plugin_tag_map = {}
 | 
			
		||||
    plugin_attrib_map = {}
 | 
			
		||||
 | 
			
		||||
    def get_methods(self):
 | 
			
		||||
        methods = []
 | 
			
		||||
        for method in self.xml.findall('{%s}method' % self.namespace):
 | 
			
		||||
            methods.append(method.text)
 | 
			
		||||
        return methods
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Compress(StanzaBase):
 | 
			
		||||
    name = 'compress'
 | 
			
		||||
    namespace = 'http://jabber.org/protocol/compress'
 | 
			
		||||
    interfaces = set(('method',))
 | 
			
		||||
    sub_interfaces = interfaces
 | 
			
		||||
    plugin_attrib = 'compress'
 | 
			
		||||
    plugin_tag_map = {}
 | 
			
		||||
    plugin_attrib_map = {}
 | 
			
		||||
 | 
			
		||||
    def setup(self, xml):
 | 
			
		||||
        StanzaBase.setup(self, xml)
 | 
			
		||||
        self.xml.tag = self.tag_name()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Compressed(StanzaBase):
 | 
			
		||||
    name = 'compressed'
 | 
			
		||||
    namespace = 'http://jabber.org/protocol/compress'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
    plugin_tag_map = {}
 | 
			
		||||
    plugin_attrib_map = {}
 | 
			
		||||
 | 
			
		||||
    def setup(self, xml):
 | 
			
		||||
        StanzaBase.setup(self, xml)
 | 
			
		||||
        self.xml.tag = self.tag_name()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ZlibSocket(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, socketobj):
 | 
			
		||||
        self.__socket = socketobj
 | 
			
		||||
        self.compressor = zlib.compressobj()
 | 
			
		||||
        self.decompressor = zlib.decompressobj(zlib.MAX_WBITS)
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, name):
 | 
			
		||||
        return getattr(self.__socket, name)
 | 
			
		||||
 | 
			
		||||
    def send(self, data):
 | 
			
		||||
        sentlen = len(data)
 | 
			
		||||
        data = self.compressor.compress(data)
 | 
			
		||||
        data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
 | 
			
		||||
        log.debug(b'>>> (compressed)' + (data.encode("hex")))
 | 
			
		||||
        #return self.__socket.send(data)
 | 
			
		||||
        sentactuallen = self.__socket.send(data)
 | 
			
		||||
        assert(sentactuallen == len(data))
 | 
			
		||||
 | 
			
		||||
        return sentlen
 | 
			
		||||
 | 
			
		||||
    def recv(self, *args, **kwargs):
 | 
			
		||||
        data = self.__socket.recv(*args, **kwargs)
 | 
			
		||||
        log.debug(b'<<< (compressed)' + data.encode("hex"))
 | 
			
		||||
        return self.decompressor.decompress(self.decompressor.unconsumed_tail + data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0138(BasePlugin):
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0138: Compression
 | 
			
		||||
    """
 | 
			
		||||
    name = "xep_0138"
 | 
			
		||||
    description = "XEP-0138: Compression"
 | 
			
		||||
    dependencies = set(["xep_0030"])
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        self.xep = '0138'
 | 
			
		||||
        self.description = 'Stream Compression (Generic)'
 | 
			
		||||
 | 
			
		||||
        self.compression_methods = {'zlib': True}
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(StreamFeatures, Compression)
 | 
			
		||||
        self.xmpp.register_stanza(Compress)
 | 
			
		||||
        self.xmpp.register_stanza(Compressed)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
                Callback('Compressed',
 | 
			
		||||
                    StanzaPath('compressed'),
 | 
			
		||||
                    self._handle_compressed,
 | 
			
		||||
                    instream=True))
 | 
			
		||||
 | 
			
		||||
        self.xmpp.register_feature('compression',
 | 
			
		||||
                self._handle_compression,
 | 
			
		||||
                restart=True,
 | 
			
		||||
                order=self.config.get('order', 5))
 | 
			
		||||
 | 
			
		||||
    def register_compression_method(self, name, handler):
 | 
			
		||||
        self.compression_methods[name] = handler
 | 
			
		||||
 | 
			
		||||
    def _handle_compression(self, features):
 | 
			
		||||
        for method in features['compression']['methods']:
 | 
			
		||||
            if method in self.compression_methods:
 | 
			
		||||
                log.info('Attempting to use %s compression' % method)
 | 
			
		||||
                c = Compress(self.xmpp)
 | 
			
		||||
                c['method'] = method
 | 
			
		||||
                c.send(now=True)
 | 
			
		||||
                return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def _handle_compressed(self, stanza):
 | 
			
		||||
        self.xmpp.features.add('compression')
 | 
			
		||||
        log.debug('Stream Compressed!')
 | 
			
		||||
        compressed_socket = ZlibSocket(self.xmpp.socket)
 | 
			
		||||
        self.xmpp.set_socket(compressed_socket)
 | 
			
		||||
        raise RestartStream()
 | 
			
		||||
 | 
			
		||||
    def _handle_failure(self, stanza):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
xep_0138 = XEP_0138
 | 
			
		||||
register_plugin(XEP_0138)
 | 
			
		||||
@@ -96,3 +96,4 @@ class XEP_0202(BasePlugin):
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq.enable('entity_time')
 | 
			
		||||
        return iq.send(**iqargs)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,10 @@ class Device(object):
 | 
			
		||||
          request_fields
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, nodeId, fields={}):
 | 
			
		||||
    def __init__(self, nodeId, fields=None):
 | 
			
		||||
        if not fields:
 | 
			
		||||
            fields = {}
 | 
			
		||||
 | 
			
		||||
        self.nodeId = nodeId
 | 
			
		||||
        self.fields = fields # see fields described below
 | 
			
		||||
        # {'type':'numeric',
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ from slixmpp.plugins.base import BasePlugin
 | 
			
		||||
from slixmpp.plugins.xep_0323 import stanza
 | 
			
		||||
from slixmpp.plugins.xep_0323.stanza import Sensordata
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -108,7 +107,6 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    default_config = {
 | 
			
		||||
        'threaded': True
 | 
			
		||||
#        'session_db': None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
@@ -161,11 +159,11 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
        self.last_seqnr = 0
 | 
			
		||||
        self.seqnr_lock = Lock()
 | 
			
		||||
 | 
			
		||||
        ## For testning only
 | 
			
		||||
        ## For testing only
 | 
			
		||||
        self.test_authenticated_from = ""
 | 
			
		||||
 | 
			
		||||
    def post_init(self):
 | 
			
		||||
        """ Init complete. Register our features in Serivce discovery. """
 | 
			
		||||
        """ Init complete. Register our features in Service discovery. """
 | 
			
		||||
        BasePlugin.post_init(self)
 | 
			
		||||
        self.xmpp['xep_0030'].add_feature(Sensordata.namespace)
 | 
			
		||||
        self.xmpp['xep_0030'].set_items(node=Sensordata.namespace, items=tuple())
 | 
			
		||||
@@ -301,8 +299,6 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
            self.sessions[session]["commTimers"] = {}
 | 
			
		||||
            self.sessions[session]["nodeDone"] = {}
 | 
			
		||||
 | 
			
		||||
            #print("added session: " + str(self.sessions))
 | 
			
		||||
 | 
			
		||||
            iq = iq.reply()
 | 
			
		||||
            iq['accepted']['seqnr'] = seqnr
 | 
			
		||||
            if not request_delay_sec is None:
 | 
			
		||||
@@ -319,10 +315,8 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            if self.threaded:
 | 
			
		||||
                #print("starting thread")
 | 
			
		||||
                tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields, req_flags))
 | 
			
		||||
                tr_req.start()
 | 
			
		||||
                #print("started thread")
 | 
			
		||||
            else:
 | 
			
		||||
                self._threaded_node_request(session, process_fields, req_flags)
 | 
			
		||||
 | 
			
		||||
@@ -349,7 +343,6 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
        for node in self.sessions[session]["node_list"]:
 | 
			
		||||
            timer = TimerReset(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node))
 | 
			
		||||
            self.sessions[session]["commTimers"][node] = timer
 | 
			
		||||
            #print("Starting timer " + str(timer) + ", timeout: " + str(self.nodes[node]['commTimeout']))
 | 
			
		||||
            timer.start()
 | 
			
		||||
            self.nodes[node]['device'].request_fields(process_fields, flags=flags, session=session, callback=self._device_field_request_callback)
 | 
			
		||||
 | 
			
		||||
@@ -377,7 +370,6 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
            msg['failure']['done'] = 'true'
 | 
			
		||||
        msg.send()
 | 
			
		||||
        # The session is complete, delete it
 | 
			
		||||
        #print("del session " + session + " due to timeout")
 | 
			
		||||
        del self.sessions[session]
 | 
			
		||||
 | 
			
		||||
    def _event_delayed_req(self, session, process_fields, req_flags):
 | 
			
		||||
@@ -404,7 +396,7 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    def _all_nodes_done(self, session):
 | 
			
		||||
        """
 | 
			
		||||
        Checks wheter all devices are done replying to the readout.
 | 
			
		||||
        Checks whether all devices are done replying to the readout.
 | 
			
		||||
 | 
			
		||||
        Arguments:
 | 
			
		||||
            session         -- The request session id
 | 
			
		||||
@@ -448,7 +440,7 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
                                Error details when a request failed.
 | 
			
		||||
        """
 | 
			
		||||
        if not session in self.sessions:
 | 
			
		||||
            # This can happend if a session was deleted, like in a cancellation. Just drop the data.
 | 
			
		||||
            # This can happen if a session was deleted, like in a cancellation. Just drop the data.
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if result == "error":
 | 
			
		||||
@@ -467,7 +459,6 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
            if (self._all_nodes_done(session)):
 | 
			
		||||
                msg['failure']['done'] = 'true'
 | 
			
		||||
                # The session is complete, delete it
 | 
			
		||||
                # print("del session " + session + " due to error")
 | 
			
		||||
                del self.sessions[session]
 | 
			
		||||
            msg.send()
 | 
			
		||||
        else:
 | 
			
		||||
@@ -491,11 +482,10 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
            if result == "done":
 | 
			
		||||
                self.sessions[session]["commTimers"][nodeId].cancel()
 | 
			
		||||
                self.sessions[session]["nodeDone"][nodeId] = True
 | 
			
		||||
                msg['fields']['done'] = 'true'
 | 
			
		||||
                if (self._all_nodes_done(session)):
 | 
			
		||||
                    # The session is complete, delete it
 | 
			
		||||
                    # print("del session " + session + " due to complete")
 | 
			
		||||
                    del self.sessions[session]
 | 
			
		||||
                    msg['fields']['done'] = 'true'
 | 
			
		||||
            else:
 | 
			
		||||
                # Restart comm timer
 | 
			
		||||
                self.sessions[session]["commTimers"][nodeId].reset()
 | 
			
		||||
@@ -531,19 +521,19 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
        iq['rejected']['error'] = "Cancel request received, no matching request is active."
 | 
			
		||||
        iq.send()
 | 
			
		||||
 | 
			
		||||
    # =================================================================
 | 
			
		||||
        # =================================================================
 | 
			
		||||
    # Client side (data retriever) API
 | 
			
		||||
 | 
			
		||||
    def request_data(self, from_jid, to_jid, callback, nodeIds=None, fields=None, flags=None):
 | 
			
		||||
        """
 | 
			
		||||
        Called on the client side to initiade a data readout.
 | 
			
		||||
        Called on the client side to initiate a data readout.
 | 
			
		||||
        Composes a message with the request and sends it to the device(s).
 | 
			
		||||
        Does not block, the callback will be called when data is available.
 | 
			
		||||
 | 
			
		||||
        Arguments:
 | 
			
		||||
            from_jid        -- The jid of the requester
 | 
			
		||||
            to_jid          -- The jid of the device(s)
 | 
			
		||||
            callback        -- The callback function to call when data is availble.
 | 
			
		||||
            callback        -- The callback function to call when data is available.
 | 
			
		||||
 | 
			
		||||
                            The callback function must support the following arguments:
 | 
			
		||||
 | 
			
		||||
@@ -636,7 +626,7 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
    def _get_new_seqnr(self):
 | 
			
		||||
        """ Returns a unique sequence number (unique across threads) """
 | 
			
		||||
        self.seqnr_lock.acquire()
 | 
			
		||||
        self.last_seqnr = self.last_seqnr + 1
 | 
			
		||||
        self.last_seqnr += 1
 | 
			
		||||
        self.seqnr_lock.release()
 | 
			
		||||
        return str(self.last_seqnr)
 | 
			
		||||
 | 
			
		||||
@@ -664,7 +654,6 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
        Received Iq with cancelled - this is a cancel confirm.
 | 
			
		||||
        Delete the session.
 | 
			
		||||
        """
 | 
			
		||||
        #print("Got cancelled")
 | 
			
		||||
        seqnr = iq['cancelled']['seqnr']
 | 
			
		||||
        callback = self.sessions[seqnr]["callback"]
 | 
			
		||||
        callback(from_jid=iq['from'], result="cancelled")
 | 
			
		||||
@@ -673,7 +662,7 @@ class XEP_0323(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    def _handle_event_fields(self, msg):
 | 
			
		||||
        """
 | 
			
		||||
        Received Msg with fields - this is a data reponse to a request.
 | 
			
		||||
        Received Msg with fields - this is a data response to a request.
 | 
			
		||||
        If this is the last data block, issue a "done" callback.
 | 
			
		||||
        """
 | 
			
		||||
        seqnr = msg['fields']['seqnr']
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,12 @@ class _TimerReset(Thread):
 | 
			
		||||
    t.cancel() # stop the timer's action if it's still waiting
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, interval, function, args=[], kwargs={}):
 | 
			
		||||
    def __init__(self, interval, function, args=None, kwargs=None):
 | 
			
		||||
        if not kwargs:
 | 
			
		||||
            kwargs = {}
 | 
			
		||||
        if not args:
 | 
			
		||||
            args = []
 | 
			
		||||
 | 
			
		||||
        Thread.__init__(self)
 | 
			
		||||
        self.interval = interval
 | 
			
		||||
        self.function = function
 | 
			
		||||
 
 | 
			
		||||
@@ -223,7 +223,6 @@ class XEP_0325(BasePlugin):
 | 
			
		||||
            error_msg = "Access denied"
 | 
			
		||||
 | 
			
		||||
        # Nodes
 | 
			
		||||
        process_nodes = []
 | 
			
		||||
        if len(iq['set']['nodes']) > 0:
 | 
			
		||||
            for n in iq['set']['nodes']:
 | 
			
		||||
                if not n['nodeId'] in self.nodes:
 | 
			
		||||
@@ -286,7 +285,6 @@ class XEP_0325(BasePlugin):
 | 
			
		||||
        req_ok = True
 | 
			
		||||
 | 
			
		||||
        # Nodes
 | 
			
		||||
        process_nodes = []
 | 
			
		||||
        if len(msg['set']['nodes']) > 0:
 | 
			
		||||
            for n in msg['set']['nodes']:
 | 
			
		||||
                if not n['nodeId'] in self.nodes:
 | 
			
		||||
@@ -548,4 +546,3 @@ class XEP_0325(BasePlugin):
 | 
			
		||||
        callback = self.sessions[seqnr]["callback"]
 | 
			
		||||
        callback(from_jid=from_jid, result=result, nodeIds=nodeIds, fields=fields, error_msg=error_msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								slixmpp/plugins/xep_0332/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								slixmpp/plugins/xep_0332/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Implementation of HTTP over XMPP transport
 | 
			
		||||
    http://xmpp.org/extensions/xep-0332.html
 | 
			
		||||
    Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from slixmpp.plugins.xep_0332 import stanza
 | 
			
		||||
from slixmpp.plugins.xep_0332.http import XEP_0332
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0332)
 | 
			
		||||
							
								
								
									
										159
									
								
								slixmpp/plugins/xep_0332/http.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								slixmpp/plugins/xep_0332/http.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Implementation of HTTP over XMPP transport
 | 
			
		||||
    http://xmpp.org/extensions/xep-0332.html
 | 
			
		||||
    Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from slixmpp import Iq
 | 
			
		||||
 | 
			
		||||
from slixmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from slixmpp.xmlstream.handler import Callback
 | 
			
		||||
from slixmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
 | 
			
		||||
from slixmpp.plugins.base import BasePlugin
 | 
			
		||||
from slixmpp.plugins.xep_0332.stanza import (
 | 
			
		||||
    HTTPRequest, HTTPResponse, HTTPData
 | 
			
		||||
)
 | 
			
		||||
from slixmpp.plugins.xep_0131.stanza import Headers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0332(BasePlugin):
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0332: HTTP over XMPP transport
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0332'
 | 
			
		||||
    description = 'XEP-0332: HTTP over XMPP transport'
 | 
			
		||||
 | 
			
		||||
    #: xep_0047 not included.
 | 
			
		||||
    #: xep_0001, 0137 and 0166 are missing
 | 
			
		||||
    dependencies = set(['xep_0030', 'xep_0131'])
 | 
			
		||||
 | 
			
		||||
    #: TODO: Do we really need to mention the supported_headers?!
 | 
			
		||||
    default_config = {
 | 
			
		||||
        'supported_headers': set([
 | 
			
		||||
            'Content-Length', 'Transfer-Encoding', 'DateTime',
 | 
			
		||||
            'Accept-Charset', 'Location', 'Content-ID', 'Description',
 | 
			
		||||
            'Content-Language', 'Content-Transfer-Encoding', 'Timestamp',
 | 
			
		||||
            'Expires', 'User-Agent', 'Host', 'Proxy-Authorization', 'Date',
 | 
			
		||||
            'WWW-Authenticate', 'Accept-Encoding', 'Server', 'Error-Info',
 | 
			
		||||
            'Identifier', 'Content-Location', 'Content-Encoding', 'Distribute',
 | 
			
		||||
            'Accept', 'Proxy-Authenticate', 'ETag', 'Expect', 'Content-Type'
 | 
			
		||||
        ])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
            Callback(
 | 
			
		||||
                'HTTP Request',
 | 
			
		||||
                StanzaPath('iq/http-req'),
 | 
			
		||||
                self._handle_request
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
            Callback(
 | 
			
		||||
                'HTTP Response',
 | 
			
		||||
                StanzaPath('iq/http-resp'),
 | 
			
		||||
                self._handle_response
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        register_stanza_plugin(Iq, HTTPRequest, iterable=True)
 | 
			
		||||
        register_stanza_plugin(Iq, HTTPResponse, iterable=True)
 | 
			
		||||
        register_stanza_plugin(HTTPRequest, Headers, iterable=True)
 | 
			
		||||
        register_stanza_plugin(HTTPRequest, HTTPData, iterable=True)
 | 
			
		||||
        register_stanza_plugin(HTTPResponse, Headers, iterable=True)
 | 
			
		||||
        register_stanza_plugin(HTTPResponse, HTTPData, iterable=True)
 | 
			
		||||
        # TODO: Should we register any api's here? self.api.register()
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp.remove_handler('HTTP Request')
 | 
			
		||||
        self.xmpp.remove_handler('HTTP Response')
 | 
			
		||||
        self.xmpp['xep_0030'].del_feature('urn:xmpp:http')
 | 
			
		||||
        for header in self.supported_headers:
 | 
			
		||||
            self.xmpp['xep_0030'].del_feature(
 | 
			
		||||
                feature='%s#%s' % (Headers.namespace, header)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp['xep_0030'].add_feature('urn:xmpp:http')
 | 
			
		||||
        for header in self.supported_headers:
 | 
			
		||||
            self.xmpp['xep_0030'].add_feature(
 | 
			
		||||
                '%s#%s' % (Headers.namespace, header)
 | 
			
		||||
            )
 | 
			
		||||
            # TODO: Do we need to add the supported headers to xep_0131?
 | 
			
		||||
            # self.xmpp['xep_0131'].supported_headers.add(header)
 | 
			
		||||
 | 
			
		||||
    def _handle_request(self, iq):
 | 
			
		||||
        self.xmpp.event('http_request', iq)
 | 
			
		||||
 | 
			
		||||
    def _handle_response(self, iq):
 | 
			
		||||
        self.xmpp.event('http_response', iq)
 | 
			
		||||
 | 
			
		||||
    def send_request(self, to=None, method=None, resource=None, headers=None,
 | 
			
		||||
                     data=None, **kwargs):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['from'] = self.xmpp.boundjid
 | 
			
		||||
        iq['to'] = to
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['http-req']['headers'] = headers
 | 
			
		||||
        iq['http-req']['method'] = method
 | 
			
		||||
        iq['http-req']['resource'] = resource
 | 
			
		||||
        iq['http-req']['version'] = '1.1'        # TODO: set this implicitly
 | 
			
		||||
        if 'id' in kwargs:
 | 
			
		||||
            iq['id'] = kwargs["id"]
 | 
			
		||||
        if data is not None:
 | 
			
		||||
            iq['http-req']['data'] = data
 | 
			
		||||
        return iq.send(
 | 
			
		||||
            timeout=kwargs.get('timeout', None),
 | 
			
		||||
            block=kwargs.get('block', True),
 | 
			
		||||
            callback=kwargs.get('callback', None),
 | 
			
		||||
            timeout_callback=kwargs.get('timeout_callback', None)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def send_response(self, to=None, code=None, message=None, headers=None,
 | 
			
		||||
                      data=None, **kwargs):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['from'] = self.xmpp.boundjid
 | 
			
		||||
        iq['to'] = to
 | 
			
		||||
        iq['type'] = 'result'
 | 
			
		||||
        iq['http-resp']['headers'] = headers
 | 
			
		||||
        iq['http-resp']['code'] = code
 | 
			
		||||
        iq['http-resp']['message'] = message
 | 
			
		||||
        iq['http-resp']['version'] = '1.1'       # TODO: set this implicitly
 | 
			
		||||
        if 'id' in kwargs:
 | 
			
		||||
            iq['id'] = kwargs["id"]
 | 
			
		||||
        if data is not None:
 | 
			
		||||
            iq['http-resp']['data'] = data
 | 
			
		||||
        return iq.send(
 | 
			
		||||
            timeout=kwargs.get('timeout', None),
 | 
			
		||||
            block=kwargs.get('block', True),
 | 
			
		||||
            callback=kwargs.get('callback', None),
 | 
			
		||||
            timeout_callback=kwargs.get('timeout_callback', None)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def send_error(self, to=None, ecode='500', etype='wait',
 | 
			
		||||
                   econd='internal-server-error', **kwargs):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['from'] = self.xmpp.boundjid
 | 
			
		||||
        iq['to'] = to
 | 
			
		||||
        iq['type'] = 'error'
 | 
			
		||||
        iq['error']['code'] = ecode
 | 
			
		||||
        iq['error']['type'] = etype
 | 
			
		||||
        iq['error']['condition'] = econd
 | 
			
		||||
        if 'id' in kwargs:
 | 
			
		||||
            iq['id'] = kwargs["id"]
 | 
			
		||||
        return iq.send(
 | 
			
		||||
            timeout=kwargs.get('timeout', None),
 | 
			
		||||
            block=kwargs.get('block', True),
 | 
			
		||||
            callback=kwargs.get('callback', None),
 | 
			
		||||
            timeout_callback=kwargs.get('timeout_callback', None)
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										13
									
								
								slixmpp/plugins/xep_0332/stanza/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								slixmpp/plugins/xep_0332/stanza/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Implementation of HTTP over XMPP transport
 | 
			
		||||
    http://xmpp.org/extensions/xep-0332.html
 | 
			
		||||
    Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.plugins.xep_0332.stanza.request import HTTPRequest
 | 
			
		||||
from slixmpp.plugins.xep_0332.stanza.response import HTTPResponse
 | 
			
		||||
from slixmpp.plugins.xep_0332.stanza.data import HTTPData
 | 
			
		||||
							
								
								
									
										30
									
								
								slixmpp/plugins/xep_0332/stanza/data.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								slixmpp/plugins/xep_0332/stanza/data.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Implementation of HTTP over XMPP transport
 | 
			
		||||
    http://xmpp.org/extensions/xep-0332.html
 | 
			
		||||
    Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPData(ElementBase):
 | 
			
		||||
    """
 | 
			
		||||
    The data element.
 | 
			
		||||
    """
 | 
			
		||||
    name = 'data'
 | 
			
		||||
    namespace = 'urn:xmpp:http'
 | 
			
		||||
    interfaces = set(['data'])
 | 
			
		||||
    plugin_attrib = 'data'
 | 
			
		||||
    is_extension = True
 | 
			
		||||
 | 
			
		||||
    def get_data(self, encoding='text'):
 | 
			
		||||
        data = self._get_sub_text(encoding, None)
 | 
			
		||||
        return str(data) if data is not None else data
 | 
			
		||||
 | 
			
		||||
    def set_data(self, data, encoding='text'):
 | 
			
		||||
        self._set_sub_text(encoding, text=data)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								slixmpp/plugins/xep_0332/stanza/request.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								slixmpp/plugins/xep_0332/stanza/request.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
"""
 | 
			
		||||
    slixmpp: The Slick XMPP Library
 | 
			
		||||
    Implementation of HTTP over XMPP transport
 | 
			
		||||
    http://xmpp.org/extensions/xep-0332.html
 | 
			
		||||
    Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPRequest(ElementBase):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    All HTTP communication is done using the `Request`/`Response` paradigm.
 | 
			
		||||
    Each HTTP Request is made sending an `iq` stanza containing a `req`
 | 
			
		||||
    element to the server. Each `iq` stanza sent is of type `set`.
 | 
			
		||||
 | 
			
		||||
    Examples:
 | 
			
		||||
    <iq type='set' from='a@b.com/browser' to='x@y.com' id='1'>
 | 
			
		||||
        <req xmlns='urn:xmpp:http'
 | 
			
		||||
             method='GET'
 | 
			
		||||
             resource='/api/users'
 | 
			
		||||
             version='1.1'>
 | 
			
		||||
            <headers xmlns='http://jabber.org/protocol/shim'>
 | 
			
		||||
                <header name='Host'>b.com</header>
 | 
			
		||||
            </headers>
 | 
			
		||||
        </req>
 | 
			
		||||
    </iq>
 | 
			
		||||
 | 
			
		||||
    <iq type='set' from='a@b.com/browser' to='x@y.com' id='2'>
 | 
			
		||||
        <req xmlns='urn:xmpp:http'
 | 
			
		||||
             method='PUT'
 | 
			
		||||
             resource='/api/users'
 | 
			
		||||
             version='1.1'>
 | 
			
		||||
            <headers xmlns='http://jabber.org/protocol/shim'>
 | 
			
		||||
                <header name='Host'>b.com</header>
 | 
			
		||||
                <header name='Content-Type'>text/html</header>
 | 
			
		||||
                <header name='Content-Length'>...</header>
 | 
			
		||||
            </headers>
 | 
			
		||||
            <data>
 | 
			
		||||
                <text>...</text>
 | 
			
		||||
            </data>
 | 
			
		||||
        </req>
 | 
			
		||||
    </iq>
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'request'
 | 
			
		||||
    namespace = 'urn:xmpp:http'
 | 
			
		||||
    interfaces = set(['method', 'resource', 'version'])
 | 
			
		||||
    plugin_attrib = 'http-req'
 | 
			
		||||
 | 
			
		||||
    def get_method(self):
 | 
			
		||||
        return self._get_attr('method', None)
 | 
			
		||||
 | 
			
		||||
    def set_method(self, method):
 | 
			
		||||
        self._set_attr('method', method)
 | 
			
		||||
 | 
			
		||||
    def get_resource(self):
 | 
			
		||||
        return self._get_attr('resource', None)
 | 
			
		||||
 | 
			
		||||
    def set_resource(self, resource):
 | 
			
		||||
        self._set_attr('resource', resource)
 | 
			
		||||
 | 
			
		||||
    def get_version(self):
 | 
			
		||||
        return self._get_attr('version', None)
 | 
			
		||||
 | 
			
		||||
    def set_version(self, version='1.1'):
 | 
			
		||||
        self._set_attr('version', version)
 | 
			
		||||
							
								
								
									
										66
									
								
								slixmpp/plugins/xep_0332/stanza/response.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								slixmpp/plugins/xep_0332/stanza/response.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
"""
 | 
			
		||||
    Slixmpp: The Slick XMPP Library
 | 
			
		||||
    Implementation of HTTP over XMPP transport
 | 
			
		||||
    http://xmpp.org/extensions/xep-0332.html
 | 
			
		||||
    Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
 | 
			
		||||
    This file is part of slixmpp.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from slixmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPResponse(ElementBase):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    When the HTTP Server responds, it does so by sending an `iq` stanza
 | 
			
		||||
    response (type=`result`) back to the client containing the `resp` element.
 | 
			
		||||
    Since response are asynchronous, and since multiple requests may be active
 | 
			
		||||
    at the same time, responses may be returned in a different order than the
 | 
			
		||||
    in which the original requests were made.
 | 
			
		||||
 | 
			
		||||
    Examples:
 | 
			
		||||
    <iq type='result'
 | 
			
		||||
        from='httpserver@clayster.com'
 | 
			
		||||
        to='httpclient@clayster.com/browser' id='2'>
 | 
			
		||||
        <resp xmlns='urn:xmpp:http'
 | 
			
		||||
              version='1.1'
 | 
			
		||||
              statusCode='200'
 | 
			
		||||
              statusMessage='OK'>
 | 
			
		||||
            <headers xmlns='http://jabber.org/protocol/shim'>
 | 
			
		||||
                <header name='Date'>Fri, 03 May 2013 16:39:54GMT-4</header>
 | 
			
		||||
                <header name='Server'>Clayster</header>
 | 
			
		||||
                <header name='Content-Type'>text/turtle</header>
 | 
			
		||||
                <header name='Content-Length'>...</header>
 | 
			
		||||
                <header name='Connection'>Close</header>
 | 
			
		||||
            </headers>
 | 
			
		||||
            <data>
 | 
			
		||||
                <text>
 | 
			
		||||
                    ...
 | 
			
		||||
                </text>
 | 
			
		||||
            </data>
 | 
			
		||||
        </resp>
 | 
			
		||||
    </iq>
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'response'
 | 
			
		||||
    namespace = 'urn:xmpp:http'
 | 
			
		||||
    interfaces = set(['code', 'message', 'version'])
 | 
			
		||||
    plugin_attrib = 'http-resp'
 | 
			
		||||
 | 
			
		||||
    def get_code(self):
 | 
			
		||||
        code = self._get_attr('statusCode', None)
 | 
			
		||||
        return int(code) if code is not None else code
 | 
			
		||||
 | 
			
		||||
    def set_code(self, code):
 | 
			
		||||
        self._set_attr('statusCode', str(code))
 | 
			
		||||
 | 
			
		||||
    def get_message(self):
 | 
			
		||||
        return self._get_attr('statusMessage', '')
 | 
			
		||||
 | 
			
		||||
    def set_message(self, message):
 | 
			
		||||
        self._set_attr('statusMessage', message)
 | 
			
		||||
 | 
			
		||||
    def set_version(self, version='1.1'):
 | 
			
		||||
        self._set_attr('version', version)
 | 
			
		||||
		Reference in New Issue
	
	Block a user