A portion of doc-string refactoring

This commit is contained in:
Alexander Cherniuk 2009-11-26 12:21:43 +02:00
parent a9a442c01c
commit cea7c66f75
11 changed files with 637 additions and 364 deletions

View file

@ -213,7 +213,9 @@ if gajim.HAVE_GPG:
return self.get_keys(True) return self.get_keys(True)
def _stripHeaderFooter(self, data): def _stripHeaderFooter(self, data):
"""Remove header and footer from data""" """
Remove header and footer from data
"""
if not data: return '' if not data: return ''
lines = data.split('\n') lines = data.split('\n')
while lines[0] != '': while lines[0] != '':
@ -229,7 +231,9 @@ if gajim.HAVE_GPG:
return line return line
def _addHeaderFooter(self, data, type_): def _addHeaderFooter(self, data, type_):
"""Add header and footer from data""" """
Add header and footer from data
"""
out = "-----BEGIN PGP %s-----\n" % type_ out = "-----BEGIN PGP %s-----\n" % type_
out = out + "Version: PGP\n" out = out + "Version: PGP\n"
out = out + "\n" out = out + "\n"

View file

@ -21,7 +21,8 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
"""Interface to GNU Privacy Guard (GnuPG) """
Interface to GNU Privacy Guard (GnuPG)
GnuPGInterface is a Python module to interface with GnuPG. GnuPGInterface is a Python module to interface with GnuPG.
It concentrates on interacting with GnuPG via filehandles, It concentrates on interacting with GnuPG via filehandles,
@ -249,7 +250,8 @@ _fd_options = { 'passphrase': '--passphrase-fd',
'command': '--command-fd' } 'command': '--command-fd' }
class GnuPG: class GnuPG:
"""Class instances represent GnuPG. """
Class instances represent GnuPG
Instance attributes of a GnuPG object are: Instance attributes of a GnuPG object are:
@ -275,8 +277,10 @@ class GnuPG:
self.options = Options() self.options = Options()
def run(self, gnupg_commands, args=None, create_fhs=None, attach_fhs=None): def run(self, gnupg_commands, args=None, create_fhs=None, attach_fhs=None):
"""Calls GnuPG with the list of string commands gnupg_commands, """
complete with prefixing dashes. Calls GnuPG with the list of string commands gnupg_commands, complete
with prefixing dashes
For example, gnupg_commands could be For example, gnupg_commands could be
'["--sign", "--encrypt"]' '["--sign", "--encrypt"]'
Returns a GnuPGInterface.Process object. Returns a GnuPGInterface.Process object.
@ -336,7 +340,6 @@ class GnuPG:
is a FileObject connected to GnuPG's standard input, is a FileObject connected to GnuPG's standard input,
and can be written to. and can be written to.
""" """
if args is None: args = [] if args is None: args = []
if create_fhs is None: create_fhs = [] if create_fhs is None: create_fhs = []
if attach_fhs is None: attach_fhs = {} if attach_fhs is None: attach_fhs = {}
@ -367,9 +370,10 @@ class GnuPG:
def _attach_fork_exec(self, gnupg_commands, args, create_fhs, attach_fhs): def _attach_fork_exec(self, gnupg_commands, args, create_fhs, attach_fhs):
"""This is like run(), but without the passphrase-helping """
(note that run() calls this).""" This is like run(), but without the passphrase-helping (note that run()
calls this)
"""
process = Process() process = Process()
for fh_name in create_fhs + attach_fhs.keys(): for fh_name in create_fhs + attach_fhs.keys():
@ -404,7 +408,9 @@ class GnuPG:
def _as_parent(self, process): def _as_parent(self, process):
"""Stuff run after forking in parent""" """
Stuff run after forking in parent
"""
for k, p in process._pipes.items(): for k, p in process._pipes.items():
if not p.direct: if not p.direct:
os.close(p.child) os.close(p.child)
@ -417,7 +423,9 @@ class GnuPG:
def _as_child(self, process, gnupg_commands, args): def _as_child(self, process, gnupg_commands, args):
"""Stuff run after forking in child""" """
Stuff run after forking in child
"""
# child # child
for std in _stds: for std in _stds:
p = process._pipes[std] p = process._pipes[std]
@ -444,7 +452,10 @@ class GnuPG:
class Pipe: class Pipe:
"""simple struct holding stuff about pipes we use""" """
Simple struct holding stuff about pipes we use
"""
def __init__(self, parent, child, direct): def __init__(self, parent, child, direct):
self.parent = parent self.parent = parent
self.child = child self.child = child
@ -452,7 +463,8 @@ class Pipe:
class Options: class Options:
"""Objects of this class encompass options passed to GnuPG. """
Objects of this class encompass options passed to GnuPG.
This class is responsible for determining command-line arguments This class is responsible for determining command-line arguments
which are based on options. It can be said that a GnuPG which are based on options. It can be said that a GnuPG
object has-a Options object in its options attribute. object has-a Options object in its options attribute.
@ -522,7 +534,6 @@ class Options:
>>> gnupg.options.get_args() >>> gnupg.options.get_args()
['--armor', '--recipient', 'Alice', '--recipient', 'Bob', '--no-secmem-warning'] ['--armor', '--recipient', 'Alice', '--recipient', 'Bob', '--no-secmem-warning']
""" """
def __init__(self): def __init__(self):
# booleans # booleans
self.armor = 0 self.armor = 0
@ -558,12 +569,15 @@ class Options:
self.extra_args = [] self.extra_args = []
def get_args( self ): def get_args( self ):
"""Generate a list of GnuPG arguments based upon attributes.""" """
Generate a list of GnuPG arguments based upon attributes
"""
return self.get_meta_args() + self.get_standard_args() + self.extra_args return self.get_meta_args() + self.get_standard_args() + self.extra_args
def get_standard_args( self ): def get_standard_args( self ):
"""Generate a list of standard, non-meta or extra arguments""" """
Generate a list of standard, non-meta or extra arguments
"""
args = [] args = []
if self.homedir is not None: if self.homedir is not None:
args.extend( [ '--homedir', self.homedir ] ) args.extend( [ '--homedir', self.homedir ] )
@ -595,7 +609,9 @@ class Options:
return args return args
def get_meta_args( self ): def get_meta_args( self ):
"""Get a list of generated meta-arguments""" """
Get a list of generated meta-arguments
"""
args = [] args = []
if self.meta_pgp_5_compatible: args.extend( [ '--compress-algo', '1', if self.meta_pgp_5_compatible: args.extend( [ '--compress-algo', '1',
@ -608,8 +624,9 @@ class Options:
class Process: class Process:
"""Objects of this class encompass properties of a GnuPG """
process spawned by GnuPG.run(). Objects of this class encompass properties of a GnuPG process spawned by
GnuPG.run()
# gnupg is a GnuPG object # gnupg is a GnuPG object
process = gnupg.run( [ '--decrypt' ], stdout = 1 ) process = gnupg.run( [ '--decrypt' ], stdout = 1 )
@ -637,9 +654,10 @@ class Process:
self._waited = None self._waited = None
def wait(self): def wait(self):
"""Wait on the process to exit, allowing for child cleanup. """
Will raise an IOError if the process exits non-zero.""" Wait on the process to exit, allowing for child cleanup. Will raise an
IOError if the process exits non-zero
"""
e = os.waitpid(self.pid, 0)[1] e = os.waitpid(self.pid, 0)[1]
if e != 0: if e != 0:
raise IOError, "GnuPG exited non-zero, with code %d" % (e << 8) raise IOError, "GnuPG exited non-zero, with code %d" % (e << 8)

View file

@ -19,14 +19,14 @@
## ##
class Account(object): class Account(object):
def __init__(self, name, contacts, gc_contacts): def __init__(self, name, contacts, gc_contacts):
self.name = name self.name = name
self.contacts = contacts self.contacts = contacts
self.gc_contacts = gc_contacts self.gc_contacts = gc_contacts
def __repr__(self): def __repr__(self):
return self.name return self.name
def __hash__(self): def __hash__(self):
return self.name.__hash__() return hash(self.name)

View file

@ -20,9 +20,11 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
''' Atom (rfc 4287) feed parser, used to read data from atom-over-pubsub transports """
Atom (rfc 4287) feed parser, used to read data from atom-over-pubsub transports
and services. Very simple. Actually implements only atom:entry. Implement more features and services. Very simple. Actually implements only atom:entry. Implement more features
if you need. ''' if you need
"""
# suggestion: rewrite functions that return dates to return standard python time tuples, # suggestion: rewrite functions that return dates to return standard python time tuples,
# exteneded to contain timezone # exteneded to contain timezone
@ -31,8 +33,11 @@ import xmpp
import time import time
class PersonConstruct(xmpp.Node, object): class PersonConstruct(xmpp.Node, object):
''' Not used for now, as we don't need authors/contributors in pubsub.com feeds. """
They rarely exist there. ''' Not used for now, as we don't need authors/contributors in pubsub.com feeds.
They rarely exist there
"""
def __init__(self, node): def __init__(self, node):
''' Create person construct from node. ''' ''' Create person construct from node. '''
xmpp.Node.__init__(self, node=node) xmpp.Node.__init__(self, node=node)
@ -60,15 +65,17 @@ class PersonConstruct(xmpp.Node, object):
class Entry(xmpp.Node, object): class Entry(xmpp.Node, object):
def __init__(self, node=None): def __init__(self, node=None):
''' Create new atom entry object. '''
xmpp.Node.__init__(self, 'entry', node=node) xmpp.Node.__init__(self, 'entry', node=node)
def __repr__(self): def __repr__(self):
return '<Atom:Entry object of id="%r">' % self.getAttr('id') return '<Atom:Entry object of id="%r">' % self.getAttr('id')
class OldEntry(xmpp.Node, object): class OldEntry(xmpp.Node, object):
''' Parser for feeds from pubsub.com. They use old Atom 0.3 format with """
their extensions. ''' Parser for feeds from pubsub.com. They use old Atom 0.3 format with their
extensions
"""
def __init__(self, node=None): def __init__(self, node=None):
''' Create new Atom 0.3 entry object. ''' ''' Create new Atom 0.3 entry object. '''
xmpp.Node.__init__(self, 'entry', node=node) xmpp.Node.__init__(self, 'entry', node=node)
@ -77,8 +84,10 @@ class OldEntry(xmpp.Node, object):
return '<Atom0.3:Entry object of id="%r">' % self.getAttr('id') return '<Atom0.3:Entry object of id="%r">' % self.getAttr('id')
def get_feed_title(self): def get_feed_title(self):
''' Returns title of feed, where the entry was created. The result is the feed name """
concatenated with source-feed title. ''' Return title of feed, where the entry was created. The result is the feed
name concatenated with source-feed title
"""
if self.parent is not None: if self.parent is not None:
main_feed = self.parent.getTagData('title') main_feed = self.parent.getTagData('title')
else: else:
@ -104,7 +113,9 @@ class OldEntry(xmpp.Node, object):
which delivered this entry. ''') which delivered this entry. ''')
def get_feed_link(self): def get_feed_link(self):
''' Get source link ''' """
Get source link
"""
try: try:
return self.getTag('feed').getTags('link',{'rel':'alternate'})[1].getData() return self.getTag('feed').getTags('link',{'rel':'alternate'})[1].getData()
except Exception: except Exception:
@ -114,15 +125,19 @@ class OldEntry(xmpp.Node, object):
''' Link to main webpage of the feed. ''') ''' Link to main webpage of the feed. ''')
def get_title(self): def get_title(self):
''' Get an entry's title. ''' """
Get an entry's title
"""
return self.getTagData('title') return self.getTagData('title')
title = property(get_title, None, None, title = property(get_title, None, None,
''' Entry's title. ''') ''' Entry's title. ''')
def get_uri(self): def get_uri(self):
''' Get the uri the entry points to (entry's first link element with rel='alternate' """
or without rel attribute). ''' Get the uri the entry points to (entry's first link element with
rel='alternate' or without rel attribute)
"""
for element in self.getTags('link'): for element in self.getTags('link'):
if 'rel' in element.attrs and element.attrs['rel']!='alternate': continue if 'rel' in element.attrs and element.attrs['rel']!='alternate': continue
try: try:
@ -135,12 +150,16 @@ class OldEntry(xmpp.Node, object):
''' URI that is pointed by the entry. ''') ''' URI that is pointed by the entry. ''')
def get_updated(self): def get_updated(self):
''' Get the time the entry was updated last time. This should be standarized, """
but pubsub.com sends it in human-readable format. We won't try to parse it. Get the time the entry was updated last time
(Atom 0.3 uses the word «modified» for that).
This should be standarized, but pubsub.com sends it in human-readable
format. We won't try to parse it. (Atom 0.3 uses the word «modified» for
that).
If there's no time given in the entry, we try with <published> If there's no time given in the entry, we try with <published>
and <issued> elements. ''' and <issued> elements.
"""
for name in ('updated', 'modified', 'published', 'issued'): for name in ('updated', 'modified', 'published', 'issued'):
date = self.getTagData(name) date = self.getTagData(name)
if date is not None: break if date is not None: break

View file

@ -23,13 +23,13 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
''' """
Module containing all XEP-115 (Entity Capabilities) related classes Module containing all XEP-115 (Entity Capabilities) related classes
Basic Idea: Basic Idea:
CapsCache caches features to hash relationships. The cache is queried CapsCache caches features to hash relationships. The cache is queried
through ClientCaps objects which are hold by contact instances. through ClientCaps objects which are hold by contact instances.
''' """
import gajim import gajim
import helpers import helpers
@ -54,7 +54,9 @@ CACHED = 2 # got the answer
capscache = None capscache = None
def initialize(logger): def initialize(logger):
''' Initializes this module ''' """
Initialize this module
"""
global capscache global capscache
capscache = CapsCache(logger) capscache = CapsCache(logger)
@ -73,11 +75,12 @@ def client_supports(client_caps, requested_feature):
return False return False
def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'): def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
'''Compute caps hash according to XEP-0115, V1.5 """
Compute caps hash according to XEP-0115, V1.5
dataforms are xmpp.DataForms objects as common.dataforms don't allow several dataforms are xmpp.DataForms objects as common.dataforms don't allow several
values without a field type list-multi''' values without a field type list-multi
"""
def sort_identities_func(i1, i2): def sort_identities_func(i1, i2):
cat1 = i1['category'] cat1 = i1['category']
cat2 = i2['category'] cat2 = i2['category']
@ -98,14 +101,14 @@ def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
if lang1 > lang2: if lang1 > lang2:
return 1 return 1
return 0 return 0
def sort_dataforms_func(d1, d2): def sort_dataforms_func(d1, d2):
f1 = d1.getField('FORM_TYPE') f1 = d1.getField('FORM_TYPE')
f2 = d2.getField('FORM_TYPE') f2 = d2.getField('FORM_TYPE')
if f1 and f2 and (f1.getValue() < f2.getValue()): if f1 and f2 and (f1.getValue() < f2.getValue()):
return -1 return -1
return 1 return 1
S = '' S = ''
identities.sort(cmp=sort_identities_func) identities.sort(cmp=sort_identities_func)
for i in identities: for i in identities:
@ -140,108 +143,119 @@ def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
else: else:
return '' return ''
return base64.b64encode(hash_.digest()) return base64.b64encode(hash_.digest())
################################################################################ ################################################################################
### Internal classes of this module ### Internal classes of this module
################################################################################ ################################################################################
class AbstractClientCaps(object): class AbstractClientCaps(object):
''' """
Base class representing a client and its capabilities as advertised by Base class representing a client and its capabilities as advertised by a
a caps tag in a presence. caps tag in a presence
''' """
def __init__(self, caps_hash, node): def __init__(self, caps_hash, node):
self._hash = caps_hash self._hash = caps_hash
self._node = node self._node = node
def get_discover_strategy(self): def get_discover_strategy(self):
return self._discover return self._discover
def _discover(self, connection, jid): def _discover(self, connection, jid):
''' To be implemented by subclassess ''' """
raise NotImplementedError() To be implemented by subclassess
"""
raise NotImplementedError
def get_cache_lookup_strategy(self): def get_cache_lookup_strategy(self):
return self._lookup_in_cache return self._lookup_in_cache
def _lookup_in_cache(self, caps_cache): def _lookup_in_cache(self, caps_cache):
''' To be implemented by subclassess ''' """
raise NotImplementedError() To be implemented by subclassess
"""
raise NotImplementedError
def get_hash_validation_strategy(self): def get_hash_validation_strategy(self):
return self._is_hash_valid return self._is_hash_valid
def _is_hash_valid(self, identities, features, dataforms): def _is_hash_valid(self, identities, features, dataforms):
''' To be implemented by subclassess ''' """
raise NotImplementedError() To be implemented by subclassess
"""
raise NotImplementedError
class ClientCaps(AbstractClientCaps): class ClientCaps(AbstractClientCaps):
''' The current XEP-115 implementation ''' """
The current XEP-115 implementation
"""
def __init__(self, caps_hash, node, hash_method): def __init__(self, caps_hash, node, hash_method):
AbstractClientCaps.__init__(self, caps_hash, node) AbstractClientCaps.__init__(self, caps_hash, node)
assert hash_method != 'old' assert hash_method != 'old'
self._hash_method = hash_method self._hash_method = hash_method
def _lookup_in_cache(self, caps_cache): def _lookup_in_cache(self, caps_cache):
return caps_cache[(self._hash_method, self._hash)] return caps_cache[(self._hash_method, self._hash)]
def _discover(self, connection, jid): def _discover(self, connection, jid):
connection.discoverInfo(jid, '%s#%s' % (self._node, self._hash)) connection.discoverInfo(jid, '%s#%s' % (self._node, self._hash))
def _is_hash_valid(self, identities, features, dataforms): def _is_hash_valid(self, identities, features, dataforms):
computed_hash = compute_caps_hash(identities, features, computed_hash = compute_caps_hash(identities, features,
dataforms=dataforms, hash_method=self._hash_method) dataforms=dataforms, hash_method=self._hash_method)
return computed_hash == self._hash return computed_hash == self._hash
class OldClientCaps(AbstractClientCaps): class OldClientCaps(AbstractClientCaps):
''' Old XEP-115 implemtation. Kept around for background competability. ''' """
Old XEP-115 implemtation. Kept around for background competability
"""
def __init__(self, caps_hash, node): def __init__(self, caps_hash, node):
AbstractClientCaps.__init__(self, caps_hash, node) AbstractClientCaps.__init__(self, caps_hash, node)
def _lookup_in_cache(self, caps_cache): def _lookup_in_cache(self, caps_cache):
return caps_cache[('old', self._node + '#' + self._hash)] return caps_cache[('old', self._node + '#' + self._hash)]
def _discover(self, connection, jid): def _discover(self, connection, jid):
connection.discoverInfo(jid) connection.discoverInfo(jid)
def _is_hash_valid(self, identities, features, dataforms):
return True
def _is_hash_valid(self, identities, features, dataforms):
return True
class NullClientCaps(AbstractClientCaps): class NullClientCaps(AbstractClientCaps):
''' """
This is a NULL-Object to streamline caps handling if a client has not This is a NULL-Object to streamline caps handling if a client has not
advertised any caps or has advertised them in an improper way. advertised any caps or has advertised them in an improper way
Assumes (almost) everything is supported. Assumes (almost) everything is supported.
''' """
def __init__(self): def __init__(self):
AbstractClientCaps.__init__(self, None, None) AbstractClientCaps.__init__(self, None, None)
def _lookup_in_cache(self, caps_cache): def _lookup_in_cache(self, caps_cache):
# lookup something which does not exist to get a new CacheItem created # lookup something which does not exist to get a new CacheItem created
cache_item = caps_cache[('dummy', '')] cache_item = caps_cache[('dummy', '')]
assert cache_item.status != CACHED assert cache_item.status != CACHED
return cache_item return cache_item
def _discover(self, connection, jid): def _discover(self, connection, jid):
pass pass
def _is_hash_valid(self, identities, features, dataforms): def _is_hash_valid(self, identities, features, dataforms):
return False return False
class CapsCache(object): class CapsCache(object):
''' """
This object keeps the mapping between caps data and real disco This object keeps the mapping between caps data and real disco features they
features they represent, and provides simple way to query that info. represent, and provides simple way to query that info
''' """
def __init__(self, logger=None): def __init__(self, logger=None):
# our containers: # our containers:
# __cache is a dictionary mapping: pair of hash method and hash maps # __cache is a dictionary mapping: pair of hash method and hash maps
@ -255,7 +269,7 @@ class CapsCache(object):
# another object, and we will have plenty of identical long # another object, and we will have plenty of identical long
# strings. therefore we can cache them # strings. therefore we can cache them
__names = {} __names = {}
def __init__(self, hash_method, hash_, logger): def __init__(self, hash_method, hash_, logger):
# cached into db # cached into db
self.hash_method = hash_method self.hash_method = hash_method
@ -274,7 +288,7 @@ class CapsCache(object):
self._features = [] self._features = []
for feature in value: for feature in value:
self._features.append(self.__names.setdefault(feature, feature)) self._features.append(self.__names.setdefault(feature, feature))
features = property(_get_features, _set_features) features = property(_get_features, _set_features)
def _get_identities(self): def _get_identities(self):
@ -291,7 +305,7 @@ class CapsCache(object):
d['name'] = i[3] d['name'] = i[3]
list_.append(d) list_.append(d)
return list_ return list_
def _set_identities(self, value): def _set_identities(self, value):
self._identities = [] self._identities = []
for identity in value: for identity in value:
@ -299,7 +313,7 @@ class CapsCache(object):
t = (identity['category'], identity.get('type'), t = (identity['category'], identity.get('type'),
identity.get('xml:lang'), identity.get('name')) identity.get('xml:lang'), identity.get('name'))
self._identities.append(self.__names.setdefault(t, t)) self._identities.append(self.__names.setdefault(t, t))
identities = property(_get_identities, _set_identities) identities = property(_get_identities, _set_identities)
def set_and_store(self, identities, features): def set_and_store(self, identities, features):
@ -308,7 +322,7 @@ class CapsCache(object):
self._logger.add_caps_entry(self.hash_method, self.hash, self._logger.add_caps_entry(self.hash_method, self.hash,
identities, features) identities, features)
self.status = CACHED self.status = CACHED
def update_last_seen(self): def update_last_seen(self):
if not self._recently_seen: if not self._recently_seen:
self._recently_seen = True self._recently_seen = True
@ -325,9 +339,11 @@ class CapsCache(object):
x.identities = identities x.identities = identities
x.features = features x.features = features
x.status = CACHED x.status = CACHED
def _remove_outdated_caps(self): def _remove_outdated_caps(self):
'''Removes outdated values from the db''' """
Remove outdated values from the db
"""
self.logger.clean_caps_table() self.logger.clean_caps_table()
def __getitem__(self, caps): def __getitem__(self, caps):
@ -341,20 +357,20 @@ class CapsCache(object):
return x return x
def query_client_of_jid_if_unknown(self, connection, jid, client_caps): def query_client_of_jid_if_unknown(self, connection, jid, client_caps):
''' """
Start a disco query to determine caps (node, ver, exts). Start a disco query to determine caps (node, ver, exts). Won't query if
Won't query if the data is already in cache. the data is already in cache
''' """
lookup_cache_item = client_caps.get_cache_lookup_strategy() lookup_cache_item = client_caps.get_cache_lookup_strategy()
q = lookup_cache_item(self) q = lookup_cache_item(self)
if q.status == NEW: if q.status == NEW:
# do query for bare node+hash pair # do query for bare node+hash pair
# this will create proper object # this will create proper object
q.status = QUERIED q.status = QUERIED
discover = client_caps.get_discover_strategy() discover = client_caps.get_discover_strategy()
discover(connection, jid) discover(connection, jid)
else: else:
q.update_last_seen() q.update_last_seen()
################################################################################ ################################################################################
@ -362,14 +378,15 @@ class CapsCache(object):
################################################################################ ################################################################################
class ConnectionCaps(object): class ConnectionCaps(object):
''' """
This class highly depends on that it is a part of Connection class. This class highly depends on that it is a part of Connection class
''' """
def _capsPresenceCB(self, con, presence): def _capsPresenceCB(self, con, presence):
''' """
Handle incoming presence stanzas... This is a callback for xmpp Handle incoming presence stanzas... This is a callback for xmpp
registered in connection_handlers.py registered in connection_handlers.py
''' """
# we will put these into proper Contact object and ask # we will put these into proper Contact object and ask
# for disco... so that disco will learn how to interpret # for disco... so that disco will learn how to interpret
# these caps # these caps
@ -411,7 +428,7 @@ class ConnectionCaps(object):
if pm_ctrl: if pm_ctrl:
pm_ctrl.update_contact() pm_ctrl.update_contact()
def _capsDiscoCB(self, jid, node, identities, features, dataforms): def _capsDiscoCB(self, jid, node, identities, features, dataforms):
contact = gajim.contacts.get_contact_from_full_jid(self.name, jid) contact = gajim.contacts.get_contact_from_full_jid(self.name, jid)
if not contact: if not contact:
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid) room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
@ -420,14 +437,14 @@ class ConnectionCaps(object):
return return
lookup = contact.client_caps.get_cache_lookup_strategy() lookup = contact.client_caps.get_cache_lookup_strategy()
cache_item = lookup(capscache) cache_item = lookup(capscache)
if cache_item.status == CACHED: if cache_item.status == CACHED:
return return
else: else:
validate = contact.client_caps.get_hash_validation_strategy() validate = contact.client_caps.get_hash_validation_strategy()
hash_is_valid = validate(identities, features, dataforms) hash_is_valid = validate(identities, features, dataforms)
if hash_is_valid: if hash_is_valid:
cache_item.set_and_store(identities, features) cache_item.set_and_store(identities, features)
else: else:

View file

@ -34,10 +34,13 @@ class AdHocCommand:
@staticmethod @staticmethod
def isVisibleFor(samejid): def isVisibleFor(samejid):
''' This returns True if that command should be visible and invokable """
for others. This returns True if that command should be visible and invokable for
others
samejid - True when command is invoked by an entity with the same bare samejid - True when command is invoked by an entity with the same bare
jid.''' jid.
"""
return True return True
def __init__(self, conn, jid, sessionid): def __init__(self, conn, jid, sessionid):
@ -80,7 +83,9 @@ class ChangeStatusCommand(AdHocCommand):
@staticmethod @staticmethod
def isVisibleFor(samejid): def isVisibleFor(samejid):
''' Change status is visible only if the entity has the same bare jid. ''' """
Change status is visible only if the entity has the same bare jid
"""
return samejid return samejid
def execute(self, request): def execute(self, request):
@ -177,7 +182,9 @@ class LeaveGroupchatsCommand(AdHocCommand):
@staticmethod @staticmethod
def isVisibleFor(samejid): def isVisibleFor(samejid):
''' Change status is visible only if the entity has the same bare jid. ''' """
Change status is visible only if the entity has the same bare jid
"""
return samejid return samejid
def execute(self, request): def execute(self, request):
@ -259,7 +266,9 @@ class ForwardMessagesCommand(AdHocCommand):
@staticmethod @staticmethod
def isVisibleFor(samejid): def isVisibleFor(samejid):
''' Change status is visible only if the entity has the same bare jid. ''' """
Change status is visible only if the entity has the same bare jid
"""
return samejid return samejid
def execute(self, request): def execute(self, request):
@ -282,7 +291,10 @@ class ForwardMessagesCommand(AdHocCommand):
return False # finish the session return False # finish the session
class ConnectionCommands: class ConnectionCommands:
''' This class depends on that it is a part of Connection() class. ''' """
This class depends on that it is a part of Connection() class
"""
def __init__(self): def __init__(self):
# a list of all commands exposed: node -> command class # a list of all commands exposed: node -> command class
self.__commands = {} self.__commands = {}
@ -297,7 +309,9 @@ class ConnectionCommands:
return gajim.get_jid_from_account(self.name) return gajim.get_jid_from_account(self.name)
def isSameJID(self, jid): def isSameJID(self, jid):
''' Tests if the bare jid given is the same as our bare jid. ''' """
Test if the bare jid given is the same as our bare jid
"""
return xmpp.JID(jid).getStripped() == self.getOurBareJID() return xmpp.JID(jid).getStripped() == self.getOurBareJID()
def commandListQuery(self, con, iq_obj): def commandListQuery(self, con, iq_obj):
@ -318,8 +332,10 @@ class ConnectionCommands:
self.connection.send(iq) self.connection.send(iq)
def commandInfoQuery(self, con, iq_obj): def commandInfoQuery(self, con, iq_obj):
''' Send disco#info result for query for command (JEP-0050, example 6.). """
Return True if the result was sent, False if not. ''' Send disco#info result for query for command (JEP-0050, example 6.).
Return True if the result was sent, False if not
"""
jid = helpers.get_full_jid_from_iq(iq_obj) jid = helpers.get_full_jid_from_iq(iq_obj)
node = iq_obj.getTagAttr('query', 'node') node = iq_obj.getTagAttr('query', 'node')
@ -342,8 +358,10 @@ class ConnectionCommands:
return False return False
def commandItemsQuery(self, con, iq_obj): def commandItemsQuery(self, con, iq_obj):
''' Send disco#items result for query for command. """
Return True if the result was sent, False if not. ''' Send disco#items result for query for command. Return True if the result
was sent, False if not.
"""
jid = helpers.get_full_jid_from_iq(iq_obj) jid = helpers.get_full_jid_from_iq(iq_obj)
node = iq_obj.getTagAttr('query', 'node') node = iq_obj.getTagAttr('query', 'node')

View file

@ -512,7 +512,9 @@ class Config:
cb(data, opt3, [opt, opt2], dict_[opt2][opt3]) cb(data, opt3, [opt, opt2], dict_[opt2][opt3])
def get_children(self, node=None): def get_children(self, node=None):
''' Tree-like interface ''' """
Tree-like interface
"""
if node is None: if node is None:
for child, option in self.__options.iteritems(): for child, option in self.__options.iteritems():
yield (child, ), option yield (child, ), option
@ -698,8 +700,9 @@ class Config:
return False return False
def should_log(self, account, jid): def should_log(self, account, jid):
'''should conversations between a local account and a remote jid be """
logged?''' Should conversations between a local account and a remote jid be logged?
"""
no_log_for = self.get_per('accounts', account, 'no_log_for') no_log_for = self.get_per('accounts', account, 'no_log_for')
if not no_log_for: if not no_log_for:

View file

@ -46,7 +46,9 @@ import tempfile
# not displayed to the user, Unicode is not really necessary here. # not displayed to the user, Unicode is not really necessary here.
def fse(s): def fse(s):
'''Convert from filesystem encoding if not already Unicode''' """
Convert from filesystem encoding if not already Unicode
"""
return unicode(s, sys.getfilesystemencoding()) return unicode(s, sys.getfilesystemencoding())
def windowsify(s): def windowsify(s):

View file

@ -100,10 +100,11 @@ ssl_error = {
} }
class CommonConnection: class CommonConnection:
''' """
Common connection class, can be derivated for normal connection or zeroconf Common connection class, can be derivated for normal connection or zeroconf
connection connection
''' """
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
# self.connected: # self.connected:
@ -162,11 +163,15 @@ class CommonConnection:
return resource return resource
def dispatch(self, event, data): def dispatch(self, event, data):
'''always passes account name as first param''' """
Always passes account name as first param
"""
gajim.interface.dispatch(event, self.name, data) gajim.interface.dispatch(event, self.name, data)
def _reconnect(self): def _reconnect(self):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def quit(self, kill_core): def quit(self, kill_core):
@ -174,7 +179,9 @@ class CommonConnection:
self.disconnect(on_purpose=True) self.disconnect(on_purpose=True)
def test_gpg_passphrase(self, password): def test_gpg_passphrase(self, password):
'''Returns 'ok', 'bad_pass' or 'expired' ''' """
Returns 'ok', 'bad_pass' or 'expired'
"""
if not self.gpg: if not self.gpg:
return False return False
self.gpg.passphrase = password self.gpg.passphrase = password
@ -188,10 +195,12 @@ class CommonConnection:
return 'ok' return 'ok'
def get_signed_msg(self, msg, callback = None): def get_signed_msg(self, msg, callback = None):
'''returns the signed message if possible """
or an empty string if gpg is not used Returns the signed message if possible or an empty string if gpg is not
or None if waiting for passphrase. used or None if waiting for passphrase
callback is the function to call when user give the passphrase'''
callback is the function to call when user give the passphrase
"""
signed = '' signed = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid') keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID and self.USE_GPG: if keyID and self.USE_GPG:
@ -208,7 +217,9 @@ class CommonConnection:
return signed return signed
def _on_disconnected(self): def _on_disconnected(self):
''' called when a disconnect request has completed successfully''' """
Called when a disconnect request has completed successfully
"""
self.disconnect(on_purpose=True) self.disconnect(on_purpose=True)
self.dispatch('STATUS', 'offline') self.dispatch('STATUS', 'offline')
@ -216,9 +227,10 @@ class CommonConnection:
return gajim.SHOW_LIST[self.connected] return gajim.SHOW_LIST[self.connected]
def check_jid(self, jid): def check_jid(self, jid):
'''this function must be implemented by derivated classes. """
It has to return the valid jid, or raise a helpers.InvalidFormat exception This function must be implemented by derivated classes. It has to return
''' the valid jid, or raise a helpers.InvalidFormat exception
"""
raise NotImplementedError raise NotImplementedError
def _prepare_message(self, jid, msg, keyID, type_='chat', subject='', def _prepare_message(self, jid, msg, keyID, type_='chat', subject='',
@ -424,32 +436,46 @@ class CommonConnection:
common.logger.LOG_DB_PATH common.logger.LOG_DB_PATH
def ack_subscribed(self, jid): def ack_subscribed(self, jid):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def ack_unsubscribed(self, jid): def ack_unsubscribed(self, jid):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def request_subscription(self, jid, msg='', name='', groups=[], def request_subscription(self, jid, msg='', name='', groups=[],
auto_auth=False): auto_auth=False):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def send_authorization(self, jid): def send_authorization(self, jid):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def refuse_authorization(self, jid): def refuse_authorization(self, jid):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def unsubscribe(self, jid, remove_auth = True): def unsubscribe(self, jid, remove_auth = True):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def unsubscribe_agent(self, agent): def unsubscribe_agent(self, agent):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def update_contact(self, jid, name, groups): def update_contact(self, jid, name, groups):
@ -457,47 +483,67 @@ class CommonConnection:
self.connection.getRoster().setItem(jid=jid, name=name, groups=groups) self.connection.getRoster().setItem(jid=jid, name=name, groups=groups)
def update_contacts(self, contacts): def update_contacts(self, contacts):
'''update multiple roster items''' """
Update multiple roster items
"""
if self.connection: if self.connection:
self.connection.getRoster().setItemMulti(contacts) self.connection.getRoster().setItemMulti(contacts)
def new_account(self, name, config, sync=False): def new_account(self, name, config, sync=False):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def _on_new_account(self, con=None, con_type=None): def _on_new_account(self, con=None, con_type=None):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def account_changed(self, new_name): def account_changed(self, new_name):
self.name = new_name self.name = new_name
def request_last_status_time(self, jid, resource): def request_last_status_time(self, jid, resource):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def request_os_info(self, jid, resource): def request_os_info(self, jid, resource):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def get_settings(self): def get_settings(self):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def get_bookmarks(self): def get_bookmarks(self):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def store_bookmarks(self): def store_bookmarks(self):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def get_metacontacts(self): def get_metacontacts(self):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def send_agent_status(self, agent, ptype): def send_agent_status(self, agent, ptype):
'''To be implemented by derivated classes''' """
To be implemented by derivated classes
"""
raise NotImplementedError raise NotImplementedError
def gpg_passphrase(self, passphrase): def gpg_passphrase(self, passphrase):
@ -581,7 +627,6 @@ class CommonConnection:
self._update_status(show, msg) self._update_status(show, msg)
class Connection(CommonConnection, ConnectionHandlers): class Connection(CommonConnection, ConnectionHandlers):
'''Connection class'''
def __init__(self, name): def __init__(self, name):
CommonConnection.__init__(self, name) CommonConnection.__init__(self, name)
ConnectionHandlers.__init__(self) ConnectionHandlers.__init__(self)
@ -672,7 +717,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection = None self.connection = None
def _disconnectedReconnCB(self): def _disconnectedReconnCB(self):
'''Called when we are disconnected''' """
Called when we are disconnected
"""
log.info('disconnectedReconnCB called') log.info('disconnectedReconnCB called')
if gajim.account_is_connected(self.name): if gajim.account_is_connected(self.name):
# we cannot change our status to offline or connecting # we cannot change our status to offline or connecting
@ -830,11 +877,11 @@ class Connection(CommonConnection, ConnectionHandlers):
self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data)) self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data))
def _select_next_host(self, hosts): def _select_next_host(self, hosts):
'''Selects the next host according to RFC2782 p.3 based on it's """
priority. Chooses between hosts with the same priority randomly, Selects the next host according to RFC2782 p.3 based on it's priority.
where the probability of being selected is proportional to the weight Chooses between hosts with the same priority randomly, where the
of the host.''' probability of being selected is proportional to the weight of the host
"""
hosts_by_prio = sorted(hosts, key=operator.itemgetter('prio')) hosts_by_prio = sorted(hosts, key=operator.itemgetter('prio'))
try: try:
@ -856,10 +903,13 @@ class Connection(CommonConnection, ConnectionHandlers):
return host return host
def connect(self, data = None): def connect(self, data = None):
''' Start a connection to the Jabber server. """
Returns connection, and connection type ('tls', 'ssl', 'plain', '') Start a connection to the Jabber server
data MUST contain hostname, usessl, proxy, use_custom_host,
custom_host (if use_custom_host), custom_port (if use_custom_host)''' Returns connection, and connection type ('tls', 'ssl', 'plain', '') data
MUST contain hostname, usessl, proxy, use_custom_host, custom_host (if
use_custom_host), custom_port (if use_custom_host)
"""
if self.connection: if self.connection:
return self.connection, '' return self.connection, ''
@ -1256,8 +1306,10 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(' ') self.connection.send(' ')
def sendPing(self, pingTo=None): def sendPing(self, pingTo=None):
'''Send XMPP Ping (XEP-0199) request. If pingTo is not set, ping is sent """
to server to detect connection failure at application level.''' Send XMPP Ping (XEP-0199) request. If pingTo is not set, ping is sent to
server to detect connection failure at application level
"""
if not self.connection: if not self.connection:
return return
id_ = self.connection.getAnID() id_ = self.connection.getAnID()
@ -1325,7 +1377,9 @@ class Connection(CommonConnection, ConnectionHandlers):
common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname) common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname)
def build_privacy_rule(self, name, action, order=1): def build_privacy_rule(self, name, action, order=1):
'''Build a Privacy rule stanza for invisibility''' """
Build a Privacy rule stanza for invisibility
"""
iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '') iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
l = iq.getTag('query').setTag('list', {'name': name}) l = iq.getTag('query').setTag('list', {'name': name})
i = l.setTag('item', {'action': action, 'order': str(order)}) i = l.setTag('item', {'action': action, 'order': str(order)})
@ -1358,7 +1412,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
def activate_privacy_rule(self, name): def activate_privacy_rule(self, name):
'''activate a privacy rule''' """
Activate a privacy rule
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '') iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
@ -1534,7 +1590,9 @@ class Connection(CommonConnection, ConnectionHandlers):
original_message=original_message, delayed=delayed, callback=cb) original_message=original_message, delayed=delayed, callback=cb)
def send_contacts(self, contacts, jid): def send_contacts(self, contacts, jid):
'''Send contacts with RosterX (Xep-0144)''' """
Send contacts with RosterX (Xep-0144)
"""
if not self.connection: if not self.connection:
return return
if len(contacts) == 1: if len(contacts) == 1:
@ -1553,7 +1611,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(msg_iq) self.connection.send(msg_iq)
def send_stanza(self, stanza): def send_stanza(self, stanza):
''' send a stanza untouched ''' """
Send a stanza untouched
"""
if not self.connection: if not self.connection:
return return
self.connection.send(stanza) self.connection.send(stanza)
@ -1573,7 +1633,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(p) self.connection.send(p)
def request_subscription(self, jid, msg = '', name = '', groups = [], def request_subscription(self, jid, msg = '', name = '', groups = [],
auto_auth = False, user_nick = ''): auto_auth = False, user_nick = ''):
if not self.connection: if not self.connection:
return return
log.debug('subscription request for %s' % jid) log.debug('subscription request for %s' % jid)
@ -1677,8 +1737,10 @@ class Connection(CommonConnection, ConnectionHandlers):
common.xmpp.features_nb.getRegInfo(con, self._hostname) common.xmpp.features_nb.getRegInfo(con, self._hostname)
def request_last_status_time(self, jid, resource, groupchat_jid=None): def request_last_status_time(self, jid, resource, groupchat_jid=None):
'''groupchat_jid is used when we want to send a request to a real jid """
and act as if the answer comes from the groupchat_jid''' groupchat_jid is used when we want to send a request to a real jid and
act as if the answer comes from the groupchat_jid
"""
if not self.connection: if not self.connection:
return return
to_whom_jid = jid to_whom_jid = jid
@ -1694,8 +1756,10 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
def request_os_info(self, jid, resource, groupchat_jid=None): def request_os_info(self, jid, resource, groupchat_jid=None):
'''groupchat_jid is used when we want to send a request to a real jid """
and act as if the answer comes from the groupchat_jid''' groupchat_jid is used when we want to send a request to a real jid and
act as if the answer comes from the groupchat_jid
"""
if not self.connection: if not self.connection:
return return
# If we are invisible, do not request # If we are invisible, do not request
@ -1715,8 +1779,10 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
def request_entity_time(self, jid, resource, groupchat_jid=None): def request_entity_time(self, jid, resource, groupchat_jid=None):
'''groupchat_jid is used when we want to send a request to a real jid """
and act as if the answer comes from the groupchat_jid''' groupchat_jid is used when we want to send a request to a real jid and
act as if the answer comes from the groupchat_jid
"""
if not self.connection: if not self.connection:
return return
# If we are invisible, do not request # If we are invisible, do not request
@ -1736,7 +1802,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
def get_settings(self): def get_settings(self):
''' Get Gajim settings as described in XEP 0049 ''' """
Get Gajim settings as described in XEP 0049
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ='get') iq = common.xmpp.Iq(typ='get')
@ -1757,9 +1825,12 @@ class Connection(CommonConnection, ConnectionHandlers):
self._request_bookmarks_xml() self._request_bookmarks_xml()
def get_bookmarks(self, storage_type=None): def get_bookmarks(self, storage_type=None):
'''Get Bookmarks from storage or PubSub if supported as described in """
XEP 0048 Get Bookmarks from storage or PubSub if supported as described in XEP
storage_type can be set to xml to force request to xml storage''' 0048
storage_type can be set to xml to force request to xml storage
"""
if not self.connection: if not self.connection:
return return
if self.pubsub_supported and storage_type != 'xml': if self.pubsub_supported and storage_type != 'xml':
@ -1771,9 +1842,12 @@ class Connection(CommonConnection, ConnectionHandlers):
self._request_bookmarks_xml() self._request_bookmarks_xml()
def store_bookmarks(self, storage_type=None): def store_bookmarks(self, storage_type=None):
''' Send bookmarks to the storage namespace or PubSub if supported """
Send bookmarks to the storage namespace or PubSub if supported
storage_type can be set to 'pubsub' or 'xml' so store in only one method storage_type can be set to 'pubsub' or 'xml' so store in only one method
else it will be stored on both''' else it will be stored on both
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Node(tag='storage', attrs={'xmlns': 'storage:bookmarks'}) iq = common.xmpp.Node(tag='storage', attrs={'xmlns': 'storage:bookmarks'})
@ -1815,7 +1889,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iqA) self.connection.send(iqA)
def get_annotations(self): def get_annotations(self):
'''Get Annonations from storage as described in XEP 0048, and XEP 0145''' """
Get Annonations from storage as described in XEP 0048, and XEP 0145
"""
self.annotations = {} self.annotations = {}
if not self.connection: if not self.connection:
return return
@ -1825,7 +1901,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
def store_annotations(self): def store_annotations(self):
'''Set Annonations in private storage as described in XEP 0048, and XEP 0145''' """
Set Annonations in private storage as described in XEP 0048, and XEP 0145
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ='set') iq = common.xmpp.Iq(typ='set')
@ -1840,7 +1918,9 @@ class Connection(CommonConnection, ConnectionHandlers):
def get_metacontacts(self): def get_metacontacts(self):
'''Get metacontacts list from storage as described in XEP 0049''' """
Get metacontacts list from storage as described in XEP 0049
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ='get') iq = common.xmpp.Iq(typ='get')
@ -1852,7 +1932,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
def store_metacontacts(self, tags_list): def store_metacontacts(self, tags_list):
''' Send meta contacts to the storage namespace ''' """
Send meta contacts to the storage namespace
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ='set') iq = common.xmpp.Iq(typ='set')
@ -2000,16 +2082,20 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(p) self.connection.send(p)
def gc_got_disconnected(self, room_jid): def gc_got_disconnected(self, room_jid):
''' A groupchat got disconnected. This can be or purpose or not. """
A groupchat got disconnected. This can be or purpose or not
Save the time we quit to avoid duplicate logs AND be faster than get that Save the time we quit to avoid duplicate logs AND be faster than get that
date from DB. Save it in mem AND in a small table (with fast access) date from DB. Save it in mem AND in a small table (with fast access)
''' """
log_time = time_time() log_time = time_time()
self.last_history_time[room_jid] = log_time self.last_history_time[room_jid] = log_time
gajim.logger.set_room_last_message_time(room_jid, log_time) gajim.logger.set_room_last_message_time(room_jid, log_time)
def gc_set_role(self, room_jid, nick, role, reason = ''): def gc_set_role(self, room_jid, nick, role, reason = ''):
'''role is for all the life of the room so it's based on nick''' """
Role is for all the life of the room so it's based on nick
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\ iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
@ -2022,7 +2108,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
def gc_set_affiliation(self, room_jid, jid, affiliation, reason = ''): def gc_set_affiliation(self, room_jid, jid, affiliation, reason = ''):
'''affiliation is for all the life of the room so it's based on jid''' """
Affiliation is for all the life of the room so it's based on jid
"""
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\ iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
@ -2117,7 +2205,9 @@ class Connection(CommonConnection, ConnectionHandlers):
_on_unregister_account_connect(self.connection) _on_unregister_account_connect(self.connection)
def send_invite(self, room, to, reason='', continue_tag=False): def send_invite(self, room, to, reason='', continue_tag=False):
'''sends invitation''' """
Send invitation
"""
message=common.xmpp.Message(to = room) message=common.xmpp.Message(to = room)
c = message.addChild(name = 'x', namespace = common.xmpp.NS_MUC_USER) c = message.addChild(name = 'x', namespace = common.xmpp.NS_MUC_USER)
c = c.addChild(name = 'invite', attrs={'to' : to}) c = c.addChild(name = 'invite', attrs={'to' : to})

View file

@ -100,9 +100,9 @@ class ConnectionBytestream:
return True return True
def send_success_connect_reply(self, streamhost): def send_success_connect_reply(self, streamhost):
''' send reply to the initiator of FT that we """
made a connection Send reply to the initiator of FT that we made a connection
''' """
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
if streamhost is None: if streamhost is None:
@ -117,7 +117,9 @@ class ConnectionBytestream:
self.connection.send(iq) self.connection.send(iq)
def remove_transfers_for_contact(self, contact): def remove_transfers_for_contact(self, contact):
''' stop all active transfer for contact ''' """
Stop all active transfer for contact
"""
for file_props in self.files_props.values(): for file_props in self.files_props.values():
if self.is_transfer_stopped(file_props): if self.is_transfer_stopped(file_props):
continue continue
@ -132,7 +134,9 @@ class ConnectionBytestream:
self.remove_transfer(file_props) self.remove_transfer(file_props)
def remove_all_transfers(self): def remove_all_transfers(self):
''' stops and removes all active connections from the socks5 pool ''' """
Stop and remove all active connections from the socks5 pool
"""
for file_props in self.files_props.values(): for file_props in self.files_props.values():
self.remove_transfer(file_props, remove_from_list = False) self.remove_transfer(file_props, remove_from_list = False)
del(self.files_props) del(self.files_props)
@ -161,9 +165,11 @@ class ConnectionBytestream:
gajim.socks5queue.remove_receiver(host['idx']) gajim.socks5queue.remove_receiver(host['idx'])
gajim.socks5queue.remove_sender(host['idx']) gajim.socks5queue.remove_sender(host['idx'])
def send_socks5_info(self, file_props, fast = True, receiver = None, def send_socks5_info(self, file_props, fast = True, receiver = None, sender
sender = None): = None):
''' send iq for the present streamhosts and proxies ''' """
Send iq for the present streamhosts and proxies
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
if not isinstance(self.peerhost, tuple): if not isinstance(self.peerhost, tuple):
@ -269,9 +275,12 @@ class ConnectionBytestream:
self.connection.send(iq) self.connection.send(iq)
def send_file_rejection(self, file_props, code='403', typ=None): def send_file_rejection(self, file_props, code='403', typ=None):
''' informs sender that we refuse to download the file """
Inform sender that we refuse to download the file
typ is used when code = '400', in this case typ can be 'strean' for typ is used when code = '400', in this case typ can be 'strean' for
invalid stream or 'profile' for invalid profile''' invalid stream or 'profile' for invalid profile
"""
# user response to ConfirmationDialog may come after we've disconneted # user response to ConfirmationDialog may come after we've disconneted
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
@ -294,7 +303,9 @@ class ConnectionBytestream:
self.connection.send(iq) self.connection.send(iq)
def send_file_approval(self, file_props): def send_file_approval(self, file_props):
''' send iq, confirming that we want to download the file ''' """
Send iq, confirming that we want to download the file
"""
# user response to ConfirmationDialog may come after we've disconneted # user response to ConfirmationDialog may come after we've disconneted
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
@ -326,7 +337,9 @@ class ConnectionBytestream:
return file_props['receiver'].jid + '/' + file_props['receiver'].resource return file_props['receiver'].jid + '/' + file_props['receiver'].resource
def send_file_request(self, file_props): def send_file_request(self, file_props):
''' send iq for new FT request ''' """
Send iq for new FT request
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
file_props['sender'] = self._ft_get_our_jid() file_props['sender'] = self._ft_get_our_jid()
@ -357,7 +370,9 @@ class ConnectionBytestream:
self.connection.send(iq) self.connection.send(iq)
def _result_socks5_sid(self, sid, hash_id): def _result_socks5_sid(self, sid, hash_id):
''' store the result of sha message from auth. ''' """
Store the result of SHA message from auth
"""
if sid not in self.files_props: if sid not in self.files_props:
return return
file_props = self.files_props[sid] file_props = self.files_props[sid]
@ -365,8 +380,10 @@ class ConnectionBytestream:
return return
def _connect_error(self, to, _id, sid, code=404): def _connect_error(self, to, _id, sid, code=404):
''' cb, when there is an error establishing BS connection, or """
when connection is rejected''' Called when there is an error establishing BS connection, or when
connection is rejected
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
msg_dict = { msg_dict = {
@ -391,7 +408,9 @@ class ConnectionBytestream:
self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg)) self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
def _proxy_auth_ok(self, proxy): def _proxy_auth_ok(self, proxy):
'''cb, called after authentication to proxy server ''' """
Called after authentication to proxy server
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
file_props = self.files_props[proxy['sid']] file_props = self.files_props[proxy['sid']]
@ -673,16 +692,24 @@ class ConnectionBytestream:
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
class ConnectionDisco: class ConnectionDisco:
''' hold xmpppy handlers and public methods for discover services''' """
Holds xmpppy handlers and public methods for discover services
"""
def discoverItems(self, jid, node = None, id_prefix = None): def discoverItems(self, jid, node = None, id_prefix = None):
'''According to XEP-0030: jid is mandatory, """
name, node, action is optional.''' According to XEP-0030:
jid is mandatory;
name, node, action is optional.
"""
self._discover(common.xmpp.NS_DISCO_ITEMS, jid, node, id_prefix) self._discover(common.xmpp.NS_DISCO_ITEMS, jid, node, id_prefix)
def discoverInfo(self, jid, node = None, id_prefix = None): def discoverInfo(self, jid, node = None, id_prefix = None):
'''According to XEP-0030: """
According to XEP-0030:
For identity: category, type is mandatory, name is optional. For identity: category, type is mandatory, name is optional.
For feature: var is mandatory''' For feature: var is mandatory.
"""
self._discover(common.xmpp.NS_DISCO_INFO, jid, node, id_prefix) self._discover(common.xmpp.NS_DISCO_INFO, jid, node, id_prefix)
def request_register_agent_info(self, agent): def request_register_agent_info(self, agent):
@ -738,7 +765,9 @@ class ConnectionDisco:
self._IqCB(con, resp) self._IqCB(con, resp)
def _discoGetCB(self, con, iq_obj): def _discoGetCB(self, con, iq_obj):
''' get disco info ''' """
Get disco info
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
frm = helpers.get_full_jid_from_iq(iq_obj) frm = helpers.get_full_jid_from_iq(iq_obj)
@ -1008,9 +1037,11 @@ class ConnectionVcard:
self.dispatch('ERROR', (_('Disk Write Error'), str(e))) self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
def get_cached_vcard(self, fjid, is_fake_jid = False): def get_cached_vcard(self, fjid, is_fake_jid = False):
'''return the vcard as a dict """
return {} if vcard was too old Return the vcard as a dict.
return None if we don't have cached vcard''' Return {} if vcard was too old.
Return None if we don't have cached vcard.
"""
jid, nick = gajim.get_room_and_nick_from_fjid(fjid) jid, nick = gajim.get_room_and_nick_from_fjid(fjid)
puny_jid = helpers.sanitize_filename(jid) puny_jid = helpers.sanitize_filename(jid)
if is_fake_jid: if is_fake_jid:
@ -1045,9 +1076,13 @@ class ConnectionVcard:
return vcard return vcard
def request_vcard(self, jid = None, groupchat_jid = None): def request_vcard(self, jid = None, groupchat_jid = None):
'''request the VCARD. If groupchat_jid is not nul, it means we request a vcard """
to a fake jid, like in private messages in groupchat. jid can be the Request the VCARD
real jid of the contact, but we want to consider it comes from a fake jid'''
If groupchat_jid is not nul, it means we request a vcard to a fake jid,
like in private messages in groupchat. jid can be the real jid of the
contact, but we want to consider it comes from a fake jid
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
iq = common.xmpp.Iq(typ = 'get') iq = common.xmpp.Iq(typ = 'get')
@ -1238,8 +1273,9 @@ class ConnectionVcard:
del self.awaiting_answers[id_] del self.awaiting_answers[id_]
def _vCardCB(self, con, vc): def _vCardCB(self, con, vc):
'''Called when we receive a vCard """
Parse the vCard and send it to plugins''' Called when we receive a vCard Parse the vCard and send it to plugins
"""
if not vc.getTag('vCard'): if not vc.getTag('vCard'):
return return
if not vc.getTag('vCard').getNamespace() == common.xmpp.NS_VCARD: if not vc.getTag('vCard').getNamespace() == common.xmpp.NS_VCARD:
@ -1345,8 +1381,9 @@ class ConnectionHandlersBase:
self.sessions = {} self.sessions = {}
def get_sessions(self, jid): def get_sessions(self, jid):
'''get all sessions for the given full jid''' """
Get all sessions for the given full jid
"""
if not gajim.interface.is_pm_contact(jid, self.name): if not gajim.interface.is_pm_contact(jid, self.name):
jid = gajim.get_jid_without_resource(jid) jid = gajim.get_jid_without_resource(jid)
@ -1356,9 +1393,10 @@ class ConnectionHandlersBase:
return [] return []
def get_or_create_session(self, fjid, thread_id): def get_or_create_session(self, fjid, thread_id):
'''returns an existing session between this connection and 'jid', returns a """
new one if none exist.''' Return an existing session between this connection and 'jid', returns a
new one if none exist
"""
pm = True pm = True
jid = fjid jid = fjid
@ -1386,7 +1424,9 @@ class ConnectionHandlersBase:
return None return None
def terminate_sessions(self, send_termination=False): def terminate_sessions(self, send_termination=False):
'''send termination messages and delete all active sessions''' """
Send termination messages and delete all active sessions
"""
for jid in self.sessions: for jid in self.sessions:
for thread_id in self.sessions[jid]: for thread_id in self.sessions[jid]:
self.sessions[jid][thread_id].terminate(send_termination) self.sessions[jid][thread_id].terminate(send_termination)
@ -1405,10 +1445,11 @@ class ConnectionHandlersBase:
del self.sessions[jid] del self.sessions[jid]
def find_null_session(self, jid): def find_null_session(self, jid):
'''finds all of the sessions between us and a remote jid in which we """
haven't received a thread_id yet and returns the session that we last Find all of the sessions between us and a remote jid in which we haven't
sent a message to.''' received a thread_id yet and returns the session that we last sent a
message to
"""
sessions = self.sessions[jid].values() sessions = self.sessions[jid].values()
# sessions that we haven't received a thread ID in # sessions that we haven't received a thread ID in
@ -1425,8 +1466,9 @@ sent a message to.'''
return None return None
def find_controlless_session(self, jid, resource=None): def find_controlless_session(self, jid, resource=None):
'''find an active session that doesn't have a control attached''' """
Find an active session that doesn't have a control attached
"""
try: try:
sessions = self.sessions[jid].values() sessions = self.sessions[jid].values()
@ -1444,8 +1486,12 @@ sent a message to.'''
return None return None
def make_new_session(self, jid, thread_id=None, type_='chat', cls=None): def make_new_session(self, jid, thread_id=None, type_='chat', cls=None):
'''create and register a new session. thread_id=None to generate one. """
type_ should be 'chat' or 'pm'.''' Create and register a new session
thread_id=None to generate one.
type_ should be 'chat' or 'pm'.
"""
if not cls: if not cls:
cls = gajim.default_session_type cls = gajim.default_session_type
@ -1463,7 +1509,9 @@ sent a message to.'''
return sess return sess
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps, ConnectionHandlersBase, ConnectionJingle): class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionPEP,
ConnectionCaps, ConnectionHandlersBase, ConnectionJingle):
def __init__(self): def __init__(self):
ConnectionVcard.__init__(self) ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self) ConnectionBytestream.__init__(self)
@ -1544,9 +1592,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('ERROR_ANSWER', (id_, jid_from, errmsg, errcode)) self.dispatch('ERROR_ANSWER', (id_, jid_from, errmsg, errcode))
def _PrivateCB(self, con, iq_obj): def _PrivateCB(self, con, iq_obj):
''' """
Private Data (XEP 048 and 049) Private Data (XEP 048 and 049)
''' """
log.debug('PrivateCB') log.debug('PrivateCB')
query = iq_obj.getTag('query') query = iq_obj.getTag('query')
storage = query.getTag('storage') storage = query.getTag('storage')
@ -1573,8 +1621,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.annotations[jid] = annotation self.annotations[jid] = annotation
def _parse_bookmarks(self, storage, storage_type): def _parse_bookmarks(self, storage, storage_type):
'''storage_type can be 'pubsub' or 'xml' to tell from where we got """
bookmarks''' storage_type can be 'pubsub' or 'xml' to tell from where we got bookmarks
"""
# Bookmarked URLs and Conferences # Bookmarked URLs and Conferences
# http://www.xmpp.org/extensions/xep-0048.html # http://www.xmpp.org/extensions/xep-0048.html
resend_to_pubsub = False resend_to_pubsub = False
@ -1788,7 +1837,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('ENTITY_TIME', (jid_stripped, resource, time_info)) self.dispatch('ENTITY_TIME', (jid_stripped, resource, time_info))
def _gMailNewMailCB(self, con, gm): def _gMailNewMailCB(self, con, gm):
'''Called when we get notified of new mail messages in gmail account''' """
Called when we get notified of new mail messages in gmail account
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
if not gm.getTag('new-mail'): if not gm.getTag('new-mail'):
@ -1810,7 +1861,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _gMailQueryCB(self, con, gm): def _gMailQueryCB(self, con, gm):
'''Called when we receive results from Querying the server for mail messages in gmail account''' """
Called when we receive results from Querying the server for mail messages
in gmail account
"""
if not gm.getTag('mailbox'): if not gm.getTag('mailbox'):
return return
self.gmail_url = gm.getTag('mailbox').getAttr('url') self.gmail_url = gm.getTag('mailbox').getAttr('url')
@ -1856,7 +1910,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _rosterItemExchangeCB(self, con, msg): def _rosterItemExchangeCB(self, con, msg):
''' XEP-0144 Roster Item Echange ''' """
XEP-0144 Roster Item Echange
"""
exchange_items_list = {} exchange_items_list = {}
jid_from = helpers.get_full_jid_from_iq(msg) jid_from = helpers.get_full_jid_from_iq(msg)
items_list = msg.getTag('x').getChildren() items_list = msg.getTag('x').getChildren()
@ -1898,7 +1954,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _messageCB(self, con, msg): def _messageCB(self, con, msg):
'''Called when we receive a message''' """
Called when we receive a message
"""
log.debug('MessageCB') log.debug('MessageCB')
mtype = msg.getType() mtype = msg.getType()
@ -2167,7 +2225,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
is_continued)) is_continued))
def _presenceCB(self, con, prs): def _presenceCB(self, con, prs):
'''Called when we receive a presence''' """
Called when we receive a presence
"""
ptype = prs.getType() ptype = prs.getType()
if ptype == 'available': if ptype == 'available':
ptype = None ptype = None
@ -2269,7 +2329,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
gc_control = gajim.interface.msg_win_mgr.get_gc_control(room_jid, gc_control = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
self.name) self.name)
# If gc_control is missing - it may be minimized. Try to get it from # If gc_control is missing - it may be minimized. Try to get it from
# there. If it's not there - then it's missing anyway and will # there. If it's not there - then it's missing anyway and will
# remain set to None. # remain set to None.
@ -2550,11 +2610,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _PrivacySetCB(self, con, iq_obj): def _PrivacySetCB(self, con, iq_obj):
''' """
Privacy lists (XEP 016) Privacy lists (XEP 016)
A list has been set A list has been set.
''' """
log.debug('PrivacySetCB') log.debug('PrivacySetCB')
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return

View file

@ -33,26 +33,28 @@ import caps
from account import Account from account import Account
class XMPPEntity(object): class XMPPEntity(object):
'''Base representation of entities in XMPP''' """
Base representation of entities in XMPP
"""
def __init__(self, jid, account, resource): def __init__(self, jid, account, resource):
self.jid = jid self.jid = jid
self.resource = resource self.resource = resource
self.account = account self.account = account
class CommonContact(XMPPEntity): class CommonContact(XMPPEntity):
def __init__(self, jid, account, resource, show, status, name, our_chatstate, def __init__(self, jid, account, resource, show, status, name,
composing_xep, chatstate, client_caps=None): our_chatstate, composing_xep, chatstate, client_caps=None):
XMPPEntity.__init__(self, jid, account, resource) XMPPEntity.__init__(self, jid, account, resource)
self.show = show self.show = show
self.status = status self.status = status
self.name = name self.name = name
self.client_caps = client_caps or caps.NullClientCaps() self.client_caps = client_caps or caps.NullClientCaps()
# please read xep-85 http://www.xmpp.org/extensions/xep-0085.html # please read xep-85 http://www.xmpp.org/extensions/xep-0085.html
# we keep track of xep85 support with the peer by three extra states: # we keep track of xep85 support with the peer by three extra states:
# None, False and 'ask' # None, False and 'ask'
@ -67,18 +69,18 @@ class CommonContact(XMPPEntity):
self.composing_xep = composing_xep self.composing_xep = composing_xep
# this is contact's chatstate # this is contact's chatstate
self.chatstate = chatstate self.chatstate = chatstate
def get_full_jid(self): def get_full_jid(self):
raise NotImplementedError raise NotImplementedError
def get_shown_name(self): def get_shown_name(self):
raise NotImplementedError raise NotImplementedError
def supports(self, requested_feature): def supports(self, requested_feature):
''' """
Returns True if the contact has advertised to support the feature Return True if the contact has advertised to support the feature
identified by the given namespace. False otherwise. identified by the given namespace. False otherwise.
''' """
if self.show == 'offline': if self.show == 'offline':
# Unfortunately, if all resources are offline, the contact # Unfortunately, if all resources are offline, the contact
# includes the last resource that was online. Check for its # includes the last resource that was online. Check for its
@ -90,26 +92,29 @@ class CommonContact(XMPPEntity):
class Contact(CommonContact): class Contact(CommonContact):
'''Information concerning each contact''' """
def __init__(self, jid, account, name='', groups=[], show='', status='', sub='', Information concerning each contact
ask='', resource='', priority=0, keyID='', client_caps=None, """
our_chatstate=None, chatstate=None, last_status_time=None, msg_id = None,
composing_xep=None): def __init__(self, jid, account, name='', groups=[], show='', status='',
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
CommonContact.__init__(self, jid, account, resource, show, status, name, our_chatstate=None, chatstate=None, last_status_time=None, msg_id =
None, composing_xep=None):
CommonContact.__init__(self, jid, account, resource, show, status, name,
our_chatstate, composing_xep, chatstate, client_caps=client_caps) our_chatstate, composing_xep, chatstate, client_caps=client_caps)
self.contact_name = '' # nick choosen by contact self.contact_name = '' # nick choosen by contact
self.groups = [i for i in set(groups)] # filter duplicate values self.groups = [i for i in set(groups)] # filter duplicate values
self.sub = sub self.sub = sub
self.ask = ask self.ask = ask
self.priority = priority self.priority = priority
self.keyID = keyID self.keyID = keyID
self.msg_id = msg_id self.msg_id = msg_id
self.last_status_time = last_status_time self.last_status_time = last_status_time
self.pep = {} self.pep = {}
def get_full_jid(self): def get_full_jid(self):
@ -137,7 +142,9 @@ class Contact(CommonContact):
return self.groups return self.groups
def is_hidden_from_roster(self): def is_hidden_from_roster(self):
'''if contact should not be visible in roster''' """
If contact should not be visible in roster
"""
# XEP-0162: http://www.xmpp.org/extensions/xep-0162.html # XEP-0162: http://www.xmpp.org/extensions/xep-0162.html
if self.is_transport(): if self.is_transport():
return False return False
@ -173,43 +180,50 @@ class Contact(CommonContact):
class GC_Contact(CommonContact): class GC_Contact(CommonContact):
'''Information concerning each groupchat contact''' """
Information concerning each groupchat contact
"""
def __init__(self, room_jid, account, name='', show='', status='', role='', def __init__(self, room_jid, account, name='', show='', status='', role='',
affiliation='', jid='', resource='', our_chatstate=None, affiliation='', jid='', resource='', our_chatstate=None,
composing_xep=None, chatstate=None): composing_xep=None, chatstate=None):
CommonContact.__init__(self, jid, account, resource, show, status, name, CommonContact.__init__(self, jid, account, resource, show, status, name,
our_chatstate, composing_xep, chatstate) our_chatstate, composing_xep, chatstate)
self.room_jid = room_jid self.room_jid = room_jid
self.role = role self.role = role
self.affiliation = affiliation self.affiliation = affiliation
def get_full_jid(self): def get_full_jid(self):
return self.room_jid + '/' + self.name return self.room_jid + '/' + self.name
def get_shown_name(self): def get_shown_name(self):
return self.name return self.name
def as_contact(self): def as_contact(self):
'''Create a Contact instance from this GC_Contact instance''' """
Create a Contact instance from this GC_Contact instance
"""
return Contact(jid=self.get_full_jid(), account=self.account, return Contact(jid=self.get_full_jid(), account=self.account,
resource=self.resource, name=self.name, groups=[], show=self.show, resource=self.resource, name=self.name, groups=[], show=self.show,
status=self.status, sub='none', client_caps=self.client_caps) status=self.status, sub='none', client_caps=self.client_caps)
class Contacts: class Contacts:
'''Information concerning all contacts and groupchat contacts''' """
Information concerning all contacts and groupchat contacts
"""
def __init__(self): def __init__(self):
self._metacontact_manager = MetacontactManager(self) self._metacontact_manager = MetacontactManager(self)
self._accounts = {} self._accounts = {}
def change_account_name(self, old_name, new_name): def change_account_name(self, old_name, new_name):
self._accounts[new_name] = self._accounts[old_name] self._accounts[new_name] = self._accounts[old_name]
self._accounts[new_name].name = new_name self._accounts[new_name].name = new_name
del self._accounts[old_name] del self._accounts[old_name]
self._metacontact_manager.change_account_name(old_name, new_name) self._metacontact_manager.change_account_name(old_name, new_name)
def add_account(self, account_name): def add_account(self, account_name):
@ -234,7 +248,7 @@ class Contacts:
keyID=keyID, client_caps=client_caps, our_chatstate=our_chatstate, keyID=keyID, client_caps=client_caps, our_chatstate=our_chatstate,
chatstate=chatstate, last_status_time=last_status_time, chatstate=chatstate, last_status_time=last_status_time,
composing_xep=composing_xep) composing_xep=composing_xep)
def create_self_contact(self, jid, account, resource, show, status, priority, def create_self_contact(self, jid, account, resource, show, status, priority,
name='', keyID=''): name='', keyID=''):
conn = common.gajim.connections[account] conn = common.gajim.connections[account]
@ -246,7 +260,7 @@ class Contacts:
resource=resource) resource=resource)
self_contact.pep = conn.pep self_contact.pep = conn.pep
return self_contact return self_contact
def create_not_in_roster_contact(self, jid, account, resource='', name='', keyID=''): def create_not_in_roster_contact(self, jid, account, resource='', name='', keyID=''):
account = self._accounts.get(account, account) # Use Account object if available account = self._accounts.get(account, account) # Use Account object if available
return self.create_contact(jid=jid, account=account, resource=resource, return self.create_contact(jid=jid, account=account, resource=resource,
@ -275,7 +289,7 @@ class Contacts:
self._accounts[account].contacts.remove_jid(jid) self._accounts[account].contacts.remove_jid(jid)
if remove_meta: if remove_meta:
self._metacontact_manager.remove_metacontact(account, jid) self._metacontact_manager.remove_metacontact(account, jid)
def get_contacts(self, account, jid): def get_contacts(self, account, jid):
return self._accounts[account].contacts.get_contacts(jid) return self._accounts[account].contacts.get_contacts(jid)
@ -288,13 +302,13 @@ class Contacts:
def get_contact_from_full_jid(self, account, fjid): def get_contact_from_full_jid(self, account, fjid):
return self._accounts[account].contacts.get_contact_from_full_jid(fjid) return self._accounts[account].contacts.get_contact_from_full_jid(fjid)
def get_first_contact_from_jid(self, account, jid): def get_first_contact_from_jid(self, account, jid):
return self._accounts[account].contacts.get_first_contact_from_jid(jid) return self._accounts[account].contacts.get_first_contact_from_jid(jid)
def get_contacts_from_group(self, account, group): def get_contacts_from_group(self, account, group):
return self._accounts[account].contacts.get_contacts_from_group(group) return self._accounts[account].contacts.get_contacts_from_group(group)
def get_jid_list(self, account): def get_jid_list(self, account):
return self._accounts[account].contacts.get_jid_list() return self._accounts[account].contacts.get_jid_list()
@ -318,10 +332,11 @@ class Contacts:
contact = self.get_gc_contact(account, room, nick) contact = self.get_gc_contact(account, room, nick)
return contact return contact
return self.get_highest_prio_contact_from_contacts(contacts) return self.get_highest_prio_contact_from_contacts(contacts)
def get_nb_online_total_contacts(self, accounts=[], groups=[]): def get_nb_online_total_contacts(self, accounts=[], groups=[]):
'''Returns the number of online contacts and the total number of """
contacts''' Return the number of online contacts and the total number of contacts
"""
if accounts == []: if accounts == []:
accounts = self.get_accounts() accounts = self.get_accounts()
nbr_online = 0 nbr_online = 0
@ -358,24 +373,28 @@ class Contacts:
return nbr_online, nbr_total return nbr_online, nbr_total
def is_pm_from_jid(self, account, jid): def is_pm_from_jid(self, account, jid):
'''Returns True if the given jid is a private message jid''' """
Return True if the given jid is a private message jid
"""
if jid in self._contacts[account]: if jid in self._contacts[account]:
return False return False
return True return True
def is_pm_from_contact(self, account, contact): def is_pm_from_contact(self, account, contact):
'''Returns True if the given contact is a private message contact''' """
Return True if the given contact is a private message contact
"""
if isinstance(contact, Contact): if isinstance(contact, Contact):
return False return False
return True return True
def __getattr__(self, attr_name): def __getattr__(self, attr_name):
# Only called if self has no attr_name # Only called if self has no attr_name
if hasattr(self._metacontact_manager, attr_name): if hasattr(self._metacontact_manager, attr_name):
return getattr(self._metacontact_manager, attr_name) return getattr(self._metacontact_manager, attr_name)
else: else:
raise AttributeError(attr_name) raise AttributeError(attr_name)
def create_gc_contact(self, room_jid, account, name='', show='', status='', def create_gc_contact(self, room_jid, account, name='', show='', status='',
role='', affiliation='', jid='', resource=''): role='', affiliation='', jid='', resource=''):
account = self._accounts.get(account, account) # Use Account object if available account = self._accounts.get(account, account) # Use Account object if available
@ -402,15 +421,15 @@ class Contacts:
def get_nb_role_total_gc_contacts(self, account, room_jid, role): def get_nb_role_total_gc_contacts(self, account, room_jid, role):
return self._accounts[account].gc_contacts.get_nb_role_total_gc_contacts(room_jid, role) return self._accounts[account].gc_contacts.get_nb_role_total_gc_contacts(room_jid, role)
class Contacts_New(): class Contacts_New():
def __init__(self): def __init__(self):
# list of contacts {jid1: [C1, C2]}, } one Contact per resource # list of contacts {jid1: [C1, C2]}, } one Contact per resource
self._contacts = {} self._contacts = {}
def add_contact(self, contact): def add_contact(self, contact):
if contact.jid not in self._contacts: if contact.jid not in self._contacts:
self._contacts[contact.jid] = [contact] self._contacts[contact.jid] = [contact]
return return
@ -426,7 +445,7 @@ class Contacts_New():
self.remove_contact(c) self.remove_contact(c)
break break
contacts.append(contact) contacts.append(contact)
def remove_contact(self, contact): def remove_contact(self, contact):
if contact.jid not in self._contacts: if contact.jid not in self._contacts:
return return
@ -434,28 +453,34 @@ class Contacts_New():
self._contacts[contact.jid].remove(contact) self._contacts[contact.jid].remove(contact)
if len(self._contacts[contact.jid]) == 0: if len(self._contacts[contact.jid]) == 0:
del self._contacts[contact.jid] del self._contacts[contact.jid]
def remove_jid(self, jid): def remove_jid(self, jid):
'''Removes all contacts for a given jid''' """
Remove all contacts for a given jid
"""
if jid not in self._contacts: if jid not in self._contacts:
return return
del self._contacts[jid] del self._contacts[jid]
def get_contacts(self, jid): def get_contacts(self, jid):
'''Returns the list of contact instances for this jid.''' """
Return the list of contact instances for this jid
"""
if jid in self._contacts: if jid in self._contacts:
return self._contacts[jid] return self._contacts[jid]
else: else:
return [] return []
def get_contact(self, jid, resource=None): def get_contact(self, jid, resource=None):
### WARNING ### ### WARNING ###
# This function returns a *RANDOM* resource if resource = None! # This function returns a *RANDOM* resource if resource = None!
# Do *NOT* use if you need to get the contact to which you # Do *NOT* use if you need to get the contact to which you
# send a message for example, as a bare JID in Jabber means # send a message for example, as a bare JID in Jabber means
# highest available resource, which this function ignores! # highest available resource, which this function ignores!
'''Returns the contact instance for the given resource if it's given else """
the first contact is no resource is given or None if there is not''' Return the contact instance for the given resource if it's given else the
first contact is no resource is given or None if there is not
"""
if jid in self._contacts: if jid in self._contacts:
if not resource: if not resource:
return self._contacts[jid][0] return self._contacts[jid][0]
@ -468,22 +493,26 @@ class Contacts_New():
for jid in self._contacts.keys(): for jid in self._contacts.keys():
for contact in self._contacts[jid][:]: for contact in self._contacts[jid][:]:
yield contact yield contact
def get_jid_list(self): def get_jid_list(self):
return self._contacts.keys() return self._contacts.keys()
def get_contact_from_full_jid(self, fjid): def get_contact_from_full_jid(self, fjid):
''' Get Contact object for specific resource of given jid''' """
Get Contact object for specific resource of given jid
"""
barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid) barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid)
return self.get_contact(barejid, resource) return self.get_contact(barejid, resource)
def get_first_contact_from_jid(self, jid): def get_first_contact_from_jid(self, jid):
if jid in self._contacts: if jid in self._contacts:
return self._contacts[jid][0] return self._contacts[jid][0]
return None return None
def get_contacts_from_group(self, group): def get_contacts_from_group(self, group):
'''Returns all contacts in the given group''' """
Return all contacts in the given group
"""
group_contacts = [] group_contacts = []
for jid in self._contacts: for jid in self._contacts:
contacts = self.get_contacts(jid) contacts = self.get_contacts(jid)
@ -502,11 +531,11 @@ class Contacts_New():
class GC_Contacts(): class GC_Contacts():
def __init__(self): def __init__(self):
# list of contacts that are in gc {room_jid: {nick: C}}} # list of contacts that are in gc {room_jid: {nick: C}}}
self._rooms = {} self._rooms = {}
def add_gc_contact(self, gc_contact): def add_gc_contact(self, gc_contact):
if gc_contact.room_jid not in self._rooms: if gc_contact.room_jid not in self._rooms:
self._rooms[gc_contact.room_jid] = {gc_contact.name: gc_contact} self._rooms[gc_contact.room_jid] = {gc_contact.name: gc_contact}
@ -544,8 +573,10 @@ class GC_Contacts():
return self._rooms[room_jid][nick] return self._rooms[room_jid][nick]
def get_nb_role_total_gc_contacts(self, room_jid, role): def get_nb_role_total_gc_contacts(self, room_jid, role):
'''Returns the number of group chat contacts for the given role and the """
total number of group chat contacts''' Return the number of group chat contacts for the given role and the total
number of group chat contacts
"""
if room_jid not in self._rooms: if room_jid not in self._rooms:
return 0, 0 return 0, 0
nb_role = nb_total = 0 nb_role = nb_total = 0
@ -554,25 +585,25 @@ class GC_Contacts():
nb_role += 1 nb_role += 1
nb_total += 1 nb_total += 1
return nb_role, nb_total return nb_role, nb_total
class MetacontactManager(): class MetacontactManager():
def __init__(self, contacts): def __init__(self, contacts):
self._metacontacts_tags = {} self._metacontacts_tags = {}
self._contacts = contacts self._contacts = contacts
def change_account_name(self, old_name, new_name): def change_account_name(self, old_name, new_name):
self._metacontacts_tags[new_name] = self._metacontacts_tags[old_name] self._metacontacts_tags[new_name] = self._metacontacts_tags[old_name]
del self._metacontacts_tags[old_name] del self._metacontacts_tags[old_name]
def add_account(self, account): def add_account(self, account):
if account not in self._metacontacts_tags: if account not in self._metacontacts_tags:
self._metacontacts_tags[account] = {} self._metacontacts_tags[account] = {}
def remove_account(self, account): def remove_account(self, account):
del self._metacontacts_tags[account] del self._metacontacts_tags[account]
def define_metacontacts(self, account, tags_list): def define_metacontacts(self, account, tags_list):
self._metacontacts_tags[account] = tags_list self._metacontacts_tags[account] = tags_list
@ -582,13 +613,15 @@ class MetacontactManager():
#FIXME: can this append ? #FIXME: can this append ?
assert False assert False
def iter_metacontacts_families(self, account): def iter_metacontacts_families(self, account):
for tag in self._metacontacts_tags[account]: for tag in self._metacontacts_tags[account]:
family = self._get_metacontacts_family_from_tag(account, tag) family = self._get_metacontacts_family_from_tag(account, tag)
yield family yield family
def _get_metacontacts_tag(self, account, jid): def _get_metacontacts_tag(self, account, jid):
'''Returns the tag of a jid''' """
Return the tag of a jid
"""
if not account in self._metacontacts_tags: if not account in self._metacontacts_tags:
return None return None
for tag in self._metacontacts_tags[account]: for tag in self._metacontacts_tags[account]:
@ -626,7 +659,7 @@ class MetacontactManager():
def remove_metacontact(self, account, jid): def remove_metacontact(self, account, jid):
if not account in self._metacontacts_tags: if not account in self._metacontacts_tags:
return None return None
found = None found = None
for tag in self._metacontacts_tags[account]: for tag in self._metacontacts_tags[account]:
for data in self._metacontacts_tags[account][tag]: for data in self._metacontacts_tags[account][tag]:
@ -657,7 +690,9 @@ class MetacontactManager():
return False return False
def _get_metacontacts_jids(self, tag, accounts): def _get_metacontacts_jids(self, tag, accounts):
'''Returns all jid for the given tag in the form {acct: [jid1, jid2],.}''' """
Return all jid for the given tag in the form {acct: [jid1, jid2],.}
"""
answers = {} answers = {}
for account in self._metacontacts_tags: for account in self._metacontacts_tags:
if tag in self._metacontacts_tags[account]: if tag in self._metacontacts_tags[account]:
@ -669,9 +704,10 @@ class MetacontactManager():
return answers return answers
def get_metacontacts_family(self, account, jid): def get_metacontacts_family(self, account, jid):
'''return the family of the given jid, including jid in the form: """
[{'account': acct, 'jid': jid, 'order': order}, ] Return the family of the given jid, including jid in the form:
'order' is optional''' [{'account': acct, 'jid': jid, 'order': order}, ] 'order' is optional
"""
tag = self._get_metacontacts_tag(account, jid) tag = self._get_metacontacts_tag(account, jid)
return self._get_metacontacts_family_from_tag(account, tag) return self._get_metacontacts_family_from_tag(account, tag)
@ -687,9 +723,12 @@ class MetacontactManager():
return answers return answers
def _compare_metacontacts(self, data1, data2): def _compare_metacontacts(self, data1, data2):
'''compare 2 metacontacts. """
Data is {'jid': jid, 'account': account, 'order': order} Compare 2 metacontacts
order is optional'''
Data is {'jid': jid, 'account': account, 'order': order} order is
optional
"""
jid1 = data1['jid'] jid1 = data1['jid']
jid2 = data2['jid'] jid2 = data2['jid']
account1 = data1['account'] account1 = data1['account']
@ -765,16 +804,17 @@ class MetacontactManager():
if account2 > account1: if account2 > account1:
return -1 return -1
return 0 return 0
def get_nearby_family_and_big_brother(self, family, account):
'''Return the nearby family and its Big Brother
Nearby family is the part of the family that is grouped with the metacontact. def get_nearby_family_and_big_brother(self, family, account):
A metacontact may be over different accounts. If accounts are not merged """
then the given family is split account wise. Return the nearby family and its Big Brother
Nearby family is the part of the family that is grouped with the
metacontact. A metacontact may be over different accounts. If accounts
are not merged then the given family is split account wise.
(nearby_family, big_brother_jid, big_brother_account) (nearby_family, big_brother_jid, big_brother_account)
''' """
if common.gajim.config.get('mergeaccounts'): if common.gajim.config.get('mergeaccounts'):
# group all together # group all together
nearby_family = family nearby_family = family
@ -789,9 +829,11 @@ class MetacontactManager():
return (nearby_family, big_brother_jid, big_brother_account) return (nearby_family, big_brother_jid, big_brother_account)
def _get_metacontacts_big_brother(self, family): def _get_metacontacts_big_brother(self, family):
'''which of the family will be the big brother under wich all """
others will be ?''' Which of the family will be the big brother under wich all others will be
?
"""
family.sort(cmp=self._compare_metacontacts) family.sort(cmp=self._compare_metacontacts)
return family[-1] return family[-1]
# vim: se ts=3: # vim: se ts=3: