From 97ac572c20083fe0283c4621dc6e54603a32a310 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Tue, 8 May 2012 14:19:11 +0200 Subject: initial revision --- doc/apidocs/nbxmpp.protocol-pysrc.html | 2183 ++++++++++++++++++++++++++++++++ 1 file changed, 2183 insertions(+) create mode 100644 doc/apidocs/nbxmpp.protocol-pysrc.html (limited to 'doc/apidocs/nbxmpp.protocol-pysrc.html') diff --git a/doc/apidocs/nbxmpp.protocol-pysrc.html b/doc/apidocs/nbxmpp.protocol-pysrc.html new file mode 100644 index 0000000..63f88db --- /dev/null +++ b/doc/apidocs/nbxmpp.protocol-pysrc.html @@ -0,0 +1,2183 @@ + + + + + nbxmpp.protocol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Package nbxmpp :: + Module protocol + + + + + + +
[hide private]
[frames] | no frames]
+
+

Source Code for Module nbxmpp.protocol

+
+   1  ##   protocol.py 
+   2  ## 
+   3  ##   Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov 
+   4  ## 
+   5  ##   This program is free software; you can redistribute it and/or modify 
+   6  ##   it under the terms of the GNU General Public License as published by 
+   7  ##   the Free Software Foundation; either version 2, or (at your option) 
+   8  ##   any later version. 
+   9  ## 
+  10  ##   This program is distributed in the hope that it will be useful, 
+  11  ##   but WITHOUT ANY WARRANTY; without even the implied warranty of 
+  12  ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+  13  ##   GNU General Public License for more details. 
+  14   
+  15  # $Id: protocol.py,v 1.52 2006/01/09 22:08:57 normanr Exp $ 
+  16   
+  17  """ 
+  18  Protocol module contains tools that are needed for processing of xmpp-related 
+  19  data structures, including jabber-objects like JID or different stanzas and 
+  20  sub- stanzas) handling routines 
+  21  """ 
+  22   
+  23  from simplexml import Node, NodeBuilder 
+  24  import time 
+  25  import string 
+  26  import hashlib 
+  27   
+
28 -def ascii_upper(s): +
29 trans_table = string.maketrans(string.ascii_lowercase, + 30 string.ascii_uppercase) + 31 return s.translate(trans_table) +
32 + 33 NS_ACTIVITY = 'http://jabber.org/protocol/activity' # XEP-0108 + 34 NS_ADDRESS = 'http://jabber.org/protocol/address' # XEP-0033 + 35 NS_AGENTS = 'jabber:iq:agents' + 36 NS_AMP = 'http://jabber.org/protocol/amp' + 37 NS_AMP_ERRORS = NS_AMP + '#errors' + 38 NS_ARCHIVE = 'urn:xmpp:archive' # XEP-0136 + 39 NS_ARCHIVE_AUTO = NS_ARCHIVE + ':auto' # XEP-0136 + 40 NS_ARCHIVE_MANAGE = NS_ARCHIVE + ':manage' # XEP-0136 + 41 NS_ARCHIVE_MANUAL = NS_ARCHIVE + ':manual' # XEP-0136 + 42 NS_ARCHIVE_PREF = NS_ARCHIVE + ':pref' + 43 NS_ATOM = 'http://www.w3.org/2005/Atom' + 44 NS_ATTENTION = 'urn:xmpp:attention:0' # XEP-0224 + 45 NS_AUTH = 'jabber:iq:auth' + 46 NS_AVATAR = 'http://www.xmpp.org/extensions/xep-0084.html#ns-metadata' + 47 NS_BIND = 'urn:ietf:params:xml:ns:xmpp-bind' + 48 NS_BOB = 'urn:xmpp:bob' # XEP-0231 + 49 NS_BOOKMARKS = 'storage:bookmarks' # XEP-0048 + 50 NS_BROWSE = 'jabber:iq:browse' + 51 NS_BROWSING = 'http://jabber.org/protocol/browsing' # XEP-0195 + 52 NS_BYTESTREAM = 'http://jabber.org/protocol/bytestreams' # XEP-0065 + 53 NS_CAPS = 'http://jabber.org/protocol/caps' # XEP-0115 + 54 NS_CAPTCHA = 'urn:xmpp:captcha' # XEP-0158 + 55 NS_CARBONS = 'urn:xmpp:carbons:1' # XEP-0280 + 56 NS_CHATSTATES = 'http://jabber.org/protocol/chatstates' # XEP-0085 + 57 NS_CHATTING = 'http://jabber.org/protocol/chatting' # XEP-0194 + 58 NS_CLIENT = 'jabber:client' + 59 NS_CONDITIONS = 'urn:xmpp:muc:conditions:0' # XEP-0306 + 60 NS_COMMANDS = 'http://jabber.org/protocol/commands' + 61 NS_COMPONENT_ACCEPT = 'jabber:component:accept' + 62 NS_COMPONENT_1 = 'http://jabberd.jabberstudio.org/ns/component/1.0' + 63 NS_COMPRESS = 'http://jabber.org/protocol/compress' # XEP-0138 + 64 NS_CONFERENCE = 'jabber:x:conference' + 65 NS_DATA = 'jabber:x:data' # XEP-0004 + 66 NS_DATA_MEDIA = 'urn:xmpp:media-element' # XEP-0221 + 67 NS_DELAY = 'jabber:x:delay' + 68 NS_DELAY2 = 'urn:xmpp:delay' + 69 NS_DIALBACK = 'jabber:server:dialback' + 70 NS_DISCO = 'http://jabber.org/protocol/disco' + 71 NS_DISCO_INFO = NS_DISCO + '#info' + 72 NS_DISCO_ITEMS = NS_DISCO + '#items' + 73 NS_ENCRYPTED = 'jabber:x:encrypted' # XEP-0027 + 74 NS_ESESSION = 'http://www.xmpp.org/extensions/xep-0116.html#ns' + 75 NS_ESESSION_INIT = 'http://www.xmpp.org/extensions/xep-0116.html#ns-init' # XEP-0116 + 76 NS_EVENT = 'jabber:x:event' # XEP-0022 + 77 NS_FEATURE = 'http://jabber.org/protocol/feature-neg' + 78 NS_FILE = 'http://jabber.org/protocol/si/profile/file-transfer' # XEP-0096 + 79 NS_FORWARD = 'urn:xmpp:forward:0' # XEP-0297 + 80 NS_GAMING = 'http://jabber.org/protocol/gaming' # XEP-0196 + 81 NS_GATEWAY = 'jabber:iq:gateway' # XEP-0100 + 82 NS_GEOLOC = 'http://jabber.org/protocol/geoloc' # XEP-0080 + 83 NS_GROUPCHAT = 'gc-1.0' + 84 NS_HTTP_AUTH = 'http://jabber.org/protocol/http-auth' # XEP-0070 + 85 NS_HTTP_BIND = 'http://jabber.org/protocol/httpbind' # XEP-0124 + 86 NS_IBB = 'http://jabber.org/protocol/ibb' + 87 NS_INVISIBLE = 'presence-invisible' # Jabberd2 + 88 NS_IQ = 'iq' # Jabberd2 + 89 NS_JINGLE ='urn:xmpp:jingle:1' # XEP-0166 + 90 NS_JINGLE_ERRORS = 'urn:xmpp:jingle:errors:1' # XEP-0166 + 91 NS_JINGLE_RTP = 'urn:xmpp:jingle:apps:rtp:1' # XEP-0167 + 92 NS_JINGLE_RTP_AUDIO = 'urn:xmpp:jingle:apps:rtp:audio' # XEP-0167 + 93 NS_JINGLE_RTP_VIDEO = 'urn:xmpp:jingle:apps:rtp:video' # XEP-0167 + 94 NS_JINGLE_FILE_TRANSFER ='urn:xmpp:jingle:apps:file-transfer:3' # XEP-0234 + 95 NS_JINGLE_XTLS='urn:xmpp:jingle:security:xtls:0' # XTLS: EXPERIMENTAL security layer of jingle + 96 NS_JINGLE_RAW_UDP = 'urn:xmpp:jingle:transports:raw-udp:1' # XEP-0177 + 97 NS_JINGLE_ICE_UDP = 'urn:xmpp:jingle:transports:ice-udp:1' # XEP-0176 + 98 NS_JINGLE_BYTESTREAM ='urn:xmpp:jingle:transports:s5b:1' # XEP-0260 + 99 NS_JINGLE_IBB = 'urn:xmpp:jingle:transports:ibb:1' # XEP-0261 + 100 NS_LAST = 'jabber:iq:last' + 101 NS_LOCATION = 'http://jabber.org/protocol/geoloc' # XEP-0080 + 102 NS_MESSAGE = 'message' # Jabberd2 + 103 NS_MOOD = 'http://jabber.org/protocol/mood' # XEP-0107 + 104 NS_MUC = 'http://jabber.org/protocol/muc' + 105 NS_MUC_USER = NS_MUC + '#user' + 106 NS_MUC_ADMIN = NS_MUC + '#admin' + 107 NS_MUC_OWNER = NS_MUC + '#owner' + 108 NS_MUC_UNIQUE = NS_MUC + '#unique' + 109 NS_MUC_CONFIG = NS_MUC + '#roomconfig' + 110 NS_NICK = 'http://jabber.org/protocol/nick' # XEP-0172 + 111 NS_OFFLINE = 'http://www.jabber.org/jeps/jep-0030.html' # XEP-0013 + 112 NS_PHYSLOC = 'http://jabber.org/protocol/physloc' # XEP-0112 + 113 NS_PING = 'urn:xmpp:ping' # XEP-0199 + 114 NS_PRESENCE = 'presence' # Jabberd2 + 115 NS_PRIVACY = 'jabber:iq:privacy' + 116 NS_PRIVATE = 'jabber:iq:private' + 117 NS_PROFILE = 'http://jabber.org/protocol/profile' # XEP-0154 + 118 NS_PUBSUB = 'http://jabber.org/protocol/pubsub' # XEP-0060 + 119 NS_PUBSUB_EVENT = 'http://jabber.org/protocol/pubsub#event' + 120 NS_PUBSUB_PUBLISH_OPTIONS = NS_PUBSUB + '#publish-options' # XEP-0060 + 121 NS_PUBSUB_OWNER = 'http://jabber.org/protocol/pubsub#owner' # XEP-0060 + 122 NS_REGISTER = 'jabber:iq:register' + 123 NS_ROSTER = 'jabber:iq:roster' + 124 NS_ROSTERNOTES = 'storage:rosternotes' + 125 NS_ROSTERX = 'http://jabber.org/protocol/rosterx' # XEP-0144 + 126 NS_ROSTER_VER = 'urn:xmpp:features:rosterver' # XEP-0273 + 127 NS_RPC = 'jabber:iq:rpc' # XEP-0009 + 128 NS_RSM = 'http://jabber.org/protocol/rsm' + 129 NS_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl' + 130 NS_SECLABEL = 'urn:xmpp:sec-label:0' + 131 NS_SECLABEL_CATALOG = 'urn:xmpp:sec-label:catalog:2' + 132 NS_SEARCH = 'jabber:iq:search' + 133 NS_SERVER = 'jabber:server' + 134 NS_SESSION = 'urn:ietf:params:xml:ns:xmpp-session' + 135 NS_SI = 'http://jabber.org/protocol/si' # XEP-0096 + 136 NS_SI_PUB = 'http://jabber.org/protocol/sipub' # XEP-0137 + 137 NS_SIGNED = 'jabber:x:signed' # XEP-0027 + 138 NS_SSN = 'urn:xmpp:ssn' # XEP-0155 + 139 NS_STANZA_CRYPTO = 'http://www.xmpp.org/extensions/xep-0200.html#ns' # XEP-0200 + 140 NS_STANZAS = 'urn:ietf:params:xml:ns:xmpp-stanzas' + 141 NS_STREAM = 'http://affinix.com/jabber/stream' + 142 NS_STREAMS = 'http://etherx.jabber.org/streams' + 143 NS_TIME = 'jabber:iq:time' # XEP-0900 + 144 NS_TIME_REVISED = 'urn:xmpp:time' # XEP-0202 + 145 NS_TLS = 'urn:ietf:params:xml:ns:xmpp-tls' + 146 NS_TUNE = 'http://jabber.org/protocol/tune' # XEP-0118 + 147 NS_VACATION = 'http://jabber.org/protocol/vacation' + 148 NS_VCARD = 'vcard-temp' + 149 NS_GMAILNOTIFY = 'google:mail:notify' + 150 NS_GTALKSETTING = 'google:setting' + 151 NS_VCARD_UPDATE = NS_VCARD + ':x:update' + 152 NS_VERSION = 'jabber:iq:version' + 153 NS_VIEWING = 'http://jabber.org/protocol/viewing' # XEP--197 + 154 NS_WAITINGLIST = 'http://jabber.org/protocol/waitinglist' # XEP-0130 + 155 NS_XHTML_IM = 'http://jabber.org/protocol/xhtml-im' # XEP-0071 + 156 NS_XHTML = 'http://www.w3.org/1999/xhtml' # " + 157 NS_DATA_LAYOUT = 'http://jabber.org/protocol/xdata-layout' # XEP-0141 + 158 NS_DATA_VALIDATE = 'http://jabber.org/protocol/xdata-validate' # XEP-0122 + 159 NS_XMPP_STREAMS = 'urn:ietf:params:xml:ns:xmpp-streams' + 160 NS_RECEIPTS = 'urn:xmpp:receipts' + 161 NS_PUBKEY_PUBKEY = 'urn:xmpp:pubkey:2' # XEP-0189 + 162 NS_PUBKEY_REVOKE = 'urn:xmpp:revoke:2' + 163 NS_PUBKEY_ATTEST = 'urn:xmpp:attest:2' + 164 NS_STREAM_MGMT = 'urn:xmpp:sm:2' # XEP-198 + 165 NS_HASHES = 'urn:xmpp:hashes:0' # XEP-300 + 166 NS_HASHES_MD5 = 'urn:xmpp:hash-function-textual-names:md5' + 167 NS_HASHES_SHA1 = 'urn:xmpp:hash-function-textual-names:sha-1' + 168 NS_HASHES_SHA256 = 'urn:xmpp:hash-function-textual-names:sha-256' + 169 NS_HASHES_SHA512 = 'urn:xmpp:hash-function-textual-names:sha-512' + 170 + 171 xmpp_stream_error_conditions = ''' + 172 bad-format -- -- -- The entity has sent XML that cannot be processed. + 173 bad-namespace-prefix -- -- -- The entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires such a prefix. + 174 conflict -- -- -- The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream. + 175 connection-timeout -- -- -- The entity has not generated any traffic over the stream for some period of time. + 176 host-gone -- -- -- The value of the 'to' attribute provided by the initiating entity in the stream header corresponds to a hostname that is no longer hosted by the server. + 177 host-unknown -- -- -- The value of the 'to' attribute provided by the initiating entity in the stream header does not correspond to a hostname that is hosted by the server. + 178 improper-addressing -- -- -- A stanza sent between two servers lacks a 'to' or 'from' attribute (or the attribute has no value). + 179 internal-server-error -- -- -- The server has experienced a misconfiguration or an otherwise-undefined internal error that prevents it from servicing the stream. + 180 invalid-from -- cancel -- -- The JID or hostname provided in a 'from' address does not match an authorized JID or validated domain negotiated between servers via SASL or dialback, or between a client and a server via authentication and resource authorization. + 181 invalid-id -- -- -- The stream ID or dialback ID is invalid or does not match an ID previously provided. + 182 invalid-namespace -- -- -- The streams namespace name is something other than "http://etherx.jabber.org/streams" or the dialback namespace name is something other than "jabber:server:dialback". + 183 invalid-xml -- -- -- The entity has sent invalid XML over the stream to a server that performs validation. + 184 not-authorized -- -- -- The entity has attempted to send data before the stream has been authenticated, or otherwise is not authorized to perform an action related to stream negotiation. + 185 policy-violation -- -- -- The entity has violated some local service policy. + 186 remote-connection-failed -- -- -- The server is unable to properly connect to a remote resource that is required for authentication or authorization. + 187 resource-constraint -- -- -- The server lacks the system resources necessary to service the stream. + 188 restricted-xml -- -- -- The entity has attempted to send restricted XML features such as a comment, processing instruction, DTD, entity reference, or unescaped character. + 189 see-other-host -- -- -- The server will not provide service to the initiating entity but is redirecting traffic to another host. + 190 system-shutdown -- -- -- The server is being shut down and all active streams are being closed. + 191 undefined-condition -- -- -- The error condition is not one of those defined by the other conditions in this list. + 192 unsupported-encoding -- -- -- The initiating entity has encoded the stream in an encoding that is not supported by the server. + 193 unsupported-stanza-type -- -- -- The initiating entity has sent a first-level child of the stream that is not supported by the server. + 194 unsupported-version -- -- -- The value of the 'version' attribute provided by the initiating entity in the stream header specifies a version of XMPP that is not supported by the server. + 195 xml-not-well-formed -- -- -- The initiating entity has sent XML that is not well-formed.''' + 196 + 197 xmpp_stanza_error_conditions = ''' + 198 bad-request -- 400 -- modify -- The sender has sent XML that is malformed or that cannot be processed. + 199 conflict -- 409 -- cancel -- Access cannot be granted because an existing resource or session exists with the same name or address. + 200 feature-not-implemented -- 501 -- cancel -- The feature requested is not implemented by the recipient or server and therefore cannot be processed. + 201 forbidden -- 403 -- auth -- The requesting entity does not possess the required permissions to perform the action. + 202 gone -- 302 -- modify -- The recipient or server can no longer be contacted at this address. + 203 internal-server-error -- 500 -- wait -- The server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error. + 204 item-not-found -- 404 -- cancel -- The addressed JID or item requested cannot be found. + 205 jid-malformed -- 400 -- modify -- The value of the 'to' attribute in the sender's stanza does not adhere to the syntax defined in Addressing Scheme. + 206 not-acceptable -- 406 -- cancel -- The recipient or server understands the request but is refusing to process it because it does not meet criteria defined by the recipient or server. + 207 not-allowed -- 405 -- cancel -- The recipient or server does not allow any entity to perform the action. + 208 not-authorized -- 401 -- auth -- The sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials. + 209 payment-required -- 402 -- auth -- The requesting entity is not authorized to access the requested service because payment is required. + 210 recipient-unavailable -- 404 -- wait -- The intended recipient is temporarily unavailable. + 211 redirect -- 302 -- modify -- The recipient or server is redirecting requests for this information to another entity. + 212 registration-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because registration is required. + 213 remote-server-not-found -- 404 -- cancel -- A remote server or service specified as part or all of the JID of the intended recipient does not exist. + 214 remote-server-timeout -- 504 -- wait -- A remote server or service specified as part or all of the JID of the intended recipient could not be contacted within a reasonable amount of time. + 215 resource-constraint -- 500 -- wait -- The server or recipient lacks the system resources necessary to service the request. + 216 service-unavailable -- 503 -- cancel -- The server or recipient does not currently provide the requested service. + 217 subscription-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because a subscription is required. + 218 undefined-condition -- 500 -- -- Undefined Condition + 219 unexpected-request -- 400 -- wait -- The recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order).''' + 220 + 221 sasl_error_conditions = ''' + 222 aborted -- -- -- The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element. + 223 incorrect-encoding -- -- -- The data provided by the initiating entity could not be processed because the [BASE64]Josefsson, S., The Base16, Base32, and Base64 Data Encodings, July 2003. encoding is incorrect (e.g., because the encoding does not adhere to the definition in Section 3 of [BASE64]Josefsson, S., The Base16, Base32, and Base64 Data Encodings, July 2003.); sent in reply to a <response/> element or an <auth/> element with initial response data. + 224 invalid-authzid -- -- -- The authzid provided by the initiating entity is invalid, either because it is incorrectly formatted or because the initiating entity does not have permissions to authorize that ID; sent in reply to a <response/> element or an <auth/> element with initial response data. + 225 invalid-mechanism -- -- -- The initiating entity did not provide a mechanism or requested a mechanism that is not supported by the receiving entity; sent in reply to an <auth/> element. + 226 mechanism-too-weak -- -- -- The mechanism requested by the initiating entity is weaker than server policy permits for that initiating entity; sent in reply to a <response/> element or an <auth/> element with initial response data. + 227 not-authorized -- -- -- The authentication failed because the initiating entity did not provide valid credentials (this includes but is not limited to the case of an unknown username); sent in reply to a <response/> element or an <auth/> element with initial response data. + 228 temporary-auth-failure -- -- -- The authentication failed because of a temporary error condition within the receiving entity; sent in reply to an <auth/> element or <response/> element.''' + 229 + 230 ERRORS, _errorcodes = {}, {} + 231 for ns, errname, errpool in ((NS_XMPP_STREAMS, 'STREAM', + 232 xmpp_stream_error_conditions), (NS_STANZAS, 'ERR', xmpp_stanza_error_conditions), + 233 (NS_SASL, 'SASL', sasl_error_conditions)): + 234 for err in errpool.split('\n')[1:]: + 235 cond, code, typ, text = err.split(' -- ') + 236 name = errname + '_' + ascii_upper(cond).replace('-', '_') + 237 locals()[name] = ns + ' ' + cond + 238 ERRORS[ns + ' ' + cond] = [code, typ, text] + 239 if code: + 240 _errorcodes[code] = cond + 241 del ns, errname, errpool, err, cond, code, typ, text + 242 +
243 -def isResultNode(node): +
244 """ + 245 Return true if the node is a positive reply + 246 """ + 247 return node and node.getType() == 'result' +
248 +
249 -def isErrorNode(node): +
250 """ + 251 Return true if the node is a negative reply + 252 """ + 253 return node and node.getType() == 'error' +
254 +
255 -class NodeProcessed(Exception): +
256 """ + 257 Exception that should be raised by handler when the handling should be + 258 stopped + 259 """ + 260 pass +
261 +
262 -class StreamError(Exception): +
263 """ + 264 Base exception class for stream errors + 265 """ + 266 pass +
267 +
268 -class BadFormat(StreamError): +
269 pass +
270 +
271 -class BadNamespacePrefix(StreamError): +
272 pass +
273 +
274 -class Conflict(StreamError): +
275 pass +
276 +
277 -class ConnectionTimeout(StreamError): +
278 pass +
279 +
280 -class HostGone(StreamError): +
281 pass +
282 +
283 -class HostUnknown(StreamError): +
284 pass +
285 +
286 -class ImproperAddressing(StreamError): +
287 pass +
288 +
289 -class InternalServerError(StreamError): +
290 pass +
291 +
292 -class InvalidFrom(StreamError): +
293 pass +
294 +
295 -class InvalidID(StreamError): +
296 pass +
297 +
298 -class InvalidNamespace(StreamError): +
299 pass +
300 +
301 -class InvalidXML(StreamError): +
302 pass +
303 +
304 -class NotAuthorized(StreamError): +
305 pass +
306 +
307 -class PolicyViolation(StreamError): +
308 pass +
309 +
310 -class RemoteConnectionFailed(StreamError): +
311 pass +
312 +
313 -class ResourceConstraint(StreamError): +
314 pass +
315 +
316 -class RestrictedXML(StreamError): +
317 pass +
318 +
319 -class SeeOtherHost(StreamError): +
320 pass +
321 +
322 -class SystemShutdown(StreamError): +
323 pass +
324 +
325 -class UndefinedCondition(StreamError): +
326 pass +
327 +
328 -class UnsupportedEncoding(StreamError): +
329 pass +
330 +
331 -class UnsupportedStanzaType(StreamError): +
332 pass +
333 +
334 -class UnsupportedVersion(StreamError): +
335 pass +
336 +
337 -class XMLNotWellFormed(StreamError): +
338 pass +
339 + 340 stream_exceptions = {'bad-format': BadFormat, + 341 'bad-namespace-prefix': BadNamespacePrefix, + 342 'conflict': Conflict, + 343 'connection-timeout': ConnectionTimeout, + 344 'host-gone': HostGone, + 345 'host-unknown': HostUnknown, + 346 'improper-addressing': ImproperAddressing, + 347 'internal-server-error': InternalServerError, + 348 'invalid-from': InvalidFrom, + 349 'invalid-id': InvalidID, + 350 'invalid-namespace': InvalidNamespace, + 351 'invalid-xml': InvalidXML, + 352 'not-authorized': NotAuthorized, + 353 'policy-violation': PolicyViolation, + 354 'remote-connection-failed': RemoteConnectionFailed, + 355 'resource-constraint': ResourceConstraint, + 356 'restricted-xml': RestrictedXML, + 357 'see-other-host': SeeOtherHost, + 358 'system-shutdown': SystemShutdown, + 359 'undefined-condition': UndefinedCondition, + 360 'unsupported-encoding': UnsupportedEncoding, + 361 'unsupported-stanza-type': UnsupportedStanzaType, + 362 'unsupported-version': UnsupportedVersion, + 363 'xml-not-well-formed': XMLNotWellFormed} + 364 +
365 -class JID: +
366 """ + 367 JID can be built from string, modified, compared, serialised into string + 368 """ + 369 +
370 - def __init__(self, jid=None, node='', domain='', resource=''): +
371 """ + 372 JID can be specified as string (jid argument) or as separate parts + 373 + 374 Examples: + 375 JID('node@domain/resource') + 376 JID(node='node',domain='domain.org') + 377 """ + 378 if not jid and not domain: + 379 raise ValueError('JID must contain at least domain name') + 380 elif type(jid) == type(self): + 381 self.node, self.domain = jid.node, jid.domain + 382 self.resource = jid.resource + 383 elif domain: + 384 self.node, self.domain, self.resource = node, domain, resource + 385 else: + 386 if jid.find('@') + 1: + 387 self.node, jid = jid.split('@', 1) + 388 else: + 389 self.node = '' + 390 if jid.find('/')+1: + 391 self.domain, self.resource = jid.split('/', 1) + 392 else: + 393 self.domain, self.resource = jid, '' +
394 +
395 - def getNode(self): +
396 """ + 397 Return the node part of the JID + 398 """ + 399 return self.node +
400 +
401 - def setNode(self, node): +
402 """ + 403 Set the node part of the JID to new value. Specify None to remove + 404 the node part + 405 """ + 406 self.node = node.lower() +
407 +
408 - def getDomain(self): +
409 """ + 410 Return the domain part of the JID + 411 """ + 412 return self.domain +
413 +
414 - def setDomain(self, domain): +
415 """ + 416 Set the domain part of the JID to new value + 417 """ + 418 self.domain = domain.lower() +
419 +
420 - def getResource(self): +
421 """ + 422 Return the resource part of the JID + 423 """ + 424 return self.resource +
425 +
426 - def setResource(self, resource): +
427 """ + 428 Set the resource part of the JID to new value. Specify None to remove the + 429 resource part + 430 """ + 431 self.resource = resource +
432 +
433 - def getStripped(self): +
434 """ + 435 Return the bare representation of JID. I.e. string value w/o resource + 436 """ + 437 return self.__str__(0) +
438 +
439 - def __eq__(self, other): +
440 """ + 441 Compare the JID to another instance or to string for equality + 442 """ + 443 try: + 444 other = JID(other) + 445 except ValueError: + 446 return 0 + 447 return self.resource == other.resource and \ + 448 self.__str__(0) == other.__str__(0) +
449 +
450 - def __ne__(self, other): +
451 """ + 452 Compare the JID to another instance or to string for non-equality + 453 """ + 454 return not self.__eq__(other) +
455 +
456 - def bareMatch(self, other): +
457 """ + 458 Compare the node and domain parts of the JID's for equality + 459 """ + 460 return self.__str__(0) == JID(other).__str__(0) +
461 +
462 - def __str__(self, wresource=1): +
463 """ + 464 Serialise JID into string + 465 """ + 466 if self.node: + 467 jid = self.node + '@' + self.domain + 468 else: + 469 jid = self.domain + 470 if wresource and self.resource: + 471 return jid + '/' + self.resource + 472 return jid +
473 +
474 - def __hash__(self): +
475 """ + 476 Produce hash of the JID, Allows to use JID objects as keys of the + 477 dictionary + 478 """ + 479 return hash(str(self)) +
480 +
481 -class BOSHBody(Node): +
482 """ + 483 <body> tag that wraps usual XMPP stanzas in XMPP over BOSH + 484 """ + 485 +
486 - def __init__(self, attrs={}, payload=[], node=None): +
487 Node.__init__(self, tag='body', attrs=attrs, payload=payload, node=node) + 488 self.setNamespace(NS_HTTP_BIND) +
489 + 490 +
491 -class Protocol(Node): +
492 """ + 493 A "stanza" object class. Contains methods that are common for presences, iqs + 494 and messages + 495 """ + 496 +
497 - def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, + 498 payload=[], timestamp=None, xmlns=None, node=None): +
499 """ + 500 Constructor, name is the name of the stanza + 501 i.e. 'message' or 'presence'or 'iq' + 502 + 503 to is the value of 'to' attribure, 'typ' - 'type' attribute + 504 frn - from attribure, attrs - other attributes mapping, + 505 payload - same meaning as for simplexml payload definition + 506 timestamp - the time value that needs to be stamped over stanza + 507 xmlns - namespace of top stanza node + 508 node - parsed or unparsed stana to be taken as prototype. + 509 """ + 510 if not attrs: + 511 attrs = {} + 512 if to: + 513 attrs['to'] = to + 514 if frm: + 515 attrs['from'] = frm + 516 if typ: + 517 attrs['type'] = typ + 518 Node.__init__(self, tag=name, attrs=attrs, payload=payload, node=node) + 519 if not node and xmlns: + 520 self.setNamespace(xmlns) + 521 if self['to']: + 522 self.setTo(self['to']) + 523 if self['from']: + 524 self.setFrom(self['from']) + 525 if node and type(self) == type(node) and \ + 526 self.__class__ == node.__class__ and self.attrs.has_key('id'): + 527 del self.attrs['id'] + 528 self.timestamp = None + 529 for d in self.getTags('delay', namespace=NS_DELAY2): + 530 try: + 531 if d.getAttr('stamp') < self.getTimestamp2(): + 532 self.setTimestamp(d.getAttr('stamp')) + 533 except Exception: + 534 pass + 535 if not self.timestamp: + 536 for x in self.getTags('x', namespace=NS_DELAY): + 537 try: + 538 if x.getAttr('stamp') < self.getTimestamp(): + 539 self.setTimestamp(x.getAttr('stamp')) + 540 except Exception: + 541 pass + 542 if timestamp is not None: + 543 self.setTimestamp(timestamp) # To auto-timestamp stanza just pass timestamp='' +
544 +
545 - def getTo(self): +
546 """ + 547 Return value of the 'to' attribute + 548 """ + 549 try: + 550 return self['to'] + 551 except: + 552 return None +
553 +
554 - def getFrom(self): +
555 """ + 556 Return value of the 'from' attribute + 557 """ + 558 try: + 559 return self['from'] + 560 except: + 561 return None +
562 +
563 - def getTimestamp(self): +
564 """ + 565 Return the timestamp in the 'yyyymmddThhmmss' format + 566 """ + 567 if self.timestamp: + 568 return self.timestamp + 569 return time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) +
570 +
571 - def getTimestamp2(self): +
572 """ + 573 Return the timestamp in the 'yyyymmddThhmmss' format + 574 """ + 575 if self.timestamp: + 576 return self.timestamp + 577 return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) +
578 +
579 - def getID(self): +
580 """ + 581 Return the value of the 'id' attribute + 582 """ + 583 return self.getAttr('id') +
584 +
585 - def setTo(self, val): +
586 """ + 587 Set the value of the 'to' attribute + 588 """ + 589 self.setAttr('to', JID(val)) +
590 +
591 - def getType(self): +
592 """ + 593 Return the value of the 'type' attribute + 594 """ + 595 return self.getAttr('type') +
596 +
597 - def setFrom(self, val): +
598 """ + 599 Set the value of the 'from' attribute + 600 """ + 601 self.setAttr('from', JID(val)) +
602 +
603 - def setType(self, val): +
604 """ + 605 Set the value of the 'type' attribute + 606 """ + 607 self.setAttr('type', val) +
608 +
609 - def setID(self, val): +
610 """ + 611 Set the value of the 'id' attribute + 612 """ + 613 self.setAttr('id', val) +
614 +
615 - def getError(self): +
616 """ + 617 Return the error-condition (if present) or the textual description + 618 of the error (otherwise) + 619 """ + 620 errtag = self.getTag('error') + 621 if errtag: + 622 for tag in errtag.getChildren(): + 623 if tag.getName() != 'text': + 624 return tag.getName() + 625 return errtag.getData() +
626 +
627 - def getErrorMsg(self): +
628 """ + 629 Return the textual description of the error (if present) + 630 or the error condition + 631 """ + 632 errtag = self.getTag('error') + 633 if errtag: + 634 for tag in errtag.getChildren(): + 635 if tag.getName() == 'text': + 636 return tag.getData() + 637 return self.getError() +
638 +
639 - def getErrorCode(self): +
640 """ + 641 Return the error code. Obsolete. + 642 """ + 643 return self.getTagAttr('error', 'code') +
644 +
645 - def getStatusConditions(self): +
646 """ + 647 Return the status conditions list as defined in XEP-0306. + 648 """ + 649 conds = [] + 650 condtag = self.getTag('conditions', namespace=NS_CONDITIONS) + 651 if condtag: + 652 for tag in condtag.getChildren(): + 653 conds.append(tag.getName()) + 654 return conds +
655 +
656 - def setError(self, error, code=None): +
657 """ + 658 Set the error code. Obsolete. Use error-conditions instead + 659 """ + 660 if code: + 661 if str(code) in _errorcodes.keys(): + 662 error = ErrorNode(_errorcodes[str(code)], text=error) + 663 else: + 664 error = ErrorNode(ERR_UNDEFINED_CONDITION, code=code, + 665 typ='cancel', text=error) + 666 elif type(error) in [type(''), type(u'')]: + 667 error=ErrorNode(error) + 668 self.setType('error') + 669 self.addChild(node=error) +
670 +
671 - def setTimestamp(self, val=None): +
672 """ + 673 Set the timestamp. timestamp should be the yyyymmddThhmmss string + 674 """ + 675 if not val: + 676 val = time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) + 677 self.timestamp=val + 678 self.setTag('x', {'stamp': self.timestamp}, namespace=NS_DELAY) +
679 +
680 - def getProperties(self): +
681 """ + 682 Return the list of namespaces to which belongs the direct childs of element + 683 """ + 684 props = [] + 685 for child in self.getChildren(): + 686 prop = child.getNamespace() + 687 if prop not in props: + 688 props.append(prop) + 689 return props +
690 +
691 - def __setitem__(self, item, val): +
692 """ + 693 Set the item 'item' to the value 'val' + 694 """ + 695 if item in ['to', 'from']: + 696 val = JID(val) + 697 return self.setAttr(item, val) +
698 + 699 +
700 -class Message(Protocol): +
701 """ + 702 XMPP Message stanza - "push" mechanism + 703 """ + 704 +
705 - def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None, + 706 attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, + 707 node=None): +
708 """ + 709 You can specify recipient, text of message, type of message any + 710 additional attributes, sender of the message, any additional payload + 711 (f.e. jabber:x:delay element) and namespace in one go. + 712 + 713 Alternatively you can pass in the other XML object as the 'node' + 714 parameted to replicate it as message + 715 """ + 716 Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, + 717 payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) + 718 if body: + 719 self.setBody(body) + 720 if xhtml: + 721 self.setXHTML(xhtml) + 722 if subject is not None: + 723 self.setSubject(subject) +
724 +
725 - def getBody(self): +
726 """ + 727 Return text of the message + 728 """ + 729 return self.getTagData('body') +
730 +
731 - def getXHTML(self, xmllang=None): +
732 """ + 733 Return serialized xhtml-im element text of the message + 734 + 735 TODO: Returning a DOM could make rendering faster. + 736 """ + 737 xhtml = self.getTag('html') + 738 if xhtml: + 739 if xmllang: + 740 body = xhtml.getTag('body', attrs={'xml:lang': xmllang}) + 741 else: + 742 body = xhtml.getTag('body') + 743 return str(body) + 744 return None +
745 +
746 - def getSubject(self): +
747 """ + 748 Return subject of the message + 749 """ + 750 return self.getTagData('subject') +
751 +
752 - def getThread(self): +
753 """ + 754 Return thread of the message + 755 """ + 756 return self.getTagData('thread') +
757 +
758 - def setBody(self, val): +
759 """ + 760 Set the text of the message""" + 761 self.setTagData('body', val) +
762 +
763 - def setXHTML(self, val, xmllang=None): +
764 """ + 765 Sets the xhtml text of the message (XEP-0071). The parameter is the + 766 "inner html" to the body. + 767 """ + 768 try: + 769 if xmllang: + 770 dom = NodeBuilder('<body xmlns="%s" xml:lang="%s">%s</body>' \ + 771 % (NS_XHTML, xmllang, val)).getDom() + 772 else: + 773 dom = NodeBuilder('<body xmlns="%s">%s</body>' % (NS_XHTML, + 774 val), 0).getDom() + 775 if self.getTag('html'): + 776 self.getTag('html').addChild(node=dom) + 777 else: + 778 self.setTag('html', namespace=NS_XHTML_IM).addChild(node=dom) + 779 except Exception, e: + 780 print "Error", e +
781 # FIXME: log. we could not set xhtml (parse error, whatever) + 782 +
783 - def setSubject(self, val): +
784 """ + 785 Set the subject of the message + 786 """ + 787 self.setTagData('subject', val) +
788 +
789 - def setThread(self, val): +
790 """ + 791 Set the thread of the message + 792 """ + 793 self.setTagData('thread', val) +
794 +
795 - def buildReply(self, text=None): +
796 """ + 797 Builds and returns another message object with specified text. The to, + 798 from, thread and type properties of new message are pre-set as reply to + 799 this message + 800 """ + 801 m = Message(to=self.getFrom(), frm=self.getTo(), body=text, + 802 typ=self.getType()) + 803 th = self.getThread() + 804 if th: + 805 m.setThread(th) + 806 return m +
807 +
808 - def getStatusCode(self): +
809 """ + 810 Return the status code of the message (for groupchat config change) + 811 """ + 812 attrs = [] + 813 for xtag in self.getTags('x'): + 814 for child in xtag.getTags('status'): + 815 attrs.append(child.getAttr('code')) + 816 return attrs +
817 +
818 -class Presence(Protocol): +
819 +
820 - def __init__(self, to=None, typ=None, priority=None, show=None, status=None, + 821 attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, + 822 node=None): +
823 """ + 824 You can specify recipient, type of message, priority, show and status + 825 values any additional attributes, sender of the presence, timestamp, any + 826 additional payload (f.e. jabber:x:delay element) and namespace in one go. + 827 Alternatively you can pass in the other XML object as the 'node' + 828 parameted to replicate it as presence + 829 """ + 830 Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, + 831 payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) + 832 if priority: + 833 self.setPriority(priority) + 834 if show: + 835 self.setShow(show) + 836 if status: + 837 self.setStatus(status) +
838 +
839 - def getPriority(self): +
840 """ + 841 Return the priority of the message + 842 """ + 843 return self.getTagData('priority') +
844 +
845 - def getShow(self): +
846 """ + 847 Return the show value of the message + 848 """ + 849 return self.getTagData('show') +
850 +
851 - def getStatus(self): +
852 """ + 853 Return the status string of the message + 854 """ + 855 return self.getTagData('status') +
856 +
857 - def setPriority(self, val): +
858 """ + 859 Set the priority of the message + 860 """ + 861 self.setTagData('priority', val) +
862 +
863 - def setShow(self, val): +
864 """ + 865 Set the show value of the message + 866 """ + 867 self.setTagData('show', val) +
868 +
869 - def setStatus(self, val): +
870 """ + 871 Set the status string of the message + 872 """ + 873 self.setTagData('status', val) +
874 +
875 - def _muc_getItemAttr(self, tag, attr): +
876 for xtag in self.getTags('x'): + 877 if xtag.getNamespace() not in (NS_MUC_USER, NS_MUC_ADMIN): + 878 continue + 879 for child in xtag.getTags(tag): + 880 return child.getAttr(attr) +
881 +
882 - def _muc_getSubTagDataAttr(self, tag, attr): +
883 for xtag in self.getTags('x'): + 884 if xtag.getNamespace() not in (NS_MUC_USER, NS_MUC_ADMIN): + 885 continue + 886 for child in xtag.getTags('item'): + 887 for cchild in child.getTags(tag): + 888 return cchild.getData(), cchild.getAttr(attr) + 889 return None, None +
890 +
891 - def getRole(self): +
892 """ + 893 Return the presence role (for groupchat) + 894 """ + 895 return self._muc_getItemAttr('item', 'role') +
896 +
897 - def getAffiliation(self): +
898 """ + 899 Return the presence affiliation (for groupchat) + 900 """ + 901 return self._muc_getItemAttr('item', 'affiliation') +
902 +
903 - def getNewNick(self): +
904 """ + 905 Return the status code of the presence (for groupchat) + 906 """ + 907 return self._muc_getItemAttr('item', 'nick') +
908 +
909 - def getJid(self): +
910 """ + 911 Return the presence jid (for groupchat) + 912 """ + 913 return self._muc_getItemAttr('item', 'jid') +
914 +
915 - def getReason(self): +
916 """ + 917 Returns the reason of the presence (for groupchat) + 918 """ + 919 return self._muc_getSubTagDataAttr('reason', '')[0] +
920 +
921 - def getActor(self): +
922 """ + 923 Return the reason of the presence (for groupchat) + 924 """ + 925 return self._muc_getSubTagDataAttr('actor', 'jid')[1] +
926 +
927 - def getStatusCode(self): +
928 """ + 929 Return the status code of the presence (for groupchat) + 930 """ + 931 attrs = [] + 932 for xtag in self.getTags('x'): + 933 for child in xtag.getTags('status'): + 934 attrs.append(child.getAttr('code')) + 935 return attrs +
936 +
937 -class Iq(Protocol): +
938 """ + 939 XMPP Iq object - get/set dialog mechanism + 940 """ + 941 +
942 - def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, + 943 payload=[], xmlns=NS_CLIENT, node=None): +
944 """ + 945 You can specify type, query namespace any additional attributes, + 946 recipient of the iq, sender of the iq, any additional payload (f.e. + 947 jabber:x:data node) and namespace in one go. + 948 + 949 Alternatively you can pass in the other XML object as the 'node' + 950 parameted to replicate it as an iq + 951 """ + 952 Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, + 953 xmlns=xmlns, node=node) + 954 if payload: + 955 self.setQueryPayload(payload) + 956 if queryNS: + 957 self.setQueryNS(queryNS) +
958 +
959 - def getQuery(self): +
960 """ + 961 Return the IQ's child element if it exists, None otherwise. + 962 """ + 963 children = self.getChildren() + 964 if children and self.getType() != 'error' and \ + 965 children[0].getName() != 'error': + 966 return children[0] +
967 +
968 - def getQueryNS(self): +
969 """ + 970 Return the namespace of the 'query' child element + 971 """ + 972 tag = self.getQuery() + 973 if tag: + 974 return tag.getNamespace() +
975 +
976 - def getQuerynode(self): +
977 """ + 978 Return the 'node' attribute value of the 'query' child element + 979 """ + 980 tag = self.getQuery() + 981 if tag: + 982 return tag.getAttr('node') +
983 +
984 - def getQueryPayload(self): +
985 """ + 986 Return the 'query' child element payload + 987 """ + 988 tag = self.getQuery() + 989 if tag: + 990 return tag.getPayload() +
991 +
992 - def getQueryChildren(self): +
993 """ + 994 Return the 'query' child element child nodes + 995 """ + 996 tag = self.getQuery() + 997 if tag: + 998 return tag.getChildren() +
999 +
1000 - def setQuery(self, name=None): +
1001 """ +1002 Change the name of the query node, creating it if needed. Keep the +1003 existing name if none is given (use 'query' if it's a creation). +1004 Return the query node. +1005 """ +1006 query = self.getQuery() +1007 if query is None: +1008 query = self.addChild('query') +1009 if name is not None: +1010 query.setName(name) +1011 return query +
1012 +
1013 - def setQueryNS(self, namespace): +
1014 """ +1015 Set the namespace of the 'query' child element +1016 """ +1017 self.setQuery().setNamespace(namespace) +
1018 +
1019 - def setQueryPayload(self, payload): +
1020 """ +1021 Set the 'query' child element payload +1022 """ +1023 self.setQuery().setPayload(payload) +
1024 +
1025 - def setQuerynode(self, node): +
1026 """ +1027 Set the 'node' attribute value of the 'query' child element +1028 """ +1029 self.setQuery().setAttr('node', node) +
1030 +
1031 - def buildReply(self, typ): +
1032 """ +1033 Build and return another Iq object of specified type. The to, from and +1034 query child node of new Iq are pre-set as reply to this Iq. +1035 """ +1036 iq = Iq(typ, to=self.getFrom(), frm=self.getTo(), +1037 attrs={'id': self.getID()}) +1038 iq.setQuery(self.getQuery().getName()).setNamespace(self.getQueryNS()) +1039 return iq +
1040 +
1041 -class Hashes(Node): +
1042 """ +1043 Hash elements for various XEPs as defined in XEP-300 +1044 """ +1045 +1046 """ +1047 RECOMENDED HASH USE: +1048 Algorithm Support +1049 MD2 MUST NOT +1050 MD4 MUST NOT +1051 MD5 MAY +1052 SHA-1 MUST +1053 SHA-256 MUST +1054 SHA-512 SHOULD +1055 """ +1056 +1057 supported = ('md5', 'sha-1', 'sha-256', 'sha-512') +1058 +
1059 - def __init__(self, nsp=NS_HASHES): +
1060 Node.__init__(self, None, {}, [], None, None,False, None) +1061 self.setNamespace(nsp) +1062 self.setName('hashes') +
1063 +
1064 - def calculateHash(self, algo, file_string): +
1065 """ +1066 Calculate the hash and add it. It is preferable doing it here +1067 instead of doing it all over the place in Gajim. +1068 """ +1069 hl = None +1070 hash_ = None +1071 # file_string can be a string or a file +1072 if type(file_string) == str: # if it is a string +1073 if algo == 'md5': +1074 hl = hashlib.md5() +1075 elif algo == 'sha-1': +1076 hl = hashlib.sha1() +1077 elif algo == 'sha-256': +1078 hl = hashlib.sha256() +1079 elif algo == 'sha-512': +1080 hl = hashlib.sha512() +1081 +1082 if hl: +1083 hl.update(file_string) +1084 hash_ = hl.hexdigest() +1085 else: # if it is a file +1086 +1087 if algo == 'md5': +1088 hl = hashlib.md5() +1089 elif algo == 'sha-1': +1090 hl = hashlib.sha1() +1091 elif algo == 'sha-256': +1092 hl = hashlib.sha256() +1093 elif algo == 'sha-512': +1094 hl = hashlib.sha512() +1095 +1096 if hl: +1097 for line in file_string: +1098 hl.update(line) +1099 hash_ = hl.hexdigest() +1100 +1101 return hash_ +
1102 +
1103 - def addHash(self, hash_, algo): +
1104 """ +1105 More than one hash can be added. Although it is permitted, it should +1106 not be done for big files because it could slow down Gajim. +1107 """ +1108 attrs = {} +1109 attrs['algo'] = algo +1110 self.addChild('hash', attrs, [hash_]) +
1111 +
1112 -class Acks(Node): +
1113 """ +1114 Acknowledgement elements for Stream Management +1115 """ +
1116 - def __init__(self, nsp=NS_STREAM_MGMT): +
1117 Node.__init__(self, None, {}, [], None, None, False, None) +1118 self.setNamespace(nsp) +
1119 +
1120 - def buildAnswer(self, handled): +
1121 """ +1122 handled is the number of stanzas handled +1123 """ +1124 self.setName('a') +1125 self.setAttr('h', handled) +
1126 +
1127 - def buildRequest(self): +
1128 self.setName('r') +
1129 +
1130 - def buildEnable(self, resume=False): +
1131 self.setName('enable') +1132 if resume: +1133 self.setAttr('resume', 'true') +
1134 +
1135 - def buildResume(self, handled, previd): +
1136 self.setName('resume') +1137 self.setAttr('h', handled) +1138 self.setAttr('previd', previd) +
1139 +
1140 -class ErrorNode(Node): +
1141 """ +1142 XMPP-style error element +1143 +1144 In the case of stanza error should be attached to XMPP stanza. +1145 In the case of stream-level errors should be used separately. +1146 """ +1147 +
1148 - def __init__(self, name, code=None, typ=None, text=None): +
1149 """ +1150 Mandatory parameter: name - name of error condition. +1151 Optional parameters: code, typ, text. +1152 Used for backwards compartibility with older jabber protocol. +1153 """ +1154 if name in ERRORS: +1155 cod, type_, txt = ERRORS[name] +1156 ns = name.split()[0] +1157 else: +1158 cod, ns, type_, txt = '500', NS_STANZAS, 'cancel', '' +1159 if typ: +1160 type_ = typ +1161 if code: +1162 cod = code +1163 if text: +1164 txt = text +1165 Node.__init__(self, 'error', {}, [Node(name)]) +1166 if type_: +1167 self.setAttr('type', type_) +1168 if not cod: +1169 self.setName('stream:error') +1170 if txt: +1171 self.addChild(node=Node(ns + ' text', {}, [txt])) +1172 if cod: +1173 self.setAttr('code', cod) +
1174 +
1175 -class Error(Protocol): +
1176 """ +1177 Used to quickly transform received stanza into error reply +1178 """ +1179 +
1180 - def __init__(self, node, error, reply=1): +
1181 """ +1182 Create error reply basing on the received 'node' stanza and the 'error' +1183 error condition +1184 +1185 If the 'node' is not the received stanza but locally created ('to' and +1186 'from' fields needs not swapping) specify the 'reply' argument as false. +1187 """ +1188 if reply: +1189 Protocol.__init__(self, to=node.getFrom(), frm=node.getTo(), node=node) +1190 else: +1191 Protocol.__init__(self, node=node) +1192 self.setError(error) +1193 if node.getType() == 'error': +1194 self.__str__ = self.__dupstr__ +
1195 +
1196 - def __dupstr__(self, dup1=None, dup2=None): +
1197 """ +1198 Dummy function used as preventor of creating error node in reply to error +1199 node. I.e. you will not be able to serialise "double" error into string. +1200 """ +1201 return '' +
1202 +
1203 -class DataField(Node): +
1204 """ +1205 This class is used in the DataForm class to describe the single data item +1206 +1207 If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122) then +1208 you will need to work with instances of this class. +1209 """ +1210 +
1211 - def __init__(self, name=None, value=None, typ=None, required=0, desc=None, +1212 options=[], node=None): +
1213 """ +1214 Create new data field of specified name,value and type +1215 +1216 Also 'required','desc' and 'options' fields can be set. Alternatively +1217 other XML object can be passed in as the 'node' parameted +1218 to replicate it as a new datafiled. +1219 """ +1220 Node.__init__(self, 'field', node=node) +1221 if name: +1222 self.setVar(name) +1223 if isinstance(value, (list, tuple)): +1224 self.setValues(value) +1225 elif value: +1226 self.setValue(value) +1227 if typ: +1228 self.setType(typ) +1229 elif not typ and not node: +1230 self.setType('text-single') +1231 if required: +1232 self.setRequired(required) +1233 if desc: +1234 self.setDesc(desc) +1235 if options: +1236 self.setOptions(options) +
1237 +
1238 - def setRequired(self, req=1): +
1239 """ +1240 Change the state of the 'required' flag +1241 """ +1242 if req: +1243 self.setTag('required') +1244 else: +1245 try: +1246 self.delChild('required') +1247 except ValueError: +1248 return +
1249 +
1250 - def isRequired(self): +
1251 """ +1252 Return in this field a required one +1253 """ +1254 return self.getTag('required') +
1255 +
1256 - def setDesc(self, desc): +
1257 """ +1258 Set the description of this field +1259 """ +1260 self.setTagData('desc', desc) +
1261 +
1262 - def getDesc(self): +
1263 """ +1264 Return the description of this field +1265 """ +1266 return self.getTagData('desc') +
1267 +
1268 - def setValue(self, val): +
1269 """ +1270 Set the value of this field +1271 """ +1272 self.setTagData('value', val) +
1273 +
1274 - def getValue(self): +
1275 return self.getTagData('value') +
1276 +
1277 - def setValues(self, lst): +
1278 """ +1279 Set the values of this field as values-list. Replaces all previous filed +1280 values! If you need to just add a value - use addValue method +1281 """ +1282 while self.getTag('value'): +1283 self.delChild('value') +1284 for val in lst: +1285 self.addValue(val) +
1286 +
1287 - def addValue(self, val): +
1288 """ +1289 Add one more value to this field. Used in 'get' iq's or such +1290 """ +1291 self.addChild('value', {}, [val]) +
1292 +
1293 - def getValues(self): +
1294 """ +1295 Return the list of values associated with this field +1296 """ +1297 ret = [] +1298 for tag in self.getTags('value'): +1299 ret.append(tag.getData()) +1300 return ret +
1301 +
1302 - def getOptions(self): +
1303 """ +1304 Return label-option pairs list associated with this field +1305 """ +1306 ret = [] +1307 for tag in self.getTags('option'): +1308 ret.append([tag.getAttr('label'), tag.getTagData('value')]) +1309 return ret +
1310 +
1311 - def setOptions(self, lst): +
1312 """ +1313 Set label-option pairs list associated with this field +1314 """ +1315 while self.getTag('option'): +1316 self.delChild('option') +1317 for opt in lst: +1318 self.addOption(opt) +
1319 +
1320 - def addOption(self, opt): +
1321 """ +1322 Add one more label-option pair to this field +1323 """ +1324 if isinstance(opt, basestring): +1325 self.addChild('option').setTagData('value', opt) +1326 else: +1327 self.addChild('option', {'label': opt[0]}).setTagData('value', +1328 opt[1]) +
1329 +
1330 - def getType(self): +
1331 """ +1332 Get type of this field +1333 """ +1334 return self.getAttr('type') +
1335 +
1336 - def setType(self, val): +
1337 """ +1338 Set type of this field +1339 """ +1340 return self.setAttr('type', val) +
1341 +
1342 - def getVar(self): +
1343 """ +1344 Get 'var' attribute value of this field +1345 """ +1346 return self.getAttr('var') +
1347 +
1348 - def setVar(self, val): +
1349 """ +1350 Set 'var' attribute value of this field +1351 """ +1352 return self.setAttr('var', val) +
1353 +
1354 -class DataForm(Node): +
1355 """ +1356 Used for manipulating dataforms in XMPP +1357 +1358 Relevant XEPs: 0004, 0068, 0122. Can be used in disco, pub-sub and many +1359 other applications. +1360 """ +
1361 - def __init__(self, typ=None, data=[], title=None, node=None): +
1362 """ +1363 Create new dataform of type 'typ'. 'data' is the list of DataField +1364 instances that this dataform contains, 'title' - the title string. You +1365 can specify the 'node' argument as the other node to be used as base for +1366 constructing this dataform +1367 +1368 title and instructions is optional and SHOULD NOT contain newlines. +1369 Several instructions MAY be present. +1370 'typ' can be one of ('form' | 'submit' | 'cancel' | 'result' ) +1371 'typ' of reply iq can be ( 'result' | 'set' | 'set' | 'result' ) respectively. +1372 'cancel' form can not contain any fields. All other forms contains AT LEAST one field. +1373 'title' MAY be included in forms of type "form" and "result" +1374 """ +1375 Node.__init__(self, 'x', node=node) +1376 if node: +1377 newkids = [] +1378 for n in self.getChildren(): +1379 if n.getName() == 'field': +1380 newkids.append(DataField(node=n)) +1381 else: +1382 newkids.append(n) +1383 self.kids = newkids +1384 if typ: +1385 self.setType(typ) +1386 self.setNamespace(NS_DATA) +1387 if title: +1388 self.setTitle(title) +1389 if isinstance(data, dict): +1390 newdata = [] +1391 for name in data.keys(): +1392 newdata.append(DataField(name, data[name])) +1393 data = newdata +1394 for child in data: +1395 if isinstance(child, basestring): +1396 self.addInstructions(child) +1397 elif child.__class__.__name__ == 'DataField': +1398 self.kids.append(child) +1399 else: +1400 self.kids.append(DataField(node=child)) +
1401 +
1402 - def getType(self): +
1403 """ +1404 Return the type of dataform +1405 """ +1406 return self.getAttr('type') +
1407 +
1408 - def setType(self, typ): +
1409 """ +1410 Set the type of dataform +1411 """ +1412 self.setAttr('type', typ) +
1413 +
1414 - def getTitle(self): +
1415 """ +1416 Return the title of dataform +1417 """ +1418 return self.getTagData('title') +
1419 +
1420 - def setTitle(self, text): +
1421 """ +1422 Set the title of dataform +1423 """ +1424 self.setTagData('title', text) +
1425 +
1426 - def getInstructions(self): +
1427 """ +1428 Return the instructions of dataform +1429 """ +1430 return self.getTagData('instructions') +
1431 +
1432 - def setInstructions(self, text): +
1433 """ +1434 Set the instructions of dataform +1435 """ +1436 self.setTagData('instructions', text) +
1437 +
1438 - def addInstructions(self, text): +
1439 """ +1440 Add one more instruction to the dataform +1441 """ +1442 self.addChild('instructions', {}, [text]) +
1443 +
1444 - def getField(self, name): +
1445 """ +1446 Return the datafield object with name 'name' (if exists) +1447 """ +1448 return self.getTag('field', attrs={'var': name}) +
1449 +
1450 - def setField(self, name): +
1451 """ +1452 Create if nessessary or get the existing datafield object with name +1453 'name' and return it +1454 """ +1455 f = self.getField(name) +1456 if f: +1457 return f +1458 return self.addChild(node=DataField(name)) +
1459 +
1460 - def asDict(self): +
1461 """ +1462 Represent dataform as simple dictionary mapping of datafield names to +1463 their values +1464 """ +1465 ret = {} +1466 for field in self.getTags('field'): +1467 name = field.getAttr('var') +1468 typ = field.getType() +1469 if isinstance(typ, basestring) and typ.endswith('-multi'): +1470 val = [] +1471 for i in field.getTags('value'): +1472 val.append(i.getData()) +1473 else: +1474 val = field.getTagData('value') +1475 ret[name] = val +1476 if self.getTag('instructions'): +1477 ret['instructions'] = self.getInstructions() +1478 return ret +
1479 +
1480 - def __getitem__(self, name): +
1481 """ +1482 Simple dictionary interface for getting datafields values by their names +1483 """ +1484 item = self.getField(name) +1485 if item: +1486 return item.getValue() +1487 raise IndexError('No such field') +
1488 +
1489 - def __setitem__(self, name, val): +
1490 """ +1491 Simple dictionary interface for setting datafields values by their names +1492 """ +1493 return self.setField(name).setValue(val) +
1494 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + -- cgit v1.2.3