A portion of doc-string refactoring
This commit is contained in:
parent
a9a442c01c
commit
cea7c66f75
11 changed files with 637 additions and 364 deletions
|
@ -213,7 +213,9 @@ if gajim.HAVE_GPG:
|
|||
return self.get_keys(True)
|
||||
|
||||
def _stripHeaderFooter(self, data):
|
||||
"""Remove header and footer from data"""
|
||||
"""
|
||||
Remove header and footer from data
|
||||
"""
|
||||
if not data: return ''
|
||||
lines = data.split('\n')
|
||||
while lines[0] != '':
|
||||
|
@ -229,7 +231,9 @@ if gajim.HAVE_GPG:
|
|||
return line
|
||||
|
||||
def _addHeaderFooter(self, data, type_):
|
||||
"""Add header and footer from data"""
|
||||
"""
|
||||
Add header and footer from data
|
||||
"""
|
||||
out = "-----BEGIN PGP %s-----\n" % type_
|
||||
out = out + "Version: PGP\n"
|
||||
out = out + "\n"
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
## 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.
|
||||
It concentrates on interacting with GnuPG via filehandles,
|
||||
|
@ -249,7 +250,8 @@ _fd_options = { 'passphrase': '--passphrase-fd',
|
|||
'command': '--command-fd' }
|
||||
|
||||
class GnuPG:
|
||||
"""Class instances represent GnuPG.
|
||||
"""
|
||||
Class instances represent GnuPG
|
||||
|
||||
Instance attributes of a GnuPG object are:
|
||||
|
||||
|
@ -275,8 +277,10 @@ class GnuPG:
|
|||
self.options = Options()
|
||||
|
||||
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
|
||||
'["--sign", "--encrypt"]'
|
||||
Returns a GnuPGInterface.Process object.
|
||||
|
@ -336,7 +340,6 @@ class GnuPG:
|
|||
is a FileObject connected to GnuPG's standard input,
|
||||
and can be written to.
|
||||
"""
|
||||
|
||||
if args is None: args = []
|
||||
if create_fhs is None: create_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):
|
||||
"""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()
|
||||
|
||||
for fh_name in create_fhs + attach_fhs.keys():
|
||||
|
@ -404,7 +408,9 @@ class GnuPG:
|
|||
|
||||
|
||||
def _as_parent(self, process):
|
||||
"""Stuff run after forking in parent"""
|
||||
"""
|
||||
Stuff run after forking in parent
|
||||
"""
|
||||
for k, p in process._pipes.items():
|
||||
if not p.direct:
|
||||
os.close(p.child)
|
||||
|
@ -417,7 +423,9 @@ class GnuPG:
|
|||
|
||||
|
||||
def _as_child(self, process, gnupg_commands, args):
|
||||
"""Stuff run after forking in child"""
|
||||
"""
|
||||
Stuff run after forking in child
|
||||
"""
|
||||
# child
|
||||
for std in _stds:
|
||||
p = process._pipes[std]
|
||||
|
@ -444,7 +452,10 @@ class GnuPG:
|
|||
|
||||
|
||||
class Pipe:
|
||||
"""simple struct holding stuff about pipes we use"""
|
||||
"""
|
||||
Simple struct holding stuff about pipes we use
|
||||
"""
|
||||
|
||||
def __init__(self, parent, child, direct):
|
||||
self.parent = parent
|
||||
self.child = child
|
||||
|
@ -452,7 +463,8 @@ class Pipe:
|
|||
|
||||
|
||||
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
|
||||
which are based on options. It can be said that a GnuPG
|
||||
object has-a Options object in its options attribute.
|
||||
|
@ -522,7 +534,6 @@ class Options:
|
|||
>>> gnupg.options.get_args()
|
||||
['--armor', '--recipient', 'Alice', '--recipient', 'Bob', '--no-secmem-warning']
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# booleans
|
||||
self.armor = 0
|
||||
|
@ -558,12 +569,15 @@ class Options:
|
|||
self.extra_args = []
|
||||
|
||||
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
|
||||
|
||||
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 = []
|
||||
if self.homedir is not None:
|
||||
args.extend( [ '--homedir', self.homedir ] )
|
||||
|
@ -595,7 +609,9 @@ class Options:
|
|||
return args
|
||||
|
||||
def get_meta_args( self ):
|
||||
"""Get a list of generated meta-arguments"""
|
||||
"""
|
||||
Get a list of generated meta-arguments
|
||||
"""
|
||||
args = []
|
||||
|
||||
if self.meta_pgp_5_compatible: args.extend( [ '--compress-algo', '1',
|
||||
|
@ -608,8 +624,9 @@ class Options:
|
|||
|
||||
|
||||
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
|
||||
process = gnupg.run( [ '--decrypt' ], stdout = 1 )
|
||||
|
@ -637,9 +654,10 @@ class Process:
|
|||
self._waited = None
|
||||
|
||||
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]
|
||||
if e != 0:
|
||||
raise IOError, "GnuPG exited non-zero, with code %d" % (e << 8)
|
||||
|
|
|
@ -19,14 +19,14 @@
|
|||
##
|
||||
|
||||
class Account(object):
|
||||
|
||||
|
||||
def __init__(self, name, contacts, gc_contacts):
|
||||
self.name = name
|
||||
self.contacts = contacts
|
||||
self.gc_contacts = gc_contacts
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
def __hash__(self):
|
||||
return self.name.__hash__()
|
||||
return hash(self.name)
|
||||
|
|
|
@ -20,9 +20,11 @@
|
|||
## 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
|
||||
if you need. '''
|
||||
if you need
|
||||
"""
|
||||
|
||||
# suggestion: rewrite functions that return dates to return standard python time tuples,
|
||||
# exteneded to contain timezone
|
||||
|
@ -31,8 +33,11 @@ import xmpp
|
|||
import time
|
||||
|
||||
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):
|
||||
''' Create person construct from node. '''
|
||||
xmpp.Node.__init__(self, node=node)
|
||||
|
@ -60,15 +65,17 @@ class PersonConstruct(xmpp.Node, object):
|
|||
|
||||
class Entry(xmpp.Node, object):
|
||||
def __init__(self, node=None):
|
||||
''' Create new atom entry object. '''
|
||||
xmpp.Node.__init__(self, 'entry', node=node)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Atom:Entry object of id="%r">' % self.getAttr('id')
|
||||
|
||||
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):
|
||||
''' Create new Atom 0.3 entry object. '''
|
||||
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')
|
||||
|
||||
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:
|
||||
main_feed = self.parent.getTagData('title')
|
||||
else:
|
||||
|
@ -104,7 +113,9 @@ class OldEntry(xmpp.Node, object):
|
|||
which delivered this entry. ''')
|
||||
|
||||
def get_feed_link(self):
|
||||
''' Get source link '''
|
||||
"""
|
||||
Get source link
|
||||
"""
|
||||
try:
|
||||
return self.getTag('feed').getTags('link',{'rel':'alternate'})[1].getData()
|
||||
except Exception:
|
||||
|
@ -114,15 +125,19 @@ class OldEntry(xmpp.Node, object):
|
|||
''' Link to main webpage of the feed. ''')
|
||||
|
||||
def get_title(self):
|
||||
''' Get an entry's title. '''
|
||||
"""
|
||||
Get an entry's title
|
||||
"""
|
||||
return self.getTagData('title')
|
||||
|
||||
title = property(get_title, None, None,
|
||||
''' Entry's title. ''')
|
||||
|
||||
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'):
|
||||
if 'rel' in element.attrs and element.attrs['rel']!='alternate': continue
|
||||
try:
|
||||
|
@ -135,12 +150,16 @@ class OldEntry(xmpp.Node, object):
|
|||
''' URI that is pointed by the entry. ''')
|
||||
|
||||
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.
|
||||
(Atom 0.3 uses the word «modified» for that).
|
||||
"""
|
||||
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. (Atom 0.3 uses the word «modified» for
|
||||
that).
|
||||
|
||||
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'):
|
||||
date = self.getTagData(name)
|
||||
if date is not None: break
|
||||
|
|
|
@ -23,13 +23,13 @@
|
|||
## 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:
|
||||
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 helpers
|
||||
|
@ -54,7 +54,9 @@ CACHED = 2 # got the answer
|
|||
|
||||
capscache = None
|
||||
def initialize(logger):
|
||||
''' Initializes this module '''
|
||||
"""
|
||||
Initialize this module
|
||||
"""
|
||||
global capscache
|
||||
capscache = CapsCache(logger)
|
||||
|
||||
|
@ -73,11 +75,12 @@ def client_supports(client_caps, requested_feature):
|
|||
return False
|
||||
|
||||
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
|
||||
values without a field type list-multi'''
|
||||
|
||||
values without a field type list-multi
|
||||
"""
|
||||
def sort_identities_func(i1, i2):
|
||||
cat1 = i1['category']
|
||||
cat2 = i2['category']
|
||||
|
@ -98,14 +101,14 @@ def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
|
|||
if lang1 > lang2:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def sort_dataforms_func(d1, d2):
|
||||
f1 = d1.getField('FORM_TYPE')
|
||||
f2 = d2.getField('FORM_TYPE')
|
||||
if f1 and f2 and (f1.getValue() < f2.getValue()):
|
||||
return -1
|
||||
return 1
|
||||
|
||||
|
||||
S = ''
|
||||
identities.sort(cmp=sort_identities_func)
|
||||
for i in identities:
|
||||
|
@ -140,108 +143,119 @@ def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
|
|||
else:
|
||||
return ''
|
||||
return base64.b64encode(hash_.digest())
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
### Internal classes of this module
|
||||
################################################################################
|
||||
|
||||
class AbstractClientCaps(object):
|
||||
'''
|
||||
Base class representing a client and its capabilities as advertised by
|
||||
a caps tag in a presence.
|
||||
'''
|
||||
"""
|
||||
Base class representing a client and its capabilities as advertised by a
|
||||
caps tag in a presence
|
||||
"""
|
||||
def __init__(self, caps_hash, node):
|
||||
self._hash = caps_hash
|
||||
self._node = node
|
||||
|
||||
|
||||
def get_discover_strategy(self):
|
||||
return self._discover
|
||||
|
||||
|
||||
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):
|
||||
return self._lookup_in_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):
|
||||
return self._is_hash_valid
|
||||
|
||||
|
||||
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):
|
||||
''' The current XEP-115 implementation '''
|
||||
|
||||
"""
|
||||
The current XEP-115 implementation
|
||||
"""
|
||||
|
||||
def __init__(self, caps_hash, node, hash_method):
|
||||
AbstractClientCaps.__init__(self, caps_hash, node)
|
||||
assert hash_method != 'old'
|
||||
self._hash_method = hash_method
|
||||
|
||||
|
||||
def _lookup_in_cache(self, caps_cache):
|
||||
return caps_cache[(self._hash_method, self._hash)]
|
||||
|
||||
|
||||
def _discover(self, connection, jid):
|
||||
connection.discoverInfo(jid, '%s#%s' % (self._node, self._hash))
|
||||
|
||||
|
||||
def _is_hash_valid(self, identities, features, dataforms):
|
||||
computed_hash = compute_caps_hash(identities, features,
|
||||
dataforms=dataforms, hash_method=self._hash_method)
|
||||
return computed_hash == self._hash
|
||||
return computed_hash == self._hash
|
||||
|
||||
|
||||
|
||||
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):
|
||||
AbstractClientCaps.__init__(self, caps_hash, node)
|
||||
|
||||
def _lookup_in_cache(self, caps_cache):
|
||||
return caps_cache[('old', self._node + '#' + self._hash)]
|
||||
|
||||
|
||||
def _discover(self, connection, 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):
|
||||
'''
|
||||
"""
|
||||
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.
|
||||
'''
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
AbstractClientCaps.__init__(self, None, None)
|
||||
|
||||
|
||||
def _lookup_in_cache(self, caps_cache):
|
||||
# lookup something which does not exist to get a new CacheItem created
|
||||
cache_item = caps_cache[('dummy', '')]
|
||||
assert cache_item.status != CACHED
|
||||
return cache_item
|
||||
|
||||
|
||||
def _discover(self, connection, jid):
|
||||
pass
|
||||
|
||||
def _is_hash_valid(self, identities, features, dataforms):
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
class CapsCache(object):
|
||||
'''
|
||||
This object keeps the mapping between caps data and real disco
|
||||
features they represent, and provides simple way to query that info.
|
||||
'''
|
||||
"""
|
||||
This object keeps the mapping between caps data and real disco features they
|
||||
represent, and provides simple way to query that info
|
||||
"""
|
||||
|
||||
def __init__(self, logger=None):
|
||||
# our containers:
|
||||
# __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
|
||||
# strings. therefore we can cache them
|
||||
__names = {}
|
||||
|
||||
|
||||
def __init__(self, hash_method, hash_, logger):
|
||||
# cached into db
|
||||
self.hash_method = hash_method
|
||||
|
@ -274,7 +288,7 @@ class CapsCache(object):
|
|||
self._features = []
|
||||
for feature in value:
|
||||
self._features.append(self.__names.setdefault(feature, feature))
|
||||
|
||||
|
||||
features = property(_get_features, _set_features)
|
||||
|
||||
def _get_identities(self):
|
||||
|
@ -291,7 +305,7 @@ class CapsCache(object):
|
|||
d['name'] = i[3]
|
||||
list_.append(d)
|
||||
return list_
|
||||
|
||||
|
||||
def _set_identities(self, value):
|
||||
self._identities = []
|
||||
for identity in value:
|
||||
|
@ -299,7 +313,7 @@ class CapsCache(object):
|
|||
t = (identity['category'], identity.get('type'),
|
||||
identity.get('xml:lang'), identity.get('name'))
|
||||
self._identities.append(self.__names.setdefault(t, t))
|
||||
|
||||
|
||||
identities = property(_get_identities, _set_identities)
|
||||
|
||||
def set_and_store(self, identities, features):
|
||||
|
@ -308,7 +322,7 @@ class CapsCache(object):
|
|||
self._logger.add_caps_entry(self.hash_method, self.hash,
|
||||
identities, features)
|
||||
self.status = CACHED
|
||||
|
||||
|
||||
def update_last_seen(self):
|
||||
if not self._recently_seen:
|
||||
self._recently_seen = True
|
||||
|
@ -325,9 +339,11 @@ class CapsCache(object):
|
|||
x.identities = identities
|
||||
x.features = features
|
||||
x.status = CACHED
|
||||
|
||||
|
||||
def _remove_outdated_caps(self):
|
||||
'''Removes outdated values from the db'''
|
||||
"""
|
||||
Remove outdated values from the db
|
||||
"""
|
||||
self.logger.clean_caps_table()
|
||||
|
||||
def __getitem__(self, caps):
|
||||
|
@ -341,20 +357,20 @@ class CapsCache(object):
|
|||
return x
|
||||
|
||||
def query_client_of_jid_if_unknown(self, connection, jid, client_caps):
|
||||
'''
|
||||
Start a disco query to determine caps (node, ver, exts).
|
||||
Won't query if the data is already in cache.
|
||||
'''
|
||||
"""
|
||||
Start a disco query to determine caps (node, ver, exts). Won't query if
|
||||
the data is already in cache
|
||||
"""
|
||||
lookup_cache_item = client_caps.get_cache_lookup_strategy()
|
||||
q = lookup_cache_item(self)
|
||||
|
||||
q = lookup_cache_item(self)
|
||||
|
||||
if q.status == NEW:
|
||||
# do query for bare node+hash pair
|
||||
# this will create proper object
|
||||
q.status = QUERIED
|
||||
discover = client_caps.get_discover_strategy()
|
||||
discover(connection, jid)
|
||||
else:
|
||||
else:
|
||||
q.update_last_seen()
|
||||
|
||||
################################################################################
|
||||
|
@ -362,14 +378,15 @@ class CapsCache(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):
|
||||
'''
|
||||
"""
|
||||
Handle incoming presence stanzas... This is a callback for xmpp
|
||||
registered in connection_handlers.py
|
||||
'''
|
||||
"""
|
||||
# we will put these into proper Contact object and ask
|
||||
# for disco... so that disco will learn how to interpret
|
||||
# these caps
|
||||
|
@ -411,7 +428,7 @@ class ConnectionCaps(object):
|
|||
if pm_ctrl:
|
||||
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)
|
||||
if not contact:
|
||||
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||
|
@ -420,14 +437,14 @@ class ConnectionCaps(object):
|
|||
return
|
||||
|
||||
lookup = contact.client_caps.get_cache_lookup_strategy()
|
||||
cache_item = lookup(capscache)
|
||||
|
||||
cache_item = lookup(capscache)
|
||||
|
||||
if cache_item.status == CACHED:
|
||||
return
|
||||
else:
|
||||
validate = contact.client_caps.get_hash_validation_strategy()
|
||||
hash_is_valid = validate(identities, features, dataforms)
|
||||
|
||||
|
||||
if hash_is_valid:
|
||||
cache_item.set_and_store(identities, features)
|
||||
else:
|
||||
|
|
|
@ -34,10 +34,13 @@ class AdHocCommand:
|
|||
|
||||
@staticmethod
|
||||
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
|
||||
jid.'''
|
||||
jid.
|
||||
"""
|
||||
return True
|
||||
|
||||
def __init__(self, conn, jid, sessionid):
|
||||
|
@ -80,7 +83,9 @@ class ChangeStatusCommand(AdHocCommand):
|
|||
|
||||
@staticmethod
|
||||
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
|
||||
|
||||
def execute(self, request):
|
||||
|
@ -177,7 +182,9 @@ class LeaveGroupchatsCommand(AdHocCommand):
|
|||
|
||||
@staticmethod
|
||||
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
|
||||
|
||||
def execute(self, request):
|
||||
|
@ -259,7 +266,9 @@ class ForwardMessagesCommand(AdHocCommand):
|
|||
|
||||
@staticmethod
|
||||
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
|
||||
|
||||
def execute(self, request):
|
||||
|
@ -282,7 +291,10 @@ class ForwardMessagesCommand(AdHocCommand):
|
|||
return False # finish the session
|
||||
|
||||
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):
|
||||
# a list of all commands exposed: node -> command class
|
||||
self.__commands = {}
|
||||
|
@ -297,7 +309,9 @@ class ConnectionCommands:
|
|||
return gajim.get_jid_from_account(self.name)
|
||||
|
||||
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()
|
||||
|
||||
def commandListQuery(self, con, iq_obj):
|
||||
|
@ -318,8 +332,10 @@ class ConnectionCommands:
|
|||
self.connection.send(iq)
|
||||
|
||||
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)
|
||||
node = iq_obj.getTagAttr('query', 'node')
|
||||
|
||||
|
@ -342,8 +358,10 @@ class ConnectionCommands:
|
|||
return False
|
||||
|
||||
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)
|
||||
node = iq_obj.getTagAttr('query', 'node')
|
||||
|
||||
|
|
|
@ -512,7 +512,9 @@ class Config:
|
|||
cb(data, opt3, [opt, opt2], dict_[opt2][opt3])
|
||||
|
||||
def get_children(self, node=None):
|
||||
''' Tree-like interface '''
|
||||
"""
|
||||
Tree-like interface
|
||||
"""
|
||||
if node is None:
|
||||
for child, option in self.__options.iteritems():
|
||||
yield (child, ), option
|
||||
|
@ -698,8 +700,9 @@ class Config:
|
|||
return False
|
||||
|
||||
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')
|
||||
|
||||
if not no_log_for:
|
||||
|
|
|
@ -46,7 +46,9 @@ import tempfile
|
|||
# not displayed to the user, Unicode is not really necessary here.
|
||||
|
||||
def fse(s):
|
||||
'''Convert from filesystem encoding if not already Unicode'''
|
||||
"""
|
||||
Convert from filesystem encoding if not already Unicode
|
||||
"""
|
||||
return unicode(s, sys.getfilesystemencoding())
|
||||
|
||||
def windowsify(s):
|
||||
|
|
|
@ -100,10 +100,11 @@ ssl_error = {
|
|||
}
|
||||
|
||||
class CommonConnection:
|
||||
'''
|
||||
"""
|
||||
Common connection class, can be derivated for normal connection or zeroconf
|
||||
connection
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
# self.connected:
|
||||
|
@ -162,11 +163,15 @@ class CommonConnection:
|
|||
return resource
|
||||
|
||||
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)
|
||||
|
||||
def _reconnect(self):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def quit(self, kill_core):
|
||||
|
@ -174,7 +179,9 @@ class CommonConnection:
|
|||
self.disconnect(on_purpose=True)
|
||||
|
||||
def test_gpg_passphrase(self, password):
|
||||
'''Returns 'ok', 'bad_pass' or 'expired' '''
|
||||
"""
|
||||
Returns 'ok', 'bad_pass' or 'expired'
|
||||
"""
|
||||
if not self.gpg:
|
||||
return False
|
||||
self.gpg.passphrase = password
|
||||
|
@ -188,10 +195,12 @@ class CommonConnection:
|
|||
return 'ok'
|
||||
|
||||
def get_signed_msg(self, msg, callback = None):
|
||||
'''returns the signed message if possible
|
||||
or an empty string if gpg is not used
|
||||
or None if waiting for passphrase.
|
||||
callback is the function to call when user give the passphrase'''
|
||||
"""
|
||||
Returns the signed message if possible or an empty string if gpg is not
|
||||
used or None if waiting for passphrase
|
||||
|
||||
callback is the function to call when user give the passphrase
|
||||
"""
|
||||
signed = ''
|
||||
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
|
||||
if keyID and self.USE_GPG:
|
||||
|
@ -208,7 +217,9 @@ class CommonConnection:
|
|||
return signed
|
||||
|
||||
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.dispatch('STATUS', 'offline')
|
||||
|
||||
|
@ -216,9 +227,10 @@ class CommonConnection:
|
|||
return gajim.SHOW_LIST[self.connected]
|
||||
|
||||
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
|
||||
|
||||
def _prepare_message(self, jid, msg, keyID, type_='chat', subject='',
|
||||
|
@ -424,32 +436,46 @@ class CommonConnection:
|
|||
common.logger.LOG_DB_PATH
|
||||
|
||||
def ack_subscribed(self, jid):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def ack_unsubscribed(self, jid):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def request_subscription(self, jid, msg='', name='', groups=[],
|
||||
auto_auth=False):
|
||||
'''To be implemented by derivated classes'''
|
||||
auto_auth=False):
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def send_authorization(self, jid):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def refuse_authorization(self, jid):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def unsubscribe(self, jid, remove_auth = True):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def unsubscribe_agent(self, agent):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def update_contact(self, jid, name, groups):
|
||||
|
@ -457,47 +483,67 @@ class CommonConnection:
|
|||
self.connection.getRoster().setItem(jid=jid, name=name, groups=groups)
|
||||
|
||||
def update_contacts(self, contacts):
|
||||
'''update multiple roster items'''
|
||||
"""
|
||||
Update multiple roster items
|
||||
"""
|
||||
if self.connection:
|
||||
self.connection.getRoster().setItemMulti(contacts)
|
||||
|
||||
def new_account(self, name, config, sync=False):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _on_new_account(self, con=None, con_type=None):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def account_changed(self, new_name):
|
||||
self.name = new_name
|
||||
|
||||
def request_last_status_time(self, jid, resource):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def request_os_info(self, jid, resource):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_settings(self):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_bookmarks(self):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def store_bookmarks(self):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_metacontacts(self):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def send_agent_status(self, agent, ptype):
|
||||
'''To be implemented by derivated classes'''
|
||||
"""
|
||||
To be implemented by derivated classes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def gpg_passphrase(self, passphrase):
|
||||
|
@ -581,7 +627,6 @@ class CommonConnection:
|
|||
self._update_status(show, msg)
|
||||
|
||||
class Connection(CommonConnection, ConnectionHandlers):
|
||||
'''Connection class'''
|
||||
def __init__(self, name):
|
||||
CommonConnection.__init__(self, name)
|
||||
ConnectionHandlers.__init__(self)
|
||||
|
@ -672,7 +717,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection = None
|
||||
|
||||
def _disconnectedReconnCB(self):
|
||||
'''Called when we are disconnected'''
|
||||
"""
|
||||
Called when we are disconnected
|
||||
"""
|
||||
log.info('disconnectedReconnCB called')
|
||||
if gajim.account_is_connected(self.name):
|
||||
# we cannot change our status to offline or connecting
|
||||
|
@ -830,11 +877,11 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data))
|
||||
|
||||
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,
|
||||
where the probability of being selected is proportional to the weight
|
||||
of the host.'''
|
||||
|
||||
"""
|
||||
Selects the next host according to RFC2782 p.3 based on it's priority.
|
||||
Chooses between hosts with the same priority randomly, where the
|
||||
probability of being selected is proportional to the weight of the host
|
||||
"""
|
||||
hosts_by_prio = sorted(hosts, key=operator.itemgetter('prio'))
|
||||
|
||||
try:
|
||||
|
@ -856,10 +903,13 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
return host
|
||||
|
||||
def connect(self, data = None):
|
||||
''' Start a connection to the Jabber server.
|
||||
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)'''
|
||||
"""
|
||||
Start a connection to the Jabber server
|
||||
|
||||
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:
|
||||
return self.connection, ''
|
||||
|
||||
|
@ -1256,8 +1306,10 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(' ')
|
||||
|
||||
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:
|
||||
return
|
||||
id_ = self.connection.getAnID()
|
||||
|
@ -1325,7 +1377,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname)
|
||||
|
||||
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 = '')
|
||||
l = iq.getTag('query').setTag('list', {'name': name})
|
||||
i = l.setTag('item', {'action': action, 'order': str(order)})
|
||||
|
@ -1358,7 +1412,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
def activate_privacy_rule(self, name):
|
||||
'''activate a privacy rule'''
|
||||
"""
|
||||
Activate a privacy rule
|
||||
"""
|
||||
if not self.connection:
|
||||
return
|
||||
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)
|
||||
|
||||
def send_contacts(self, contacts, jid):
|
||||
'''Send contacts with RosterX (Xep-0144)'''
|
||||
"""
|
||||
Send contacts with RosterX (Xep-0144)
|
||||
"""
|
||||
if not self.connection:
|
||||
return
|
||||
if len(contacts) == 1:
|
||||
|
@ -1553,7 +1611,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(msg_iq)
|
||||
|
||||
def send_stanza(self, stanza):
|
||||
''' send a stanza untouched '''
|
||||
"""
|
||||
Send a stanza untouched
|
||||
"""
|
||||
if not self.connection:
|
||||
return
|
||||
self.connection.send(stanza)
|
||||
|
@ -1573,7 +1633,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(p)
|
||||
|
||||
def request_subscription(self, jid, msg = '', name = '', groups = [],
|
||||
auto_auth = False, user_nick = ''):
|
||||
auto_auth = False, user_nick = ''):
|
||||
if not self.connection:
|
||||
return
|
||||
log.debug('subscription request for %s' % jid)
|
||||
|
@ -1677,8 +1737,10 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
common.xmpp.features_nb.getRegInfo(con, self._hostname)
|
||||
|
||||
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:
|
||||
return
|
||||
to_whom_jid = jid
|
||||
|
@ -1694,8 +1756,10 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
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:
|
||||
return
|
||||
# If we are invisible, do not request
|
||||
|
@ -1715,8 +1779,10 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
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:
|
||||
return
|
||||
# If we are invisible, do not request
|
||||
|
@ -1736,7 +1802,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
def get_settings(self):
|
||||
''' Get Gajim settings as described in XEP 0049 '''
|
||||
"""
|
||||
Get Gajim settings as described in XEP 0049
|
||||
"""
|
||||
if not self.connection:
|
||||
return
|
||||
iq = common.xmpp.Iq(typ='get')
|
||||
|
@ -1757,9 +1825,12 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self._request_bookmarks_xml()
|
||||
|
||||
def get_bookmarks(self, storage_type=None):
|
||||
'''Get Bookmarks from storage or PubSub if supported as described in
|
||||
XEP 0048
|
||||
storage_type can be set to xml to force request to xml storage'''
|
||||
"""
|
||||
Get Bookmarks from storage or PubSub if supported as described in XEP
|
||||
0048
|
||||
|
||||
storage_type can be set to xml to force request to xml storage
|
||||
"""
|
||||
if not self.connection:
|
||||
return
|
||||
if self.pubsub_supported and storage_type != 'xml':
|
||||
|
@ -1771,9 +1842,12 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self._request_bookmarks_xml()
|
||||
|
||||
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
|
||||
else it will be stored on both'''
|
||||
else it will be stored on both
|
||||
"""
|
||||
if not self.connection:
|
||||
return
|
||||
iq = common.xmpp.Node(tag='storage', attrs={'xmlns': 'storage:bookmarks'})
|
||||
|
@ -1815,7 +1889,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iqA)
|
||||
|
||||
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 = {}
|
||||
if not self.connection:
|
||||
return
|
||||
|
@ -1825,7 +1901,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
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:
|
||||
return
|
||||
iq = common.xmpp.Iq(typ='set')
|
||||
|
@ -1840,7 +1918,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
|
||||
|
||||
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:
|
||||
return
|
||||
iq = common.xmpp.Iq(typ='get')
|
||||
|
@ -1852,7 +1932,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
def store_metacontacts(self, tags_list):
|
||||
''' Send meta contacts to the storage namespace '''
|
||||
"""
|
||||
Send meta contacts to the storage namespace
|
||||
"""
|
||||
if not self.connection:
|
||||
return
|
||||
iq = common.xmpp.Iq(typ='set')
|
||||
|
@ -2000,16 +2082,20 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(p)
|
||||
|
||||
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
|
||||
date from DB. Save it in mem AND in a small table (with fast access)
|
||||
'''
|
||||
"""
|
||||
log_time = time_time()
|
||||
self.last_history_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 = ''):
|
||||
'''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:
|
||||
return
|
||||
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
|
||||
|
@ -2022,7 +2108,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
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:
|
||||
return
|
||||
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
|
||||
|
@ -2117,7 +2205,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
_on_unregister_account_connect(self.connection)
|
||||
|
||||
def send_invite(self, room, to, reason='', continue_tag=False):
|
||||
'''sends invitation'''
|
||||
"""
|
||||
Send invitation
|
||||
"""
|
||||
message=common.xmpp.Message(to = room)
|
||||
c = message.addChild(name = 'x', namespace = common.xmpp.NS_MUC_USER)
|
||||
c = c.addChild(name = 'invite', attrs={'to' : to})
|
||||
|
|
|
@ -100,9 +100,9 @@ class ConnectionBytestream:
|
|||
return True
|
||||
|
||||
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:
|
||||
return
|
||||
if streamhost is None:
|
||||
|
@ -117,7 +117,9 @@ class ConnectionBytestream:
|
|||
self.connection.send(iq)
|
||||
|
||||
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():
|
||||
if self.is_transfer_stopped(file_props):
|
||||
continue
|
||||
|
@ -132,7 +134,9 @@ class ConnectionBytestream:
|
|||
self.remove_transfer(file_props)
|
||||
|
||||
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():
|
||||
self.remove_transfer(file_props, remove_from_list = False)
|
||||
del(self.files_props)
|
||||
|
@ -161,9 +165,11 @@ class ConnectionBytestream:
|
|||
gajim.socks5queue.remove_receiver(host['idx'])
|
||||
gajim.socks5queue.remove_sender(host['idx'])
|
||||
|
||||
def send_socks5_info(self, file_props, fast = True, receiver = None,
|
||||
sender = None):
|
||||
''' send iq for the present streamhosts and proxies '''
|
||||
def send_socks5_info(self, file_props, fast = True, receiver = None, sender
|
||||
= None):
|
||||
"""
|
||||
Send iq for the present streamhosts and proxies
|
||||
"""
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
if not isinstance(self.peerhost, tuple):
|
||||
|
@ -269,9 +275,12 @@ class ConnectionBytestream:
|
|||
self.connection.send(iq)
|
||||
|
||||
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
|
||||
invalid stream or 'profile' for invalid profile'''
|
||||
invalid stream or 'profile' for invalid profile
|
||||
"""
|
||||
# user response to ConfirmationDialog may come after we've disconneted
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
|
@ -294,7 +303,9 @@ class ConnectionBytestream:
|
|||
self.connection.send(iq)
|
||||
|
||||
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
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
|
@ -326,7 +337,9 @@ class ConnectionBytestream:
|
|||
return file_props['receiver'].jid + '/' + file_props['receiver'].resource
|
||||
|
||||
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:
|
||||
return
|
||||
file_props['sender'] = self._ft_get_our_jid()
|
||||
|
@ -357,7 +370,9 @@ class ConnectionBytestream:
|
|||
self.connection.send(iq)
|
||||
|
||||
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:
|
||||
return
|
||||
file_props = self.files_props[sid]
|
||||
|
@ -365,8 +380,10 @@ class ConnectionBytestream:
|
|||
return
|
||||
|
||||
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:
|
||||
return
|
||||
msg_dict = {
|
||||
|
@ -391,7 +408,9 @@ class ConnectionBytestream:
|
|||
self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
|
||||
|
||||
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:
|
||||
return
|
||||
file_props = self.files_props[proxy['sid']]
|
||||
|
@ -673,16 +692,24 @@ class ConnectionBytestream:
|
|||
raise common.xmpp.NodeProcessed
|
||||
|
||||
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):
|
||||
'''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)
|
||||
|
||||
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 feature: var is mandatory'''
|
||||
For feature: var is mandatory.
|
||||
"""
|
||||
self._discover(common.xmpp.NS_DISCO_INFO, jid, node, id_prefix)
|
||||
|
||||
def request_register_agent_info(self, agent):
|
||||
|
@ -738,7 +765,9 @@ class ConnectionDisco:
|
|||
self._IqCB(con, resp)
|
||||
|
||||
def _discoGetCB(self, con, iq_obj):
|
||||
''' get disco info '''
|
||||
"""
|
||||
Get disco info
|
||||
"""
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
frm = helpers.get_full_jid_from_iq(iq_obj)
|
||||
|
@ -1008,9 +1037,11 @@ class ConnectionVcard:
|
|||
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
||||
|
||||
def get_cached_vcard(self, fjid, is_fake_jid = False):
|
||||
'''return the vcard as a dict
|
||||
return {} if vcard was too old
|
||||
return None if we don't have cached vcard'''
|
||||
"""
|
||||
Return the vcard as a dict.
|
||||
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)
|
||||
puny_jid = helpers.sanitize_filename(jid)
|
||||
if is_fake_jid:
|
||||
|
@ -1045,9 +1076,13 @@ class ConnectionVcard:
|
|||
return vcard
|
||||
|
||||
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
|
||||
real jid of the contact, but we want to consider it comes from a fake jid'''
|
||||
"""
|
||||
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 real jid of the
|
||||
contact, but we want to consider it comes from a fake jid
|
||||
"""
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
iq = common.xmpp.Iq(typ = 'get')
|
||||
|
@ -1238,8 +1273,9 @@ class ConnectionVcard:
|
|||
del self.awaiting_answers[id_]
|
||||
|
||||
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'):
|
||||
return
|
||||
if not vc.getTag('vCard').getNamespace() == common.xmpp.NS_VCARD:
|
||||
|
@ -1345,8 +1381,9 @@ class ConnectionHandlersBase:
|
|||
self.sessions = {}
|
||||
|
||||
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):
|
||||
jid = gajim.get_jid_without_resource(jid)
|
||||
|
||||
|
@ -1356,9 +1393,10 @@ class ConnectionHandlersBase:
|
|||
return []
|
||||
|
||||
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
|
||||
jid = fjid
|
||||
|
||||
|
@ -1386,7 +1424,9 @@ class ConnectionHandlersBase:
|
|||
return None
|
||||
|
||||
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 thread_id in self.sessions[jid]:
|
||||
self.sessions[jid][thread_id].terminate(send_termination)
|
||||
|
@ -1405,10 +1445,11 @@ class ConnectionHandlersBase:
|
|||
del self.sessions[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
|
||||
sent a message to.'''
|
||||
|
||||
"""
|
||||
Find 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 sent a
|
||||
message to
|
||||
"""
|
||||
sessions = self.sessions[jid].values()
|
||||
|
||||
# sessions that we haven't received a thread ID in
|
||||
|
@ -1425,8 +1466,9 @@ sent a message to.'''
|
|||
return 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:
|
||||
sessions = self.sessions[jid].values()
|
||||
|
||||
|
@ -1444,8 +1486,12 @@ sent a message to.'''
|
|||
return 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:
|
||||
cls = gajim.default_session_type
|
||||
|
||||
|
@ -1463,7 +1509,9 @@ sent a message to.'''
|
|||
|
||||
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):
|
||||
ConnectionVcard.__init__(self)
|
||||
ConnectionBytestream.__init__(self)
|
||||
|
@ -1544,9 +1592,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
self.dispatch('ERROR_ANSWER', (id_, jid_from, errmsg, errcode))
|
||||
|
||||
def _PrivateCB(self, con, iq_obj):
|
||||
'''
|
||||
"""
|
||||
Private Data (XEP 048 and 049)
|
||||
'''
|
||||
"""
|
||||
log.debug('PrivateCB')
|
||||
query = iq_obj.getTag('query')
|
||||
storage = query.getTag('storage')
|
||||
|
@ -1573,8 +1621,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
self.annotations[jid] = annotation
|
||||
|
||||
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
|
||||
# http://www.xmpp.org/extensions/xep-0048.html
|
||||
resend_to_pubsub = False
|
||||
|
@ -1788,7 +1837,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
self.dispatch('ENTITY_TIME', (jid_stripped, resource, time_info))
|
||||
|
||||
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:
|
||||
return
|
||||
if not gm.getTag('new-mail'):
|
||||
|
@ -1810,7 +1861,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
raise common.xmpp.NodeProcessed
|
||||
|
||||
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'):
|
||||
return
|
||||
self.gmail_url = gm.getTag('mailbox').getAttr('url')
|
||||
|
@ -1856,7 +1910,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
raise common.xmpp.NodeProcessed
|
||||
|
||||
def _rosterItemExchangeCB(self, con, msg):
|
||||
''' XEP-0144 Roster Item Echange '''
|
||||
"""
|
||||
XEP-0144 Roster Item Echange
|
||||
"""
|
||||
exchange_items_list = {}
|
||||
jid_from = helpers.get_full_jid_from_iq(msg)
|
||||
items_list = msg.getTag('x').getChildren()
|
||||
|
@ -1898,7 +1954,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
raise common.xmpp.NodeProcessed
|
||||
|
||||
def _messageCB(self, con, msg):
|
||||
'''Called when we receive a message'''
|
||||
"""
|
||||
Called when we receive a message
|
||||
"""
|
||||
log.debug('MessageCB')
|
||||
mtype = msg.getType()
|
||||
|
||||
|
@ -2167,7 +2225,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
is_continued))
|
||||
|
||||
def _presenceCB(self, con, prs):
|
||||
'''Called when we receive a presence'''
|
||||
"""
|
||||
Called when we receive a presence
|
||||
"""
|
||||
ptype = prs.getType()
|
||||
if ptype == 'available':
|
||||
ptype = None
|
||||
|
@ -2269,7 +2329,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
|
||||
gc_control = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
|
||||
self.name)
|
||||
|
||||
|
||||
# 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
|
||||
# remain set to None.
|
||||
|
@ -2550,11 +2610,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
raise common.xmpp.NodeProcessed
|
||||
|
||||
def _PrivacySetCB(self, con, iq_obj):
|
||||
'''
|
||||
"""
|
||||
Privacy lists (XEP 016)
|
||||
|
||||
A list has been set
|
||||
'''
|
||||
A list has been set.
|
||||
"""
|
||||
log.debug('PrivacySetCB')
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
|
|
|
@ -33,26 +33,28 @@ import caps
|
|||
from account import Account
|
||||
|
||||
class XMPPEntity(object):
|
||||
'''Base representation of entities in XMPP'''
|
||||
|
||||
"""
|
||||
Base representation of entities in XMPP
|
||||
"""
|
||||
|
||||
def __init__(self, jid, account, resource):
|
||||
self.jid = jid
|
||||
self.resource = resource
|
||||
self.account = account
|
||||
|
||||
class CommonContact(XMPPEntity):
|
||||
|
||||
def __init__(self, jid, account, resource, show, status, name, our_chatstate,
|
||||
composing_xep, chatstate, client_caps=None):
|
||||
|
||||
|
||||
def __init__(self, jid, account, resource, show, status, name,
|
||||
our_chatstate, composing_xep, chatstate, client_caps=None):
|
||||
|
||||
XMPPEntity.__init__(self, jid, account, resource)
|
||||
|
||||
|
||||
self.show = show
|
||||
self.status = status
|
||||
self.name = name
|
||||
|
||||
|
||||
self.client_caps = client_caps or caps.NullClientCaps()
|
||||
|
||||
|
||||
# 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:
|
||||
# None, False and 'ask'
|
||||
|
@ -67,18 +69,18 @@ class CommonContact(XMPPEntity):
|
|||
self.composing_xep = composing_xep
|
||||
# this is contact's chatstate
|
||||
self.chatstate = chatstate
|
||||
|
||||
|
||||
def get_full_jid(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def get_shown_name(self):
|
||||
raise NotImplementedError
|
||||
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
if self.show == 'offline':
|
||||
# Unfortunately, if all resources are offline, the contact
|
||||
# includes the last resource that was online. Check for its
|
||||
|
@ -90,26 +92,29 @@ class CommonContact(XMPPEntity):
|
|||
|
||||
|
||||
class Contact(CommonContact):
|
||||
'''Information concerning each contact'''
|
||||
def __init__(self, jid, account, name='', groups=[], show='', status='', sub='',
|
||||
ask='', resource='', priority=0, keyID='', client_caps=None,
|
||||
our_chatstate=None, chatstate=None, last_status_time=None, msg_id = None,
|
||||
composing_xep=None):
|
||||
|
||||
CommonContact.__init__(self, jid, account, resource, show, status, name,
|
||||
"""
|
||||
Information concerning each contact
|
||||
"""
|
||||
|
||||
def __init__(self, jid, account, name='', groups=[], show='', status='',
|
||||
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
|
||||
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)
|
||||
|
||||
|
||||
self.contact_name = '' # nick choosen by contact
|
||||
self.groups = [i for i in set(groups)] # filter duplicate values
|
||||
|
||||
self.sub = sub
|
||||
self.ask = ask
|
||||
|
||||
|
||||
self.priority = priority
|
||||
self.keyID = keyID
|
||||
self.msg_id = msg_id
|
||||
self.last_status_time = last_status_time
|
||||
|
||||
|
||||
self.pep = {}
|
||||
|
||||
def get_full_jid(self):
|
||||
|
@ -137,7 +142,9 @@ class Contact(CommonContact):
|
|||
return self.groups
|
||||
|
||||
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
|
||||
if self.is_transport():
|
||||
return False
|
||||
|
@ -173,43 +180,50 @@ class 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='',
|
||||
affiliation='', jid='', resource='', our_chatstate=None,
|
||||
composing_xep=None, chatstate=None):
|
||||
|
||||
affiliation='', jid='', resource='', our_chatstate=None,
|
||||
composing_xep=None, chatstate=None):
|
||||
|
||||
CommonContact.__init__(self, jid, account, resource, show, status, name,
|
||||
our_chatstate, composing_xep, chatstate)
|
||||
|
||||
|
||||
self.room_jid = room_jid
|
||||
self.role = role
|
||||
self.affiliation = affiliation
|
||||
|
||||
|
||||
def get_full_jid(self):
|
||||
return self.room_jid + '/' + self.name
|
||||
|
||||
def get_shown_name(self):
|
||||
return self.name
|
||||
|
||||
|
||||
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,
|
||||
resource=self.resource, name=self.name, groups=[], show=self.show,
|
||||
status=self.status, sub='none', client_caps=self.client_caps)
|
||||
|
||||
|
||||
|
||||
class Contacts:
|
||||
'''Information concerning all contacts and groupchat contacts'''
|
||||
"""
|
||||
Information concerning all contacts and groupchat contacts
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._metacontact_manager = MetacontactManager(self)
|
||||
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].name = new_name
|
||||
del self._accounts[old_name]
|
||||
|
||||
|
||||
self._metacontact_manager.change_account_name(old_name, new_name)
|
||||
|
||||
def add_account(self, account_name):
|
||||
|
@ -234,7 +248,7 @@ class Contacts:
|
|||
keyID=keyID, client_caps=client_caps, our_chatstate=our_chatstate,
|
||||
chatstate=chatstate, last_status_time=last_status_time,
|
||||
composing_xep=composing_xep)
|
||||
|
||||
|
||||
def create_self_contact(self, jid, account, resource, show, status, priority,
|
||||
name='', keyID=''):
|
||||
conn = common.gajim.connections[account]
|
||||
|
@ -246,7 +260,7 @@ class Contacts:
|
|||
resource=resource)
|
||||
self_contact.pep = conn.pep
|
||||
return self_contact
|
||||
|
||||
|
||||
def create_not_in_roster_contact(self, jid, account, resource='', name='', keyID=''):
|
||||
account = self._accounts.get(account, account) # Use Account object if available
|
||||
return self.create_contact(jid=jid, account=account, resource=resource,
|
||||
|
@ -275,7 +289,7 @@ class Contacts:
|
|||
self._accounts[account].contacts.remove_jid(jid)
|
||||
if remove_meta:
|
||||
self._metacontact_manager.remove_metacontact(account, jid)
|
||||
|
||||
|
||||
def get_contacts(self, account, jid):
|
||||
return self._accounts[account].contacts.get_contacts(jid)
|
||||
|
||||
|
@ -288,13 +302,13 @@ class Contacts:
|
|||
|
||||
def get_contact_from_full_jid(self, account, fjid):
|
||||
return self._accounts[account].contacts.get_contact_from_full_jid(fjid)
|
||||
|
||||
|
||||
def get_first_contact_from_jid(self, account, jid):
|
||||
return self._accounts[account].contacts.get_first_contact_from_jid(jid)
|
||||
|
||||
def get_contacts_from_group(self, account, group):
|
||||
return self._accounts[account].contacts.get_contacts_from_group(group)
|
||||
|
||||
|
||||
def get_jid_list(self, account):
|
||||
return self._accounts[account].contacts.get_jid_list()
|
||||
|
||||
|
@ -318,10 +332,11 @@ class Contacts:
|
|||
contact = self.get_gc_contact(account, room, nick)
|
||||
return contact
|
||||
return self.get_highest_prio_contact_from_contacts(contacts)
|
||||
|
||||
|
||||
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 == []:
|
||||
accounts = self.get_accounts()
|
||||
nbr_online = 0
|
||||
|
@ -358,24 +373,28 @@ class Contacts:
|
|||
return nbr_online, nbr_total
|
||||
|
||||
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]:
|
||||
return False
|
||||
return True
|
||||
|
||||
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):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
# Only called if self has no attr_name
|
||||
if hasattr(self._metacontact_manager, attr_name):
|
||||
return getattr(self._metacontact_manager, attr_name)
|
||||
else:
|
||||
raise AttributeError(attr_name)
|
||||
|
||||
|
||||
def create_gc_contact(self, room_jid, account, name='', show='', status='',
|
||||
role='', affiliation='', jid='', resource=''):
|
||||
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):
|
||||
return self._accounts[account].gc_contacts.get_nb_role_total_gc_contacts(room_jid, role)
|
||||
|
||||
|
||||
|
||||
|
||||
class Contacts_New():
|
||||
|
||||
|
||||
def __init__(self):
|
||||
# list of contacts {jid1: [C1, C2]}, } one Contact per resource
|
||||
self._contacts = {}
|
||||
|
||||
def add_contact(self, contact):
|
||||
|
||||
def add_contact(self, contact):
|
||||
if contact.jid not in self._contacts:
|
||||
self._contacts[contact.jid] = [contact]
|
||||
return
|
||||
|
@ -426,7 +445,7 @@ class Contacts_New():
|
|||
self.remove_contact(c)
|
||||
break
|
||||
contacts.append(contact)
|
||||
|
||||
|
||||
def remove_contact(self, contact):
|
||||
if contact.jid not in self._contacts:
|
||||
return
|
||||
|
@ -434,28 +453,34 @@ class Contacts_New():
|
|||
self._contacts[contact.jid].remove(contact)
|
||||
if len(self._contacts[contact.jid]) == 0:
|
||||
del self._contacts[contact.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:
|
||||
return
|
||||
del self._contacts[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:
|
||||
return self._contacts[jid]
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_contact(self, jid, resource=None):
|
||||
### WARNING ###
|
||||
# This function returns a *RANDOM* resource if resource = None!
|
||||
# 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
|
||||
# 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 not resource:
|
||||
return self._contacts[jid][0]
|
||||
|
@ -468,22 +493,26 @@ class Contacts_New():
|
|||
for jid in self._contacts.keys():
|
||||
for contact in self._contacts[jid][:]:
|
||||
yield contact
|
||||
|
||||
|
||||
def get_jid_list(self):
|
||||
return self._contacts.keys()
|
||||
|
||||
|
||||
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)
|
||||
return self.get_contact(barejid, resource)
|
||||
|
||||
|
||||
def get_first_contact_from_jid(self, jid):
|
||||
if jid in self._contacts:
|
||||
return self._contacts[jid][0]
|
||||
return None
|
||||
|
||||
def get_contacts_from_group(self, group):
|
||||
'''Returns all contacts in the given group'''
|
||||
"""
|
||||
Return all contacts in the given group
|
||||
"""
|
||||
group_contacts = []
|
||||
for jid in self._contacts:
|
||||
contacts = self.get_contacts(jid)
|
||||
|
@ -502,11 +531,11 @@ class Contacts_New():
|
|||
|
||||
|
||||
class GC_Contacts():
|
||||
|
||||
|
||||
def __init__(self):
|
||||
# list of contacts that are in gc {room_jid: {nick: C}}}
|
||||
self._rooms = {}
|
||||
|
||||
|
||||
def add_gc_contact(self, gc_contact):
|
||||
if gc_contact.room_jid not in self._rooms:
|
||||
self._rooms[gc_contact.room_jid] = {gc_contact.name: gc_contact}
|
||||
|
@ -544,8 +573,10 @@ class GC_Contacts():
|
|||
return self._rooms[room_jid][nick]
|
||||
|
||||
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:
|
||||
return 0, 0
|
||||
nb_role = nb_total = 0
|
||||
|
@ -554,25 +585,25 @@ class GC_Contacts():
|
|||
nb_role += 1
|
||||
nb_total += 1
|
||||
return nb_role, nb_total
|
||||
|
||||
|
||||
|
||||
class MetacontactManager():
|
||||
|
||||
|
||||
def __init__(self, contacts):
|
||||
self._metacontacts_tags = {}
|
||||
self._contacts = contacts
|
||||
|
||||
|
||||
def change_account_name(self, old_name, new_name):
|
||||
self._metacontacts_tags[new_name] = self._metacontacts_tags[old_name]
|
||||
del self._metacontacts_tags[old_name]
|
||||
|
||||
|
||||
def add_account(self, account):
|
||||
if account not in self._metacontacts_tags:
|
||||
self._metacontacts_tags[account] = {}
|
||||
|
||||
|
||||
def remove_account(self, account):
|
||||
del self._metacontacts_tags[account]
|
||||
|
||||
|
||||
def define_metacontacts(self, account, tags_list):
|
||||
self._metacontacts_tags[account] = tags_list
|
||||
|
||||
|
@ -582,13 +613,15 @@ class MetacontactManager():
|
|||
#FIXME: can this append ?
|
||||
assert False
|
||||
|
||||
def iter_metacontacts_families(self, account):
|
||||
def iter_metacontacts_families(self, account):
|
||||
for tag in self._metacontacts_tags[account]:
|
||||
family = self._get_metacontacts_family_from_tag(account, tag)
|
||||
yield family
|
||||
|
||||
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:
|
||||
return None
|
||||
for tag in self._metacontacts_tags[account]:
|
||||
|
@ -626,7 +659,7 @@ class MetacontactManager():
|
|||
def remove_metacontact(self, account, jid):
|
||||
if not account in self._metacontacts_tags:
|
||||
return None
|
||||
|
||||
|
||||
found = None
|
||||
for tag in self._metacontacts_tags[account]:
|
||||
for data in self._metacontacts_tags[account][tag]:
|
||||
|
@ -657,7 +690,9 @@ class MetacontactManager():
|
|||
return False
|
||||
|
||||
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 = {}
|
||||
for account in self._metacontacts_tags:
|
||||
if tag in self._metacontacts_tags[account]:
|
||||
|
@ -669,9 +704,10 @@ class MetacontactManager():
|
|||
return answers
|
||||
|
||||
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}, ]
|
||||
'order' is optional'''
|
||||
"""
|
||||
Return the family of the given jid, including jid in the form:
|
||||
[{'account': acct, 'jid': jid, 'order': order}, ] 'order' is optional
|
||||
"""
|
||||
tag = self._get_metacontacts_tag(account, jid)
|
||||
return self._get_metacontacts_family_from_tag(account, tag)
|
||||
|
||||
|
@ -687,9 +723,12 @@ class MetacontactManager():
|
|||
return answers
|
||||
|
||||
def _compare_metacontacts(self, data1, data2):
|
||||
'''compare 2 metacontacts.
|
||||
Data is {'jid': jid, 'account': account, 'order': order}
|
||||
order is optional'''
|
||||
"""
|
||||
Compare 2 metacontacts
|
||||
|
||||
Data is {'jid': jid, 'account': account, 'order': order} order is
|
||||
optional
|
||||
"""
|
||||
jid1 = data1['jid']
|
||||
jid2 = data2['jid']
|
||||
account1 = data1['account']
|
||||
|
@ -765,16 +804,17 @@ class MetacontactManager():
|
|||
if account2 > account1:
|
||||
return -1
|
||||
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.
|
||||
A metacontact may be over different accounts. If accounts are not merged
|
||||
then the given family is split account wise.
|
||||
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. 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)
|
||||
'''
|
||||
"""
|
||||
if common.gajim.config.get('mergeaccounts'):
|
||||
# group all together
|
||||
nearby_family = family
|
||||
|
@ -789,9 +829,11 @@ class MetacontactManager():
|
|||
return (nearby_family, big_brother_jid, big_brother_account)
|
||||
|
||||
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)
|
||||
return family[-1]
|
||||
|
||||
|
||||
# vim: se ts=3:
|
||||
|
|
Loading…
Add table
Reference in a new issue