From 952e4a1569f8eb08e070b61e751cfad05ba71650 Mon Sep 17 00:00:00 2001 From: tomk Date: Wed, 2 Jul 2008 23:29:10 +0000 Subject: [PATCH] moved bosh code from client_nb.py to bosh.py, replaced debug logging with debug.py by logging in whole xmpppy (debug.py is now unused) --- src/common/connection.py | 33 +++--- src/common/xmpp/__init__.py | 3 +- src/common/xmpp/auth_nb.py | 65 ++++++----- src/common/xmpp/bosh.py | 112 ++++++++++++++++++ src/common/xmpp/client.py | 45 +------ src/common/xmpp/client_nb.py | 162 +++++-------------------- src/common/xmpp/dispatcher_nb.py | 39 ++++--- src/common/xmpp/idlequeue.py | 7 +- src/common/xmpp/roster_nb.py | 11 +- src/common/xmpp/simplexml.py | 15 +-- src/common/xmpp/tls_nb.py | 24 ++-- src/common/xmpp/transports_nb.py | 195 ++++++++++++++++--------------- src/gajim.py | 2 +- 13 files changed, 356 insertions(+), 357 deletions(-) create mode 100644 src/common/xmpp/bosh.py diff --git a/src/common/connection.py b/src/common/connection.py index edb788aa3..649baf178 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -208,7 +208,7 @@ class Connection(ConnectionHandlers): def _disconnectedReconnCB(self): '''Called when we are disconnected''' - log.error('disconnectedReconnCB') + log.info('disconnectedReconnCB called') if gajim.account_is_connected(self.name): # we cannot change our status to offline or connecting # after we auth to server @@ -531,23 +531,17 @@ class Connection(ConnectionHandlers): secur = None if self._proxy and self._proxy['type'] == 'bosh': - clientClass = common.xmpp.BOSHClient + clientClass = common.xmpp.bosh.BOSHClient else: clientClass = common.xmpp.NonBlockingClient - if gajim.verbose: - con = common.xmpp.NonBlockingClient( - hostname=self._current_host['host'], - port=port, - caller=self, - idlequeue=gajim.idlequeue) - else: - con = common.xmpp.NonBlockingClient( - hostname=self._current_host['host'], - debug=[], - port=port, - caller=self, - idlequeue=gajim.idlequeue) + # there was: + # "if gajim.verbose:" + # here + con = clientClass( + domain=self._hostname, + caller=self, + idlequeue=gajim.idlequeue) self.last_connection = con # increase default timeout for server responses @@ -555,10 +549,19 @@ class Connection(ConnectionHandlers): # FIXME: this is a hack; need a better way if self.on_connect_success == self._on_new_account: con.RegisterDisconnectHandler(self._on_new_account) + + # FIXME: BOSH properties should be in proxy dictionary - loaded from + # config + if self._proxy and self._proxy['type'] == 'bosh': + self._proxy['bosh_hold'] = '1' + self._proxy['bosh_wait'] = '60' + log.info('Connecting to %s: [%s:%d]', self.name, self._current_host['host'], port) con.connect( + hostname=self._current_host['host'], + port=port, on_connect=self.on_connect_success, on_proxy_failure=self.on_proxy_failure, on_connect_failure=self.connect_to_next_type, diff --git a/src/common/xmpp/__init__.py b/src/common/xmpp/__init__.py index 90f852598..109b9b4c0 100644 --- a/src/common/xmpp/__init__.py +++ b/src/common/xmpp/__init__.py @@ -26,7 +26,8 @@ and use only methods for access all values you should not have any problems. """ -import simplexml,protocol,debug,auth_nb,transports_nb,roster_nb,dispatcher_nb,features_nb,idlequeue +import simplexml, protocol, auth_nb, transports_nb, roster_nb +import dispatcher_nb, features_nb, idlequeue, bosh, tls_nb from client_nb import * from client import * from protocol import * diff --git a/src/common/xmpp/auth_nb.py b/src/common/xmpp/auth_nb.py index 662b3f60f..a05f82481 100644 --- a/src/common/xmpp/auth_nb.py +++ b/src/common/xmpp/auth_nb.py @@ -22,6 +22,10 @@ from protocol import * from client import PlugIn import sha,base64,random,dispatcher_nb +import logging +log = logging.getLogger('gajim.c.x.auth_nb') + + import md5 def HH(some): return md5.new(some).hexdigest() def H(some): return md5.new(some).digest() @@ -128,7 +132,7 @@ class SASL(PlugIn): ''' Used to determine if server supports SASL auth. Used internally. ''' if not feats.getTag('mechanisms', namespace=NS_SASL): self.startsasl='not-supported' - self.DEBUG('SASL not supported by server', 'error') + log.error('SASL not supported by server') return mecs=[] for mec in feats.getTag('mechanisms', namespace=NS_SASL).getTags('mechanism'): @@ -145,7 +149,7 @@ class SASL(PlugIn): payload=[base64.encodestring(sasl_data).replace('\n','')]) else: self.startsasl='failure' - self.DEBUG('I can only use DIGEST-MD5 and PLAIN mecanisms.', 'error') + log.error('I can only use DIGEST-MD5 and PLAIN mecanisms.') return self.startsasl='in-process' self._owner.send(node.__str__()) @@ -161,13 +165,13 @@ class SASL(PlugIn): reason = challenge.getChildren()[0] except: reason = challenge - self.DEBUG('Failed SASL authentification: %s' % reason, 'error') + log.error('Failed SASL authentification: %s' % reason) if self.on_sasl : self.on_sasl () raise NodeProcessed elif challenge.getName() == 'success': self.startsasl='success' - self.DEBUG('Successfully authenticated with remote server.', 'ok') + log.info('Successfully authenticated with remote server.') handlers=self._owner.Dispatcher.dumpHandlers() print '6' * 79 print handlers @@ -182,7 +186,7 @@ class SASL(PlugIn): ########################################3333 incoming_data = challenge.getData() data=base64.decodestring(incoming_data) - self.DEBUG('Got challenge:'+data,'ok') + log.info('Got challenge:'+data) chal = challenge_splitter(data) if not self.realm and chal.has_key('realm'): self.realm = chal['realm'] @@ -224,7 +228,7 @@ class SASL(PlugIn): self._owner.send(Node('response', attrs={'xmlns':NS_SASL}).__str__()) else: self.startsasl='failure' - self.DEBUG('Failed SASL authentification: unknown challenge', 'error') + log.error('Failed SASL authentification: unknown challenge') if self.on_sasl : self.on_sasl () raise NodeProcessed @@ -236,7 +240,6 @@ class NonBlockingNonSASL(PlugIn): def __init__(self, user, password, resource, on_auth): ''' Caches username, password and resource for auth. ''' PlugIn.__init__(self) - self.DBG_LINE ='gen_auth' self.user = user self.password= password self.resource = resource @@ -248,7 +251,7 @@ class NonBlockingNonSASL(PlugIn): Returns used method name on success. Used internally. ''' if not self.resource: return self.authComponent(owner) - self.DEBUG('Querying server about possible auth methods', 'start') + log.info('Querying server about possible auth methods') self.owner = owner resp = owner.Dispatcher.SendAndWaitForResponse( @@ -257,7 +260,7 @@ class NonBlockingNonSASL(PlugIn): def _on_username(self, resp): if not isResultNode(resp): - self.DEBUG('No result node arrived! Aborting...','error') + log.error('No result node arrived! Aborting...') return self.on_auth(None) iq=Iq(typ='set',node=resp) query=iq.getTag('query') @@ -265,7 +268,7 @@ class NonBlockingNonSASL(PlugIn): query.setTagData('resource',self.resource) if query.getTag('digest'): - self.DEBUG("Performing digest authentication",'ok') + log.info("Performing digest authentication") query.setTagData('digest', sha.new(self.owner.Dispatcher.Stream._document_attrs['id']+self.password).hexdigest()) if query.getTag('password'): @@ -274,26 +277,26 @@ class NonBlockingNonSASL(PlugIn): elif query.getTag('token'): token=query.getTagData('token') seq=query.getTagData('sequence') - self.DEBUG("Performing zero-k authentication",'ok') + log.info("Performing zero-k authentication") hash = sha.new(sha.new(self.password).hexdigest()+token).hexdigest() for foo in xrange(int(seq)): hash = sha.new(hash).hexdigest() query.setTagData('hash',hash) self._method='0k' else: - self.DEBUG("Sequre methods unsupported, performing plain text authentication",'warn') + log.warn("Sequre methods unsupported, performing plain text authentication") query.setTagData('password',self.password) self._method='plain' resp=self.owner.Dispatcher.SendAndWaitForResponse(iq, func=self._on_auth) def _on_auth(self, resp): if isResultNode(resp): - self.DEBUG('Sucessfully authenticated with remove host.','ok') + log.info('Sucessfully authenticated with remove host.') self.owner.User=self.user self.owner.Resource=self.resource self.owner._registered_name=self.owner.User+'@'+self.owner.Server+'/'+self.owner.Resource return self.on_auth(self._method) - self.DEBUG('Authentication failed!','error') + log.error('Authentication failed!') return self.on_auth(None) def authComponent(self,owner): @@ -309,7 +312,7 @@ class NonBlockingNonSASL(PlugIn): if data: self.Dispatcher.ProcessNonBlocking(data) if not self.handshake: - self.DEBUG('waiting on handshake', 'notify') + log.info('waiting on handshake') return self._owner.onreceive(None) owner._registered_name=self.user @@ -329,14 +332,13 @@ class NonBlockingBind(PlugIn): def __init__(self): PlugIn.__init__(self) - self.DBG_LINE='bind' 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' - self.DEBUG('Server does not requested binding.','error') + log.error('Server does not requested binding.') return if feats.getTag('session',namespace=NS_SESSION): self.session=1 else: self.session=-1 @@ -372,36 +374,36 @@ class NonBlockingBind(PlugIn): def _on_bound(self, resp): if isResultNode(resp): self.bound.append(resp.getTag('bind').getTagData('jid')) - self.DEBUG('Successfully bound %s.'%self.bound[-1],'ok') + log.info('Successfully bound %s.'%self.bound[-1]) jid=JID(resp.getTag('bind').getTagData('jid')) self._owner.User=jid.getNode() self._owner.Resource=jid.getResource() self._owner.SendAndWaitForResponse(Protocol('iq', typ='set', payload=[Node('session', attrs={'xmlns':NS_SESSION})]), func=self._on_session) elif resp: - self.DEBUG('Binding failed: %s.' % resp.getTag('error'),'error') + log.error('Binding failed: %s.' % resp.getTag('error')) self.on_bound(None) else: - self.DEBUG('Binding failed: timeout expired.', 'error') + log.error('Binding failed: timeout expired.') self.on_bound(None) def _on_session(self, resp): self._owner.onreceive(None) if isResultNode(resp): - self.DEBUG('Successfully opened session.', 'ok') + log.info('Successfully opened session.') self.session = 1 self.on_bound('ok') else: - self.DEBUG('Session open failed.', 'error') + log.error('Session open failed.') self.session = 0 self.on_bound(None) self._owner.onreceive(None) if isResultNode(resp): - self.DEBUG('Successfully opened session.', 'ok') + log.info('Successfully opened session.') self.session = 1 self.on_bound('ok') else: - self.DEBUG('Session open failed.', 'error') + log.error('Session open failed.') self.session = 0 self.on_bound(None) @@ -411,7 +413,6 @@ class NBComponentBind(PlugIn): ''' def __init__(self): PlugIn.__init__(self) - self.DBG_LINE='bind' self.bound=None self.needsUnregister=None @@ -448,13 +449,13 @@ class NBComponentBind(PlugIn): def _on_bind_reponse(self, res): if resp and resp.getAttr('error'): - self.DEBUG('Binding failed: %s.' % resp.getAttr('error'), 'error') + log.error('Binding failed: %s.' % resp.getAttr('error')) elif resp: - self.DEBUG('Successfully bound.', 'ok') + log.info('Successfully bound.') if self.on_bind: self.on_bind('ok') else: - self.DEBUG('Binding failed: timeout expired.', 'error') + log.error('Binding failed: timeout expired.') if self.on_bind: self.on_bind(None) @@ -462,7 +463,7 @@ class NBComponentBind(PlugIn): """ Determine if server supports resource binding and set some internal attributes accordingly. """ if not feats.getTag('bind',namespace=NS_BIND): self.bound='failure' - self.DEBUG('Server does not requested binding.','error') + log.error('Server does not requested binding.') return if feats.getTag('session',namespace=NS_SESSION): self.session=1 else: self.session=-1 @@ -473,10 +474,10 @@ class NBComponentBind(PlugIn): 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'): - self.DEBUG('Binding failed: %s.'%resp.getAttr('error'),'error') + log.error('Binding failed: %s.'%resp.getAttr('error')) elif resp: - self.DEBUG('Successfully bound.','ok') + log.info('Successfully bound.') return 'ok' else: - self.DEBUG('Binding failed: timeout expired.','error') + log.error('Binding failed: timeout expired.') return '' diff --git a/src/common/xmpp/bosh.py b/src/common/xmpp/bosh.py new file mode 100644 index 000000000..e41322973 --- /dev/null +++ b/src/common/xmpp/bosh.py @@ -0,0 +1,112 @@ + +import protocol, simplexml, locale, random, dispatcher_nb +from client_nb import NBCommonClient +import logging +log = logging.getLogger('gajim.c.x.bosh') + + +class BOSHClient(NBCommonClient): + ''' + Client class implementing BOSH. + ''' + def __init__(self, *args, **kw): + '''Preceeds constructor of NBCommonClient and sets some of values that will + be used as attributes in tag''' + self.Namespace = protocol.NS_HTTP_BIND + # BOSH parameters should be given via Advanced Configuration Editor + self.bosh_xml_lang = None + self.bosh_hold = 1 + self.bosh_wait=60 + self.bosh_rid=None + self.bosh_sid=None + + self.bosh_httpversion = 'HTTP/1.1' + NBCommonClient.__init__(self, *args, **kw) + + + def connect(self, *args, **kw): + + + if locale.getdefaultlocale()[0]: + self.bosh_xml_lang = locale.getdefaultlocale()[0].split('_')[0] + + # with 50-bit random initial rid, session would have to go up + # to 7881299347898368 messages to raise rid over 2**53 + # (see http://www.xmpp.org/extensions/xep-0124.html#rids) + r = random.Random() + r.seed() + self.bosh_rid = r.getrandbits(50) + + proxy = kw['proxy'] + #self.bosh_protocol, self.bosh_host, self.bosh_uri = transports_nb.urisplit(proxy['host']) + self.bosh_port = proxy['port'] + self.bosh_wait = proxy['bosh_wait'] + self.bosh_hold = proxy['bosh_hold'] + self.bosh_to = proxy['to'] + #self.bosh_ack = proxy['bosh_ack'] + #self.bosh_secure = proxy['bosh_secure'] + NBCommonClient.connect(self, *args, **kw) + + def send(self, stanza, now = False): + (id, stanza_to_send) = self.Dispatcher.assign_id(stanza) + + self.Connection.send( + self.boshify_stanza(stanza_to_send), + now = now) + return id + + def get_bodytag(self): + # this should be called not until after session creation response so sid has + # to be initialized. + assert(self.sid is not None) + self.rid = self.rid+1 + return protocol.BOSHBody( + attrs={ 'rid': str(self.bosh_rid), + 'sid': self.bosh_sid}) + + + def get_initial_bodytag(self): + return protocol.BOSHBody( + attrs={'content': 'text/xml; charset=utf-8', + 'hold': str(self.bosh_hold), + 'to': self.bosh_to, + 'wait': str(self.bosh_wait), + 'rid': str(self.bosh_rid), + 'xmpp:version': '1.0', + 'xmlns:xmpp': 'urn:xmpp:xbosh'} + ) + + def get_closing_bodytag(self): + closing_bodytag = self.get_bodytag() + closing_bodytag.setAttr('type', 'terminate') + return closing_bodytag + + + def boshify_stanza(self, stanza): + ''' wraps stanza by body tag or modifies message entirely (in case of stream + opening and closing''' + log.info('boshify_staza - type is: %s' % type(stanza)) + if isinstance(stanza, simplexml.Node): + tag = self.get_bodytag() + return tag.setPayload(stanza) + else: + # only stream initialization and stream terminatoion are not Nodes + if stanza.startswith(dispatcher_nb.XML_DECLARATION): + # stream init + return self.get_initial_bodytag() + else: + # should be stream closing + assert(stanza == dispatcher_nb.STREAM_TERMINATOR) + return self.get_closing_bodytag() + + + + def _on_stream_start(self): + ''' + Called after XMPP stream is opened. In BOSH, TLS is negotiated elsewhere + so success callback can be invoked. + (authentication is started from auth() method) + ''' + self.onreceive(None) + if self.connected == 'tcp': + self._on_connect() diff --git a/src/common/xmpp/client.py b/src/common/xmpp/client.py index 201da2a78..c30905963 100644 --- a/src/common/xmpp/client.py +++ b/src/common/xmpp/client.py @@ -21,49 +21,21 @@ examples of xmpppy structures usage. These classes can be used for simple applications "AS IS" though. """ -import socket -import debug -Debug=debug -Debug.DEBUGGING_IS_ON=1 -Debug.Debug.colors['socket']=debug.color_dark_gray -Debug.Debug.colors['CONNECTproxy']=debug.color_dark_gray -Debug.Debug.colors['nodebuilder']=debug.color_brown -Debug.Debug.colors['client']=debug.color_cyan -Debug.Debug.colors['component']=debug.color_cyan -Debug.Debug.colors['dispatcher']=debug.color_green -Debug.Debug.colors['browser']=debug.color_blue -Debug.Debug.colors['auth']=debug.color_yellow -Debug.Debug.colors['roster']=debug.color_magenta -Debug.Debug.colors['ibb']=debug.color_yellow - -Debug.Debug.colors['down']=debug.color_brown -Debug.Debug.colors['up']=debug.color_brown -Debug.Debug.colors['data']=debug.color_brown -Debug.Debug.colors['ok']=debug.color_green -Debug.Debug.colors['warn']=debug.color_yellow -Debug.Debug.colors['error']=debug.color_red -Debug.Debug.colors['start']=debug.color_dark_gray -Debug.Debug.colors['stop']=debug.color_dark_gray -Debug.Debug.colors['sent']=debug.color_yellow -Debug.Debug.colors['got']=debug.color_bright_cyan - -DBG_CLIENT='client' -DBG_COMPONENT='component' +import logging +log = logging.getLogger('gajim.c.x.plugin') class PlugIn: """ Common xmpppy plugins infrastructure: plugging in/out, debugging. """ def __init__(self): self._exported_methods=[] - self.DBG_LINE=self.__class__.__name__.lower() def PlugIn(self,owner): """ Attach to main instance and register ourself and all our staff in it. """ self._owner=owner - if self.DBG_LINE not in owner.debug_flags: - owner.debug_flags.append(self.DBG_LINE) - self.DEBUG('Plugging %s into %s'%(self,self._owner),'start') + log.debug('Plugging %s into %s'%(self,self._owner)) if owner.__dict__.has_key(self.__class__.__name__): - return self.DEBUG('Plugging ignored: another instance already plugged.','error') + log.debug('Plugging ignored: another instance already plugged.') + return self._old_owners_methods=[] for method in self._exported_methods: if owner.__dict__.has_key(method.__name__): @@ -76,15 +48,10 @@ class PlugIn: def PlugOut(self): """ Unregister all our staff from main instance and detach from it. """ - self.DEBUG('Plugging %s out of %s.'%(self,self._owner),'stop') - self._owner.debug_flags.remove(self.DBG_LINE) + log.debug('Plugging %s out of %s.'%(self,self._owner)) 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 del self._owner.__dict__[self.__class__.__name__] if self.__class__.__dict__.has_key('plugout'): return self.plugout() del self._owner - def DEBUG(self,text,severity='info'): - """ Feed a provided debug line to main instance's debug facility along with our ID string. """ - self._owner.DEBUG(self.DBG_LINE,text,severity) - diff --git a/src/common/xmpp/client_nb.py b/src/common/xmpp/client_nb.py index 308300151..df6c4ca20 100644 --- a/src/common/xmpp/client_nb.py +++ b/src/common/xmpp/client_nb.py @@ -22,8 +22,6 @@ These classes can be used for simple applications "AS IS" though. ''' import socket -import debug -import random import transports_nb, tls_nb, dispatcher_nb, auth_nb, roster_nb, protocol from client import * @@ -31,54 +29,29 @@ from client import * import logging log = logging.getLogger('gajim.c.x.client_nb') -consoleloghandler = logging.StreamHandler() -consoleloghandler.setLevel(logging.DEBUG) -consoleloghandler.setFormatter( - logging.Formatter('%(levelname)s: %(message)s') -) -log.setLevel(logging.DEBUG) -log.addHandler(consoleloghandler) -log.propagate = False - class NBCommonClient: ''' Base for Client and Component classes.''' - def __init__(self, hostname, idlequeue, port=5222, debug=['always', 'nodebuilder'], caller=None): + def __init__(self, domain, idlequeue, caller=None): ''' Caches connection data: - :param hostname: hostname of machine where the XMPP server is running (from Account - of from SRV request) and port to connect to. + :param domain: domain - for to: attribute (from account info) :param idlequeue: processing idlequeue :param port: port of listening XMPP server - :param debug: specifies the debug IDs that will go into debug output. You can either - specifiy an "include" or "exclude" list. The latter is done via adding "always" - pseudo-ID to the list. Full list: ['nodebuilder', 'dispatcher', 'gen_auth', - 'SASL_auth', 'bind', 'socket', 'CONNECTproxy', 'TLS', 'roster', 'browser', 'ibb']. - TODO: get rid of debug.py using :param caller: calling object - it has to implement certain methods (necessary?) ''' - self.DBG = DBG_CLIENT - self.Namespace = protocol.NS_CLIENT self.idlequeue = idlequeue self.defaultNamespace = self.Namespace self.disconnect_handlers = [] - # XMPP server and port from account or SRV - self.Server = hostname - self.Port = port + self.Server = domain # caller is who initiated this client, it is sed to register the EventDispatcher self._caller = caller - if debug and type(debug) != list: - debug = ['always', 'nodebuilder'] - self._DEBUG = Debug.Debug(debug) - self.DEBUG = self._DEBUG.Show - self.debug_flags = self._DEBUG.debug_flags - self.debug_flags.append(self.DBG) self._owner = self self._registered_name = None self.connected = '' @@ -98,9 +71,9 @@ class NBCommonClient: ''' self.connected='' - self.DEBUG(self.DBG,'Disconnect detected','stop') + log.debug('Client disconnected..') for i in reversed(self.disconnect_handlers): - self.DEBUG(self.DBG, 'Calling disc handler %s' % i, 'stop') + log.debug('Calling disconnect handler %s' % i) i() if self.__dict__.has_key('NonBlockingRoster'): self.NonBlockingRoster.PlugOut() @@ -120,23 +93,22 @@ class NBCommonClient: self.NonBlockingTcp.PlugOut() - def send(self, stanza, is_message = False, now = False): + def send(self, stanza, now = False): ''' interface for putting stanzas on wire. Puts ID to stanza if needed and sends it via socket wrapper''' (id, stanza_to_send) = self.Dispatcher.assign_id(stanza) - if is_message: - # somehow zeroconf-specific - self.Connection.send(stanza_to_send, True, now = now) - else: - self.Connection.send(stanza_to_send, now = now) + self.Connection.send(stanza_to_send, now = now) return id - def connect(self, on_connect, on_connect_failure, on_proxy_failure=None, proxy=None, secure=None): + def connect(self, on_connect, on_connect_failure, hostname=None, port=5222, + on_proxy_failure=None, proxy=None, secure=None): ''' Open XMPP connection (open streams in both directions). + :param hostname: hostname of XMPP server from SRV request + :param port: port number of XMPP server :param on_connect: called after stream is successfully opened :param on_connect_failure: called when error occures during connection :param on_proxy_failure: called if error occurres during TCP connection to @@ -146,7 +118,12 @@ class NBCommonClient: optionally keys 'user' and 'pass' as proxy credentials :param secure: ''' - + self.Port = port + if hostname: + xmpp_hostname = hostname + else: + xmpp_hostname = self.Server + self.on_connect = on_connect self.on_connect_failure=on_connect_failure self.on_proxy_failure = on_proxy_failure @@ -155,8 +132,8 @@ class NBCommonClient: if proxy: # with proxies, client connects to proxy instead of directly to - # XMPP server from __init__. - # tcp_server is hostname used for socket connecting + # XMPP server ((hostname, port)) + # tcp_server is machine used for socket connection tcp_server=proxy['host'] tcp_port=proxy['port'] self._on_tcp_failure = self.on_proxy_failure @@ -168,30 +145,33 @@ class NBCommonClient: type_ = proxy['type'] if type_ == 'socks5': + # SOCKS5 proxy self.socket = transports_nb.NBSOCKS5ProxySocket( on_disconnect=self.on_disconnect, proxy_creds=proxy_creds, - xmpp_server=(self.Server, self.Port)) + xmpp_server=(xmpp_hostname, self.Port)) elif type_ == 'http': + # HTTP CONNECT to proxy self.socket = transports_nb.NBHTTPProxySocket( on_disconnect=self.on_disconnect, proxy_creds=proxy_creds, - xmpp_server=(self.Server, self.Port)) + xmpp_server=(xmpp_hostname, self.Port)) elif type_ == 'bosh': + # BOSH - XMPP over HTTP tcp_server = transports_nb.urisplit(tcp_server)[1] - self.socket = transports_nb.NonBlockingHttpBOSH( + self.socket = transports_nb.NonBlockingHTTP( on_disconnect=self.on_disconnect, - bosh_uri = proxy['host'], - bosh_port = tcp_port) + http_uri = proxy['host'], + http_port = tcp_port) else: # HTTP CONNECT to proxy from environment variables self.socket = transports_nb.NBHTTPProxySocket( on_disconnect=self.on_disconnect, proxy_creds=(None, None), - xmpp_server=(self.Server, self.Port)) + xmpp_server=(xmpp_hostname, self.Port)) else: self._on_tcp_failure = self._on_connect_failure - tcp_server=self.Server + tcp_server=xmpp_hostname tcp_port=self.Port self.socket = transports_nb.NonBlockingTcp(on_disconnect = self.on_disconnect) @@ -221,7 +201,7 @@ class NBCommonClient: def _try_next_ip(self, err_message=None): '''iterates over IP addresses from getaddinfo''' if err_message: - self.DEBUG(self.DBG,err_message,'connect') + log.debug('While looping over DNS A records: %s' % connect) if self.ip_addresses == []: self._on_tcp_failure(err_message='Run out of hosts for name %s:%s' % (self.Server, self.Port)) @@ -305,7 +285,7 @@ class NBCommonClient: def _on_connect_failure(self, retry=None, err_message=None): self.connected = None if err_message: - self.DEBUG(self.DBG, err_message, 'connecting') + log.debug('While connecting: %s' % err_message) if self.socket: self.socket.disconnect() self.on_connect_failure(retry) @@ -460,83 +440,3 @@ class NonBlockingClient(NBCommonClient): self.send(dispatcher_nb.Presence(to=jid, typ=typ)) -class BOSHClient(NBCommonClient): - ''' - Client class implementing BOSH. - ''' - def __init__(self, *args, **kw): - '''Preceeds constructor of NBCommonClient and sets some of values that will - be used as attributes in tag''' - self.Namespace = NS_HTTP_BIND - # BOSH parameters should be given via Advanced Configuration Editor - self.bosh_hold = 1 - self.bosh_wait=60 - self.bosh_rid=-1 - self.bosh_httpversion = 'HTTP/1.1' - NBCommonClient.__init__(self, *args, **kw) - - - def connect(self, *args, **kw): - proxy = kw['proxy'] - self.bosh_protocol, self.bosh_host, self.bosh_uri = self.urisplit(proxy['host']) - self.bosh_port = proxy['port'] - NBCommonClient.connect(*args, **kw) - - - def _on_stream_start(self): - ''' - Called after XMPP stream is opened. In BOSH TLS is negotiated on tranport layer - so success callback can be invoked. - (authentication is started from auth() method) - ''' - self.onreceive(None) - if self.connected == 'tcp': - self._on_connect() - - - - - - def bosh_raise_event(self, realm, event, data): - # should to extract stanza from body - self.DEBUG(self.DBG,'realm: %s, event: %s, data: %s' % (realm, event, data), - 'BOSH EventHandler') - self._caller._event_dispatcher(realm, event, data) - - - def StreamInit(self): - ''' - Init of BOSH session. Called instead of Dispatcher.StreamInit() - Initial body tag is created and sent. - ''' - #self.Dispatcher.RegisterEventHandler(self.bosh_event_handler) - self.Dispatcher.Stream = simplexml.NodeBuilder() - self.Dispatcher.Stream._dispatch_depth = 2 - self.Dispatcher.Stream.dispatch = self.Dispatcher.dispatch - self.Dispatcher.Stream.stream_header_received = self._check_stream_start - self.Dispatcher.Stream.features = None - - r = random.Random() - r.seed() - # with 50-bit random initial rid, session would have to go up - # to 7881299347898368 messages to raise rid over 2**53 - # (see http://www.xmpp.org/extensions/xep-0124.html#rids) - self.bosh_rid = r.getrandbits(50) - - initial_body_tag = BOSHBody( - attrs={'content': 'text/xml; charset=utf-8', - 'hold': str(self.bosh_hold), - # "to" should be domain, not hostname of machine - 'to': self.Server, - 'wait': str(self.bosh_wait), - 'rid': str(self.bosh_rid), - 'xmpp:version': '1.0', - 'xmlns:xmpp': 'urn:xmpp:xbosh'} - ) - - if locale.getdefaultlocale()[0]: - initial_body_tag.setAttr('xml:lang', - locale.getdefaultlocale()[0].split('_')[0]) - initial_body_tag.setAttr('xmpp:version', '1.0') - initial_body_tag.setAttr('xmlns:xmpp', 'urn:xmpp:xbosh') - self.send(initial_body_tag) diff --git a/src/common/xmpp/dispatcher_nb.py b/src/common/xmpp/dispatcher_nb.py index 4633db28b..3734d16a9 100644 --- a/src/common/xmpp/dispatcher_nb.py +++ b/src/common/xmpp/dispatcher_nb.py @@ -28,16 +28,21 @@ from xml.parsers.expat import ExpatError from protocol import * from client import PlugIn +import logging +log = logging.getLogger('gajim.c.x.dispatcher_nb') + # default timeout to wait for response for our id DEFAULT_TIMEOUT_SECONDS = 25 ID = 0 +STREAM_TERMINATOR = '' +XML_DECLARATION = '' + class Dispatcher(PlugIn): ''' Ancestor of PlugIn class. Handles XMPP stream, i.e. aware of stream headers. Can be plugged out/in to restart these headers (used for SASL f.e.). ''' def __init__(self): PlugIn.__init__(self) - DBG_LINE='dispatcher' self.handlers={} self._expected={} self._defaultHandler=None @@ -79,7 +84,7 @@ class Dispatcher(PlugIn): def plugin(self, owner): ''' Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally.''' - self.DEBUG('Dispatcher plugin', 'PlugIn') + log.debug('Dispatcher plugin') self._init() self._owner.lastErrNode = None @@ -93,7 +98,6 @@ class Dispatcher(PlugIn): def plugout(self): ''' Prepares instance to be destructed. ''' self.Stream.dispatch = None - self.Stream.DEBUG = None self.Stream.features = None self.Stream.destroy() self._owner = None @@ -105,8 +109,6 @@ class Dispatcher(PlugIn): self.Stream._dispatch_depth = 2 self.Stream.dispatch = self.dispatch self.Stream.stream_header_received = self._check_stream_start - self._owner.debug_flags.append(simplexml.DBG_NODEBUILDER) - self.Stream.DEBUG = self._owner.DEBUG self.Stream.features = None self._metastream = Node('stream:stream') self._metastream.setNamespace(self._owner.Namespace) @@ -116,11 +118,11 @@ class Dispatcher(PlugIn): if locale.getdefaultlocale()[0]: self._metastream.setAttr('xml:lang', locale.getdefaultlocale()[0].split('_')[0]) - self._owner.send("%s>" % str(self._metastream)[:-2]) + self._owner.send("%s%s>" % (XML_DECLARATION,str(self._metastream)[:-2])) def StreamTerminate(self): ''' Send a stream terminator. ''' - self._owner.send('') + self._owner.send(STREAM_TERMINATOR) def _check_stream_start(self, ns, tag, attrs): if ns<>NS_STREAMS or tag<>'stream': @@ -144,7 +146,7 @@ class Dispatcher(PlugIn): self._owner.Connection.disconnect() return 0 except ExpatError: - self.DEBUG('Invalid XML received from server. Forcing disconnect.', 'error') + log.error('Invalid XML received from server. Forcing disconnect.') self._owner.Connection.disconnect() return 0 if len(self._pendingExceptions) > 0: @@ -157,7 +159,7 @@ class Dispatcher(PlugIn): ''' Creates internal structures for newly registered namespace. You can register handlers for this namespace afterwards. By default one namespace already registered (jabber:client or jabber:component:accept depending on context. ''' - self.DEBUG('Registering namespace "%s"' % xmlns, order) + log.info('Registering namespace "%s"' % xmlns) self.handlers[xmlns]={} self.RegisterProtocol('unknown', Protocol, xmlns=xmlns) self.RegisterProtocol('default', Protocol, xmlns=xmlns) @@ -167,8 +169,7 @@ class Dispatcher(PlugIn): Needed to start registering handlers for such stanzas. Iq, message and presence protocols are registered by default. ''' if not xmlns: xmlns=self._owner.defaultNamespace - self.DEBUG('Registering protocol "%s" as %s(%s)' % - (tag_name, Proto, xmlns), order) + log.info('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns)) self.handlers[xmlns][tag_name]={type:Proto, 'default':[]} def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='', makefirst=0, system=0): @@ -195,8 +196,8 @@ class Dispatcher(PlugIn): ''' if not xmlns: xmlns=self._owner.defaultNamespace - self.DEBUG('Registering handler %s for "%s" type->%s ns->%s(%s)' % - (handler, name, typ, ns, xmlns), 'info') + log.info('Registering handler %s for "%s" type->%s ns->%s(%s)' % + (handler, name, typ, ns, xmlns)) if not typ and not ns: typ='default' if not self.handlers.has_key(xmlns): @@ -313,13 +314,13 @@ class Dispatcher(PlugIn): xmlns=stanza.getNamespace() if not self.handlers.has_key(xmlns): - self.DEBUG("Unknown namespace: " + xmlns, 'warn') + log.warn("Unknown namespace: " + xmlns) xmlns='unknown' if not self.handlers[xmlns].has_key(name): - self.DEBUG("Unknown stanza: " + name, 'warn') + log.warn("Unknown stanza: " + name) name='unknown' else: - self.DEBUG("Got %s/%s stanza" % (xmlns, name), 'ok') + log.debug("Got %s/%s stanza" % (xmlns, name)) if stanza.__class__.__name__=='Node': stanza=self.handlers[xmlns][name][type](node=stanza) @@ -329,7 +330,7 @@ class Dispatcher(PlugIn): stanza.props=stanza.getProperties() ID=stanza.getID() - session.DEBUG("Dispatching %s stanza with type->%s props->%s id->%s"%(name,typ,stanza.props,ID),'ok') + log.debug("Dispatching %s stanza with type->%s props->%s id->%s"%(name,typ,stanza.props,ID)) list=['default'] # we will use all handlers: if self.handlers[xmlns][name].has_key(typ): list.append(typ) # from very common... for prop in stanza.props: @@ -345,13 +346,13 @@ class Dispatcher(PlugIn): user=0 if type(session._expected[ID]) == type(()): cb,args = session._expected[ID] - session.DEBUG("Expected stanza arrived. Callback %s(%s) found!" % (cb, args), 'ok') + log.debug("Expected stanza arrived. Callback %s(%s) found!" % (cb, args)) try: cb(session,stanza,**args) except Exception, typ: if typ.__class__.__name__ <>'NodeProcessed': raise else: - session.DEBUG("Expected stanza arrived!",'ok') + log.debug("Expected stanza arrived!") session._expected[ID]=stanza else: user=1 diff --git a/src/common/xmpp/idlequeue.py b/src/common/xmpp/idlequeue.py index e9c4f0fde..2ca1b0bd3 100644 --- a/src/common/xmpp/idlequeue.py +++ b/src/common/xmpp/idlequeue.py @@ -13,6 +13,8 @@ ## GNU General Public License for more details. import select +import logging +log = logging.getLogger('gajim.c.x.idlequeue') class IdleObject: ''' base class for all idle listeners, these are the methods, which are called from IdleQueue @@ -53,7 +55,7 @@ class IdleQueue: self.selector = select.poll() def remove_timeout(self, fd): - print 'read timeout removed for fd %s' % fd + log.debug('read timeout removed for fd %s' % fd) if self.read_timeouts.has_key(fd): del(self.read_timeouts[fd]) @@ -69,7 +71,7 @@ class IdleQueue: def set_read_timeout(self, fd, seconds): ''' set a new timeout, if it is not removed after 'seconds', then obj.read_timeout() will be called ''' - print 'read timeout set for fd %s on %s seconds' % (fd, seconds) + log.debug('read timeout set for fd %s on %s seconds' % (fd, seconds)) timeout = self.current_time() + seconds self.read_timeouts[fd] = timeout @@ -79,6 +81,7 @@ class IdleQueue: if timeout > current_time: continue if self.queue.has_key(fd): + log.debug('Calling read_timeout for fd %s' % fd) self.queue[fd].read_timeout() else: self.remove_timeout(fd) diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py index b843525e7..a269880ba 100644 --- a/src/common/xmpp/roster_nb.py +++ b/src/common/xmpp/roster_nb.py @@ -24,6 +24,10 @@ mass-renaming of contacts. from protocol import * from client import PlugIn +import logging +log = logging.getLogger('gajim.c.x.roster_nb') + + class NonBlockingRoster(PlugIn): """ Defines a plenty of methods that will allow you to manage roster. Also automatically track presences from remote JIDs taking into @@ -35,7 +39,6 @@ class NonBlockingRoster(PlugIn): def __init__(self): """ Init internal variables. """ PlugIn.__init__(self) - self.DBG_LINE='roster' self._data = {} self.set=None self._exported_methods=[self.getRoster] @@ -46,7 +49,7 @@ class NonBlockingRoster(PlugIn): if self.set is None: self.set=0 elif not force: return self._owner.send(Iq('get',NS_ROSTER)) - self.DEBUG('Roster requested from server','start') + log.info('Roster requested from server') def RosterIqHandler(self,dis,stanza): """ Subscription tracker. Used internally for setting items state in @@ -60,7 +63,7 @@ class NonBlockingRoster(PlugIn): if item.getAttr('subscription')=='remove': if self._data.has_key(jid): del self._data[jid] return - self.DEBUG('Setting roster item %s...'%jid,'ok') + log.info('Setting roster item %s...' % jid) if not self._data.has_key(jid): self._data[jid]={} self._data[jid]['name']=item.getAttr('name') self._data[jid]['ask']=item.getAttr('ask') @@ -86,7 +89,7 @@ class NonBlockingRoster(PlugIn): typ=pres.getType() if not typ: - self.DEBUG('Setting roster item %s for resource %s...'%(jid.getStripped(),jid.getResource()),'ok') + log.info('Setting roster item %s for resource %s...'%(jid.getStripped(),jid.getResource())) item['resources'][jid.getResource()]=res={'show':None,'status':None,'priority':'0','timestamp':None} if pres.getTag('show'): res['show']=pres.getShow() if pres.getTag('status'): res['status']=pres.getStatus() diff --git a/src/common/xmpp/simplexml.py b/src/common/xmpp/simplexml.py index 89a6cec59..f7561269b 100644 --- a/src/common/xmpp/simplexml.py +++ b/src/common/xmpp/simplexml.py @@ -18,6 +18,9 @@ I'm personally using it in many other separate projects. It is designed to be as standalone as possible.""" import xml.parsers.expat +import logging +log = logging.getLogger('gajim.c.x.simplexml') + def XMLescape(txt): """Returns provided string with symbols & < > " replaced by their respective XML entities.""" @@ -279,7 +282,6 @@ class NT(T): if isinstance(val,Node): self.node.addChild(attr,node=val) else: return self.node.addChild(attr,payload=[val]) -DBG_NODEBUILDER = 'nodebuilder' class NodeBuilder: """ 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. @@ -293,7 +295,7 @@ class NodeBuilder: You can think about it as of "node upgrade". "data" (if provided) feeded to parser immidiatedly after instance init. """ - self.DEBUG(DBG_NODEBUILDER, "Preparing to handle incoming XML stream.", 'start') + log.debug("Preparing to handle incoming XML stream.") self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ') self._parser.StartElementHandler = self.starttag self._parser.EndElementHandler = self.endtag @@ -341,7 +343,7 @@ class NodeBuilder: attrs[self.namespaces[ns]+attr[sp+1:]]=attrs[attr] del attrs[attr] # self._inc_depth() - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`), 'down') + log.info("DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`)) if self.__depth == self._dispatch_depth: if not self._mini_dom : self._mini_dom = Node(tag=tag, attrs=attrs) @@ -360,14 +362,14 @@ class NodeBuilder: self.last_is_data = 0 def endtag(self, tag ): """XML Parser callback. Used internally""" - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s" % (self.__depth, tag), 'up') + log.info("DEPTH -> %i , tag -> %s" % (self.__depth, tag)) self.check_data_buffer() if self.__depth == self._dispatch_depth: self.dispatch(self._mini_dom) elif self.__depth > self._dispatch_depth: self._ptr = self._ptr.parent else: - self.DEBUG(DBG_NODEBUILDER, "Got higher than dispatch level. Stream terminated?", 'stop') + log.info("Got higher than dispatch level. Stream terminated?") self._dec_depth() self.last_is_data = 0 if self.__depth == 0: self.stream_footer_received() @@ -385,8 +387,7 @@ class NodeBuilder: self.check_data_buffer() if prefix: self.namespaces[uri]=prefix+':' else: self.xmlns=uri - def DEBUG(self, level, text, comment=None): - """ Gets all NodeBuilder walking events. Can be used for debugging if redefined.""" + def getDom(self): """ Returns just built Node. """ self.check_data_buffer() diff --git a/src/common/xmpp/tls_nb.py b/src/common/xmpp/tls_nb.py index e0b975de9..148a8aac3 100644 --- a/src/common/xmpp/tls_nb.py +++ b/src/common/xmpp/tls_nb.py @@ -26,16 +26,8 @@ import time import traceback import logging - log = logging.getLogger('gajim.c.x.tls_nb') -consoleloghandler = logging.StreamHandler() -consoleloghandler.setLevel(logging.DEBUG) -consoleloghandler.setFormatter( - logging.Formatter('%(levelname)s: %(message)s') -) -log.setLevel(logging.DEBUG) -log.addHandler(consoleloghandler) -log.propagate = False + # I don't need to load gajim.py just because of few TLS variables, so I changed # %s/common\.gajim\.DATA_DIR/\'\.\.\/data\'/c # %s/common\.gajim\.MY_CACERTS/\'\%s\/\.gajim\/cacerts\.pem\' \% os\.environ\[\'HOME\'\]/c @@ -49,6 +41,11 @@ log.propagate = False USE_PYOPENSSL = False + +#TODO: add callback set from PlugIn for errors during runtime +# - sth like on_disconnect in socket wrappers + + try: #raise ImportError("Manually disabled PyOpenSSL") import OpenSSL.SSL @@ -164,8 +161,8 @@ class PyOpenSSLWrapper(SSLWrapper): if flags is None: retval = self.sslobj.recv(bufsize) else: retval = self.sslobj.recv(bufsize, flags) except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: + log.debug("Recv: Want-error: " + repr(e)) pass - # log.debug("Recv: " + repr(e)) except OpenSSL.SSL.SysCallError, e: log.debug("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True) #traceback.print_exc() @@ -253,7 +250,6 @@ class NonBlockingTLS(PlugIn): if owner.__dict__.has_key('NonBlockingTLS'): return # Already enabled. PlugIn.PlugIn(self, owner) - DBG_LINE='NonBlockingTLS' self.on_tls_success = on_tls_success self.on_tls_faliure = on_tls_failure if now: @@ -288,10 +284,10 @@ class NonBlockingTLS(PlugIn): ''' Used to analyse server tag for TLS support. If TLS is supported starts the encryption negotiation. Used internally ''' if not feats.getTag('starttls', namespace=NS_TLS): - self.DEBUG("TLS unsupported by remote server.", 'warn') + log.warn("TLS unsupported by remote server.") self.on_tls_failure("TLS unsupported by remote server.") return - self.DEBUG("TLS supported by remote server. Requesting TLS start.", 'ok') + log.debug("TLS supported by remote server. Requesting TLS start.") self._owner.RegisterHandlerOnce('proceed', self.StartTLSHandler, xmlns=NS_TLS) self._owner.RegisterHandlerOnce('failure', self.StartTLSHandler, xmlns=NS_TLS) self._owner.send('' % NS_TLS) @@ -425,7 +421,7 @@ class NonBlockingTLS(PlugIn): if self.starttls == 'failure': self.on_tls_failure('TLS received: %s' % self.starttls) return - self.DEBUG('Got starttls proceed response. Switching to TLS/SSL...','ok') + log.debug('Got starttls proceed response. Switching to TLS/SSL...') try: self._startSSL() except Exception, e: diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py index c69f33be8..613dbd693 100644 --- a/src/common/xmpp/transports_nb.py +++ b/src/common/xmpp/transports_nb.py @@ -30,17 +30,9 @@ import traceback import logging log = logging.getLogger('gajim.c.x.transports_nb') -consoleloghandler = logging.StreamHandler() -consoleloghandler.setLevel(logging.DEBUG) -consoleloghandler.setFormatter( - logging.Formatter('%(levelname)s: %(message)s') -) -log.setLevel(logging.DEBUG) -log.addHandler(consoleloghandler) -log.propagate = False -def urisplit(self, uri): +def urisplit(uri): ''' Function for splitting URI string to tuple (protocol, host, path). e.g. urisplit('http://httpcm.jabber.org/webclient') returns @@ -72,6 +64,23 @@ CONNECTING ='CONNECTING' CONNECTED ='CONNECTED' DISCONNECTING ='DISCONNECTING' + +class NonBlockingTransport(PlugIn): + def __init__(self, on_disconnect): + PlugIn.__init__(self) + self.on_disconnect = on_disconnect + + def plugin(self, owner): + owner.Connection=self + self.idlequeue = owner.idlequeue + + + def plugout(self): + self._owner.Connection = None + self._owner = None + + + class NonBlockingTcp(PlugIn, IdleObject): ''' Non-blocking TCP socket wrapper @@ -114,7 +123,6 @@ class NonBlockingTcp(PlugIn, IdleObject): self.set_timeout, self.remove_timeout] def plugin(self, owner): - print 'plugin called' owner.Connection=self self.idlequeue = owner.idlequeue @@ -142,7 +150,7 @@ class NonBlockingTcp(PlugIn, IdleObject): self.on_connect = on_connect self.on_connect_failure = on_connect_failure (self.server, self.port) = conn_5tuple[4] - log.debug('NonBlocking Connect :: About tot connect to %s:%s' % conn_5tuple[4]) + log.info('NonBlocking Connect :: About tot connect to %s:%s' % conn_5tuple[4]) try: self._sock = socket.socket(*conn_5tuple[:3]) except socket.error, (errnum, errstr): @@ -170,14 +178,14 @@ class NonBlockingTcp(PlugIn, IdleObject): if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): # connecting in progress self.set_state(CONNECTING) - log.debug('After connect. "%s" raised => CONNECTING' % errstr) + log.info('After connect. "%s" raised => CONNECTING' % errstr) # on_connect/failure will be called from self.pollin/self.pollout return elif errnum in (0, 10056, errno.EISCONN): # already connected - this branch is very unlikely, nonblocking connect() will # return EINPROGRESS exception in most cases. When here, we don't need timeout # on connected descriptor and success callback can be called. - log.debug('After connect. "%s" raised => CONNECTED' % errstr) + log.info('After connect. "%s" raised => CONNECTED' % errstr) self._on_connect(self) return @@ -212,12 +220,12 @@ class NonBlockingTcp(PlugIn, IdleObject): def pollin(self): '''called when receive on plugged socket is possible ''' - log.debug('pollin called, state == %s' % self.state) + log.info('pollin called, state == %s' % self.state) self._do_receive() def pollout(self): '''called when send to plugged socket is possible''' - log.debug('pollout called, state == %s' % self.state) + log.info('pollout called, state == %s' % self.state) if self.state==CONNECTING: self._on_connect(self) @@ -225,7 +233,7 @@ class NonBlockingTcp(PlugIn, IdleObject): self._do_send() def pollend(self): - log.debug('pollend called, state == %s' % self.state) + log.info('pollend called, state == %s' % self.state) if self.state==CONNECTING: self._on_connect_failure('Error during connect to %s:%s' % @@ -251,7 +259,7 @@ class NonBlockingTcp(PlugIn, IdleObject): ''' Implemntation of IdleObject function called on timeouts from IdleQueue. ''' - log.debug('read_timeout called, state == %s' % self.state) + log.warn('read_timeout called, state == %s' % self.state) if self.state==CONNECTING: # if read_timeout is called during connecting, connect() didn't end yet # thus we have to call the tcp failure callback @@ -309,17 +317,18 @@ class NonBlockingTcp(PlugIn, IdleObject): def _plug_idle(self): # readable if socket is connected or disconnecting readable = self.state != DISCONNECTED + fd = self.get_fd() # writeable if sth to send if self.sendqueue or self.sendbuff: writable = True else: writable = False - print 'About to plug fd %d, W:%s, R:%s' % (self.get_fd(), writable, readable) + log.debug('About to plug fd %d, W:%s, R:%s' % (fd, writable, readable)) if self.writable != writable or self.readable != readable: - print 'Really plugging fd %d, W:%s, R:%s' % (self.get_fd(), writable, readable) + log.debug('Really plugging fd %d, W:%s, R:%s' % (fd, writable, readable)) self.idlequeue.plug_idle(self, writable, readable) else: - print 'Not plugging - is already plugged' + log.debug('Not plugging fd %s because it\'s already plugged' % fd) @@ -343,7 +352,7 @@ class NonBlockingTcp(PlugIn, IdleObject): def _raise_event(self, event_type, data): if data and data.strip(): - log.debug('raising event from transport: %s %s' % (event_type,data)) + log.info('raising event from transport: %s %s' % (event_type,data)) if hasattr(self._owner, 'Dispatcher'): self._owner.Dispatcher.Event('', event_type, data) @@ -356,7 +365,7 @@ class NonBlockingTcp(PlugIn, IdleObject): else: self.on_receive = None return - log.debug('setting onreceive on %s' % recv_handler) + log.info('setting onreceive on %s' % recv_handler) self.on_receive = recv_handler @@ -372,7 +381,7 @@ class NonBlockingTcp(PlugIn, IdleObject): received = self._recv(RECV_BUFSIZE) except (socket.error, socket.herror, socket.gaierror), (errnum, errstr): # save exception number and message to errnum, errstr - log.debug("_do_receive: got %s:" % received , exc_info=True) + log.info("_do_receive: got %s:" % received , exc_info=True) if received == '': errnum = ERR_DISCONN @@ -412,10 +421,76 @@ class NonBlockingTcp(PlugIn, IdleObject): def _on_receive(self, data): # Overriding this method allows modifying received data before it is passed # to owner's callback. - log.debug('About to call on_receive which is %s' % self.on_receive) + log.info('About to call on_receive which is %s' % self.on_receive) self.on_receive(data) +class NonBlockingHTTP(NonBlockingTcp): + ''' + Socket wrapper that cretes HTTP message out of sent data and peels-off + HTTP headers from incoming messages + ''' + + def __init__(self, http_uri, http_port, on_disconnect): + self.http_protocol, self.http_host, self.http_path = urisplit(http_uri) + if self.http_protocol is None: + self.http_protocol = 'http' + if self.http_path == '': + http_path = '/' + self.http_port = http_port + NonBlockingTcp.__init__(self, on_disconnect) + + def send(self, raw_data, now=False): + + NonBlockingTcp.send( + self, + self.build_http_message(raw_data), + now) + + def _on_receive(self,data): + '''Preceeds passing received data to Client class. Gets rid of HTTP headers + and checks them.''' + statusline, headers, httpbody = self.parse_http_message(data) + if statusline[1] != '200': + log.error('HTTP Error: %s %s' % (statusline[1], statusline[2])) + self.disconnect() + return + self.on_receive(httpbody) + + + def build_http_message(self, httpbody): + ''' + Builds http message with given body. + Values for headers and status line fields are taken from class variables. + ) + ''' + headers = ['POST %s HTTP/1.1' % self.http_path, + 'Host: %s:%s' % (self.http_host, self.http_port), + 'Content-Type: text/xml; charset=utf-8', + 'Content-Length: %s' % len(str(httpbody)), + '\r\n'] + headers = '\r\n'.join(headers) + return('%s%s\r\n' % (headers, httpbody)) + + def parse_http_message(self, message): + ''' + splits http message to tuple ( + statusline - list of e.g. ['HTTP/1.1', '200', 'OK'], + headers - dictionary of headers e.g. {'Content-Length': '604', + 'Content-Type': 'text/xml; charset=utf-8'}, + httpbody - string with http body + ) + ''' + message = message.replace('\r','') + (header, httpbody) = message.split('\n\n') + header = header.split('\n') + statusline = header[0].split(' ') + header = header[1:] + headers = {} + for dummy in header: + row = dummy.split(' ',1) + headers[row[0][:-1]] = row[1] + return (statusline, headers, httpbody) @@ -460,7 +535,7 @@ class NBHTTPProxySocket(NBProxySocket): ''' Starts connection. Connects to proxy, supplies login and password to it (if were specified while creating instance). Instructs proxy to make connection to the target server. Returns non-empty sting on success. ''' - log.debug('Proxy server contacted, performing authentification') + log.info('Proxy server contacted, performing authentification') connector = ['CONNECT %s:%s HTTP/1.0' % self.xmpp_server, 'Proxy-Connection: Keep-Alive', 'Pragma: no-cache', @@ -504,7 +579,7 @@ class NBHTTPProxySocket(NBProxySocket): self.reply += reply.replace('\r', '') self._on_connect_failure('Proxy authentification failed') return - log.debug('Authentification successfull. Jabber server contacted.') + log.info('Authentification successfull. Jabber server contacted.') self._on_connect(self) @@ -517,7 +592,7 @@ class NBSOCKS5ProxySocket(NBProxySocket): # _on_connect_failure, at the end call _on_connect() def _on_tcp_connect(self): - self.DEBUG('Proxy server contacted, performing authentification', 'start') + log.info('Proxy server contacted, performing authentification') if self.proxy.has_key('user') and self.proxy.has_key('password'): to_send = '\x05\x02\x00\x02' else: @@ -532,7 +607,7 @@ class NBSOCKS5ProxySocket(NBProxySocket): self.on_proxy_failure('Invalid proxy reply') return if reply[0] != '\x05': - self.DEBUG('Invalid proxy reply', 'error') + log.info('Invalid proxy reply') self._owner.disconnected() self.on_proxy_failure('Invalid proxy reply') return @@ -648,67 +723,3 @@ class NBSOCKS5ProxySocket(NBProxySocket): -class NonBlockingHttpBOSH(NonBlockingTcp): - ''' - Socket wrapper that makes HTTP message out of send data and peels-off - HTTP headers from incoming messages - ''' - - def __init__(self, bosh_uri, bosh_port, on_disconnect): - self.bosh_protocol, self.bosh_host, self.bosh_path = self.urisplit(bosh_uri) - if self.bosh_protocol is None: - self.bosh_protocol = 'http' - if self.bosh_path == '': - bosh_path = '/' - self.bosh_port = bosh_port - - def send(self, raw_data, now=False): - - NonBlockingTcp.send( - self, - self.build_http_message(raw_data), - now) - - def _on_receive(self,data): - '''Preceeds passing received data to Client class. Gets rid of HTTP headers - and checks them.''' - statusline, headers, httpbody = self.parse_http_message(data) - if statusline[1] != '200': - log.error('HTTP Error: %s %s' % (statusline[1], statusline[2])) - self.disconnect() - self.on_receive(httpbody) - - - def build_http_message(self, httpbody): - ''' - Builds bosh http message with given body. - Values for headers and status line fields are taken from class variables. - ) - ''' - headers = ['POST %s HTTP/1.1' % self.bosh_path, - 'Host: %s:%s' % (self.bosh_host, self.bosh_port), - 'Content-Type: text/xml; charset=utf-8', - 'Content-Length: %s' % len(str(httpbody)), - '\r\n'] - headers = '\r\n'.join(headers) - return('%s%s\r\n' % (headers, httpbody)) - - def parse_http_message(self, message): - ''' - splits http message to tuple ( - statusline - list of e.g. ['HTTP/1.1', '200', 'OK'], - headers - dictionary of headers e.g. {'Content-Length': '604', - 'Content-Type': 'text/xml; charset=utf-8'}, - httpbody - string with http body - ) - ''' - message = message.replace('\r','') - (header, httpbody) = message.split('\n\n') - header = header.split('\n') - statusline = header[0].split(' ') - header = header[1:] - headers = {} - for dummy in header: - row = dummy.split(' ',1) - headers[row[0][:-1]] = row[1] - return (statusline, headers, httpbody) diff --git a/src/gajim.py b/src/gajim.py index 47d90a8d2..50b2d9f7e 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -50,7 +50,7 @@ import logging consoleloghandler = logging.StreamHandler() consoleloghandler.setLevel(1) consoleloghandler.setFormatter( -logging.Formatter('%(asctime)s %(name)s: %(levelname)s: %(message)s')) +logging.Formatter('%(name)s: %(levelname)s: %(message)s')) log = logging.getLogger('gajim') log.setLevel(logging.WARNING) log.addHandler(consoleloghandler)