More doc-string (and not only) refactoring

This commit is contained in:
Alexander Cherniuk 2009-11-26 16:32:56 +02:00
parent 99472b1702
commit 6d0f28c47d
9 changed files with 1350 additions and 744 deletions

View File

@ -13,12 +13,14 @@
## but WITHOUT ANY WARRANTY; without even the implied warranty of ## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
'''
"""
Provides plugs for SASL and NON-SASL authentication mechanisms. Provides plugs for SASL and NON-SASL authentication mechanisms.
Can be used both for client and transport authentication. Can be used both for client and transport authentication
See client_nb.py See client_nb.py
''' """
from protocol import NS_SASL, NS_SESSION, NS_STREAMS, NS_BIND, NS_AUTH from protocol import NS_SASL, NS_SESSION, NS_STREAMS, NS_BIND, NS_AUTH
from protocol import Node, NodeProcessed, isResultNode, Iq, Protocol, JID from protocol import Node, NodeProcessed, isResultNode, Iq, Protocol, JID
from plugin import PlugIn from plugin import PlugIn
@ -49,7 +51,8 @@ SASL_UNSUPPORTED = 'not-supported'
SASL_IN_PROCESS = 'in-process' SASL_IN_PROCESS = 'in-process'
def challenge_splitter(data): def challenge_splitter(data):
''' Helper function that creates a dict from challenge string. """
Helper function that creates a dict from challenge string
Sample challenge string: Sample challenge string:
username="example.org",realm="somerealm",\ username="example.org",realm="somerealm",\
@ -59,7 +62,7 @@ def challenge_splitter(data):
Expected result for challan: Expected result for challan:
dict['qop'] = ('auth','auth-int','auth-conf') dict['qop'] = ('auth','auth-int','auth-conf')
dict['realm'] = 'somerealm' dict['realm'] = 'somerealm'
''' """
X_KEYWORD, X_VALUE, X_END = 0, 1, 2 X_KEYWORD, X_VALUE, X_END = 0, 1, 2
quotes_open = False quotes_open = False
keyword, value = '', '' keyword, value = '', ''
@ -112,16 +115,17 @@ def challenge_splitter(data):
class SASL(PlugIn): class SASL(PlugIn):
''' """
Implements SASL authentication. Can be plugged into NonBlockingClient Implements SASL authentication. Can be plugged into NonBlockingClient
to start authentication. to start authentication
''' """
def __init__(self, username, password, on_sasl): def __init__(self, username, password, on_sasl):
''' """
:param user: XMPP username :param user: XMPP username
:param password: XMPP password :param password: XMPP password
:param on_sasl: Callback, will be called after each SASL auth-step. :param on_sasl: Callback, will be called after each SASL auth-step.
''' """
PlugIn.__init__(self) PlugIn.__init__(self)
self.username = username self.username = username
self.password = password self.password = password
@ -141,7 +145,9 @@ class SASL(PlugIn):
self.startsasl = None self.startsasl = None
def plugout(self): def plugout(self):
''' Remove SASL handlers from owner's dispatcher. Used internally. ''' """
Remove SASL handlers from owner's dispatcher. Used internally
"""
if 'features' in self._owner.__dict__: if 'features' in self._owner.__dict__:
self._owner.UnregisterHandler('features', self.FeaturesHandler, self._owner.UnregisterHandler('features', self.FeaturesHandler,
xmlns=NS_STREAMS) xmlns=NS_STREAMS)
@ -156,13 +162,13 @@ class SASL(PlugIn):
xmlns=NS_SASL) xmlns=NS_SASL)
def auth(self): def auth(self):
''' """
Start authentication. Result can be obtained via "SASL.startsasl" Start authentication. Result can be obtained via "SASL.startsasl"
attribute and will be either SASL_SUCCESS or SASL_FAILURE. attribute and will be either SASL_SUCCESS or SASL_FAILURE
Note that successfull auth will take at least two Dispatcher.Process() Note that successfull auth will take at least two Dispatcher.Process()
calls. calls.
''' """
if self.startsasl: if self.startsasl:
pass pass
elif self._owner.Dispatcher.Stream.features: elif self._owner.Dispatcher.Stream.features:
@ -176,7 +182,9 @@ class SASL(PlugIn):
self.FeaturesHandler, xmlns=NS_STREAMS) self.FeaturesHandler, xmlns=NS_STREAMS)
def FeaturesHandler(self, conn, feats): def FeaturesHandler(self, conn, feats):
''' Used to determine if server supports SASL auth. Used internally. ''' """
Used to determine if server supports SASL auth. Used internally
"""
if not feats.getTag('mechanisms', namespace=NS_SASL): if not feats.getTag('mechanisms', namespace=NS_SASL):
self.startsasl='not-supported' self.startsasl='not-supported'
log.error('SASL not supported by server') log.error('SASL not supported by server')
@ -235,7 +243,9 @@ class SASL(PlugIn):
return return
def SASLHandler(self, conn, challenge): def SASLHandler(self, conn, challenge):
''' Perform next SASL auth step. Used internally. ''' """
Perform next SASL auth step. Used internally
"""
if challenge.getNamespace() != NS_SASL: if challenge.getNamespace() != NS_SASL:
return return
### Handle Auth result ### Handle Auth result
@ -359,12 +369,15 @@ class SASL(PlugIn):
class NonBlockingNonSASL(PlugIn): class NonBlockingNonSASL(PlugIn):
''' """
Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and
transport authentication. transport authentication
''' """
def __init__(self, user, password, resource, on_auth): def __init__(self, user, password, resource, on_auth):
''' Caches username, password and resource for auth. ''' """
Caches username, password and resource for auth
"""
PlugIn.__init__(self) PlugIn.__init__(self)
self.user = user self.user = user
if password is None: if password is None:
@ -375,10 +388,10 @@ class NonBlockingNonSASL(PlugIn):
self.on_auth = on_auth self.on_auth = on_auth
def plugin(self, owner): def plugin(self, owner):
''' """
Determine the best auth method (digest/0k/plain) and use it for auth. Determine the best auth method (digest/0k/plain) and use it for auth.
Returns used method name on success. Used internally. Returns used method name on success. Used internally
''' """
log.info('Querying server about possible auth methods') log.info('Querying server about possible auth methods')
self.owner = owner self.owner = owner
@ -438,10 +451,11 @@ class NonBlockingNonSASL(PlugIn):
class NonBlockingBind(PlugIn): class NonBlockingBind(PlugIn):
''' """
Bind some JID to the current connection to allow router know of our Bind some JID to the current connection to allow router know of our
location. Must be plugged after successful SASL auth. location. Must be plugged after successful SASL auth
''' """
def __init__(self): def __init__(self):
PlugIn.__init__(self) PlugIn.__init__(self)
self.bound = None self.bound = None
@ -459,10 +473,10 @@ class NonBlockingBind(PlugIn):
xmlns=NS_STREAMS) xmlns=NS_STREAMS)
def FeaturesHandler(self, conn, feats): def FeaturesHandler(self, conn, feats):
''' """
Determine if server supports resource binding and set some internal Determine if server supports resource binding and set some internal
attributes accordingly. attributes accordingly
''' """
if not feats.getTag('bind', namespace=NS_BIND): if not feats.getTag('bind', namespace=NS_BIND):
log.error('Server does not requested binding.') log.error('Server does not requested binding.')
# we try to bind resource anyway # we try to bind resource anyway
@ -476,14 +490,16 @@ class NonBlockingBind(PlugIn):
self.bound = [] self.bound = []
def plugout(self): def plugout(self):
''' Remove Bind handler from owner's dispatcher. Used internally. ''' """
Remove Bind handler from owner's dispatcher. Used internally
"""
self._owner.UnregisterHandler('features', self.FeaturesHandler, self._owner.UnregisterHandler('features', self.FeaturesHandler,
xmlns=NS_STREAMS) xmlns=NS_STREAMS)
def NonBlockingBind(self, resource=None, on_bound=None): def NonBlockingBind(self, resource=None, on_bound=None):
''' Perform binding. """
Use provided resource name or random (if not provided). Perform binding. Use provided resource name or random (if not provided).
''' """
self.on_bound = on_bound self.on_bound = on_bound
self._resource = resource self._resource = resource
if self._resource: if self._resource:

View File

@ -125,11 +125,12 @@ class NonBlockingBOSH(NonBlockingTransport):
log.warn('set_timeout: TIMEOUT NOT SET: state is %s, fd is %s' % (self.get_state(), self.fd)) log.warn('set_timeout: TIMEOUT NOT SET: state is %s, fd is %s' % (self.get_state(), self.fd))
def on_http_request_possible(self): def on_http_request_possible(self):
''' """
Called when HTTP request it's possible to send a HTTP request. It can be when Called when HTTP request it's possible to send a HTTP request. It can be when
socket is connected or when HTTP response arrived. socket is connected or when HTTP response arrived
There should be always one pending request to BOSH CM. There should be always one pending request to BOSH CM.
''' """
log.debug('on_http_req possible, state:\n%s' % self.get_current_state()) log.debug('on_http_req possible, state:\n%s' % self.get_current_state())
if self.get_state()==DISCONNECTED: return if self.get_state()==DISCONNECTED: return
@ -149,14 +150,18 @@ class NonBlockingBOSH(NonBlockingTransport):
def get_socket_in(self, state): def get_socket_in(self, state):
''' gets sockets in desired state ''' """
Get sockets in desired state
"""
for s in self.http_socks: for s in self.http_socks:
if s.get_state()==state: return s if s.get_state()==state: return s
return None return None
def get_free_socket(self): def get_free_socket(self):
''' Selects and returns socket eligible for sending a data to.''' """
Select and returns socket eligible for sending a data to
"""
if self.http_pipelining: if self.http_pipelining:
return self.get_socket_in(CONNECTED) return self.get_socket_in(CONNECTED)
else: else:
@ -176,10 +181,10 @@ class NonBlockingBOSH(NonBlockingTransport):
def send_BOSH(self, payload): def send_BOSH(self, payload):
''' """
Tries to send a stanza in payload by appeding it to a buffer and plugging a Tries to send a stanza in payload by appeding it to a buffer and plugging a
free socket for writing. free socket for writing.
''' """
total_pending_reqs = sum([s.pending_requests for s in self.http_socks]) total_pending_reqs = sum([s.pending_requests for s in self.http_socks])
# when called after HTTP response (Payload=None) and when there are already # when called after HTTP response (Payload=None) and when there are already
@ -236,15 +241,16 @@ class NonBlockingBOSH(NonBlockingTransport):
log.error('=====!!!!!!!!====> Couldn\'t get free socket in plug_socket())') log.error('=====!!!!!!!!====> Couldn\'t get free socket in plug_socket())')
def build_stanza(self, socket): def build_stanza(self, socket):
''' """
Builds a BOSH body tag from data in buffers and adds key, rid and ack Build a BOSH body tag from data in buffers and adds key, rid and ack
attributes to it. attributes to it
This method is called from _do_send() of underlying transport. This is to This method is called from _do_send() of underlying transport. This is to
ensure rid and keys will be processed in correct order. If I generate them ensure rid and keys will be processed in correct order. If I generate
before plugging a socket for write (and did it for two sockets/HTTP them before plugging a socket for write (and did it for two sockets/HTTP
connections) in parallel, they might be sent in wrong order, which results connections) in parallel, they might be sent in wrong order, which
in violating the BOSH session and server-side disconnect. results in violating the BOSH session and server-side disconnect.
''' """
if self.prio_bosh_stanzas: if self.prio_bosh_stanzas:
stanza, add_payload = self.prio_bosh_stanzas.pop(0) stanza, add_payload = self.prio_bosh_stanzas.pop(0)
if add_payload: if add_payload:
@ -285,10 +291,11 @@ class NonBlockingBOSH(NonBlockingTransport):
self.wait_cb_time) self.wait_cb_time)
def on_persistent_fallback(self, socket): def on_persistent_fallback(self, socket):
''' """
Called from underlying transport when server closes TCP connection. Called from underlying transport when server closes TCP connection
:param socket: disconnected transport object :param socket: disconnected transport object
''' """
if socket.http_persistent: if socket.http_persistent:
log.warn('Fallback to nonpersistent HTTP (no pipelining as well)') log.warn('Fallback to nonpersistent HTTP (no pipelining as well)')
socket.http_persistent = False socket.http_persistent = False
@ -302,9 +309,10 @@ class NonBlockingBOSH(NonBlockingTransport):
def handle_body_attrs(self, stanza_attrs): def handle_body_attrs(self, stanza_attrs):
''' """
Called for each incoming body stanza from dispatcher. Checks body attributes. Called for each incoming body stanza from dispatcher. Checks body
''' attributes.
"""
self.remove_bosh_wait_timeout() self.remove_bosh_wait_timeout()
if self.after_init: if self.after_init:
@ -345,7 +353,9 @@ class NonBlockingBOSH(NonBlockingTransport):
def append_stanza(self, stanza): def append_stanza(self, stanza):
''' appends stanza to a buffer to send ''' """
Append stanza to a buffer to send
"""
if stanza: if stanza:
if isinstance(stanza, tuple): if isinstance(stanza, tuple):
# stanza is tuple of BOSH stanza and bool value for whether to add payload # stanza is tuple of BOSH stanza and bool value for whether to add payload
@ -378,7 +388,9 @@ class NonBlockingBOSH(NonBlockingTransport):
def boshify_stanzas(self, stanzas=[], body_attrs=None): def boshify_stanzas(self, stanzas=[], body_attrs=None):
''' wraps zero to many stanzas by body tag with xmlns and sid ''' """
Wraps zero to many stanzas by body tag with xmlns and sid
"""
log.debug('boshify_staza - type is: %s, stanza is %s' % (type(stanzas), stanzas)) log.debug('boshify_staza - type is: %s, stanza is %s' % (type(stanzas), stanzas))
tag = BOSHBody(attrs={'sid': self.bosh_sid}) tag = BOSHBody(attrs={'sid': self.bosh_sid})
tag.setPayload(stanzas) tag.setPayload(stanzas)
@ -470,10 +482,10 @@ def get_rand_number():
class AckChecker(): class AckChecker():
''' """
Class for generating rids and generating and checking acknowledgements in Class for generating rids and generating and checking acknowledgements in
BOSH messages. BOSH messages
''' """
def __init__(self): def __init__(self):
self.rid = get_rand_number() self.rid = get_rand_number()
self.ack = 1 self.ack = 1
@ -516,9 +528,9 @@ class AckChecker():
class KeyStack(): class KeyStack():
''' """
Class implementing key sequences for BOSH messages Class implementing key sequences for BOSH messages
''' """
def __init__(self, count): def __init__(self, count):
self.count = count self.count = count
self.keys = [] self.keys = []

View File

@ -18,7 +18,10 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
''' XML canonicalisation methods (for XEP-0116) ''' """
XML canonicalisation methods (for XEP-0116)
"""
from simplexml import ustr from simplexml import ustr
def c14n(node, is_buggy): def c14n(node, is_buggy):

View File

@ -16,9 +16,10 @@
# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $ # $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $
''' """
Client class establishs connection to XMPP Server and handles authentication Client class establishs connection to XMPP Server and handles authentication
''' """
import socket import socket
import transports_nb, dispatcher_nb, auth_nb, roster_nb, protocol, bosh import transports_nb, dispatcher_nb, auth_nb, roster_nb, protocol, bosh
from protocol import NS_TLS from protocol import NS_TLS
@ -28,21 +29,22 @@ log = logging.getLogger('gajim.c.x.client_nb')
class NonBlockingClient: class NonBlockingClient:
''' """
Client class is XMPP connection mountpoint. Objects for authentication, Client class is XMPP connection mountpoint. Objects for authentication,
network communication, roster, xml parsing ... are plugged to client object. network communication, roster, xml parsing ... are plugged to client object.
Client implements the abstract behavior - mostly negotioation and callbacks Client implements the abstract behavior - mostly negotioation and callbacks
handling, whereas underlying modules take care of feature-specific logic. handling, whereas underlying modules take care of feature-specific logic
''' """
def __init__(self, domain, idlequeue, caller=None): def __init__(self, domain, idlequeue, caller=None):
''' """
Caches connection data: Caches connection data
:param domain: domain - for to: attribute (from account info) :param domain: domain - for to: attribute (from account info)
:param idlequeue: processing idlequeue :param idlequeue: processing idlequeue
:param caller: calling object - it has to implement methods :param caller: calling object - it has to implement methods
_event_dispatcher which is called from dispatcher instance _event_dispatcher which is called from dispatcher instance
''' """
self.Namespace = protocol.NS_CLIENT self.Namespace = protocol.NS_CLIENT
self.defaultNamespace = self.Namespace self.defaultNamespace = self.Namespace
@ -69,10 +71,10 @@ class NonBlockingClient:
self.protocol_type = 'XMPP' self.protocol_type = 'XMPP'
def disconnect(self, message=''): def disconnect(self, message=''):
''' """
Called on disconnection - disconnect callback is picked based on state Called on disconnection - disconnect callback is picked based on state of
of the client. the client.
''' """
# to avoid recursive calls # to avoid recursive calls
if self.disconnecting: return if self.disconnecting: return
@ -132,9 +134,10 @@ class NonBlockingClient:
self.disconnecting = False self.disconnecting = False
def connect(self, on_connect, on_connect_failure, hostname=None, port=5222, def connect(self, on_connect, on_connect_failure, hostname=None, port=5222,
on_proxy_failure=None, proxy=None, secure_tuple=('plain', None, None)): on_proxy_failure=None, proxy=None, secure_tuple=('plain', None,
''' None)):
Open XMPP connection (open XML streams in both directions). """
Open XMPP connection (open XML streams in both directions)
:param on_connect: called after stream is successfully opened :param on_connect: called after stream is successfully opened
:param on_connect_failure: called when error occures during connection :param on_connect_failure: called when error occures during connection
@ -150,7 +153,7 @@ class NonBlockingClient:
'tls' - TLS established after negotiation with starttls, or 'plain'. 'tls' - TLS established after negotiation with starttls, or 'plain'.
cacerts, mycerts - see tls_nb.NonBlockingTLS constructor for more cacerts, mycerts - see tls_nb.NonBlockingTLS constructor for more
details details
''' """
self.on_connect = on_connect self.on_connect = on_connect
self.on_connect_failure=on_connect_failure self.on_connect_failure=on_connect_failure
self.on_proxy_failure = on_proxy_failure self.on_proxy_failure = on_proxy_failure
@ -223,7 +226,11 @@ class NonBlockingClient:
on_success=self._try_next_ip) on_success=self._try_next_ip)
def _resolve_hostname(self, hostname, port, on_success): def _resolve_hostname(self, hostname, port, on_success):
''' wrapper for getaddinfo call. FIXME: getaddinfo blocks''' """
Wrapper for getaddinfo call
FIXME: getaddinfo blocks
"""
try: try:
self.ip_addresses = socket.getaddrinfo(hostname, port, self.ip_addresses = socket.getaddrinfo(hostname, port,
socket.AF_UNSPEC, socket.SOCK_STREAM) socket.AF_UNSPEC, socket.SOCK_STREAM)
@ -234,7 +241,9 @@ class NonBlockingClient:
on_success() on_success()
def _try_next_ip(self, err_message=None): def _try_next_ip(self, err_message=None):
'''Iterates over IP addresses tries to connect to it''' """
Iterate over IP addresses tries to connect to it
"""
if err_message: if err_message:
log.debug('While looping over DNS A records: %s' % err_message) log.debug('While looping over DNS A records: %s' % err_message)
if self.ip_addresses == []: if self.ip_addresses == []:
@ -249,18 +258,20 @@ class NonBlockingClient:
on_connect_failure=self._try_next_ip) on_connect_failure=self._try_next_ip)
def incoming_stream_version(self): def incoming_stream_version(self):
''' gets version of xml stream''' """
Get version of xml stream
"""
if 'version' in self.Dispatcher.Stream._document_attrs: if 'version' in self.Dispatcher.Stream._document_attrs:
return self.Dispatcher.Stream._document_attrs['version'] return self.Dispatcher.Stream._document_attrs['version']
else: else:
return None return None
def _xmpp_connect(self, socket_type=None): def _xmpp_connect(self, socket_type=None):
''' """
Starts XMPP connecting process - opens the XML stream. Is called after TCP Start XMPP connecting process - open the XML stream. Is called after TCP
connection is established or after switch to TLS when successfully connection is established or after switch to TLS when successfully
negotiated with <starttls>. negotiated with <starttls>.
''' """
# socket_type contains info which transport connection was established # socket_type contains info which transport connection was established
if not socket_type: if not socket_type:
if self.Connection.ssl_lib: if self.Connection.ssl_lib:
@ -273,19 +284,19 @@ class NonBlockingClient:
self._xmpp_connect_machine() self._xmpp_connect_machine()
def _xmpp_connect_machine(self, mode=None, data=None): def _xmpp_connect_machine(self, mode=None, data=None):
''' """
Finite automaton taking care of stream opening and features tag Finite automaton taking care of stream opening and features tag handling.
handling. Calls _on_stream_start when stream is started, and disconnect() Calls _on_stream_start when stream is started, and disconnect() on
on failure. failure.
''' """
log.info('-------------xmpp_connect_machine() >> mode: %s, data: %s...' % log.info('-------------xmpp_connect_machine() >> mode: %s, data: %s...' %
(mode, str(data)[:20])) (mode, str(data)[:20]))
def on_next_receive(mode): def on_next_receive(mode):
''' """
Sets desired on_receive callback on transport based on the state of Set desired on_receive callback on transport based on the state of
connect_machine. connect_machine.
''' """
log.info('setting %s on next receive' % mode) log.info('setting %s on next receive' % mode)
if mode is None: if mode is None:
self.onreceive(None) # switch to Dispatcher.ProcessNonBlocking self.onreceive(None) # switch to Dispatcher.ProcessNonBlocking
@ -346,10 +357,10 @@ class NonBlockingClient:
self._on_stream_start() self._on_stream_start()
def _on_stream_start(self): def _on_stream_start(self):
''' """
Called after XMPP stream is opened. TLS negotiation may follow if Called after XMPP stream is opened. TLS negotiation may follow if
supported and desired. supported and desired.
''' """
self.stream_started = True self.stream_started = True
self.onreceive(None) self.onreceive(None)
@ -381,7 +392,9 @@ class NonBlockingClient:
assert False, 'Stream opened for unsupported connection' assert False, 'Stream opened for unsupported connection'
def _tls_negotiation_handler(self, con=None, tag=None): def _tls_negotiation_handler(self, con=None, tag=None):
''' takes care of TLS negotioation with <starttls> ''' """
Take care of TLS negotioation with <starttls>
"""
log.info('-------------tls_negotiaton_handler() >> tag: %s' % tag) log.info('-------------tls_negotiaton_handler() >> tag: %s' % tag)
if not con and not tag: if not con and not tag:
# starting state when we send the <starttls> # starting state when we send the <starttls>
@ -407,15 +420,17 @@ class NonBlockingClient:
on_fail = lambda: self.disconnect('error while etabilishing TLS')) on_fail = lambda: self.disconnect('error while etabilishing TLS'))
def _on_connect(self): def _on_connect(self):
''' preceeds call of on_connect callback ''' """
Preceed call of on_connect callback
"""
self.onreceive(None) self.onreceive(None)
self.on_connect(self, self.connected) self.on_connect(self, self.connected)
def raise_event(self, event_type, data): def raise_event(self, event_type, data):
''' """
Raises event to connection instance. DATA_SENT and DATA_RECIVED events Raise event to connection instance. DATA_SENT and DATA_RECIVED events
are used in XML console to show XMPP traffic are used in XML console to show XMPP traffic
''' """
log.info('raising event from transport: :::::%s::::\n_____________\n%s\n_____________\n' % (event_type,data)) log.info('raising event from transport: :::::%s::::\n_____________\n%s\n_____________\n' % (event_type,data))
if hasattr(self, 'Dispatcher'): if hasattr(self, 'Dispatcher'):
self.Dispatcher.Event('', event_type, data) self.Dispatcher.Event('', event_type, data)
@ -425,9 +440,9 @@ class NonBlockingClient:
############################################################################### ###############################################################################
def auth(self, user, password, resource='', sasl=True, on_auth=None): def auth(self, user, password, resource='', sasl=True, on_auth=None):
''' """
Authenticate connnection and bind resource. If resource is not provided Authenticate connnection and bind resource. If resource is not provided
random one or library name used. random one or library name used
:param user: XMPP username :param user: XMPP username
:param password: XMPP password :param password: XMPP password
@ -435,7 +450,7 @@ class NonBlockingClient:
:param sasl: Boolean indicating if SASL shall be used. (default: True) :param sasl: Boolean indicating if SASL shall be used. (default: True)
:param on_auth: Callback, called after auth. On auth failure, argument :param on_auth: Callback, called after auth. On auth failure, argument
is None. is None.
''' """
self._User, self._Password = user, password self._User, self._Password = user, password
self._Resource, self._sasl = resource, sasl self._Resource, self._sasl = resource, sasl
self.on_auth = on_auth self.on_auth = on_auth
@ -443,7 +458,9 @@ class NonBlockingClient:
return return
def _on_old_auth(self, res): def _on_old_auth(self, res):
''' Callback used by NON-SASL auth. On auth failure, res is None. ''' """
Callback used by NON-SASL auth. On auth failure, res is None
"""
if res: if res:
self.connected += '+old_auth' self.connected += '+old_auth'
self.on_auth(self, 'old_auth') self.on_auth(self, 'old_auth')
@ -451,7 +468,9 @@ class NonBlockingClient:
self.on_auth(self, None) self.on_auth(self, None)
def _on_sasl_auth(self, res): def _on_sasl_auth(self, res):
''' Used internally. On auth failure, res is None. ''' """
Used internally. On auth failure, res is None
"""
self.onreceive(None) self.onreceive(None)
if res: if res:
self.connected += '+sasl' self.connected += '+sasl'
@ -460,7 +479,9 @@ class NonBlockingClient:
self.on_auth(self, None) self.on_auth(self, None)
def _on_doc_attrs(self): def _on_doc_attrs(self):
''' Plug authentication objects and start auth. ''' """
Plug authentication objects and start auth
"""
if self._sasl: if self._sasl:
auth_nb.SASL.get_instance(self._User, self._Password, auth_nb.SASL.get_instance(self._User, self._Password,
self._on_start_sasl).PlugIn(self) self._on_start_sasl).PlugIn(self)
@ -474,7 +495,9 @@ class NonBlockingClient:
return True return True
def _on_start_sasl(self, data=None): def _on_start_sasl(self, data=None):
''' Callback used by SASL, called on each auth step.''' """
Callback used by SASL, called on each auth step
"""
if data: if data:
self.Dispatcher.ProcessNonBlocking(data) self.Dispatcher.ProcessNonBlocking(data)
if not 'SASL' in self.__dict__: if not 'SASL' in self.__dict__:
@ -504,20 +527,26 @@ class NonBlockingClient:
return True return True
def initRoster(self, version=''): def initRoster(self, version=''):
''' Plug in the roster. ''' """
Plug in the roster
"""
if not self.__dict__.has_key('NonBlockingRoster'): if not self.__dict__.has_key('NonBlockingRoster'):
return roster_nb.NonBlockingRoster.get_instance(version=version).PlugIn(self) return roster_nb.NonBlockingRoster.get_instance(version=version).PlugIn(self)
def getRoster(self, on_ready=None, force=False): def getRoster(self, on_ready=None, force=False):
''' Return the Roster instance, previously plugging it in and """
requesting roster from server if needed. ''' Return the Roster instance, previously plugging it in and requesting
roster from server if needed
"""
if self.__dict__.has_key('NonBlockingRoster'): if self.__dict__.has_key('NonBlockingRoster'):
return self.NonBlockingRoster.getRoster(on_ready, force) return self.NonBlockingRoster.getRoster(on_ready, force)
return None return None
def sendPresence(self, jid=None, typ=None, requestRoster=0): def sendPresence(self, jid=None, typ=None, requestRoster=0):
''' Send some specific presence state. """
Can also request roster from server if according agrument is set.''' Send some specific presence state. Can also request roster from server if
according agrument is set
"""
if requestRoster: if requestRoster:
# FIXME: used somewhere? # FIXME: used somewhere?
roster_nb.NonBlockingRoster.get_instance().PlugIn(self) roster_nb.NonBlockingRoster.get_instance().PlugIn(self)
@ -528,31 +557,38 @@ class NonBlockingClient:
############################################################################### ###############################################################################
def RegisterDisconnectHandler(self,handler): def RegisterDisconnectHandler(self,handler):
''' Register handler that will be called on disconnect.''' """
Register handler that will be called on disconnect
"""
self.disconnect_handlers.append(handler) self.disconnect_handlers.append(handler)
def UnregisterDisconnectHandler(self,handler): def UnregisterDisconnectHandler(self,handler):
''' Unregister handler that is called on disconnect.''' """
Unregister handler that is called on disconnect
"""
self.disconnect_handlers.remove(handler) self.disconnect_handlers.remove(handler)
def DisconnectHandler(self): def DisconnectHandler(self):
''' """
Default disconnect handler. Just raises an IOError. If you choosed to use Default disconnect handler. Just raises an IOError. If you choosed to use
this class in your production client, override this method or at least this class in your production client, override this method or at least
unregister it. unregister it.
''' """
raise IOError('Disconnected from server.') raise IOError('Disconnected from server.')
def get_connect_type(self): def get_connect_type(self):
''' Returns connection state. F.e.: None / 'tls' / 'plain+non_sasl'. ''' """
Return connection state. F.e.: None / 'tls' / 'plain+non_sasl'
"""
return self.connected return self.connected
def get_peerhost(self): def get_peerhost(self):
''' """
Gets the ip address of the account, from which is made connection to the Gets the ip address of the account, from which is made connection to the
server (e.g. IP and port of gajim's socket). server (e.g. IP and port of gajim's socket)
We will create listening socket on the same ip We will create listening socket on the same ip
''' """
# FIXME: tuple (ip, port) is expected (and checked for) but port num is # FIXME: tuple (ip, port) is expected (and checked for) but port num is
# useless # useless
return self.socket.peerhost return self.socket.peerhost

View File

@ -15,10 +15,10 @@
## GNU General Public License for more details. ## GNU General Public License for more details.
''' """
Main xmpp decision making logic. Provides library with methods to assign Main xmpp decision making logic. Provides library with methods to assign
different handlers to different XMPP stanzas and namespaces. different handlers to different XMPP stanzas and namespaces
''' """
import simplexml, sys, locale import simplexml, sys, locale
from xml.parsers.expat import ExpatError from xml.parsers.expat import ExpatError
@ -36,7 +36,7 @@ XML_DECLARATION = '<?xml version=\'1.0\'?>'
# FIXME: ugly # FIXME: ugly
class Dispatcher(): class Dispatcher():
''' """
Why is this here - I needed to redefine Dispatcher for BOSH and easiest way Why is this here - I needed to redefine Dispatcher for BOSH and easiest way
was to inherit original Dispatcher (now renamed to XMPPDispatcher). Trouble was to inherit original Dispatcher (now renamed to XMPPDispatcher). Trouble
is that reference used to access dispatcher instance is in Client attribute is that reference used to access dispatcher instance is in Client attribute
@ -46,7 +46,8 @@ class Dispatcher():
If having two kinds of dispatcher will go well, I will rewrite the dispatcher If having two kinds of dispatcher will go well, I will rewrite the dispatcher
references in other scripts references in other scripts
''' """
def PlugIn(self, client_obj, after_SASL=False, old_features=None): def PlugIn(self, client_obj, after_SASL=False, old_features=None):
if client_obj.protocol_type == 'XMPP': if client_obj.protocol_type == 'XMPP':
XMPPDispatcher().PlugIn(client_obj) XMPPDispatcher().PlugIn(client_obj)
@ -57,22 +58,23 @@ class Dispatcher():
@classmethod @classmethod
def get_instance(cls, *args, **kwargs): def get_instance(cls, *args, **kwargs):
''' """
Factory Method for object creation. Factory Method for object creation
Use this instead of directly initializing the class in order to make Use this instead of directly initializing the class in order to make
unit testing much easier. unit testing much easier.
''' """
return cls(*args, **kwargs) return cls(*args, **kwargs)
class XMPPDispatcher(PlugIn): class XMPPDispatcher(PlugIn):
''' """
Handles XMPP stream and is the first who takes control over a fresh stanza. Handles XMPP stream and is the first who takes control over a fresh stanza
Is plugged into NonBlockingClient but can be replugged to restart handled Is plugged into NonBlockingClient but can be replugged to restart handled
stream headers (used by SASL f.e.). stream headers (used by SASL f.e.).
''' """
def __init__(self): def __init__(self):
PlugIn.__init__(self) PlugIn.__init__(self)
self.handlers = {} self.handlers = {}
@ -94,25 +96,24 @@ class XMPPDispatcher(PlugIn):
return repr(outgoingID) return repr(outgoingID)
def dumpHandlers(self): def dumpHandlers(self):
''' """
Return set of user-registered callbacks in it's internal format. Return set of user-registered callbacks in it's internal format. Used
Used within the library to carry user handlers set over Dispatcher within the library to carry user handlers set over Dispatcher replugins
replugins. """
'''
return self.handlers return self.handlers
def restoreHandlers(self, handlers): def restoreHandlers(self, handlers):
''' """
Restores user-registered callbacks structure from dump previously Restore user-registered callbacks structure from dump previously obtained
obtained via dumpHandlers. Used within the library to carry user via dumpHandlers. Used within the library to carry user handlers set over
handlers set over Dispatcher replugins. Dispatcher replugins.
''' """
self.handlers = handlers self.handlers = handlers
def _init(self): def _init(self):
''' """
Registers default namespaces/protocols/handlers. Used internally. Register default namespaces/protocols/handlers. Used internally
''' """
# FIXME: inject dependencies, do not rely that they are defined by our # FIXME: inject dependencies, do not rely that they are defined by our
# owner # owner
self.RegisterNamespace('unknown') self.RegisterNamespace('unknown')
@ -126,10 +127,10 @@ class XMPPDispatcher(PlugIn):
self.on_responses = {} self.on_responses = {}
def plugin(self, owner): def plugin(self, owner):
''' """
Plug the Dispatcher instance into Client class instance and send Plug the Dispatcher instance into Client class instance and send initial
initial stream header. Used internally. stream header. Used internally
''' """
self._init() self._init()
self._owner.lastErrNode = None self._owner.lastErrNode = None
self._owner.lastErr = None self._owner.lastErr = None
@ -140,7 +141,9 @@ class XMPPDispatcher(PlugIn):
self.StreamInit() self.StreamInit()
def plugout(self): def plugout(self):
''' Prepares instance to be destructed. ''' """
Prepare instance to be destructed
"""
self.Stream.dispatch = None self.Stream.dispatch = None
self.Stream.features = None self.Stream.features = None
self.Stream.destroy() self.Stream.destroy()
@ -148,7 +151,9 @@ class XMPPDispatcher(PlugIn):
self.Stream = None self.Stream = None
def StreamInit(self): def StreamInit(self):
''' Send an initial stream header. ''' """
Send an initial stream header
"""
self.Stream = simplexml.NodeBuilder() self.Stream = simplexml.NodeBuilder()
self.Stream.dispatch = self.dispatch self.Stream.dispatch = self.dispatch
self.Stream._dispatch_depth = 2 self.Stream._dispatch_depth = 2
@ -170,15 +175,15 @@ class XMPPDispatcher(PlugIn):
% (tag, ns)) % (tag, ns))
def ProcessNonBlocking(self, data): def ProcessNonBlocking(self, data):
''' """
Check incoming stream for data waiting. Check incoming stream for data waiting
:param data: data received from transports/IO sockets :param data: data received from transports/IO sockets
:return: :return:
1) length of processed data if some data were processed; 1) length of processed data if some data were processed;
2) '0' string if no data were processed but link is alive; 2) '0' string if no data were processed but link is alive;
3) 0 (zero) if underlying connection is closed. 3) 0 (zero) if underlying connection is closed.
''' """
# FIXME: # FIXME:
# When an error occurs we disconnect the transport directly. Client's # When an error occurs we disconnect the transport directly. Client's
# disconnect method will never be called. # disconnect method will never be called.
@ -211,24 +216,25 @@ class XMPPDispatcher(PlugIn):
return len(data) return len(data)
def RegisterNamespace(self, xmlns, order='info'): def RegisterNamespace(self, xmlns, order='info'):
''' """
Creates internal structures for newly registered namespace. Create internal structures for newly registered namespace
You can register handlers for this namespace afterwards. By default You can register handlers for this namespace afterwards. By default
one namespace is already registered one namespace is already registered
(jabber:client or jabber:component:accept depending on context. (jabber:client or jabber:component:accept depending on context.
''' """
log.debug('Registering namespace "%s"' % xmlns) log.debug('Registering namespace "%s"' % xmlns)
self.handlers[xmlns] = {} self.handlers[xmlns] = {}
self.RegisterProtocol('unknown', Protocol, xmlns=xmlns) self.RegisterProtocol('unknown', Protocol, xmlns=xmlns)
self.RegisterProtocol('default', Protocol, xmlns=xmlns) self.RegisterProtocol('default', Protocol, xmlns=xmlns)
def RegisterProtocol(self, tag_name, Proto, xmlns=None, order='info'): def RegisterProtocol(self, tag_name, Proto, xmlns=None, order='info'):
''' """
Used to declare some top-level stanza name to dispatcher. Used to declare some top-level stanza name to dispatcher
Needed to start registering handlers for such stanzas.
Iq, message and presence protocols are registered by default. Needed to start registering handlers for such stanzas. Iq, message and
''' presence protocols are registered by default.
"""
if not xmlns: if not xmlns:
xmlns=self._owner.defaultNamespace xmlns=self._owner.defaultNamespace
log.debug('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns)) log.debug('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns))
@ -236,16 +242,16 @@ class XMPPDispatcher(PlugIn):
def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='', def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='',
makefirst=0, system=0): makefirst=0, system=0):
''' """
Register handler for processing all stanzas for specified namespace. Register handler for processing all stanzas for specified namespace
''' """
self.RegisterHandler('default', handler, typ, ns, xmlns, makefirst, self.RegisterHandler('default', handler, typ, ns, xmlns, makefirst,
system) system)
def RegisterHandler(self, name, handler, typ='', ns='', xmlns=None, def RegisterHandler(self, name, handler, typ='', ns='', xmlns=None,
makefirst=False, system=False): makefirst=False, system=False):
''' """
Register user callback as stanzas handler of declared type. Register user callback as stanzas handler of declared type
Callback arguments: Callback arguments:
dispatcher instance (for replying), incoming return of previous handlers. dispatcher instance (for replying), incoming return of previous handlers.
@ -263,7 +269,7 @@ class XMPPDispatcher(PlugIn):
and " will be called first nevertheless. and " will be called first nevertheless.
:param system: call handler even if NodeProcessed Exception were raised :param system: call handler even if NodeProcessed Exception were raised
already. already.
''' """
if not xmlns: if not xmlns:
xmlns=self._owner.defaultNamespace xmlns=self._owner.defaultNamespace
log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' % log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' %
@ -285,17 +291,19 @@ class XMPPDispatcher(PlugIn):
def RegisterHandlerOnce(self, name, handler, typ='', ns='', xmlns=None, def RegisterHandlerOnce(self, name, handler, typ='', ns='', xmlns=None,
makefirst=0, system=0): makefirst=0, system=0):
''' Unregister handler after first call (not implemented yet). ''' """
Unregister handler after first call (not implemented yet)
"""
# FIXME Drop or implement # FIXME Drop or implement
if not xmlns: if not xmlns:
xmlns = self._owner.defaultNamespace xmlns = self._owner.defaultNamespace
self.RegisterHandler(name, handler, typ, ns, xmlns, makefirst, system) self.RegisterHandler(name, handler, typ, ns, xmlns, makefirst, system)
def UnregisterHandler(self, name, handler, typ='', ns='', xmlns=None): def UnregisterHandler(self, name, handler, typ='', ns='', xmlns=None):
''' """
Unregister handler. "typ" and "ns" must be specified exactly the same as Unregister handler. "typ" and "ns" must be specified exactly the same as
with registering. with registering.
''' """
if not xmlns: if not xmlns:
xmlns = self._owner.defaultNamespace xmlns = self._owner.defaultNamespace
if not typ and not ns: if not typ and not ns:
@ -314,58 +322,59 @@ class XMPPDispatcher(PlugIn):
pass pass
def RegisterDefaultHandler(self, handler): def RegisterDefaultHandler(self, handler):
''' """
Specify the handler that will be used if no NodeProcessed exception were Specify the handler that will be used if no NodeProcessed exception were
raised. This is returnStanzaHandler by default. raised. This is returnStanzaHandler by default.
''' """
self._defaultHandler = handler self._defaultHandler = handler
def RegisterEventHandler(self, handler): def RegisterEventHandler(self, handler):
''' """
Register handler that will process events. F.e. Register handler that will process events. F.e. "FILERECEIVED" event. See
"FILERECEIVED" event. See common/connection: _event_dispatcher() common/connection: _event_dispatcher()
''' """
self._eventHandler = handler self._eventHandler = handler
def returnStanzaHandler(self, conn, stanza): def returnStanzaHandler(self, conn, stanza):
''' """
Return stanza back to the sender with <feature-not-implemented/> error set Return stanza back to the sender with <feature-not-implemented/> error
''' set
"""
if stanza.getType() in ('get','set'): if stanza.getType() in ('get','set'):
conn._owner.send(Error(stanza, ERR_FEATURE_NOT_IMPLEMENTED)) conn._owner.send(Error(stanza, ERR_FEATURE_NOT_IMPLEMENTED))
def RegisterCycleHandler(self, handler): def RegisterCycleHandler(self, handler):
''' """
Register handler that will be called on every Dispatcher.Process() call. Register handler that will be called on every Dispatcher.Process() call
''' """
if handler not in self._cycleHandlers: if handler not in self._cycleHandlers:
self._cycleHandlers.append(handler) self._cycleHandlers.append(handler)
def UnregisterCycleHandler(self, handler): def UnregisterCycleHandler(self, handler):
''' """
Unregister handler that will is called on every Dispatcher.Process() call Unregister handler that will is called on every Dispatcher.Process() call
''' """
if handler in self._cycleHandlers: if handler in self._cycleHandlers:
self._cycleHandlers.remove(handler) self._cycleHandlers.remove(handler)
def Event(self, realm, event, data): def Event(self, realm, event, data):
''' """
Raise some event. Raise some event
:param realm: scope of event. Usually a namespace. :param realm: scope of event. Usually a namespace.
:param event: the event itself. F.e. "SUCCESSFUL SEND". :param event: the event itself. F.e. "SUCCESSFUL SEND".
:param data: data that comes along with event. Depends on event. :param data: data that comes along with event. Depends on event.
''' """
if self._eventHandler: if self._eventHandler:
self._eventHandler(realm, event, data) self._eventHandler(realm, event, data)
else: else:
log.warning('Received unhandled event: %s' % event) log.warning('Received unhandled event: %s' % event)
def dispatch(self, stanza, session=None, direct=0): def dispatch(self, stanza, session=None, direct=0):
''' """
Main procedure that performs XMPP stanza recognition and calling Main procedure that performs XMPP stanza recognition and calling
apppropriate handlers for it. Called by simplexml. apppropriate handlers for it. Called by simplexml
''' """
# FIXME: Where do we set session and direct. Why? What are those intended # FIXME: Where do we set session and direct. Why? What are those intended
# to do? # to do?
@ -450,9 +459,9 @@ class XMPPDispatcher(PlugIn):
self._defaultHandler(session, stanza) self._defaultHandler(session, stanza)
def _WaitForData(self, data): def _WaitForData(self, data):
''' """
Internal wrapper around ProcessNonBlocking. Will check for Internal wrapper around ProcessNonBlocking. Will check for
''' """
if data is None: if data is None:
return return
res = self.ProcessNonBlocking(data) res = self.ProcessNonBlocking(data)
@ -481,12 +490,12 @@ class XMPPDispatcher(PlugIn):
del self._expected[_id] del self._expected[_id]
def SendAndWaitForResponse(self, stanza, timeout=None, func=None, args=None): def SendAndWaitForResponse(self, stanza, timeout=None, func=None, args=None):
''' """
Send stanza and wait for recipient's response to it. Will call transports Send stanza and wait for recipient's response to it. Will call transports
on_timeout callback if response is not retrieved in time. on_timeout callback if response is not retrieved in time
Be aware: Only timeout of latest call of SendAndWait is active. Be aware: Only timeout of latest call of SendAndWait is active.
''' """
if timeout is None: if timeout is None:
timeout = DEFAULT_TIMEOUT_SECONDS timeout = DEFAULT_TIMEOUT_SECONDS
_waitid = self.send(stanza) _waitid = self.send(stanza)
@ -499,15 +508,17 @@ class XMPPDispatcher(PlugIn):
return _waitid return _waitid
def SendAndCallForResponse(self, stanza, func=None, args=None): def SendAndCallForResponse(self, stanza, func=None, args=None):
''' Put stanza on the wire and call back when recipient replies. """
Additional callback arguments can be specified in args. ''' Put stanza on the wire and call back when recipient replies. Additional
callback arguments can be specified in args
"""
self.SendAndWaitForResponse(stanza, 0, func, args) self.SendAndWaitForResponse(stanza, 0, func, args)
def send(self, stanza, now=False): def send(self, stanza, now=False):
''' """
Wraps transports send method when plugged into NonBlockingClient. Wrap transports send method when plugged into NonBlockingClient. Makes
Makes sure stanzas get ID and from tag. sure stanzas get ID and from tag.
''' """
ID = None ID = None
if type(stanza) not in [type(''), type(u'')]: if type(stanza) not in [type(''), type(u'')]:
if isinstance(stanza, Protocol): if isinstance(stanza, Protocol):
@ -529,7 +540,9 @@ class BOSHDispatcher(XMPPDispatcher):
XMPPDispatcher.PlugIn(self, owner) XMPPDispatcher.PlugIn(self, owner)
def StreamInit(self): def StreamInit(self):
''' Send an initial stream header. ''' """
Send an initial stream header
"""
self.Stream = simplexml.NodeBuilder() self.Stream = simplexml.NodeBuilder()
self.Stream.dispatch = self.dispatch self.Stream.dispatch = self.dispatch
self.Stream._dispatch_depth = 2 self.Stream._dispatch_depth = 2
@ -549,7 +562,9 @@ class BOSHDispatcher(XMPPDispatcher):
self._owner.Connection.send_init(after_SASL=self.after_SASL) self._owner.Connection.send_init(after_SASL=self.after_SASL)
def StreamTerminate(self): def StreamTerminate(self):
''' Send a stream terminator. ''' """
Send a stream terminator
"""
self._owner.Connection.send_terminator() self._owner.Connection.send_terminator()
def ProcessNonBlocking(self, data=None): def ProcessNonBlocking(self, data=None):

View File

@ -15,10 +15,10 @@
# $Id: features.py,v 1.22 2005/09/30 20:13:04 mikealbon Exp $ # $Id: features.py,v 1.22 2005/09/30 20:13:04 mikealbon Exp $
''' """
Different stuff that wasn't worth separating it into modules Different stuff that wasn't worth separating it into modules
(Registration, Privacy Lists, ...) (Registration, Privacy Lists, ...)
''' """
from protocol import NS_REGISTER, NS_PRIVACY, NS_DATA, Iq, isResultNode, Node from protocol import NS_REGISTER, NS_PRIVACY, NS_DATA, Iq, isResultNode, Node
@ -38,13 +38,13 @@ def _on_default_response(disp, iq, cb):
REGISTER_DATA_RECEIVED = 'REGISTER DATA RECEIVED' REGISTER_DATA_RECEIVED = 'REGISTER DATA RECEIVED'
def getRegInfo(disp, host, info={}, sync=True): def getRegInfo(disp, host, info={}, sync=True):
''' """
Gets registration form from remote host. Info dict can be prefilled Get registration form from remote host. Info dict can be prefilled
:param disp: plugged dispatcher instance :param disp: plugged dispatcher instance
:param info: dict, like {'username':'joey'}. :param info: dict, like {'username':'joey'}.
See JEP-0077 for details. See JEP-0077 for details.
''' """
iq=Iq('get',NS_REGISTER,to=host) iq=Iq('get',NS_REGISTER,to=host)
for i in info.keys(): for i in info.keys():
iq.setTagData(i,info[i]) iq.setTagData(i,info[i])
@ -77,12 +77,12 @@ def _ReceivedRegInfo(con, resp, agent):
con.Event(NS_REGISTER, REGISTER_DATA_RECEIVED, (agent,df,False,'')) con.Event(NS_REGISTER, REGISTER_DATA_RECEIVED, (agent,df,False,''))
def register(disp, host, info, cb): def register(disp, host, info, cb):
''' """
Perform registration on remote server with provided info. Perform registration on remote server with provided info
If registration fails you can get additional info from the dispatcher's If registration fails you can get additional info from the dispatcher's
owner attributes lastErrNode, lastErr and lastErrCode. owner attributes lastErrNode, lastErr and lastErrCode.
''' """
iq=Iq('set', NS_REGISTER, to=host) iq=Iq('set', NS_REGISTER, to=host)
if not isinstance(info, dict): if not isinstance(info, dict):
info=info.asDict() info=info.asDict()
@ -91,17 +91,17 @@ def register(disp, host, info, cb):
disp.SendAndCallForResponse(iq, cb) disp.SendAndCallForResponse(iq, cb)
def unregister(disp, host, cb): def unregister(disp, host, cb):
''' """
Unregisters with host (permanently removes account). Returns true on success Unregisters with host (permanently removes account). Returns true on success
''' """
iq = Iq('set', NS_REGISTER, to=host, payload=[Node('remove')]) iq = Iq('set', NS_REGISTER, to=host, payload=[Node('remove')])
_on_default_response(disp, iq, cb) _on_default_response(disp, iq, cb)
def changePasswordTo(disp, newpassword, host=None, cb = None): def changePasswordTo(disp, newpassword, host=None, cb = None):
''' """
Changes password on specified or current (if not specified) server. Changes password on specified or current (if not specified) server. Returns
Returns true on success. true on success.
''' """
if not host: if not host:
host = disp._owner.Server host = disp._owner.Server
iq = Iq('set',NS_REGISTER,to=host, payload=[Node('username', iq = Iq('set',NS_REGISTER,to=host, payload=[Node('username',
@ -123,10 +123,10 @@ PRIVACY_LIST_RECEIVED = 'PRIVACY LIST RECEIVED'
PRIVACY_LISTS_ACTIVE_DEFAULT = 'PRIVACY LISTS ACTIVE DEFAULT' PRIVACY_LISTS_ACTIVE_DEFAULT = 'PRIVACY LISTS ACTIVE DEFAULT'
def getPrivacyLists(disp): def getPrivacyLists(disp):
''' """
Requests privacy lists from connected server. Request privacy lists from connected server. Returns dictionary of existing
Returns dictionary of existing lists on success. lists on success.
''' """
iq = Iq('get', NS_PRIVACY) iq = Iq('get', NS_PRIVACY)
def _on_response(resp): def _on_response(resp):
dict_ = {'lists': []} dict_ = {'lists': []}
@ -157,10 +157,10 @@ def getActiveAndDefaultPrivacyLists(disp):
disp.SendAndCallForResponse(iq, _on_response) disp.SendAndCallForResponse(iq, _on_response)
def getPrivacyList(disp, listname): def getPrivacyList(disp, listname):
''' """
Requests specific privacy list listname. Returns list of XML nodes (rules) Request specific privacy list listname. Returns list of XML nodes (rules)
taken from the server responce. taken from the server responce.
''' """
def _on_response(resp): def _on_response(resp):
if not isResultNode(resp): if not isResultNode(resp):
disp.Event(NS_PRIVACY, PRIVACY_LIST_RECEIVED, (False)) disp.Event(NS_PRIVACY, PRIVACY_LIST_RECEIVED, (False))
@ -170,10 +170,10 @@ def getPrivacyList(disp, listname):
disp.SendAndCallForResponse(iq, _on_response) disp.SendAndCallForResponse(iq, _on_response)
def setActivePrivacyList(disp, listname=None, typ='active', cb=None): def setActivePrivacyList(disp, listname=None, typ='active', cb=None):
''' """
Switches privacy list 'listname' to specified type. Switch privacy list 'listname' to specified type. By default the type is
By default the type is 'active'. Returns true on success. 'active'. Returns true on success.
''' """
if listname: if listname:
attrs={'name':listname} attrs={'name':listname}
else: else:
@ -182,15 +182,20 @@ def setActivePrivacyList(disp, listname=None, typ='active', cb=None):
_on_default_response(disp, iq, cb) _on_default_response(disp, iq, cb)
def setDefaultPrivacyList(disp, listname=None): def setDefaultPrivacyList(disp, listname=None):
''' Sets the default privacy list as 'listname'. Returns true on success. ''' """
Set the default privacy list as 'listname'. Returns true on success
"""
return setActivePrivacyList(disp, listname,'default') return setActivePrivacyList(disp, listname,'default')
def setPrivacyList(disp, listname, tags): def setPrivacyList(disp, listname, tags):
''' """
Set the ruleset. Set the ruleset
'list' should be the simpleXML node formatted according to RFC 3921 (XMPP-IM) I.e. Node('list',{'name':listname},payload=[...]). Returns true on success. 'list' should be the simpleXML node formatted according to RFC 3921
''' (XMPP-IM) I.e. Node('list',{'name':listname},payload=[...]).
Returns true on success.
"""
iq = Iq('set', NS_PRIVACY, xmlns = '') iq = Iq('set', NS_PRIVACY, xmlns = '')
list_query = iq.getTag('query').setTag('list', {'name': listname}) list_query = iq.getTag('query').setTag('list', {'name': listname})
for item in tags: for item in tags:

View File

@ -12,10 +12,12 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
'''
Idlequeues are Gajim's network heartbeat. Transports can be plugged as """
idle objects and be informed about possible IO. Idlequeues are Gajim's network heartbeat. Transports can be plugged as idle
''' objects and be informed about possible IO
"""
import os import os
import select import select
import logging import logging
@ -45,7 +47,9 @@ IS_CLOSED = 16 # channel closed
def get_idlequeue(): def get_idlequeue():
''' Get an appropriate idlequeue ''' """
Get an appropriate idlequeue
"""
if os.name == 'nt': if os.name == 'nt':
# gobject.io_add_watch does not work on windows # gobject.io_add_watch does not work on windows
return SelectIdleQueue() return SelectIdleQueue()
@ -59,34 +63,44 @@ def get_idlequeue():
class IdleObject: class IdleObject:
''' """
Idle listener interface. Listed methods are called by IdleQueue. Idle listener interface. Listed methods are called by IdleQueue.
''' """
def __init__(self): def __init__(self):
self.fd = -1 #: filedescriptor, must be unique for each IdleObject self.fd = -1 #: filedescriptor, must be unique for each IdleObject
def pollend(self): def pollend(self):
''' called on stream failure ''' """
Called on stream failure
"""
pass pass
def pollin(self): def pollin(self):
''' called on new read event ''' """
Called on new read event
"""
pass pass
def pollout(self): def pollout(self):
''' called on new write event (connect in sockets is a pollout) ''' """
Called on new write event (connect in sockets is a pollout)
"""
pass pass
def read_timeout(self): def read_timeout(self):
''' called when timeout happened ''' """
Called when timeout happened
"""
pass pass
class IdleCommand(IdleObject): class IdleCommand(IdleObject):
''' """
Can be subclassed to execute commands asynchronously by the idlequeue. Can be subclassed to execute commands asynchronously by the idlequeue.
Result will be optained via file descriptor of created pipe Result will be optained via file descriptor of created pipe
''' """
def __init__(self, on_result): def __init__(self, on_result):
IdleObject.__init__(self) IdleObject.__init__(self)
# how long (sec.) to wait for result ( 0 - forever ) # how long (sec.) to wait for result ( 0 - forever )
@ -111,7 +125,9 @@ class IdleCommand(IdleObject):
return ['echo', 'da'] return ['echo', 'da']
def _compose_command_line(self): def _compose_command_line(self):
''' return one line representation of command and its arguments ''' """
Return one line representation of command and its arguments
"""
return reduce(lambda left, right: left + ' ' + right, return reduce(lambda left, right: left + ' ' + right,
self._compose_command_args()) self._compose_command_args())
@ -187,7 +203,7 @@ class IdleCommand(IdleObject):
class IdleQueue: class IdleQueue:
''' """
IdleQueue provide three distinct time based features. Uses select.poll() IdleQueue provide three distinct time based features. Uses select.poll()
1. Alarm timeout: Execute a callback after foo seconds 1. Alarm timeout: Execute a callback after foo seconds
@ -195,7 +211,8 @@ class IdleQueue:
has been set, but not removed in time. has been set, but not removed in time.
3. Check file descriptor of plugged objects for read, write and error 3. Check file descriptor of plugged objects for read, write and error
events events
''' """
# (timeout, boolean) # (timeout, boolean)
# Boolean is True if timeout is specified in seconds, False means miliseconds # Boolean is True if timeout is specified in seconds, False means miliseconds
PROCESS_TIMEOUT = (200, False) PROCESS_TIMEOUT = (200, False)
@ -215,13 +232,15 @@ class IdleQueue:
self._init_idle() self._init_idle()
def _init_idle(self): def _init_idle(self):
''' Hook method for subclassed. Will be called by __init__. ''' """
Hook method for subclassed. Will be called by __init__
"""
self.selector = select.poll() self.selector = select.poll()
def set_alarm(self, alarm_cb, seconds): def set_alarm(self, alarm_cb, seconds):
''' """
Sets up a new alarm. alarm_cb will be called after specified seconds. Set up a new alarm. alarm_cb will be called after specified seconds.
''' """
alarm_time = self.current_time() + seconds alarm_time = self.current_time() + seconds
# almost impossible, but in case we have another alarm_cb at this time # almost impossible, but in case we have another alarm_cb at this time
if alarm_time in self.alarms: if alarm_time in self.alarms:
@ -231,10 +250,10 @@ class IdleQueue:
return alarm_time return alarm_time
def remove_alarm(self, alarm_cb, alarm_time): def remove_alarm(self, alarm_cb, alarm_time):
''' """
Removes alarm callback alarm_cb scheduled on alarm_time. Remove alarm callback alarm_cb scheduled on alarm_time. Returns True if
Returns True if it was removed sucessfully, otherwise False it was removed sucessfully, otherwise False
''' """
if not alarm_time in self.alarms: if not alarm_time in self.alarms:
return False return False
i = -1 i = -1
@ -251,7 +270,9 @@ class IdleQueue:
return False return False
def remove_timeout(self, fd, timeout=None): def remove_timeout(self, fd, timeout=None):
''' Removes the read timeout ''' """
Remove the read timeout
"""
log.info('read timeout removed for fd %s' % fd) log.info('read timeout removed for fd %s' % fd)
if fd in self.read_timeouts: if fd in self.read_timeouts:
if timeout: if timeout:
@ -263,12 +284,12 @@ class IdleQueue:
del(self.read_timeouts[fd]) del(self.read_timeouts[fd])
def set_read_timeout(self, fd, seconds, func=None): def set_read_timeout(self, fd, seconds, func=None):
''' """
Sets a new timeout. If it is not removed after specified seconds, Seta a new timeout. If it is not removed after specified seconds,
func or obj.read_timeout() will be called. func or obj.read_timeout() will be called
A filedescriptor fd can have several timeouts. A filedescriptor fd can have several timeouts.
''' """
log_txt = 'read timeout set for fd %s on %s seconds' % (fd, seconds) log_txt = 'read timeout set for fd %s on %s seconds' % (fd, seconds)
if func: if func:
log_txt += ' with function ' + str(func) log_txt += ' with function ' + str(func)
@ -280,10 +301,10 @@ class IdleQueue:
self.read_timeouts[fd] = {timeout: func} self.read_timeouts[fd] = {timeout: func}
def _check_time_events(self): def _check_time_events(self):
''' """
Execute and remove alarm callbacks and execute func() or read_timeout() Execute and remove alarm callbacks and execute func() or read_timeout()
for plugged objects if specified time has ellapsed. for plugged objects if specified time has ellapsed
''' """
log.info('check time evs') log.info('check time evs')
current_time = self.current_time() current_time = self.current_time()
@ -313,13 +334,13 @@ class IdleQueue:
del(self.alarms[alarm_time]) del(self.alarms[alarm_time])
def plug_idle(self, obj, writable=True, readable=True): def plug_idle(self, obj, writable=True, readable=True):
''' """
Plug an IdleObject into idlequeue. Filedescriptor fd must be set. Plug an IdleObject into idlequeue. Filedescriptor fd must be set
:param obj: the IdleObject :param obj: the IdleObject
:param writable: True if obj has data to sent :param writable: True if obj has data to sent
:param readable: True if obj expects data to be reiceived :param readable: True if obj expects data to be reiceived
''' """
if obj.fd == -1: if obj.fd == -1:
return return
if obj.fd in self.queue: if obj.fd in self.queue:
@ -339,11 +360,15 @@ class IdleQueue:
self._add_idle(obj.fd, flags) self._add_idle(obj.fd, flags)
def _add_idle(self, fd, flags): def _add_idle(self, fd, flags):
''' Hook method for subclasses, called by plug_idle ''' """
Hook method for subclasses, called by plug_idle
"""
self.selector.register(fd, flags) self.selector.register(fd, flags)
def unplug_idle(self, fd): def unplug_idle(self, fd):
''' Removed plugged IdleObject, specified by filedescriptor fd. ''' """
Remove plugged IdleObject, specified by filedescriptor fd
"""
if fd in self.queue: if fd in self.queue:
del(self.queue[fd]) del(self.queue[fd])
self._remove_idle(fd) self._remove_idle(fd)
@ -353,7 +378,9 @@ class IdleQueue:
return time() return time()
def _remove_idle(self, fd): def _remove_idle(self, fd):
''' Hook method for subclassed, called by unplug_idle ''' """
Hook method for subclassed, called by unplug_idle
"""
self.selector.unregister(fd) self.selector.unregister(fd)
def _process_events(self, fd, flags): def _process_events(self, fd, flags):
@ -379,13 +406,13 @@ class IdleQueue:
return False return False
def process(self): def process(self):
''' """
Process idlequeue. Check for any pending timeout or alarm events. Process idlequeue. Check for any pending timeout or alarm events. Call
Call IdleObjects on possible and requested read, write and error events IdleObjects on possible and requested read, write and error events on
on their file descriptors. their file descriptors
Call this in regular intervals. Call this in regular intervals.
''' """
if not self.queue: if not self.queue:
# check for timeouts/alert also when there are no active fds # check for timeouts/alert also when there are no active fds
self._check_time_events() self._check_time_events()
@ -403,24 +430,26 @@ class IdleQueue:
class SelectIdleQueue(IdleQueue): class SelectIdleQueue(IdleQueue):
''' """
Extends IdleQueue to use select.select() for polling Extends IdleQueue to use select.select() for polling
This class exisists for the sake of gtk2.8 on windows, which This class exisists for the sake of gtk2.8 on windows, which doesn't seem to
doesn't seem to support io_add_watch properly (yet) support io_add_watch properly (yet)
''' """
def _init_idle(self): def _init_idle(self):
''' """
Creates a dict, which maps file/pipe/sock descriptor to glib event id Create a dict, which maps file/pipe/sock descriptor to glib event id
''' """
self.read_fds = {} self.read_fds = {}
self.write_fds = {} self.write_fds = {}
self.error_fds = {} self.error_fds = {}
def _add_idle(self, fd, flags): def _add_idle(self, fd, flags):
''' this method is called when we plug a new idle object. """
Remove descriptor to read/write/error lists, according flags This method is called when we plug a new idle object. Remove descriptor
''' to read/write/error lists, according flags
"""
if flags & 3: if flags & 3:
self.read_fds[fd] = fd self.read_fds[fd] = fd
if flags & 4: if flags & 4:
@ -428,9 +457,10 @@ class SelectIdleQueue(IdleQueue):
self.error_fds[fd] = fd self.error_fds[fd] = fd
def _remove_idle(self, fd): def _remove_idle(self, fd):
''' this method is called when we unplug a new idle object. """
Remove descriptor from read/write/error lists This method is called when we unplug a new idle object. Remove descriptor
''' from read/write/error lists
"""
if fd in self.read_fds: if fd in self.read_fds:
del(self.read_fds[fd]) del(self.read_fds[fd])
if fd in self.write_fds: if fd in self.write_fds:
@ -466,27 +496,29 @@ class SelectIdleQueue(IdleQueue):
class GlibIdleQueue(IdleQueue): class GlibIdleQueue(IdleQueue):
''' """
Extends IdleQueue to use glib io_add_wath, instead of select/poll Extends IdleQueue to use glib io_add_wath, instead of select/poll In another
In another 'non gui' implementation of Gajim IdleQueue can be used safetly. 'non gui' implementation of Gajim IdleQueue can be used safetly
''' """
# (timeout, boolean) # (timeout, boolean)
# Boolean is True if timeout is specified in seconds, False means miliseconds # Boolean is True if timeout is specified in seconds, False means miliseconds
PROCESS_TIMEOUT = (2, True) PROCESS_TIMEOUT = (2, True)
def _init_idle(self): def _init_idle(self):
''' """
Creates a dict, which maps file/pipe/sock descriptor to glib event id Creates a dict, which maps file/pipe/sock descriptor to glib event id
''' """
self.events = {} self.events = {}
# time() is already called in glib, we just get the last value # time() is already called in glib, we just get the last value
# overrides IdleQueue.current_time() # overrides IdleQueue.current_time()
self.current_time = gobject.get_current_time self.current_time = gobject.get_current_time
def _add_idle(self, fd, flags): def _add_idle(self, fd, flags):
''' this method is called when we plug a new idle object. """
Start listening for events from fd This method is called when we plug a new idle object. Start listening for
''' events from fd
"""
res = gobject.io_add_watch(fd, flags, self._process_events, res = gobject.io_add_watch(fd, flags, self._process_events,
priority=gobject.PRIORITY_LOW) priority=gobject.PRIORITY_LOW)
# store the id of the watch, so that we can remove it on unplug # store the id of the watch, so that we can remove it on unplug
@ -501,9 +533,10 @@ class GlibIdleQueue(IdleQueue):
raise raise
def _remove_idle(self, fd): def _remove_idle(self, fd):
''' this method is called when we unplug a new idle object. """
Stop listening for events from fd This method is called when we unplug a new idle object. Stop listening
''' for events from fd
"""
if not fd in self.events: if not fd in self.events:
return return
gobject.source_remove(self.events[fd]) gobject.source_remove(self.events[fd])

View File

@ -14,33 +14,34 @@
# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $ # $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $
''' """
Provides PlugIn class functionality to develop extentions for xmpppy. Provides PlugIn class functionality to develop extentions for xmpppy
''' """
import logging import logging
log = logging.getLogger('gajim.c.x.plugin') log = logging.getLogger('gajim.c.x.plugin')
class PlugIn: class PlugIn:
''' """
Abstract xmpppy plugin infrastructure code, providing plugging in/out and Abstract xmpppy plugin infrastructure code, providing plugging in/out and
debugging functionality. debugging functionality
Inherit to develop pluggable objects. No code change on the owner class Inherit to develop pluggable objects. No code change on the owner class
required (the object where we plug into) required (the object where we plug into)
For every instance of PlugIn class the 'owner' is the class in what the plug For every instance of PlugIn class the 'owner' is the class in what the plug
was plugged. was plugged.
''' """
def __init__(self): def __init__(self):
self._exported_methods=[] self._exported_methods=[]
def PlugIn(self, owner): def PlugIn(self, owner):
''' """
Attach to owner and register ourself and our _exported_methods in it. Attach to owner and register ourself and our _exported_methods in it.
If defined by a subclass, call self.plugin(owner) to execute hook If defined by a subclass, call self.plugin(owner) to execute hook
code after plugging. code after plugging
''' """
self._owner=owner self._owner=owner
log.info('Plugging %s __INTO__ %s' % (self, self._owner)) log.info('Plugging %s __INTO__ %s' % (self, self._owner))
if self.__class__.__name__ in owner.__dict__: if self.__class__.__name__ in owner.__dict__:
@ -63,11 +64,11 @@ class PlugIn:
return self.plugin(owner) return self.plugin(owner)
def PlugOut(self): def PlugOut(self):
''' """
Unregister our _exported_methods from owner and detach from it. Unregister our _exported_methods from owner and detach from it.
If defined by a subclass, call self.plugout() after unplugging to execute If defined by a subclass, call self.plugout() after unplugging to execute
hook code. hook code
''' """
log.info('Plugging %s __OUT__ of %s.' % (self, self._owner)) log.info('Plugging %s __OUT__ of %s.' % (self, self._owner))
for method in self._exported_methods: for method in self._exported_methods:
del self._owner.__dict__[method.__name__] del self._owner.__dict__[method.__name__]
@ -85,13 +86,13 @@ class PlugIn:
@classmethod @classmethod
def get_instance(cls, *args, **kwargs): def get_instance(cls, *args, **kwargs):
''' """
Factory Method for object creation. Factory Method for object creation
Use this instead of directly initializing the class in order to make Use this instead of directly initializing the class in order to make
unit testing easier. For testing, this method can be patched to inject unit testing easier. For testing, this method can be patched to inject
mock objects. mock objects.
''' """
return cls(*args, **kwargs) return cls(*args, **kwargs)
# vim: se ts=3: # vim: se ts=3:

File diff suppressed because it is too large Load Diff