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)
|
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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Add table
Reference in a new issue