A portion of doc-string refactoring

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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