def complete_xml_element(self, xmlnode, doc): """Complete the XML node with `self` content. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `doc`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `doc`: `libxml2.xmlDoc`""" if self.type is not None and self.type not in self.allowed_types: raise ValueError, "Invalid form field type: %r" % (self.type,) if self.type is not None: xmlnode.setProp("type", self.type) if not self.label is None: xmlnode.setProp("label", to_utf8(self.label)) if not self.name is None: xmlnode.setProp("var", to_utf8(self.name)) if self.values: if self.type and len(self.values) > 1 and not self.type.endswith(u"-multi"): raise ValueError, "Multiple values not allowed for %r field" % (self.type,) for value in self.values: xmlnode.newTextChild(xmlnode.ns(), "value", to_utf8(value)) for option in self.options: option.as_xml(xmlnode, doc) if self.required: xmlnode.newChild(xmlnode.ns(), "required", None) if self.desc: xmlnode.newTextChild(xmlnode.ns(), "desc", to_utf8(self.desc)) return xmlnode
def complete_xml_element(self, xmlnode, _unused): """Complete the XML node with `self` content. Should be overriden in classes derived from `StanzaPayloadObject`. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `_unused`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `_unused`: `libxml2.xmlDoc`""" if self.node: xmlnode.setProp("node", to_utf8(self.node)) if self.sid: xmlnode.setProp("sid", to_utf8(self.sid)) if self.mode and self.mode != 'tcp': xmlnode.setProp("mode", to_utf8(self.mode)) for host in self.hosts: try: host.as_xml(xmlnode, _unused) except: pprint(host) raise if self.activate: xmlnode.newChild(None, "activate", JID(self.activate).as_utf8()) if self.host_used: h = xmlnode.newChild(None, "streamhost-used", None) h.setProp("jid", JID(self.host_used).as_utf8())
def complete_xml_element(self, xmlnode, doc): """Complete the XML node with `self` content. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `doc`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `doc`: `libxml2.xmlDoc`""" if self.type not in self.allowed_types: raise ValueError, "Form type %r not allowed." % (self.type, ) xmlnode.setProp("type", self.type) if self.type == "cancel": return ns = xmlnode.ns() if self.title is not None: xmlnode.newTextChild(ns, "title", to_utf8(self.title)) if self.instructions is not None: xmlnode.newTextChild(ns, "instructions", to_utf8(self.instructions)) for field in self.fields: field.as_xml(xmlnode, doc) if self.type != "result": return if self.reported_fields: reported = xmlnode.newChild(ns, "reported", None) for field in self.reported_fields: field.as_xml(reported, doc) for item in self.items: item.as_xml(xmlnode, doc)
def complete_xml_element(self, xmlnode, doc): """Complete the XML node with `self` content. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `doc`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `doc`: `libxml2.xmlDoc`""" if self.type not in self.allowed_types: raise ValueError, "Form type %r not allowed." % (self.type,) xmlnode.setProp("type", self.type) if self.type == "cancel": return ns = xmlnode.ns() if self.title is not None: xmlnode.newTextChild(ns, "title", to_utf8(self.title)) if self.instructions is not None: xmlnode.newTextChild(ns, "instructions", to_utf8(self.instructions)) for field in self.fields: field.as_xml(xmlnode, doc) if self.type != "result": return if self.reported_fields: reported = xmlnode.newChild(ns, "reported", None) for field in self.reported_fields: field.as_xml(reported, doc) for item in self.items: item.as_xml(xmlnode, doc)
def complete_xml_element(self, xmlnode, doc): """Complete the XML node with `self` content. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `doc`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `doc`: `libxml2.xmlDoc`""" ns = xmlnode.ns() if self.instructions is not None: xmlnode.newTextChild(ns, "instructions", to_utf8(self.instructions)) if self.form: self.form.as_xml(xmlnode, doc) if self.remove: xmlnode.newChild(ns, "remove", None) else: if self.registered: xmlnode.newChild(ns, "registered", None) for field in legacy_fields: value = getattr(self, field) if value is not None: xmlnode.newTextChild(ns, field, to_utf8(value))
def complete_xml_element(self, xmlnode, doc): """Complete the XML node with `self` content. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `doc`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `doc`: `libxml2.xmlDoc`""" if self.type is not None and self.type not in self.allowed_types: raise ValueError, "Invalid form field type: %r" % (self.type, ) if self.type is not None: xmlnode.setProp("type", self.type) if not self.label is None: xmlnode.setProp("label", to_utf8(self.label)) if not self.name is None: xmlnode.setProp("var", to_utf8(self.name)) if self.values: if self.type and len( self.values) > 1 and not self.type.endswith(u"-multi"): raise ValueError, "Multiple values not allowed for %r field" % ( self.type, ) for value in self.values: xmlnode.newTextChild(xmlnode.ns(), "value", to_utf8(value)) for option in self.options: option.as_xml(xmlnode, doc) if self.required: xmlnode.newChild(xmlnode.ns(), "required", None) if self.desc: xmlnode.newTextChild(xmlnode.ns(), "desc", to_utf8(self.desc)) return xmlnode
def complete_xml_element(self, xmlnode, _unused): """Complete the XML node with `self` content. Should be overriden in classes derived from `StanzaPayloadObject`. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `_unused`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `_unused`: `libxml2.xmlDoc`""" if self.node: xmlnode.setProp("node", to_utf8(self.node)) if self.sid: xmlnode.setProp("sid", to_utf8(self.sid)) if self.mode and self.mode != 'tcp': xmlnode.setProp("mode", to_utf8(self.mode)) for host in self.hosts: try: host.as_xml(xmlnode, _unused) except: pprint(host) raise if self.activate: xmlnode.newChild(None, "activate", JID(self.activate).as_utf8()) if self.host_used: h = xmlnode.newChild(None, "streamhost-used", None) h.setProp("jid",JID(self.host_used).as_utf8())
def __init__(self, xmlnode = None, from_jid = None, to_jid = None, stanza_type = None, stanza_id = None, show = None, status = None, priority = 0, error = None, error_cond = None, stream = None): """Initialize a `Presence` object. :Parameters: - `xmlnode`: XML node to_jid be wrapped into the `Presence` object or other Presence object to be copied. If not given then new presence stanza is created using following parameters. - `from_jid`: sender JID. - `to_jid`: recipient JID. - `stanza_type`: staza type: one of: None, "available", "unavailable", "subscribe", "subscribed", "unsubscribe", "unsubscribed" or "error". "available" is automaticaly changed to_jid None. - `stanza_id`: stanza id -- value of stanza's "id" attribute - `show`: "show" field of presence stanza. One of: None, "away", "xa", "dnd", "chat". - `status`: descriptive text for the presence stanza. - `priority`: presence priority. - `error_cond`: error condition name. Ignored if `stanza_type` is not "error" :Types: - `xmlnode`: `unicode` or `libxml2.xmlNode` or `Stanza` - `from_jid`: `JID` - `to_jid`: `JID` - `stanza_type`: `unicode` - `stanza_id`: `unicode` - `show`: `unicode` - `status`: `unicode` - `priority`: `unicode` - `error_cond`: `unicode`""" self.xmlnode=None if isinstance(xmlnode,Presence): pass elif isinstance(xmlnode,Stanza): raise TypeError,"Couldn't make Presence from other Stanza" elif isinstance(xmlnode,libxml2.xmlNode): pass elif xmlnode is not None: raise TypeError,"Couldn't make Presence from %r" % (type(xmlnode),) if stanza_type and stanza_type not in presence_types: raise ValueError, "Invalid presence type: %r" % (type,) if stanza_type=="available": stanza_type=None if xmlnode is None: xmlnode="presence" Stanza.__init__(self, xmlnode, from_jid = from_jid, to_jid = to_jid, stanza_type = stanza_type, stanza_id = stanza_id, error = error, error_cond = error_cond, stream = stream) if show: self.xmlnode.newTextChild(common_ns,"show",to_utf8(show)) if status: self.xmlnode.newTextChild(common_ns,"status",to_utf8(status)) if priority and priority!=0: self.xmlnode.newTextChild(common_ns,"priority",to_utf8(unicode(priority)))
def __from_xml(self, node): if node.type != "element": raise ValueError, "XML node is not an si (not en element)" ns = get_node_ns_uri(node) if ns and ns != SI_NS or node.name != self.xml_element_name: raise ValueError, "XML node is not an si" sid = node.prop("id") self.sid = to_utf8(sid) if sid else None mime_type = node.prop("mime-type") self.mime_type = to_utf8(mime_type) if mime_type else None profile_ns = node.prop("profile") self.profile_ns = to_utf8(profile_ns) if profile_ns else None
def complete_xml_element(self, xmlnode, _unused): 'to xml' if isinstance(self.id, int) and self.id >=0: xmlnode.setProp("id", to_utf8(str(self.id))) else: raise ValueError, "self.id must be int" xmlnode.setProp("protocol", to_utf8(self.protocol)) if self.protocol else None xmlnode.setProp("username", to_utf8(self.username)) if self.username else None xmlnode.setProp("password", base64.b64encode(self.password)) if self.password else None xmlnode.setProp("action", to_utf8(self.action)) if self.action else None xmlnode.newTextChild(None, "data", binascii.b2a_base64(self.data)) if self.data is not None else None
def __from_xml(self, node): if node.type!="element": raise ValueError,"XML node is not an si (not en element)" ns=get_node_ns_uri(node) if ns and ns!=SI_NS or node.name!=self.xml_element_name: raise ValueError,"XML node is not an si" sid = node.prop("id") self.sid = to_utf8(sid) if sid else None mime_type = node.prop("mime-type") self.mime_type = to_utf8(mime_type) if mime_type else None profile_ns = node.prop("profile") self.profile_ns = to_utf8(profile_ns) if profile_ns else None
def _plain_auth_stage2(self, _unused): """Do the second stage (<iq type='set'/>) of legacy "plain" authentication. [client only]""" iq = Iq(stanza_type="set") q = iq.new_query("jabber:iq:auth") q.newTextChild(None, "username", to_utf8(self.my_jid.node)) q.newTextChild(None, "resource", to_utf8(self.my_jid.resource)) q.newTextChild(None, "password", to_utf8(self.password)) self.send(iq) self.set_response_handlers(iq, self.auth_finish, self.auth_error) iq.free()
def _plain_auth_stage2(self, _unused): """Do the second stage (<iq type='set'/>) of legacy "plain" authentication. [client only]""" iq=Iq(stanza_type="set") q=iq.new_query("jabber:iq:auth") q.newTextChild(None,"username",to_utf8(self.my_jid.node)) q.newTextChild(None,"resource",to_utf8(self.my_jid.resource)) q.newTextChild(None,"password",to_utf8(self.password)) self.send(iq) self.set_response_handlers(iq,self.auth_finish,self.auth_error) iq.free()
def _auth_stage1(self): """Do the first stage (<iq type='get'/>) of legacy ("plain" or "digest") authentication. [client only]""" iq=Iq(stanza_type="get") q=iq.new_query("jabber:iq:auth") q.newTextChild(None,"username",to_utf8(self.my_jid.node)) q.newTextChild(None,"resource",to_utf8(self.my_jid.resource)) self.send(iq) self.set_response_handlers(iq,self.auth_stage2,self.auth_error, self.auth_timeout,timeout=60) iq.free()
def __init__(self, xmlnode = None, from_jid = None, to_jid = None, stanza_type = None, stanza_id = None, subject = None, body = None, thread = None, error = None, error_cond = None, stream = None): """Initialize a `Message` object. :Parameters: - `xmlnode`: XML node to_jid be wrapped into the `Message` object or other Message object to be copied. If not given then new presence stanza is created using following parameters. - `from_jid`: sender JID. - `to_jid`: recipient JID. - `stanza_type`: staza type: one of: "get", "set", "result" or "error". - `stanza_id`: stanza id -- value of stanza's "id" attribute. If not given, then unique for the session value is generated. - `subject`: message subject, - `body`: message body. - `thread`: message thread id. - `error_cond`: error condition name. Ignored if `stanza_type` is not "error". :Types: - `xmlnode`: `unicode` or `libxml2.xmlNode` or `Stanza` - `from_jid`: `JID` - `to_jid`: `JID` - `stanza_type`: `unicode` - `stanza_id`: `unicode` - `subject`: `unicode` - `body`: `unicode` - `thread`: `unicode` - `error_cond`: `unicode`""" self.xmlnode=None if isinstance(xmlnode,Message): pass elif isinstance(xmlnode,Stanza): raise TypeError, "Couldn't make Message from other Stanza" elif isinstance(xmlnode,libxml2.xmlNode): pass elif xmlnode is not None: raise TypeError, "Couldn't make Message from %r" % (type(xmlnode),) if xmlnode is None: xmlnode="message" Stanza.__init__(self, xmlnode, from_jid = from_jid, to_jid = to_jid, stanza_type = stanza_type, stanza_id = stanza_id, error = error, error_cond = error_cond, stream = stream) if subject is not None: self.xmlnode.newTextChild(common_ns,"subject",to_utf8(subject)) if body is not None: self.xmlnode.newTextChild(common_ns,"body",to_utf8(body)) if thread is not None: self.xmlnode.newTextChild(common_ns,"thread",to_utf8(thread))
def _auth_stage1(self): """Do the first stage (<iq type='get'/>) of legacy ("plain" or "digest") authentication. [client only]""" iq = Iq(stanza_type="get") q = iq.new_query("jabber:iq:auth") q.newTextChild(None, "username", to_utf8(self.my_jid.node)) q.newTextChild(None, "resource", to_utf8(self.my_jid.resource)) self.send(iq) self.set_response_handlers(iq, self.auth_stage2, self.auth_error, self.auth_timeout, timeout=60) iq.free()
def xpath_eval(self,expr,namespaces=None): """Evaluate XPath expression on the error element. The expression will be evaluated in context where the common namespace (the one used for stanza elements, mapped to 'jabber:client', 'jabber:server', etc.) is bound to prefix "ns" and other namespaces are bound accordingly to the `namespaces` list. :Parameters: - `expr`: the XPath expression. - `namespaces`: prefix to namespace mapping. :Types: - `expr`: `unicode` - `namespaces`: `dict` :return: the result of the expression evaluation. """ ctxt = common_doc.xpathNewContext() ctxt.setContextNode(self.xmlnode) ctxt.xpathRegisterNs("ns",to_utf8(self.ns)) if namespaces: for prefix,uri in namespaces.items(): ctxt.xpathRegisterNs(prefix,uri) ret=ctxt.xpathEval(expr) ctxt.xpathFreeContext() return ret
def __init__(self,xmlnode_or_cond,error_type=None,copy=1,parent=None): """Initialize a StreamErrorNode object. :Parameters: - `xmlnode_or_cond`: XML node to be wrapped into this object or the primary (defined by XMPP specification) error condition name. - `error_type`: type of the error, one of: 'cancel', 'continue', 'modify', 'auth', 'wait'. - `copy`: When `True` then the XML node will be copied, otherwise it is only borrowed. - `parent`: Parent node for the XML node to be copied or created. :Types: - `xmlnode_or_cond`: `libxml2.xmlNode` or `unicode` - `error_type`: `unicode` - `copy`: `bool` - `parent`: `libxml2.xmlNode`""" if type(xmlnode_or_cond) is str: xmlnode_or_cond=unicode(xmlnode_or_cond,"utf-8") if type(xmlnode_or_cond) is unicode: if not stanza_errors.has_key(xmlnode_or_cond): raise ValueError, "Bad error condition" ErrorNode.__init__(self,xmlnode_or_cond,STANZA_ERROR_NS,copy=copy,parent=parent) if type(xmlnode_or_cond) is unicode: if error_type is None: error_type=stanza_errors[xmlnode_or_cond][1] self.xmlnode.setProp("type",to_utf8(error_type))
def do_bind(self,stanza): """Do the resource binding requested by a client connected. [server only] :Parameters: - `stanza`: resource binding request stanza. :Types: - `stanza`: `pyxmpp.Iq`""" fr=stanza.get_from() if fr and fr!=self.peer: r=stanza.make_error_response("forbidden") self.send(r) r.free() return resource_n=stanza.xpath_eval("bind:bind/bind:resource",{"bind":BIND_NS}) if resource_n: resource=resource_n[0].getContent() else: resource="auto" if not resource: r=stanza.make_error_response("bad-request") else: self.unset_iq_set_handler("bind",BIND_NS) r=stanza.make_result_response() self.peer.set_resource(resource) q=r.new_query(BIND_NS,"bind") q.newTextChild(None,"jid",to_utf8(self.peer.as_unicode())) self.state_change("authorized",self.peer) r.set_to(None) self.send(r) r.free()
def xpath_eval(self, expr, namespaces=None): """Evaluate XPath expression on the error element. The expression will be evaluated in context where the common namespace (the one used for stanza elements, mapped to 'jabber:client', 'jabber:server', etc.) is bound to prefix "ns" and other namespaces are bound accordingly to the `namespaces` list. :Parameters: - `expr`: the XPath expression. - `namespaces`: prefix to namespace mapping. :Types: - `expr`: `unicode` - `namespaces`: `dict` :return: the result of the expression evaluation. """ ctxt = common_doc.xpathNewContext() ctxt.setContextNode(self.xmlnode) ctxt.xpathRegisterNs("ns", to_utf8(self.ns)) if namespaces: for prefix, uri in namespaces.items(): ctxt.xpathRegisterNs(prefix, uri) ret = ctxt.xpathEval(expr) ctxt.xpathFreeContext() return ret
def __init__(self, xmlnode_or_cond, error_type=None, copy=1, parent=None): """Initialize a StreamErrorNode object. :Parameters: - `xmlnode_or_cond`: XML node to be wrapped into this object or the primary (defined by XMPP specification) error condition name. - `error_type`: type of the error, one of: 'cancel', 'continue', 'modify', 'auth', 'wait'. - `copy`: When `True` then the XML node will be copied, otherwise it is only borrowed. - `parent`: Parent node for the XML node to be copied or created. :Types: - `xmlnode_or_cond`: `libxml2.xmlNode` or `unicode` - `error_type`: `unicode` - `copy`: `bool` - `parent`: `libxml2.xmlNode`""" if type(xmlnode_or_cond) is str: xmlnode_or_cond = unicode(xmlnode_or_cond, "utf-8") if type(xmlnode_or_cond) is unicode: if not stanza_errors.has_key(xmlnode_or_cond): raise ValueError, "Bad error condition" ErrorNode.__init__(self, xmlnode_or_cond, STANZA_ERROR_NS, copy=copy, parent=parent) if type(xmlnode_or_cond) is unicode: if error_type is None: error_type = stanza_errors[xmlnode_or_cond][1] self.xmlnode.setProp("type", to_utf8(error_type))
def do_bind(self, stanza): """Do the resource binding requested by a client connected. [server only] :Parameters: - `stanza`: resource binding request stanza. :Types: - `stanza`: `pyxmpp.Iq`""" fr = stanza.get_from() if fr and fr != self.peer: r = stanza.make_error_response("forbidden") self.send(r) r.free() return resource_n = stanza.xpath_eval("bind:bind/bind:resource", {"bind": BIND_NS}) if resource_n: resource = resource_n[0].getContent() else: resource = "auto" if not resource: r = stanza.make_error_response("bad-request") else: self.unset_iq_set_handler("bind", BIND_NS) r = stanza.make_result_response() self.peer.set_resource(resource) q = r.new_query(BIND_NS, "bind") q.newTextChild(None, "jid", to_utf8(self.peer.as_unicode())) self.state_change("authorized", self.peer) r.set_to(None) self.send(r) r.free()
def _digest_auth_stage2(self, _unused): """Do the second stage (<iq type='set'/>) of legacy "digest" authentication. [client only]""" iq=Iq(stanza_type="set") q=iq.new_query("jabber:iq:auth") q.newTextChild(None,"username",to_utf8(self.my_jid.node)) q.newTextChild(None,"resource",to_utf8(self.my_jid.resource)) digest = hashlib.sha1(to_utf8(self.stream_id)+to_utf8(self.password)).hexdigest() q.newTextChild(None,"digest",digest) self.send(iq) self.set_response_handlers(iq,self.auth_finish,self.auth_error) iq.free()
def _parse_response(self, response): """Parse a client reponse and pass to further processing. :Parameters: - `response`: the response from the client. :Types: - `response`: `str` :return: a challenge, a success indicator or a failure indicator. :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`""" response = response.split('\x00')[ 0] # workaround for some SASL implementations if self.realm: realm = to_utf8(self.realm) realm = _quote(realm) else: realm = None username = None cnonce = None digest_uri = None response_val = None authzid = None nonce_count = None while response: m = _param_re.match(response) if not m: self.__logger.debug("Response syntax error: %r" % (response, )) return Failure("not-authorized") response = m.group("rest") var = m.group("var") val = m.group("val") self.__logger.debug("%r: %r" % (var, val)) if var == "realm": realm = val[1:-1] elif var == "cnonce": if cnonce: self.__logger.debug("Duplicate cnonce") return Failure("not-authorized") cnonce = val[1:-1] elif var == "qop": if val != 'auth': self.__logger.debug("qop other then 'auth'") return Failure("not-authorized") elif var == "digest-uri": digest_uri = val[1:-1] elif var == "authzid": authzid = val[1:-1] elif var == "username": username = val[1:-1] elif var == "response": response_val = val elif var == "nc": nonce_count = val self.last_nonce_count += 1 if int(nonce_count) != self.last_nonce_count: self.__logger.debug("bad nonce: %r != %r" % (nonce_count, self.last_nonce_count)) return Failure("not-authorized") return self._check_params(username, realm, cnonce, digest_uri, response_val, authzid, nonce_count)
def _parse_response(self,response): """Parse a client reponse and pass to further processing. :Parameters: - `response`: the response from the client. :Types: - `response`: `str` :return: a challenge, a success indicator or a failure indicator. :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`""" response=response.split('\x00')[0] # workaround for some SASL implementations if self.realm: realm=to_utf8(self.realm) realm=_quote(realm) else: realm=None username=None cnonce=None digest_uri=None response_val=None authzid=None nonce_count=None while response: m=_param_re.match(response) if not m: self.__logger.debug("Response syntax error: %r" % (response,)) return Failure("not-authorized") response=m.group("rest") var=m.group("var") val=m.group("val") self.__logger.debug("%r: %r" % (var,val)) if var=="realm": realm=val[1:-1] elif var=="cnonce": if cnonce: self.__logger.debug("Duplicate cnonce") return Failure("not-authorized") cnonce=val[1:-1] elif var=="qop": if val!='auth': self.__logger.debug("qop other then 'auth'") return Failure("not-authorized") elif var=="digest-uri": digest_uri=val[1:-1] elif var=="authzid": authzid=val[1:-1] elif var=="username": username=val[1:-1] elif var=="response": response_val=val elif var=="nc": nonce_count=val self.last_nonce_count+=1 if int(nonce_count)!=self.last_nonce_count: self.__logger.debug("bad nonce: %r != %r" % (nonce_count,self.last_nonce_count)) return Failure("not-authorized") return self._check_params(username,realm,cnonce,digest_uri, response_val,authzid,nonce_count)
def _digest_auth_stage2(self, _unused): """Do the second stage (<iq type='set'/>) of legacy "digest" authentication. [client only]""" iq = Iq(stanza_type="set") q = iq.new_query("jabber:iq:auth") q.newTextChild(None, "username", to_utf8(self.my_jid.node)) q.newTextChild(None, "resource", to_utf8(self.my_jid.resource)) digest = hashlib.sha1( to_utf8(self.stream_id) + to_utf8(self.password)).hexdigest() q.newTextChild(None, "digest", digest) self.send(iq) self.set_response_handlers(iq, self.auth_finish, self.auth_error) iq.free()
def send_si_request(self, session) : ''' <iq type='set' id='offer1' to='[email protected]/resource'> <si xmlns='http://jabber.org/protocol/si' id='a0' mime-type='text/plain' profile='http://jabber.org/protocol/si/profile/file-transfer'> <file xmlns='http://jabber.org/protocol/si/profile/file-transfer' name='test.txt' size='1022' hash='552da749930852c69ae5d2141d3766b1' date='1969-07-21T02:56:15Z'> <desc>This is a test. If this were a real file...</desc> </file> <feature xmlns='http://jabber.org/protocol/feature-neg'> <x xmlns='jabber:x:data' type='form'> <field var='stream-method' type='list-single'> <option><value>>>http://jabber.org/protocol/bytestreams</value></option> <option><value>>>http://jabber.org/protocol/ibb</value></option> </field> </x> </feature> </si> </iq> ''' stream = self.client.get_stream() #new_id = stream.generate_id() iq = Iq(None, session.jid, session.to_jid, "set", session.sid) si_node = iq.add_new_content(SI_NS, 'si') si_node.setProp("id", session.sid) si_node.setProp("mime-type", 'application/octet-stream') si_node.setProp("profile", FILE_TRANSFER_NS) file_node = si_node.newChild(None,"file",None) file_node.setProp("xmlns", FILE_TRANSFER_NS) file_node.setProp("name", to_utf8(session.file_name)) file_node.setProp("size", to_utf8(session.file_size)) feature_node = si_node.newChild(None, "feature", None) feature_node.setProp("xmlns", FEATURE_NS) form = Form() form.add_field( name = 'stream-method', field_type = 'list-single', #options = [Option('http://jabber.org/protocol/bytestreams', None), Option('http://jabber.org/protocol/ibb', None)]) options = [Option('http://jabber.org/protocol/ibb', None)]) form.as_xml(feature_node) stream.set_response_handlers(iq, self.received_si_success, self.received_si_error) stream.send(iq) self.sessions[session.sid] = session
def set_status(self,status): """Change presence status description. :Parameters: - `status`: descriptive text for the presence stanza. :Types: - `status`: `unicode`""" n=self.xpath_eval("ns:status") if not status: if n: n[0].unlinkNode() n[0].freeNode() else: return if n: n[0].setContent(to_utf8(status)) else: self.xmlnode.newTextChild(common_ns,"status",to_utf8(status))
def complete_xml_element(self, xmlnode, doc): xmlnode.setProp("type", self.type) if self.type else None xmlnode.setProp("value", self.value) if self.value else None xmlnode.setProp("action", self.action) xmlnode.setProp("order", to_utf8(self.order)) [xmlnode.newChild(None, child, None) for child in PRIVACY_TYPES if getattr(self, util.pythonize(child))]
def set_status(self, status): """Change presence status description. :Parameters: - `status`: descriptive text for the presence stanza. :Types: - `status`: `unicode`""" n = self.xpath_eval("ns:status") if not status: if n: n[0].unlinkNode() n[0].freeNode() else: return if n: n[0].setContent(to_utf8(status)) else: self.xmlnode.newTextChild(common_ns, "status", to_utf8(status))
def complete_xml_element(self, xmlnode, doc): xmlnode.setProp("type", self.type) if self.type else None xmlnode.setProp("value", self.value) if self.value else None xmlnode.setProp("action", self.action) xmlnode.setProp("order", to_utf8(self.order)) [ xmlnode.newChild(None, child, None) for child in PRIVACY_TYPES if getattr(self, util.pythonize(child)) ]
def add_custom_condition(self, ns, cond, content=None): """Add custom condition element to the error. :Parameters: - `ns`: namespace URI. - `cond`: condition name. - `content`: content of the element. :Types: - `ns`: `unicode` - `cond`: `unicode` - `content`: `unicode` :return: the new condition element. :returntype: `libxml2.xmlNode`""" c = self.xmlnode.newTextChild(None, to_utf8(cond), content) ns = c.newNs(to_utf8(ns), None) c.setNs(ns) return c
def set_show(self, show): """Change presence "show" field. :Parameters: - `show`: new value for the "show" field of presence stanza. One of: None, "away", "xa", "dnd", "chat". :Types: - `show`: `unicode`""" n = self.xpath_eval("ns:show") if not show: if n: n[0].unlinkNode() n[0].freeNode() else: return if n: n[0].setContent(to_utf8(show)) else: self.xmlnode.newTextChild(common_ns, "show", to_utf8(show))
def set_show(self,show): """Change presence "show" field. :Parameters: - `show`: new value for the "show" field of presence stanza. One of: None, "away", "xa", "dnd", "chat". :Types: - `show`: `unicode`""" n=self.xpath_eval("ns:show") if not show: if n: n[0].unlinkNode() n[0].freeNode() else: return if n: n[0].setContent(to_utf8(show)) else: self.xmlnode.newTextChild(common_ns,"show",to_utf8(show))
def add_custom_condition(self,ns,cond,content=None): """Add custom condition element to the error. :Parameters: - `ns`: namespace URI. - `cond`: condition name. - `content`: content of the element. :Types: - `ns`: `unicode` - `cond`: `unicode` - `content`: `unicode` :return: the new condition element. :returntype: `libxml2.xmlNode`""" c=self.xmlnode.newTextChild(None,to_utf8(cond),content) ns=c.newNs(to_utf8(ns),None) c.setNs(ns) return c
def add_feature(self,var): """Add a feature to `self`. :Parameters: - `var`: the feature name. :Types: - `var`: `unicode`""" if self.has_feature(var): return n=self.xmlnode.newChild(None, "feature", None) n.setProp("var", to_utf8(var))
def complete_xml_element(self, xmlnode, _unused): """Complete the XML node with `self` content. Should be overriden in classes derived from `StanzaPayloadObject`. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `_unused`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `_unused`: `libxml2.xmlDoc`""" xmlnode.setProp("jid",self.jid.as_utf8()) if self.name: xmlnode.setProp("name",to_utf8(self.name)) xmlnode.setProp("subscription",self.subscription) if self.ask: xmlnode.setProp("ask",to_utf8(self.ask)) for g in self.groups: xmlnode.newTextChild(None, "group", to_utf8(g))
def set_type(self,stanza_type): """Set "type" attribute of the stanza. :Parameters: - `stanza_type`: new value of the "type" attribute (stanza type). :Types: - `stanza_type`: `unicode`""" if stanza_type: return self.xmlnode.setProp("type",to_utf8(stanza_type)) else: return self.xmlnode.unsetProp("type")
def set_id(self,stanza_id): """Set "id" attribute of the stanza. :Parameters: - `stanza_id`: new value of the "id" attribute (stanza identifier). :Types: - `stanza_id`: `unicode`""" if stanza_id: return self.xmlnode.setProp("id",to_utf8(stanza_id)) else: return self.xmlnode.unsetProp("id")
def complete_xml_element(self, xmlnode, _unused): """Complete the XML node with `self` content. Should be overriden in classes derived from `StanzaPayloadObject`. :Parameters: - `xmlnode`: XML node with the element being built. It has already right name and namespace, but no attributes or content. - `_unused`: document to which the element belongs. :Types: - `xmlnode`: `libxml2.xmlNode` - `_unused`: `libxml2.xmlDoc`""" xmlnode.setProp("jid", self.jid.as_utf8()) if self.name: xmlnode.setProp("name", to_utf8(self.name)) xmlnode.setProp("subscription", self.subscription) if self.ask: xmlnode.setProp("ask", to_utf8(self.ask)) for g in self.groups: xmlnode.newTextChild(None, "group", to_utf8(g))
def add_feature(self, var): """Add a feature to `self`. :Parameters: - `var`: the feature name. :Types: - `var`: `unicode`""" if self.has_feature(var): return n = self.xmlnode.newChild(None, "feature", None) n.setProp("var", to_utf8(var))
def downgrade(self): """Downgrade an XMPP error element to the legacy format. Add a numeric code attribute according to the condition name.""" if self.xmlnode.hasProp("code"): return cond = self.get_condition() if not cond: return cond = cond.name if stanza_errors.has_key(cond) and stanza_errors[cond][2]: self.xmlnode.setProp("code", to_utf8(stanza_errors[cond][2]))
def downgrade(self): """Downgrade an XMPP error element to the legacy format. Add a numeric code attribute according to the condition name.""" if self.xmlnode.hasProp("code"): return cond=self.get_condition() if not cond: return cond=cond.name if stanza_errors.has_key(cond) and stanza_errors[cond][2]: self.xmlnode.setProp("code",to_utf8(stanza_errors[cond][2]))
def upgrade(self): """Upgrade a legacy error element to the XMPP compliant one. Use the error code provided to select the condition and the <error/> CDATA for the error text.""" if not self.xmlnode.hasProp("code"): code = None else: try: code = int(self.xmlnode.prop("code")) except (ValueError, KeyError): code = None if code and legacy_codes.has_key(code): cond = legacy_codes[code] else: cond = None condition = self.xpath_eval("ns:*") if condition: return elif cond is None: condition = self.xmlnode.newChild(None, "undefined-condition", None) ns = condition.newNs(to_utf8(self.ns), None) condition.setNs(ns) condition = self.xmlnode.newChild(None, "unknown-legacy-error", None) ns = condition.newNs(PYXMPP_ERROR_NS, None) condition.setNs(ns) else: condition = self.xmlnode.newChild(None, cond, None) ns = condition.newNs(to_utf8(self.ns), None) condition.setNs(ns) txt = self.xmlnode.getContent() if txt: text = self.xmlnode.newTextChild(None, "text", txt) ns = text.newNs(to_utf8(self.ns), None) text.setNs(ns)
def as_xml(self, parent): """ Create XML representation of `self`. :Parameters: - `parent`: the element to which the created node should be linked to. :Types: - `parent`: `libxml2.xmlNode` :return: an XML node. :returntype: `libxml2.xmlNode` """ n = parent.newChild(None, "item", None) if self.actor: n.newTextChild(None, "actor", to_utf8(self.actor)) if self.reason: n.newTextChild(None, "reason", to_utf8(self.reason)) n.setProp("affiliation", to_utf8(self.affiliation)) if self.role: n.setProp("role", to_utf8(self.role)) if self.jid: n.setProp("jid", to_utf8(self.jid.as_unicode())) if self.nick: n.setProp("nick", to_utf8(self.nick)) return n
def as_xml(self,parent): """ Create XML representation of `self`. :Parameters: - `parent`: the element to which the created node should be linked to. :Types: - `parent`: `libxml2.xmlNode` :return: an XML node. :returntype: `libxml2.xmlNode` """ n=parent.newChild(None,"item",None) if self.actor: n.newTextChild(None,"actor",to_utf8(self.actor)) if self.reason: n.newTextChild(None,"reason",to_utf8(self.reason)) n.setProp("affiliation",to_utf8(self.affiliation)) if self.role: n.setProp("role",to_utf8(self.role)) if self.jid: n.setProp("jid",to_utf8(self.jid.as_unicode())) if self.nick: n.setProp("nick",to_utf8(self.nick)) return n
def _digest_auth_in_stage2(self, username, _unused, stanza): """Handle the second stage (<iq type='set'/>) of legacy "digest" authentication. [server only]""" digest = stanza.xpath_eval("a:query/a:digest", {"a": "jabber:iq:auth"}) if digest: digest = digest[0].getContent() if not digest: self.__logger.debug("No digest found in digest auth request") iq = stanza.make_error_response("bad-request") self.send(iq) return password, pwformat = self.get_password(username) if not password or pwformat != "plain": iq = stanza.make_error_response("bad-request") e = iq.get_error() e.add_custom_condition('jabber:iq:auth:error', "user-unauthorized") self.send(iq) return mydigest = hashlib.sha1(to_utf8(self.stream_id) + to_utf8(password)).hexdigest() if mydigest == digest: iq = stanza.make_result_response() self.send(iq) self.peer_authenticated = True self.auth_method_used = "digest" self.state_change("authorized", self.peer) self._post_auth() else: self.__logger.debug("Digest auth failed: %r != %r" % (digest, mydigest)) iq = stanza.make_error_response("bad-request") e = iq.get_error() e.add_custom_condition('jabber:iq:auth:error', "user-unauthorized") self.send(iq)
def query_set(self, ns, element, data, handler=None): stream = self.client.get_stream() new_id = stream.generate_id() iq = Iq(None, None, None, "set", new_id) node = iq.new_query('jabber:iq:private') enode = node.newChild(None, to_utf8(element), None) ns = enode.newNs(ns, None) enode.setNs(ns) enode.addContent(data) stream.set_response_handlers(iq, self.on_set_result, self.on_set_error) stream.send(iq) if handler: self.handlers[new_id] = handler
def upgrade(self): """Upgrade a legacy error element to the XMPP compliant one. Use the error code provided to select the condition and the <error/> CDATA for the error text.""" if not self.xmlnode.hasProp("code"): code=None else: try: code=int(self.xmlnode.prop("code")) except (ValueError,KeyError): code=None if code and legacy_codes.has_key(code): cond=legacy_codes[code] else: cond=None condition=self.xpath_eval("ns:*") if condition: return elif cond is None: condition=self.xmlnode.newChild(None,"undefined-condition",None) ns=condition.newNs(to_utf8(self.ns),None) condition.setNs(ns) condition=self.xmlnode.newChild(None,"unknown-legacy-error",None) ns=condition.newNs(PYXMPP_ERROR_NS,None) condition.setNs(ns) else: condition=self.xmlnode.newChild(None,cond,None) ns=condition.newNs(to_utf8(self.ns),None) condition.setNs(ns) txt=self.xmlnode.getContent() if txt: text=self.xmlnode.newTextChild(None,"text",txt) ns=text.newNs(to_utf8(self.ns),None) text.setNs(ns)
def challenge(self, challenge): """Process the challenge and return the response. :Parameters: - `challenge`: the challenge. :Types: - `challenge`: `str` :return: the response or a failure indicator. :returntype: `sasl.Response` or `sasl.Failure`""" _unused = challenge if self.finished: self.__logger.debug("Already authenticated") return Failure("extra-challenge") self.finished=1 if self.password is None: self.password,pformat=self.password_manager.get_password(self.username) if not self.password or pformat!="plain": self.__logger.debug("Couldn't retrieve plain password") return Failure("password-unavailable") return Response("%s\000%s\000%s" % ( to_utf8(self.authzid), to_utf8(self.username), to_utf8(self.password)))
def _digest_auth_in_stage2(self, username, _unused, stanza): """Handle the second stage (<iq type='set'/>) of legacy "digest" authentication. [server only]""" digest=stanza.xpath_eval("a:query/a:digest",{"a":"jabber:iq:auth"}) if digest: digest=digest[0].getContent() if not digest: self.__logger.debug("No digest found in digest auth request") iq=stanza.make_error_response("bad-request") self.send(iq) return password,pwformat=self.get_password(username) if not password or pwformat!="plain": iq=stanza.make_error_response("bad-request") e=iq.get_error() e.add_custom_condition('jabber:iq:auth:error',"user-unauthorized") self.send(iq) return mydigest = hashlib.sha1(to_utf8(self.stream_id)+to_utf8(password)).hexdigest() if mydigest==digest: iq=stanza.make_result_response() self.send(iq) self.peer_authenticated=True self.auth_method_used="digest" self.state_change("authorized",self.peer) self._post_auth() else: self.__logger.debug("Digest auth failed: %r != %r" % (digest,mydigest)) iq=stanza.make_error_response("bad-request") e=iq.get_error() e.add_custom_condition('jabber:iq:auth:error',"user-unauthorized") self.send(iq)
def from_xml(self, xmlnode): if xmlnode.type != "element": raise ValueError, "XML node is not a photo (not en element)" ns = get_node_ns_uri(xmlnode) if ns and ns != self.xml_element_namespace or xmlnode.name != self.xml_element_name: raise ValueError, "XML node is not a %s descriptor" % self.xml_element_name from_ = xmlnode.prop("from") self.from_ = to_utf8(from_) if from_ else None timestamp = xmlnode.prop("stamp") try: self.timestamp = datetime.strptime(timestamp, DELAY_TIME_FORMAT) except Exception: self.timestamp = None
def from_xml(self, xmlnode): if xmlnode.type!="element": raise ValueError,"XML node is not a photo (not en element)" ns=get_node_ns_uri(xmlnode) if ns and ns!=self.xml_element_namespace or xmlnode.name!=self.xml_element_name: raise ValueError,"XML node is not a %s descriptor" % self.xml_element_name from_ = xmlnode.prop("from") self.from_ = to_utf8(from_) if from_ else None timestamp = xmlnode.prop("stamp") try: self.timestamp = datetime.strptime(timestamp, DELAY_TIME_FORMAT) except Exception: self.timestamp = None
def add_new_content(self,ns_uri,name): """Add a new XML element to the stanza payload. :Parameters: - `ns_uri`: XML namespace URI of the element. - `name`: element name. :Types: - `ns_uri`: `str` - `name`: `str` or `unicode` """ c=self.xmlnode.newChild(None,to_utf8(name),None) if ns_uri: ns=c.newNs(ns_uri,None) c.setNs(ns) return c
def set_password(self, password): """Set password for the MUC request. :Parameters: - `password`: password :Types: - `password`: `unicode`""" for child in xml_element_iter(self.xmlnode.children): if get_node_ns_uri(child) == MUC_NS and child.name == "password": child.unlinkNode() child.freeNode() break if password is not None: self.xmlnode.newTextChild(self.xmlnode.ns(), "password", to_utf8(password))