summaryrefslogtreecommitdiff
path: root/sleekxmpp
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp')
-rw-r--r--sleekxmpp/plugins/xep_0323/device.py378
-rw-r--r--sleekxmpp/plugins/xep_0323/stanza/sensordata.py20
-rw-r--r--sleekxmpp/plugins/xep_0325/device.py136
-rw-r--r--sleekxmpp/plugins/xep_0325/stanza/control.py12
-rw-r--r--sleekxmpp/stanza/iq.py15
-rw-r--r--sleekxmpp/test/sleektest.py3
-rw-r--r--sleekxmpp/util/sasl/mechanisms.py2
-rw-r--r--sleekxmpp/version.py4
-rw-r--r--sleekxmpp/xmlstream/matcher/__init__.py1
-rw-r--r--sleekxmpp/xmlstream/matcher/idsender.py47
10 files changed, 337 insertions, 281 deletions
diff --git a/sleekxmpp/plugins/xep_0323/device.py b/sleekxmpp/plugins/xep_0323/device.py
index 23bdd153..0bc20327 100644
--- a/sleekxmpp/plugins/xep_0323/device.py
+++ b/sleekxmpp/plugins/xep_0323/device.py
@@ -12,51 +12,51 @@ import datetime
import logging
class Device(object):
- """
- Example implementation of a device readout object.
+ """
+ Example implementation of a device readout object.
Is registered in the XEP_0323.register_node call
The device object may be any custom implementation to support
specific devices, but it must implement the functions:
has_field
request_fields
- """
-
- def __init__(self, nodeId, fields={}):
- self.nodeId = nodeId
- self.fields = fields # see fields described below
- # {'type':'numeric',
- # 'name':'myname',
- # 'value': 42,
- # 'unit':'Z'}];
- self.timestamp_data = {}
- self.momentary_data = {}
- self.momentary_timestamp = ""
- logging.debug("Device object started nodeId %s",nodeId)
-
- def has_field(self, field):
- """
- Returns true if the supplied field name exists in this device.
+ """
+
+ def __init__(self, nodeId, fields={}):
+ self.nodeId = nodeId
+ self.fields = fields # see fields described below
+ # {'type':'numeric',
+ # 'name':'myname',
+ # 'value': 42,
+ # 'unit':'Z'}];
+ self.timestamp_data = {}
+ self.momentary_data = {}
+ self.momentary_timestamp = ""
+ logging.debug("Device object started nodeId %s",nodeId)
+
+ def has_field(self, field):
+ """
+ Returns true if the supplied field name exists in this device.
Arguments:
- field -- The field name
- """
- if field in self.fields.keys():
- return True;
- return False;
-
- def refresh(self, fields):
- """
- override method to do the refresh work
- refresh values from hardware or other
- """
- pass
-
-
- def request_fields(self, fields, flags, session, callback):
- """
- Starts a data readout. Verifies the requested fields,
- refreshes the data (if needed) and calls the callback
- with requested data.
+ field -- The field name
+ """
+ if field in self.fields.keys():
+ return True;
+ return False;
+
+ def refresh(self, fields):
+ """
+ override method to do the refresh work
+ refresh values from hardware or other
+ """
+ pass
+
+
+ def request_fields(self, fields, flags, session, callback):
+ """
+ Starts a data readout. Verifies the requested fields,
+ refreshes the data (if needed) and calls the callback
+ with requested data.
Arguments:
@@ -65,153 +65,153 @@ class Device(object):
Formatted as a dictionary like { "flag name": "flag value" ... }
session -- Session id, only used in the callback as identifier
callback -- Callback function to call when data is available.
-
- The callback function must support the following arguments:
+
+ The callback function must support the following arguments:
- session -- Session id, as supplied in the request_fields call
- nodeId -- Identifier for this device
- result -- The current result status of the readout. Valid values are:
+ session -- Session id, as supplied in the request_fields call
+ nodeId -- Identifier for this device
+ result -- The current result status of the readout. Valid values are:
"error" - Readout failed.
"fields" - Contains readout data.
"done" - Indicates that the readout is complete. May contain
readout data.
- timestamp_block -- [optional] Only applies when result != "error"
+ timestamp_block -- [optional] Only applies when result != "error"
The readout data. Structured as a dictionary:
- {
- timestamp: timestamp for this datablock,
- fields: list of field dictionary (one per readout field).
- readout field dictionary format:
- {
- type: The field type (numeric, boolean, dateTime, timeSpan, string, enum)
- name: The field name
- value: The field value
- unit: The unit of the field. Only applies to type numeric.
- dataType: The datatype of the field. Only applies to type enum.
- flags: [optional] data classifier flags for the field, e.g. momentary
+ {
+ timestamp: timestamp for this datablock,
+ fields: list of field dictionary (one per readout field).
+ readout field dictionary format:
+ {
+ type: The field type (numeric, boolean, dateTime, timeSpan, string, enum)
+ name: The field name
+ value: The field value
+ unit: The unit of the field. Only applies to type numeric.
+ dataType: The datatype of the field. Only applies to type enum.
+ flags: [optional] data classifier flags for the field, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... }
- }
- }
- error_msg -- [optional] Only applies when result == "error".
+ }
+ }
+ error_msg -- [optional] Only applies when result == "error".
Error details when a request failed.
- """
- logging.debug("request_fields called looking for fields %s",fields)
- if len(fields) > 0:
- # Check availiability
- for f in fields:
- if f not in self.fields.keys():
- self._send_reject(session, callback)
- return False;
- else:
- # Request all fields
- fields = self.fields.keys();
-
-
- # Refresh data from device
- # ...
- logging.debug("about to refresh device fields %s",fields)
- self.refresh(fields)
-
- if "momentary" in flags and flags['momentary'] == "true" or \
- "all" in flags and flags['all'] == "true":
- ts_block = {};
- timestamp = "";
-
- if len(self.momentary_timestamp) > 0:
- timestamp = self.momentary_timestamp;
- else:
- timestamp = self._get_timestamp();
-
- field_block = [];
- for f in self.momentary_data:
- if f in fields:
- field_block.append({"name": f,
- "type": self.fields[f]["type"],
- "unit": self.fields[f]["unit"],
- "dataType": self.fields[f]["dataType"],
- "value": self.momentary_data[f]["value"],
- "flags": self.momentary_data[f]["flags"]});
- ts_block["timestamp"] = timestamp;
- ts_block["fields"] = field_block;
-
- callback(session, result="done", nodeId=self.nodeId, timestamp_block=ts_block);
- return
-
- from_flag = self._datetime_flag_parser(flags, 'from')
- to_flag = self._datetime_flag_parser(flags, 'to')
-
- for ts in sorted(self.timestamp_data.keys()):
- tsdt = datetime.datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S")
- if not from_flag is None:
- if tsdt < from_flag:
- #print (str(tsdt) + " < " + str(from_flag))
- continue
- if not to_flag is None:
- if tsdt > to_flag:
- #print (str(tsdt) + " > " + str(to_flag))
- continue
-
- ts_block = {};
- field_block = [];
-
- for f in self.timestamp_data[ts]:
- if f in fields:
- field_block.append({"name": f,
- "type": self.fields[f]["type"],
- "unit": self.fields[f]["unit"],
- "dataType": self.fields[f]["dataType"],
- "value": self.timestamp_data[ts][f]["value"],
- "flags": self.timestamp_data[ts][f]["flags"]});
-
- ts_block["timestamp"] = ts;
- ts_block["fields"] = field_block;
- callback(session, result="fields", nodeId=self.nodeId, timestamp_block=ts_block);
- callback(session, result="done", nodeId=self.nodeId, timestamp_block=None);
-
- def _datetime_flag_parser(self, flags, flagname):
- if not flagname in flags:
- return None
-
- dt = None
- try:
- dt = datetime.datetime.strptime(flags[flagname], "%Y-%m-%dT%H:%M:%S")
- except ValueError:
- # Badly formatted datetime, ignore it
- pass
- return dt
-
-
- def _get_timestamp(self):
- """
- Generates a properly formatted timestamp of current time
- """
- return datetime.datetime.now().replace(microsecond=0).isoformat()
-
- def _send_reject(self, session, callback):
- """
- Sends a reject to the caller
+ """
+ logging.debug("request_fields called looking for fields %s",fields)
+ if len(fields) > 0:
+ # Check availiability
+ for f in fields:
+ if f not in self.fields.keys():
+ self._send_reject(session, callback)
+ return False;
+ else:
+ # Request all fields
+ fields = self.fields.keys();
+
+
+ # Refresh data from device
+ # ...
+ logging.debug("about to refresh device fields %s",fields)
+ self.refresh(fields)
+
+ if "momentary" in flags and flags['momentary'] == "true" or \
+ "all" in flags and flags['all'] == "true":
+ ts_block = {};
+ timestamp = "";
+
+ if len(self.momentary_timestamp) > 0:
+ timestamp = self.momentary_timestamp;
+ else:
+ timestamp = self._get_timestamp();
+
+ field_block = [];
+ for f in self.momentary_data:
+ if f in fields:
+ field_block.append({"name": f,
+ "type": self.fields[f]["type"],
+ "unit": self.fields[f]["unit"],
+ "dataType": self.fields[f]["dataType"],
+ "value": self.momentary_data[f]["value"],
+ "flags": self.momentary_data[f]["flags"]});
+ ts_block["timestamp"] = timestamp;
+ ts_block["fields"] = field_block;
+
+ callback(session, result="done", nodeId=self.nodeId, timestamp_block=ts_block);
+ return
+
+ from_flag = self._datetime_flag_parser(flags, 'from')
+ to_flag = self._datetime_flag_parser(flags, 'to')
+
+ for ts in sorted(self.timestamp_data.keys()):
+ tsdt = datetime.datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S")
+ if not from_flag is None:
+ if tsdt < from_flag:
+ #print (str(tsdt) + " < " + str(from_flag))
+ continue
+ if not to_flag is None:
+ if tsdt > to_flag:
+ #print (str(tsdt) + " > " + str(to_flag))
+ continue
+
+ ts_block = {};
+ field_block = [];
+
+ for f in self.timestamp_data[ts]:
+ if f in fields:
+ field_block.append({"name": f,
+ "type": self.fields[f]["type"],
+ "unit": self.fields[f]["unit"],
+ "dataType": self.fields[f]["dataType"],
+ "value": self.timestamp_data[ts][f]["value"],
+ "flags": self.timestamp_data[ts][f]["flags"]});
+
+ ts_block["timestamp"] = ts;
+ ts_block["fields"] = field_block;
+ callback(session, result="fields", nodeId=self.nodeId, timestamp_block=ts_block);
+ callback(session, result="done", nodeId=self.nodeId, timestamp_block=None);
+
+ def _datetime_flag_parser(self, flags, flagname):
+ if not flagname in flags:
+ return None
+
+ dt = None
+ try:
+ dt = datetime.datetime.strptime(flags[flagname], "%Y-%m-%dT%H:%M:%S")
+ except ValueError:
+ # Badly formatted datetime, ignore it
+ pass
+ return dt
+
+
+ def _get_timestamp(self):
+ """
+ Generates a properly formatted timestamp of current time
+ """
+ return datetime.datetime.now().replace(microsecond=0).isoformat()
+
+ def _send_reject(self, session, callback):
+ """
+ Sends a reject to the caller
Arguments:
session -- Session id, see definition in request_fields function
callback -- Callback function, see definition in request_fields function
- """
- callback(session, result="error", nodeId=self.nodeId, timestamp_block=None, error_msg="Reject");
+ """
+ callback(session, result="error", nodeId=self.nodeId, timestamp_block=None, error_msg="Reject");
- def _add_field(self, name, typename, unit=None, dataType=None):
- """
- Adds a field to the device
+ def _add_field(self, name, typename, unit=None, dataType=None):
+ """
+ Adds a field to the device
Arguments:
name -- Name of the field
typename -- Type of the field (numeric, boolean, dateTime, timeSpan, string, enum)
unit -- [optional] only applies to "numeric". Unit for the field.
dataType -- [optional] only applies to "enum". Datatype for the field.
- """
- self.fields[name] = {"type": typename, "unit": unit, "dataType": dataType};
+ """
+ self.fields[name] = {"type": typename, "unit": unit, "dataType": dataType};
- def _add_field_timestamp_data(self, name, timestamp, value, flags=None):
- """
- Adds timestamped data to a field
+ def _add_field_timestamp_data(self, name, timestamp, value, flags=None):
+ """
+ Adds timestamped data to a field
Arguments:
name -- Name of the field
@@ -219,37 +219,37 @@ class Device(object):
value -- Field value at the timestamp
flags -- [optional] data classifier flags for the field, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... }
- """
- if not name in self.fields.keys():
- return False;
- if not timestamp in self.timestamp_data:
- self.timestamp_data[timestamp] = {};
+ """
+ if not name in self.fields.keys():
+ return False;
+ if not timestamp in self.timestamp_data:
+ self.timestamp_data[timestamp] = {};
- self.timestamp_data[timestamp][name] = {"value": value, "flags": flags};
- return True;
+ self.timestamp_data[timestamp][name] = {"value": value, "flags": flags};
+ return True;
- def _add_field_momentary_data(self, name, value, flags=None):
- """
- Sets momentary data to a field
+ def _add_field_momentary_data(self, name, value, flags=None):
+ """
+ Sets momentary data to a field
Arguments:
name -- Name of the field
value -- Field value at the timestamp
flags -- [optional] data classifier flags for the field, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... }
- """
- if not self.fields.has_key(name):
- return False;
- if flags is None:
- flags = {};
-
- flags["momentary"] = "true"
- self.momentary_data[name] = {"value": value, "flags": flags};
- return True;
-
- def _set_momentary_timestamp(self, timestamp):
- """
- This function is only for unit testing to produce predictable results.
- """
- self.momentary_timestamp = timestamp;
+ """
+ if name not in self.fields:
+ return False;
+ if flags is None:
+ flags = {};
+
+ flags["momentary"] = "true"
+ self.momentary_data[name] = {"value": value, "flags": flags};
+ return True;
+
+ def _set_momentary_timestamp(self, timestamp):
+ """
+ This function is only for unit testing to produce predictable results.
+ """
+ self.momentary_timestamp = timestamp;
diff --git a/sleekxmpp/plugins/xep_0323/stanza/sensordata.py b/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
index 55e5060f..a11c3e94 100644
--- a/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
+++ b/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
@@ -124,10 +124,10 @@ class Request(ElementBase):
def get_nodes(self):
"""Return all nodes."""
- nodes = set()
+ nodes = []
for node in self['substanzas']:
if isinstance(node, RequestNode):
- nodes.add(node)
+ nodes.append(node)
return nodes
def set_nodes(self, nodes):
@@ -190,10 +190,10 @@ class Request(ElementBase):
def get_fields(self):
"""Return all fields."""
- fields = set()
+ fields = []
for field in self['substanzas']:
if isinstance(field, RequestField):
- fields.add(field)
+ fields.append(field)
return fields
def set_fields(self, fields):
@@ -351,10 +351,10 @@ class Fields(ElementBase):
def get_nodes(self):
"""Return all nodes."""
- nodes = set()
+ nodes = []
for node in self['substanzas']:
if isinstance(node, FieldsNode):
- nodes.add(node)
+ nodes.append(node)
return nodes
def set_nodes(self, nodes):
@@ -450,10 +450,10 @@ class FieldsNode(ElementBase):
def get_timestamps(self):
"""Return all timestamps."""
#print(str(id(self)) + " get_timestamps: ")
- timestamps = set()
+ timestamps = []
for timestamp in self['substanzas']:
if isinstance(timestamp, Timestamp):
- timestamps.add(timestamp)
+ timestamps.append(timestamp)
return timestamps
def set_timestamps(self, timestamps):
@@ -634,10 +634,10 @@ class Timestamp(ElementBase):
def get_datas(self):
""" Return all data elements. """
- datas = set()
+ datas = []
for data in self['substanzas']:
if isinstance(data, Field):
- datas.add(data)
+ datas.append(data)
return datas
def set_datas(self, datas):
diff --git a/sleekxmpp/plugins/xep_0325/device.py b/sleekxmpp/plugins/xep_0325/device.py
index 1cb99510..a60d5f9a 100644
--- a/sleekxmpp/plugins/xep_0325/device.py
+++ b/sleekxmpp/plugins/xep_0325/device.py
@@ -11,36 +11,36 @@
import datetime
class Device(object):
- """
- Example implementation of a device control object.
+ """
+ Example implementation of a device control object.
The device object may by any custom implementation to support
specific devices, but it must implement the functions:
has_control_field
set_control_fields
- """
+ """
- def __init__(self, nodeId):
- self.nodeId = nodeId;
- self.control_fields = {};
+ def __init__(self, nodeId):
+ self.nodeId = nodeId;
+ self.control_fields = {};
- def has_control_field(self, field, typename):
- """
- Returns true if the supplied field name exists
- and the type matches for control in this device.
+ def has_control_field(self, field, typename):
+ """
+ Returns true if the supplied field name exists
+ and the type matches for control in this device.
Arguments:
- field -- The field name
+ field -- The field name
typename -- The expected type
- """
- if field in self.control_fields and self.control_fields[field]["type"] == typename:
- return True;
- return False;
+ """
+ if field in self.control_fields and self.control_fields[field]["type"] == typename:
+ return True;
+ return False;
- def set_control_fields(self, fields, session, callback):
- """
- Starts a control setting procedure. Verifies the fields,
- sets the data and (if needed) and calls the callback.
+ def set_control_fields(self, fields, session, callback):
+ """
+ Starts a control setting procedure. Verifies the fields,
+ sets the data and (if needed) and calls the callback.
Arguments:
fields -- List of control fields in tuple format:
@@ -48,50 +48,50 @@ class Device(object):
session -- Session id, only used in the callback as identifier
callback -- Callback function to call when control set is complete.
- The callback function must support the following arguments:
+ The callback function must support the following arguments:
- session -- Session id, as supplied in the
- request_fields call
- nodeId -- Identifier for this device
- result -- The current result status of the readout.
- Valid values are:
+ session -- Session id, as supplied in the
+ request_fields call
+ nodeId -- Identifier for this device
+ result -- The current result status of the readout.
+ Valid values are:
"error" - Set fields failed.
"ok" - All fields were set.
- error_field -- [optional] Only applies when result == "error"
- The field name that failed
- (usually means it is missing)
- error_msg -- [optional] Only applies when result == "error".
- Error details when a request failed.
- """
-
- if len(fields) > 0:
- # Check availiability
- for name, typename, value in fields:
- if not self.has_control_field(name, typename):
- self._send_control_reject(session, name, "NotFound", callback)
- return False;
-
- for name, typename, value in fields:
- self._set_field_value(name, value)
-
- callback(session, result="ok", nodeId=self.nodeId);
- return True
-
- def _send_control_reject(self, session, field, message, callback):
- """
- Sends a reject to the caller
+ error_field -- [optional] Only applies when result == "error"
+ The field name that failed
+ (usually means it is missing)
+ error_msg -- [optional] Only applies when result == "error".
+ Error details when a request failed.
+ """
+
+ if len(fields) > 0:
+ # Check availiability
+ for name, typename, value in fields:
+ if not self.has_control_field(name, typename):
+ self._send_control_reject(session, name, "NotFound", callback)
+ return False;
+
+ for name, typename, value in fields:
+ self._set_field_value(name, value)
+
+ callback(session, result="ok", nodeId=self.nodeId);
+ return True
+
+ def _send_control_reject(self, session, field, message, callback):
+ """
+ Sends a reject to the caller
Arguments:
session -- Session id, see definition in
set_control_fields function
callback -- Callback function, see definition in
set_control_fields function
- """
- callback(session, result="error", nodeId=self.nodeId, error_field=field, error_msg=message);
+ """
+ callback(session, result="error", nodeId=self.nodeId, error_field=field, error_msg=message);
- def _add_control_field(self, name, typename, value):
- """
- Adds a control field to the device
+ def _add_control_field(self, name, typename, value):
+ """
+ Adds a control field to the device
Arguments:
name -- Name of the field
@@ -99,27 +99,27 @@ class Device(object):
(boolean, color, string, date, dateTime,
double, duration, int, long, time)
value -- Field value
- """
- self.control_fields[name] = {"type": typename, "value": value};
+ """
+ self.control_fields[name] = {"type": typename, "value": value};
- def _set_field_value(self, name, value):
- """
- Set the value of a control field
+ def _set_field_value(self, name, value):
+ """
+ Set the value of a control field
Arguments:
name -- Name of the field
value -- New value for the field
- """
- if name in self.control_fields:
- self.control_fields[name]["value"] = value;
+ """
+ if name in self.control_fields:
+ self.control_fields[name]["value"] = value;
- def _get_field_value(self, name):
- """
- Get the value of a control field. Only used for unit testing.
+ def _get_field_value(self, name):
+ """
+ Get the value of a control field. Only used for unit testing.
Arguments:
name -- Name of the field
- """
- if name in self.control_fields:
- return self.control_fields[name]["value"];
- return None;
+ """
+ if name in self.control_fields:
+ return self.control_fields[name]["value"];
+ return None;
diff --git a/sleekxmpp/plugins/xep_0325/stanza/control.py b/sleekxmpp/plugins/xep_0325/stanza/control.py
index 2707191f..67107ecb 100644
--- a/sleekxmpp/plugins/xep_0325/stanza/control.py
+++ b/sleekxmpp/plugins/xep_0325/stanza/control.py
@@ -83,10 +83,10 @@ class ControlSet(ElementBase):
def get_nodes(self):
"""Return all nodes."""
- nodes = set()
+ nodes = []
for node in self['substanzas']:
if isinstance(node, RequestNode):
- nodes.add(node)
+ nodes.append(node)
return nodes
def set_nodes(self, nodes):
@@ -175,10 +175,10 @@ class ControlSet(ElementBase):
def get_datas(self):
""" Return all data elements. """
- datas = set()
+ datas = []
for data in self['substanzas']:
if isinstance(data, BaseParameter):
- datas.add(data)
+ datas.append(data)
return datas
def set_datas(self, datas):
@@ -274,10 +274,10 @@ class ControlSetResponse(ElementBase):
def get_nodes(self):
"""Return all nodes."""
- nodes = set()
+ nodes = []
for node in self['substanzas']:
if isinstance(node, RequestNode):
- nodes.add(node)
+ nodes.append(node)
return nodes
def set_nodes(self, nodes):
diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py
index ba945e08..e377b82f 100644
--- a/sleekxmpp/stanza/iq.py
+++ b/sleekxmpp/stanza/iq.py
@@ -9,7 +9,7 @@
from sleekxmpp.stanza.rootstanza import RootStanza
from sleekxmpp.xmlstream import StanzaBase, ET
from sleekxmpp.xmlstream.handler import Waiter, Callback
-from sleekxmpp.xmlstream.matcher import MatcherId
+from sleekxmpp.xmlstream.matcher import MatchIDSender
from sleekxmpp.exceptions import IqTimeout, IqError
@@ -193,6 +193,13 @@ class Iq(RootStanza):
"""
if timeout is None:
timeout = self.stream.response_timeout
+
+ criteria = {
+ 'id': self['id'],
+ 'self': self.stream.boundjid,
+ 'peer': self['to']
+ }
+
if callback is not None and self['type'] in ('get', 'set'):
handler_name = 'IqCallback_%s' % self['id']
if timeout_callback:
@@ -203,19 +210,19 @@ class Iq(RootStanza):
self._fire_timeout,
repeat=False)
handler = Callback(handler_name,
- MatcherId(self['id']),
+ MatchIDSender(criteria),
self._handle_result,
once=True)
else:
handler = Callback(handler_name,
- MatcherId(self['id']),
+ MatchIDSender(criteria),
callback,
once=True)
self.stream.register_handler(handler)
StanzaBase.send(self, now=now)
return handler_name
elif block and self['type'] in ('get', 'set'):
- waitfor = Waiter('IqWait_%s' % self['id'], MatcherId(self['id']))
+ waitfor = Waiter('IqWait_%s' % self['id'], MatchIDSender(criteria))
self.stream.register_handler(waitfor)
StanzaBase.send(self, now=now)
result = waitfor.wait(timeout)
diff --git a/sleekxmpp/test/sleektest.py b/sleekxmpp/test/sleektest.py
index 04fb106d..51cda3ee 100644
--- a/sleekxmpp/test/sleektest.py
+++ b/sleekxmpp/test/sleektest.py
@@ -16,7 +16,7 @@ from sleekxmpp.test import TestSocket, TestLiveSocket
from sleekxmpp.xmlstream import ET
from sleekxmpp.xmlstream import ElementBase
from sleekxmpp.xmlstream.tostring import tostring
-from sleekxmpp.xmlstream.matcher import StanzaPath, MatcherId
+from sleekxmpp.xmlstream.matcher import StanzaPath, MatcherId, MatchIDSender
from sleekxmpp.xmlstream.matcher import MatchXMLMask, MatchXPath
@@ -212,6 +212,7 @@ class SleekTest(unittest.TestCase):
matchers = {'stanzapath': StanzaPath,
'xpath': MatchXPath,
'mask': MatchXMLMask,
+ 'idsender': MatchIDSender,
'id': MatcherId}
Matcher = matchers.get(method, None)
if Matcher is None:
diff --git a/sleekxmpp/util/sasl/mechanisms.py b/sleekxmpp/util/sasl/mechanisms.py
index 1eb6af83..523eabc2 100644
--- a/sleekxmpp/util/sasl/mechanisms.py
+++ b/sleekxmpp/util/sasl/mechanisms.py
@@ -111,7 +111,7 @@ class X_FACEBOOK_PLATFORM(Mech):
b'api_key': self.credentials['api_key']
}
- resp = '&'.join(['%s=%s' % (k, v) for k, v in resp_data.items()])
+ resp = '&'.join(['%s=%s' % (k.decode("utf-8"), v.decode("utf-8")) for k, v in resp_data.items()])
return bytes(resp)
return b''
diff --git a/sleekxmpp/version.py b/sleekxmpp/version.py
index 6874ce6d..33f7ccdd 100644
--- a/sleekxmpp/version.py
+++ b/sleekxmpp/version.py
@@ -9,5 +9,5 @@
# We don't want to have to import the entire library
# just to get the version info for setup.py
-__version__ = '1.2.0'
-__version_info__ = (1, 2, 0, '', 0)
+__version__ = '1.2.2'
+__version_info__ = (1, 2, 2, '', 0)
diff --git a/sleekxmpp/xmlstream/matcher/__init__.py b/sleekxmpp/xmlstream/matcher/__init__.py
index 1038d1bd..aa74c434 100644
--- a/sleekxmpp/xmlstream/matcher/__init__.py
+++ b/sleekxmpp/xmlstream/matcher/__init__.py
@@ -7,6 +7,7 @@
"""
from sleekxmpp.xmlstream.matcher.id import MatcherId
+from sleekxmpp.xmlstream.matcher.idsender import MatchIDSender
from sleekxmpp.xmlstream.matcher.many import MatchMany
from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath
from sleekxmpp.xmlstream.matcher.xmlmask import MatchXMLMask
diff --git a/sleekxmpp/xmlstream/matcher/idsender.py b/sleekxmpp/xmlstream/matcher/idsender.py
new file mode 100644
index 00000000..5c2c1f51
--- /dev/null
+++ b/sleekxmpp/xmlstream/matcher/idsender.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+"""
+ sleekxmpp.xmlstream.matcher.id
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Part of SleekXMPP: The Sleek XMPP Library
+
+ :copyright: (c) 2011 Nathanael C. Fritz
+ :license: MIT, see LICENSE for more details
+"""
+
+from sleekxmpp.xmlstream.matcher.base import MatcherBase
+
+
+class MatchIDSender(MatcherBase):
+
+ """
+ The IDSender matcher selects stanzas that have the same stanza 'id'
+ interface value as the desired ID, and that the 'from' value is one
+ of a set of approved entities that can respond to a request.
+ """
+
+ def match(self, xml):
+ """Compare the given stanza's ``'id'`` attribute to the stored
+ ``id`` value, and verify the sender's JID.
+
+ :param xml: The :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
+ stanza to compare against.
+ """
+
+ selfjid = self._criteria['self']
+ peerjid = self._criteria['peer']
+
+ allowed = {}
+ allowed[''] = True
+ allowed[selfjid.bare] = True
+ allowed[selfjid.host] = True
+ allowed[peerjid.full] = True
+ allowed[peerjid.bare] = True
+ allowed[peerjid.host] = True
+
+ _from = xml['from']
+
+ try:
+ return xml['id'] == self._criteria['id'] and allowed[_from]
+ except KeyError:
+ return False