persistent HTTP connections in BOSH roughly implemented, added hack for openfire incapability of after-SASL-stream-restart-response in BOSH, changed doubles quotes to single
This commit is contained in:
parent
cecce21c53
commit
a58618c843
15 changed files with 471 additions and 499 deletions
|
@ -550,6 +550,7 @@ class Connection(ConnectionHandlers):
|
||||||
self._proxy['bosh_hold'] = '1'
|
self._proxy['bosh_hold'] = '1'
|
||||||
self._proxy['bosh_wait'] = '60'
|
self._proxy['bosh_wait'] = '60'
|
||||||
self._proxy['bosh_content'] = 'text/xml; charset=utf-8'
|
self._proxy['bosh_content'] = 'text/xml; charset=utf-8'
|
||||||
|
self._proxy['wait_for_restart_response'] = False
|
||||||
|
|
||||||
|
|
||||||
log.info('Connecting to %s: [%s:%d]', self.name,
|
log.info('Connecting to %s: [%s:%d]', self.name,
|
||||||
|
|
|
@ -143,10 +143,10 @@ class SASL(PlugIn):
|
||||||
if "DIGEST-MD5" in mecs:
|
if "DIGEST-MD5" in mecs:
|
||||||
node=Node('auth',attrs={'xmlns': NS_SASL, 'mechanism': 'DIGEST-MD5'})
|
node=Node('auth',attrs={'xmlns': NS_SASL, 'mechanism': 'DIGEST-MD5'})
|
||||||
elif "PLAIN" in mecs:
|
elif "PLAIN" in mecs:
|
||||||
sasl_data='%s\x00%s\x00%s' % (self.username+'@' + self._owner.Server,
|
sasl_data='%s\x00%s\x00%s' % (self.username+'@' + self._owner.Server,
|
||||||
self.username, self.password)
|
self.username, self.password)
|
||||||
node=Node('auth', attrs={'xmlns':NS_SASL,'mechanism':'PLAIN'},
|
node=Node('auth', attrs={'xmlns':NS_SASL,'mechanism':'PLAIN'},
|
||||||
payload=[base64.encodestring(sasl_data).replace('\n','')])
|
payload=[base64.encodestring(sasl_data).replace('\n','')])
|
||||||
else:
|
else:
|
||||||
self.startsasl='failure'
|
self.startsasl='failure'
|
||||||
log.error('I can only use DIGEST-MD5 and PLAIN mecanisms.')
|
log.error('I can only use DIGEST-MD5 and PLAIN mecanisms.')
|
||||||
|
@ -173,12 +173,19 @@ class SASL(PlugIn):
|
||||||
self.startsasl='success'
|
self.startsasl='success'
|
||||||
log.info('Successfully authenticated with remote server.')
|
log.info('Successfully authenticated with remote server.')
|
||||||
handlers=self._owner.Dispatcher.dumpHandlers()
|
handlers=self._owner.Dispatcher.dumpHandlers()
|
||||||
|
|
||||||
|
# save old features. They will be used in case we won't get response on
|
||||||
|
# stream restart after SASL auth (happens with XMPP over BOSH with Openfire)
|
||||||
|
old_features = self._owner.Dispatcher.Stream.features
|
||||||
|
|
||||||
self._owner.Dispatcher.PlugOut()
|
self._owner.Dispatcher.PlugOut()
|
||||||
dispatcher_nb.Dispatcher().PlugIn(self._owner, after_SASL=True)
|
dispatcher_nb.Dispatcher().PlugIn(self._owner, after_SASL=True,
|
||||||
|
old_features=old_features)
|
||||||
|
|
||||||
self._owner.Dispatcher.restoreHandlers(handlers)
|
self._owner.Dispatcher.restoreHandlers(handlers)
|
||||||
self._owner.User = self.username
|
self._owner.User = self.username
|
||||||
if self.on_sasl :
|
if self.on_sasl :
|
||||||
self.on_sasl ()
|
self.on_sasl()
|
||||||
raise NodeProcessed
|
raise NodeProcessed
|
||||||
########################################3333
|
########################################3333
|
||||||
incoming_data = challenge.getData()
|
incoming_data = challenge.getData()
|
||||||
|
@ -232,7 +239,7 @@ class SASL(PlugIn):
|
||||||
|
|
||||||
class NonBlockingNonSASL(PlugIn):
|
class NonBlockingNonSASL(PlugIn):
|
||||||
''' Implements old Non-SASL (JEP-0078) authentication used
|
''' Implements old Non-SASL (JEP-0078) authentication used
|
||||||
in jabberd1.4 and transport authentication.
|
in jabberd1.4 and transport authentication.
|
||||||
'''
|
'''
|
||||||
def __init__(self, user, password, resource, on_auth):
|
def __init__(self, user, password, resource, on_auth):
|
||||||
''' Caches username, password and resource for auth. '''
|
''' Caches username, password and resource for auth. '''
|
||||||
|
@ -331,16 +338,6 @@ class NonBlockingBind(PlugIn):
|
||||||
PlugIn.__init__(self)
|
PlugIn.__init__(self)
|
||||||
self.bound=None
|
self.bound=None
|
||||||
|
|
||||||
def FeaturesHandler(self,conn,feats):
|
|
||||||
""" Determine if server supports resource binding and set some internal attributes accordingly. """
|
|
||||||
if not feats.getTag('bind',namespace=NS_BIND):
|
|
||||||
self.bound='failure'
|
|
||||||
log.error('Server does not requested binding.')
|
|
||||||
return
|
|
||||||
if feats.getTag('session',namespace=NS_SESSION): self.session=1
|
|
||||||
else: self.session=-1
|
|
||||||
self.bound=[]
|
|
||||||
|
|
||||||
def plugin(self, owner):
|
def plugin(self, owner):
|
||||||
''' Start resource binding, if allowed at this time. Used internally. '''
|
''' Start resource binding, if allowed at this time. Used internally. '''
|
||||||
if self._owner.Dispatcher.Stream.features:
|
if self._owner.Dispatcher.Stream.features:
|
||||||
|
@ -348,7 +345,23 @@ class NonBlockingBind(PlugIn):
|
||||||
self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features)
|
self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features)
|
||||||
except NodeProcessed:
|
except NodeProcessed:
|
||||||
pass
|
pass
|
||||||
else: self._owner.RegisterHandler('features', self.FeaturesHandler, xmlns=NS_STREAMS)
|
else:
|
||||||
|
self._owner.RegisterHandler('features', self.FeaturesHandler, xmlns=NS_STREAMS)
|
||||||
|
|
||||||
|
def FeaturesHandler(self,conn,feats):
|
||||||
|
''' Determine if server supports resource binding and set some internal attributes accordingly. '''
|
||||||
|
if not feats.getTag('bind',namespace=NS_BIND):
|
||||||
|
log.error('Server does not requested binding.')
|
||||||
|
# we try to bind resource anyway
|
||||||
|
#self.bound='failure'
|
||||||
|
self.bound=[]
|
||||||
|
return
|
||||||
|
if feats.getTag('session',namespace=NS_SESSION):
|
||||||
|
self.session=1
|
||||||
|
else:
|
||||||
|
self.session=-1
|
||||||
|
self.bound=[]
|
||||||
|
|
||||||
|
|
||||||
def plugout(self):
|
def plugout(self):
|
||||||
''' Remove Bind handler from owner's dispatcher. Used internally. '''
|
''' Remove Bind handler from owner's dispatcher. Used internally. '''
|
||||||
|
@ -404,77 +417,3 @@ class NonBlockingBind(PlugIn):
|
||||||
self.session = 0
|
self.session = 0
|
||||||
self.on_bound(None)
|
self.on_bound(None)
|
||||||
|
|
||||||
class NBComponentBind(PlugIn):
|
|
||||||
''' ComponentBind some JID to the current connection to allow
|
|
||||||
router know of our location.
|
|
||||||
'''
|
|
||||||
def __init__(self):
|
|
||||||
PlugIn.__init__(self)
|
|
||||||
self.bound=None
|
|
||||||
self.needsUnregister=None
|
|
||||||
|
|
||||||
def plugin(self,owner):
|
|
||||||
''' Start resource binding, if allowed at this time. Used internally. '''
|
|
||||||
if self._owner.Dispatcher.Stream.features:
|
|
||||||
try:
|
|
||||||
self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features)
|
|
||||||
except NodeProcessed:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self._owner.RegisterHandler('features', self.FeaturesHandler, xmlns=NS_STREAMS)
|
|
||||||
self.needsUnregister = 1
|
|
||||||
|
|
||||||
def plugout(self):
|
|
||||||
''' Remove ComponentBind handler from owner's dispatcher. Used internally. '''
|
|
||||||
if self.needsUnregister:
|
|
||||||
self._owner.UnregisterHandler('features', self.FeaturesHandler, xmlns=NS_STREAMS)
|
|
||||||
|
|
||||||
def Bind(self, domain = None, on_bind = None):
|
|
||||||
''' Perform binding. Use provided domain name (if not provided). '''
|
|
||||||
self._owner.onreceive(self._on_bound)
|
|
||||||
self.on_bind = on_bind
|
|
||||||
|
|
||||||
def _on_bound(self, resp):
|
|
||||||
if data:
|
|
||||||
self.Dispatcher.ProcessNonBlocking(data)
|
|
||||||
if self.bound is None:
|
|
||||||
return
|
|
||||||
self._owner.onreceive(None)
|
|
||||||
self._owner.SendAndWaitForResponse(
|
|
||||||
Protocol('bind', attrs={'name':domain}, xmlns=NS_COMPONENT_1),
|
|
||||||
func=self._on_bind_reponse)
|
|
||||||
|
|
||||||
def _on_bind_reponse(self, res):
|
|
||||||
if resp and resp.getAttr('error'):
|
|
||||||
log.error('Binding failed: %s.' % resp.getAttr('error'))
|
|
||||||
elif resp:
|
|
||||||
log.info('Successfully bound.')
|
|
||||||
if self.on_bind:
|
|
||||||
self.on_bind('ok')
|
|
||||||
else:
|
|
||||||
log.error('Binding failed: timeout expired.')
|
|
||||||
if self.on_bind:
|
|
||||||
self.on_bind(None)
|
|
||||||
|
|
||||||
def FeaturesHandler(self,conn,feats):
|
|
||||||
""" Determine if server supports resource binding and set some internal attributes accordingly. """
|
|
||||||
if not feats.getTag('bind',namespace=NS_BIND):
|
|
||||||
self.bound='failure'
|
|
||||||
log.error('Server does not requested binding.')
|
|
||||||
return
|
|
||||||
if feats.getTag('session',namespace=NS_SESSION): self.session=1
|
|
||||||
else: self.session=-1
|
|
||||||
self.bound=[]
|
|
||||||
|
|
||||||
def Bind(self,domain=None):
|
|
||||||
""" Perform binding. Use provided domain name (if not provided). """
|
|
||||||
while self.bound is None and self._owner.Process(1): pass
|
|
||||||
resp=self._owner.SendAndWaitForResponse(Protocol('bind',attrs={'name':domain},xmlns=NS_COMPONENT_1))
|
|
||||||
if resp and resp.getAttr('error'):
|
|
||||||
log.error('Binding failed: %s.'%resp.getAttr('error'))
|
|
||||||
elif resp:
|
|
||||||
log.info('Successfully bound.')
|
|
||||||
return 'ok'
|
|
||||||
else:
|
|
||||||
log.error('Binding failed: timeout expired.')
|
|
||||||
return ''
|
|
||||||
|
|
|
@ -24,9 +24,6 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
# (see http://www.xmpp.org/extensions/xep-0124.html#rids)
|
# (see http://www.xmpp.org/extensions/xep-0124.html#rids)
|
||||||
r = random.Random()
|
r = random.Random()
|
||||||
r.seed()
|
r.seed()
|
||||||
global FAKE_DESCRIPTOR
|
|
||||||
FAKE_DESCRIPTOR = FAKE_DESCRIPTOR - 1
|
|
||||||
self.fake_fd = FAKE_DESCRIPTOR
|
|
||||||
self.bosh_rid = r.getrandbits(50)
|
self.bosh_rid = r.getrandbits(50)
|
||||||
self.bosh_sid = None
|
self.bosh_sid = None
|
||||||
if locale.getdefaultlocale()[0]:
|
if locale.getdefaultlocale()[0]:
|
||||||
|
@ -35,7 +32,7 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
self.bosh_xml_lang = 'en'
|
self.bosh_xml_lang = 'en'
|
||||||
|
|
||||||
self.http_version = 'HTTP/1.1'
|
self.http_version = 'HTTP/1.1'
|
||||||
self.http_persistent = False
|
self.http_persistent = True
|
||||||
self.http_pipelining = False
|
self.http_pipelining = False
|
||||||
self.bosh_to = domain
|
self.bosh_to = domain
|
||||||
|
|
||||||
|
@ -57,33 +54,38 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
|
|
||||||
def connect(self, conn_5tuple, on_connect, on_connect_failure):
|
def connect(self, conn_5tuple, on_connect, on_connect_failure):
|
||||||
NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure)
|
NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure)
|
||||||
|
|
||||||
|
global FAKE_DESCRIPTOR
|
||||||
|
FAKE_DESCRIPTOR = FAKE_DESCRIPTOR - 1
|
||||||
|
self.fd = FAKE_DESCRIPTOR
|
||||||
|
|
||||||
|
self.http_persistent = True
|
||||||
self.http_socks.append(self.get_http_socket())
|
self.http_socks.append(self.get_http_socket())
|
||||||
self.tcp_connection_started()
|
self.tcp_connection_started()
|
||||||
|
|
||||||
# this connect() is not needed because sockets can be connected on send but
|
# this connect() is not needed because sockets can be connected on send but
|
||||||
# we need to know if host is reachable in order to invoke callback for
|
# we need to know if host is reachable in order to invoke callback for
|
||||||
# failure when connecting (it's different than callback for errors
|
# connecting failurei eventually (it's different than callback for errors
|
||||||
# occurring after connection is etabilished)
|
# occurring after connection is etabilished)
|
||||||
|
|
||||||
self.http_socks[0].connect(
|
self.http_socks[0].connect(
|
||||||
conn_5tuple = conn_5tuple,
|
conn_5tuple = conn_5tuple,
|
||||||
on_connect = lambda: self._on_connect(self.http_socks[0]),
|
on_connect = lambda: self._on_connect(self.http_socks[0]),
|
||||||
on_connect_failure = self._on_connect_failure)
|
on_connect_failure = self._on_connect_failure)
|
||||||
|
|
||||||
|
def set_timeout(self, timeout):
|
||||||
|
if self.state in [CONNECTING, CONNECTED] and self.fd != -1:
|
||||||
def get_fd(self):
|
NonBlockingTransport.set_timeout(self, timeout)
|
||||||
return self.fake_fd
|
else:
|
||||||
|
log.warn('set_timeout: TIMEOUT NOT SET: state is %s, fd is %s' % (self.state, self.fd))
|
||||||
|
|
||||||
def on_http_request_possible(self):
|
def on_http_request_possible(self):
|
||||||
'''
|
'''
|
||||||
Called after HTTP response is received - another request is possible.
|
Called after HTTP response is received - another request is possible.
|
||||||
There should be always one pending request on BOSH CM.
|
There should be always one pending request on BOSH CM.
|
||||||
'''
|
'''
|
||||||
log.info('on_http_req possible, stanzas in list: %s, state:\n%s' %
|
log.info('on_http_req possible state:\n%s' % self.get_current_state())
|
||||||
(self.stanzas_to_send, self.get_current_state()))
|
# if one of sockets is connecting, sth is about to be sent
|
||||||
# if one of sockets is connecting, sth is about to be sent - we don't have to
|
# if there is a pending request, we shouldn't send another one
|
||||||
# send request from here
|
|
||||||
for s in self.http_socks:
|
for s in self.http_socks:
|
||||||
if s.state==CONNECTING or s.pending_requests>0: return
|
if s.state==CONNECTING or s.pending_requests>0: return
|
||||||
self.flush_stanzas()
|
self.flush_stanzas()
|
||||||
|
@ -96,19 +98,17 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
tmp = self.prio_bosh_stanza
|
tmp = self.prio_bosh_stanza
|
||||||
self.prio_bosh_stanza = None
|
self.prio_bosh_stanza = None
|
||||||
else:
|
else:
|
||||||
tmp = self.stanzas_to_send
|
if self.stanzas_to_send:
|
||||||
self.stanzas_to_send = []
|
tmp = self.stanzas_to_send.pop(0)
|
||||||
|
else:
|
||||||
|
tmp = []
|
||||||
self.send_http(tmp)
|
self.send_http(tmp)
|
||||||
|
|
||||||
|
|
||||||
def send(self, stanza, now=False):
|
def send(self, stanza, now=False):
|
||||||
# body tags should be send only via send_http()
|
# body tags should be send only via send_http()
|
||||||
assert(not isinstance(stanza, BOSHBody))
|
assert(not isinstance(stanza, BOSHBody))
|
||||||
now = True
|
self.send_http([stanza])
|
||||||
if now:
|
|
||||||
self.send_http([stanza])
|
|
||||||
else:
|
|
||||||
self.stanzas_to_send.append(stanza)
|
|
||||||
|
|
||||||
|
|
||||||
def send_http(self, payload):
|
def send_http(self, payload):
|
||||||
|
@ -130,12 +130,16 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
else:
|
else:
|
||||||
# no socket was picked but one is about to connect - save the stanza and
|
# no socket was picked but one is about to connect - save the stanza and
|
||||||
# return
|
# return
|
||||||
|
log.info('send_http: no free socket:\n%s' % self.get_current_state())
|
||||||
if self.prio_bosh_stanza:
|
if self.prio_bosh_stanza:
|
||||||
payload = self.merge_stanzas(payload, self.prio_bosh_stanza)
|
payload = self.merge_stanzas(payload, self.prio_bosh_stanza)
|
||||||
if payload is None:
|
if payload is None:
|
||||||
log.error('Error in BOSH socket handling - unable to send %s because %s\
|
# if we cant merge the stanzas (both are BOSH <body>), add the current to
|
||||||
is already about to be sent' % (payload, self.prio_bosh_stanza))
|
# queue to be sent later
|
||||||
self.disconnect()
|
self.stanzas_to_send.append(bosh_stanza)
|
||||||
|
log.warn('in BOSH send_http - unable to send %s because %s\
|
||||||
|
is already about to be sent' % (str(payload), str(self.prio_bosh_stanza)))
|
||||||
|
return
|
||||||
self.prio_bosh_stanza = payload
|
self.prio_bosh_stanza = payload
|
||||||
|
|
||||||
def merge_stanzas(self, s1, s2):
|
def merge_stanzas(self, s1, s2):
|
||||||
|
@ -156,9 +160,11 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
|
|
||||||
|
|
||||||
def get_current_state(self):
|
def get_current_state(self):
|
||||||
t = '\t\tSOCKET_ID\tSOCKET_STATE\tPENDING_REQS\n'
|
t = '------ SOCKET_ID\tSOCKET_STATE\tPENDING_REQS\n'
|
||||||
for s in self.http_socks:
|
for s in self.http_socks:
|
||||||
t = '%s\t\t%s\t%s\t%s\n' % (t,id(s), s.state, s.pending_requests)
|
t = '%s------ %s\t%s\t%s\n' % (t,id(s), s.state, s.pending_requests)
|
||||||
|
t = '%s------ prio stanza to send: %s, queued stanzas: %s' \
|
||||||
|
% (t, self.prio_bosh_stanza, self.stanzas_to_send)
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,9 +187,10 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
return
|
return
|
||||||
# being here means there are only CONNECTED scokets with pending requests.
|
# being here means there are only CONNECTED scokets with pending requests.
|
||||||
# Lets create and connect another one
|
# Lets create and connect another one
|
||||||
s = self.get_http_socket()
|
if len(self.http_socks) < 2:
|
||||||
self.http_socks.append(s)
|
s = self.get_http_socket()
|
||||||
self.connect_and_flush(s)
|
self.http_socks.append(s)
|
||||||
|
self.connect_and_flush(s)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -219,7 +226,6 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
attrs={ 'to': self.bosh_to,
|
attrs={ 'to': self.bosh_to,
|
||||||
'sid': self.bosh_sid,
|
'sid': self.bosh_sid,
|
||||||
'xml:lang': self.bosh_xml_lang,
|
'xml:lang': self.bosh_xml_lang,
|
||||||
'xmpp:version': '1.0',
|
|
||||||
'xmpp:restart': 'true',
|
'xmpp:restart': 'true',
|
||||||
'xmlns:xmpp': 'urn:xmpp:xbosh'})
|
'xmlns:xmpp': 'urn:xmpp:xbosh'})
|
||||||
|
|
||||||
|
@ -239,7 +245,8 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
on_http_request_possible = self.on_http_request_possible,
|
on_http_request_possible = self.on_http_request_possible,
|
||||||
http_uri = self.bosh_host,
|
http_uri = self.bosh_host,
|
||||||
http_port = self.bosh_port,
|
http_port = self.bosh_port,
|
||||||
http_version = self.http_version)
|
http_version = self.http_version,
|
||||||
|
http_persistent = self.http_persistent)
|
||||||
if self.current_recv_handler:
|
if self.current_recv_handler:
|
||||||
s.onreceive(self.current_recv_handler)
|
s.onreceive(self.current_recv_handler)
|
||||||
return s
|
return s
|
||||||
|
@ -251,9 +258,15 @@ class NonBlockingBOSH(NonBlockingTransport):
|
||||||
for s in self.http_socks:
|
for s in self.http_socks:
|
||||||
s.onreceive(recv_handler)
|
s.onreceive(recv_handler)
|
||||||
|
|
||||||
|
def http_socket_disconnect(self, socket):
|
||||||
|
if self.http_persistent:
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def disconnect(self, do_callback=True):
|
def disconnect(self, do_callback=True):
|
||||||
if self.state == DISCONNECTED: return
|
if self.state == DISCONNECTED: return
|
||||||
|
self.fd = -1
|
||||||
for s in self.http_socks:
|
for s in self.http_socks:
|
||||||
s.disconnect(do_callback=False)
|
s.disconnect(do_callback=False)
|
||||||
NonBlockingTransport.disconnect(self, do_callback)
|
NonBlockingTransport.disconnect(self, do_callback)
|
||||||
|
|
|
@ -14,23 +14,23 @@
|
||||||
|
|
||||||
# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $
|
# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $
|
||||||
|
|
||||||
"""
|
'''
|
||||||
Provides PlugIn class functionality to develop extentions for xmpppy.
|
Provides PlugIn class functionality to develop extentions for xmpppy.
|
||||||
Also provides Client and Component classes implementations as the
|
Also provides Client and Component classes implementations as the
|
||||||
examples of xmpppy structures usage.
|
examples of xmpppy structures usage.
|
||||||
These classes can be used for simple applications "AS IS" though.
|
These classes can be used for simple applications "AS IS" though.
|
||||||
"""
|
'''
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger('gajim.c.x.plugin')
|
log = logging.getLogger('gajim.c.x.plugin')
|
||||||
|
|
||||||
class PlugIn:
|
class PlugIn:
|
||||||
""" Common xmpppy plugins infrastructure: plugging in/out, debugging. """
|
''' Common xmpppy plugins infrastructure: plugging in/out, debugging. '''
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._exported_methods=[]
|
self._exported_methods=[]
|
||||||
|
|
||||||
def PlugIn(self,owner):
|
def PlugIn(self,owner):
|
||||||
""" Attach to main instance and register ourself and all our staff in it. """
|
''' Attach to main instance and register ourself and all our staff in it. '''
|
||||||
self._owner=owner
|
self._owner=owner
|
||||||
log.info('Plugging %s __INTO__ %s' % (self,self._owner))
|
log.info('Plugging %s __INTO__ %s' % (self,self._owner))
|
||||||
if owner.__dict__.has_key(self.__class__.__name__):
|
if owner.__dict__.has_key(self.__class__.__name__):
|
||||||
|
@ -53,7 +53,7 @@ class PlugIn:
|
||||||
if hasattr(self,'plugin'): return self.plugin(owner)
|
if hasattr(self,'plugin'): return self.plugin(owner)
|
||||||
|
|
||||||
def PlugOut(self):
|
def PlugOut(self):
|
||||||
""" Unregister all our staff from main instance and detach from it. """
|
''' Unregister all our staff from main instance and detach from it. '''
|
||||||
log.info('Plugging %s __OUT__ of %s.' % (self,self._owner))
|
log.info('Plugging %s __OUT__ of %s.' % (self,self._owner))
|
||||||
for method in self._exported_methods: del self._owner.__dict__[method.__name__]
|
for method in self._exported_methods: del self._owner.__dict__[method.__name__]
|
||||||
for method in self._old_owners_methods: self._owner.__dict__[method.__name__]=method
|
for method in self._old_owners_methods: self._owner.__dict__[method.__name__]=method
|
||||||
|
|
|
@ -138,9 +138,9 @@ class NBCommonClient:
|
||||||
def _try_next_ip(self, err_message=None):
|
def _try_next_ip(self, err_message=None):
|
||||||
'''iterates over IP addresses from getaddinfo'''
|
'''iterates over IP addresses from getaddinfo'''
|
||||||
if err_message:
|
if err_message:
|
||||||
log.debug('While looping over DNS A records: %s' % connect)
|
log.debug('While looping over DNS A records: %s' % err_message)
|
||||||
if self.ip_addresses == []:
|
if self.ip_addresses == []:
|
||||||
self._on_tcp_failure(err_message='Run out of hosts for name %s:%s' %
|
self._on_tcp_failure('Run out of hosts for name %s:%s' %
|
||||||
(self.Server, self.Port))
|
(self.Server, self.Port))
|
||||||
else:
|
else:
|
||||||
self.current_ip = self.ip_addresses.pop(0)
|
self.current_ip = self.ip_addresses.pop(0)
|
||||||
|
@ -237,7 +237,7 @@ class NBCommonClient:
|
||||||
self.on_connect(self, self.connected)
|
self.on_connect(self, self.connected)
|
||||||
|
|
||||||
def raise_event(self, event_type, data):
|
def raise_event(self, event_type, data):
|
||||||
log.info('raising event from transport: :::::%s::::\n_____________\n%s\n_____________\n' % (event_type,data))
|
log.info('raising event from transport: >>>>>%s<<<<<\n_____________\n%s\n_____________\n' % (event_type,data))
|
||||||
if hasattr(self, 'Dispatcher'):
|
if hasattr(self, 'Dispatcher'):
|
||||||
self.Dispatcher.Event('', event_type, data)
|
self.Dispatcher.Event('', event_type, data)
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ class NBCommonClient:
|
||||||
self._Resource = 'xmpppy'
|
self._Resource = 'xmpppy'
|
||||||
auth_nb.NonBlockingNonSASL(self._User, self._Password, self._Resource, self._on_old_auth).PlugIn(self)
|
auth_nb.NonBlockingNonSASL(self._User, self._Password, self._Resource, self._on_old_auth).PlugIn(self)
|
||||||
return
|
return
|
||||||
self.onreceive(self._on_start_sasl)
|
#self.onreceive(self._on_start_sasl)
|
||||||
self.SASL.auth()
|
self.SASL.auth()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -313,8 +313,18 @@ class NBCommonClient:
|
||||||
self.SASL.PlugOut()
|
self.SASL.PlugOut()
|
||||||
elif self.SASL.startsasl == 'success':
|
elif self.SASL.startsasl == 'success':
|
||||||
auth_nb.NonBlockingBind().PlugIn(self)
|
auth_nb.NonBlockingBind().PlugIn(self)
|
||||||
self.onreceive(self._on_auth_bind)
|
if self.protocol_type == 'BOSH':
|
||||||
return True
|
if self.wait_for_restart_response:
|
||||||
|
self.onreceive(self._on_auth_bind)
|
||||||
|
else:
|
||||||
|
self._on_auth_bind(None)
|
||||||
|
return
|
||||||
|
|
||||||
|
elif self.protocol_type == 'XMPP':
|
||||||
|
auth_nb.NonBlockingBind().PlugIn(self)
|
||||||
|
self.onreceive(self._on_auth_bind)
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
def _on_auth_bind(self, data):
|
def _on_auth_bind(self, data):
|
||||||
if data:
|
if data:
|
||||||
|
@ -390,6 +400,7 @@ class NonBlockingClient(NBCommonClient):
|
||||||
domain = self.Server,
|
domain = self.Server,
|
||||||
bosh_dict = proxy)
|
bosh_dict = proxy)
|
||||||
self.protocol_type = 'BOSH'
|
self.protocol_type = 'BOSH'
|
||||||
|
self.wait_for_restart_response = proxy['wait_for_restart_response']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if proxy['type'] == 'socks5':
|
if proxy['type'] == 'socks5':
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
_version_ = '1.4.0'
|
_version_ = '1.4.0'
|
||||||
|
|
||||||
"""\
|
'''\
|
||||||
|
|
||||||
Generic debug class
|
Generic debug class
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ by the individual classes.
|
||||||
For samples of usage, see samples subdir in distro source, and selftest
|
For samples of usage, see samples subdir in distro source, and selftest
|
||||||
in this code
|
in this code
|
||||||
|
|
||||||
"""
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ color_bright_cyan = chr(27) + "[36;1m"
|
||||||
color_white = chr(27) + "[37;1m"
|
color_white = chr(27) + "[37;1m"
|
||||||
|
|
||||||
|
|
||||||
"""
|
'''
|
||||||
Define your flags in yor modules like this:
|
Define your flags in yor modules like this:
|
||||||
|
|
||||||
from debug import *
|
from debug import *
|
||||||
|
@ -99,7 +99,7 @@ DBG_MULTI = [ DBG_INIT, DBG_CONNECTION ]
|
||||||
-------
|
-------
|
||||||
To speed code up, typically for product releases or such
|
To speed code up, typically for product releases or such
|
||||||
use this class instead if you globaly want to disable debugging
|
use this class instead if you globaly want to disable debugging
|
||||||
"""
|
'''
|
||||||
|
|
||||||
|
|
||||||
class NoDebug:
|
class NoDebug:
|
||||||
|
@ -214,7 +214,7 @@ class Debug:
|
||||||
|
|
||||||
def show( self, msg, flag = None, prefix = None, sufix = None,
|
def show( self, msg, flag = None, prefix = None, sufix = None,
|
||||||
lf = 0 ):
|
lf = 0 ):
|
||||||
"""
|
'''
|
||||||
flag can be of folowing types:
|
flag can be of folowing types:
|
||||||
None - this msg will always be shown if any debugging is on
|
None - this msg will always be shown if any debugging is on
|
||||||
flag - will be shown if flag is active
|
flag - will be shown if flag is active
|
||||||
|
@ -225,7 +225,7 @@ class Debug:
|
||||||
|
|
||||||
lf = -1 means strip linefeed if pressent
|
lf = -1 means strip linefeed if pressent
|
||||||
lf = 1 means add linefeed if not pressent
|
lf = 1 means add linefeed if not pressent
|
||||||
"""
|
'''
|
||||||
|
|
||||||
if self.validate_flags:
|
if self.validate_flags:
|
||||||
self._validate_flag( flag )
|
self._validate_flag( flag )
|
||||||
|
@ -343,10 +343,10 @@ class Debug:
|
||||||
|
|
||||||
|
|
||||||
def _as_one_list( self, items ):
|
def _as_one_list( self, items ):
|
||||||
""" init param might contain nested lists, typically from group flags.
|
''' init param might contain nested lists, typically from group flags.
|
||||||
|
|
||||||
This code organises lst and remves dupes
|
This code organises lst and remves dupes
|
||||||
"""
|
'''
|
||||||
if type( items ) <> type( [] ) and type( items ) <> type( () ):
|
if type( items ) <> type( [] ) and type( items ) <> type( () ):
|
||||||
return [ items ]
|
return [ items ]
|
||||||
r = []
|
r = []
|
||||||
|
@ -363,7 +363,7 @@ class Debug:
|
||||||
|
|
||||||
|
|
||||||
def _append_unique_str( self, lst, item ):
|
def _append_unique_str( self, lst, item ):
|
||||||
"""filter out any dupes."""
|
'''filter out any dupes.'''
|
||||||
if type(item) <> type(''):
|
if type(item) <> type(''):
|
||||||
msg2 = '%s' % item
|
msg2 = '%s' % item
|
||||||
raise 'Invalid item type (should be string)',msg2
|
raise 'Invalid item type (should be string)',msg2
|
||||||
|
@ -381,10 +381,10 @@ class Debug:
|
||||||
raise 'Invalid debugflag given', msg2
|
raise 'Invalid debugflag given', msg2
|
||||||
|
|
||||||
def _remove_dupe_flags( self ):
|
def _remove_dupe_flags( self ):
|
||||||
"""
|
'''
|
||||||
if multiple instances of Debug is used in same app,
|
if multiple instances of Debug is used in same app,
|
||||||
some flags might be created multiple time, filter out dupes
|
some flags might be created multiple time, filter out dupes
|
||||||
"""
|
'''
|
||||||
unique_flags = []
|
unique_flags = []
|
||||||
for f in self.debug_flags:
|
for f in self.debug_flags:
|
||||||
if f not in unique_flags:
|
if f not in unique_flags:
|
||||||
|
|
|
@ -38,9 +38,6 @@ ID = 0
|
||||||
STREAM_TERMINATOR = '</stream:stream>'
|
STREAM_TERMINATOR = '</stream:stream>'
|
||||||
XML_DECLARATION = '<?xml version=\'1.0\'?>'
|
XML_DECLARATION = '<?xml version=\'1.0\'?>'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# FIXME: ugly
|
# FIXME: ugly
|
||||||
class Dispatcher():
|
class Dispatcher():
|
||||||
# Why is this here - I needed to redefine Dispatcher for BOSH and easiest way
|
# Why is this here - I needed to redefine Dispatcher for BOSH and easiest way
|
||||||
|
@ -50,11 +47,11 @@ class Dispatcher():
|
||||||
# I wrote following to avoid changing each client.Dispatcher.whatever() in xmpp/
|
# I wrote following to avoid changing each client.Dispatcher.whatever() in xmpp/
|
||||||
|
|
||||||
# If having two kinds of dispatcher will go well, I will rewrite the
|
# If having two kinds of dispatcher will go well, I will rewrite the
|
||||||
def PlugIn(self, client_obj, after_SASL=False):
|
def PlugIn(self, client_obj, after_SASL=False, old_features=None):
|
||||||
if client_obj.protocol_type == 'XMPP':
|
if client_obj.protocol_type == 'XMPP':
|
||||||
XMPPDispatcher().PlugIn(client_obj)
|
XMPPDispatcher().PlugIn(client_obj)
|
||||||
elif client_obj.protocol_type == 'BOSH':
|
elif client_obj.protocol_type == 'BOSH':
|
||||||
BOSHDispatcher().PlugIn(client_obj, after_SASL)
|
BOSHDispatcher().PlugIn(client_obj, after_SASL, old_features)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,12 +157,11 @@ class XMPPDispatcher(PlugIn):
|
||||||
self.Stream.Parse(data)
|
self.Stream.Parse(data)
|
||||||
# end stream:stream tag received
|
# end stream:stream tag received
|
||||||
if self.Stream and self.Stream.has_received_endtag():
|
if self.Stream and self.Stream.has_received_endtag():
|
||||||
# FIXME call client method
|
self._owner.disconnect()
|
||||||
self._owner.Connection.disconnect()
|
|
||||||
return 0
|
return 0
|
||||||
except ExpatError:
|
except ExpatError:
|
||||||
log.error('Invalid XML received from server. Forcing disconnect.')
|
log.error('Invalid XML received from server. Forcing disconnect.')
|
||||||
self._owner.Connection.disconnect()
|
self._owner.disconnect()
|
||||||
return 0
|
return 0
|
||||||
if len(self._pendingExceptions) > 0:
|
if len(self._pendingExceptions) > 0:
|
||||||
_pendingException = self._pendingExceptions.pop()
|
_pendingException = self._pendingExceptions.pop()
|
||||||
|
@ -380,7 +376,6 @@ class XMPPDispatcher(PlugIn):
|
||||||
if not res:
|
if not res:
|
||||||
return
|
return
|
||||||
self._owner.remove_timeout()
|
self._owner.remove_timeout()
|
||||||
print self._expected
|
|
||||||
if self._expected[self._witid] is None:
|
if self._expected[self._witid] is None:
|
||||||
return
|
return
|
||||||
if self.on_responses.has_key(self._witid):
|
if self.on_responses.has_key(self._witid):
|
||||||
|
@ -427,7 +422,8 @@ class XMPPDispatcher(PlugIn):
|
||||||
|
|
||||||
class BOSHDispatcher(XMPPDispatcher):
|
class BOSHDispatcher(XMPPDispatcher):
|
||||||
|
|
||||||
def PlugIn(self, owner, after_SASL=False):
|
def PlugIn(self, owner, after_SASL=False, old_features=None):
|
||||||
|
self.old_features = old_features
|
||||||
self.after_SASL = after_SASL
|
self.after_SASL = after_SASL
|
||||||
XMPPDispatcher.PlugIn(self, owner)
|
XMPPDispatcher.PlugIn(self, owner)
|
||||||
|
|
||||||
|
@ -437,7 +433,7 @@ class BOSHDispatcher(XMPPDispatcher):
|
||||||
self.Stream.dispatch = self.dispatch
|
self.Stream.dispatch = self.dispatch
|
||||||
self.Stream._dispatch_depth = 2
|
self.Stream._dispatch_depth = 2
|
||||||
self.Stream.stream_header_received = self._check_stream_start
|
self.Stream.stream_header_received = self._check_stream_start
|
||||||
self.Stream.features = None
|
self.Stream.features = self.old_features
|
||||||
|
|
||||||
self._metastream = Node('stream:stream')
|
self._metastream = Node('stream:stream')
|
||||||
self._metastream.setNamespace(self._owner.Namespace)
|
self._metastream.setNamespace(self._owner.Namespace)
|
||||||
|
@ -487,8 +483,7 @@ class BOSHDispatcher(XMPPDispatcher):
|
||||||
self._owner.Connection.bosh_sid = stanza_attrs['sid']
|
self._owner.Connection.bosh_sid = stanza_attrs['sid']
|
||||||
|
|
||||||
if stanza_attrs.has_key('terminate'):
|
if stanza_attrs.has_key('terminate'):
|
||||||
# staznas under body still should be passed to XMPP dispatcher
|
self._owner.disconnect()
|
||||||
self._owner.on_disconnect()
|
|
||||||
|
|
||||||
if stanza_attrs.has_key('error'):
|
if stanza_attrs.has_key('error'):
|
||||||
# recoverable error
|
# recoverable error
|
||||||
|
@ -498,6 +493,9 @@ class BOSHDispatcher(XMPPDispatcher):
|
||||||
|
|
||||||
if children:
|
if children:
|
||||||
for child in children:
|
for child in children:
|
||||||
|
# if child doesn't have any ns specified, simplexml (or expat) thinks it's
|
||||||
|
# of parent's (BOSH body) namespace, so we have to rewrite it to
|
||||||
|
# jabber:client
|
||||||
if child.getNamespace() == NS_HTTP_BIND:
|
if child.getNamespace() == NS_HTTP_BIND:
|
||||||
child.setNamespace(self._owner.defaultNamespace)
|
child.setNamespace(self._owner.defaultNamespace)
|
||||||
XMPPDispatcher.dispatch(self, child, session, direct)
|
XMPPDispatcher.dispatch(self, child, session, direct)
|
||||||
|
|
|
@ -32,10 +32,10 @@ def _on_default_response(disp, iq, cb):
|
||||||
disp.SendAndCallForResponse(iq, _on_response)
|
disp.SendAndCallForResponse(iq, _on_response)
|
||||||
|
|
||||||
def _discover(disp, ns, jid, node = None, fb2b=0, fb2a=1, cb=None):
|
def _discover(disp, ns, jid, node = None, fb2b=0, fb2a=1, cb=None):
|
||||||
""" Try to obtain info from the remote object.
|
''' Try to obtain info from the remote object.
|
||||||
If remote object doesn't support disco fall back to browse (if fb2b is true)
|
If remote object doesn't support disco fall back to browse (if fb2b is true)
|
||||||
and if it doesnt support browse (or fb2b is not true) fall back to agents protocol
|
and if it doesnt support browse (or fb2b is not true) fall back to agents protocol
|
||||||
(if gb2a is true). Returns obtained info. Used internally. """
|
(if gb2a is true). Returns obtained info. Used internally. '''
|
||||||
iq=Iq(to=jid, typ='get', queryNS=ns)
|
iq=Iq(to=jid, typ='get', queryNS=ns)
|
||||||
if node:
|
if node:
|
||||||
iq.setQuerynode(node)
|
iq.setQuerynode(node)
|
||||||
|
@ -61,11 +61,11 @@ def _discover(disp, ns, jid, node = None, fb2b=0, fb2a=1, cb=None):
|
||||||
|
|
||||||
# this function is not used in gajim ???
|
# this function is not used in gajim ???
|
||||||
def discoverItems(disp,jid,node=None, cb=None):
|
def discoverItems(disp,jid,node=None, cb=None):
|
||||||
""" Query remote object about any items that it contains. Return items list. """
|
''' Query remote object about any items that it contains. Return items list. '''
|
||||||
""" According to JEP-0030:
|
''' According to JEP-0030:
|
||||||
query MAY have node attribute
|
query MAY have node attribute
|
||||||
item: MUST HAVE jid attribute and MAY HAVE name, node, action attributes.
|
item: MUST HAVE jid attribute and MAY HAVE name, node, action attributes.
|
||||||
action attribute of item can be either of remove or update value."""
|
action attribute of item can be either of remove or update value.'''
|
||||||
def _on_response(result_array):
|
def _on_response(result_array):
|
||||||
ret=[]
|
ret=[]
|
||||||
for result in result_array:
|
for result in result_array:
|
||||||
|
@ -78,11 +78,11 @@ def discoverItems(disp,jid,node=None, cb=None):
|
||||||
|
|
||||||
# this one is
|
# this one is
|
||||||
def discoverInfo(disp,jid,node=None, cb=None):
|
def discoverInfo(disp,jid,node=None, cb=None):
|
||||||
""" Query remote object about info that it publishes. Returns identities and features lists."""
|
''' Query remote object about info that it publishes. Returns identities and features lists.'''
|
||||||
""" According to JEP-0030:
|
''' According to JEP-0030:
|
||||||
query MAY have node attribute
|
query MAY have node attribute
|
||||||
identity: MUST HAVE category and name attributes and MAY HAVE type attribute.
|
identity: MUST HAVE category and name attributes and MAY HAVE type attribute.
|
||||||
feature: MUST HAVE var attribute"""
|
feature: MUST HAVE var attribute'''
|
||||||
def _on_response(result):
|
def _on_response(result):
|
||||||
identities , features = [] , []
|
identities , features = [] , []
|
||||||
for i in result:
|
for i in result:
|
||||||
|
@ -108,11 +108,11 @@ def discoverInfo(disp,jid,node=None, cb=None):
|
||||||
|
|
||||||
### Registration ### jabber:iq:register ### JEP-0077 ###########################
|
### Registration ### jabber:iq:register ### JEP-0077 ###########################
|
||||||
def getRegInfo(disp, host, info={}, sync=True):
|
def getRegInfo(disp, host, info={}, sync=True):
|
||||||
""" Gets registration form from remote host.
|
''' Gets registration form from remote host.
|
||||||
You can pre-fill the info dictionary.
|
You can pre-fill the info dictionary.
|
||||||
F.e. if you are requesting info on registering user joey than specify
|
F.e. if you are requesting info on registering user joey than specify
|
||||||
info as {'username':'joey'}. See JEP-0077 for details.
|
info as {'username':'joey'}. See JEP-0077 for details.
|
||||||
'disp' must be connected dispatcher instance."""
|
'disp' must be connected dispatcher instance.'''
|
||||||
iq=Iq('get',NS_REGISTER,to=host)
|
iq=Iq('get',NS_REGISTER,to=host)
|
||||||
for i in info.keys():
|
for i in info.keys():
|
||||||
iq.setTagData(i,info[i])
|
iq.setTagData(i,info[i])
|
||||||
|
@ -144,11 +144,11 @@ def _ReceivedRegInfo(con, resp, agent):
|
||||||
con.Event(NS_REGISTER, REGISTER_DATA_RECEIVED, (agent,df,False,''))
|
con.Event(NS_REGISTER, REGISTER_DATA_RECEIVED, (agent,df,False,''))
|
||||||
|
|
||||||
def register(disp, host, info, cb):
|
def register(disp, host, info, cb):
|
||||||
""" Perform registration on remote server with provided info.
|
''' Perform registration on remote server with provided info.
|
||||||
disp must be connected dispatcher instance.
|
disp must be connected dispatcher instance.
|
||||||
If registration fails you can get additional info from the dispatcher's owner
|
If registration fails you can get additional info from the dispatcher's owner
|
||||||
attributes lastErrNode, lastErr and lastErrCode.
|
attributes lastErrNode, lastErr and lastErrCode.
|
||||||
"""
|
'''
|
||||||
iq=Iq('set', NS_REGISTER, to=host)
|
iq=Iq('set', NS_REGISTER, to=host)
|
||||||
if not isinstance(info, dict):
|
if not isinstance(info, dict):
|
||||||
info=info.asDict()
|
info=info.asDict()
|
||||||
|
@ -157,16 +157,16 @@ def register(disp, host, info, cb):
|
||||||
disp.SendAndCallForResponse(iq, cb)
|
disp.SendAndCallForResponse(iq, cb)
|
||||||
|
|
||||||
def unregister(disp, host, cb):
|
def unregister(disp, host, cb):
|
||||||
""" Unregisters with host (permanently removes account).
|
''' Unregisters with host (permanently removes account).
|
||||||
disp must be connected and authorized dispatcher instance.
|
disp must be connected and authorized dispatcher instance.
|
||||||
Returns true on success."""
|
Returns true on success.'''
|
||||||
iq = Iq('set', NS_REGISTER, to=host, payload=[Node('remove')])
|
iq = Iq('set', NS_REGISTER, to=host, payload=[Node('remove')])
|
||||||
_on_default_response(disp, iq, cb)
|
_on_default_response(disp, iq, cb)
|
||||||
|
|
||||||
def changePasswordTo(disp, newpassword, host=None, cb = None):
|
def changePasswordTo(disp, newpassword, host=None, cb = None):
|
||||||
""" Changes password on specified or current (if not specified) server.
|
''' Changes password on specified or current (if not specified) server.
|
||||||
disp must be connected and authorized dispatcher instance.
|
disp must be connected and authorized dispatcher instance.
|
||||||
Returns true on success."""
|
Returns true on success.'''
|
||||||
if not host: host=disp._owner.Server
|
if not host: host=disp._owner.Server
|
||||||
iq = Iq('set',NS_REGISTER,to=host, payload=[Node('username',
|
iq = Iq('set',NS_REGISTER,to=host, payload=[Node('username',
|
||||||
payload=[disp._owner.Server]),Node('password',payload=[newpassword])])
|
payload=[disp._owner.Server]),Node('password',payload=[newpassword])])
|
||||||
|
@ -177,8 +177,8 @@ def changePasswordTo(disp, newpassword, host=None, cb = None):
|
||||||
#action=[allow|deny]
|
#action=[allow|deny]
|
||||||
|
|
||||||
def getPrivacyLists(disp):
|
def getPrivacyLists(disp):
|
||||||
""" Requests privacy lists from connected server.
|
''' Requests privacy lists from connected server.
|
||||||
Returns dictionary of existing lists on success."""
|
Returns dictionary of existing lists on success.'''
|
||||||
iq = Iq('get', NS_PRIVACY)
|
iq = Iq('get', NS_PRIVACY)
|
||||||
def _on_response(resp):
|
def _on_response(resp):
|
||||||
dict = {'lists': []}
|
dict = {'lists': []}
|
||||||
|
@ -209,8 +209,8 @@ def getActiveAndDefaultPrivacyLists(disp):
|
||||||
disp.SendAndCallForResponse(iq, _on_response)
|
disp.SendAndCallForResponse(iq, _on_response)
|
||||||
|
|
||||||
def getPrivacyList(disp, listname):
|
def getPrivacyList(disp, listname):
|
||||||
""" Requests specific privacy list listname. Returns list of XML nodes (rules)
|
''' Requests specific privacy list listname. Returns list of XML nodes (rules)
|
||||||
taken from the server responce."""
|
taken from the server responce.'''
|
||||||
def _on_response(resp):
|
def _on_response(resp):
|
||||||
if not isResultNode(resp):
|
if not isResultNode(resp):
|
||||||
disp.Event(NS_PRIVACY, PRIVACY_LIST_RECEIVED, (False))
|
disp.Event(NS_PRIVACY, PRIVACY_LIST_RECEIVED, (False))
|
||||||
|
@ -220,8 +220,8 @@ def getPrivacyList(disp, listname):
|
||||||
disp.SendAndCallForResponse(iq, _on_response)
|
disp.SendAndCallForResponse(iq, _on_response)
|
||||||
|
|
||||||
def setActivePrivacyList(disp, listname=None, typ='active', cb=None):
|
def setActivePrivacyList(disp, listname=None, typ='active', cb=None):
|
||||||
""" Switches privacy list 'listname' to specified type.
|
''' Switches privacy list 'listname' to specified type.
|
||||||
By default the type is 'active'. Returns true on success."""
|
By default the type is 'active'. Returns true on success.'''
|
||||||
if listname:
|
if listname:
|
||||||
attrs={'name':listname}
|
attrs={'name':listname}
|
||||||
else:
|
else:
|
||||||
|
@ -230,13 +230,13 @@ def setActivePrivacyList(disp, listname=None, typ='active', cb=None):
|
||||||
_on_default_response(disp, iq, cb)
|
_on_default_response(disp, iq, cb)
|
||||||
|
|
||||||
def setDefaultPrivacyList(disp, listname=None):
|
def setDefaultPrivacyList(disp, listname=None):
|
||||||
""" Sets the default privacy list as 'listname'. Returns true on success."""
|
''' Sets the default privacy list as 'listname'. Returns true on success.'''
|
||||||
return setActivePrivacyList(disp, listname,'default')
|
return setActivePrivacyList(disp, listname,'default')
|
||||||
|
|
||||||
def setPrivacyList(disp, listname, tags):
|
def setPrivacyList(disp, listname, tags):
|
||||||
""" Set the ruleset. 'list' should be the simpleXML node formatted
|
''' Set the ruleset. 'list' should be the simpleXML node formatted
|
||||||
according to RFC 3921 (XMPP-IM) (I.e. Node('list',{'name':listname},payload=[...]) )
|
according to RFC 3921 (XMPP-IM) (I.e. Node('list',{'name':listname},payload=[...]) )
|
||||||
Returns true on success."""
|
Returns true on success.'''
|
||||||
iq = Iq('set', NS_PRIVACY, xmlns = '')
|
iq = Iq('set', NS_PRIVACY, xmlns = '')
|
||||||
list_query = iq.getTag('query').setTag('list', {'name': listname})
|
list_query = iq.getTag('query').setTag('list', {'name': listname})
|
||||||
for item in tags:
|
for item in tags:
|
||||||
|
@ -252,6 +252,6 @@ def setPrivacyList(disp, listname, tags):
|
||||||
_on_default_response(disp, iq, None)
|
_on_default_response(disp, iq, None)
|
||||||
|
|
||||||
def delPrivacyList(disp,listname,cb=None):
|
def delPrivacyList(disp,listname,cb=None):
|
||||||
""" Deletes privacy list 'listname'. Returns true on success."""
|
''' Deletes privacy list 'listname'. Returns true on success.'''
|
||||||
iq = Iq('set',NS_PRIVACY,payload=[Node('list',{'name':listname})])
|
iq = Iq('set',NS_PRIVACY,payload=[Node('list',{'name':listname})])
|
||||||
_on_default_response(disp, iq, cb)
|
_on_default_response(disp, iq, cb)
|
||||||
|
|
|
@ -56,8 +56,7 @@ class IdleQueue:
|
||||||
self.selector = select.poll()
|
self.selector = select.poll()
|
||||||
|
|
||||||
def remove_timeout(self, fd):
|
def remove_timeout(self, fd):
|
||||||
#log.debug('read timeout removed for fd %s' % fd)
|
log.info('read timeout removed for fd %s' % fd)
|
||||||
print 'read timeout removed for fd %s' % fd
|
|
||||||
if self.read_timeouts.has_key(fd):
|
if self.read_timeouts.has_key(fd):
|
||||||
del(self.read_timeouts[fd])
|
del(self.read_timeouts[fd])
|
||||||
|
|
||||||
|
@ -73,13 +72,12 @@ class IdleQueue:
|
||||||
def set_read_timeout(self, fd, seconds):
|
def set_read_timeout(self, fd, seconds):
|
||||||
''' set a new timeout, if it is not removed after 'seconds',
|
''' set a new timeout, if it is not removed after 'seconds',
|
||||||
then obj.read_timeout() will be called '''
|
then obj.read_timeout() will be called '''
|
||||||
#log.debug('read timeout set for fd %s on %s seconds' % (fd, seconds))
|
log.info('read timeout set for fd %s on %s seconds' % (fd, seconds))
|
||||||
print 'read timeout set for fd %s on %s seconds' % (fd, seconds)
|
|
||||||
timeout = self.current_time() + seconds
|
timeout = self.current_time() + seconds
|
||||||
self.read_timeouts[fd] = timeout
|
self.read_timeouts[fd] = timeout
|
||||||
|
|
||||||
def check_time_events(self):
|
def check_time_events(self):
|
||||||
print 'check time evs'
|
log.info('check time evs')
|
||||||
current_time = self.current_time()
|
current_time = self.current_time()
|
||||||
for fd, timeout in self.read_timeouts.items():
|
for fd, timeout in self.read_timeouts.items():
|
||||||
if timeout > current_time:
|
if timeout > current_time:
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
|
|
||||||
# $Id: protocol.py,v 1.52 2006/01/09 22:08:57 normanr Exp $
|
# $Id: protocol.py,v 1.52 2006/01/09 22:08:57 normanr Exp $
|
||||||
|
|
||||||
"""
|
'''
|
||||||
Protocol module contains tools that is needed for processing of
|
Protocol module contains tools that is needed for processing of
|
||||||
xmpp-related data structures.
|
xmpp-related data structures.
|
||||||
"""
|
'''
|
||||||
|
|
||||||
from simplexml import Node,NodeBuilder,ustr
|
from simplexml import Node,NodeBuilder,ustr
|
||||||
import time
|
import time
|
||||||
|
@ -112,7 +112,7 @@ NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # XEP-01
|
||||||
NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # XEP-0122
|
NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # XEP-0122
|
||||||
NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
|
NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
|
||||||
|
|
||||||
xmpp_stream_error_conditions="""
|
xmpp_stream_error_conditions='''
|
||||||
bad-format -- -- -- The entity has sent XML that cannot be processed.
|
bad-format -- -- -- The entity has sent XML that cannot be processed.
|
||||||
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.
|
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.
|
||||||
conflict -- -- -- The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream.
|
conflict -- -- -- The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream.
|
||||||
|
@ -136,8 +136,8 @@ undefined-condition -- -- -- The error condition is not one of those defined b
|
||||||
unsupported-encoding -- -- -- The initiating entity has encoded the stream in an encoding that is not supported by the server.
|
unsupported-encoding -- -- -- The initiating entity has encoded the stream in an encoding that is not supported by the server.
|
||||||
unsupported-stanza-type -- -- -- The initiating entity has sent a first-level child of the stream that is not supported by the server.
|
unsupported-stanza-type -- -- -- The initiating entity has sent a first-level child of the stream that is not supported by the server.
|
||||||
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.
|
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.
|
||||||
xml-not-well-formed -- -- -- The initiating entity has sent XML that is not well-formed."""
|
xml-not-well-formed -- -- -- The initiating entity has sent XML that is not well-formed.'''
|
||||||
xmpp_stanza_error_conditions="""
|
xmpp_stanza_error_conditions='''
|
||||||
bad-request -- 400 -- modify -- The sender has sent XML that is malformed or that cannot be processed.
|
bad-request -- 400 -- modify -- The sender has sent XML that is malformed or that cannot be processed.
|
||||||
conflict -- 409 -- cancel -- Access cannot be granted because an existing resource or session exists with the same name or address.
|
conflict -- 409 -- cancel -- Access cannot be granted because an existing resource or session exists with the same name or address.
|
||||||
feature-not-implemented -- 501 -- cancel -- The feature requested is not implemented by the recipient or server and therefore cannot be processed.
|
feature-not-implemented -- 501 -- cancel -- The feature requested is not implemented by the recipient or server and therefore cannot be processed.
|
||||||
|
@ -159,15 +159,15 @@ resource-constraint -- 500 -- wait -- The server or recipient lacks the system r
|
||||||
service-unavailable -- 503 -- cancel -- The server or recipient does not currently provide the requested service.
|
service-unavailable -- 503 -- cancel -- The server or recipient does not currently provide the requested service.
|
||||||
subscription-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because a subscription is required.
|
subscription-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because a subscription is required.
|
||||||
undefined-condition -- 500 -- --
|
undefined-condition -- 500 -- --
|
||||||
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)."""
|
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).'''
|
||||||
sasl_error_conditions="""
|
sasl_error_conditions='''
|
||||||
aborted -- -- -- The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element.
|
aborted -- -- -- The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element.
|
||||||
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.
|
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.
|
||||||
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.
|
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.
|
||||||
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.
|
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.
|
||||||
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.
|
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.
|
||||||
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.
|
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.
|
||||||
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."""
|
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.'''
|
||||||
|
|
||||||
ERRORS,_errorcodes={},{}
|
ERRORS,_errorcodes={},{}
|
||||||
for ns,errname,errpool in [(NS_XMPP_STREAMS,'STREAM',xmpp_stream_error_conditions),
|
for ns,errname,errpool in [(NS_XMPP_STREAMS,'STREAM',xmpp_stream_error_conditions),
|
||||||
|
@ -182,16 +182,16 @@ for ns,errname,errpool in [(NS_XMPP_STREAMS,'STREAM',xmpp_stream_error_condition
|
||||||
del ns,errname,errpool,err,cond,code,typ,text
|
del ns,errname,errpool,err,cond,code,typ,text
|
||||||
|
|
||||||
def isResultNode(node):
|
def isResultNode(node):
|
||||||
""" Returns true if the node is a positive reply. """
|
''' Returns true if the node is a positive reply. '''
|
||||||
return node and node.getType()=='result'
|
return node and node.getType()=='result'
|
||||||
def isErrorNode(node):
|
def isErrorNode(node):
|
||||||
""" Returns true if the node is a negative reply. """
|
''' Returns true if the node is a negative reply. '''
|
||||||
return node and node.getType()=='error'
|
return node and node.getType()=='error'
|
||||||
|
|
||||||
class NodeProcessed(Exception):
|
class NodeProcessed(Exception):
|
||||||
""" Exception that should be raised by handler when the handling should be stopped. """
|
''' Exception that should be raised by handler when the handling should be stopped. '''
|
||||||
class StreamError(Exception):
|
class StreamError(Exception):
|
||||||
""" Base exception class for stream errors."""
|
''' Base exception class for stream errors.'''
|
||||||
class BadFormat(StreamError): pass
|
class BadFormat(StreamError): pass
|
||||||
class BadNamespacePrefix(StreamError): pass
|
class BadNamespacePrefix(StreamError): pass
|
||||||
class Conflict(StreamError): pass
|
class Conflict(StreamError): pass
|
||||||
|
@ -243,13 +243,13 @@ stream_exceptions = {'bad-format': BadFormat,
|
||||||
'xml-not-well-formed': XMLNotWellFormed}
|
'xml-not-well-formed': XMLNotWellFormed}
|
||||||
|
|
||||||
class JID:
|
class JID:
|
||||||
""" JID object. JID can be built from string, modified, compared, serialised into string. """
|
''' JID object. JID can be built from string, modified, compared, serialised into string. '''
|
||||||
def __init__(self, jid=None, node='', domain='', resource=''):
|
def __init__(self, jid=None, node='', domain='', resource=''):
|
||||||
""" Constructor. JID can be specified as string (jid argument) or as separate parts.
|
''' Constructor. JID can be specified as string (jid argument) or as separate parts.
|
||||||
Examples:
|
Examples:
|
||||||
JID('node@domain/resource')
|
JID('node@domain/resource')
|
||||||
JID(node='node',domain='domain.org')
|
JID(node='node',domain='domain.org')
|
||||||
"""
|
'''
|
||||||
if not jid and not domain: raise ValueError('JID must contain at least domain name')
|
if not jid and not domain: raise ValueError('JID must contain at least domain name')
|
||||||
elif type(jid)==type(self): self.node,self.domain,self.resource=jid.node,jid.domain,jid.resource
|
elif type(jid)==type(self): self.node,self.domain,self.resource=jid.node,jid.domain,jid.resource
|
||||||
elif domain: self.node,self.domain,self.resource=node,domain,resource
|
elif domain: self.node,self.domain,self.resource=node,domain,resource
|
||||||
|
@ -259,45 +259,45 @@ class JID:
|
||||||
if jid.find('/')+1: self.domain,self.resource=jid.split('/',1)
|
if jid.find('/')+1: self.domain,self.resource=jid.split('/',1)
|
||||||
else: self.domain,self.resource=jid,''
|
else: self.domain,self.resource=jid,''
|
||||||
def getNode(self):
|
def getNode(self):
|
||||||
""" Return the node part of the JID """
|
''' Return the node part of the JID '''
|
||||||
return self.node
|
return self.node
|
||||||
def setNode(self,node):
|
def setNode(self,node):
|
||||||
""" Set the node part of the JID to new value. Specify None to remove the node part."""
|
''' Set the node part of the JID to new value. Specify None to remove the node part.'''
|
||||||
self.node=node.lower()
|
self.node=node.lower()
|
||||||
def getDomain(self):
|
def getDomain(self):
|
||||||
""" Return the domain part of the JID """
|
''' Return the domain part of the JID '''
|
||||||
return self.domain
|
return self.domain
|
||||||
def setDomain(self,domain):
|
def setDomain(self,domain):
|
||||||
""" Set the domain part of the JID to new value."""
|
''' Set the domain part of the JID to new value.'''
|
||||||
self.domain=domain.lower()
|
self.domain=domain.lower()
|
||||||
def getResource(self):
|
def getResource(self):
|
||||||
""" Return the resource part of the JID """
|
''' Return the resource part of the JID '''
|
||||||
return self.resource
|
return self.resource
|
||||||
def setResource(self,resource):
|
def setResource(self,resource):
|
||||||
""" Set the resource part of the JID to new value. Specify None to remove the resource part."""
|
''' Set the resource part of the JID to new value. Specify None to remove the resource part.'''
|
||||||
self.resource=resource
|
self.resource=resource
|
||||||
def getStripped(self):
|
def getStripped(self):
|
||||||
""" Return the bare representation of JID. I.e. string value w/o resource. """
|
''' Return the bare representation of JID. I.e. string value w/o resource. '''
|
||||||
return self.__str__(0)
|
return self.__str__(0)
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
""" Compare the JID to another instance or to string for equality. """
|
''' Compare the JID to another instance or to string for equality. '''
|
||||||
try: other=JID(other)
|
try: other=JID(other)
|
||||||
except ValueError: return 0
|
except ValueError: return 0
|
||||||
return self.resource==other.resource and self.__str__(0) == other.__str__(0)
|
return self.resource==other.resource and self.__str__(0) == other.__str__(0)
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
""" Compare the JID to another instance or to string for non-equality. """
|
''' Compare the JID to another instance or to string for non-equality. '''
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
def bareMatch(self, other):
|
def bareMatch(self, other):
|
||||||
""" Compare the node and domain parts of the JID's for equality. """
|
''' Compare the node and domain parts of the JID's for equality. '''
|
||||||
return self.__str__(0) == JID(other).__str__(0)
|
return self.__str__(0) == JID(other).__str__(0)
|
||||||
def __str__(self,wresource=1):
|
def __str__(self,wresource=1):
|
||||||
""" Serialise JID into string. """
|
''' Serialise JID into string. '''
|
||||||
if self.node: jid=self.node+'@'+self.domain
|
if self.node: jid=self.node+'@'+self.domain
|
||||||
else: jid=self.domain
|
else: jid=self.domain
|
||||||
if wresource and self.resource: return jid+'/'+self.resource
|
if wresource and self.resource: return jid+'/'+self.resource
|
||||||
return jid
|
return jid
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
""" Produce hash of the JID, Allows to use JID objects as keys of the dictionary. """
|
''' Produce hash of the JID, Allows to use JID objects as keys of the dictionary. '''
|
||||||
return hash(self.__str__())
|
return hash(self.__str__())
|
||||||
|
|
||||||
class BOSHBody(Node):
|
class BOSHBody(Node):
|
||||||
|
@ -310,16 +310,16 @@ class BOSHBody(Node):
|
||||||
|
|
||||||
|
|
||||||
class Protocol(Node):
|
class Protocol(Node):
|
||||||
""" A "stanza" object class. Contains methods that are common for presences, iqs and messages. """
|
''' A "stanza" object class. Contains methods that are common for presences, iqs and messages. '''
|
||||||
def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, payload=[], timestamp=None, xmlns=None, node=None):
|
def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, payload=[], timestamp=None, xmlns=None, node=None):
|
||||||
""" Constructor, name is the name of the stanza i.e. 'message' or 'presence' or 'iq'.
|
''' Constructor, name is the name of the stanza i.e. 'message' or 'presence' or 'iq'.
|
||||||
to is the value of 'to' attribure, 'typ' - 'type' attribute
|
to is the value of 'to' attribure, 'typ' - 'type' attribute
|
||||||
frn - from attribure, attrs - other attributes mapping,
|
frn - from attribure, attrs - other attributes mapping,
|
||||||
payload - same meaning as for simplexml payload definition
|
payload - same meaning as for simplexml payload definition
|
||||||
timestamp - the time value that needs to be stamped over stanza
|
timestamp - the time value that needs to be stamped over stanza
|
||||||
xmlns - namespace of top stanza node
|
xmlns - namespace of top stanza node
|
||||||
node - parsed or unparsed stana to be taken as prototype.
|
node - parsed or unparsed stana to be taken as prototype.
|
||||||
"""
|
'''
|
||||||
if not attrs: attrs={}
|
if not attrs: attrs={}
|
||||||
if to: attrs['to']=to
|
if to: attrs['to']=to
|
||||||
if frm: attrs['from']=frm
|
if frm: attrs['from']=frm
|
||||||
|
@ -336,54 +336,54 @@ class Protocol(Node):
|
||||||
except: pass
|
except: pass
|
||||||
if timestamp is not None: self.setTimestamp(timestamp) # To auto-timestamp stanza just pass timestamp=''
|
if timestamp is not None: self.setTimestamp(timestamp) # To auto-timestamp stanza just pass timestamp=''
|
||||||
def getTo(self):
|
def getTo(self):
|
||||||
""" Return value of the 'to' attribute. """
|
''' Return value of the 'to' attribute. '''
|
||||||
try: return self['to']
|
try: return self['to']
|
||||||
except: return None
|
except: return None
|
||||||
def getFrom(self):
|
def getFrom(self):
|
||||||
""" Return value of the 'from' attribute. """
|
''' Return value of the 'from' attribute. '''
|
||||||
try: return self['from']
|
try: return self['from']
|
||||||
except: return None
|
except: return None
|
||||||
def getTimestamp(self):
|
def getTimestamp(self):
|
||||||
""" Return the timestamp in the 'yyyymmddThhmmss' format. """
|
''' Return the timestamp in the 'yyyymmddThhmmss' format. '''
|
||||||
if self.timestamp: return self.timestamp
|
if self.timestamp: return self.timestamp
|
||||||
return time.strftime('%Y%m%dT%H:%M:%S', time.gmtime())
|
return time.strftime('%Y%m%dT%H:%M:%S', time.gmtime())
|
||||||
def getID(self):
|
def getID(self):
|
||||||
""" Return the value of the 'id' attribute. """
|
''' Return the value of the 'id' attribute. '''
|
||||||
return self.getAttr('id')
|
return self.getAttr('id')
|
||||||
def setTo(self,val):
|
def setTo(self,val):
|
||||||
""" Set the value of the 'to' attribute. """
|
''' Set the value of the 'to' attribute. '''
|
||||||
self.setAttr('to', JID(val))
|
self.setAttr('to', JID(val))
|
||||||
def getType(self):
|
def getType(self):
|
||||||
""" Return the value of the 'type' attribute. """
|
''' Return the value of the 'type' attribute. '''
|
||||||
return self.getAttr('type')
|
return self.getAttr('type')
|
||||||
def setFrom(self,val):
|
def setFrom(self,val):
|
||||||
""" Set the value of the 'from' attribute. """
|
''' Set the value of the 'from' attribute. '''
|
||||||
self.setAttr('from', JID(val))
|
self.setAttr('from', JID(val))
|
||||||
def setType(self,val):
|
def setType(self,val):
|
||||||
""" Set the value of the 'type' attribute. """
|
''' Set the value of the 'type' attribute. '''
|
||||||
self.setAttr('type', val)
|
self.setAttr('type', val)
|
||||||
def setID(self,val):
|
def setID(self,val):
|
||||||
""" Set the value of the 'id' attribute. """
|
''' Set the value of the 'id' attribute. '''
|
||||||
self.setAttr('id', val)
|
self.setAttr('id', val)
|
||||||
def getError(self):
|
def getError(self):
|
||||||
""" Return the error-condition (if present) or the textual description of the error (otherwise). """
|
''' Return the error-condition (if present) or the textual description of the error (otherwise). '''
|
||||||
errtag=self.getTag('error')
|
errtag=self.getTag('error')
|
||||||
if errtag:
|
if errtag:
|
||||||
for tag in errtag.getChildren():
|
for tag in errtag.getChildren():
|
||||||
if tag.getName()<>'text': return tag.getName()
|
if tag.getName()<>'text': return tag.getName()
|
||||||
return errtag.getData()
|
return errtag.getData()
|
||||||
def getErrorMsg(self):
|
def getErrorMsg(self):
|
||||||
""" Return the textual description of the error (if present) or the error condition """
|
''' Return the textual description of the error (if present) or the error condition '''
|
||||||
errtag=self.getTag('error')
|
errtag=self.getTag('error')
|
||||||
if errtag:
|
if errtag:
|
||||||
for tag in errtag.getChildren():
|
for tag in errtag.getChildren():
|
||||||
if tag.getName()=='text': return tag.getData()
|
if tag.getName()=='text': return tag.getData()
|
||||||
return self.getError()
|
return self.getError()
|
||||||
def getErrorCode(self):
|
def getErrorCode(self):
|
||||||
""" Return the error code. Obsolete. """
|
''' Return the error code. Obsolete. '''
|
||||||
return self.getTagAttr('error','code')
|
return self.getTagAttr('error','code')
|
||||||
def setError(self,error,code=None):
|
def setError(self,error,code=None):
|
||||||
""" Set the error code. Obsolete. Use error-conditions instead. """
|
''' Set the error code. Obsolete. Use error-conditions instead. '''
|
||||||
if code:
|
if code:
|
||||||
if str(code) in _errorcodes.keys(): error=ErrorNode(_errorcodes[str(code)],text=error)
|
if str(code) in _errorcodes.keys(): error=ErrorNode(_errorcodes[str(code)],text=error)
|
||||||
else: error=ErrorNode(ERR_UNDEFINED_CONDITION,code=code,typ='cancel',text=error)
|
else: error=ErrorNode(ERR_UNDEFINED_CONDITION,code=code,typ='cancel',text=error)
|
||||||
|
@ -391,40 +391,40 @@ class Protocol(Node):
|
||||||
self.setType('error')
|
self.setType('error')
|
||||||
self.addChild(node=error)
|
self.addChild(node=error)
|
||||||
def setTimestamp(self,val=None):
|
def setTimestamp(self,val=None):
|
||||||
"""Set the timestamp. timestamp should be the yyyymmddThhmmss string."""
|
'''Set the timestamp. timestamp should be the yyyymmddThhmmss string.'''
|
||||||
if not val: val=time.strftime('%Y%m%dT%H:%M:%S', time.gmtime())
|
if not val: val=time.strftime('%Y%m%dT%H:%M:%S', time.gmtime())
|
||||||
self.timestamp=val
|
self.timestamp=val
|
||||||
self.setTag('x',{'stamp':self.timestamp},namespace=NS_DELAY)
|
self.setTag('x',{'stamp':self.timestamp},namespace=NS_DELAY)
|
||||||
def getProperties(self):
|
def getProperties(self):
|
||||||
""" Return the list of namespaces to which belongs the direct childs of element"""
|
''' Return the list of namespaces to which belongs the direct childs of element'''
|
||||||
props=[]
|
props=[]
|
||||||
for child in self.getChildren():
|
for child in self.getChildren():
|
||||||
prop=child.getNamespace()
|
prop=child.getNamespace()
|
||||||
if prop not in props: props.append(prop)
|
if prop not in props: props.append(prop)
|
||||||
return props
|
return props
|
||||||
def __setitem__(self,item,val):
|
def __setitem__(self,item,val):
|
||||||
""" Set the item 'item' to the value 'val'."""
|
''' Set the item 'item' to the value 'val'.'''
|
||||||
if item in ['to','from']: val=JID(val)
|
if item in ['to','from']: val=JID(val)
|
||||||
return self.setAttr(item,val)
|
return self.setAttr(item,val)
|
||||||
|
|
||||||
|
|
||||||
class Message(Protocol):
|
class Message(Protocol):
|
||||||
""" XMPP Message stanza - "push" mechanism."""
|
''' XMPP Message stanza - "push" mechanism.'''
|
||||||
def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None):
|
def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None):
|
||||||
""" Create message object. You can specify recipient, text of message, type of message
|
''' Create message object. You can specify recipient, text of message, type of message
|
||||||
any additional attributes, sender of the message, any additional payload (f.e. jabber:x:delay element) and namespace in one go.
|
any additional attributes, sender of the message, any additional payload (f.e. jabber:x:delay element) and namespace in one go.
|
||||||
Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as message. """
|
Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as message. '''
|
||||||
Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
|
Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
|
||||||
if body: self.setBody(body)
|
if body: self.setBody(body)
|
||||||
if xhtml: self.setXHTML(xhtml)
|
if xhtml: self.setXHTML(xhtml)
|
||||||
if subject is not None: self.setSubject(subject)
|
if subject is not None: self.setSubject(subject)
|
||||||
def getBody(self):
|
def getBody(self):
|
||||||
""" Returns text of the message. """
|
''' Returns text of the message. '''
|
||||||
return self.getTagData('body')
|
return self.getTagData('body')
|
||||||
def getXHTML(self, xmllang=None):
|
def getXHTML(self, xmllang=None):
|
||||||
""" Returns serialized xhtml-im element text of the message.
|
''' Returns serialized xhtml-im element text of the message.
|
||||||
|
|
||||||
TODO: Returning a DOM could make rendering faster."""
|
TODO: Returning a DOM could make rendering faster.'''
|
||||||
xhtml = self.getTag('html')
|
xhtml = self.getTag('html')
|
||||||
if xhtml:
|
if xhtml:
|
||||||
if xmllang:
|
if xmllang:
|
||||||
|
@ -434,18 +434,18 @@ class Message(Protocol):
|
||||||
return str(body)
|
return str(body)
|
||||||
return None
|
return None
|
||||||
def getSubject(self):
|
def getSubject(self):
|
||||||
""" Returns subject of the message. """
|
''' Returns subject of the message. '''
|
||||||
return self.getTagData('subject')
|
return self.getTagData('subject')
|
||||||
def getThread(self):
|
def getThread(self):
|
||||||
""" Returns thread of the message. """
|
''' Returns thread of the message. '''
|
||||||
return self.getTagData('thread')
|
return self.getTagData('thread')
|
||||||
def setBody(self,val):
|
def setBody(self,val):
|
||||||
""" Sets the text of the message. """
|
''' Sets the text of the message. '''
|
||||||
self.setTagData('body',val)
|
self.setTagData('body',val)
|
||||||
|
|
||||||
def setXHTML(self,val,xmllang=None):
|
def setXHTML(self,val,xmllang=None):
|
||||||
""" Sets the xhtml text of the message (XEP-0071).
|
''' Sets the xhtml text of the message (XEP-0071).
|
||||||
The parameter is the "inner html" to the body."""
|
The parameter is the "inner html" to the body.'''
|
||||||
try:
|
try:
|
||||||
if xmllang:
|
if xmllang:
|
||||||
dom = NodeBuilder('<body xmlns="'+NS_XHTML+'" xml:lang="'+xmllang+'" >' + val + '</body>').getDom()
|
dom = NodeBuilder('<body xmlns="'+NS_XHTML+'" xml:lang="'+xmllang+'" >' + val + '</body>').getDom()
|
||||||
|
@ -459,21 +459,21 @@ class Message(Protocol):
|
||||||
print "Error", e
|
print "Error", e
|
||||||
pass #FIXME: log. we could not set xhtml (parse error, whatever)
|
pass #FIXME: log. we could not set xhtml (parse error, whatever)
|
||||||
def setSubject(self,val):
|
def setSubject(self,val):
|
||||||
""" Sets the subject of the message. """
|
''' Sets the subject of the message. '''
|
||||||
self.setTagData('subject',val)
|
self.setTagData('subject',val)
|
||||||
def setThread(self,val):
|
def setThread(self,val):
|
||||||
""" Sets the thread of the message. """
|
''' Sets the thread of the message. '''
|
||||||
self.setTagData('thread',val)
|
self.setTagData('thread',val)
|
||||||
def buildReply(self,text=None):
|
def buildReply(self,text=None):
|
||||||
""" Builds and returns another message object with specified text.
|
''' Builds and returns another message object with specified text.
|
||||||
The to, from and thread properties of new message are pre-set as reply to this message. """
|
The to, from and thread properties of new message are pre-set as reply to this message. '''
|
||||||
m=Message(to=self.getFrom(),frm=self.getTo(),body=text,node=self)
|
m=Message(to=self.getFrom(),frm=self.getTo(),body=text,node=self)
|
||||||
th=self.getThread()
|
th=self.getThread()
|
||||||
if th: m.setThread(th)
|
if th: m.setThread(th)
|
||||||
return m
|
return m
|
||||||
def getStatusCode(self):
|
def getStatusCode(self):
|
||||||
"""Returns the status code of the message (for groupchat config
|
'''Returns the status code of the message (for groupchat config
|
||||||
change)"""
|
change)'''
|
||||||
attrs = []
|
attrs = []
|
||||||
for xtag in self.getTags('x'):
|
for xtag in self.getTags('x'):
|
||||||
for child in xtag.getTags('status'):
|
for child in xtag.getTags('status'):
|
||||||
|
@ -481,32 +481,32 @@ class Message(Protocol):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
class Presence(Protocol):
|
class Presence(Protocol):
|
||||||
""" XMPP Presence object."""
|
''' XMPP Presence object.'''
|
||||||
def __init__(self, to=None, typ=None, priority=None, show=None, status=None, attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, node=None):
|
def __init__(self, to=None, typ=None, priority=None, show=None, status=None, attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, node=None):
|
||||||
""" Create presence object. You can specify recipient, type of message, priority, show and status values
|
''' Create presence object. You can specify recipient, type of message, priority, show and status values
|
||||||
any additional attributes, sender of the presence, timestamp, any additional payload (f.e. jabber:x:delay element) and namespace in one go.
|
any additional attributes, sender of the presence, timestamp, any additional payload (f.e. jabber:x:delay element) and namespace in one go.
|
||||||
Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as presence. """
|
Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as presence. '''
|
||||||
Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
|
Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
|
||||||
if priority: self.setPriority(priority)
|
if priority: self.setPriority(priority)
|
||||||
if show: self.setShow(show)
|
if show: self.setShow(show)
|
||||||
if status: self.setStatus(status)
|
if status: self.setStatus(status)
|
||||||
def getPriority(self):
|
def getPriority(self):
|
||||||
""" Returns the priority of the message. """
|
''' Returns the priority of the message. '''
|
||||||
return self.getTagData('priority')
|
return self.getTagData('priority')
|
||||||
def getShow(self):
|
def getShow(self):
|
||||||
""" Returns the show value of the message. """
|
''' Returns the show value of the message. '''
|
||||||
return self.getTagData('show')
|
return self.getTagData('show')
|
||||||
def getStatus(self):
|
def getStatus(self):
|
||||||
""" Returns the status string of the message. """
|
''' Returns the status string of the message. '''
|
||||||
return self.getTagData('status')
|
return self.getTagData('status')
|
||||||
def setPriority(self,val):
|
def setPriority(self,val):
|
||||||
""" Sets the priority of the message. """
|
''' Sets the priority of the message. '''
|
||||||
self.setTagData('priority',val)
|
self.setTagData('priority',val)
|
||||||
def setShow(self,val):
|
def setShow(self,val):
|
||||||
""" Sets the show value of the message. """
|
''' Sets the show value of the message. '''
|
||||||
self.setTagData('show',val)
|
self.setTagData('show',val)
|
||||||
def setStatus(self,val):
|
def setStatus(self,val):
|
||||||
""" Sets the status string of the message. """
|
''' Sets the status string of the message. '''
|
||||||
self.setTagData('status',val)
|
self.setTagData('status',val)
|
||||||
|
|
||||||
def _muc_getItemAttr(self,tag,attr):
|
def _muc_getItemAttr(self,tag,attr):
|
||||||
|
@ -520,25 +520,25 @@ class Presence(Protocol):
|
||||||
return cchild.getData(),cchild.getAttr(attr)
|
return cchild.getData(),cchild.getAttr(attr)
|
||||||
return None,None
|
return None,None
|
||||||
def getRole(self):
|
def getRole(self):
|
||||||
"""Returns the presence role (for groupchat)"""
|
'''Returns the presence role (for groupchat)'''
|
||||||
return self._muc_getItemAttr('item','role')
|
return self._muc_getItemAttr('item','role')
|
||||||
def getAffiliation(self):
|
def getAffiliation(self):
|
||||||
"""Returns the presence affiliation (for groupchat)"""
|
'''Returns the presence affiliation (for groupchat)'''
|
||||||
return self._muc_getItemAttr('item','affiliation')
|
return self._muc_getItemAttr('item','affiliation')
|
||||||
def getNewNick(self):
|
def getNewNick(self):
|
||||||
"""Returns the status code of the presence (for groupchat)"""
|
'''Returns the status code of the presence (for groupchat)'''
|
||||||
return self._muc_getItemAttr('item','nick')
|
return self._muc_getItemAttr('item','nick')
|
||||||
def getJid(self):
|
def getJid(self):
|
||||||
"""Returns the presence jid (for groupchat)"""
|
'''Returns the presence jid (for groupchat)'''
|
||||||
return self._muc_getItemAttr('item','jid')
|
return self._muc_getItemAttr('item','jid')
|
||||||
def getReason(self):
|
def getReason(self):
|
||||||
"""Returns the reason of the presence (for groupchat)"""
|
'''Returns the reason of the presence (for groupchat)'''
|
||||||
return self._muc_getSubTagDataAttr('reason','')[0]
|
return self._muc_getSubTagDataAttr('reason','')[0]
|
||||||
def getActor(self):
|
def getActor(self):
|
||||||
"""Returns the reason of the presence (for groupchat)"""
|
'''Returns the reason of the presence (for groupchat)'''
|
||||||
return self._muc_getSubTagDataAttr('actor','jid')[1]
|
return self._muc_getSubTagDataAttr('actor','jid')[1]
|
||||||
def getStatusCode(self):
|
def getStatusCode(self):
|
||||||
"""Returns the status code of the presence (for groupchat)"""
|
'''Returns the status code of the presence (for groupchat)'''
|
||||||
attrs = []
|
attrs = []
|
||||||
for xtag in self.getTags('x'):
|
for xtag in self.getTags('x'):
|
||||||
for child in xtag.getTags('status'):
|
for child in xtag.getTags('status'):
|
||||||
|
@ -546,53 +546,53 @@ class Presence(Protocol):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
class Iq(Protocol):
|
class Iq(Protocol):
|
||||||
""" XMPP Iq object - get/set dialog mechanism. """
|
''' XMPP Iq object - get/set dialog mechanism. '''
|
||||||
def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, payload=[], xmlns=NS_CLIENT, node=None):
|
def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, payload=[], xmlns=NS_CLIENT, node=None):
|
||||||
""" Create Iq object. You can specify type, query namespace
|
''' Create Iq object. You can specify type, query namespace
|
||||||
any additional attributes, recipient of the iq, sender of the iq, any additional payload (f.e. jabber:x:data node) and namespace in one go.
|
any additional attributes, recipient of the iq, sender of the iq, any additional payload (f.e. jabber:x:data node) and namespace in one go.
|
||||||
Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as an iq. """
|
Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as an iq. '''
|
||||||
Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, xmlns=xmlns, node=node)
|
Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, xmlns=xmlns, node=node)
|
||||||
if payload: self.setQueryPayload(payload)
|
if payload: self.setQueryPayload(payload)
|
||||||
if queryNS: self.setQueryNS(queryNS)
|
if queryNS: self.setQueryNS(queryNS)
|
||||||
def getQueryNS(self):
|
def getQueryNS(self):
|
||||||
""" Return the namespace of the 'query' child element."""
|
''' Return the namespace of the 'query' child element.'''
|
||||||
tag=self.getTag('query')
|
tag=self.getTag('query')
|
||||||
if tag: return tag.getNamespace()
|
if tag: return tag.getNamespace()
|
||||||
def getQuerynode(self):
|
def getQuerynode(self):
|
||||||
""" Return the 'node' attribute value of the 'query' child element."""
|
''' Return the 'node' attribute value of the 'query' child element.'''
|
||||||
return self.getTagAttr('query','node')
|
return self.getTagAttr('query','node')
|
||||||
def getQueryPayload(self):
|
def getQueryPayload(self):
|
||||||
""" Return the 'query' child element payload."""
|
''' Return the 'query' child element payload.'''
|
||||||
tag=self.getTag('query')
|
tag=self.getTag('query')
|
||||||
if tag: return tag.getPayload()
|
if tag: return tag.getPayload()
|
||||||
def getQueryChildren(self):
|
def getQueryChildren(self):
|
||||||
""" Return the 'query' child element child nodes."""
|
''' Return the 'query' child element child nodes.'''
|
||||||
tag=self.getTag('query')
|
tag=self.getTag('query')
|
||||||
if tag: return tag.getChildren()
|
if tag: return tag.getChildren()
|
||||||
def setQueryNS(self,namespace):
|
def setQueryNS(self,namespace):
|
||||||
""" Set the namespace of the 'query' child element."""
|
''' Set the namespace of the 'query' child element.'''
|
||||||
self.setTag('query').setNamespace(namespace)
|
self.setTag('query').setNamespace(namespace)
|
||||||
def setQueryPayload(self,payload):
|
def setQueryPayload(self,payload):
|
||||||
""" Set the 'query' child element payload."""
|
''' Set the 'query' child element payload.'''
|
||||||
self.setTag('query').setPayload(payload)
|
self.setTag('query').setPayload(payload)
|
||||||
def setQuerynode(self,node):
|
def setQuerynode(self,node):
|
||||||
""" Set the 'node' attribute value of the 'query' child element."""
|
''' Set the 'node' attribute value of the 'query' child element.'''
|
||||||
self.setTagAttr('query','node',node)
|
self.setTagAttr('query','node',node)
|
||||||
def buildReply(self,typ):
|
def buildReply(self,typ):
|
||||||
""" Builds and returns another Iq object of specified type.
|
''' Builds and returns another Iq object of specified type.
|
||||||
The to, from and query child node of new Iq are pre-set as reply to this Iq. """
|
The to, from and query child node of new Iq are pre-set as reply to this Iq. '''
|
||||||
iq=Iq(typ,to=self.getFrom(),frm=self.getTo(),attrs={'id':self.getID()})
|
iq=Iq(typ,to=self.getFrom(),frm=self.getTo(),attrs={'id':self.getID()})
|
||||||
if self.getTag('query'): iq.setQueryNS(self.getQueryNS())
|
if self.getTag('query'): iq.setQueryNS(self.getQueryNS())
|
||||||
return iq
|
return iq
|
||||||
|
|
||||||
class ErrorNode(Node):
|
class ErrorNode(Node):
|
||||||
""" XMPP-style error element.
|
''' XMPP-style error element.
|
||||||
In the case of stanza error should be attached to XMPP stanza.
|
In the case of stanza error should be attached to XMPP stanza.
|
||||||
In the case of stream-level errors should be used separately. """
|
In the case of stream-level errors should be used separately. '''
|
||||||
def __init__(self,name,code=None,typ=None,text=None):
|
def __init__(self,name,code=None,typ=None,text=None):
|
||||||
""" Create new error node object.
|
''' Create new error node object.
|
||||||
Mandatory parameter: name - name of error condition.
|
Mandatory parameter: name - name of error condition.
|
||||||
Optional parameters: code, typ, text. Used for backwards compartibility with older jabber protocol."""
|
Optional parameters: code, typ, text. Used for backwards compartibility with older jabber protocol.'''
|
||||||
if ERRORS.has_key(name):
|
if ERRORS.has_key(name):
|
||||||
cod,type,txt=ERRORS[name]
|
cod,type,txt=ERRORS[name]
|
||||||
ns=name.split()[0]
|
ns=name.split()[0]
|
||||||
|
@ -607,30 +607,30 @@ class ErrorNode(Node):
|
||||||
if cod: self.setAttr('code',cod)
|
if cod: self.setAttr('code',cod)
|
||||||
|
|
||||||
class Error(Protocol):
|
class Error(Protocol):
|
||||||
""" Used to quickly transform received stanza into error reply."""
|
''' Used to quickly transform received stanza into error reply.'''
|
||||||
def __init__(self,node,error,reply=1):
|
def __init__(self,node,error,reply=1):
|
||||||
""" Create error reply basing on the received 'node' stanza and the 'error' error condition.
|
''' Create error reply basing on the received 'node' stanza and the 'error' error condition.
|
||||||
If the 'node' is not the received stanza but locally created ('to' and 'from' fields needs not swapping)
|
If the 'node' is not the received stanza but locally created ('to' and 'from' fields needs not swapping)
|
||||||
specify the 'reply' argument as false."""
|
specify the 'reply' argument as false.'''
|
||||||
if reply: Protocol.__init__(self,to=node.getFrom(),frm=node.getTo(),node=node)
|
if reply: Protocol.__init__(self,to=node.getFrom(),frm=node.getTo(),node=node)
|
||||||
else: Protocol.__init__(self,node=node)
|
else: Protocol.__init__(self,node=node)
|
||||||
self.setError(error)
|
self.setError(error)
|
||||||
if node.getType()=='error': self.__str__=self.__dupstr__
|
if node.getType()=='error': self.__str__=self.__dupstr__
|
||||||
def __dupstr__(self,dup1=None,dup2=None):
|
def __dupstr__(self,dup1=None,dup2=None):
|
||||||
""" Dummy function used as preventor of creating error node in reply to error node.
|
''' Dummy function used as preventor of creating error node in reply to error node.
|
||||||
I.e. you will not be able to serialise "double" error into string.
|
I.e. you will not be able to serialise "double" error into string.
|
||||||
"""
|
'''
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
class DataField(Node):
|
class DataField(Node):
|
||||||
""" This class is used in the DataForm class to describe the single data item.
|
''' This class is used in the DataForm class to describe the single data item.
|
||||||
If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122)
|
If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122)
|
||||||
then you will need to work with instances of this class. """
|
then you will need to work with instances of this class. '''
|
||||||
def __init__(self,name=None,value=None,typ=None,required=0,desc=None,options=[],node=None):
|
def __init__(self,name=None,value=None,typ=None,required=0,desc=None,options=[],node=None):
|
||||||
""" Create new data field of specified name,value and type.
|
''' Create new data field of specified name,value and type.
|
||||||
Also 'required','desc' and 'options' fields can be set.
|
Also 'required','desc' and 'options' fields can be set.
|
||||||
Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new datafiled.
|
Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new datafiled.
|
||||||
"""
|
'''
|
||||||
Node.__init__(self,'field',node=node)
|
Node.__init__(self,'field',node=node)
|
||||||
if name: self.setVar(name)
|
if name: self.setVar(name)
|
||||||
if type(value) in [list,tuple]: self.setValues(value)
|
if type(value) in [list,tuple]: self.setValues(value)
|
||||||
|
@ -641,70 +641,70 @@ class DataField(Node):
|
||||||
if desc: self.setDesc(desc)
|
if desc: self.setDesc(desc)
|
||||||
if options: self.setOptions(options)
|
if options: self.setOptions(options)
|
||||||
def setRequired(self,req=1):
|
def setRequired(self,req=1):
|
||||||
""" Change the state of the 'required' flag. """
|
''' Change the state of the 'required' flag. '''
|
||||||
if req: self.setTag('required')
|
if req: self.setTag('required')
|
||||||
else:
|
else:
|
||||||
try: self.delChild('required')
|
try: self.delChild('required')
|
||||||
except ValueError: return
|
except ValueError: return
|
||||||
def isRequired(self):
|
def isRequired(self):
|
||||||
""" Returns in this field a required one. """
|
''' Returns in this field a required one. '''
|
||||||
return self.getTag('required')
|
return self.getTag('required')
|
||||||
def setDesc(self,desc):
|
def setDesc(self,desc):
|
||||||
""" Set the description of this field. """
|
''' Set the description of this field. '''
|
||||||
self.setTagData('desc',desc)
|
self.setTagData('desc',desc)
|
||||||
def getDesc(self):
|
def getDesc(self):
|
||||||
""" Return the description of this field. """
|
''' Return the description of this field. '''
|
||||||
return self.getTagData('desc')
|
return self.getTagData('desc')
|
||||||
def setValue(self,val):
|
def setValue(self,val):
|
||||||
""" Set the value of this field. """
|
''' Set the value of this field. '''
|
||||||
self.setTagData('value',val)
|
self.setTagData('value',val)
|
||||||
def getValue(self):
|
def getValue(self):
|
||||||
return self.getTagData('value')
|
return self.getTagData('value')
|
||||||
def setValues(self,lst):
|
def setValues(self,lst):
|
||||||
""" Set the values of this field as values-list.
|
''' Set the values of this field as values-list.
|
||||||
Replaces all previous filed values! If you need to just add a value - use addValue method."""
|
Replaces all previous filed values! If you need to just add a value - use addValue method.'''
|
||||||
while self.getTag('value'): self.delChild('value')
|
while self.getTag('value'): self.delChild('value')
|
||||||
for val in lst: self.addValue(val)
|
for val in lst: self.addValue(val)
|
||||||
def addValue(self,val):
|
def addValue(self,val):
|
||||||
""" Add one more value to this field. Used in 'get' iq's or such."""
|
''' Add one more value to this field. Used in 'get' iq's or such.'''
|
||||||
self.addChild('value',{},[val])
|
self.addChild('value',{},[val])
|
||||||
def getValues(self):
|
def getValues(self):
|
||||||
""" Return the list of values associated with this field."""
|
''' Return the list of values associated with this field.'''
|
||||||
ret=[]
|
ret=[]
|
||||||
for tag in self.getTags('value'): ret.append(tag.getData())
|
for tag in self.getTags('value'): ret.append(tag.getData())
|
||||||
return ret
|
return ret
|
||||||
def getOptions(self):
|
def getOptions(self):
|
||||||
""" Return label-option pairs list associated with this field."""
|
''' Return label-option pairs list associated with this field.'''
|
||||||
ret=[]
|
ret=[]
|
||||||
for tag in self.getTags('option'): ret.append([tag.getAttr('label'),tag.getTagData('value')])
|
for tag in self.getTags('option'): ret.append([tag.getAttr('label'),tag.getTagData('value')])
|
||||||
return ret
|
return ret
|
||||||
def setOptions(self,lst):
|
def setOptions(self,lst):
|
||||||
""" Set label-option pairs list associated with this field."""
|
''' Set label-option pairs list associated with this field.'''
|
||||||
while self.getTag('option'): self.delChild('option')
|
while self.getTag('option'): self.delChild('option')
|
||||||
for opt in lst: self.addOption(opt)
|
for opt in lst: self.addOption(opt)
|
||||||
def addOption(self,opt):
|
def addOption(self,opt):
|
||||||
""" Add one more label-option pair to this field."""
|
''' Add one more label-option pair to this field.'''
|
||||||
if type(opt) in [str,unicode]: self.addChild('option').setTagData('value',opt)
|
if type(opt) in [str,unicode]: self.addChild('option').setTagData('value',opt)
|
||||||
else: self.addChild('option',{'label':opt[0]}).setTagData('value',opt[1])
|
else: self.addChild('option',{'label':opt[0]}).setTagData('value',opt[1])
|
||||||
def getType(self):
|
def getType(self):
|
||||||
""" Get type of this field. """
|
''' Get type of this field. '''
|
||||||
return self.getAttr('type')
|
return self.getAttr('type')
|
||||||
def setType(self,val):
|
def setType(self,val):
|
||||||
""" Set type of this field. """
|
''' Set type of this field. '''
|
||||||
return self.setAttr('type',val)
|
return self.setAttr('type',val)
|
||||||
def getVar(self):
|
def getVar(self):
|
||||||
""" Get 'var' attribute value of this field. """
|
''' Get 'var' attribute value of this field. '''
|
||||||
return self.getAttr('var')
|
return self.getAttr('var')
|
||||||
def setVar(self,val):
|
def setVar(self,val):
|
||||||
""" Set 'var' attribute value of this field. """
|
''' Set 'var' attribute value of this field. '''
|
||||||
return self.setAttr('var',val)
|
return self.setAttr('var',val)
|
||||||
|
|
||||||
class DataForm(Node):
|
class DataForm(Node):
|
||||||
""" DataForm class. Used for manipulating dataforms in XMPP.
|
''' DataForm class. Used for manipulating dataforms in XMPP.
|
||||||
Relevant XEPs: 0004, 0068, 0122.
|
Relevant XEPs: 0004, 0068, 0122.
|
||||||
Can be used in disco, pub-sub and many other applications."""
|
Can be used in disco, pub-sub and many other applications.'''
|
||||||
def __init__(self, typ=None, data=[], title=None, node=None):
|
def __init__(self, typ=None, data=[], title=None, node=None):
|
||||||
"""
|
'''
|
||||||
Create new dataform of type 'typ'. 'data' is the list of DataField
|
Create new dataform of type 'typ'. 'data' is the list of DataField
|
||||||
instances that this dataform contains, 'title' - the title string.
|
instances that this dataform contains, 'title' - the title string.
|
||||||
You can specify the 'node' argument as the other node to be used as
|
You can specify the 'node' argument as the other node to be used as
|
||||||
|
@ -716,7 +716,7 @@ class DataForm(Node):
|
||||||
'typ' of reply iq can be ( 'result' | 'set' | 'set' | 'result' ) respectively.
|
'typ' of reply iq can be ( 'result' | 'set' | 'set' | 'result' ) respectively.
|
||||||
'cancel' form can not contain any fields. All other forms contains AT LEAST one field.
|
'cancel' form can not contain any fields. All other forms contains AT LEAST one field.
|
||||||
'title' MAY be included in forms of type "form" and "result"
|
'title' MAY be included in forms of type "form" and "result"
|
||||||
"""
|
'''
|
||||||
Node.__init__(self,'x',node=node)
|
Node.__init__(self,'x',node=node)
|
||||||
if node:
|
if node:
|
||||||
newkids=[]
|
newkids=[]
|
||||||
|
@ -736,36 +736,36 @@ class DataForm(Node):
|
||||||
elif child.__class__.__name__=='DataField': self.kids.append(child)
|
elif child.__class__.__name__=='DataField': self.kids.append(child)
|
||||||
else: self.kids.append(DataField(node=child))
|
else: self.kids.append(DataField(node=child))
|
||||||
def getType(self):
|
def getType(self):
|
||||||
""" Return the type of dataform. """
|
''' Return the type of dataform. '''
|
||||||
return self.getAttr('type')
|
return self.getAttr('type')
|
||||||
def setType(self,typ):
|
def setType(self,typ):
|
||||||
""" Set the type of dataform. """
|
''' Set the type of dataform. '''
|
||||||
self.setAttr('type',typ)
|
self.setAttr('type',typ)
|
||||||
def getTitle(self):
|
def getTitle(self):
|
||||||
""" Return the title of dataform. """
|
''' Return the title of dataform. '''
|
||||||
return self.getTagData('title')
|
return self.getTagData('title')
|
||||||
def setTitle(self,text):
|
def setTitle(self,text):
|
||||||
""" Set the title of dataform. """
|
''' Set the title of dataform. '''
|
||||||
self.setTagData('title',text)
|
self.setTagData('title',text)
|
||||||
def getInstructions(self):
|
def getInstructions(self):
|
||||||
""" Return the instructions of dataform. """
|
''' Return the instructions of dataform. '''
|
||||||
return self.getTagData('instructions')
|
return self.getTagData('instructions')
|
||||||
def setInstructions(self,text):
|
def setInstructions(self,text):
|
||||||
""" Set the instructions of dataform. """
|
''' Set the instructions of dataform. '''
|
||||||
self.setTagData('instructions',text)
|
self.setTagData('instructions',text)
|
||||||
def addInstructions(self,text):
|
def addInstructions(self,text):
|
||||||
""" Add one more instruction to the dataform. """
|
''' Add one more instruction to the dataform. '''
|
||||||
self.addChild('instructions',{},[text])
|
self.addChild('instructions',{},[text])
|
||||||
def getField(self,name):
|
def getField(self,name):
|
||||||
""" Return the datafield object with name 'name' (if exists). """
|
''' Return the datafield object with name 'name' (if exists). '''
|
||||||
return self.getTag('field',attrs={'var':name})
|
return self.getTag('field',attrs={'var':name})
|
||||||
def setField(self,name):
|
def setField(self,name):
|
||||||
""" Create if nessessary or get the existing datafield object with name 'name' and return it. """
|
''' Create if nessessary or get the existing datafield object with name 'name' and return it. '''
|
||||||
f=self.getField(name)
|
f=self.getField(name)
|
||||||
if f: return f
|
if f: return f
|
||||||
return self.addChild(node=DataField(name))
|
return self.addChild(node=DataField(name))
|
||||||
def asDict(self):
|
def asDict(self):
|
||||||
""" Represent dataform as simple dictionary mapping of datafield names to their values."""
|
''' Represent dataform as simple dictionary mapping of datafield names to their values.'''
|
||||||
ret={}
|
ret={}
|
||||||
for field in self.getTags('field'):
|
for field in self.getTags('field'):
|
||||||
name=field.getAttr('var')
|
name=field.getAttr('var')
|
||||||
|
@ -778,10 +778,10 @@ class DataForm(Node):
|
||||||
if self.getTag('instructions'): ret['instructions']=self.getInstructions()
|
if self.getTag('instructions'): ret['instructions']=self.getInstructions()
|
||||||
return ret
|
return ret
|
||||||
def __getitem__(self,name):
|
def __getitem__(self,name):
|
||||||
""" Simple dictionary interface for getting datafields values by their names."""
|
''' Simple dictionary interface for getting datafields values by their names.'''
|
||||||
item=self.getField(name)
|
item=self.getField(name)
|
||||||
if item: return item.getValue()
|
if item: return item.getValue()
|
||||||
raise IndexError('No such field')
|
raise IndexError('No such field')
|
||||||
def __setitem__(self,name,val):
|
def __setitem__(self,name,val):
|
||||||
""" Simple dictionary interface for setting datafields values by their names."""
|
''' Simple dictionary interface for setting datafields values by their names.'''
|
||||||
return self.setField(name).setValue(val)
|
return self.setField(name).setValue(val)
|
||||||
|
|
|
@ -29,31 +29,31 @@ log = logging.getLogger('gajim.c.x.roster_nb')
|
||||||
|
|
||||||
|
|
||||||
class NonBlockingRoster(PlugIn):
|
class NonBlockingRoster(PlugIn):
|
||||||
""" Defines a plenty of methods that will allow you to manage roster.
|
''' Defines a plenty of methods that will allow you to manage roster.
|
||||||
Also automatically track presences from remote JIDs taking into
|
Also automatically track presences from remote JIDs taking into
|
||||||
account that every JID can have multiple resources connected. Does not
|
account that every JID can have multiple resources connected. Does not
|
||||||
currently support 'error' presences.
|
currently support 'error' presences.
|
||||||
You can also use mapping interface for access to the internal representation of
|
You can also use mapping interface for access to the internal representation of
|
||||||
contacts in roster.
|
contacts in roster.
|
||||||
"""
|
'''
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
""" Init internal variables. """
|
''' Init internal variables. '''
|
||||||
PlugIn.__init__(self)
|
PlugIn.__init__(self)
|
||||||
self._data = {}
|
self._data = {}
|
||||||
self.set=None
|
self.set=None
|
||||||
self._exported_methods=[self.getRoster]
|
self._exported_methods=[self.getRoster]
|
||||||
|
|
||||||
def Request(self,force=0):
|
def Request(self,force=0):
|
||||||
""" Request roster from server if it were not yet requested
|
''' Request roster from server if it were not yet requested
|
||||||
(or if the 'force' argument is set). """
|
(or if the 'force' argument is set). '''
|
||||||
if self.set is None: self.set=0
|
if self.set is None: self.set=0
|
||||||
elif not force: return
|
elif not force: return
|
||||||
self._owner.send(Iq('get',NS_ROSTER))
|
self._owner.send(Iq('get',NS_ROSTER))
|
||||||
log.info('Roster requested from server')
|
log.info('Roster requested from server')
|
||||||
|
|
||||||
def RosterIqHandler(self,dis,stanza):
|
def RosterIqHandler(self,dis,stanza):
|
||||||
""" Subscription tracker. Used internally for setting items state in
|
''' Subscription tracker. Used internally for setting items state in
|
||||||
internal roster representation. """
|
internal roster representation. '''
|
||||||
sender = stanza.getAttr('from')
|
sender = stanza.getAttr('from')
|
||||||
if not sender is None and not sender.bareMatch(
|
if not sender is None and not sender.bareMatch(
|
||||||
self._owner.User + '@' + self._owner.Server):
|
self._owner.User + '@' + self._owner.Server):
|
||||||
|
@ -75,8 +75,8 @@ class NonBlockingRoster(PlugIn):
|
||||||
self.set=1
|
self.set=1
|
||||||
|
|
||||||
def PresenceHandler(self,dis,pres):
|
def PresenceHandler(self,dis,pres):
|
||||||
""" Presence tracker. Used internally for setting items' resources state in
|
''' Presence tracker. Used internally for setting items' resources state in
|
||||||
internal roster representation. """
|
internal roster representation. '''
|
||||||
jid=pres.getFrom()
|
jid=pres.getFrom()
|
||||||
if not jid:
|
if not jid:
|
||||||
# If no from attribue, it's from server
|
# If no from attribue, it's from server
|
||||||
|
@ -100,11 +100,11 @@ class NonBlockingRoster(PlugIn):
|
||||||
# Need to handle type='error' also
|
# Need to handle type='error' also
|
||||||
|
|
||||||
def _getItemData(self,jid,dataname):
|
def _getItemData(self,jid,dataname):
|
||||||
""" Return specific jid's representation in internal format. Used internally. """
|
''' Return specific jid's representation in internal format. Used internally. '''
|
||||||
jid=jid[:(jid+'/').find('/')]
|
jid=jid[:(jid+'/').find('/')]
|
||||||
return self._data[jid][dataname]
|
return self._data[jid][dataname]
|
||||||
def _getResourceData(self,jid,dataname):
|
def _getResourceData(self,jid,dataname):
|
||||||
""" Return specific jid's resource representation in internal format. Used internally. """
|
''' Return specific jid's resource representation in internal format. Used internally. '''
|
||||||
if jid.find('/')+1:
|
if jid.find('/')+1:
|
||||||
jid,resource=jid.split('/',1)
|
jid,resource=jid.split('/',1)
|
||||||
if self._data[jid]['resources'].has_key(resource): return self._data[jid]['resources'][resource][dataname]
|
if self._data[jid]['resources'].has_key(resource): return self._data[jid]['resources'][resource][dataname]
|
||||||
|
@ -114,40 +114,40 @@ class NonBlockingRoster(PlugIn):
|
||||||
if int(self._data[jid]['resources'][r]['priority'])>lastpri: resource,lastpri=r,int(self._data[jid]['resources'][r]['priority'])
|
if int(self._data[jid]['resources'][r]['priority'])>lastpri: resource,lastpri=r,int(self._data[jid]['resources'][r]['priority'])
|
||||||
return self._data[jid]['resources'][resource][dataname]
|
return self._data[jid]['resources'][resource][dataname]
|
||||||
def delItem(self,jid):
|
def delItem(self,jid):
|
||||||
""" Delete contact 'jid' from roster."""
|
''' Delete contact 'jid' from roster.'''
|
||||||
self._owner.send(Iq('set',NS_ROSTER,payload=[Node('item',{'jid':jid,'subscription':'remove'})]))
|
self._owner.send(Iq('set',NS_ROSTER,payload=[Node('item',{'jid':jid,'subscription':'remove'})]))
|
||||||
def getAsk(self,jid):
|
def getAsk(self,jid):
|
||||||
""" Returns 'ask' value of contact 'jid'."""
|
''' Returns 'ask' value of contact 'jid'.'''
|
||||||
return self._getItemData(jid,'ask')
|
return self._getItemData(jid,'ask')
|
||||||
def getGroups(self,jid):
|
def getGroups(self,jid):
|
||||||
""" Returns groups list that contact 'jid' belongs to."""
|
''' Returns groups list that contact 'jid' belongs to.'''
|
||||||
return self._getItemData(jid,'groups')
|
return self._getItemData(jid,'groups')
|
||||||
def getName(self,jid):
|
def getName(self,jid):
|
||||||
""" Returns name of contact 'jid'."""
|
''' Returns name of contact 'jid'.'''
|
||||||
return self._getItemData(jid,'name')
|
return self._getItemData(jid,'name')
|
||||||
def getPriority(self,jid):
|
def getPriority(self,jid):
|
||||||
""" Returns priority of contact 'jid'. 'jid' should be a full (not bare) JID."""
|
''' Returns priority of contact 'jid'. 'jid' should be a full (not bare) JID.'''
|
||||||
return self._getResourceData(jid,'priority')
|
return self._getResourceData(jid,'priority')
|
||||||
def getRawRoster(self):
|
def getRawRoster(self):
|
||||||
""" Returns roster representation in internal format. """
|
''' Returns roster representation in internal format. '''
|
||||||
return self._data
|
return self._data
|
||||||
def getRawItem(self,jid):
|
def getRawItem(self,jid):
|
||||||
""" Returns roster item 'jid' representation in internal format. """
|
''' Returns roster item 'jid' representation in internal format. '''
|
||||||
return self._data[jid[:(jid+'/').find('/')]]
|
return self._data[jid[:(jid+'/').find('/')]]
|
||||||
def getShow(self, jid):
|
def getShow(self, jid):
|
||||||
""" Returns 'show' value of contact 'jid'. 'jid' should be a full (not bare) JID."""
|
''' Returns 'show' value of contact 'jid'. 'jid' should be a full (not bare) JID.'''
|
||||||
return self._getResourceData(jid,'show')
|
return self._getResourceData(jid,'show')
|
||||||
def getStatus(self, jid):
|
def getStatus(self, jid):
|
||||||
""" Returns 'status' value of contact 'jid'. 'jid' should be a full (not bare) JID."""
|
''' Returns 'status' value of contact 'jid'. 'jid' should be a full (not bare) JID.'''
|
||||||
return self._getResourceData(jid,'status')
|
return self._getResourceData(jid,'status')
|
||||||
def getSubscription(self,jid):
|
def getSubscription(self,jid):
|
||||||
""" Returns 'subscription' value of contact 'jid'."""
|
''' Returns 'subscription' value of contact 'jid'.'''
|
||||||
return self._getItemData(jid,'subscription')
|
return self._getItemData(jid,'subscription')
|
||||||
def getResources(self,jid):
|
def getResources(self,jid):
|
||||||
""" Returns list of connected resources of contact 'jid'."""
|
''' Returns list of connected resources of contact 'jid'.'''
|
||||||
return self._data[jid[:(jid+'/').find('/')]]['resources'].keys()
|
return self._data[jid[:(jid+'/').find('/')]]['resources'].keys()
|
||||||
def setItem(self,jid,name=None,groups=[]):
|
def setItem(self,jid,name=None,groups=[]):
|
||||||
""" Renames contact 'jid' and sets the groups list that it now belongs to."""
|
''' Renames contact 'jid' and sets the groups list that it now belongs to.'''
|
||||||
iq=Iq('set',NS_ROSTER)
|
iq=Iq('set',NS_ROSTER)
|
||||||
query=iq.getTag('query')
|
query=iq.getTag('query')
|
||||||
attrs={'jid':jid}
|
attrs={'jid':jid}
|
||||||
|
@ -156,32 +156,32 @@ class NonBlockingRoster(PlugIn):
|
||||||
for group in groups: item.addChild(node=Node('group',payload=[group]))
|
for group in groups: item.addChild(node=Node('group',payload=[group]))
|
||||||
self._owner.send(iq)
|
self._owner.send(iq)
|
||||||
def getItems(self):
|
def getItems(self):
|
||||||
""" Return list of all [bare] JIDs that the roster is currently tracks."""
|
''' Return list of all [bare] JIDs that the roster is currently tracks.'''
|
||||||
return self._data.keys()
|
return self._data.keys()
|
||||||
def keys(self):
|
def keys(self):
|
||||||
""" Same as getItems. Provided for the sake of dictionary interface."""
|
''' Same as getItems. Provided for the sake of dictionary interface.'''
|
||||||
return self._data.keys()
|
return self._data.keys()
|
||||||
def __getitem__(self,item):
|
def __getitem__(self,item):
|
||||||
""" Get the contact in the internal format. Raises KeyError if JID 'item' is not in roster."""
|
''' Get the contact in the internal format. Raises KeyError if JID 'item' is not in roster.'''
|
||||||
return self._data[item]
|
return self._data[item]
|
||||||
def getItem(self,item):
|
def getItem(self,item):
|
||||||
""" Get the contact in the internal format (or None if JID 'item' is not in roster)."""
|
''' Get the contact in the internal format (or None if JID 'item' is not in roster).'''
|
||||||
if self._data.has_key(item): return self._data[item]
|
if self._data.has_key(item): return self._data[item]
|
||||||
def Subscribe(self,jid):
|
def Subscribe(self,jid):
|
||||||
""" Send subscription request to JID 'jid'."""
|
''' Send subscription request to JID 'jid'.'''
|
||||||
self._owner.send(Presence(jid,'subscribe'))
|
self._owner.send(Presence(jid,'subscribe'))
|
||||||
def Unsubscribe(self,jid):
|
def Unsubscribe(self,jid):
|
||||||
""" Ask for removing our subscription for JID 'jid'."""
|
''' Ask for removing our subscription for JID 'jid'.'''
|
||||||
self._owner.send(Presence(jid,'unsubscribe'))
|
self._owner.send(Presence(jid,'unsubscribe'))
|
||||||
def Authorize(self,jid):
|
def Authorize(self,jid):
|
||||||
""" Authorise JID 'jid'. Works only if these JID requested auth previously. """
|
''' Authorise JID 'jid'. Works only if these JID requested auth previously. '''
|
||||||
self._owner.send(Presence(jid,'subscribed'))
|
self._owner.send(Presence(jid,'subscribed'))
|
||||||
def Unauthorize(self,jid):
|
def Unauthorize(self,jid):
|
||||||
""" Unauthorise JID 'jid'. Use for declining authorisation request
|
''' Unauthorise JID 'jid'. Use for declining authorisation request
|
||||||
or for removing existing authorization. """
|
or for removing existing authorization. '''
|
||||||
self._owner.send(Presence(jid,'unsubscribed'))
|
self._owner.send(Presence(jid,'unsubscribed'))
|
||||||
def getRaw(self):
|
def getRaw(self):
|
||||||
"""Returns the internal data representation of the roster."""
|
'''Returns the internal data representation of the roster.'''
|
||||||
return self._data
|
return self._data
|
||||||
# copypasted methods for roster.py from constructor to here
|
# copypasted methods for roster.py from constructor to here
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
# $Id: simplexml.py,v 1.27 2005/04/30 07:20:27 snakeru Exp $
|
# $Id: simplexml.py,v 1.27 2005/04/30 07:20:27 snakeru Exp $
|
||||||
|
|
||||||
"""Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams.
|
'''Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams.
|
||||||
I'm personally using it in many other separate projects. It is designed to be as standalone as possible."""
|
I'm personally using it in many other separate projects. It is designed to be as standalone as possible.'''
|
||||||
|
|
||||||
import xml.parsers.expat
|
import xml.parsers.expat
|
||||||
import logging
|
import logging
|
||||||
|
@ -23,13 +23,13 @@ log = logging.getLogger('gajim.c.x.simplexml')
|
||||||
#log.setLevel(logging.DEBUG)
|
#log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
def XMLescape(txt):
|
def XMLescape(txt):
|
||||||
"""Returns provided string with symbols & < > " replaced by their respective XML entities."""
|
'''Returns provided string with symbols & < > " replaced by their respective XML entities.'''
|
||||||
# replace also FORM FEED and ESC, because they are not valid XML chars
|
# replace also FORM FEED and ESC, because they are not valid XML chars
|
||||||
return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """).replace(u'\x0C', "").replace(u'\x1B', "")
|
return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """).replace(u'\x0C', "").replace(u'\x1B', "")
|
||||||
|
|
||||||
ENCODING='utf-8'
|
ENCODING='utf-8'
|
||||||
def ustr(what):
|
def ustr(what):
|
||||||
"""Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise."""
|
'''Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise.'''
|
||||||
if type(what) == type(u''): return what
|
if type(what) == type(u''): return what
|
||||||
try: r=what.__str__()
|
try: r=what.__str__()
|
||||||
except AttributeError: r=str(what)
|
except AttributeError: r=str(what)
|
||||||
|
@ -37,7 +37,7 @@ def ustr(what):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
class Node(object):
|
class Node(object):
|
||||||
""" Node class describes syntax of separate XML Node. It have a constructor that permits node creation
|
''' Node class describes syntax of separate XML Node. It have a constructor that permits node creation
|
||||||
from set of "namespace name", attributes and payload of text strings and other nodes.
|
from set of "namespace name", attributes and payload of text strings and other nodes.
|
||||||
It does not natively support building node from text string and uses NodeBuilder class for that purpose.
|
It does not natively support building node from text string and uses NodeBuilder class for that purpose.
|
||||||
After creation node can be mangled in many ways so it can be completely changed.
|
After creation node can be mangled in many ways so it can be completely changed.
|
||||||
|
@ -50,16 +50,16 @@ class Node(object):
|
||||||
info with the "original" node that is changing the one node may influence the other. Though it is
|
info with the "original" node that is changing the one node may influence the other. Though it is
|
||||||
rarely needed (in xmpppy it is never needed at all since I'm usually never using original node after
|
rarely needed (in xmpppy it is never needed at all since I'm usually never using original node after
|
||||||
replication (and using replication only to move upwards on the classes tree).
|
replication (and using replication only to move upwards on the classes tree).
|
||||||
"""
|
'''
|
||||||
FORCE_NODE_RECREATION=0
|
FORCE_NODE_RECREATION=0
|
||||||
def __init__(self, tag=None, attrs={}, payload=[], parent=None, node=None):
|
def __init__(self, tag=None, attrs={}, payload=[], parent=None, node=None):
|
||||||
""" Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it
|
''' Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it
|
||||||
by a space), attrs dictionary as the set of arguments, payload list as the set of textual strings
|
by a space), attrs dictionary as the set of arguments, payload list as the set of textual strings
|
||||||
and child nodes that this node carries within itself and "parent" argument that is another node
|
and child nodes that this node carries within itself and "parent" argument that is another node
|
||||||
that this one will be the child of. Also the __init__ can be provided with "node" argument that is
|
that this one will be the child of. Also the __init__ can be provided with "node" argument that is
|
||||||
either a text string containing exactly one node or another Node instance to begin with. If both
|
either a text string containing exactly one node or another Node instance to begin with. If both
|
||||||
"node" and other arguments is provided then the node initially created as replica of "node"
|
"node" and other arguments is provided then the node initially created as replica of "node"
|
||||||
provided and then modified to be compliant with other arguments."""
|
provided and then modified to be compliant with other arguments.'''
|
||||||
if node:
|
if node:
|
||||||
if self.FORCE_NODE_RECREATION and isinstance(node, Node):
|
if self.FORCE_NODE_RECREATION and isinstance(node, Node):
|
||||||
node=str(node)
|
node=str(node)
|
||||||
|
@ -83,8 +83,8 @@ class Node(object):
|
||||||
else: self.data.append(ustr(i))
|
else: self.data.append(ustr(i))
|
||||||
|
|
||||||
def __str__(self,fancy=0):
|
def __str__(self,fancy=0):
|
||||||
""" Method used to dump node into textual representation.
|
''' Method used to dump node into textual representation.
|
||||||
if "fancy" argument is set to True produces indented output for readability."""
|
if "fancy" argument is set to True produces indented output for readability.'''
|
||||||
s = (fancy-1) * 2 * ' ' + "<" + self.name
|
s = (fancy-1) * 2 * ' ' + "<" + self.name
|
||||||
if self.namespace:
|
if self.namespace:
|
||||||
if not self.parent or self.parent.namespace!=self.namespace:
|
if not self.parent or self.parent.namespace!=self.namespace:
|
||||||
|
@ -115,8 +115,8 @@ class Node(object):
|
||||||
if fancy: s = s + "\n"
|
if fancy: s = s + "\n"
|
||||||
return s
|
return s
|
||||||
def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None):
|
def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None):
|
||||||
""" If "node" argument is provided, adds it as child node. Else creates new node from
|
''' If "node" argument is provided, adds it as child node. Else creates new node from
|
||||||
the other arguments' values and adds it as well."""
|
the other arguments' values and adds it as well.'''
|
||||||
if namespace: name=namespace+' '+name
|
if namespace: name=namespace+' '+name
|
||||||
if node:
|
if node:
|
||||||
newnode=node
|
newnode=node
|
||||||
|
@ -125,46 +125,46 @@ class Node(object):
|
||||||
self.kids.append(newnode)
|
self.kids.append(newnode)
|
||||||
return newnode
|
return newnode
|
||||||
def addData(self, data):
|
def addData(self, data):
|
||||||
""" Adds some CDATA to node. """
|
''' Adds some CDATA to node. '''
|
||||||
self.data.append(ustr(data))
|
self.data.append(ustr(data))
|
||||||
def clearData(self):
|
def clearData(self):
|
||||||
""" Removes all CDATA from the node. """
|
''' Removes all CDATA from the node. '''
|
||||||
self.data=[]
|
self.data=[]
|
||||||
def delAttr(self, key):
|
def delAttr(self, key):
|
||||||
""" Deletes an attribute "key" """
|
''' Deletes an attribute "key" '''
|
||||||
del self.attrs[key]
|
del self.attrs[key]
|
||||||
def delChild(self, node, attrs={}):
|
def delChild(self, node, attrs={}):
|
||||||
""" Deletes the "node" from the node's childs list, if "node" is an instance.
|
''' Deletes the "node" from the node's childs list, if "node" is an instance.
|
||||||
Else deletes the first node that have specified name and (optionally) attributes. """
|
Else deletes the first node that have specified name and (optionally) attributes. '''
|
||||||
if not isinstance(node, Node): node=self.getTag(node,attrs)
|
if not isinstance(node, Node): node=self.getTag(node,attrs)
|
||||||
self.kids.remove(node)
|
self.kids.remove(node)
|
||||||
return node
|
return node
|
||||||
def getAttrs(self):
|
def getAttrs(self):
|
||||||
""" Returns all node's attributes as dictionary. """
|
''' Returns all node's attributes as dictionary. '''
|
||||||
return self.attrs
|
return self.attrs
|
||||||
def getAttr(self, key):
|
def getAttr(self, key):
|
||||||
""" Returns value of specified attribute. """
|
''' Returns value of specified attribute. '''
|
||||||
try: return self.attrs[key]
|
try: return self.attrs[key]
|
||||||
except: return None
|
except: return None
|
||||||
def getChildren(self):
|
def getChildren(self):
|
||||||
""" Returns all node's child nodes as list. """
|
''' Returns all node's child nodes as list. '''
|
||||||
return self.kids
|
return self.kids
|
||||||
def getData(self):
|
def getData(self):
|
||||||
""" Returns all node CDATA as string (concatenated). """
|
''' Returns all node CDATA as string (concatenated). '''
|
||||||
return ''.join(self.data)
|
return ''.join(self.data)
|
||||||
def getName(self):
|
def getName(self):
|
||||||
""" Returns the name of node """
|
''' Returns the name of node '''
|
||||||
return self.name
|
return self.name
|
||||||
def getNamespace(self):
|
def getNamespace(self):
|
||||||
""" Returns the namespace of node """
|
''' Returns the namespace of node '''
|
||||||
return self.namespace
|
return self.namespace
|
||||||
def getParent(self):
|
def getParent(self):
|
||||||
""" Returns the parent of node (if present). """
|
''' Returns the parent of node (if present). '''
|
||||||
return self.parent
|
return self.parent
|
||||||
def getPayload(self):
|
def getPayload(self):
|
||||||
""" Return the payload of node i.e. list of child nodes and CDATA entries.
|
''' Return the payload of node i.e. list of child nodes and CDATA entries.
|
||||||
F.e. for "<node>text1<nodea/><nodeb/> text2</node>" will be returned list:
|
F.e. for "<node>text1<nodea/><nodeb/> text2</node>" will be returned list:
|
||||||
['text1', <nodea instance>, <nodeb instance>, ' text2']. """
|
['text1', <nodea instance>, <nodeb instance>, ' text2']. '''
|
||||||
ret=[]
|
ret=[]
|
||||||
for i in range(len(self.kids)+len(self.data)+1):
|
for i in range(len(self.kids)+len(self.data)+1):
|
||||||
try:
|
try:
|
||||||
|
@ -174,20 +174,20 @@ class Node(object):
|
||||||
except IndexError: pass
|
except IndexError: pass
|
||||||
return ret
|
return ret
|
||||||
def getTag(self, name, attrs={}, namespace=None):
|
def getTag(self, name, attrs={}, namespace=None):
|
||||||
""" Filters all child nodes using specified arguments as filter.
|
''' Filters all child nodes using specified arguments as filter.
|
||||||
Returns the first found or None if not found. """
|
Returns the first found or None if not found. '''
|
||||||
return self.getTags(name, attrs, namespace, one=1)
|
return self.getTags(name, attrs, namespace, one=1)
|
||||||
def getTagAttr(self,tag,attr):
|
def getTagAttr(self,tag,attr):
|
||||||
""" Returns attribute value of the child with specified name (or None if no such attribute)."""
|
''' Returns attribute value of the child with specified name (or None if no such attribute).'''
|
||||||
try: return self.getTag(tag).attrs[attr]
|
try: return self.getTag(tag).attrs[attr]
|
||||||
except: return None
|
except: return None
|
||||||
def getTagData(self,tag):
|
def getTagData(self,tag):
|
||||||
""" Returns cocatenated CDATA of the child with specified name."""
|
''' Returns cocatenated CDATA of the child with specified name.'''
|
||||||
try: return self.getTag(tag).getData()
|
try: return self.getTag(tag).getData()
|
||||||
except: return None
|
except: return None
|
||||||
def getTags(self, name, attrs={}, namespace=None, one=0):
|
def getTags(self, name, attrs={}, namespace=None, one=0):
|
||||||
""" Filters all child nodes using specified arguments as filter.
|
''' Filters all child nodes using specified arguments as filter.
|
||||||
Returns the list of nodes found. """
|
Returns the list of nodes found. '''
|
||||||
nodes=[]
|
nodes=[]
|
||||||
for node in self.kids:
|
for node in self.kids:
|
||||||
if namespace and namespace<>node.getNamespace(): continue
|
if namespace and namespace<>node.getNamespace(): continue
|
||||||
|
@ -199,7 +199,7 @@ class Node(object):
|
||||||
if not one: return nodes
|
if not one: return nodes
|
||||||
|
|
||||||
def iterTags(self, name, attrs={}, namespace=None):
|
def iterTags(self, name, attrs={}, namespace=None):
|
||||||
""" Iterate over all children using specified arguments as filter. """
|
''' Iterate over all children using specified arguments as filter. '''
|
||||||
for node in self.kids:
|
for node in self.kids:
|
||||||
if namespace is not None and namespace!=node.getNamespace(): continue
|
if namespace is not None and namespace!=node.getNamespace(): continue
|
||||||
if node.getName() == name:
|
if node.getName() == name:
|
||||||
|
@ -210,57 +210,57 @@ class Node(object):
|
||||||
yield node
|
yield node
|
||||||
|
|
||||||
def setAttr(self, key, val):
|
def setAttr(self, key, val):
|
||||||
""" Sets attribute "key" with the value "val". """
|
''' Sets attribute "key" with the value "val". '''
|
||||||
self.attrs[key]=val
|
self.attrs[key]=val
|
||||||
def setData(self, data):
|
def setData(self, data):
|
||||||
""" Sets node's CDATA to provided string. Resets all previous CDATA!"""
|
''' Sets node's CDATA to provided string. Resets all previous CDATA!'''
|
||||||
self.data=[ustr(data)]
|
self.data=[ustr(data)]
|
||||||
def setName(self,val):
|
def setName(self,val):
|
||||||
""" Changes the node name. """
|
''' Changes the node name. '''
|
||||||
self.name = val
|
self.name = val
|
||||||
def setNamespace(self, namespace):
|
def setNamespace(self, namespace):
|
||||||
""" Changes the node namespace. """
|
''' Changes the node namespace. '''
|
||||||
self.namespace=namespace
|
self.namespace=namespace
|
||||||
def setParent(self, node):
|
def setParent(self, node):
|
||||||
""" Sets node's parent to "node". WARNING: do not checks if the parent already present
|
''' Sets node's parent to "node". WARNING: do not checks if the parent already present
|
||||||
and not removes the node from the list of childs of previous parent. """
|
and not removes the node from the list of childs of previous parent. '''
|
||||||
self.parent = node
|
self.parent = node
|
||||||
def setPayload(self,payload,add=0):
|
def setPayload(self,payload,add=0):
|
||||||
""" Sets node payload according to the list specified. WARNING: completely replaces all node's
|
''' Sets node payload according to the list specified. WARNING: completely replaces all node's
|
||||||
previous content. If you wish just to add child or CDATA - use addData or addChild methods. """
|
previous content. If you wish just to add child or CDATA - use addData or addChild methods. '''
|
||||||
if type(payload) in (type(''),type(u'')): payload=[payload]
|
if type(payload) in (type(''),type(u'')): payload=[payload]
|
||||||
if add: self.kids+=payload
|
if add: self.kids+=payload
|
||||||
else: self.kids=payload
|
else: self.kids=payload
|
||||||
def setTag(self, name, attrs={}, namespace=None):
|
def setTag(self, name, attrs={}, namespace=None):
|
||||||
""" Same as getTag but if the node with specified namespace/attributes not found, creates such
|
''' Same as getTag but if the node with specified namespace/attributes not found, creates such
|
||||||
node and returns it. """
|
node and returns it. '''
|
||||||
node=self.getTags(name, attrs, namespace=namespace, one=1)
|
node=self.getTags(name, attrs, namespace=namespace, one=1)
|
||||||
if node: return node
|
if node: return node
|
||||||
else: return self.addChild(name, attrs, namespace=namespace)
|
else: return self.addChild(name, attrs, namespace=namespace)
|
||||||
def setTagAttr(self,tag,attr,val):
|
def setTagAttr(self,tag,attr,val):
|
||||||
""" Creates new node (if not already present) with name "tag"
|
''' Creates new node (if not already present) with name "tag"
|
||||||
and sets it's attribute "attr" to value "val". """
|
and sets it's attribute "attr" to value "val". '''
|
||||||
try: self.getTag(tag).attrs[attr]=val
|
try: self.getTag(tag).attrs[attr]=val
|
||||||
except: self.addChild(tag,attrs={attr:val})
|
except: self.addChild(tag,attrs={attr:val})
|
||||||
def setTagData(self,tag,val,attrs={}):
|
def setTagData(self,tag,val,attrs={}):
|
||||||
""" Creates new node (if not already present) with name "tag" and (optionally) attributes "attrs"
|
''' Creates new node (if not already present) with name "tag" and (optionally) attributes "attrs"
|
||||||
and sets it's CDATA to string "val". """
|
and sets it's CDATA to string "val". '''
|
||||||
try: self.getTag(tag,attrs).setData(ustr(val))
|
try: self.getTag(tag,attrs).setData(ustr(val))
|
||||||
except: self.addChild(tag,attrs,payload=[ustr(val)])
|
except: self.addChild(tag,attrs,payload=[ustr(val)])
|
||||||
def has_attr(self,key):
|
def has_attr(self,key):
|
||||||
""" Checks if node have attribute "key"."""
|
''' Checks if node have attribute "key".'''
|
||||||
return self.attrs.has_key(key)
|
return self.attrs.has_key(key)
|
||||||
def __getitem__(self,item):
|
def __getitem__(self,item):
|
||||||
""" Returns node's attribute "item" value. """
|
''' Returns node's attribute "item" value. '''
|
||||||
return self.getAttr(item)
|
return self.getAttr(item)
|
||||||
def __setitem__(self,item,val):
|
def __setitem__(self,item,val):
|
||||||
""" Sets node's attribute "item" value. """
|
''' Sets node's attribute "item" value. '''
|
||||||
return self.setAttr(item,val)
|
return self.setAttr(item,val)
|
||||||
def __delitem__(self,item):
|
def __delitem__(self,item):
|
||||||
""" Deletes node's attribute "item". """
|
''' Deletes node's attribute "item". '''
|
||||||
return self.delAttr(item)
|
return self.delAttr(item)
|
||||||
def __getattr__(self,attr):
|
def __getattr__(self,attr):
|
||||||
""" Reduce memory usage caused by T/NT classes - use memory only when needed. """
|
''' Reduce memory usage caused by T/NT classes - use memory only when needed. '''
|
||||||
if attr=='T':
|
if attr=='T':
|
||||||
self.T=T(self)
|
self.T=T(self)
|
||||||
return self.T
|
return self.T
|
||||||
|
@ -270,7 +270,7 @@ class Node(object):
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
class T:
|
class T:
|
||||||
""" Auxiliary class used to quick access to node's child nodes. """
|
''' Auxiliary class used to quick access to node's child nodes. '''
|
||||||
def __init__(self,node): self.__dict__['node']=node
|
def __init__(self,node): self.__dict__['node']=node
|
||||||
def __getattr__(self,attr): return self.node.setTag(attr)
|
def __getattr__(self,attr): return self.node.setTag(attr)
|
||||||
def __setattr__(self,attr,val):
|
def __setattr__(self,attr,val):
|
||||||
|
@ -279,25 +279,25 @@ class T:
|
||||||
def __delattr__(self,attr): return self.node.delChild(attr)
|
def __delattr__(self,attr): return self.node.delChild(attr)
|
||||||
|
|
||||||
class NT(T):
|
class NT(T):
|
||||||
""" Auxiliary class used to quick create node's child nodes. """
|
''' Auxiliary class used to quick create node's child nodes. '''
|
||||||
def __getattr__(self,attr): return self.node.addChild(attr)
|
def __getattr__(self,attr): return self.node.addChild(attr)
|
||||||
def __setattr__(self,attr,val):
|
def __setattr__(self,attr,val):
|
||||||
if isinstance(val,Node): self.node.addChild(attr,node=val)
|
if isinstance(val,Node): self.node.addChild(attr,node=val)
|
||||||
else: return self.node.addChild(attr,payload=[val])
|
else: return self.node.addChild(attr,payload=[val])
|
||||||
|
|
||||||
class NodeBuilder:
|
class NodeBuilder:
|
||||||
""" Builds a Node class minidom from data parsed to it. This class used for two purposes:
|
''' Builds a Node class minidom from data parsed to it. This class used for two purposes:
|
||||||
1. Creation an XML Node from a textual representation. F.e. reading a config file. See an XML2Node method.
|
1. Creation an XML Node from a textual representation. F.e. reading a config file. See an XML2Node method.
|
||||||
2. Handling an incoming XML stream. This is done by mangling
|
2. Handling an incoming XML stream. This is done by mangling
|
||||||
the __dispatch_depth parameter and redefining the dispatch method.
|
the __dispatch_depth parameter and redefining the dispatch method.
|
||||||
You do not need to use this class directly if you do not designing your own XML handler."""
|
You do not need to use this class directly if you do not designing your own XML handler.'''
|
||||||
def __init__(self,data=None,initial_node=None):
|
def __init__(self,data=None,initial_node=None):
|
||||||
""" Takes two optional parameters: "data" and "initial_node".
|
''' Takes two optional parameters: "data" and "initial_node".
|
||||||
By default class initialised with empty Node class instance.
|
By default class initialised with empty Node class instance.
|
||||||
Though, if "initial_node" is provided it used as "starting point".
|
Though, if "initial_node" is provided it used as "starting point".
|
||||||
You can think about it as of "node upgrade".
|
You can think about it as of "node upgrade".
|
||||||
"data" (if provided) feeded to parser immidiatedly after instance init.
|
"data" (if provided) feeded to parser immidiatedly after instance init.
|
||||||
"""
|
'''
|
||||||
log.debug("Preparing to handle incoming XML stream.")
|
log.debug("Preparing to handle incoming XML stream.")
|
||||||
self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ')
|
self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ')
|
||||||
self._parser.StartElementHandler = self.starttag
|
self._parser.StartElementHandler = self.starttag
|
||||||
|
@ -328,7 +328,7 @@ class NodeBuilder:
|
||||||
self.data_buffer = None
|
self.data_buffer = None
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
""" Method used to allow class instance to be garbage-collected. """
|
''' Method used to allow class instance to be garbage-collected. '''
|
||||||
self.check_data_buffer()
|
self.check_data_buffer()
|
||||||
self._parser.StartElementHandler = None
|
self._parser.StartElementHandler = None
|
||||||
self._parser.EndElementHandler = None
|
self._parser.EndElementHandler = None
|
||||||
|
@ -336,7 +336,7 @@ class NodeBuilder:
|
||||||
self._parser.StartNamespaceDeclHandler = None
|
self._parser.StartNamespaceDeclHandler = None
|
||||||
|
|
||||||
def starttag(self, tag, attrs):
|
def starttag(self, tag, attrs):
|
||||||
"""XML Parser callback. Used internally"""
|
'''XML Parser callback. Used internally'''
|
||||||
self.check_data_buffer()
|
self.check_data_buffer()
|
||||||
attlist=attrs.keys() #
|
attlist=attrs.keys() #
|
||||||
for attr in attlist: # FIXME: Crude hack. And it also slows down the whole library considerably.
|
for attr in attlist: # FIXME: Crude hack. And it also slows down the whole library considerably.
|
||||||
|
@ -364,7 +364,7 @@ class NodeBuilder:
|
||||||
self._ptr.parent.data.append('')
|
self._ptr.parent.data.append('')
|
||||||
self.last_is_data = 0
|
self.last_is_data = 0
|
||||||
def endtag(self, tag ):
|
def endtag(self, tag ):
|
||||||
"""XML Parser callback. Used internally"""
|
'''XML Parser callback. Used internally'''
|
||||||
log.info("DEPTH -> %i , tag -> %s" % (self.__depth, tag))
|
log.info("DEPTH -> %i , tag -> %s" % (self.__depth, tag))
|
||||||
self.check_data_buffer()
|
self.check_data_buffer()
|
||||||
if self.__depth == self._dispatch_depth:
|
if self.__depth == self._dispatch_depth:
|
||||||
|
@ -386,27 +386,27 @@ class NodeBuilder:
|
||||||
self.last_is_data = 1
|
self.last_is_data = 1
|
||||||
|
|
||||||
def handle_namespace_start(self, prefix, uri):
|
def handle_namespace_start(self, prefix, uri):
|
||||||
"""XML Parser callback. Used internally"""
|
'''XML Parser callback. Used internally'''
|
||||||
self.check_data_buffer()
|
self.check_data_buffer()
|
||||||
if prefix: self.namespaces[uri]=prefix+':'
|
if prefix: self.namespaces[uri]=prefix+':'
|
||||||
else: self.xmlns=uri
|
else: self.xmlns=uri
|
||||||
|
|
||||||
def getDom(self):
|
def getDom(self):
|
||||||
""" Returns just built Node. """
|
''' Returns just built Node. '''
|
||||||
self.check_data_buffer()
|
self.check_data_buffer()
|
||||||
return self._mini_dom
|
return self._mini_dom
|
||||||
def dispatch(self,stanza):
|
def dispatch(self,stanza):
|
||||||
""" Gets called when the NodeBuilder reaches some level of depth on it's way up with the built
|
''' Gets called when the NodeBuilder reaches some level of depth on it's way up with the built
|
||||||
node as argument. Can be redefined to convert incoming XML stanzas to program events. """
|
node as argument. Can be redefined to convert incoming XML stanzas to program events. '''
|
||||||
def stream_header_received(self,ns,tag,attrs):
|
def stream_header_received(self,ns,tag,attrs):
|
||||||
""" Method called when stream just opened. """
|
''' Method called when stream just opened. '''
|
||||||
self.check_data_buffer()
|
self.check_data_buffer()
|
||||||
def stream_footer_received(self):
|
def stream_footer_received(self):
|
||||||
""" Method called when stream just closed. """
|
''' Method called when stream just closed. '''
|
||||||
self.check_data_buffer()
|
self.check_data_buffer()
|
||||||
|
|
||||||
def has_received_endtag(self, level=0):
|
def has_received_endtag(self, level=0):
|
||||||
""" Return True if at least one end tag was seen (at level) """
|
''' Return True if at least one end tag was seen (at level) '''
|
||||||
return self.__depth <= level and self.__max_depth > level
|
return self.__depth <= level and self.__max_depth > level
|
||||||
|
|
||||||
def _inc_depth(self):
|
def _inc_depth(self):
|
||||||
|
@ -419,12 +419,12 @@ class NodeBuilder:
|
||||||
self.__depth -= 1
|
self.__depth -= 1
|
||||||
|
|
||||||
def XML2Node(xml):
|
def XML2Node(xml):
|
||||||
""" Converts supplied textual string into XML node. Handy f.e. for reading configuration file.
|
''' Converts supplied textual string into XML node. Handy f.e. for reading configuration file.
|
||||||
Raises xml.parser.expat.parsererror if provided string is not well-formed XML. """
|
Raises xml.parser.expat.parsererror if provided string is not well-formed XML. '''
|
||||||
return NodeBuilder(xml).getDom()
|
return NodeBuilder(xml).getDom()
|
||||||
|
|
||||||
def BadXML2Node(xml):
|
def BadXML2Node(xml):
|
||||||
""" Converts supplied textual string into XML node. Survives if xml data is cutted half way round.
|
''' Converts supplied textual string into XML node. Survives if xml data is cutted half way round.
|
||||||
I.e. "<html>some text <br>some more text". Will raise xml.parser.expat.parsererror on misplaced
|
I.e. "<html>some text <br>some more text". Will raise xml.parser.expat.parsererror on misplaced
|
||||||
tags though. F.e. "<b>some text <br>some more text</b>" will not work."""
|
tags though. F.e. "<b>some text <br>some more text</b>" will not work.'''
|
||||||
return NodeBuilder(xml).getDom()
|
return NodeBuilder(xml).getDom()
|
||||||
|
|
|
@ -129,13 +129,13 @@ class SSLWrapper:
|
||||||
log.debug("%s.__init__ called with %s", self.__class__, sslobj)
|
log.debug("%s.__init__ called with %s", self.__class__, sslobj)
|
||||||
|
|
||||||
def recv(self, data, flags=None):
|
def recv(self, data, flags=None):
|
||||||
""" Receive wrapper for SSL object
|
''' Receive wrapper for SSL object
|
||||||
|
|
||||||
We can return None out of this function to signal that no data is
|
We can return None out of this function to signal that no data is
|
||||||
available right now. Better than an exception, which differs
|
available right now. Better than an exception, which differs
|
||||||
depending on which SSL lib we're using. Unfortunately returning ''
|
depending on which SSL lib we're using. Unfortunately returning ''
|
||||||
can indicate that the socket has been closed, so to be sure, we avoid
|
can indicate that the socket has been closed, so to be sure, we avoid
|
||||||
this by returning None. """
|
this by returning None. '''
|
||||||
|
|
||||||
raise NotImplementedException()
|
raise NotImplementedException()
|
||||||
|
|
||||||
|
|
|
@ -61,14 +61,10 @@ def get_proxy_data_from_dict(proxy):
|
||||||
|
|
||||||
# user and pass for socks5/http_connect proxy. In case of BOSH, it's user and
|
# user and pass for socks5/http_connect proxy. In case of BOSH, it's user and
|
||||||
# pass for http proxy - If there's no proxy_host they won't be used
|
# pass for http proxy - If there's no proxy_host they won't be used
|
||||||
if proxy.has_key('user'):
|
if proxy.has_key('user'): proxy_user = proxy['user']
|
||||||
proxy_user = proxy['user']
|
else: proxy_user = None
|
||||||
else:
|
if proxy.has_key('pass'): proxy_pass = proxy['pass']
|
||||||
proxy_user = None
|
else: proxy_pass = None
|
||||||
if proxy.has_key('pass'):
|
|
||||||
proxy_pass = proxy['pass']
|
|
||||||
else:
|
|
||||||
proxy_pass = None
|
|
||||||
return tcp_host, tcp_port, proxy_user, proxy_pass
|
return tcp_host, tcp_port, proxy_user, proxy_pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,6 +85,7 @@ DATA_SENT='DATA SENT'
|
||||||
|
|
||||||
|
|
||||||
DISCONNECTED ='DISCONNECTED'
|
DISCONNECTED ='DISCONNECTED'
|
||||||
|
DISCONNECTING ='DISCONNECTING'
|
||||||
CONNECTING ='CONNECTING'
|
CONNECTING ='CONNECTING'
|
||||||
CONNECTED ='CONNECTED'
|
CONNECTED ='CONNECTED'
|
||||||
|
|
||||||
|
@ -132,11 +129,10 @@ class NonBlockingTransport(PlugIn):
|
||||||
self.on_connect_failure = on_connect_failure
|
self.on_connect_failure = on_connect_failure
|
||||||
(self.server, self.port) = conn_5tuple[4][:2]
|
(self.server, self.port) = conn_5tuple[4][:2]
|
||||||
self.conn_5tuple = conn_5tuple
|
self.conn_5tuple = conn_5tuple
|
||||||
log.info('NonBlocking Connect :: About to connect to %s:%s' % (self.server, self.port))
|
|
||||||
|
|
||||||
|
|
||||||
def set_state(self, newstate):
|
def set_state(self, newstate):
|
||||||
assert(newstate in [DISCONNECTED, CONNECTING, CONNECTED])
|
assert(newstate in [DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING])
|
||||||
self.state = newstate
|
self.state = newstate
|
||||||
|
|
||||||
def _on_connect(self, data):
|
def _on_connect(self, data):
|
||||||
|
@ -156,9 +152,8 @@ class NonBlockingTransport(PlugIn):
|
||||||
self.on_connect_failure(err_message=err_message)
|
self.on_connect_failure(err_message=err_message)
|
||||||
|
|
||||||
def send(self, raw_data, now=False):
|
def send(self, raw_data, now=False):
|
||||||
if self.state not in [CONNECTED]:
|
if self.state != CONNECTED:
|
||||||
# FIXME better handling needed
|
log.error('Trying to send %s when state is %s.' %
|
||||||
log.error('Trying to send %s when transport is %s.' %
|
|
||||||
(raw_data, self.state))
|
(raw_data, self.state))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -195,13 +190,13 @@ class NonBlockingTransport(PlugIn):
|
||||||
self.remove_timeout()
|
self.remove_timeout()
|
||||||
|
|
||||||
def set_timeout(self, timeout):
|
def set_timeout(self, timeout):
|
||||||
self.idlequeue.set_read_timeout(self.get_fd(), timeout)
|
self.idlequeue.set_read_timeout(self.fd, timeout)
|
||||||
|
|
||||||
def get_fd(self):
|
def get_fd(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def remove_timeout(self):
|
def remove_timeout(self):
|
||||||
self.idlequeue.remove_timeout(self.get_fd())
|
self.idlequeue.remove_timeout(self.fd)
|
||||||
|
|
||||||
def set_send_timeout(self, timeout, on_timeout):
|
def set_send_timeout(self, timeout, on_timeout):
|
||||||
self.sendtimeout = timeout
|
self.sendtimeout = timeout
|
||||||
|
@ -220,26 +215,15 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
Class constructor.
|
Class constructor.
|
||||||
'''
|
'''
|
||||||
NonBlockingTransport.__init__(self, raise_event, on_disconnect, idlequeue)
|
NonBlockingTransport.__init__(self, raise_event, on_disconnect, idlequeue)
|
||||||
# writable, readable - keep state of the last pluged flags
|
|
||||||
# This prevents replug of same object with the same flags
|
|
||||||
self.writable = True
|
|
||||||
self.readable = False
|
|
||||||
|
|
||||||
# queue with messages to be send
|
# queue with messages to be send
|
||||||
self.sendqueue = []
|
self.sendqueue = []
|
||||||
|
|
||||||
# bytes remained from the last send message
|
# bytes remained from the last send message
|
||||||
self.sendbuff = ''
|
self.sendbuff = ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_fd(self):
|
|
||||||
try:
|
|
||||||
tmp = self._sock.fileno()
|
|
||||||
return tmp
|
|
||||||
except socket.error, (errnum, errstr):
|
|
||||||
log.error('Trying to get file descriptor of not-connected socket: %s' % errstr )
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def connect(self, conn_5tuple, on_connect, on_connect_failure):
|
def connect(self, conn_5tuple, on_connect, on_connect_failure):
|
||||||
'''
|
'''
|
||||||
Creates and connects socket to server and port defined in conn_5tupe which
|
Creates and connects socket to server and port defined in conn_5tupe which
|
||||||
|
@ -250,17 +234,21 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
connection
|
connection
|
||||||
'''
|
'''
|
||||||
NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure)
|
NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure)
|
||||||
|
log.info('NonBlockingTCP Connect :: About to connect to %s:%s' % (self.server, self.port))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._sock = socket.socket(*conn_5tuple[:3])
|
self._sock = socket.socket(*conn_5tuple[:3])
|
||||||
except socket.error, (errnum, errstr):
|
except socket.error, (errnum, errstr):
|
||||||
self._on_connect_failure('NonBlockingTCP: Error while creating socket: %s %s' % (errnum, errstr))
|
self._on_connect_failure('NonBlockingTCP Connect: Error while creating socket:\
|
||||||
|
%s %s' % (errnum, errstr))
|
||||||
return
|
return
|
||||||
|
|
||||||
self._send = self._sock.send
|
self._send = self._sock.send
|
||||||
self._recv = self._sock.recv
|
self._recv = self._sock.recv
|
||||||
self.fd = self._sock.fileno()
|
self.fd = self._sock.fileno()
|
||||||
self.idlequeue.plug_idle(self, True, False)
|
|
||||||
|
# we want to be notified when send is possible to connected socket
|
||||||
|
self._plug_idle(writable=True, readable=False)
|
||||||
self.peerhost = None
|
self.peerhost = None
|
||||||
|
|
||||||
errnum = 0
|
errnum = 0
|
||||||
|
@ -268,7 +256,7 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
|
|
||||||
# set timeout for TCP connecting - if nonblocking connect() fails, pollend
|
# set timeout for TCP connecting - if nonblocking connect() fails, pollend
|
||||||
# is called. If if succeeds pollout is called.
|
# is called. If if succeeds pollout is called.
|
||||||
self.idlequeue.set_read_timeout(self.get_fd(), CONNECT_TIMEOUT_SECONDS)
|
self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT_SECONDS)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._sock.setblocking(False)
|
self._sock.setblocking(False)
|
||||||
|
@ -296,7 +284,7 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
|
|
||||||
def _on_connect(self, data):
|
def _on_connect(self, data):
|
||||||
''' with TCP socket, we have to remove send-timeout '''
|
''' with TCP socket, we have to remove send-timeout '''
|
||||||
self.idlequeue.remove_timeout(self.get_fd())
|
self.idlequeue.remove_timeout(self.fd)
|
||||||
|
|
||||||
NonBlockingTransport._on_connect(self, data)
|
NonBlockingTransport._on_connect(self, data)
|
||||||
|
|
||||||
|
@ -328,12 +316,14 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
def disconnect(self, do_callback=True):
|
def disconnect(self, do_callback=True):
|
||||||
if self.state == DISCONNECTED:
|
if self.state == DISCONNECTED:
|
||||||
return
|
return
|
||||||
self.idlequeue.unplug_idle(self.get_fd())
|
self.set_state(DISCONNECTING)
|
||||||
|
self.idlequeue.unplug_idle(self.fd)
|
||||||
try:
|
try:
|
||||||
self._sock.shutdown(socket.SHUT_RDWR)
|
self._sock.shutdown(socket.SHUT_RDWR)
|
||||||
self._sock.close()
|
self._sock.close()
|
||||||
except socket.error, (errnum, errstr):
|
except socket.error, (errnum, errstr):
|
||||||
log.error('Error disconnecting a socket: %s %s' % (errnum,errstr))
|
log.error('Error while disconnecting a socket: %s %s' % (errnum,errstr))
|
||||||
|
self.fd = -1
|
||||||
NonBlockingTransport.disconnect(self, do_callback)
|
NonBlockingTransport.disconnect(self, do_callback)
|
||||||
|
|
||||||
def read_timeout(self):
|
def read_timeout(self):
|
||||||
|
@ -352,12 +342,16 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
|
|
||||||
|
|
||||||
def set_timeout(self, timeout):
|
def set_timeout(self, timeout):
|
||||||
if self.state in [CONNECTING, CONNECTED] and self.get_fd() > 0:
|
if self.state in [CONNECTING, CONNECTED] and self.fd != -1:
|
||||||
NonBlockingTransport.set_timeout(self, timeout)
|
NonBlockingTransport.set_timeout(self, timeout)
|
||||||
|
else:
|
||||||
|
log.warn('set_timeout: TIMEOUT NOT SET: state is %s, fd is %s' % (self.state, self.fd))
|
||||||
|
|
||||||
def remove_timeout(self):
|
def remove_timeout(self):
|
||||||
if self.get_fd():
|
if self.fd:
|
||||||
NonBlockingTransport.remove_timeout(self)
|
NonBlockingTransport.remove_timeout(self)
|
||||||
|
else:
|
||||||
|
log.warn('remove_timeout: no self.fd state is %s' % self.state)
|
||||||
|
|
||||||
def send(self, raw_data, now=False):
|
def send(self, raw_data, now=False):
|
||||||
'''Append raw_data to the queue of messages to be send.
|
'''Append raw_data to the queue of messages to be send.
|
||||||
|
@ -374,39 +368,42 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
self._do_send()
|
self._do_send()
|
||||||
else:
|
else:
|
||||||
self.sendqueue.append(r)
|
self.sendqueue.append(r)
|
||||||
self._plug_idle()
|
self._plug_idle(writable=True, readable=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _plug_idle(self):
|
def _plug_idle(self, writable, readable):
|
||||||
# readable if socket is connected or disconnecting
|
'''
|
||||||
readable = self.state != DISCONNECTED
|
Plugs file descriptor of socket to Idlequeue. Plugged socket
|
||||||
fd = self.get_fd()
|
will be watched for "send possible" or/and "recv possible" events. pollin()
|
||||||
# writeable if sth to send
|
callback is invoked on "recv possible", pollout() on "send_possible".
|
||||||
if self.sendqueue or self.sendbuff:
|
Plugged socket will always be watched for "error" event - in that case,
|
||||||
writable = True
|
pollend() is called.
|
||||||
else:
|
'''
|
||||||
writable = False
|
# if we are connecting, we shouln't touch the socket until it's connected
|
||||||
log.debug('About to plug fd %d, W:%s, R:%s' % (fd, writable, readable))
|
assert(self.state!=CONNECTING)
|
||||||
if self.writable != writable or self.readable != readable:
|
self.idlequeue.plug_idle(self, writable, readable)
|
||||||
log.debug('Really plugging fd %d, W:%s, R:%s' % (fd, writable, readable))
|
|
||||||
self.idlequeue.plug_idle(self, writable, readable)
|
log.info('Plugging fd %d, W:%s, R:%s' % (self.fd, writable, readable))
|
||||||
else:
|
self.idlequeue.plug_idle(self, writable, readable)
|
||||||
log.debug('Not plugging fd %s because it\'s already plugged' % fd)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _do_send(self):
|
def _do_send(self):
|
||||||
if not self.sendbuff:
|
if not self.sendbuff:
|
||||||
if not self.sendqueue:
|
if not self.sendqueue:
|
||||||
return None # nothing to send
|
log.warn('calling send on empty buffer and queue')
|
||||||
|
return None
|
||||||
self.sendbuff = self.sendqueue.pop(0)
|
self.sendbuff = self.sendqueue.pop(0)
|
||||||
try:
|
try:
|
||||||
send_count = self._send(self.sendbuff)
|
send_count = self._send(self.sendbuff)
|
||||||
if send_count:
|
if send_count:
|
||||||
sent_data = self.sendbuff[:send_count]
|
sent_data = self.sendbuff[:send_count]
|
||||||
self.sendbuff = self.sendbuff[send_count:]
|
self.sendbuff = self.sendbuff[send_count:]
|
||||||
self._plug_idle()
|
self._plug_idle(
|
||||||
|
writable=self.sendqueue or self.sendbuff,
|
||||||
|
readable=True)
|
||||||
self.raise_event(DATA_SENT, sent_data)
|
self.raise_event(DATA_SENT, sent_data)
|
||||||
|
|
||||||
except socket.error, e:
|
except socket.error, e:
|
||||||
|
@ -439,7 +436,10 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
# ESHUTDOWN - shutdown(2) has been called on a socket to close down the
|
# ESHUTDOWN - shutdown(2) has been called on a socket to close down the
|
||||||
# sending end of the transmision, and then data was attempted to be sent
|
# sending end of the transmision, and then data was attempted to be sent
|
||||||
log.error("Connection to %s lost: %s %s" % ( self.server, errnum, errstr))
|
log.error("Connection to %s lost: %s %s" % ( self.server, errnum, errstr))
|
||||||
self.disconnect()
|
if self.on_remote_disconnect:
|
||||||
|
self.on_remote_disconnect()
|
||||||
|
else:
|
||||||
|
self.disconnect()
|
||||||
return
|
return
|
||||||
|
|
||||||
if received is None:
|
if received is None:
|
||||||
|
@ -454,15 +454,13 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
# we have received some bytes, stop the timeout!
|
# we have received some bytes, stop the timeout!
|
||||||
self.renew_send_timeout()
|
self.renew_send_timeout()
|
||||||
# pass received data to owner
|
# pass received data to owner
|
||||||
#self.
|
|
||||||
if self.on_receive:
|
if self.on_receive:
|
||||||
self.raise_event(DATA_RECEIVED, received)
|
self.raise_event(DATA_RECEIVED, received)
|
||||||
self._on_receive(received)
|
self._on_receive(received)
|
||||||
else:
|
else:
|
||||||
# This should never happen, so we need the debug. (If there is no handler
|
# This should never happen, so we need the debug. (If there is no handler
|
||||||
# on receive spacified, data are passed to Dispatcher.ProcessNonBlocking)
|
# on receive specified, data are passed to Dispatcher.ProcessNonBlocking)
|
||||||
log.error('SOCKET %s Unhandled data received: %s' % (id(self), received))
|
log.error('SOCKET %s Unhandled data received: %s' % (id(self), received))
|
||||||
import traceback
|
|
||||||
traceback.print_stack()
|
traceback.print_stack()
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
|
|
||||||
|
@ -474,12 +472,14 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
|
||||||
|
|
||||||
class NonBlockingHTTP(NonBlockingTCP):
|
class NonBlockingHTTP(NonBlockingTCP):
|
||||||
'''
|
'''
|
||||||
Socket wrapper that cretes HTTP message out of sent data and peels-off
|
Socket wrapper that creates HTTP message out of sent data and peels-off
|
||||||
HTTP headers from incoming messages
|
HTTP headers from incoming messages
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, raise_event, on_disconnect, idlequeue, on_http_request_possible,
|
def __init__(self, raise_event, on_disconnect, idlequeue, on_http_request_possible,
|
||||||
http_uri, http_port, http_version='HTTP/1.1'):
|
http_uri, http_port, http_version='HTTP/1.1', http_persistent=False):
|
||||||
|
|
||||||
|
NonBlockingTCP.__init__(self, raise_event, on_disconnect, idlequeue)
|
||||||
|
|
||||||
self.http_protocol, self.http_host, self.http_path = urisplit(http_uri)
|
self.http_protocol, self.http_host, self.http_path = urisplit(http_uri)
|
||||||
if self.http_protocol is None:
|
if self.http_protocol is None:
|
||||||
|
@ -488,12 +488,12 @@ class NonBlockingHTTP(NonBlockingTCP):
|
||||||
http_path = '/'
|
http_path = '/'
|
||||||
self.http_port = http_port
|
self.http_port = http_port
|
||||||
self.http_version = http_version
|
self.http_version = http_version
|
||||||
|
self.http_persistent = http_persistent
|
||||||
# buffer for partial responses
|
# buffer for partial responses
|
||||||
self.recvbuff = ''
|
self.recvbuff = ''
|
||||||
self.expected_length = 0
|
self.expected_length = 0
|
||||||
self.pending_requests = 0
|
self.pending_requests = 0
|
||||||
self.on_http_request_possible = on_http_request_possible
|
self.on_http_request_possible = on_http_request_possible
|
||||||
NonBlockingTCP.__init__(self, raise_event, on_disconnect, idlequeue)
|
|
||||||
|
|
||||||
def send(self, raw_data, now=False):
|
def send(self, raw_data, now=False):
|
||||||
NonBlockingTCP.send(
|
NonBlockingTCP.send(
|
||||||
|
@ -503,6 +503,19 @@ class NonBlockingHTTP(NonBlockingTCP):
|
||||||
self.pending_requests += 1
|
self.pending_requests += 1
|
||||||
|
|
||||||
|
|
||||||
|
def on_remote_disconnect(self):
|
||||||
|
if self.http_persistent:
|
||||||
|
self.http_persistent = False
|
||||||
|
self.disconnect(do_callback=False)
|
||||||
|
self.connect(
|
||||||
|
conn_5tuple = self.conn_5tuple,
|
||||||
|
on_connect = lambda: self._plug_idle(writable=True, readable=True),
|
||||||
|
on_connect_failure = self.disconnect)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
|
||||||
def _on_receive(self,data):
|
def _on_receive(self,data):
|
||||||
'''Preceeds passing received data to owner class. Gets rid of HTTP headers
|
'''Preceeds passing received data to owner class. Gets rid of HTTP headers
|
||||||
and checks them.'''
|
and checks them.'''
|
||||||
|
@ -524,9 +537,6 @@ class NonBlockingHTTP(NonBlockingTCP):
|
||||||
log.info('not enough bytes - %d expected, %d got' % (self.expected_length, len(self.recvbuff)))
|
log.info('not enough bytes - %d expected, %d got' % (self.expected_length, len(self.recvbuff)))
|
||||||
return
|
return
|
||||||
|
|
||||||
# FIXME the reassembling doesn't work - Connection Manager on jabbim.cz
|
|
||||||
# closes TCP connection before sending <Content-Length> announced bytes.. WTF
|
|
||||||
|
|
||||||
# all was received, now call the on_receive callback
|
# all was received, now call the on_receive callback
|
||||||
httpbody = self.recvbuff
|
httpbody = self.recvbuff
|
||||||
|
|
||||||
|
@ -534,17 +544,18 @@ class NonBlockingHTTP(NonBlockingTCP):
|
||||||
self.expected_length=0
|
self.expected_length=0
|
||||||
self.pending_requests -= 1
|
self.pending_requests -= 1
|
||||||
assert(self.pending_requests >= 0)
|
assert(self.pending_requests >= 0)
|
||||||
# not-persistent connections
|
if not self.http_persistent:
|
||||||
self.disconnect(do_callback = False)
|
# not-persistent connections disconnect after response
|
||||||
|
self.disconnect(do_callback = False)
|
||||||
self.on_receive(httpbody)
|
self.on_receive(httpbody)
|
||||||
self.on_http_request_possible()
|
self.on_http_request_possible()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def build_http_message(self, httpbody, method='POST'):
|
def build_http_message(self, httpbody, method='POST'):
|
||||||
'''
|
'''
|
||||||
Builds http message with given body.
|
Builds http message with given body.
|
||||||
Values for headers and status line fields are taken from class variables.
|
Values for headers and status line fields are taken from class variables.
|
||||||
)
|
|
||||||
'''
|
'''
|
||||||
absolute_uri = '%s://%s:%s%s' % (self.http_protocol, self.http_host,
|
absolute_uri = '%s://%s:%s%s' % (self.http_protocol, self.http_host,
|
||||||
self.http_port, self.http_path)
|
self.http_port, self.http_path)
|
||||||
|
|
|
@ -50,7 +50,8 @@ import logging
|
||||||
consoleloghandler = logging.StreamHandler()
|
consoleloghandler = logging.StreamHandler()
|
||||||
consoleloghandler.setLevel(1)
|
consoleloghandler.setLevel(1)
|
||||||
consoleloghandler.setFormatter(
|
consoleloghandler.setFormatter(
|
||||||
logging.Formatter('%(name)s: %(levelname)s: %(message)s'))
|
logging.Formatter('%(name)s: %(levelname)s: %(message)s')
|
||||||
|
)
|
||||||
log = logging.getLogger('gajim')
|
log = logging.getLogger('gajim')
|
||||||
log.setLevel(logging.WARNING)
|
log.setLevel(logging.WARNING)
|
||||||
log.addHandler(consoleloghandler)
|
log.addHandler(consoleloghandler)
|
||||||
|
|
Loading…
Add table
Reference in a new issue