More doc-string (and not only) refactoring
This commit is contained in:
parent
99472b1702
commit
6d0f28c47d
|
@ -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:
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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])
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue