diff --git a/src/common/xmpp/auth_nb.py b/src/common/xmpp/auth_nb.py index a05f82481..d8c045e0d 100644 --- a/src/common/xmpp/auth_nb.py +++ b/src/common/xmpp/auth_nb.py @@ -173,11 +173,8 @@ class SASL(PlugIn): self.startsasl='success' log.info('Successfully authenticated with remote server.') handlers=self._owner.Dispatcher.dumpHandlers() - print '6' * 79 - print handlers - print '6' * 79 self._owner.Dispatcher.PlugOut() - dispatcher_nb.Dispatcher().PlugIn(self._owner) + dispatcher_nb.Dispatcher().PlugIn(self._owner, after_SASL=True) self._owner.Dispatcher.restoreHandlers(handlers) self._owner.User = self.username if self.on_sasl : diff --git a/src/common/xmpp/bosh.py b/src/common/xmpp/bosh.py index e41322973..851d7cc4c 100644 --- a/src/common/xmpp/bosh.py +++ b/src/common/xmpp/bosh.py @@ -1,80 +1,208 @@ -import protocol, simplexml, locale, random, dispatcher_nb +import protocol, locale, random, dispatcher_nb from client_nb import NBCommonClient +import transports_nb import logging +from simplexml import Node log = logging.getLogger('gajim.c.x.bosh') class BOSHClient(NBCommonClient): ''' - Client class implementing BOSH. + Client class implementing BOSH. Extends common XMPP ''' - def __init__(self, *args, **kw): + def __init__(self, domain, idlequeue, caller=None): '''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) + self.bosh_sid = None + + if locale.getdefaultlocale()[0]: + self.bosh_xml_lang = locale.getdefaultlocale()[0].split('_')[0] + else: + self.bosh_xml_lang = 'en' + + self.http_version = 'HTTP/1.1' + self.bosh_to = domain + + #self.Namespace = protocol.NS_HTTP_BIND + #self.defaultNamespace = self.Namespace + self.bosh_session_on = False + + NBCommonClient.__init__(self, domain, idlequeue, caller) + + + + def connect(self, on_connect, on_connect_failure, proxy, hostname=None, port=5222, + on_proxy_failure=None, secure=None): + ''' + Open XMPP connection (open XML 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 + proxy server or during connection to the proxy + :param proxy: dictionary with bosh-related paramters. It should contain at + least values for keys 'host' and 'port' - connection details for proxy + server and optionally keys 'user' and 'pass' as proxy credentials + :param secure: if + ''' + NBCommonClient.connect(self, on_connect, on_connect_failure, hostname, port, + on_proxy_failure, proxy, secure) + + if hostname: + self.route_host = hostname + else: + self.route_host = self.Server + + assert(proxy.has_key('type')) + assert(proxy['type']=='bosh') - 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) + self.bosh_host = proxy['host'] + self.bosh_port = proxy['port'] + self.bosh_content = proxy['bosh_content'] + + # _on_tcp_failure is callback for errors which occur during name resolving or + # TCP connecting. + self._on_tcp_failure = self.on_proxy_failure + + + + # in BOSH, client connects to Connection Manager instead of directly to + # XMPP server ((hostname, port)). If HTTP Proxy is specified, client connects + # to HTTP proxy and Connection Manager is specified at URI and Host header + # in HTTP message + + # tcp_host, tcp_port is hostname and port for socket connection - Connection + # Manager or HTTP proxy + if proxy.has_key('proxy_host') and proxy['proxy_host'] and \ + proxy.has_key('proxy_port') and proxy['proxy_port']: + + tcp_host=proxy['proxy_host'] + tcp_port=proxy['proxy_port'] + + # user and password for HTTP proxy + if proxy.has_key('user') and proxy['user'] and \ + proxy.has_key('pass') and proxy['pass']: + + proxy_creds=(proxy['user'],proxy['pass']) + else: + proxy_creds=(None, None) + + else: + tcp_host = transports_nb.urisplit(proxy['host'])[1] + tcp_port=proxy['port'] + + if tcp_host is None: + self._on_connect_failure("Invalid BOSH URI") + return + + self.socket = self.get_socket() + + self._resolve_hostname( + hostname=tcp_host, + port=tcp_port, + on_success=self._try_next_ip, + on_failure=self._on_tcp_failure) + + def _on_stream_start(self): + ''' + Called after XMPP stream is opened. In BOSH, TLS is negotiated on socket + connect so success callback can be invoked after TCP connect. + (authentication is started from auth() method) + ''' + self.onreceive(None) + if self.connected == 'tcp': + self._on_connect() + + def get_socket(self): + tmp = transports_nb.NonBlockingHTTP( + raise_event=self.raise_event, + on_disconnect=self.on_http_disconnect, + http_uri = self.bosh_host, + http_port = self.bosh_port, + http_version = self.http_version + ) + tmp.PlugIn(self) + return tmp + + def on_http_disconnect(self): + log.info('HTTP socket disconnected') + #import traceback + #traceback.print_stack() + if self.bosh_session_on: + self.socket.connect( + conn_5tuple=self.current_ip, + on_connect=self.on_http_reconnect, + on_connect_failure=self.on_disconnect) + else: + self.on_disconnect() + + def on_http_reconnect(self): + self.socket._plug_idle() + log.info('Connected to BOSH CM again') + pass + + + def on_http_reconnect_fail(self): + log.error('Error when reconnecting to BOSH CM') + self.on_disconnect() def send(self, stanza, now = False): (id, stanza_to_send) = self.Dispatcher.assign_id(stanza) - self.Connection.send( + self.socket.send( self.boshify_stanza(stanza_to_send), now = now) return id + def get_rid(self): + # does this need a lock??" + self.bosh_rid = self.bosh_rid + 1 + return str(self.bosh_rid) + 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 + assert(hasattr(self, 'bosh_sid')) return protocol.BOSHBody( - attrs={ 'rid': str(self.bosh_rid), + attrs={ 'rid': self.get_rid(), 'sid': self.bosh_sid}) - - def get_initial_bodytag(self): - return protocol.BOSHBody( - attrs={'content': 'text/xml; charset=utf-8', + def get_initial_bodytag(self, after_SASL=False): + tag = protocol.BOSHBody( + attrs={'content': self.bosh_content, 'hold': str(self.bosh_hold), + 'route': '%s:%s' % (self.route_host, self.Port), 'to': self.bosh_to, 'wait': str(self.bosh_wait), - 'rid': str(self.bosh_rid), + 'rid': self.get_rid(), + 'xml:lang': self.bosh_xml_lang, 'xmpp:version': '1.0', - 'xmlns:xmpp': 'urn:xmpp:xbosh'} - ) + 'ver': '1.6', + 'xmlns:xmpp': 'urn:xmpp:xbosh'}) + if after_SASL: + tag.delAttr('content') + tag.delAttr('hold') + tag.delAttr('route') + tag.delAttr('wait') + tag.delAttr('ver') + # xmpp:restart attribute is essential for stream restart request + tag.setAttr('xmpp:restart','true') + tag.setAttr('sid',self.bosh_sid) + + return tag + def get_closing_bodytag(self): closing_bodytag = self.get_bodytag() @@ -82,31 +210,26 @@ class BOSHClient(NBCommonClient): 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 boshify_stanza(self, stanza=None, body_attrs=None): + ''' wraps stanza by body tag with rid and sid ''' + #log.info('boshify_staza - type is: %s, stanza is %s' % (type(stanza), stanza)) + tag = self.get_bodytag() + tag.setPayload([stanza]) + return tag + def on_bodytag_attrs(self, body_attrs): + #log.info('on_bodytag_attrs: %s' % body_attrs) + if body_attrs.has_key('type'): + if body_attrs['type']=='terminated': + # BOSH session terminated + self.bosh_session_on = False + elif body_attrs['type']=='error': + # recoverable error + pass + if not self.bosh_sid: + # initial response - when bosh_sid is set + self.bosh_session_on = True + self.bosh_sid = body_attrs['sid'] + self.Dispatcher.Stream._document_attrs['id']=body_attrs['authid'] - 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 c30905963..f9f200dac 100644 --- a/src/common/xmpp/client.py +++ b/src/common/xmpp/client.py @@ -32,7 +32,7 @@ class PlugIn: def PlugIn(self,owner): """ Attach to main instance and register ourself and all our staff in it. """ self._owner=owner - log.debug('Plugging %s into %s'%(self,self._owner)) + log.info('Plugging %s __INTO__ %s' % (self,self._owner)) if owner.__dict__.has_key(self.__class__.__name__): log.debug('Plugging ignored: another instance already plugged.') return @@ -41,17 +41,27 @@ class PlugIn: if owner.__dict__.has_key(method.__name__): self._old_owners_methods.append(owner.__dict__[method.__name__]) owner.__dict__[method.__name__]=method - owner.__dict__[self.__class__.__name__]=self + if self.__class__.__name__.endswith('Dispatcher'): + # FIXME: I need BOSHDispatcher or XMPPDispatcher on .Dispatcher + # there must be a better way.. + owner.__dict__['Dispatcher']=self + else: + owner.__dict__[self.__class__.__name__]=self + # following will not work for classes inheriting plugin() #if self.__class__.__dict__.has_key('plugin'): return self.plugin(owner) if hasattr(self,'plugin'): return self.plugin(owner) def PlugOut(self): """ Unregister all our staff from main instance and detach from it. """ - log.debug('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._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() + if self.__class__.__name__.endswith('Dispatcher'): + del self._owner.__dict__['Dispatcher'] + else: + del self._owner.__dict__[self.__class__.__name__] + #if self.__class__.__dict__.has_key('plugout'): return self.plugout() + if hasattr(self,'plugout'): return self.plugout() del self._owner diff --git a/src/common/xmpp/client_nb.py b/src/common/xmpp/client_nb.py index df6c4ca20..c21437de5 100644 --- a/src/common/xmpp/client_nb.py +++ b/src/common/xmpp/client_nb.py @@ -41,11 +41,10 @@ class NBCommonClient: :param caller: calling object - it has to implement certain methods (necessary?) ''' - self.Namespace = protocol.NS_CLIENT - - self.idlequeue = idlequeue self.defaultNamespace = self.Namespace + + self.idlequeue = idlequeue self.disconnect_handlers = [] self.Server = domain @@ -85,12 +84,14 @@ class NBCommonClient: self.SASL.PlugOut() if self.__dict__.has_key('NonBlockingTLS'): self.NonBlockingTLS.PlugOut() - if self.__dict__.has_key('NBHTTPPROXYsocket'): + if self.__dict__.has_key('NBHTTPProxySocket'): self.NBHTTPPROXYsocket.PlugOut() - if self.__dict__.has_key('NBSOCKS5PROXYsocket'): + if self.__dict__.has_key('NBSOCKS5ProxySocket'): self.NBSOCKS5PROXYsocket.PlugOut() - if self.__dict__.has_key('NonBlockingTcp'): - self.NonBlockingTcp.PlugOut() + if self.__dict__.has_key('NonBlockingTCP'): + self.NonBlockingTCP.PlugOut() + if self.__dict__.has_key('NonBlockingHTTP'): + self.NonBlockingHTTP.PlugOut() def send(self, stanza, now = False): @@ -106,7 +107,7 @@ class NBCommonClient: 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). + Open XMPP connection (open XML 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 @@ -118,70 +119,14 @@ 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 self._secure = secure self.Connection = None + self.Port = port - if proxy: - # with proxies, client connects to proxy instead of directly to - # 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 - if proxy.has_key('type'): - if proxy.has_key('user') and proxy.has_key('pass'): - proxy_creds=(proxy['user'],proxy['pass']) - else: - proxy_creds=(None, None) - - type_ = proxy['type'] - if type_ == 'socks5': - # SOCKS5 proxy - self.socket = transports_nb.NBSOCKS5ProxySocket( - on_disconnect=self.on_disconnect, - proxy_creds=proxy_creds, - 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=(xmpp_hostname, self.Port)) - elif type_ == 'bosh': - # BOSH - XMPP over HTTP - tcp_server = transports_nb.urisplit(tcp_server)[1] - self.socket = transports_nb.NonBlockingHTTP( - on_disconnect=self.on_disconnect, - 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=(xmpp_hostname, self.Port)) - else: - self._on_tcp_failure = self._on_connect_failure - tcp_server=xmpp_hostname - tcp_port=self.Port - self.socket = transports_nb.NonBlockingTcp(on_disconnect = self.on_disconnect) - self.socket.PlugIn(self) - - self._resolve_hostname( - hostname=tcp_server, - port=tcp_port, - on_success=self._try_next_ip, - on_failure=self._on_tcp_failure) @@ -232,13 +177,14 @@ class NBCommonClient: started, and _on_connect_failure on failure. ''' #FIXME: use RegisterHandlerOnce instead of onreceive - log.info('=============xmpp_connect_machine() >> mode: %s, data: %s' % (mode,data)) + log.info('========xmpp_connect_machine() >> mode: %s, data: %s' % (mode,str(data)[:20] )) def on_next_receive(mode): + log.info('setting %s on next receive' % mode) if mode is None: self.onreceive(None) else: - self.onreceive(lambda data:self._xmpp_connect_machine(mode, data)) + self.onreceive(lambda _data:self._xmpp_connect_machine(mode, _data)) if not mode: dispatcher_nb.Dispatcher().PlugIn(self) @@ -259,9 +205,11 @@ class NBCommonClient: if not self.Dispatcher.Stream.features: on_next_receive('RECEIVE_STREAM_FEATURES') else: + log.info('got STREAM FEATURES in first read') self._xmpp_connect_machine(mode='STREAM_STARTED') else: + log.info('incoming stream version less than 1.0') self._xmpp_connect_machine(mode='STREAM_STARTED') elif mode == 'RECEIVE_STREAM_FEATURES': @@ -274,6 +222,7 @@ class NBCommonClient: mode='FAILURE', data='Missing in 1.0 stream') else: + log.info('got STREAM FEATURES in second read') self._xmpp_connect_machine(mode='STREAM_STARTED') elif mode == 'STREAM_STARTED': @@ -294,6 +243,10 @@ class NBCommonClient: self.onreceive(None) self.on_connect(self, self.connected) + def raise_event(self, event_type, data): + log.info('raising event from transport: %s %s' % (event_type,data)) + if hasattr(self, 'Dispatcher'): + self.Dispatcher.Event('', event_type, data) # moved from client.CommonClient: @@ -324,8 +277,6 @@ class NBCommonClient: def auth(self, user, password, resource = '', sasl = 1, on_auth = None): - - print 'auth called' ''' Authenticate connnection and bind resource. If resource is not provided random one or library name used. ''' self._User, self._Password, self._Resource, self._sasl = user, password, resource, sasl @@ -388,39 +339,6 @@ class NBCommonClient: self.on_auth(self, None) - - -class NonBlockingClient(NBCommonClient): - ''' Example client class, based on CommonClient. ''' - - - def _on_stream_start(self): - ''' - Called after XMPP stream is opened. - In pure XMPP client, TLS negotiation may follow after esabilishing a stream. - ''' - self.onreceive(None) - if self.connected == 'tcp': - if not self.connected or self._secure is not None and not self._secure: - # if we are disconnected or TLS/SSL is not desired, return - self._on_connect() - return - if not self.Dispatcher.Stream.features.getTag('starttls'): - # if server doesn't advertise TLS in init response - self._on_connect() - return - if self.incoming_stream_version() != '1.0': - self._on_connect() - return - # otherwise start TLS - tls_nb.NonBlockingTLS().PlugIn( - self, - on_tls_success=lambda: self._xmpp_connect(socket_type='tls'), - on_tls_failure=self._on_connect_failure) - elif self.connected == 'tls': - self._on_connect() - - def initRoster(self): ''' Plug in the roster. ''' if not self.__dict__.has_key('NonBlockingRoster'): @@ -440,3 +358,101 @@ class NonBlockingClient(NBCommonClient): self.send(dispatcher_nb.Presence(to=jid, typ=typ)) + +class NonBlockingClient(NBCommonClient): + ''' Example client class, based on CommonClient. ''' + + def __init__(self, domain, idlequeue, caller=None): + NBCommonClient.__init__(self, domain, idlequeue, caller) + + def connect(self, on_connect, on_connect_failure, hostname=None, port=5222, + on_proxy_failure=None, proxy=None, secure=None): + + NBCommonClient.connect(self, on_connect, on_connect_failure, hostname, port, + on_proxy_failure, proxy, secure) + + if hostname: + xmpp_hostname = hostname + else: + xmpp_hostname = self.Server + + if proxy: + # with proxies, client connects to proxy instead of directly to + # XMPP server ((hostname, port)) + # tcp_host is machine used for socket connection + tcp_host=proxy['host'] + tcp_port=proxy['port'] + self._on_tcp_failure = self.on_proxy_failure + if proxy.has_key('type'): + assert(proxy['type']!='bosh') + if proxy.has_key('user') and proxy.has_key('pass'): + proxy_creds=(proxy['user'],proxy['pass']) + else: + proxy_creds=(None, None) + + type_ = proxy['type'] + if type_ == 'socks5': + # SOCKS5 proxy + self.socket = transports_nb.NBSOCKS5ProxySocket( + on_disconnect=self.on_disconnect, + proxy_creds=proxy_creds, + 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=(xmpp_hostname, self.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=(xmpp_hostname, self.Port)) + else: + self._on_tcp_failure = self._on_connect_failure + tcp_host=xmpp_hostname + tcp_port=self.Port + self.socket = transports_nb.NonBlockingTCP( + raise_event = self.raise_event, + on_disconnect = self.on_disconnect) + + self.socket.PlugIn(self) + + self._resolve_hostname( + hostname=tcp_host, + port=tcp_port, + on_success=self._try_next_ip, + on_failure=self._on_tcp_failure) + + + + + def _on_stream_start(self): + ''' + Called after XMPP stream is opened. + In pure XMPP client, TLS negotiation may follow after esabilishing a stream. + ''' + self.onreceive(None) + if self.connected == 'tcp': + if not self.connected or not self._secure: + # if we are disconnected or TLS/SSL is not desired, return + self._on_connect() + return + if not self.Dispatcher.Stream.features.getTag('starttls'): + # if server doesn't advertise TLS in init response + self._on_connect() + return + if self.incoming_stream_version() != '1.0': + self._on_connect() + return + # otherwise start TLS + tls_nb.NonBlockingTLS().PlugIn( + self, + on_tls_success=lambda: self._xmpp_connect(socket_type='tls'), + on_tls_failure=self._on_connect_failure) + elif self.connected == 'tls': + self._on_connect() + + + diff --git a/src/common/xmpp/dispatcher_nb.py b/src/common/xmpp/dispatcher_nb.py index 3734d16a9..1c6f04a8b 100644 --- a/src/common/xmpp/dispatcher_nb.py +++ b/src/common/xmpp/dispatcher_nb.py @@ -14,7 +14,6 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. -# $Id: dispatcher.py,v 1.40 2006/01/18 19:26:43 normanr Exp $ ''' Main xmpppy mechanism. Provides library with methods to assign different handlers @@ -30,6 +29,7 @@ from client import PlugIn import logging log = logging.getLogger('gajim.c.x.dispatcher_nb') +log.setLevel(logging.INFO) # default timeout to wait for response for our id DEFAULT_TIMEOUT_SECONDS = 25 @@ -38,9 +38,33 @@ ID = 0 STREAM_TERMINATOR = '' XML_DECLARATION = '' -class Dispatcher(PlugIn): + + + +# FIXME: ugly +from client_nb import NonBlockingClient +from bosh import BOSHClient +class Dispatcher(): +# Why is this here - I needed to redefine Dispatcher for BOSH and easiest way +# was to inherit original Dispatcher (now renamed to XMPPDispatcher). Trouble +# is that reference used to access dispatcher instance is in Client attribute +# named by __class__.__name__ of the dispatcher instance .. long story short: +# 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 + def PlugIn(self, client_obj, after_SASL=False): + if isinstance(client_obj, NonBlockingClient): + XMPPDispatcher().PlugIn(client_obj) + elif isinstance(client_obj, BOSHClient): + BOSHDispatcher().PlugIn(client_obj, after_SASL) + + + +class XMPPDispatcher(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) self.handlers={} @@ -84,8 +108,6 @@ class Dispatcher(PlugIn): def plugin(self, owner): ''' Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally.''' - log.debug('Dispatcher plugin') - self._init() self._owner.lastErrNode = None self._owner.lastErr = None @@ -106,8 +128,8 @@ class Dispatcher(PlugIn): def StreamInit(self): ''' Send an initial stream header. ''' self.Stream = simplexml.NodeBuilder() - self.Stream._dispatch_depth = 2 self.Stream.dispatch = self.dispatch + self.Stream._dispatch_depth = 2 self.Stream.stream_header_received = self._check_stream_start self.Stream.features = None self._metastream = Node('stream:stream') @@ -159,7 +181,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. ''' - log.info('Registering namespace "%s"' % xmlns) + log.debug('Registering namespace "%s"' % xmlns) self.handlers[xmlns]={} self.RegisterProtocol('unknown', Protocol, xmlns=xmlns) self.RegisterProtocol('default', Protocol, xmlns=xmlns) @@ -169,7 +191,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 - log.info('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns)) + log.debug('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): @@ -196,7 +218,7 @@ class Dispatcher(PlugIn): ''' if not xmlns: xmlns=self._owner.defaultNamespace - log.info('Registering handler %s for "%s" type->%s ns->%s(%s)' % + log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' % (handler, name, typ, ns, xmlns)) if not typ and not ns: typ='default' @@ -287,32 +309,18 @@ class Dispatcher(PlugIn): def dispatch(self, stanza, session=None, direct=0): ''' Main procedure that performs XMPP stanza recognition and calling apppropriate handlers for it. Called internally. ''' + #log.info('dispatch called: stanza = %s, session = %s, direct= %s' % (stanza, session, direct)) if not session: session = self session.Stream._mini_dom = None name = stanza.getName() - if not direct and self._owner._component: - if name == 'route': - if stanza.getAttr('error') is None: - if len(stanza.getChildren()) == 1: - stanza = stanza.getChildren()[0] - name=stanza.getName() - else: - for each in stanza.getChildren(): - self.dispatch(each,session,direct=1) - return - elif name == 'presence': - return - elif name in ('features','bind'): - pass - else: - raise UnsupportedStanzaType(name) - if name=='features': session.Stream.features=stanza xmlns=stanza.getNamespace() + + #log.info('in dispatch, getting ns for %s, and the ns is %s' % (stanza, xmlns)) if not self.handlers.has_key(xmlns): log.warn("Unknown namespace: " + xmlns) xmlns='unknown' @@ -330,7 +338,6 @@ class Dispatcher(PlugIn): stanza.props=stanza.getProperties() ID=stanza.getID() - 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: @@ -427,3 +434,56 @@ class Dispatcher(PlugIn): stanza.setParent(self._metastream) return (_ID, stanza) +class BOSHDispatcher(XMPPDispatcher): + + def PlugIn(self, owner, after_SASL=False): + self.after_SASL = after_SASL + XMPPDispatcher.PlugIn(self, owner) + + def StreamInit(self): + ''' Send an initial stream header. ''' + self.Stream = simplexml.NodeBuilder() + self.Stream.dispatch = self.dispatch + self.Stream._dispatch_depth = 2 + self.Stream.stream_header_received = self._check_stream_start + self.Stream.features = None + + self._metastream = Node('stream:stream') + self._metastream.setNamespace(self._owner.Namespace) + self._metastream.setAttr('version', '1.0') + self._metastream.setAttr('xmlns:stream', NS_STREAMS) + self._metastream.setAttr('to', self._owner.Server) + if locale.getdefaultlocale()[0]: + self._metastream.setAttr('xml:lang', + locale.getdefaultlocale()[0].split('_')[0]) + + self.restart = True + self._owner.Connection.send(self._owner.get_initial_bodytag(self.after_SASL)) + + + def StreamTerminate(self): + ''' Send a stream terminator. ''' + self._owner.Connection.send(self._owner.get_closing_bodytag()) + + def ProcessNonBlocking(self, data=None): + + if self.restart: + fromstream = self._metastream + fromstream.setAttr('from', fromstream.getAttr('to')) + fromstream.delAttr('to') + data = '%s%s>%s' % (XML_DECLARATION,str(fromstream)[:-2] ,data) + self.restart = False + + return XMPPDispatcher.ProcessNonBlocking(self, data) + + def dispatch(self, stanza, session=None, direct=0): + if stanza.getName()=='body' and stanza.getNamespace()==NS_HTTP_BIND: + self._owner.on_bodytag_attrs(stanza.getAttrs()) + #self._owner.send_empty_bodytag() + for child in stanza.getChildren(): + XMPPDispatcher.dispatch(self, child, session, direct) + else: + XMPPDispatcher.dispatch(self, stanza, session, direct) + + + diff --git a/src/common/xmpp/simplexml.py b/src/common/xmpp/simplexml.py index f7561269b..84d5165fa 100644 --- a/src/common/xmpp/simplexml.py +++ b/src/common/xmpp/simplexml.py @@ -20,7 +20,7 @@ I'm personally using it in many other separate projects. It is designed to be as import xml.parsers.expat import logging log = logging.getLogger('gajim.c.x.simplexml') - +#log.setLevel(logging.DEBUG) def XMLescape(txt): """Returns provided string with symbols & < > " replaced by their respective XML entities.""" @@ -99,7 +99,10 @@ class Node(object): for a in self.kids: if not fancy and (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt]) elif (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt].strip()) - s = s + a.__str__(fancy and fancy+1) + if isinstance(a, str) or isinstance(a, unicode): + s = s + a.__str__() + else: + s = s + a.__str__(fancy and fancy+1) cnt=cnt+1 if not fancy and (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt]) elif (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt].strip()) @@ -343,7 +346,7 @@ class NodeBuilder: attrs[self.namespaces[ns]+attr[sp+1:]]=attrs[attr] del attrs[attr] # self._inc_depth() - log.info("DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`)) + log.info("STARTTAG.. 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) diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py index 3ee90241d..a4e35656f 100644 --- a/src/common/xmpp/transports_nb.py +++ b/src/common/xmpp/transports_nb.py @@ -3,6 +3,7 @@ ## ## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov ## modified by Dimitur Kirov +## modified by Dimitur Kirov ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -65,10 +66,21 @@ CONNECTED ='CONNECTED' DISCONNECTING ='DISCONNECTING' + + class NonBlockingTransport(PlugIn): - def __init__(self, on_disconnect): + def __init__(self, raise_event, on_disconnect): PlugIn.__init__(self) + self.raise_event = raise_event self.on_disconnect = on_disconnect + self.on_connect = None + self.on_connect_failure = None + self.idlequeue = None + self.on_receive = None + self.server = None + self.port = None + self.state = DISCONNECTED + self._exported_methods=[self.disconnect, self.onreceive] def plugin(self, owner): owner.Connection=self @@ -79,30 +91,72 @@ class NonBlockingTransport(PlugIn): self._owner.Connection = None self._owner = None + def connect(self, conn_5tuple, on_connect, on_connect_failure): + self.on_connect = on_connect + self.on_connect_failure = on_connect_failure + (self.server, self.port) = conn_5tuple[4][:2] + log.info('NonBlocking Connect :: About tot connect to %s:%s' % (self.server, self.port)) -class NonBlockingTcp(PlugIn, IdleObject): + def set_state(self, newstate): + assert(newstate in [DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING]) + if (self.state, newstate) in [(CONNECTING, DISCONNECTING), (DISCONNECTED, DISCONNECTING)]: + log.info('strange move: %s -> %s' % (self.state, newstate)) + self.state = newstate + + def _on_connect(self, data): + ''' preceeds call of on_connect callback ''' + self.set_state(CONNECTED) + self.on_connect() + + def _on_connect_failure(self,err_message): + ''' preceeds call of on_connect_failure callback ''' + # In case of error while connecting we need to close socket + # but we don't want to call DisconnectHandlers from client, + # thus the do_callback=False + self.disconnect(do_callback=False) + self.on_connect_failure(err_message=err_message) + + def send(self, raw_data, now=False): + if self.state not in [CONNECTED, DISCONNECTING]: + # FIXME better handling needed + log.error('Trying to send %s when transport is %s.' % + (raw_data, self.state)) + return + + def disconnect(self, do_callback=True): + self.set_state(DISCONNECTED) + if do_callback: + # invoke callback given in __init__ + self.on_disconnect() + + def onreceive(self, recv_handler): + ''' Sets the on_receive callback. Do not confuse it with + on_receive() method, which is the callback itself.''' + if not recv_handler: + if hasattr(self._owner, 'Dispatcher'): + self.on_receive = self._owner.Dispatcher.ProcessNonBlocking + else: + self.on_receive = None + return + log.info('setting onreceive on %s' % recv_handler) + self.on_receive = recv_handler + + def tcp_connection_started(self): + self.set_state(CONNECTING) + # on_connect/on_conn_failure will be called from self.pollin/self.pollout + + + +class NonBlockingTCP(NonBlockingTransport, IdleObject): ''' Non-blocking TCP socket wrapper ''' - def __init__(self, on_disconnect): + def __init__(self, raise_event, on_disconnect): ''' Class constructor. ''' - - PlugIn.__init__(self) - IdleObject.__init__(self) - - self.on_disconnect = on_disconnect - - self.on_connect = None - self.on_connect_failure = None - self.sock = None - self.idlequeue = None - self.on_receive = None - self.DBG_LINE='socket' - self.state = DISCONNECTED - + NonBlockingTransport.__init__(self, raise_event, on_disconnect) # writable, readable - keep state of the last pluged flags # This prevents replug of same object with the same flags self.writable = True @@ -122,14 +176,6 @@ class NonBlockingTcp(PlugIn, IdleObject): self._exported_methods=[self.disconnect, self.onreceive, self.set_send_timeout, self.set_timeout, self.remove_timeout] - def plugin(self, owner): - owner.Connection=self - self.idlequeue = owner.idlequeue - - def plugout(self): - self._owner.Connection = None - self._owner = None - def get_fd(self): try: @@ -147,14 +193,12 @@ class NonBlockingTcp(PlugIn, IdleObject): :param on_connect_failure: callback called on failure when estabilishing tcp connection ''' - self.on_connect = on_connect - self.on_connect_failure = on_connect_failure - (self.server, self.port) = conn_5tuple[4][:2] - log.info('NonBlocking Connect :: About tot connect to %s:%s' % conn_5tuple[4][:2]) + NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure) + try: self._sock = socket.socket(*conn_5tuple[:3]) except socket.error, (errnum, errstr): - on_connect_failure('NonBlockingTcp: Error while creating socket: %s %s' % (errnum, errstr)) + self._on_connect_failure('NonBlockingTCP: Error while creating socket: %s %s' % (errnum, errstr)) return self._send = self._sock.send @@ -177,9 +221,8 @@ class NonBlockingTcp(PlugIn, IdleObject): if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): # connecting in progress - self.set_state(CONNECTING) log.info('After connect. "%s" raised => CONNECTING' % errstr) - # on_connect/failure will be called from self.pollin/self.pollout + self.tcp_connection_started() return elif errnum in (0, 10056, errno.EISCONN): # already connected - this branch is very unlikely, nonblocking connect() will @@ -195,27 +238,9 @@ class NonBlockingTcp(PlugIn, IdleObject): (self.server, self.port, errnum, errstr)) def _on_connect(self, data): - ''' preceeds call of on_connect callback ''' - self.set_state(CONNECTED) + ''' with TCP socket, we have to remove send-timeout ''' self.idlequeue.remove_timeout(self.get_fd()) - self.on_connect() - - - def set_state(self, newstate): - assert(newstate in [DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING]) - if (self.state, newstate) in [(CONNECTING, DISCONNECTING), (DISCONNECTED, DISCONNECTING)]: - log.info('strange move: %s -> %s' % (self.state, newstate)) - self.state = newstate - - - def _on_connect_failure(self,err_message): - ''' preceeds call of on_connect_failure callback ''' - # In case of error while connecting we need to close socket - # but we don't want to call DisconnectHandlers from client, - # thus the do_callback=False - self.disconnect(do_callback=False) - self.on_connect_failure(err_message=err_message) - + NonBlockingTransport._on_connect(self, data) def pollin(self): @@ -250,10 +275,7 @@ class NonBlockingTcp(PlugIn, IdleObject): self._sock.close() except socket.error, (errnum, errstr): log.error('Error disconnecting a socket: %s %s' % (errnum,errstr)) - self.set_state(DISCONNECTED) - if do_callback: - # invoke callback given in __init__ - self.on_disconnect() + NonBlockingTransport.disconnect(self, do_callback) def read_timeout(self): ''' @@ -295,11 +317,7 @@ class NonBlockingTcp(PlugIn, IdleObject): '''Append raw_data to the queue of messages to be send. If supplied data is unicode string, encode it to utf-8. ''' - - if self.state not in [CONNECTED, DISCONNECTING]: - log.error('Trying to send %s when transport is %s.' % - (raw_data, self.state)) - return + NonBlockingTransport.send(self, raw_data, now) r = raw_data if isinstance(r, unicode): r = r.encode('utf-8') @@ -343,31 +361,13 @@ class NonBlockingTcp(PlugIn, IdleObject): sent_data = self.sendbuff[:send_count] self.sendbuff = self.sendbuff[send_count:] self._plug_idle() - self._raise_event(DATA_SENT, sent_data) + self.raise_event(DATA_SENT, sent_data) except socket.error, e: log.error('_do_send:', exc_info=True) traceback.print_exc() self.disconnect() - def _raise_event(self, event_type, data): - if data and data.strip(): - log.info('raising event from transport: %s %s' % (event_type,data)) - if hasattr(self._owner, 'Dispatcher'): - self._owner.Dispatcher.Event('', event_type, data) - - def onreceive(self, recv_handler): - ''' Sets the on_receive callback. Do not confuse it with - on_receive() method, which is the callback itself.''' - if not recv_handler: - if hasattr(self._owner, 'Dispatcher'): - self.on_receive = self._owner.Dispatcher.ProcessNonBlocking - else: - self.on_receive = None - return - log.info('setting onreceive on %s' % recv_handler) - self.on_receive = recv_handler - def _do_receive(self): ''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.''' @@ -410,7 +410,7 @@ class NonBlockingTcp(PlugIn, IdleObject): # pass received data to owner #self. if self.on_receive: - self._raise_event(DATA_RECEIVED, received) + self.raise_event(DATA_RECEIVED, received) self._on_receive(received) else: # This should never happen, so we need the debug. (If there is no handler @@ -418,31 +418,37 @@ class NonBlockingTcp(PlugIn, IdleObject): log.error('SOCKET Unhandled data received: %s' % received) self.disconnect() - def _on_receive(self, data): - # Overriding this method allows modifying received data before it is passed - # to owner's callback. - log.info('About to call on_receive which is %s' % self.on_receive) + def _on_receive(self,data): + '''Preceeds passing received data to Client class. Gets rid of HTTP headers + and checks them.''' self.on_receive(data) -class NonBlockingHTTP(NonBlockingTcp): + +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): + def __init__(self, raise_event, on_disconnect, http_uri, http_port, http_version=None): 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) + if http_version: + self.http_version = http_version + else: + self.http_version = 'HTTP/1.1' + # buffer for partial responses + self.recvbuff = '' + self.expected_length = 0 + NonBlockingTCP.__init__(self, raise_event, on_disconnect) def send(self, raw_data, now=False): - - NonBlockingTcp.send( + NonBlockingTCP.send( self, self.build_http_message(raw_data), now) @@ -450,21 +456,43 @@ class NonBlockingHTTP(NonBlockingTcp): 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() + if not self.recvbuff: + # recvbuff empty - fresh HTTP message was received + statusline, headers, self.recvbuff = self.parse_http_message(data) + if statusline[1] != '200': + log.error('HTTP Error: %s %s' % (statusline[1], statusline[2])) + self.disconnect() + return + self.expected_length = int(headers['Content-Length']) + else: + #sth in recvbuff - append currently received data to HTTP mess in buffer + self.recvbuff = '%s%s' % (self.recvbuff, data) + + if self.expected_length > len(self.recvbuff): + # If we haven't received the whole HTTP mess yet, let's end the thread. + # It will be finnished from one of following poll calls on plugged socket. return + + # FIXME the reassembling doesn't work - Connection Manager on jabbim.cz + # closes TCP connection before sending announced bytes.. WTF + + # all was received, now call the on_receive callback + httpbody = self.recvbuff + + self.recvbuff='' + self.expected_length=0 self.on_receive(httpbody) - def build_http_message(self, httpbody): + def build_http_message(self, httpbody, method='POST'): ''' 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, + absolute_uri = '%s://%s:%s%s' % (self.http_protocol, self.http_host, + self.http_port, self.http_path) + headers = ['%s %s %s' % (method, absolute_uri, self.http_version), 'Host: %s:%s' % (self.http_host, self.http_port), 'Content-Type: text/xml; charset=utf-8', 'Content-Length: %s' % len(str(httpbody)), @@ -482,7 +510,7 @@ class NonBlockingHTTP(NonBlockingTcp): ) ''' message = message.replace('\r','') - (header, httpbody) = message.split('\n\n') + (header, httpbody) = message.split('\n\n',1) header = header.split('\n') statusline = header[0].split(' ') header = header[1:] @@ -494,15 +522,15 @@ class NonBlockingHTTP(NonBlockingTcp): -class NBProxySocket(NonBlockingTcp): +class NBProxySocket(NonBlockingTCP): ''' Interface for proxy socket wrappers - when tunnneling XMPP over proxies, some connecting process usually has to be done before opening stream. ''' - def __init__(self, on_disconnect, xmpp_server, proxy_creds=(None,None)): + def __init__(self, raise_event, on_disconnect, xmpp_server, proxy_creds=(None,None)): self.proxy_user, self.proxy_pass = proxy_creds self.xmpp_server = xmpp_server - NonBlockingTcp.__init__(self, on_disconnect) + NonBlockingTCP.__init__(self, raise_event, on_disconnect) def connect(self, conn_5tuple, on_connect, on_connect_failure): @@ -515,7 +543,7 @@ class NBProxySocket(NonBlockingTcp): self.after_proxy_connect = on_connect - NonBlockingTcp.connect(self, + NonBlockingTCP.connect(self, conn_5tuple=conn_5tuple, on_connect =self._on_tcp_connect, on_connect_failure =on_connect_failure) @@ -526,7 +554,7 @@ class NBProxySocket(NonBlockingTcp): class NBHTTPProxySocket(NBProxySocket): - ''' This class can be used instead of NonBlockingTcp + ''' This class can be used instead of NonBlockingTCP HTTP (CONNECT) proxy connection class. Allows to use HTTP proxies like squid with (optionally) simple authentication (using login and password). ''' @@ -588,7 +616,7 @@ class NBSOCKS5ProxySocket(NBProxySocket): redefines only connect method. Allows to use SOCKS5 proxies with (optionally) simple authentication (only USERNAME/PASSWORD auth). ''' - # TODO: replace DEBUG with ordinrar logging, replace on_proxy_failure() with + # TODO: replace on_proxy_failure() with # _on_connect_failure, at the end call _on_connect() def _on_tcp_connect(self): @@ -620,13 +648,13 @@ class NBSOCKS5ProxySocket(NBProxySocket): self.send(to_send) else: if reply[1] == '\xff': - self.DEBUG('Authentification to proxy impossible: no acceptable ' - 'auth method', 'error') + log.error('Authentification to proxy impossible: no acceptable ' + 'auth method') self._owner.disconnected() self.on_proxy_failure('Authentification to proxy impossible: no ' 'acceptable authentification method') return - self.DEBUG('Invalid proxy reply', 'error') + log.error('Invalid proxy reply') self._owner.disconnected() self.on_proxy_failure('Invalid proxy reply') return @@ -635,21 +663,21 @@ class NBSOCKS5ProxySocket(NBProxySocket): if reply is None: return if len(reply) != 2: - self.DEBUG('Invalid proxy reply', 'error') + log.error('Invalid proxy reply') self._owner.disconnected() self.on_proxy_failure('Invalid proxy reply') return if reply[0] != '\x01': - self.DEBUG('Invalid proxy reply', 'error') + log.error('Invalid proxy reply') self._owner.disconnected() self.on_proxy_failure('Invalid proxy reply') return if reply[1] != '\x00': - self.DEBUG('Authentification to proxy failed', 'error') + log.error('Authentification to proxy failed') self._owner.disconnected() self.on_proxy_failure('Authentification to proxy failed') return - self.DEBUG('Authentification successfull. Jabber server contacted.','ok') + log.info('Authentification successfull. Jabber server contacted.') # Request connection req = "\x05\x01\x00" # If the given destination address is an IP address, we'll @@ -675,12 +703,12 @@ class NBSOCKS5ProxySocket(NBProxySocket): if reply is None: return if len(reply) < 10: - self.DEBUG('Invalid proxy reply', 'error') + log.error('Invalid proxy reply') self._owner.disconnected() self.on_proxy_failure('Invalid proxy reply') return if reply[0] != '\x05': - self.DEBUG('Invalid proxy reply', 'error') + log.error('Invalid proxy reply') self._owner.disconnected() self.on_proxy_failure('Invalid proxy reply') return @@ -700,7 +728,7 @@ class NBSOCKS5ProxySocket(NBProxySocket): txt = errors[ord(reply[1])-1] else: txt = 'Invalid proxy reply' - self.DEBUG(txt, 'error') + log.error(txt) self.on_proxy_failure(txt) return # Get the bound address/port @@ -709,7 +737,7 @@ class NBSOCKS5ProxySocket(NBProxySocket): elif reply[3] == "\x03": begin, end = 4, 4 + reply[4] else: - self.DEBUG('Invalid proxy reply', 'error') + log.error('Invalid proxy reply') self._owner.disconnected() self.on_proxy_failure('Invalid proxy reply') return @@ -717,9 +745,6 @@ class NBSOCKS5ProxySocket(NBProxySocket): if self.on_connect_proxy: self.on_connect_proxy() - def DEBUG(self, text, severity): - ''' Overwrites DEBUG tag to allow debug output be presented as "CONNECTproxy".''' - return self._owner.DEBUG(DBG_CONNECT_PROXY, text, severity)