A big portion of doc-string refactoring

This commit is contained in:
Alexander Cherniuk 2009-11-26 13:58:12 +02:00
parent cea7c66f75
commit 6bf2246de5
28 changed files with 1133 additions and 615 deletions

View File

@ -21,8 +21,10 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
""" This module contains wrappers for different parts of data forms (JEP 0004). """
For information how to use them, read documentation. """ This module contains wrappers for different parts of data forms (JEP 0004). For
information how to use them, read documentation
"""
import xmpp import xmpp
@ -72,7 +74,9 @@ def Field(typ, **attrs):
return f return f
def ExtendField(node): def ExtendField(node):
''' Helper function to extend a node to field of appropriate type. ''' """
Helper function to extend a node to field of appropriate type
"""
# when validation (XEP-122) will go in, we could have another classes # when validation (XEP-122) will go in, we could have another classes
# like DateTimeField - so that dicts in Field() and ExtendField() will # like DateTimeField - so that dicts in Field() and ExtendField() will
# be different... # be different...
@ -94,19 +98,23 @@ def ExtendField(node):
return f[typ](extend=node) return f[typ](extend=node)
def ExtendForm(node): def ExtendForm(node):
''' Helper function to extend a node to form of appropriate type. ''' """
Helper function to extend a node to form of appropriate type
"""
if node.getTag('reported') is not None: if node.getTag('reported') is not None:
return MultipleDataForm(extend=node) return MultipleDataForm(extend=node)
else: else:
return SimpleDataForm(extend=node) return SimpleDataForm(extend=node)
class DataField(ExtendedNode): class DataField(ExtendedNode):
""" Keeps data about one field - var, field type, labels, instructions... """
Base class for different kinds of fields. Use Field() function to Keeps data about one field - var, field type, labels, instructions... Base
construct one of these. """ class for different kinds of fields. Use Field() function to construct one
of these
"""
def __init__(self, typ=None, var=None, value=None, label=None, desc=None, def __init__(self, typ=None, var=None, value=None, label=None, desc=None,
required=False, options=None, extend=None): required=False, options=None, extend=None):
if extend is None: if extend is None:
ExtendedNode.__init__(self, 'field') ExtendedNode.__init__(self, 'field')
@ -124,10 +132,12 @@ class DataField(ExtendedNode):
@nested_property @nested_property
def type(): def type():
'''Type of field. Recognized values are: 'boolean', 'fixed', 'hidden', """
Type of field. Recognized values are: 'boolean', 'fixed', 'hidden',
'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi', 'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi',
'text-private', 'text-single'. If you set this to something different, 'text-private', 'text-single'. If you set this to something different,
DataField will store given name, but treat all data as text-single.''' DataField will store given name, but treat all data as text-single
"""
def fget(self): def fget(self):
t = self.getAttr('type') t = self.getAttr('type')
if t is None: if t is None:
@ -142,7 +152,9 @@ class DataField(ExtendedNode):
@nested_property @nested_property
def var(): def var():
'''Field identifier.''' """
Field identifier
"""
def fget(self): def fget(self):
return self.getAttr('var') return self.getAttr('var')
@ -157,7 +169,9 @@ class DataField(ExtendedNode):
@nested_property @nested_property
def label(): def label():
'''Human-readable field name.''' """
Human-readable field name
"""
def fget(self): def fget(self):
l = self.getAttr('label') l = self.getAttr('label')
if not l: if not l:
@ -176,7 +190,9 @@ class DataField(ExtendedNode):
@nested_property @nested_property
def description(): def description():
'''Human-readable description of field meaning.''' """
Human-readable description of field meaning
"""
def fget(self): def fget(self):
return self.getTagData('desc') or u'' return self.getTagData('desc') or u''
@ -196,7 +212,9 @@ class DataField(ExtendedNode):
@nested_property @nested_property
def required(): def required():
'''Controls whether this field required to fill. Boolean.''' """
Controls whether this field required to fill. Boolean
"""
def fget(self): def fget(self):
return bool(self.getTag('required')) return bool(self.getTag('required'))
@ -212,7 +230,9 @@ class DataField(ExtendedNode):
class BooleanField(DataField): class BooleanField(DataField):
@nested_property @nested_property
def value(): def value():
'''Value of field. May contain True, False or None.''' """
Value of field. May contain True, False or None
"""
def fget(self): def fget(self):
v = self.getTagData('value') v = self.getTagData('value')
if v in ('0', 'false'): if v in ('0', 'false'):
@ -234,10 +254,15 @@ class BooleanField(DataField):
return locals() return locals()
class StringField(DataField): class StringField(DataField):
''' Covers fields of types: fixed, hidden, text-private, text-single. ''' """
Covers fields of types: fixed, hidden, text-private, text-single
"""
@nested_property @nested_property
def value(): def value():
'''Value of field. May be any unicode string.''' """
Value of field. May be any unicode string
"""
def fget(self): def fget(self):
return self.getTagData('value') or u'' return self.getTagData('value') or u''
@ -256,10 +281,15 @@ class StringField(DataField):
return locals() return locals()
class ListField(DataField): class ListField(DataField):
'''Covers fields of types: jid-multi, jid-single, list-multi, list-single.''' """
Covers fields of types: jid-multi, jid-single, list-multi, list-single
"""
@nested_property @nested_property
def options(): def options():
'''Options.''' """
Options
"""
def fget(self): def fget(self):
options = [] options = []
for element in self.getTags('option'): for element in self.getTags('option'):
@ -294,14 +324,21 @@ class ListField(DataField):
yield (v, l) yield (v, l)
class ListSingleField(ListField, StringField): class ListSingleField(ListField, StringField):
'''Covers list-single and jid-single fields.''' """
Covers list-single and jid-single fields
"""
pass pass
class ListMultiField(ListField): class ListMultiField(ListField):
'''Covers list-multi and jid-multi fields.''' """
Covers list-multi and jid-multi fields
"""
@nested_property @nested_property
def values(): def values():
'''Values held in field.''' """
Values held in field
"""
def fget(self): def fget(self):
values = [] values = []
for element in self.getTags('value'): for element in self.getTags('value'):
@ -326,7 +363,9 @@ class ListMultiField(ListField):
class TextMultiField(DataField): class TextMultiField(DataField):
@nested_property @nested_property
def value(): def value():
'''Value held in field.''' """
Value held in field
"""
def fget(self): def fget(self):
value = u'' value = u''
for element in self.iterTags('value'): for element in self.iterTags('value'):
@ -347,8 +386,10 @@ class TextMultiField(DataField):
return locals() return locals()
class DataRecord(ExtendedNode): class DataRecord(ExtendedNode):
'''The container for data fields - an xml element which has DataField """
elements as children.''' The container for data fields - an xml element which has DataField elements
as children
"""
def __init__(self, fields=None, associated=None, extend=None): def __init__(self, fields=None, associated=None, extend=None):
self.associated = associated self.associated = associated
self.vars = {} self.vars = {}
@ -373,7 +414,9 @@ class DataRecord(ExtendedNode):
@nested_property @nested_property
def fields(): def fields():
'''List of fields in this record.''' """
List of fields in this record
"""
def fget(self): def fget(self):
return self.getTags('field') return self.getTags('field')
@ -391,14 +434,17 @@ class DataRecord(ExtendedNode):
return locals() return locals()
def iter_fields(self): def iter_fields(self):
''' Iterate over fields in this record. Do not take associated """
into account. ''' Iterate over fields in this record. Do not take associated into account
"""
for field in self.iterTags('field'): for field in self.iterTags('field'):
yield field yield field
def iter_with_associated(self): def iter_with_associated(self):
''' Iterate over associated, yielding both our field and """
associated one together. ''' Iterate over associated, yielding both our field and associated one
together
"""
for field in self.associated.iter_fields(): for field in self.associated.iter_fields():
yield self[field.var], field yield self[field.var], field
@ -420,9 +466,11 @@ class DataForm(ExtendedNode):
@nested_property @nested_property
def type(): def type():
''' Type of the form. Must be one of: 'form', 'submit', 'cancel', 'result'. """
Type of the form. Must be one of: 'form', 'submit', 'cancel', 'result'.
'form' - this form is to be filled in; you will be able soon to do: 'form' - this form is to be filled in; you will be able soon to do:
filledform = DataForm(replyto=thisform)...''' filledform = DataForm(replyto=thisform)
"""
def fget(self): def fget(self):
return self.getAttr('type') return self.getAttr('type')
@ -434,7 +482,11 @@ class DataForm(ExtendedNode):
@nested_property @nested_property
def title(): def title():
''' Title of the form. Human-readable, should not contain any \\r\\n.''' """
Title of the form
Human-readable, should not contain any \\r\\n.
"""
def fget(self): def fget(self):
return self.getTagData('title') return self.getTagData('title')
@ -451,7 +503,11 @@ class DataForm(ExtendedNode):
@nested_property @nested_property
def instructions(): def instructions():
''' Instructions for this form. Human-readable, may contain \\r\\n. ''' """
Instructions for this form
Human-readable, may contain \\r\\n.
"""
# TODO: the same code is in TextMultiField. join them # TODO: the same code is in TextMultiField. join them
def fget(self): def fget(self):
value = u'' value = u''
@ -520,7 +576,9 @@ class MultipleDataForm(DataForm):
@nested_property @nested_property
def items(): def items():
''' A list of all records. ''' """
A list of all records
"""
def fget(self): def fget(self):
return list(self.iter_records()) return list(self.iter_records())
@ -543,7 +601,9 @@ class MultipleDataForm(DataForm):
# @nested_property # @nested_property
# def reported(): # def reported():
# ''' DataRecord that contains descriptions of fields in records.''' # """
# DataRecord that contains descriptions of fields in records
# """
# def fget(self): # def fget(self):
# return self.getTag('reported') # return self.getTag('reported')
# def fset(self, record): # def fset(self, record):

View File

@ -51,7 +51,10 @@ else:
print _('D-Bus capabilities of Gajim cannot be used') print _('D-Bus capabilities of Gajim cannot be used')
class SystemBus: class SystemBus:
'''A Singleton for the DBus SystemBus''' """
A Singleton for the DBus SystemBus
"""
def __init__(self): def __init__(self):
self.system_bus = None self.system_bus = None
@ -84,7 +87,10 @@ class SystemBus:
system_bus = SystemBus() system_bus = SystemBus()
class SessionBus: class SessionBus:
'''A Singleton for the D-Bus SessionBus''' """
A Singleton for the D-Bus SessionBus
"""
def __init__(self): def __init__(self):
self.session_bus = None self.session_bus = None
@ -115,8 +121,10 @@ class SessionBus:
session_bus = SessionBus() session_bus = SessionBus()
def get_interface(interface, path, start_service=True): def get_interface(interface, path, start_service=True):
'''Returns an interface on the current SessionBus. If the interface isn\'t """
running, it tries to start it first.''' Get an interface on the current SessionBus. If the interface isn't running,
try to start it first
"""
if not supported: if not supported:
return None return None
if session_bus.present(): if session_bus.present():
@ -144,9 +152,11 @@ def get_interface(interface, path, start_service=True):
def get_notifications_interface(notif=None): def get_notifications_interface(notif=None):
'''Returns the notifications interface. """
Get the notifications interface
:param notif: DesktopNotification instance''' :param notif: DesktopNotification instance
"""
# try to see if KDE notifications are available # try to see if KDE notifications are available
iface = get_interface('org.kde.VisualNotifications', '/VisualNotifications', iface = get_interface('org.kde.VisualNotifications', '/VisualNotifications',
start_service=False) start_service=False)

View File

@ -19,12 +19,13 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
''' """
This module defines a number of constants; specifically, large primes suitable This module defines a number of constants; specifically, large primes suitable
for use with the Diffie-Hellman key exchange. for use with the Diffie-Hellman key exchange
These constants have been obtained from RFC2409 and RFC3526. These constants have been obtained from RFC2409 and RFC3526.
''' """
import string import string
generators = [ None, # one to get the right offset generators = [ None, # one to get the right offset

View File

@ -27,13 +27,18 @@
import time import time
class Event: class Event:
'''Information concerning each event''' """
Information concerning each event
"""
def __init__(self, type_, time_, parameters, show_in_roster=False, def __init__(self, type_, time_, parameters, show_in_roster=False,
show_in_systray=True): show_in_systray=True):
''' type_ in chat, normal, file-request, file-error, file-completed, """
type_ in chat, normal, file-request, file-error, file-completed,
file-request-error, file-send-error, file-stopped, gc_msg, pm, file-request-error, file-send-error, file-stopped, gc_msg, pm,
printed_chat, printed_gc_msg, printed_marked_gc_msg, printed_pm, printed_chat, printed_gc_msg, printed_marked_gc_msg, printed_pm,
gc-invitation, subscription_request, unsubscribedm jingle-incoming gc-invitation, subscription_request, unsubscribedm jingle-incoming
parameters is (per type_): parameters is (per type_):
chat, normal, pm: [message, subject, kind, time, encrypted, resource, chat, normal, pm: [message, subject, kind, time, encrypted, resource,
msg_id] msg_id]
@ -47,7 +52,7 @@ class Event:
subscription_request: [text, nick] subscription_request: [text, nick]
unsubscribed: contact unsubscribed: contact
jingle-incoming: (fulljid, sessionid, content_types) jingle-incoming: (fulljid, sessionid, content_types)
''' """
self.type_ = type_ self.type_ = type_
self.time_ = time_ self.time_ = time_
self.parameters = parameters self.parameters = parameters
@ -58,29 +63,40 @@ class Event:
self.account = None self.account = None
class Events: class Events:
'''Information concerning all events''' """
Information concerning all events
"""
def __init__(self): def __init__(self):
self._events = {} # list of events {acct: {jid1: [E1, E2]}, } self._events = {} # list of events {acct: {jid1: [E1, E2]}, }
self._event_added_listeners = [] self._event_added_listeners = []
self._event_removed_listeners = [] self._event_removed_listeners = []
def event_added_subscribe(self, listener): def event_added_subscribe(self, listener):
'''Add a listener when an event is added to the queue''' """
Add a listener when an event is added to the queue
"""
if not listener in self._event_added_listeners: if not listener in self._event_added_listeners:
self._event_added_listeners.append(listener) self._event_added_listeners.append(listener)
def event_added_unsubscribe(self, listener): def event_added_unsubscribe(self, listener):
'''Remove a listener when an event is added to the queue''' """
Remove a listener when an event is added to the queue
"""
if listener in self._event_added_listeners: if listener in self._event_added_listeners:
self._event_added_listeners.remove(listener) self._event_added_listeners.remove(listener)
def event_removed_subscribe(self, listener): def event_removed_subscribe(self, listener):
'''Add a listener when an event is removed from the queue''' """
Add a listener when an event is removed from the queue
"""
if not listener in self._event_removed_listeners: if not listener in self._event_removed_listeners:
self._event_removed_listeners.append(listener) self._event_removed_listeners.append(listener)
def event_removed_unsubscribe(self, listener): def event_removed_unsubscribe(self, listener):
'''Remove a listener when an event is removed from the queue''' """
Remove a listener when an event is removed from the queue
"""
if listener in self._event_removed_listeners: if listener in self._event_removed_listeners:
self._event_removed_listeners.remove(listener) self._event_removed_listeners.remove(listener)
@ -125,9 +141,10 @@ class Events:
self.fire_event_added(event) self.fire_event_added(event)
def remove_events(self, account, jid, event = None, types = []): def remove_events(self, account, jid, event = None, types = []):
'''if event is not specified, remove all events from this jid, """
optionally only from given type If event is not specified, remove all events from this jid, optionally
return True if no such event found''' only from given type return True if no such event found
"""
if account not in self._events: if account not in self._events:
return True return True
if jid not in self._events[account]: if jid not in self._events[account]:
@ -177,10 +194,11 @@ class Events:
return self._get_nb_events(types = types, account = account) return self._get_nb_events(types = types, account = account)
def get_events(self, account, jid = None, types = []): def get_events(self, account, jid = None, types = []):
'''returns all events from the given account of the form """
{jid1: [], jid2: []} Return all events from the given account of the form {jid1: [], jid2:
if jid is given, returns all events from the given jid in a list: [] []}. If jid is given, returns all events from the given jid in a list: []
optionally only from given type''' optionally only from given type
"""
if account not in self._events: if account not in self._events:
return [] return []
if not jid: if not jid:
@ -202,7 +220,9 @@ class Events:
return events_list return events_list
def get_first_event(self, account, jid = None, type_ = None): def get_first_event(self, account, jid = None, type_ = None):
'''Return the first event of type type_ if given''' """
Return the first event of type type_ if given
"""
events_list = self.get_events(account, jid, type_) events_list = self.get_events(account, jid, type_)
# be sure it's bigger than latest event # be sure it's bigger than latest event
first_event_time = time.time() + 1 first_event_time = time.time() + 1
@ -213,9 +233,11 @@ class Events:
first_event = event first_event = event
return first_event return first_event
def _get_nb_events(self, account = None, jid = None, attribute = None, def _get_nb_events(self, account = None, jid = None, attribute = None, types
types = []): = []):
'''return the number of pending events''' """
Return the number of pending events
"""
nb = 0 nb = 0
if account: if account:
accounts = [account] accounts = [account]
@ -241,7 +263,9 @@ class Events:
return nb return nb
def _get_some_events(self, attribute): def _get_some_events(self, attribute):
'''attribute in systray, roster''' """
Attribute in systray, roster
"""
events = {} events = {}
for account in self._events: for account in self._events:
events[account] = {} events[account] = {}
@ -258,8 +282,11 @@ class Events:
return events return events
def _get_first_event_with_attribute(self, events): def _get_first_event_with_attribute(self, events):
'''get the first event """
events is in the form {account1: {jid1: [ev1, ev2], },. }''' Get the first event
events is in the form {account1: {jid1: [ev1, ev2], },. }
"""
# be sure it's bigger than latest event # be sure it's bigger than latest event
first_event_time = time.time() + 1 first_event_time = time.time() + 1
first_account = None first_account = None
@ -276,12 +303,16 @@ class Events:
return first_account, first_jid, first_event return first_account, first_jid, first_event
def get_nb_systray_events(self, types = []): def get_nb_systray_events(self, types = []):
'''returns the number of events displayed in roster''' """
Return the number of events displayed in roster
"""
return self._get_nb_events(attribute = 'systray', types = types) return self._get_nb_events(attribute = 'systray', types = types)
def get_systray_events(self): def get_systray_events(self):
'''return all events that must be displayed in systray: """
{account1: {jid1: [ev1, ev2], },. }''' Return all events that must be displayed in systray:
{account1: {jid1: [ev1, ev2], },. }
"""
return self._get_some_events('systray') return self._get_some_events('systray')
def get_first_systray_event(self): def get_first_systray_event(self):
@ -289,13 +320,17 @@ class Events:
return self._get_first_event_with_attribute(events) return self._get_first_event_with_attribute(events)
def get_nb_roster_events(self, account = None, jid = None, types = []): def get_nb_roster_events(self, account = None, jid = None, types = []):
'''returns the number of events displayed in roster''' """
Return the number of events displayed in roster
"""
return self._get_nb_events(attribute = 'roster', account = account, return self._get_nb_events(attribute = 'roster', account = account,
jid = jid, types = types) jid = jid, types = types)
def get_roster_events(self): def get_roster_events(self):
'''return all events that must be displayed in roster: """
{account1: {jid1: [ev1, ev2], },. }''' Return all events that must be displayed in roster:
{account1: {jid1: [ev1, ev2], },. }
"""
return self._get_some_events('roster') return self._get_some_events('roster')
# vim: se ts=3: # vim: se ts=3:

View File

@ -22,7 +22,10 @@
## ##
class PysqliteNotAvailable(Exception): class PysqliteNotAvailable(Exception):
'''sqlite2 is not installed or python bindings are missing''' """
Sqlite2 is not installed or python bindings are missing
"""
def __init__(self): def __init__(self):
Exception.__init__(self) Exception.__init__(self)
@ -30,7 +33,10 @@ class PysqliteNotAvailable(Exception):
return _('pysqlite2 (aka python-pysqlite2) dependency is missing. Exiting...') return _('pysqlite2 (aka python-pysqlite2) dependency is missing. Exiting...')
class PysqliteOperationalError(Exception): class PysqliteOperationalError(Exception):
'''sqlite2 raised pysqlite2.dbapi2.OperationalError''' """
Sqlite2 raised pysqlite2.dbapi2.OperationalError
"""
def __init__(self, text=''): def __init__(self, text=''):
Exception.__init__(self) Exception.__init__(self)
self.text = text self.text = text
@ -39,7 +45,10 @@ class PysqliteOperationalError(Exception):
return self.text return self.text
class DatabaseMalformed(Exception): class DatabaseMalformed(Exception):
'''The databas can't be read''' """
The databas can't be read
"""
def __init__(self): def __init__(self):
Exception.__init__(self) Exception.__init__(self)
@ -47,7 +56,10 @@ class DatabaseMalformed(Exception):
return _('Database cannot be read.') return _('Database cannot be read.')
class ServiceNotAvailable(Exception): class ServiceNotAvailable(Exception):
'''This exception is raised when we cannot use Gajim remotely''' """
This exception is raised when we cannot use Gajim remotely'
"""
def __init__(self): def __init__(self):
Exception.__init__(self) Exception.__init__(self)
@ -55,7 +67,10 @@ class ServiceNotAvailable(Exception):
return _('Service not available: Gajim is not running, or remote_control is False') return _('Service not available: Gajim is not running, or remote_control is False')
class DbusNotSupported(Exception): class DbusNotSupported(Exception):
'''D-Bus is not installed or python bindings are missing''' """
D-Bus is not installed or python bindings are missing
"""
def __init__(self): def __init__(self):
Exception.__init__(self) Exception.__init__(self)
@ -63,7 +78,10 @@ class DbusNotSupported(Exception):
return _('D-Bus is not present on this machine or python module is missing') return _('D-Bus is not present on this machine or python module is missing')
class SessionBusNotPresent(Exception): class SessionBusNotPresent(Exception):
'''This exception indicates that there is no session daemon''' """
This exception indicates that there is no session daemon
"""
def __init__(self): def __init__(self):
Exception.__init__(self) Exception.__init__(self)
@ -71,19 +89,28 @@ class SessionBusNotPresent(Exception):
return _('Session bus is not available.\nTry reading http://trac.gajim.org/wiki/GajimDBus') return _('Session bus is not available.\nTry reading http://trac.gajim.org/wiki/GajimDBus')
class NegotiationError(Exception): class NegotiationError(Exception):
'''A session negotiation failed''' """
A session negotiation failed
"""
pass pass
class DecryptionError(Exception): class DecryptionError(Exception):
'''A message couldn't be decrypted into usable XML''' """
A message couldn't be decrypted into usable XML
"""
pass pass
class Cancelled(Exception): class Cancelled(Exception):
'''The user cancelled an operation''' """
The user cancelled an operation
"""
pass pass
class LatexError(Exception): class LatexError(Exception):
'''LaTeX processing failed for some reason''' """
LaTeX processing failed for some reason
"""
def __init__(self, text=''): def __init__(self, text=''):
Exception.__init__(self) Exception.__init__(self)
self.text = text self.text = text
@ -92,7 +119,10 @@ class LatexError(Exception):
return self.text return self.text
class GajimGeneralException(Exception): class GajimGeneralException(Exception):
'''This exception is our general exception''' """
This exception is our general exception
"""
def __init__(self, text=''): def __init__(self, text=''):
Exception.__init__(self) Exception.__init__(self)
self.text = text self.text = text

View File

@ -21,7 +21,7 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
''' """
Python class to show a "fuzzy clock". Python class to show a "fuzzy clock".
Homepage of the original: http://home.gna.org/fuzzyclock/ Homepage of the original: http://home.gna.org/fuzzyclock/
Project Page of the original: http://gna.org/projects/fuzzyclock Project Page of the original: http://gna.org/projects/fuzzyclock
@ -30,7 +30,7 @@ The class is based on a port from PHP code by
Henrique Recidive <henrique at recidive.com> which was Henrique Recidive <henrique at recidive.com> which was
in turn based on the Fuzzy Clock Applet of Frerich Raabe (KDE). in turn based on the Fuzzy Clock Applet of Frerich Raabe (KDE).
So most of the credit goes to this guys, thanks :-) So most of the credit goes to this guys, thanks :-)
''' """
import time import time
@ -46,7 +46,7 @@ class FuzzyClock:
_('half past %(0)s'), _('twenty five to %(1)s'), _('twenty to %(1)s'), _('half past %(0)s'), _('twenty five to %(1)s'), _('twenty to %(1)s'),
_('quarter to %(1)s'), _('ten to %(1)s'), _('five to %(1)s'), _("%(1)s o'clock") ] _('quarter to %(1)s'), _('ten to %(1)s'), _('five to %(1)s'), _("%(1)s o'clock") ]
FUZZY_DAYTIME = [ _('Night'), _('Early morning'), _('Morning'), FUZZY_DAYTIME = [ _('Night'), _('Early morning'), _('Morning'),
_('Almost noon'), _('Noon'), _('Afternoon'), _('Evening'), _('Almost noon'), _('Noon'), _('Afternoon'), _('Evening'),
_('Late evening'), _('Night') ] _('Late evening'), _('Night') ]

View File

@ -241,8 +241,9 @@ def get_room_and_nick_from_fjid(jid):
return l return l
def get_real_jid_from_fjid(account, fjid): def get_real_jid_from_fjid(account, fjid):
'''returns real jid or returns None """
if we don't know the real jid''' Return real jid or returns None, if we don't know the real jid
"""
room_jid, nick = get_room_and_nick_from_fjid(fjid) room_jid, nick = get_room_and_nick_from_fjid(fjid)
if not nick: # It's not a fake_jid, it is a real jid if not nick: # It's not a fake_jid, it is a real jid
return fjid # we return the real jid return fjid # we return the real jid
@ -267,7 +268,9 @@ def get_jid_without_resource(jid):
return jid.split('/')[0] return jid.split('/')[0]
def construct_fjid(room_jid, nick): def construct_fjid(room_jid, nick):
''' nick is in utf8 (taken from treeview); room_jid is in unicode''' """
Nick is in UTF-8 (taken from treeview); room_jid is in unicode
"""
# fake jid is the jid for a contact in a room # fake jid is the jid for a contact in a room
# gaim@conference.jabber.org/nick # gaim@conference.jabber.org/nick
if isinstance(nick, str): if isinstance(nick, str):
@ -281,19 +284,17 @@ def get_resource_from_jid(jid):
else: else:
return '' return ''
# [15:34:28] <asterix> we should add contact.fake_jid I think
# [15:34:46] <asterix> so if we know real jid, it wil be in contact.jid, or we look in contact.fake_jid
# [15:32:54] <asterix> they can have resource if we know the real jid
# [15:33:07] <asterix> and that resource is in contact.resource
def get_number_of_accounts(): def get_number_of_accounts():
'''returns the number of ALL accounts''' """
Return the number of ALL accounts
"""
return len(connections.keys()) return len(connections.keys())
def get_number_of_connected_accounts(accounts_list = None): def get_number_of_connected_accounts(accounts_list = None):
'''returns the number of CONNECTED accounts """
you can optionally pass an accounts_list Returns the number of CONNECTED accounts. Uou can optionally pass an
and if you do those will be checked, else all will be checked''' accounts_list and if you do those will be checked, else all will be checked
"""
connected_accounts = 0 connected_accounts = 0
if accounts_list is None: if accounts_list is None:
accounts = connections.keys() accounts = connections.keys()
@ -320,7 +321,9 @@ def zeroconf_is_connected():
config.get_per('accounts', ZEROCONF_ACC_NAME, 'is_zeroconf') config.get_per('accounts', ZEROCONF_ACC_NAME, 'is_zeroconf')
def get_number_of_securely_connected_accounts(): def get_number_of_securely_connected_accounts():
'''returns the number of the accounts that are SSL/TLS connected''' """
Return the number of the accounts that are SSL/TLS connected
"""
num_of_secured = 0 num_of_secured = 0
for account in connections.keys(): for account in connections.keys():
if account_is_securely_connected(account): if account_is_securely_connected(account):
@ -335,8 +338,11 @@ def account_is_securely_connected(account):
return False return False
def get_transport_name_from_jid(jid, use_config_setting = True): def get_transport_name_from_jid(jid, use_config_setting = True):
'''returns 'aim', 'gg', 'irc' etc """
if JID is not from transport returns None''' Returns 'aim', 'gg', 'irc' etc
If JID is not from transport returns None.
"""
#FIXME: jid can be None! one TB I saw had this problem: #FIXME: jid can be None! one TB I saw had this problem:
# in the code block # it is a groupchat presence in handle_event_notify # in the code block # it is a groupchat presence in handle_event_notify
# jid was None. Yann why? # jid was None. Yann why?
@ -372,21 +378,27 @@ def jid_is_transport(jid):
return False return False
def get_jid_from_account(account_name): def get_jid_from_account(account_name):
'''return the jid we use in the given account''' """
Return the jid we use in the given account
"""
name = config.get_per('accounts', account_name, 'name') name = config.get_per('accounts', account_name, 'name')
hostname = config.get_per('accounts', account_name, 'hostname') hostname = config.get_per('accounts', account_name, 'hostname')
jid = name + '@' + hostname jid = name + '@' + hostname
return jid return jid
def get_our_jids(): def get_our_jids():
'''returns a list of the jids we use in our accounts''' """
Returns a list of the jids we use in our accounts
"""
our_jids = list() our_jids = list()
for account in contacts.get_accounts(): for account in contacts.get_accounts():
our_jids.append(get_jid_from_account(account)) our_jids.append(get_jid_from_account(account))
return our_jids return our_jids
def get_hostname_from_account(account_name, use_srv = False): def get_hostname_from_account(account_name, use_srv = False):
'''returns hostname (if custom hostname is used, that is returned)''' """
Returns hostname (if custom hostname is used, that is returned)
"""
if use_srv and connections[account_name].connected_hostname: if use_srv and connections[account_name].connected_hostname:
return connections[account_name].connected_hostname return connections[account_name].connected_hostname
if config.get_per('accounts', account_name, 'use_custom_host'): if config.get_per('accounts', account_name, 'use_custom_host'):
@ -394,7 +406,9 @@ def get_hostname_from_account(account_name, use_srv = False):
return config.get_per('accounts', account_name, 'hostname') return config.get_per('accounts', account_name, 'hostname')
def get_notification_image_prefix(jid): def get_notification_image_prefix(jid):
'''returns the prefix for the notification images''' """
Returns the prefix for the notification images
"""
transport_name = get_transport_name_from_jid(jid) transport_name = get_transport_name_from_jid(jid)
if transport_name in ('aim', 'icq', 'msn', 'yahoo', 'facebook'): if transport_name in ('aim', 'icq', 'msn', 'yahoo', 'facebook'):
prefix = transport_name prefix = transport_name
@ -403,7 +417,9 @@ def get_notification_image_prefix(jid):
return prefix return prefix
def get_name_from_jid(account, jid): def get_name_from_jid(account, jid):
'''returns from JID's shown name and if no contact returns jids''' """
Return from JID's shown name and if no contact returns jids
"""
contact = contacts.get_first_contact_from_jid(account, jid) contact = contacts.get_first_contact_from_jid(account, jid)
if contact: if contact:
actor = contact.get_shown_name() actor = contact.get_shown_name()
@ -412,7 +428,9 @@ def get_name_from_jid(account, jid):
return actor return actor
def get_priority(account, show): def get_priority(account, show):
'''return the priority an account must have''' """
Return the priority an account must have
"""
if not show: if not show:
show = 'online' show = 'online'

View File

@ -92,15 +92,19 @@ def decompose_jid(jidstring):
return user, server, resource return user, server, resource
def parse_jid(jidstring): def parse_jid(jidstring):
'''Perform stringprep on all JID fragments from a string """
and return the full jid''' Perform stringprep on all JID fragments from a string and return the full
jid
"""
# This function comes from http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py # This function comes from http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
return prep(*decompose_jid(jidstring)) return prep(*decompose_jid(jidstring))
def idn_to_ascii(host): def idn_to_ascii(host):
'''convert IDN (Internationalized Domain Names) to ACE """
(ASCII-compatible encoding)''' Convert IDN (Internationalized Domain Names) to ACE (ASCII-compatible
encoding)
"""
from encodings import idna from encodings import idna
labels = idna.dots.split(host) labels = idna.dots.split(host)
converted_labels = [] converted_labels = []
@ -109,8 +113,10 @@ def idn_to_ascii(host):
return ".".join(converted_labels) return ".".join(converted_labels)
def ascii_to_idn(host): def ascii_to_idn(host):
'''convert ACE (ASCII-compatible encoding) to IDN """
(Internationalized Domain Names)''' Convert ACE (ASCII-compatible encoding) to IDN (Internationalized Domain
Names)
"""
from encodings import idna from encodings import idna
labels = idna.dots.split(host) labels = idna.dots.split(host)
converted_labels = [] converted_labels = []
@ -119,7 +125,9 @@ def ascii_to_idn(host):
return ".".join(converted_labels) return ".".join(converted_labels)
def parse_resource(resource): def parse_resource(resource):
'''Perform stringprep on resource and return it''' """
Perform stringprep on resource and return it
"""
if resource: if resource:
try: try:
from xmpp.stringprepare import resourceprep from xmpp.stringprepare import resourceprep
@ -128,10 +136,11 @@ def parse_resource(resource):
raise InvalidFormat, 'Invalid character in resource.' raise InvalidFormat, 'Invalid character in resource.'
def prep(user, server, resource): def prep(user, server, resource):
'''Perform stringprep on all JID fragments and return the full jid''' """
Perform stringprep on all JID fragments and return the full jid
"""
# This function comes from # This function comes from
#http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py #http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
if user: if user:
try: try:
from xmpp.stringprepare import nodeprep from xmpp.stringprepare import nodeprep
@ -186,10 +195,13 @@ def temp_failure_retry(func, *args, **kwargs):
raise raise
def get_uf_show(show, use_mnemonic = False): def get_uf_show(show, use_mnemonic = False):
'''returns a userfriendly string for dnd/xa/chat """
and makes all strings translatable Return a userfriendly string for dnd/xa/chat and make all strings
if use_mnemonic is True, it adds _ so GUI should call with True translatable
for accessibility issues'''
If use_mnemonic is True, it adds _ so GUI should call with True for
accessibility issues
"""
if show == 'dnd': if show == 'dnd':
if use_mnemonic: if use_mnemonic:
uf_show = _('_Busy') uf_show = _('_Busy')
@ -324,7 +336,9 @@ def from_one_line(msg):
return msg return msg
def get_uf_chatstate(chatstate): def get_uf_chatstate(chatstate):
'''removes chatstate jargon and returns user friendly messages''' """
Remove chatstate jargon and returns user friendly messages
"""
if chatstate == 'active': if chatstate == 'active':
return _('is paying attention to the conversation') return _('is paying attention to the conversation')
elif chatstate == 'inactive': elif chatstate == 'inactive':
@ -339,10 +353,11 @@ def get_uf_chatstate(chatstate):
return '' return ''
def is_in_path(command, return_abs_path=False): def is_in_path(command, return_abs_path=False):
'''Returns True if 'command' is found in one of the directories in the """
user's path. If 'return_abs_path' is True, returns the absolute path of Return True if 'command' is found in one of the directories in the user's
the first found command instead. Returns False otherwise and on errors.''' path. If 'return_abs_path' is True, return the absolute path of the first
found command instead. Return False otherwise and on errors
"""
for directory in os.getenv('PATH').split(os.pathsep): for directory in os.getenv('PATH').split(os.pathsep):
try: try:
if command in os.listdir(directory): if command in os.listdir(directory):
@ -405,7 +420,9 @@ def get_output_of_command(command):
return output return output
def decode_string(string): def decode_string(string):
'''try to decode (to make it Unicode instance) given string''' """
Try to decode (to make it Unicode instance) given string
"""
if isinstance(string, unicode): if isinstance(string, unicode):
return string return string
# by the time we go to iso15 it better be the one else we show bad characters # by the time we go to iso15 it better be the one else we show bad characters
@ -420,7 +437,9 @@ def decode_string(string):
return string return string
def ensure_utf8_string(string): def ensure_utf8_string(string):
'''make sure string is in UTF-8''' """
Make sure string is in UTF-8
"""
try: try:
string = decode_string(string).encode('utf-8') string = decode_string(string).encode('utf-8')
except Exception: except Exception:
@ -428,28 +447,28 @@ def ensure_utf8_string(string):
return string return string
def get_windows_reg_env(varname, default=''): def get_windows_reg_env(varname, default=''):
'''asks for paths commonly used but not exposed as ENVs """
in english Windows 2003 those are: Ask for paths commonly used but not exposed as ENVs in english Windows 2003
'AppData' = %USERPROFILE%\Application Data (also an ENV) those are:
'Desktop' = %USERPROFILE%\Desktop 'AppData' = %USERPROFILE%\Application Data (also an ENV)
'Favorites' = %USERPROFILE%\Favorites 'Desktop' = %USERPROFILE%\Desktop
'NetHood' = %USERPROFILE%\NetHood 'Favorites' = %USERPROFILE%\Favorites
'Personal' = D:\My Documents (PATH TO MY DOCUMENTS) 'NetHood' = %USERPROFILE%\NetHood
'PrintHood' = %USERPROFILE%\PrintHood 'Personal' = D:\My Documents (PATH TO MY DOCUMENTS)
'Programs' = %USERPROFILE%\Start Menu\Programs 'PrintHood' = %USERPROFILE%\PrintHood
'Recent' = %USERPROFILE%\Recent 'Programs' = %USERPROFILE%\Start Menu\Programs
'SendTo' = %USERPROFILE%\SendTo 'Recent' = %USERPROFILE%\Recent
'Start Menu' = %USERPROFILE%\Start Menu 'SendTo' = %USERPROFILE%\SendTo
'Startup' = %USERPROFILE%\Start Menu\Programs\Startup 'Start Menu' = %USERPROFILE%\Start Menu
'Templates' = %USERPROFILE%\Templates 'Startup' = %USERPROFILE%\Start Menu\Programs\Startup
'My Pictures' = D:\My Documents\My Pictures 'Templates' = %USERPROFILE%\Templates
'Local Settings' = %USERPROFILE%\Local Settings 'My Pictures' = D:\My Documents\My Pictures
'Local AppData' = %USERPROFILE%\Local Settings\Application Data 'Local Settings' = %USERPROFILE%\Local Settings
'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files 'Local AppData' = %USERPROFILE%\Local Settings\Application Data
'Cookies' = %USERPROFILE%\Cookies 'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files
'History' = %USERPROFILE%\Local Settings\History 'Cookies' = %USERPROFILE%\Cookies
''' 'History' = %USERPROFILE%\Local Settings\History
"""
if os.name != 'nt': if os.name != 'nt':
return '' return ''
@ -467,7 +486,9 @@ r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders')
return val return val
def get_my_pictures_path(): def get_my_pictures_path():
'''windows-only atm. [Unix lives in the past]''' """
Windows-only atm
"""
return get_windows_reg_env('My Pictures') return get_windows_reg_env('My Pictures')
def get_desktop_path(): def get_desktop_path():
@ -485,8 +506,10 @@ def get_documents_path():
return path return path
def sanitize_filename(filename): def sanitize_filename(filename):
'''makes sure the filename we will write does contain only acceptable and """
latin characters, and is not too long (in that case hash it)''' Make sure the filename we will write does contain only acceptable and latin
characters, and is not too long (in that case hash it)
"""
# 48 is the limit # 48 is the limit
if len(filename) > 48: if len(filename) > 48:
hash = hashlib.md5(filename) hash = hashlib.md5(filename)
@ -502,11 +525,13 @@ def sanitize_filename(filename):
return filename return filename
def reduce_chars_newlines(text, max_chars = 0, max_lines = 0): def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
'''Cut the chars after 'max_chars' on each line """
and show only the first 'max_lines'. Cut the chars after 'max_chars' on each line and show only the first
If any of the params is not present (None or 0) the action 'max_lines'
on it is not performed'''
If any of the params is not present (None or 0) the action on it is not
performed
"""
def _cut_if_long(string): def _cut_if_long(string):
if len(string) > max_chars: if len(string) > max_chars:
string = string[:max_chars - 3] + '...' string = string[:max_chars - 3] + '...'
@ -535,10 +560,11 @@ def get_account_status(account):
return status return status
def get_avatar_path(prefix): def get_avatar_path(prefix):
'''Returns the filename of the avatar, distinguishes between user- and """
contact-provided one. Returns None if no avatar was found at all. Return the filename of the avatar, distinguishes between user- and contact-
prefix is the path to the requested avatar just before the ".png" or provided one. Return None if no avatar was found at all. prefix is the path
".jpeg".''' to the requested avatar just before the ".png" or ".jpeg"
"""
# First, scan for a local, user-set avatar # First, scan for a local, user-set avatar
for type_ in ('jpeg', 'png'): for type_ in ('jpeg', 'png'):
file_ = prefix + '_local.' + type_ file_ = prefix + '_local.' + type_
@ -552,13 +578,16 @@ def get_avatar_path(prefix):
return None return None
def datetime_tuple(timestamp): def datetime_tuple(timestamp):
'''Converts timestamp using strptime and the format: %Y%m%dT%H:%M:%S """
Convert timestamp using strptime and the format: %Y%m%dT%H:%M:%S
Because of various datetime formats are used the following exceptions Because of various datetime formats are used the following exceptions
are handled: are handled:
- Optional milliseconds appened to the string are removed - Optional milliseconds appened to the string are removed
- Optional Z (that means UTC) appened to the string are removed - Optional Z (that means UTC) appened to the string are removed
- XEP-082 datetime strings have all '-' cahrs removed to meet - XEP-082 datetime strings have all '-' cahrs removed to meet
the above format.''' the above format.
"""
timestamp = timestamp.split('.')[0] timestamp = timestamp.split('.')[0]
timestamp = timestamp.replace('-', '') timestamp = timestamp.replace('-', '')
timestamp = timestamp.replace('z', '') timestamp = timestamp.replace('z', '')
@ -609,8 +638,11 @@ def convert_bytes(string):
return suffix % unicode(bytes) return suffix % unicode(bytes)
def get_contact_dict_for_account(account): def get_contact_dict_for_account(account):
''' create a dict of jid, nick -> contact with all contacts of account. """
Can be used for completion lists''' Create a dict of jid, nick -> contact with all contacts of account.
Can be used for completion lists
"""
contacts_dict = {} contacts_dict = {}
for jid in gajim.contacts.get_jid_list(account): for jid in gajim.contacts.get_jid_list(account):
contact = gajim.contacts.get_contact_with_highest_priority(account, contact = gajim.contacts.get_contact_with_highest_priority(account,
@ -692,13 +724,15 @@ def play_sound(event):
path_to_soundfile = gajim.config.get_per('soundevents', event, 'path') path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
play_sound_file(path_to_soundfile) play_sound_file(path_to_soundfile)
def check_soundfile_path(file, def check_soundfile_path(file, dirs=(gajim.gajimpaths.root, gajim.DATA_DIR)):
dirs=(gajim.gajimpaths.root, gajim.DATA_DIR)): """
'''Check if the sound file exists. Check if the sound file exists
:param file: the file to check, absolute or relative to 'dirs' path :param file: the file to check, absolute or relative to 'dirs' path
:param dirs: list of knows paths to fallback if the file doesn't exists :param dirs: list of knows paths to fallback if the file doesn't exists
(eg: ~/.gajim/sounds/, DATADIR/sounds...). (eg: ~/.gajim/sounds/, DATADIR/sounds...).
:return the path to file or None if it doesn't exists.''' :return the path to file or None if it doesn't exists.
"""
if not file: if not file:
return None return None
elif os.path.exists(file): elif os.path.exists(file):
@ -710,16 +744,17 @@ def check_soundfile_path(file,
return d return d
return None return None
def strip_soundfile_path(file, def strip_soundfile_path(file, dirs=(gajim.gajimpaths.root, gajim.DATA_DIR),
dirs=(gajim.gajimpaths.root, gajim.DATA_DIR), abs=True):
abs=True): """
'''Remove knowns paths from a sound file: Remove knowns paths from a sound file
Filechooser returns absolute path. If path is a known fallback path, we remove it. Filechooser returns absolute path. If path is a known fallback path, we remove it.
So config have no hardcoded path to DATA_DIR and text in textfield is shorther. So config have no hardcoded path to DATA_DIR and text in textfield is shorther.
param: file: the filename to strip. param: file: the filename to strip.
param: dirs: list of knowns paths from which the filename should be stripped. param: dirs: list of knowns paths from which the filename should be stripped.
param: abs: force absolute path on dirs param: abs: force absolute path on dirs
''' """
if not file: if not file:
return None return None
@ -777,7 +812,9 @@ def get_global_status():
def statuses_unified(): def statuses_unified():
'''testing if all statuses are the same.''' """
Test if all statuses are the same
"""
reference = None reference = None
for account in gajim.connections: for account in gajim.connections:
if not gajim.config.get_per('accounts', account, if not gajim.config.get_per('accounts', account,
@ -790,7 +827,9 @@ def statuses_unified():
return True return True
def get_icon_name_to_show(contact, account = None): def get_icon_name_to_show(contact, account = None):
'''Get the icon name to show in online, away, requested, ...''' """
Get the icon name to show in online, away, requested, etc
"""
if account and gajim.events.get_nb_roster_events(account, contact.jid): if account and gajim.events.get_nb_roster_events(account, contact.jid):
return 'event' return 'event'
if account and gajim.events.get_nb_roster_events(account, if account and gajim.events.get_nb_roster_events(account,
@ -819,16 +858,22 @@ def get_icon_name_to_show(contact, account = None):
return 'not in roster' return 'not in roster'
def get_full_jid_from_iq(iq_obj): def get_full_jid_from_iq(iq_obj):
'''return the full jid (with resource) from an iq as unicode''' """
Return the full jid (with resource) from an iq as unicode
"""
return parse_jid(str(iq_obj.getFrom())) return parse_jid(str(iq_obj.getFrom()))
def get_jid_from_iq(iq_obj): def get_jid_from_iq(iq_obj):
'''return the jid (without resource) from an iq as unicode''' """
Return the jid (without resource) from an iq as unicode
"""
jid = get_full_jid_from_iq(iq_obj) jid = get_full_jid_from_iq(iq_obj)
return gajim.get_jid_without_resource(jid) return gajim.get_jid_without_resource(jid)
def get_auth_sha(sid, initiator, target): def get_auth_sha(sid, initiator, target):
''' return sha of sid + initiator + target used for proxy auth''' """
Return sha of sid + initiator + target used for proxy auth
"""
return hashlib.sha1("%s%s%s" % (sid, initiator, target)).hexdigest() return hashlib.sha1("%s%s%s" % (sid, initiator, target)).hexdigest()
def remove_invalid_xml_chars(string): def remove_invalid_xml_chars(string):
@ -861,7 +906,9 @@ distro_info = {
} }
def get_random_string_16(): def get_random_string_16():
''' create random string of length 16''' """
Create random string of length 16
"""
rng = range(65, 90) rng = range(65, 90)
rng.extend(range(48, 57)) rng.extend(range(48, 57))
char_sequence = [chr(e) for e in rng] char_sequence = [chr(e) for e in rng]
@ -946,11 +993,14 @@ def get_os_info():
def allow_showing_notification(account, type_ = 'notify_on_new_message', def allow_showing_notification(account, type_ = 'notify_on_new_message',
advanced_notif_num = None, is_first_message = True): advanced_notif_num = None, is_first_message = True):
'''is it allowed to show nofication? """
check OUR status and if we allow notifications for that status Is it allowed to show nofication?
type is the option that need to be True e.g.: notify_on_signing
is_first_message: set it to false when it's not the first message''' Check OUR status and if we allow notifications for that status type is the
option that need to be True e.g.: notify_on_signing is_first_message: set it
to false when it's not the first message
"""
if advanced_notif_num is not None: if advanced_notif_num is not None:
popup = gajim.config.get_per('notifications', str(advanced_notif_num), popup = gajim.config.get_per('notifications', str(advanced_notif_num),
'popup') 'popup')
@ -967,7 +1017,9 @@ advanced_notif_num = None, is_first_message = True):
return False return False
def allow_popup_window(account, advanced_notif_num = None): def allow_popup_window(account, advanced_notif_num = None):
'''is it allowed to popup windows?''' """
Is it allowed to popup windows?
"""
if advanced_notif_num is not None: if advanced_notif_num is not None:
popup = gajim.config.get_per('notifications', str(advanced_notif_num), popup = gajim.config.get_per('notifications', str(advanced_notif_num),
'auto_open') 'auto_open')
@ -1018,8 +1070,10 @@ def get_chat_control(account, contact):
return gajim.interface.msg_win_mgr.get_control(contact.jid, account) return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
def get_notification_icon_tooltip_dict(): def get_notification_icon_tooltip_dict():
'''returns a dict of the form {acct: {'show': show, 'message': message, """
'event_lines': [list of text lines to show in tooltip]}''' Return a dict of the form {acct: {'show': show, 'message': message,
'event_lines': [list of text lines to show in tooltip]}
"""
# How many events must there be before they're shown summarized, not per-user # How many events must there be before they're shown summarized, not per-user
max_ungrouped_events = 10 max_ungrouped_events = 10
@ -1131,7 +1185,9 @@ def get_notification_icon_tooltip_text():
return text return text
def get_accounts_info(): def get_accounts_info():
'''helper for notification icon tooltip''' """
Helper for notification icon tooltip
"""
accounts = [] accounts = []
accounts_list = sorted(gajim.contacts.get_accounts()) accounts_list = sorted(gajim.contacts.get_accounts())
for account in accounts_list: for account in accounts_list:
@ -1182,9 +1238,14 @@ def get_transport_path(transport):
return get_iconset_path(gajim.config.get('iconset')) return get_iconset_path(gajim.config.get('iconset'))
def prepare_and_validate_gpg_keyID(account, jid, keyID): def prepare_and_validate_gpg_keyID(account, jid, keyID):
'''Returns an eight char long keyID that can be used with for GPG encryption with this contact. """
If the given keyID is None, return UNKNOWN; if the key does not match the assigned key Return an eight char long keyID that can be used with for GPG encryption
XXXXXXXXMISMATCH is returned. If the key is trusted and not yet assigned, assign it''' with this contact
If the given keyID is None, return UNKNOWN; if the key does not match the
assigned key XXXXXXXXMISMATCH is returned. If the key is trusted and not yet
assigned, assign it.
"""
if gajim.connections[account].USE_GPG: if gajim.connections[account].USE_GPG:
if keyID and len(keyID) == 16: if keyID and len(keyID) == 16:
keyID = keyID[8:] keyID = keyID[8:]

View File

@ -32,7 +32,7 @@ def paragraph_direction_mark(text):
""" """
Determine paragraph writing direction according to Determine paragraph writing direction according to
http://www.unicode.org/reports/tr9/#The_Paragraph_Level http://www.unicode.org/reports/tr9/#The_Paragraph_Level
Returns either Unicode LTR mark or RTL mark. Returns either Unicode LTR mark or RTL mark.
""" """
for char in text: for char in text:
@ -84,11 +84,12 @@ def Q_(s):
return s return s
def ngettext(s_sing, s_plural, n, replace_sing = None, replace_plural = None): def ngettext(s_sing, s_plural, n, replace_sing = None, replace_plural = None):
'''use as: """
i18n.ngettext('leave room %s', 'leave rooms %s', len(rooms), 'a', 'a, b, c') Use as:
i18n.ngettext('leave room %s', 'leave rooms %s', len(rooms), 'a', 'a, b, c')
in other words this is a hack to ngettext() to support %s %d etc.. In other words this is a hack to ngettext() to support %s %d etc..
''' """
text = _translation.ungettext(s_sing, s_plural, n) text = _translation.ungettext(s_sing, s_plural, n)
if n == 1 and replace_sing is not None: if n == 1 and replace_sing is not None:
text = text % replace_sing text = text % replace_sing

View File

@ -73,7 +73,9 @@ except OSError, e:
xss_available = False xss_available = False
def getIdleSec(): def getIdleSec():
"""Returns the idle time in seconds""" """
Return the idle time in seconds
"""
if not xss_available: if not xss_available:
return 0 return 0
if libXss.XScreenSaverQueryInfo(dpy_p, rootwindow, xss_info_p) == 0: if libXss.XScreenSaverQueryInfo(dpy_p, rootwindow, xss_info_p) == 0:

View File

@ -10,7 +10,9 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
## ##
''' Handles the jingle signalling protocol. ''' """
Handles the jingle signalling protocol
"""
#TODO: #TODO:
# * things in XEP 0176, including: # * things in XEP 0176, including:
@ -37,7 +39,10 @@ from jingle_rtp import JingleAudio, JingleVideo
class ConnectionJingle(object): class ConnectionJingle(object):
''' This object depends on that it is a part of Connection class. ''' """
This object depends on that it is a part of Connection class.
"""
def __init__(self): def __init__(self):
# dictionary: (jid, sessionid) => JingleSession object # dictionary: (jid, sessionid) => JingleSession object
self.__sessions = {} self.__sessions = {}
@ -47,13 +52,17 @@ class ConnectionJingle(object):
self.__iq_responses = {} self.__iq_responses = {}
def add_jingle(self, jingle): def add_jingle(self, jingle):
''' Add a jingle session to a jingle stanza dispatcher """
Add a jingle session to a jingle stanza dispatcher
jingle - a JingleSession object. jingle - a JingleSession object.
''' """
self.__sessions[(jingle.peerjid, jingle.sid)] = jingle self.__sessions[(jingle.peerjid, jingle.sid)] = jingle
def delete_jingle_session(self, peerjid, sid): def delete_jingle_session(self, peerjid, sid):
''' Remove a jingle session from a jingle stanza dispatcher ''' """
Remove a jingle session from a jingle stanza dispatcher
"""
key = (peerjid, sid) key = (peerjid, sid)
if key in self.__sessions: if key in self.__sessions:
#FIXME: Move this elsewhere? #FIXME: Move this elsewhere?
@ -63,12 +72,15 @@ class ConnectionJingle(object):
del self.__sessions[key] del self.__sessions[key]
def _JingleCB(self, con, stanza): def _JingleCB(self, con, stanza):
''' The jingle stanza dispatcher. """
Route jingle stanza to proper JingleSession object, The jingle stanza dispatcher
or create one if it is a new session.
TODO: Also check if the stanza isn't an error stanza, if so
route it adequatelly.'''
Route jingle stanza to proper JingleSession object, or create one if it
is a new session.
TODO: Also check if the stanza isn't an error stanza, if so route it
adequatelly.
"""
# get data # get data
jid = helpers.get_full_jid_from_iq(stanza) jid = helpers.get_full_jid_from_iq(stanza)
id = stanza.getID() id = stanza.getID()

View File

@ -10,7 +10,10 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
## ##
''' Handles Jingle contents (XEP 0166). '''
"""
Handles Jingle contents (XEP 0166)
"""
contents = {} contents = {}
@ -23,7 +26,10 @@ def get_jingle_content(node):
class JingleContent(object): class JingleContent(object):
''' An abstraction of content in Jingle sessions. ''' """
An abstraction of content in Jingle sessions
"""
def __init__(self, session, transport): def __init__(self, session, transport):
self.session = session self.session = session
self.transport = transport self.transport = transport
@ -71,24 +77,32 @@ class JingleContent(object):
return (self.accepted and not self.sent) return (self.accepted and not self.sent)
def add_remote_candidates(self, candidates): def add_remote_candidates(self, candidates):
''' Add a list of candidates to the list of remote candidates. ''' """
Add a list of candidates to the list of remote candidates
"""
pass pass
def stanzaCB(self, stanza, content, error, action): def stanzaCB(self, stanza, content, error, action):
''' Called when something related to our content was sent by peer. ''' """
Called when something related to our content was sent by peer
"""
if action in self.callbacks: if action in self.callbacks:
for callback in self.callbacks[action]: for callback in self.callbacks[action]:
callback(stanza, content, error, action) callback(stanza, content, error, action)
def __transportInfoCB(self, stanza, content, error, action): def __transportInfoCB(self, stanza, content, error, action):
''' Got a new transport candidate. ''' """
Got a new transport candidate
"""
candidates = self.transport.parse_transport_stanza( candidates = self.transport.parse_transport_stanza(
content.getTag('transport')) content.getTag('transport'))
if candidates: if candidates:
self.add_remote_candidates(candidates) self.add_remote_candidates(candidates)
def __content(self, payload=[]): def __content(self, payload=[]):
''' Build a XML content-wrapper for our data. ''' """
Build a XML content-wrapper for our data
"""
return xmpp.Node('content', return xmpp.Node('content',
attrs={'name': self.name, 'creator': self.creator}, attrs={'name': self.name, 'creator': self.creator},
payload=payload) payload=payload)
@ -99,7 +113,9 @@ class JingleContent(object):
self.session.send_transport_info(content) self.session.send_transport_info(content)
def __fillJingleStanza(self, stanza, content, error, action): def __fillJingleStanza(self, stanza, content, error, action):
''' Add our things to session-initiate stanza. ''' """
Add our things to session-initiate stanza
"""
self._fillContent(content) self._fillContent(content)
self.sent = True self.sent = True
content.addChild(node=self.transport.make_transport()) content.addChild(node=self.transport.make_transport())

View File

@ -10,8 +10,10 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
## ##
''' Handles Jingle RTP sessions (XEP 0167). '''
"""
Handles Jingle RTP sessions (XEP 0167)
"""
import gobject import gobject
@ -23,7 +25,9 @@ from jingle_content import contents, JingleContent
# TODO: Will that be even used? # TODO: Will that be even used?
def get_first_gst_element(elements): def get_first_gst_element(elements):
''' Returns, if it exists, the first available element of the list. ''' """
Return, if it exists, the first available element of the list
"""
for name in elements: for name in elements:
factory = gst.element_factory_find(name) factory = gst.element_factory_find(name)
if factory: if factory:
@ -70,7 +74,7 @@ class JingleRTPContent(JingleContent):
self.p2psession = self.conference.new_session(self.farsight_media) self.p2psession = self.conference.new_session(self.farsight_media)
participant = self.conference.new_participant(self.session.peerjid) participant = self.conference.new_participant(self.session.peerjid)
#FIXME: Consider a workaround, here... # FIXME: Consider a workaround, here...
# pidgin and telepathy-gabble don't follow the XEP, and it won't work # pidgin and telepathy-gabble don't follow the XEP, and it won't work
# due to bad controlling-mode # due to bad controlling-mode
params = {'controlling-mode': self.session.weinitiate,# 'debug': False} params = {'controlling-mode': self.session.weinitiate,# 'debug': False}
@ -85,14 +89,14 @@ class JingleRTPContent(JingleContent):
def add_remote_candidates(self, candidates): def add_remote_candidates(self, candidates):
JingleContent.add_remote_candidates(self, candidates) JingleContent.add_remote_candidates(self, candidates)
#FIXME: connectivity should not be etablished yet # FIXME: connectivity should not be etablished yet
# Instead, it should be etablished after session-accept! # Instead, it should be etablished after session-accept!
if self.sent: if self.sent:
self.p2pstream.set_remote_candidates(candidates) self.p2pstream.set_remote_candidates(candidates)
def batch_dtmf(self, events): def batch_dtmf(self, events):
if self._dtmf_running: if self._dtmf_running:
raise Exception #TODO: Proper exception raise Exception # TODO: Proper exception
self._dtmf_running = True self._dtmf_running = True
self._start_dtmf(events.pop(0)) self._start_dtmf(events.pop(0))
gobject.timeout_add(500, self._next_dtmf, events) gobject.timeout_add(500, self._next_dtmf, events)
@ -143,7 +147,7 @@ class JingleRTPContent(JingleContent):
elif name == 'farsight-codecs-changed': elif name == 'farsight-codecs-changed':
if self.is_ready(): if self.is_ready():
self.session.on_session_state_changed(self) self.session.on_session_state_changed(self)
#TODO: description-info # TODO: description-info
elif name == 'farsight-local-candidates-prepared': elif name == 'farsight-local-candidates-prepared':
self.candidates_ready = True self.candidates_ready = True
if self.is_ready(): if self.is_ready():
@ -152,7 +156,7 @@ class JingleRTPContent(JingleContent):
candidate = message.structure['candidate'] candidate = message.structure['candidate']
self.transport.candidates.append(candidate) self.transport.candidates.append(candidate)
if self.candidates_ready: if self.candidates_ready:
#FIXME: Is this case even possible? # FIXME: Is this case even possible?
self.send_candidate(candidate) self.send_candidate(candidate)
elif name == 'farsight-component-state-changed': elif name == 'farsight-component-state-changed':
state = message.structure['state'] state = message.structure['state']
@ -173,7 +177,7 @@ class JingleRTPContent(JingleContent):
if self.transport.remote_candidates: if self.transport.remote_candidates:
self.p2pstream.set_remote_candidates(self.transport.remote_candidates) self.p2pstream.set_remote_candidates(self.transport.remote_candidates)
self.transport.remote_candidates = [] self.transport.remote_candidates = []
#TODO: farsight.DIRECTION_BOTH only if senders='both' # TODO: farsight.DIRECTION_BOTH only if senders='both'
self.p2pstream.set_property('direction', farsight.DIRECTION_BOTH) self.p2pstream.set_property('direction', farsight.DIRECTION_BOTH)
self.session.content_negociated(self.media) self.session.content_negociated(self.media)
@ -195,7 +199,7 @@ class JingleRTPContent(JingleContent):
codecs.append(c) codecs.append(c)
if len(codecs) > 0: if len(codecs) > 0:
#FIXME: Handle this case: # FIXME: Handle this case:
# glib.GError: There was no intersection between the remote codecs and # glib.GError: There was no intersection between the remote codecs and
# the local ones # the local ones
self.p2pstream.set_remote_codecs(codecs) self.p2pstream.set_remote_codecs(codecs)
@ -228,14 +232,15 @@ class JingleRTPContent(JingleContent):
class JingleAudio(JingleRTPContent): class JingleAudio(JingleRTPContent):
''' Jingle VoIP sessions consist of audio content transported """
over an ICE UDP protocol. ''' Jingle VoIP sessions consist of audio content transported over an ICE UDP
protocol
"""
def __init__(self, session, transport=None): def __init__(self, session, transport=None):
JingleRTPContent.__init__(self, session, 'audio', transport) JingleRTPContent.__init__(self, session, 'audio', transport)
self.setup_stream() self.setup_stream()
''' Things to control the gstreamer's pipeline '''
def setup_stream(self): def setup_stream(self):
JingleRTPContent.setup_stream(self) JingleRTPContent.setup_stream(self)
@ -283,9 +288,8 @@ class JingleVideo(JingleRTPContent):
JingleRTPContent.__init__(self, session, 'video', transport) JingleRTPContent.__init__(self, session, 'video', transport)
self.setup_stream() self.setup_stream()
''' Things to control the gstreamer's pipeline '''
def setup_stream(self): def setup_stream(self):
#TODO: Everything is not working properly: # TODO: Everything is not working properly:
# sometimes, one window won't show up, # sometimes, one window won't show up,
# sometimes it'll freeze... # sometimes it'll freeze...
JingleRTPContent.setup_stream(self) JingleRTPContent.setup_stream(self)

View File

@ -10,7 +10,10 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
## ##
''' Handles Jingle sessions (XEP 0166). '''
"""
Handles Jingle sessions (XEP 0166)
"""
#TODO: #TODO:
# * Have JingleContent here # * Have JingleContent here
@ -29,25 +32,38 @@ import xmpp
from jingle_transport import get_jingle_transport from jingle_transport import get_jingle_transport
from jingle_content import get_jingle_content from jingle_content import get_jingle_content
#FIXME: Move it to JingleSession.States? # FIXME: Move it to JingleSession.States?
class JingleStates(object): class JingleStates(object):
''' States in which jingle session may exist. ''' """
States in which jingle session may exist
"""
ended = 0 ended = 0
pending = 1 pending = 1
active = 2 active = 2
class OutOfOrder(Exception): class OutOfOrder(Exception):
''' Exception that should be raised when an action is received when in the wrong state. ''' """
Exception that should be raised when an action is received when in the wrong
state
"""
class TieBreak(Exception): class TieBreak(Exception):
''' Exception that should be raised in case of a tie, when we overrule the other action. ''' """
Exception that should be raised in case of a tie, when we overrule the other
action
"""
class JingleSession(object): class JingleSession(object):
''' This represents one jingle session. ''' """
This represents one jingle session
"""
def __init__(self, con, weinitiate, jid, sid=None): def __init__(self, con, weinitiate, jid, sid=None):
''' con -- connection object, """
weinitiate -- boolean, are we the initiator? con -- connection object,
jid - jid of the other entity''' weinitiate -- boolean, are we the initiator?
jid - jid of the other entity
"""
self.contents = {} # negotiated contents self.contents = {} # negotiated contents
self.connection = con # connection to use self.connection = con # connection to use
# our full jid # our full jid
@ -97,15 +113,16 @@ class JingleSession(object):
'iq-error': [self.__errorCB], 'iq-error': [self.__errorCB],
} }
''' Interaction with user '''
def approve_session(self): def approve_session(self):
''' Called when user accepts session in UI (when we aren't the initiator). """
''' Called when user accepts session in UI (when we aren't the initiator)
"""
self.accept_session() self.accept_session()
def decline_session(self): def decline_session(self):
''' Called when user declines session in UI (when we aren't the initiator) """
''' Called when user declines session in UI (when we aren't the initiator)
"""
reason = xmpp.Node('reason') reason = xmpp.Node('reason')
reason.addChild('decline') reason.addChild('decline')
self._session_terminate(reason) self._session_terminate(reason)
@ -125,7 +142,9 @@ class JingleSession(object):
self.on_session_state_changed() self.on_session_state_changed()
def end_session(self): def end_session(self):
''' Called when user stops or cancel session in UI. ''' """
Called when user stops or cancel session in UI
"""
reason = xmpp.Node('reason') reason = xmpp.Node('reason')
if self.state == JingleStates.active: if self.state == JingleStates.active:
reason.addChild('success') reason.addChild('success')
@ -133,8 +152,6 @@ class JingleSession(object):
reason.addChild('cancel') reason.addChild('cancel')
self._session_terminate(reason) self._session_terminate(reason)
''' Middle-level functions to manage contents. Handle local content
cache and send change notifications. '''
def get_content(self, media=None): def get_content(self, media=None):
if media is None: if media is None:
return None return None
@ -144,9 +161,12 @@ class JingleSession(object):
return content return content
def add_content(self, name, content, creator='we'): def add_content(self, name, content, creator='we'):
''' Add new content to session. If the session is active, """
this will send proper stanza to update session. Add new content to session. If the session is active, this will send
Creator must be one of ('we', 'peer', 'initiator', 'responder')''' proper stanza to update session
Creator must be one of ('we', 'peer', 'initiator', 'responder')
"""
assert creator in ('we', 'peer', 'initiator', 'responder') assert creator in ('we', 'peer', 'initiator', 'responder')
if (creator == 'we' and self.weinitiate) or (creator == 'peer' and \ if (creator == 'we' and self.weinitiate) or (creator == 'peer' and \
@ -164,7 +184,9 @@ class JingleSession(object):
content.accepted = True content.accepted = True
def remove_content(self, creator, name): def remove_content(self, creator, name):
''' We do not need this now ''' """
We do not need this now
"""
#TODO: #TODO:
if (creator, name) in self.contents: if (creator, name) in self.contents:
content = self.contents[(creator, name)] content = self.contents[(creator, name)]
@ -175,7 +197,9 @@ class JingleSession(object):
self.end_session() self.end_session()
def modify_content(self, creator, name, *someother): def modify_content(self, creator, name, *someother):
''' We do not need this now ''' """
We do not need this now
"""
pass pass
def on_session_state_changed(self, content=None): def on_session_state_changed(self, content=None):
@ -203,19 +227,23 @@ class JingleSession(object):
self.__content_accept(content) self.__content_accept(content)
def is_ready(self): def is_ready(self):
''' Returns True when all codecs and candidates are ready """
(for all contents). ''' Return True when all codecs and candidates are ready (for all contents)
"""
return (all((content.is_ready() for content in self.contents.itervalues())) return (all((content.is_ready() for content in self.contents.itervalues()))
and self.accepted) and self.accepted)
''' Middle-level function to do stanza exchange. '''
def accept_session(self): def accept_session(self):
''' Mark the session as accepted. ''' """
Mark the session as accepted
"""
self.accepted = True self.accepted = True
self.on_session_state_changed() self.on_session_state_changed()
def start_session(self): def start_session(self):
''' Mark the session as ready to be started. ''' """
Mark the session as ready to be started
"""
self.accepted = True self.accepted = True
self.on_session_state_changed() self.on_session_state_changed()
@ -234,11 +262,12 @@ class JingleSession(object):
jingle.addChild(node=content) jingle.addChild(node=content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
''' Session callbacks. '''
def stanzaCB(self, stanza): def stanzaCB(self, stanza):
''' A callback for ConnectionJingle. It gets stanza, then """
tries to send it to all internally registered callbacks. A callback for ConnectionJingle. It gets stanza, then tries to send it to
First one to raise xmpp.NodeProcessed breaks function.''' all internally registered callbacks. First one to raise
xmpp.NodeProcessed breaks function
"""
jingle = stanza.getTag('jingle') jingle = stanza.getTag('jingle')
error = stanza.getTag('error') error = stanza.getTag('error')
if error: if error:
@ -250,7 +279,7 @@ class JingleSession(object):
if action not in self.callbacks: if action not in self.callbacks:
self.__send_error(stanza, 'bad_request') self.__send_error(stanza, 'bad_request')
return return
#FIXME: If we aren't initiated and it's not a session-initiate... # FIXME: If we aren't initiated and it's not a session-initiate...
if action != 'session-initiate' and self.state == JingleStates.ended: if action != 'session-initiate' and self.state == JingleStates.ended:
self.__send_error(stanza, 'item-not-found', 'unknown-session') self.__send_error(stanza, 'item-not-found', 'unknown-session')
return return
@ -268,16 +297,18 @@ class JingleSession(object):
except TieBreak: except TieBreak:
self.__send_error(stanza, 'conflict', 'tiebreak') self.__send_error(stanza, 'conflict', 'tiebreak')
except OutOfOrder: except OutOfOrder:
self.__send_error(stanza, 'unexpected-request', 'out-of-order')#FIXME # FIXME
self.__send_error(stanza, 'unexpected-request', 'out-of-order')
def __defaultCB(self, stanza, jingle, error, action): def __defaultCB(self, stanza, jingle, error, action):
''' Default callback for action stanzas -- simple ack """
and stop processing. ''' Default callback for action stanzas -- simple ack and stop processing
"""
response = stanza.buildReply('result') response = stanza.buildReply('result')
self.connection.connection.send(response) self.connection.connection.send(response)
def __errorCB(self, stanza, jingle, error, action): def __errorCB(self, stanza, jingle, error, action):
#FIXME # FIXME
text = error.getTagData('text') text = error.getTagData('text')
jingle_error = None jingle_error = None
xmpp_error = None xmpp_error = None
@ -287,7 +318,7 @@ class JingleSession(object):
elif child.getNamespace() == xmpp.NS_STANZAS: elif child.getNamespace() == xmpp.NS_STANZAS:
xmpp_error = child.getName() xmpp_error = child.getName()
self.__dispatch_error(xmpp_error, jingle_error, text) self.__dispatch_error(xmpp_error, jingle_error, text)
#FIXME: Not sure when we would want to do that... # FIXME: Not sure when we would want to do that...
if xmpp_error == 'item-not-found': if xmpp_error == 'item-not-found':
self.connection.delete_jingle_session(self.peerjid, self.sid) self.connection.delete_jingle_session(self.peerjid, self.sid)
@ -298,9 +329,9 @@ class JingleSession(object):
if (creator, name) in self.contents: if (creator, name) in self.contents:
transport_ns = content.getTag('transport').getNamespace() transport_ns = content.getTag('transport').getNamespace()
if transport_ns == xmpp.JINGLE_ICE_UDP: if transport_ns == xmpp.JINGLE_ICE_UDP:
#FIXME: We don't manage anything else than ICE-UDP now... # FIXME: We don't manage anything else than ICE-UDP now...
#What was the previous transport?!? # What was the previous transport?!?
#Anyway, content's transport is not modifiable yet # Anyway, content's transport is not modifiable yet
pass pass
else: else:
stanza, jingle = self.__make_jingle('transport-reject') stanza, jingle = self.__make_jingle('transport-reject')
@ -310,8 +341,8 @@ class JingleSession(object):
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
raise xmpp.NodeProcessed raise xmpp.NodeProcessed
else: else:
#FIXME: This ressource is unknown to us, what should we do? # FIXME: This ressource is unknown to us, what should we do?
#For now, reject the transport # For now, reject the transport
stanza, jingle = self.__make_jingle('transport-reject') stanza, jingle = self.__make_jingle('transport-reject')
c = jingle.setTag('content', attrs={'creator': creator, c = jingle.setTag('content', attrs={'creator': creator,
'name': name}) 'name': name})
@ -320,7 +351,7 @@ class JingleSession(object):
raise xmpp.NodeProcessed raise xmpp.NodeProcessed
def __sessionInfoCB(self, stanza, jingle, error, action): def __sessionInfoCB(self, stanza, jingle, error, action):
#TODO: ringing, active, (un)hold, (un)mute # TODO: ringing, active, (un)hold, (un)mute
payload = jingle.getPayload() payload = jingle.getPayload()
if len(payload) > 0: if len(payload) > 0:
self.__send_error(stanza, 'feature-not-implemented', 'unsupported-info') self.__send_error(stanza, 'feature-not-implemented', 'unsupported-info')
@ -332,7 +363,7 @@ class JingleSession(object):
name = content['name'] name = content['name']
if (creator, name) in self.contents: if (creator, name) in self.contents:
content = self.contents[(creator, name)] content = self.contents[(creator, name)]
#TODO: this will fail if content is not an RTP content # TODO: this will fail if content is not an RTP content
self.connection.dispatch('JINGLE_DISCONNECTED', self.connection.dispatch('JINGLE_DISCONNECTED',
(self.peerjid, self.sid, content.media, 'removed')) (self.peerjid, self.sid, content.media, 'removed'))
content.destroy() content.destroy()
@ -342,17 +373,21 @@ class JingleSession(object):
self._session_terminate(reason) self._session_terminate(reason)
def __sessionAcceptCB(self, stanza, jingle, error, action): def __sessionAcceptCB(self, stanza, jingle, error, action):
if self.state != JingleStates.pending: #FIXME # FIXME
if self.state != JingleStates.pending:
raise OutOfOrder raise OutOfOrder
self.state = JingleStates.active self.state = JingleStates.active
def __contentAcceptCB(self, stanza, jingle, error, action): def __contentAcceptCB(self, stanza, jingle, error, action):
''' Called when we get content-accept stanza or equivalent one """
(like session-accept).''' Called when we get content-accept stanza or equivalent one (like
session-accept)
"""
# check which contents are accepted # check which contents are accepted
for content in jingle.iterTags('content'): for content in jingle.iterTags('content'):
creator = content['creator'] creator = content['creator']
name = content['name']#TODO... # TODO
name = content['name']
def __contentAddCB(self, stanza, jingle, error, action): def __contentAddCB(self, stanza, jingle, error, action):
if self.state == JingleStates.ended: if self.state == JingleStates.ended:
@ -363,7 +398,7 @@ class JingleSession(object):
rejected_contents = parse_result[3] rejected_contents = parse_result[3]
for name, creator in rejected_contents: for name, creator in rejected_contents:
#TODO: # TODO
content = JingleContent() content = JingleContent()
self.add_content(name, content, creator) self.add_content(name, content, creator)
self.__content_reject(content) self.__content_reject(content)
@ -373,10 +408,10 @@ class JingleSession(object):
contents)) contents))
def __sessionInitiateCB(self, stanza, jingle, error, action): def __sessionInitiateCB(self, stanza, jingle, error, action):
''' We got a jingle session request from other entity, """
therefore we are the receiver... Unpack the data, We got a jingle session request from other entity, therefore we are the
inform the user. ''' receiver... Unpack the data, inform the user
"""
if self.state != JingleStates.ended: if self.state != JingleStates.ended:
raise OutOfOrder raise OutOfOrder
@ -417,7 +452,9 @@ class JingleSession(object):
contents)) contents))
def __broadcastCB(self, stanza, jingle, error, action): def __broadcastCB(self, stanza, jingle, error, action):
''' Broadcast the stanza contents to proper content handlers. ''' """
Broadcast the stanza contents to proper content handlers
"""
for content in jingle.iterTags('content'): for content in jingle.iterTags('content'):
name = content['name'] name = content['name']
creator = content['creator'] creator = content['creator']
@ -437,11 +474,12 @@ class JingleSession(object):
(self.peerjid, self.sid, None, text)) (self.peerjid, self.sid, None, text))
def __broadcastAllCB(self, stanza, jingle, error, action): def __broadcastAllCB(self, stanza, jingle, error, action):
''' Broadcast the stanza to all content handlers. ''' """
Broadcast the stanza to all content handlers
"""
for content in self.contents.itervalues(): for content in self.contents.itervalues():
content.stanzaCB(stanza, None, error, action) content.stanzaCB(stanza, None, error, action)
''' Internal methods. '''
def __parse_contents(self, jingle): def __parse_contents(self, jingle):
#TODO: Needs some reworking #TODO: Needs some reworking
contents = [] contents = []
@ -492,8 +530,6 @@ class JingleSession(object):
break break
return (reason, text) return (reason, text)
''' Methods that make/send proper pieces of XML. They check if the session
is in appropriate state. '''
def __make_jingle(self, action): def __make_jingle(self, action):
stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.peerjid)) stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.peerjid))
attrs = {'action': action, attrs = {'action': action,
@ -516,14 +552,17 @@ class JingleSession(object):
self.__dispatch_error(error, jingle_error, text) self.__dispatch_error(error, jingle_error, text)
def __append_content(self, jingle, content): def __append_content(self, jingle, content):
''' Append <content/> element to <jingle/> element, """
with (full=True) or without (full=False) <content/> Append <content/> element to <jingle/> element, with (full=True) or
children. ''' without (full=False) <content/> children
"""
jingle.addChild('content', jingle.addChild('content',
attrs={'name': content.name, 'creator': content.creator}) attrs={'name': content.name, 'creator': content.creator})
def __append_contents(self, jingle): def __append_contents(self, jingle):
''' Append all <content/> elements to <jingle/>.''' """
Append all <content/> elements to <jingle/>
"""
# TODO: integrate with __appendContent? # TODO: integrate with __appendContent?
# TODO: parameters 'name', 'content'? # TODO: parameters 'name', 'content'?
for content in self.contents.values(): for content in self.contents.values():
@ -571,7 +610,7 @@ class JingleSession(object):
(self.peerjid, self.sid, None, text)) (self.peerjid, self.sid, None, text))
def __content_add(self, content): def __content_add(self, content):
#TODO: test # TODO: test
assert self.state != JingleStates.ended assert self.state != JingleStates.ended
stanza, jingle = self.__make_jingle('content-add') stanza, jingle = self.__make_jingle('content-add')
self.__append_content(jingle, content) self.__append_content(jingle, content)
@ -579,7 +618,7 @@ class JingleSession(object):
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
def __content_accept(self, content): def __content_accept(self, content):
#TODO: test # TODO: test
assert self.state != JingleStates.ended assert self.state != JingleStates.ended
stanza, jingle = self.__make_jingle('content-accept') stanza, jingle = self.__make_jingle('content-accept')
self.__append_content(jingle, content) self.__append_content(jingle, content)
@ -591,7 +630,7 @@ class JingleSession(object):
stanza, jingle = self.__make_jingle('content-reject') stanza, jingle = self.__make_jingle('content-reject')
self.__append_content(jingle, content) self.__append_content(jingle, content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
#TODO: this will fail if content is not an RTP content # TODO: this will fail if content is not an RTP content
self.connection.dispatch('JINGLE_DISCONNECTED', self.connection.dispatch('JINGLE_DISCONNECTED',
(self.peerjid, self.sid, content.media, 'rejected')) (self.peerjid, self.sid, content.media, 'rejected'))
@ -603,7 +642,7 @@ class JingleSession(object):
stanza, jingle = self.__make_jingle('content-remove') stanza, jingle = self.__make_jingle('content-remove')
self.__append_content(jingle, content) self.__append_content(jingle, content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
#TODO: this will fail if content is not an RTP content # TODO: this will fail if content is not an RTP content
self.connection.dispatch('JINGLE_DISCONNECTED', self.connection.dispatch('JINGLE_DISCONNECTED',
(self.peerjid, self.sid, content.media, 'removed')) (self.peerjid, self.sid, content.media, 'removed'))

View File

@ -10,7 +10,10 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
## ##
''' Handles Jingle Transports (currently only ICE-UDP). '''
"""
Handles Jingle Transports (currently only ICE-UDP)
"""
import xmpp import xmpp
@ -25,13 +28,18 @@ def get_jingle_transport(node):
class TransportType(object): class TransportType(object):
''' Possible types of a JingleTransport ''' """
Possible types of a JingleTransport
"""
datagram = 1 datagram = 1
streaming = 2 streaming = 2
class JingleTransport(object): class JingleTransport(object):
''' An abstraction of a transport in Jingle sessions. ''' """
An abstraction of a transport in Jingle sessions
"""
def __init__(self, type_): def __init__(self, type_):
self.type = type_ self.type = type_
self.candidates = [] self.candidates = []
@ -42,19 +50,25 @@ class JingleTransport(object):
yield self.make_candidate(candidate) yield self.make_candidate(candidate)
def make_candidate(self, candidate): def make_candidate(self, candidate):
''' Build a candidate stanza for the given candidate. ''' """
Build a candidate stanza for the given candidate
"""
pass pass
def make_transport(self, candidates=None): def make_transport(self, candidates=None):
''' Build a transport stanza with the given candidates (or """
self.candidates if candidates is None). ''' Build a transport stanza with the given candidates (or self.candidates if
candidates is None)
"""
if not candidates: if not candidates:
candidates = self._iter_candidates() candidates = self._iter_candidates()
transport = xmpp.Node('transport', payload=candidates) transport = xmpp.Node('transport', payload=candidates)
return transport return transport
def parse_transport_stanza(self, transport): def parse_transport_stanza(self, transport):
''' Returns the list of transport candidates from a transport stanza. ''' """
Return the list of transport candidates from a transport stanza
"""
return [] return []
@ -132,4 +146,3 @@ class JingleTransportICEUDP(JingleTransport):
return candidates return candidates
transports[xmpp.NS_JINGLE_ICE_UDP] = JingleTransportICEUDP transports[xmpp.NS_JINGLE_ICE_UDP] = JingleTransportICEUDP

View File

@ -25,7 +25,9 @@ import subprocess
def kwallet_available(): def kwallet_available():
"""Return True if kwalletcli can be run, False otherwise.""" """
Return True if kwalletcli can be run, False otherwise
"""
try: try:
p = subprocess.Popen(["kwalletcli", "-qV"]) p = subprocess.Popen(["kwalletcli", "-qV"])
except Exception: except Exception:
@ -37,7 +39,8 @@ def kwallet_available():
def kwallet_get(folder, entry): def kwallet_get(folder, entry):
"""Retrieve a passphrase from the KDE Wallet via kwalletcli. """
Retrieve a passphrase from the KDE Wallet via kwalletcli
Arguments: Arguments:
folder: The top-level category to use (normally the programme name) folder: The top-level category to use (normally the programme name)
@ -45,7 +48,6 @@ def kwallet_get(folder, entry):
Returns the passphrase as unicode, False if it cannot be found, Returns the passphrase as unicode, False if it cannot be found,
or None if an error occured. or None if an error occured.
""" """
p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'), p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'),
"-e", entry.encode('utf-8')], stdout=subprocess.PIPE) "-e", entry.encode('utf-8')], stdout=subprocess.PIPE)
@ -60,7 +62,8 @@ def kwallet_get(folder, entry):
def kwallet_put(folder, entry, passphrase): def kwallet_put(folder, entry, passphrase):
"""Store a passphrase into the KDE Wallet via kwalletcli. """
Store a passphrase into the KDE Wallet via kwalletcli
Arguments: Arguments:
folder: The top-level category to use (normally the programme name) folder: The top-level category to use (normally the programme name)
@ -68,7 +71,6 @@ def kwallet_put(folder, entry, passphrase):
passphrase: The value to store passphrase: The value to store
Returns True on success, False otherwise. Returns True on success, False otherwise.
""" """
p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'), p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'),
"-e", entry.encode('utf-8'), "-P"], stdin=subprocess.PIPE) "-e", entry.encode('utf-8'), "-P"], stdin=subprocess.PIPE)

View File

@ -86,8 +86,9 @@ def popen_nt_friendly(command):
return Popen(command, cwd=gettempdir(), stdout=PIPE) return Popen(command, cwd=gettempdir(), stdout=PIPE)
def check_for_latex_support(): def check_for_latex_support():
'''check is latex is available and if it can create a picture.''' """
Check if latex is available and if it can create a picture
"""
try: try:
filename = latex_to_image("test") filename = latex_to_image("test")
if filename: if filename:

View File

@ -24,7 +24,9 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
''' This module allows to access the on-disk database of logs. ''' """
This module allows to access the on-disk database of logs
"""
import os import os
import sys import sys
@ -150,7 +152,9 @@ class Logger:
self.get_jids_already_in_db() self.get_jids_already_in_db()
def simple_commit(self, sql_to_commit): def simple_commit(self, sql_to_commit):
'''helper to commit''' """
Helper to commit
"""
self.cur.execute(sql_to_commit) self.cur.execute(sql_to_commit)
try: try:
self.con.commit() self.con.commit()
@ -177,14 +181,14 @@ class Logger:
return self.jids_already_in return self.jids_already_in
def jid_is_from_pm(self, jid): def jid_is_from_pm(self, jid):
'''if jid is gajim@conf/nkour it's likely a pm one, how we know """
gajim@conf is not a normal guy and nkour is not his resource? If jid is gajim@conf/nkour it's likely a pm one, how we know gajim@conf
we ask if gajim@conf is already in jids (with type room jid) is not a normal guy and nkour is not his resource? we ask if gajim@conf
this fails if user disables logging for room and only enables for is already in jids (with type room jid) this fails if user disables
pm (so higly unlikely) and if we fail we do not go chaos logging for room and only enables for pm (so higly unlikely) and if we
(user will see the first pm as if it was message in room's public chat) fail we do not go chaos (user will see the first pm as if it was message
and after that all okay''' in room's public chat) and after that all okay
"""
if jid.find('/') > -1: if jid.find('/') > -1:
possible_room_jid = jid.split('/', 1)[0] possible_room_jid = jid.split('/', 1)[0]
return self.jid_is_room_jid(possible_room_jid) return self.jid_is_room_jid(possible_room_jid)
@ -202,13 +206,14 @@ class Logger:
return True return True
def get_jid_id(self, jid, typestr=None): def get_jid_id(self, jid, typestr=None):
'''jids table has jid and jid_id """
logs table has log_id, jid_id, contact_name, time, kind, show, message jids table has jid and jid_id logs table has log_id, jid_id,
so to ask logs we need jid_id that matches our jid in jids table contact_name, time, kind, show, message so to ask logs we need jid_id
this method wants jid and returns the jid_id for later sql-ing on logs that matches our jid in jids table this method wants jid and returns the
typestr can be 'ROOM' or anything else depending on the type of JID jid_id for later sql-ing on logs typestr can be 'ROOM' or anything else
and is only needed to be specified when the JID is new in DB depending on the type of JID and is only needed to be specified when the
''' JID is new in DB
"""
if jid.find('/') != -1: # if it has a / if jid.find('/') != -1: # if it has a /
jid_is_from_pm = self.jid_is_from_pm(jid) jid_is_from_pm = self.jid_is_from_pm(jid)
if not jid_is_from_pm: # it's normal jid with resource if not jid_is_from_pm: # it's normal jid with resource
@ -238,7 +243,9 @@ class Logger:
return jid_id return jid_id
def convert_human_values_to_db_api_values(self, kind, show): def convert_human_values_to_db_api_values(self, kind, show):
'''coverts from string style to constant ints for db''' """
Convert from string style to constant ints for db
"""
if kind == 'status': if kind == 'status':
kind_col = constants.KIND_STATUS kind_col = constants.KIND_STATUS
elif kind == 'gcstatus': elif kind == 'gcstatus':
@ -277,7 +284,9 @@ class Logger:
return kind_col, show_col return kind_col, show_col
def convert_human_transport_type_to_db_api_values(self, type_): def convert_human_transport_type_to_db_api_values(self, type_):
'''converts from string style to constant ints for db''' """
Convert from string style to constant ints for db
"""
if type_ == 'aim': if type_ == 'aim':
return constants.TYPE_AIM return constants.TYPE_AIM
if type_ == 'gadu-gadu': if type_ == 'gadu-gadu':
@ -309,7 +318,9 @@ class Logger:
return None return None
def convert_api_values_to_human_transport_type(self, type_id): def convert_api_values_to_human_transport_type(self, type_id):
'''converts from constant ints for db to string style''' """
Convert from constant ints for db to string style
"""
if type_id == constants.TYPE_AIM: if type_id == constants.TYPE_AIM:
return 'aim' return 'aim'
if type_id == constants.TYPE_GG: if type_id == constants.TYPE_GG:
@ -340,7 +351,9 @@ class Logger:
return 'mrim' return 'mrim'
def convert_human_subscription_values_to_db_api_values(self, sub): def convert_human_subscription_values_to_db_api_values(self, sub):
'''converts from string style to constant ints for db''' """
Convert from string style to constant ints for db
"""
if sub == 'none': if sub == 'none':
return constants.SUBSCRIPTION_NONE return constants.SUBSCRIPTION_NONE
if sub == 'to': if sub == 'to':
@ -351,7 +364,9 @@ class Logger:
return constants.SUBSCRIPTION_BOTH return constants.SUBSCRIPTION_BOTH
def convert_db_api_values_to_human_subscription_values(self, sub): def convert_db_api_values_to_human_subscription_values(self, sub):
'''converts from constant ints for db to string style''' """
Convert from constant ints for db to string style
"""
if sub == constants.SUBSCRIPTION_NONE: if sub == constants.SUBSCRIPTION_NONE:
return 'none' return 'none'
if sub == constants.SUBSCRIPTION_TO: if sub == constants.SUBSCRIPTION_TO:
@ -382,30 +397,40 @@ class Logger:
return message_id return message_id
def insert_unread_events(self, message_id, jid_id): def insert_unread_events(self, message_id, jid_id):
''' add unread message with id: message_id''' """
Add unread message with id: message_id
"""
sql = 'INSERT INTO unread_messages VALUES (%d, %d, 0)' % (message_id, sql = 'INSERT INTO unread_messages VALUES (%d, %d, 0)' % (message_id,
jid_id) jid_id)
self.simple_commit(sql) self.simple_commit(sql)
def set_read_messages(self, message_ids): def set_read_messages(self, message_ids):
''' mark all messages with ids in message_ids as read''' """
Mark all messages with ids in message_ids as read
"""
ids = ','.join([str(i) for i in message_ids]) ids = ','.join([str(i) for i in message_ids])
sql = 'DELETE FROM unread_messages WHERE message_id IN (%s)' % ids sql = 'DELETE FROM unread_messages WHERE message_id IN (%s)' % ids
self.simple_commit(sql) self.simple_commit(sql)
def set_shown_unread_msgs(self, msg_id): def set_shown_unread_msgs(self, msg_id):
''' mark unread message as shown un GUI ''' """
Mark unread message as shown un GUI
"""
sql = 'UPDATE unread_messages SET shown = 1 where message_id = %s' % \ sql = 'UPDATE unread_messages SET shown = 1 where message_id = %s' % \
msg_id msg_id
self.simple_commit(sql) self.simple_commit(sql)
def reset_shown_unread_messages(self): def reset_shown_unread_messages(self):
''' Set shown field to False in unread_messages table ''' """
Set shown field to False in unread_messages table
"""
sql = 'UPDATE unread_messages SET shown = 0' sql = 'UPDATE unread_messages SET shown = 0'
self.simple_commit(sql) self.simple_commit(sql)
def get_unread_msgs(self): def get_unread_msgs(self):
''' get all unread messages ''' """
Get all unread messages
"""
all_messages = [] all_messages = []
try: try:
self.cur.execute( self.cur.execute(
@ -435,15 +460,18 @@ class Logger:
return all_messages return all_messages
def write(self, kind, jid, message=None, show=None, tim=None, subject=None): def write(self, kind, jid, message=None, show=None, tim=None, subject=None):
'''write a row (status, gcstatus, message etc) to logs database """
kind can be status, gcstatus, gc_msg, (we only recv for those 3), Write a row (status, gcstatus, message etc) to logs database
single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent
we cannot know if it is pm or normal chat message, we try to guess
see jid_is_from_pm()
we analyze jid and store it as follows: kind can be status, gcstatus, gc_msg, (we only recv for those 3),
jids.jid text column will hold JID if TC-related, room_jid if GC-related, single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent we cannot
ROOM_JID/nick if pm-related.''' know if it is pm or normal chat message, we try to guess see
jid_is_from_pm()
We analyze jid and store it as follows:
jids.jid text column will hold JID if TC-related, room_jid if GC-related,
ROOM_JID/nick if pm-related.
"""
if self.jids_already_in == []: # only happens if we just created the db if self.jids_already_in == []: # only happens if we just created the db
self.open_db() self.open_db()
@ -516,12 +544,14 @@ class Logger:
return self.commit_to_db(values, write_unread) return self.commit_to_db(values, write_unread)
def get_last_conversation_lines(self, jid, restore_how_many_rows, def get_last_conversation_lines(self, jid, restore_how_many_rows,
pending_how_many, timeout, account): pending_how_many, timeout, account):
'''accepts how many rows to restore and when to time them out (in minutes) """
(mark them as too old) and number of messages that are in queue Accept how many rows to restore and when to time them out (in minutes)
and are already logged but pending to be viewed, (mark them as too old) and number of messages that are in queue and are
returns a list of tupples containg time, kind, message, already logged but pending to be viewed, returns a list of tupples
list with empty tupple if nothing found to meet our demands''' containg time, kind, message, list with empty tupple if nothing found to
meet our demands
"""
try: try:
self.get_jid_id(jid) self.get_jid_id(jid)
except exceptions.PysqliteOperationalError, e: except exceptions.PysqliteOperationalError, e:
@ -561,9 +591,12 @@ class Logger:
return start_of_day return start_of_day
def get_conversation_for_date(self, jid, year, month, day, account): def get_conversation_for_date(self, jid, year, month, day, account):
'''returns contact_name, time, kind, show, message, subject """
for each row in a list of tupples, Return contact_name, time, kind, show, message, subject
returns list with empty tupple if we found nothing to meet our demands'''
For each row in a list of tupples, returns list with empty tupple if we
found nothing to meet our demands
"""
try: try:
self.get_jid_id(jid) self.get_jid_id(jid)
except exceptions.PysqliteOperationalError, e: except exceptions.PysqliteOperationalError, e:
@ -586,9 +619,12 @@ class Logger:
return results return results
def get_search_results_for_query(self, jid, query, account): def get_search_results_for_query(self, jid, query, account):
'''returns contact_name, time, kind, show, message """
for each row in a list of tupples, Returns contact_name, time, kind, show, message
returns list with empty tupple if we found nothing to meet our demands'''
For each row in a list of tupples, returns list with empty tupple if we
found nothing to meet our demands
"""
try: try:
self.get_jid_id(jid) self.get_jid_id(jid)
except exceptions.PysqliteOperationalError, e: except exceptions.PysqliteOperationalError, e:
@ -615,7 +651,9 @@ class Logger:
return results return results
def get_days_with_logs(self, jid, year, month, max_day, account): def get_days_with_logs(self, jid, year, month, max_day, account):
'''returns the list of days that have logs (not status messages)''' """
Return the list of days that have logs (not status messages)
"""
try: try:
self.get_jid_id(jid) self.get_jid_id(jid)
except exceptions.PysqliteOperationalError, e: except exceptions.PysqliteOperationalError, e:
@ -650,8 +688,10 @@ class Logger:
return days_with_logs return days_with_logs
def get_last_date_that_has_logs(self, jid, account=None, is_room=False): def get_last_date_that_has_logs(self, jid, account=None, is_room=False):
'''returns last time (in seconds since EPOCH) for which """
we had logs (excluding statuses)''' Return last time (in seconds since EPOCH) for which we had logs
(excluding statuses)
"""
where_sql = '' where_sql = ''
if not is_room: if not is_room:
where_sql = self._build_contact_where(account, jid) where_sql = self._build_contact_where(account, jid)
@ -676,8 +716,10 @@ class Logger:
return result return result
def get_room_last_message_time(self, jid): def get_room_last_message_time(self, jid):
'''returns FASTLY last time (in seconds since EPOCH) for which """
we had logs for that room from rooms_last_message_time table''' Return FASTLY last time (in seconds since EPOCH) for which we had logs
for that room from rooms_last_message_time table
"""
try: try:
jid_id = self.get_jid_id(jid, 'ROOM') jid_id = self.get_jid_id(jid, 'ROOM')
except exceptions.PysqliteOperationalError, e: except exceptions.PysqliteOperationalError, e:
@ -697,8 +739,10 @@ class Logger:
return result return result
def set_room_last_message_time(self, jid, time): def set_room_last_message_time(self, jid, time):
'''set last time (in seconds since EPOCH) for which """
we had logs for that room in rooms_last_message_time table''' Set last time (in seconds since EPOCH) for which we had logs for that
room in rooms_last_message_time table
"""
jid_id = self.get_jid_id(jid, 'ROOM') jid_id = self.get_jid_id(jid, 'ROOM')
# jid_id is unique in this table, create or update : # jid_id is unique in this table, create or update :
sql = 'REPLACE INTO rooms_last_message_time VALUES (%d, %d)' % \ sql = 'REPLACE INTO rooms_last_message_time VALUES (%d, %d)' % \
@ -706,8 +750,9 @@ class Logger:
self.simple_commit(sql) self.simple_commit(sql)
def _build_contact_where(self, account, jid): def _build_contact_where(self, account, jid):
'''build the where clause for a jid, including metacontacts """
jid(s) if any''' Build the where clause for a jid, including metacontacts jid(s) if any
"""
where_sql = '' where_sql = ''
# will return empty list if jid is not associated with # will return empty list if jid is not associated with
# any metacontacts # any metacontacts
@ -727,7 +772,9 @@ class Logger:
return where_sql return where_sql
def save_transport_type(self, jid, type_): def save_transport_type(self, jid, type_):
'''save the type of the transport in DB''' """
Save the type of the transport in DB
"""
type_id = self.convert_human_transport_type_to_db_api_values(type_) type_id = self.convert_human_transport_type_to_db_api_values(type_)
if not type_id: if not type_id:
# unknown type # unknown type
@ -747,7 +794,9 @@ class Logger:
self.simple_commit(sql) self.simple_commit(sql)
def get_transports_type(self): def get_transports_type(self):
'''return all the type of the transports in DB''' """
Return all the type of the transports in DB
"""
self.cur.execute( self.cur.execute(
'SELECT * from transports_cache') 'SELECT * from transports_cache')
results = self.cur.fetchall() results = self.cur.fetchall()
@ -767,11 +816,13 @@ class Logger:
# (2) # (2)
# GzipFile needs a file-like object, StringIO emulates file for plain strings # GzipFile needs a file-like object, StringIO emulates file for plain strings
def iter_caps_data(self): def iter_caps_data(self):
''' Iterate over caps cache data stored in the database. """
Iterate over caps cache data stored in the database
The iterator values are pairs of (node, ver, ext, identities, features): The iterator values are pairs of (node, ver, ext, identities, features):
identities == {'category':'foo', 'type':'bar', 'name':'boo'}, identities == {'category':'foo', 'type':'bar', 'name':'boo'},
features being a list of feature namespaces. ''' features being a list of feature namespaces.
"""
# get data from table # get data from table
# the data field contains binary object (gzipped data), this is a hack # the data field contains binary object (gzipped data), this is a hack
# to get that data without trying to convert it to unicode # to get that data without trying to convert it to unicode
@ -854,16 +905,21 @@ class Logger:
self.simple_commit(sql) self.simple_commit(sql)
def clean_caps_table(self): def clean_caps_table(self):
'''Remove caps which was not seen for 3 months''' """
Remove caps which was not seen for 3 months
"""
sql = '''DELETE FROM caps_cache WHERE last_seen < %d''' % \ sql = '''DELETE FROM caps_cache WHERE last_seen < %d''' % \
int(time.time() - 3*30*24*3600) int(time.time() - 3*30*24*3600)
self.simple_commit(sql) self.simple_commit(sql)
def replace_roster(self, account_name, roster_version, roster): def replace_roster(self, account_name, roster_version, roster):
''' Replace current roster in DB by a new one. """
accout_name is the name of the account to change Replace current roster in DB by a new one
roster_version is the version of the new roster
roster is the new version ''' accout_name is the name of the account to change.
roster_version is the version of the new roster.
roster is the new version.
"""
# First we must reset roster_version value to ensure that the server # First we must reset roster_version value to ensure that the server
# sends back all the roster at the next connexion if the replacement # sends back all the roster at the next connexion if the replacement
# didn't work properly. # didn't work properly.
@ -887,7 +943,9 @@ class Logger:
roster_version) roster_version)
def del_contact(self, account_jid, jid): def del_contact(self, account_jid, jid):
''' Remove jid from account_jid roster. ''' """
Remove jid from account_jid roster
"""
try: try:
account_jid_id = self.get_jid_id(account_jid) account_jid_id = self.get_jid_id(account_jid)
jid_id = self.get_jid_id(jid) jid_id = self.get_jid_id(jid)
@ -902,7 +960,9 @@ class Logger:
self.con.commit() self.con.commit()
def add_or_update_contact(self, account_jid, jid, name, sub, ask, groups): def add_or_update_contact(self, account_jid, jid, name, sub, ask, groups):
''' Add or update a contact from account_jid roster. ''' """
Add or update a contact from account_jid roster
"""
if sub == 'remove': if sub == 'remove':
self.del_contact(account_jid, jid) self.del_contact(account_jid, jid)
return return
@ -933,7 +993,9 @@ class Logger:
self.con.commit() self.con.commit()
def get_roster(self, account_jid): def get_roster(self, account_jid):
''' Return the accound_jid roster in NonBlockingRoster format. ''' """
Return the accound_jid roster in NonBlockingRoster format
"""
data = {} data = {}
account_jid_id = self.get_jid_id(account_jid) account_jid_id = self.get_jid_id(account_jid)
@ -972,7 +1034,9 @@ class Logger:
return data return data
def remove_roster(self, account_jid): def remove_roster(self, account_jid):
''' Remove all entry from account_jid roster. ''' """
Remove all entry from account_jid roster
"""
account_jid_id = self.get_jid_id(account_jid) account_jid_id = self.get_jid_id(account_jid)
self.cur.execute('DELETE FROM roster_entry WHERE account_jid_id=?', self.cur.execute('DELETE FROM roster_entry WHERE account_jid_id=?',

View File

@ -23,7 +23,7 @@ import i18n
def parseLogLevel(arg): def parseLogLevel(arg):
""" """
eiter numeric value or level name from logging module Eiter numeric value or level name from logging module
""" """
if arg.isdigit(): if arg.isdigit():
return int(arg) return int(arg)
@ -98,7 +98,7 @@ def colorize(text, color):
class FancyFormatter(logging.Formatter): class FancyFormatter(logging.Formatter):
""" """
A Eye-candy formatter with colors An eye-candy formatter with colors
""" """
colors_mapping = { colors_mapping = {
'DEBUG': colors.BLUE, 'DEBUG': colors.BLUE,
@ -134,7 +134,7 @@ class FancyFormatter(logging.Formatter):
def init(use_color=False): def init(use_color=False):
""" """
initialize the logging system Iinitialize the logging system
""" """
consoleloghandler = logging.StreamHandler() consoleloghandler = logging.StreamHandler()
consoleloghandler.setFormatter( consoleloghandler.setFormatter(

View File

@ -228,7 +228,9 @@ class OptionsParser:
caps.capscache.initialize_from_db() caps.capscache.initialize_from_db()
def assert_unread_msgs_table_exists(self): def assert_unread_msgs_table_exists(self):
'''create table unread_messages if there is no such table''' """
Create table unread_messages if there is no such table
"""
back = os.getcwd() back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER) os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE) con = sqlite.connect(logger.LOG_DB_FILE)
@ -346,8 +348,10 @@ class OptionsParser:
gajim.config.set('version', '0.10.1.2') gajim.config.set('version', '0.10.1.2')
def update_config_to_01013(self): def update_config_to_01013(self):
'''create table transports_cache if there is no such table''' """
#FIXME see #2812 Create table transports_cache if there is no such table
"""
# FIXME see #2812
back = os.getcwd() back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER) os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE) con = sqlite.connect(logger.LOG_DB_FILE)
@ -369,9 +373,11 @@ class OptionsParser:
gajim.config.set('version', '0.10.1.3') gajim.config.set('version', '0.10.1.3')
def update_config_to_01014(self): def update_config_to_01014(self):
'''apply indeces to the logs database''' """
Apply indeces to the logs database
"""
print _('migrating logs database to indices') print _('migrating logs database to indices')
#FIXME see #2812 # FIXME see #2812
back = os.getcwd() back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER) os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE) con = sqlite.connect(logger.LOG_DB_FILE)
@ -393,7 +399,9 @@ class OptionsParser:
gajim.config.set('version', '0.10.1.4') gajim.config.set('version', '0.10.1.4')
def update_config_to_01015(self): def update_config_to_01015(self):
'''clean show values in logs database''' """
Clean show values in logs database
"""
#FIXME see #2812 #FIXME see #2812
back = os.getcwd() back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER) os.chdir(logger.LOG_DB_FOLDER)
@ -412,8 +420,10 @@ class OptionsParser:
gajim.config.set('version', '0.10.1.5') gajim.config.set('version', '0.10.1.5')
def update_config_to_01016(self): def update_config_to_01016(self):
'''#2494 : Now we play gc_received_message sound even if """
notify_on_all_muc_messages is false. Keep precedent behaviour.''' #2494 : Now we play gc_received_message sound even if
notify_on_all_muc_messages is false. Keep precedent behaviour
"""
if 'notify_on_all_muc_messages' in self.old_values and \ if 'notify_on_all_muc_messages' in self.old_values and \
self.old_values['notify_on_all_muc_messages'] == 'False' and \ self.old_values['notify_on_all_muc_messages'] == 'False' and \
gajim.config.get_per('soundevents', 'muc_message_received', 'enabled'): gajim.config.get_per('soundevents', 'muc_message_received', 'enabled'):
@ -422,36 +432,45 @@ class OptionsParser:
gajim.config.set('version', '0.10.1.6') gajim.config.set('version', '0.10.1.6')
def update_config_to_01017(self): def update_config_to_01017(self):
'''trayicon_notification_on_new_messages -> """
trayicon_notification_on_events ''' trayicon_notification_on_new_messages -> trayicon_notification_on_events
"""
if 'trayicon_notification_on_new_messages' in self.old_values: if 'trayicon_notification_on_new_messages' in self.old_values:
gajim.config.set('trayicon_notification_on_events', gajim.config.set('trayicon_notification_on_events',
self.old_values['trayicon_notification_on_new_messages']) self.old_values['trayicon_notification_on_new_messages'])
gajim.config.set('version', '0.10.1.7') gajim.config.set('version', '0.10.1.7')
def update_config_to_01018(self): def update_config_to_01018(self):
'''chat_state_notifications -> outgoing_chat_state_notifications''' """
chat_state_notifications -> outgoing_chat_state_notifications
"""
if 'chat_state_notifications' in self.old_values: if 'chat_state_notifications' in self.old_values:
gajim.config.set('outgoing_chat_state_notifications', gajim.config.set('outgoing_chat_state_notifications',
self.old_values['chat_state_notifications']) self.old_values['chat_state_notifications'])
gajim.config.set('version', '0.10.1.8') gajim.config.set('version', '0.10.1.8')
def update_config_to_01101(self): def update_config_to_01101(self):
'''fill time_stamp from before_time and after_time''' """
Fill time_stamp from before_time and after_time
"""
if 'before_time' in self.old_values: if 'before_time' in self.old_values:
gajim.config.set('time_stamp', '%s%%X%s ' % ( gajim.config.set('time_stamp', '%s%%X%s ' % (
self.old_values['before_time'], self.old_values['after_time'])) self.old_values['before_time'], self.old_values['after_time']))
gajim.config.set('version', '0.11.0.1') gajim.config.set('version', '0.11.0.1')
def update_config_to_01102(self): def update_config_to_01102(self):
'''fill time_stamp from before_time and after_time''' """
Fill time_stamp from before_time and after_time
"""
if 'ft_override_host_to_send' in self.old_values: if 'ft_override_host_to_send' in self.old_values:
gajim.config.set('ft_add_hosts_to_send', gajim.config.set('ft_add_hosts_to_send',
self.old_values['ft_override_host_to_send']) self.old_values['ft_override_host_to_send'])
gajim.config.set('version', '0.11.0.2') gajim.config.set('version', '0.11.0.2')
def update_config_to_01111(self): def update_config_to_01111(self):
'''always_hide_chatbuttons -> compact_view''' """
Always_hide_chatbuttons -> compact_view
"""
if 'always_hide_groupchat_buttons' in self.old_values and \ if 'always_hide_groupchat_buttons' in self.old_values and \
'always_hide_chat_buttons' in self.old_values: 'always_hide_chat_buttons' in self.old_values:
gajim.config.set('compact_view', self.old_values['always_hide_groupchat_buttons'] and \ gajim.config.set('compact_view', self.old_values['always_hide_groupchat_buttons'] and \
@ -459,7 +478,9 @@ class OptionsParser:
gajim.config.set('version', '0.11.1.1') gajim.config.set('version', '0.11.1.1')
def update_config_to_01112(self): def update_config_to_01112(self):
'''gtk+ theme is renamed to default''' """
GTK+ theme is renamed to default
"""
if 'roster_theme' in self.old_values and \ if 'roster_theme' in self.old_values and \
self.old_values['roster_theme'] == 'gtk+': self.old_values['roster_theme'] == 'gtk+':
gajim.config.set('roster_theme', _('default')) gajim.config.set('roster_theme', _('default'))
@ -568,7 +589,9 @@ class OptionsParser:
gajim.config.set('version', '0.11.4.1') gajim.config.set('version', '0.11.4.1')
def update_config_to_01142(self): def update_config_to_01142(self):
'''next_message_received sound event is splittedin 2 events''' """
next_message_received sound event is splittedin 2 events
"""
gajim.config.add_per('soundevents', 'next_message_received_focused') gajim.config.add_per('soundevents', 'next_message_received_focused')
gajim.config.add_per('soundevents', 'next_message_received_unfocused') gajim.config.add_per('soundevents', 'next_message_received_unfocused')
if gajim.config.get_per('soundevents', 'next_message_received'): if gajim.config.get_per('soundevents', 'next_message_received'):
@ -681,7 +704,9 @@ class OptionsParser:
gajim.config.set('version', '0.12.1.4') gajim.config.set('version', '0.12.1.4')
def update_config_to_01215(self): def update_config_to_01215(self):
'''Remove hardcoded ../data/sounds from config''' """
Remove hardcoded ../data/sounds from config
"""
dirs = ('../data', gajim.gajimpaths.root, gajim.DATA_DIR) dirs = ('../data', gajim.gajimpaths.root, gajim.DATA_DIR)
for evt in gajim.config.get_per('soundevents'): for evt in gajim.config.get_per('soundevents'):
path = gajim.config.get_per('soundevents', evt ,'path') path = gajim.config.get_per('soundevents', evt ,'path')

View File

@ -206,64 +206,64 @@ import gtkgui_helpers
class AbstractPEP(object): class AbstractPEP(object):
type = '' type = ''
namespace = '' namespace = ''
@classmethod @classmethod
def get_tag_as_PEP(cls, jid, account, event_tag): def get_tag_as_PEP(cls, jid, account, event_tag):
items = event_tag.getTag('items', {'node': cls.namespace}) items = event_tag.getTag('items', {'node': cls.namespace})
if items: if items:
log.debug("Received PEP 'user %s' from %s" % (cls.type, jid)) log.debug("Received PEP 'user %s' from %s" % (cls.type, jid))
return cls(jid, account, items) return cls(jid, account, items)
else: else:
return None return None
def __init__(self, jid, account, items): def __init__(self, jid, account, items):
self._pep_specific_data, self._retracted = self._extract_info(items) self._pep_specific_data, self._retracted = self._extract_info(items)
self._update_contacts(jid, account) self._update_contacts(jid, account)
if jid == gajim.get_jid_from_account(account): if jid == gajim.get_jid_from_account(account):
self._update_account(account) self._update_account(account)
def _extract_info(self, items): def _extract_info(self, items):
'''To be implemented by subclasses''' '''To be implemented by subclasses'''
raise NotImplementedError raise NotImplementedError
def _update_contacts(self, jid, account): def _update_contacts(self, jid, account):
for contact in gajim.contacts.get_contacts(account, jid): for contact in gajim.contacts.get_contacts(account, jid):
if self._retracted: if self._retracted:
if self.type in contact.pep: if self.type in contact.pep:
del contact.pep[self.type] del contact.pep[self.type]
else: else:
contact.pep[self.type] = self contact.pep[self.type] = self
def _update_account(self, account): def _update_account(self, account):
acc = gajim.connections[account] acc = gajim.connections[account]
if self._retracted: if self._retracted:
if self.type in acc.pep: if self.type in acc.pep:
del acc.pep[self.type] del acc.pep[self.type]
else: else:
acc.pep[self.type] = self acc.pep[self.type] = self
def asPixbufIcon(self): def asPixbufIcon(self):
'''SHOULD be implemented by subclasses''' '''SHOULD be implemented by subclasses'''
return None return None
def asMarkupText(self): def asMarkupText(self):
'''SHOULD be implemented by subclasses''' '''SHOULD be implemented by subclasses'''
return '' return ''
class UserMoodPEP(AbstractPEP): class UserMoodPEP(AbstractPEP):
'''XEP-0107: User Mood''' '''XEP-0107: User Mood'''
type = 'mood' type = 'mood'
namespace = xmpp.NS_MOOD namespace = xmpp.NS_MOOD
def _extract_info(self, items): def _extract_info(self, items):
mood_dict = {} mood_dict = {}
for item in items.getTags('item'): for item in items.getTags('item'):
mood_tag = item.getTag('mood') mood_tag = item.getTag('mood')
if mood_tag: if mood_tag:
@ -273,22 +273,22 @@ class UserMoodPEP(AbstractPEP):
mood_dict['text'] = child.getData() mood_dict['text'] = child.getData()
else: else:
mood_dict['mood'] = name mood_dict['mood'] = name
retracted = items.getTag('retract') or not 'mood' in mood_dict retracted = items.getTag('retract') or not 'mood' in mood_dict
return (mood_dict, retracted) return (mood_dict, retracted)
def asPixbufIcon(self): def asPixbufIcon(self):
assert not self._retracted assert not self._retracted
received_mood = self._pep_specific_data['mood'] received_mood = self._pep_specific_data['mood']
mood = received_mood if received_mood in MOODS else 'unknown' mood = received_mood if received_mood in MOODS else 'unknown'
pixbuf = gtkgui_helpers.load_mood_icon(mood).get_pixbuf() pixbuf = gtkgui_helpers.load_mood_icon(mood).get_pixbuf()
return pixbuf return pixbuf
def asMarkupText(self): def asMarkupText(self):
assert not self._retracted assert not self._retracted
untranslated_mood = self._pep_specific_data['mood'] untranslated_mood = self._pep_specific_data['mood']
mood = self._translate_mood(untranslated_mood) mood = self._translate_mood(untranslated_mood)
markuptext = '<b>%s</b>' % gobject.markup_escape_text(mood) markuptext = '<b>%s</b>' % gobject.markup_escape_text(mood)
if 'text' in self._pep_specific_data: if 'text' in self._pep_specific_data:
text = self._pep_specific_data['text'] text = self._pep_specific_data['text']
markuptext += ' (%s)' % gobject.markup_escape_text(text) markuptext += ' (%s)' % gobject.markup_escape_text(text)
@ -303,13 +303,13 @@ class UserMoodPEP(AbstractPEP):
class UserTunePEP(AbstractPEP): class UserTunePEP(AbstractPEP):
'''XEP-0118: User Tune''' '''XEP-0118: User Tune'''
type = 'tune' type = 'tune'
namespace = xmpp.NS_TUNE namespace = xmpp.NS_TUNE
def _extract_info(self, items): def _extract_info(self, items):
tune_dict = {} tune_dict = {}
for item in items.getTags('item'): for item in items.getTags('item'):
tune_tag = item.getTag('tune') tune_tag = item.getTag('tune')
if tune_tag: if tune_tag:
@ -318,23 +318,23 @@ class UserTunePEP(AbstractPEP):
data = child.getData().strip() data = child.getData().strip()
if child.getName() in TUNE_DATA: if child.getName() in TUNE_DATA:
tune_dict[name] = data tune_dict[name] = data
retracted = items.getTag('retract') or not ('artist' in tune_dict or retracted = items.getTag('retract') or not ('artist' in tune_dict or
'title' in tune_dict) 'title' in tune_dict)
return (tune_dict, retracted) return (tune_dict, retracted)
def asPixbufIcon(self): def asPixbufIcon(self):
import os import os
path = os.path.join(gajim.DATA_DIR, 'emoticons', 'static', 'music.png') path = os.path.join(gajim.DATA_DIR, 'emoticons', 'static', 'music.png')
return gtk.gdk.pixbuf_new_from_file(path) return gtk.gdk.pixbuf_new_from_file(path)
def asMarkupText(self): def asMarkupText(self):
assert not self._retracted assert not self._retracted
tune = self._pep_specific_data tune = self._pep_specific_data
artist = tune.get('artist', _('Unknown Artist')) artist = tune.get('artist', _('Unknown Artist'))
artist = gobject.markup_escape_text(artist) artist = gobject.markup_escape_text(artist)
title = tune.get('title', _('Unknown Title')) title = tune.get('title', _('Unknown Title'))
title = gobject.markup_escape_text(title) title = gobject.markup_escape_text(title)
@ -345,17 +345,17 @@ class UserTunePEP(AbstractPEP):
'from <i>%(source)s</i>') % {'title': title, 'from <i>%(source)s</i>') % {'title': title,
'artist': artist, 'source': source} 'artist': artist, 'source': source}
return tune_string return tune_string
class UserActivityPEP(AbstractPEP): class UserActivityPEP(AbstractPEP):
'''XEP-0108: User Activity''' '''XEP-0108: User Activity'''
type = 'activity' type = 'activity'
namespace = xmpp.NS_ACTIVITY namespace = xmpp.NS_ACTIVITY
def _extract_info(self, items): def _extract_info(self, items):
activity_dict = {} activity_dict = {}
for item in items.getTags('item'): for item in items.getTags('item'):
activity_tag = item.getTag('activity') activity_tag = item.getTag('activity')
if activity_tag: if activity_tag:
@ -369,28 +369,28 @@ class UserActivityPEP(AbstractPEP):
for subactivity in child.getChildren(): for subactivity in child.getChildren():
subactivity_name = subactivity.getName().strip() subactivity_name = subactivity.getName().strip()
activity_dict['subactivity'] = subactivity_name activity_dict['subactivity'] = subactivity_name
retracted = items.getTag('retract') or not 'activity' in activity_dict retracted = items.getTag('retract') or not 'activity' in activity_dict
return (activity_dict, retracted) return (activity_dict, retracted)
def asPixbufIcon(self): def asPixbufIcon(self):
assert not self._retracted assert not self._retracted
pep = self._pep_specific_data pep = self._pep_specific_data
activity = pep['activity'] activity = pep['activity']
has_known_activity = activity in ACTIVITIES has_known_activity = activity in ACTIVITIES
has_known_subactivity = (has_known_activity and ('subactivity' in pep) has_known_subactivity = (has_known_activity and ('subactivity' in pep)
and (pep['subactivity'] in ACTIVITIES[activity])) and (pep['subactivity'] in ACTIVITIES[activity]))
if has_known_activity: if has_known_activity:
if has_known_subactivity: if has_known_subactivity:
subactivity = pep['subactivity'] subactivity = pep['subactivity']
return gtkgui_helpers.load_activity_icon(activity, subactivity).get_pixbuf() return gtkgui_helpers.load_activity_icon(activity, subactivity).get_pixbuf()
else: else:
return gtkgui_helpers.load_activity_icon(activity).get_pixbuf() return gtkgui_helpers.load_activity_icon(activity).get_pixbuf()
else: else:
return gtkgui_helpers.load_activity_icon('unknown').get_pixbuf() return gtkgui_helpers.load_activity_icon('unknown').get_pixbuf()
def asMarkupText(self): def asMarkupText(self):
assert not self._retracted assert not self._retracted
pep = self._pep_specific_data pep = self._pep_specific_data
@ -403,45 +403,45 @@ class UserActivityPEP(AbstractPEP):
if subactivity in ACTIVITIES[activity]: if subactivity in ACTIVITIES[activity]:
subactivity = ACTIVITIES[activity][subactivity] subactivity = ACTIVITIES[activity][subactivity]
activity = ACTIVITIES[activity]['category'] activity = ACTIVITIES[activity]['category']
markuptext = '<b>' + gobject.markup_escape_text(activity) markuptext = '<b>' + gobject.markup_escape_text(activity)
if subactivity: if subactivity:
markuptext += ': ' + gobject.markup_escape_text(subactivity) markuptext += ': ' + gobject.markup_escape_text(subactivity)
markuptext += '</b>' markuptext += '</b>'
if text: if text:
markuptext += ' (%s)' % gobject.markup_escape_text(text) markuptext += ' (%s)' % gobject.markup_escape_text(text)
return markuptext return markuptext
class UserNicknamePEP(AbstractPEP): class UserNicknamePEP(AbstractPEP):
'''XEP-0172: User Nickname''' '''XEP-0172: User Nickname'''
type = 'nickname' type = 'nickname'
namespace = xmpp.NS_NICK namespace = xmpp.NS_NICK
def _extract_info(self, items): def _extract_info(self, items):
nick = '' nick = ''
for item in items.getTags('item'): for item in items.getTags('item'):
child = item.getTag('nick') child = item.getTag('nick')
if child: if child:
nick = child.getData() nick = child.getData()
break break
retracted = items.getTag('retract') or not nick retracted = items.getTag('retract') or not nick
return (nick, retracted) return (nick, retracted)
def _update_contacts(self, jid, account): def _update_contacts(self, jid, account):
nick = '' if self._retracted else self._pep_specific_data nick = '' if self._retracted else self._pep_specific_data
for contact in gajim.contacts.get_contacts(account, jid): for contact in gajim.contacts.get_contacts(account, jid):
contact.contact_name = nick contact.contact_name = nick
def _update_account(self, account): def _update_account(self, account):
if self._retracted: if self._retracted:
gajim.nicks[account] = gajim.config.get_per('accounts', account, 'name') gajim.nicks[account] = gajim.config.get_per('accounts', account, 'name')
else: else:
gajim.nicks[account] = self._pep_specific_data gajim.nicks[account] = self._pep_specific_data
SUPPORTED_PERSONAL_USER_EVENTS = [UserMoodPEP, UserTunePEP, UserActivityPEP, SUPPORTED_PERSONAL_USER_EVENTS = [UserMoodPEP, UserTunePEP, UserActivityPEP,
UserNicknamePEP] UserNicknamePEP]
@ -451,7 +451,7 @@ class ConnectionPEP(object):
self._account = account self._account = account
self._dispatcher = dispatcher self._dispatcher = dispatcher
self._pubsub_connection = pubsub_connection self._pubsub_connection = pubsub_connection
def _pubsubEventCB(self, xmpp_dispatcher, msg): def _pubsubEventCB(self, xmpp_dispatcher, msg):
''' Called when we receive <message /> with pubsub event. ''' ''' Called when we receive <message /> with pubsub event. '''
if not msg.getTag('event'): if not msg.getTag('event'):
@ -459,7 +459,7 @@ class ConnectionPEP(object):
if msg.getTag('error'): if msg.getTag('error'):
log.debug('PubsubEventCB received error stanza. Ignoring') log.debug('PubsubEventCB received error stanza. Ignoring')
raise xmpp.NodeProcessed raise xmpp.NodeProcessed
jid = helpers.get_full_jid_from_iq(msg) jid = helpers.get_full_jid_from_iq(msg)
event_tag = msg.getTag('event') event_tag = msg.getTag('event')
@ -467,7 +467,7 @@ class ConnectionPEP(object):
pep = pep_class.get_tag_as_PEP(jid, self._account, event_tag) pep = pep_class.get_tag_as_PEP(jid, self._account, event_tag)
if pep: if pep:
self._dispatcher.dispatch('PEP_RECEIVED', (jid, pep.type)) self._dispatcher.dispatch('PEP_RECEIVED', (jid, pep.type))
items = event_tag.getTag('items') items = event_tag.getTag('items')
if items: if items:
for item in items.getTags('item'): for item in items.getTags('item'):
@ -477,9 +477,9 @@ class ConnectionPEP(object):
# but to be sure... # but to be sure...
self._dispatcher.dispatch('ATOM_ENTRY', self._dispatcher.dispatch('ATOM_ENTRY',
(atom.OldEntry(node=entry),)) (atom.OldEntry(node=entry),))
raise xmpp.NodeProcessed raise xmpp.NodeProcessed
def send_activity(self, activity, subactivity=None, message=None): def send_activity(self, activity, subactivity=None, message=None):
if not self.pep_supported: if not self.pep_supported:
return return
@ -492,7 +492,7 @@ class ConnectionPEP(object):
i = item.addChild('text') i = item.addChild('text')
i.addData(message) i.addData(message)
self._pubsub_connection.send_pb_publish('', xmpp.NS_ACTIVITY, item, '0') self._pubsub_connection.send_pb_publish('', xmpp.NS_ACTIVITY, item, '0')
def retract_activity(self): def retract_activity(self):
if not self.pep_supported: if not self.pep_supported:
return return
@ -510,13 +510,13 @@ class ConnectionPEP(object):
i = item.addChild('text') i = item.addChild('text')
i.addData(message) i.addData(message)
self._pubsub_connection.send_pb_publish('', xmpp.NS_MOOD, item, '0') self._pubsub_connection.send_pb_publish('', xmpp.NS_MOOD, item, '0')
def retract_mood(self): def retract_mood(self):
if not self.pep_supported: if not self.pep_supported:
return return
self.send_mood(None) self.send_mood(None)
self._pubsub_connection.send_pb_retract('', xmpp.NS_MOOD, '0') self._pubsub_connection.send_pb_retract('', xmpp.NS_MOOD, '0')
def send_tune(self, artist='', title='', source='', track=0, length=0, def send_tune(self, artist='', title='', source='', track=0, length=0,
items=None): items=None):
if not self.pep_supported: if not self.pep_supported:

View File

@ -41,10 +41,13 @@ S_FINISHED = 4
CONNECT_TIMEOUT = 20 CONNECT_TIMEOUT = 20
class Proxy65Manager: class Proxy65Manager:
''' keep records for file transfer proxies. Each time account """
establishes a connection to its server call proxy65manger.resolve(proxy) Keep records for file transfer proxies. Each time account establishes a
for every proxy that is convigured within the account. The class takes connection to its server call proxy65manger.resolve(proxy) for every proxy
care to resolve and test each proxy only once.''' that is convigured within the account. The class takes care to resolve and
test each proxy only once
"""
def __init__(self, idlequeue): def __init__(self, idlequeue):
# dict {proxy: proxy properties} # dict {proxy: proxy properties}
self.idlequeue = idlequeue self.idlequeue = idlequeue
@ -53,7 +56,9 @@ class Proxy65Manager:
self.default_proxies = {} self.default_proxies = {}
def resolve(self, proxy, connection, sender_jid, default=None): def resolve(self, proxy, connection, sender_jid, default=None):
''' start ''' """
Start
"""
if proxy in self.proxies: if proxy in self.proxies:
resolver = self.proxies[proxy] resolver = self.proxies[proxy]
else: else:
@ -102,7 +107,9 @@ class Proxy65Manager:
class ProxyResolver: class ProxyResolver:
def resolve_result(self, host, port, jid): def resolve_result(self, host, port, jid):
''' test if host has a real proxy65 listening on port ''' """
Test if host has a real proxy65 listening on port
"""
self.host = str(host) self.host = str(host)
self.port = int(port) self.port = int(port)
self.jid = unicode(jid) self.jid = unicode(jid)
@ -175,19 +182,25 @@ class ProxyResolver:
self.try_next_connection() self.try_next_connection()
def try_next_connection(self): def try_next_connection(self):
''' try to resolve proxy with the next possible connection ''' """
Try to resolve proxy with the next possible connection
"""
if self.connections: if self.connections:
connection = self.connections.pop(0) connection = self.connections.pop(0)
self.start_resolve(connection) self.start_resolve(connection)
def add_connection(self, connection): def add_connection(self, connection):
''' add a new connection in case the first fails ''' """
Add a new connection in case the first fails
"""
self.connections.append(connection) self.connections.append(connection)
if self.state == S_INITIAL: if self.state == S_INITIAL:
self.start_resolve(connection) self.start_resolve(connection)
def start_resolve(self, connection): def start_resolve(self, connection):
''' request network address from proxy ''' """
Request network address from proxy
"""
self.state = S_STARTED self.state = S_STARTED
self.active_connection = connection self.active_connection = connection
iq = common.xmpp.Protocol(name='iq', to=self.proxy, typ='get') iq = common.xmpp.Protocol(name='iq', to=self.proxy, typ='get')
@ -209,10 +222,16 @@ class ProxyResolver:
self.sender_jid = sender_jid self.sender_jid = sender_jid
class HostTester(Socks5, IdleObject): class HostTester(Socks5, IdleObject):
''' fake proxy tester. ''' """
Fake proxy tester
"""
def __init__(self, host, port, jid, sid, sender_jid, on_success, on_failure): def __init__(self, host, port, jid, sid, sender_jid, on_success, on_failure):
''' try to establish and auth to proxy at (host, port) """
call on_success, or on_failure according to the result''' Try to establish and auth to proxy at (host, port)
Calls on_success, or on_failure according to the result.
"""
self.host = host self.host = host
self.port = port self.port = port
self.jid = jid self.jid = jid
@ -226,7 +245,9 @@ class HostTester(Socks5, IdleObject):
self.sid = sid self.sid = sid
def connect(self): def connect(self):
''' create the socket and plug it to the idlequeue ''' """
Create the socket and plug it to the idlequeue
"""
if self.host is None: if self.host is None:
self.on_failure() self.on_failure()
return None return None
@ -320,10 +341,16 @@ class HostTester(Socks5, IdleObject):
return return
class ReceiverTester(Socks5, IdleObject): class ReceiverTester(Socks5, IdleObject):
''' fake proxy tester. ''' """
Fake proxy tester
"""
def __init__(self, host, port, jid, sid, sender_jid, on_success, on_failure): def __init__(self, host, port, jid, sid, sender_jid, on_success, on_failure):
''' try to establish and auth to proxy at (host, port) """
call on_success, or on_failure according to the result''' Try to establish and auth to proxy at (host, port)
Call on_success, or on_failure according to the result.
"""
self.host = host self.host = host
self.port = port self.port = port
self.jid = jid self.jid = jid
@ -337,7 +364,9 @@ class ReceiverTester(Socks5, IdleObject):
self.sid = sid self.sid = sid
def connect(self): def connect(self):
''' create the socket and plug it to the idlequeue ''' """
Create the socket and plug it to the idlequeue
"""
if self.host is None: if self.host is None:
self.on_failure() self.on_failure()
return None return None

View File

@ -67,7 +67,9 @@ class ConnectionPubSub:
self.__callbacks[id_]=(cb, args, kwargs) self.__callbacks[id_]=(cb, args, kwargs)
def send_pb_publish(self, jid, node, item, id_, options=None): def send_pb_publish(self, jid, node, item, id_, options=None):
'''Publish item to a node.''' """
Publish item to a node
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
query = xmpp.Iq('set', to=jid) query = xmpp.Iq('set', to=jid)
@ -80,20 +82,24 @@ class ConnectionPubSub:
self.connection.send(query) self.connection.send(query)
def send_pb_retrieve(self, jid, node, cb=None, *args, **kwargs): def send_pb_retrieve(self, jid, node, cb=None, *args, **kwargs):
'''Get items from a node''' """
if not self.connection or self.connected < 2: Get items from a node
return """
query = xmpp.Iq('get', to=jid) if not self.connection or self.connected < 2:
r = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB) return
r = r.addChild('items', {'node': node}) query = xmpp.Iq('get', to=jid)
r = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
r = r.addChild('items', {'node': node})
id_ = self.connection.send(query) id_ = self.connection.send(query)
if cb: if cb:
self.__callbacks[id_]=(cb, args, kwargs) self.__callbacks[id_]=(cb, args, kwargs)
def send_pb_retract(self, jid, node, id_): def send_pb_retract(self, jid, node, id_):
'''Delete item from a node''' """
Delete item from a node
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
query = xmpp.Iq('set', to=jid) query = xmpp.Iq('set', to=jid)
@ -104,7 +110,9 @@ class ConnectionPubSub:
self.connection.send(query) self.connection.send(query)
def send_pb_delete(self, jid, node): def send_pb_delete(self, jid, node):
'''Deletes node.''' """
Delete node
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
query = xmpp.Iq('set', to=jid) query = xmpp.Iq('set', to=jid)
@ -122,7 +130,9 @@ class ConnectionPubSub:
'node': node}) 'node': node})
def send_pb_create(self, jid, node, configure = False, configure_form = None): def send_pb_create(self, jid, node, configure = False, configure_form = None):
'''Creates new node.''' """
Create a new node
"""
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
query = xmpp.Iq('set', to=jid) query = xmpp.Iq('set', to=jid)

View File

@ -88,11 +88,12 @@ class CommonResolver():
# FIXME: API usage is not consistent! This one requires that process is called # FIXME: API usage is not consistent! This one requires that process is called
class LibAsyncNSResolver(CommonResolver): class LibAsyncNSResolver(CommonResolver):
''' """
Asynchronous resolver using libasyncns-python. process() method has to be Asynchronous resolver using libasyncns-python. process() method has to be
called in order to proceed the pending requests. called in order to proceed the pending requests. Based on patch submitted by
Based on patch submitted by Damien Thebault. Damien Thebault.
''' """
def __init__(self): def __init__(self):
self.asyncns = libasyncns.Asyncns() self.asyncns = libasyncns.Asyncns()
CommonResolver.__init__(self) CommonResolver.__init__(self)
@ -146,20 +147,22 @@ class LibAsyncNSResolver(CommonResolver):
class NSLookupResolver(CommonResolver): class NSLookupResolver(CommonResolver):
''' """
Asynchronous DNS resolver calling nslookup. Processing of pending requests Asynchronous DNS resolver calling nslookup. Processing of pending requests
is invoked from idlequeue which is watching file descriptor of pipe of stdout is invoked from idlequeue which is watching file descriptor of pipe of
of nslookup process. stdout of nslookup process.
''' """
def __init__(self, idlequeue): def __init__(self, idlequeue):
self.idlequeue = idlequeue self.idlequeue = idlequeue
self.process = False self.process = False
CommonResolver.__init__(self) CommonResolver.__init__(self)
def parse_srv_result(self, fqdn, result): def parse_srv_result(self, fqdn, result):
''' parse the output of nslookup command and return list of """
properties: 'host', 'port','weight', 'priority' corresponding to the found Parse the output of nslookup command and return list of properties:
srv hosts ''' 'host', 'port','weight', 'priority' corresponding to the found srv hosts
"""
if os.name == 'nt': if os.name == 'nt':
return self._parse_srv_result_nt(fqdn, result) return self._parse_srv_result_nt(fqdn, result)
elif os.name == 'posix': elif os.name == 'posix':
@ -260,7 +263,9 @@ class NSLookupResolver(CommonResolver):
CommonResolver._on_ready(self, host, type, result_list) CommonResolver._on_ready(self, host, type, result_list)
def start_resolve(self, host, type): def start_resolve(self, host, type):
''' spawn new nslookup process and start waiting for results ''' """
Spawn new nslookup process and start waiting for results
"""
ns = NsLookup(self._on_ready, host, type) ns = NsLookup(self._on_ready, host, type)
ns.set_idlequeue(self.idlequeue) ns.set_idlequeue(self.idlequeue)
ns.commandtimeout = 10 ns.commandtimeout = 10

View File

@ -33,17 +33,19 @@ except ImportError:
return None return None
else: else:
def pos_int_validator(text): def pos_int_validator(text):
"""Validates that text can be evaluated as a positive integer.""" """
Validates that text can be evaluated as a positive integer
"""
result = int(text) result = int(text)
if result < 0: if result < 0:
raise ValueError("Error: value '%(text)s' " raise ValueError("Error: value '%(text)s' "
"must be a positive integer") "must be a positive integer")
return result return result
def generate_uri_role( role_name, aliases, def generate_uri_role( role_name, aliases, anchor_text, base_url,
anchor_text, base_url, interpret_url, validator):
interpret_url, validator): """
'''Creates and register a uri based "interpreted role". Create and register a uri based "interpreted role"
Those are similar to the RFC, and PEP ones, and take Those are similar to the RFC, and PEP ones, and take
role_name: role_name:
@ -58,7 +60,7 @@ else:
this, modulo the validated text, will be added to it this, modulo the validated text, will be added to it
validator: validator:
should return the validated text, or raise ValueError should return the validated text, or raise ValueError
''' """
def uri_reference_role(role, rawtext, text, lineno, inliner, def uri_reference_role(role, rawtext, text, lineno, inliner,
options={}, content=[]): options={}, content=[]):
try: try:
@ -94,15 +96,15 @@ else:
pos_int_validator) pos_int_validator)
class HTMLGenerator: class HTMLGenerator:
'''Really simple HTMLGenerator starting from publish_parts. """
Really simple HTMLGenerator starting from publish_parts
It reuses the docutils.core.Publisher class, which means it is *not* It reuses the docutils.core.Publisher class, which means it is *not*
threadsafe. threadsafe.
''' """
def __init__(self, def __init__(self, settings_spec=None,
settings_spec=None, settings_overrides=dict(report_level=5, halt_level=5),
settings_overrides=dict(report_level=5, halt_level=5), config_section='general'):
config_section='general'):
self.pub = Publisher(reader=None, parser=None, writer=None, self.pub = Publisher(reader=None, parser=None, writer=None,
settings=None, settings=None,
source_class=io.StringInput, source_class=io.StringInput,
@ -124,13 +126,12 @@ else:
config_section) config_section)
def create_xhtml(self, text, def create_xhtml(self, text, destination=None, destination_path=None,
destination=None, enable_exit_status=None):
destination_path=None, """
enable_exit_status=None): Create xhtml for a fragment of IM dialog. We can use the source_name
''' Create xhtml for a fragment of IM dialog. to store info about the message
We can use the source_name to store info about """
the message.'''
self.pub.set_source(text, None) self.pub.set_source(text, None)
self.pub.set_destination(destination, destination_path) self.pub.set_destination(destination, destination_path)
output = self.pub.publish(enable_exit_status=enable_exit_status) output = self.pub.publish(enable_exit_status=enable_exit_status)

View File

@ -66,7 +66,9 @@ class SleepyWindows:
return idleDelta return idleDelta
def poll(self): def poll(self):
'''checks to see if we should change state''' """
Check to see if we should change state
"""
if not SUPPORTED: if not SUPPORTED:
return False return False
@ -113,7 +115,9 @@ class SleepyUnix:
return idle.getIdleSec() return idle.getIdleSec()
def poll(self): def poll(self):
'''checks to see if we should change state''' """
Check to see if we should change state
"""
if not SUPPORTED: if not SUPPORTED:
return False return False

View File

@ -52,9 +52,12 @@ READ_TIMEOUT = 180
SEND_TIMEOUT = 180 SEND_TIMEOUT = 180
class SocksQueue: class SocksQueue:
''' queue for all file requests objects ''' """
Queue for all file requests objects
"""
def __init__(self, idlequeue, complete_transfer_cb=None, def __init__(self, idlequeue, complete_transfer_cb=None,
progress_transfer_cb=None, error_cb=None): progress_transfer_cb=None, error_cb=None):
self.connected = 0 self.connected = 0
self.readers = {} self.readers = {}
self.files_props = {} self.files_props = {}
@ -72,9 +75,10 @@ class SocksQueue:
self.on_failure = None self.on_failure = None
def start_listener(self, port, sha_str, sha_handler, sid): def start_listener(self, port, sha_str, sha_handler, sid):
''' start waiting for incomming connections on (host, port) """
and do a socks5 authentication using sid for generated sha Start waiting for incomming connections on (host, port) and do a socks5
''' authentication using sid for generated SHA
"""
self.sha_handlers[sha_str] = (sha_handler, sid) self.sha_handlers[sha_str] = (sha_handler, sid)
if self.listener is None: if self.listener is None:
self.listener = Socks5Listener(self.idlequeue, port) self.listener = Socks5Listener(self.idlequeue, port)
@ -121,8 +125,10 @@ class SocksQueue:
streamhost['idx'] = receiver.queue_idx streamhost['idx'] = receiver.queue_idx
def _socket_connected(self, streamhost, file_props): def _socket_connected(self, streamhost, file_props):
''' called when there is a host connected to one of the """
senders's streamhosts. Stop othere attempts for connections ''' Called when there is a host connected to one of the senders's
streamhosts. Stop othere attempts for connections
"""
for host in file_props['streamhosts']: for host in file_props['streamhosts']:
if host != streamhost and 'idx' in host: if host != streamhost and 'idx' in host:
if host['state'] == 1: if host['state'] == 1:
@ -137,10 +143,11 @@ class SocksQueue:
host['state'] = -2 host['state'] = -2
def reconnect_receiver(self, receiver, streamhost): def reconnect_receiver(self, receiver, streamhost):
''' Check the state of all streamhosts and if all has failed, then """
emit connection failure cb. If there are some which are still Check the state of all streamhosts and if all has failed, then emit
not connected try to establish connection to one of them. connection failure cb. If there are some which are still not connected
''' try to establish connection to one of them
"""
self.idlequeue.remove_timeout(receiver.fd) self.idlequeue.remove_timeout(receiver.fd)
self.idlequeue.unplug_idle(receiver.fd) self.idlequeue.unplug_idle(receiver.fd)
file_props = receiver.file_props file_props = receiver.file_props
@ -173,7 +180,9 @@ class SocksQueue:
self.process_result(-1, receiver) self.process_result(-1, receiver)
def _connection_refused(self, streamhost, file_props, idx): def _connection_refused(self, streamhost, file_props, idx):
''' cb, called when we loose connection during transfer''' """
Called when we loose connection during transfer
"""
if file_props is None: if file_props is None:
return return
streamhost['state'] = -1 streamhost['state'] = -1
@ -189,7 +198,9 @@ class SocksQueue:
del(file_props['failure_cb']) del(file_props['failure_cb'])
def add_receiver(self, account, sock5_receiver): def add_receiver(self, account, sock5_receiver):
''' add new file request ''' """
Add new file request
"""
self.readers[self.idx] = sock5_receiver self.readers[self.idx] = sock5_receiver
sock5_receiver.queue_idx = self.idx sock5_receiver.queue_idx = self.idx
sock5_receiver.queue = self sock5_receiver.queue = self
@ -259,9 +270,10 @@ class SocksQueue:
sender.file_props = file_props sender.file_props = file_props
def add_file_props(self, account, file_props): def add_file_props(self, account, file_props):
''' file_prop to the dict of current file_props. """
It is identified by account name and sid File_prop to the dict of current file_props. It is identified by account
''' name and sid
"""
if file_props is None or ('sid' in file_props) is False: if file_props is None or ('sid' in file_props) is False:
return return
_id = file_props['sid'] _id = file_props['sid']
@ -279,7 +291,9 @@ class SocksQueue:
self.connected = 0 self.connected = 0
def get_file_props(self, account, sid): def get_file_props(self, account, sid):
''' get fil_prop by account name and session id ''' """
Get fil_prop by account name and session id
"""
if account in self.files_props: if account in self.files_props:
fl_props = self.files_props[account] fl_props = self.files_props[account]
if sid in fl_props: if sid in fl_props:
@ -294,11 +308,12 @@ class SocksQueue:
self.connected += 1 self.connected += 1
def process_result(self, result, actor): def process_result(self, result, actor):
''' Take appropriate actions upon the result: """
[ 0, - 1 ] complete/end transfer Take appropriate actions upon the result:
[ > 0 ] send progress message [ 0, - 1 ] complete/end transfer
[ None ] do nothing [ > 0 ] send progress message
''' [ None ] do nothing
"""
if result is None: if result is None:
return return
if result in (0, -1) and self.complete_transfer_cb is not None: if result in (0, -1) and self.complete_transfer_cb is not None:
@ -310,8 +325,10 @@ class SocksQueue:
self.progress_transfer_cb(actor.account, actor.file_props) self.progress_transfer_cb(actor.account, actor.file_props)
def remove_receiver(self, idx, do_disconnect=True): def remove_receiver(self, idx, do_disconnect=True):
''' Remove reciver from the list and decrease """
the number of active connections with 1''' Remove reciver from the list and decrease the number of active
connections with 1
"""
if idx != -1: if idx != -1:
if idx in self.readers: if idx in self.readers:
reader = self.readers[idx] reader = self.readers[idx]
@ -325,8 +342,10 @@ class SocksQueue:
del(self.readers[idx]) del(self.readers[idx])
def remove_sender(self, idx, do_disconnect=True): def remove_sender(self, idx, do_disconnect=True):
''' Remove sender from the list of senders and decrease the """
number of active connections with 1''' Remove sender from the list of senders and decrease the number of active
connections with 1
"""
if idx != -1: if idx != -1:
if idx in self.senders: if idx in self.senders:
if do_disconnect: if do_disconnect:
@ -386,9 +405,10 @@ class Socks5:
self.file = None self.file = None
def get_fd(self): def get_fd(self):
''' Test if file is already open and return its fd, """
or just open the file and return the fd. Test if file is already open and return its fd, or just open the file and
''' return the fd
"""
if 'fd' in self.file_props: if 'fd' in self.file_props:
fd = self.file_props['fd'] fd = self.file_props['fd']
else: else:
@ -413,8 +433,10 @@ class Socks5:
pass pass
def receive(self): def receive(self):
''' Reads small chunks of data. """
Calls owner's disconnected() method if appropriate.''' Read small chunks of data. Call owner's disconnected() method if
appropriate
"""
received = '' received = ''
try: try:
add = self._recv(64) add = self._recv(64)
@ -426,7 +448,9 @@ class Socks5:
return add return add
def send_raw(self,raw_data): def send_raw(self,raw_data):
''' Writes raw outgoing data. ''' """
Write raw outgoing data
"""
try: try:
self._send(raw_data) self._send(raw_data)
except Exception: except Exception:
@ -483,7 +507,9 @@ class Socks5:
return -1 return -1
def get_file_contents(self, timeout): def get_file_contents(self, timeout):
''' read file contents from socket and write them to file ''' """
Read file contents from socket and write them to file
"""
if self.file_props is None or ('file-name' in self.file_props) is False: if self.file_props is None or ('file-name' in self.file_props) is False:
self.file_props['error'] = -2 self.file_props['error'] = -2
return None return None
@ -557,7 +583,9 @@ class Socks5:
return None return None
def disconnect(self): def disconnect(self):
''' Closes open descriptors and remover socket descr. from idleque ''' """
Close open descriptors and remover socket descr. from idleque
"""
# be sure that we don't leave open file # be sure that we don't leave open file
self.close_file() self.close_file()
self.idlequeue.remove_timeout(self.fd) self.idlequeue.remove_timeout(self.fd)
@ -573,13 +601,15 @@ class Socks5:
self.state = -1 self.state = -1
def _get_auth_buff(self): def _get_auth_buff(self):
''' Message, that we support 1 one auth mechanism: """
the 'no auth' mechanism. ''' Message, that we support 1 one auth mechanism: the 'no auth' mechanism
"""
return struct.pack('!BBB', 0x05, 0x01, 0x00) return struct.pack('!BBB', 0x05, 0x01, 0x00)
def _parse_auth_buff(self, buff): def _parse_auth_buff(self, buff):
''' Parse the initial message and create a list of auth """
mechanisms ''' Parse the initial message and create a list of auth mechanisms
"""
auth_mechanisms = [] auth_mechanisms = []
try: try:
num_auth = struct.unpack('!xB', buff[:2])[0] num_auth = struct.unpack('!xB', buff[:2])[0]
@ -591,9 +621,9 @@ class Socks5:
return auth_mechanisms return auth_mechanisms
def _get_auth_response(self): def _get_auth_response(self):
''' socks version(5), number of extra auth methods (we send """
0x00 - no auth Socks version(5), number of extra auth methods (we send 0x00 - no auth)
) ''' """
return struct.pack('!BB', 0x05, 0x00) return struct.pack('!BB', 0x05, 0x00)
def _get_connect_buff(self): def _get_connect_buff(self):
@ -604,8 +634,10 @@ class Socks5:
return buff return buff
def _get_request_buff(self, msg, command = 0x01): def _get_request_buff(self, msg, command = 0x01):
''' Connect request by domain name, """
sid sha, instead of domain name (jep 0096) ''' Connect request by domain name, sid sha, instead of domain name (jep
0096)
"""
buff = struct.pack('!BBBBB%dsBB' % len(msg), buff = struct.pack('!BBBBB%dsBB' % len(msg),
0x05, command, 0x00, 0x03, len(msg), msg, 0, 0) 0x05, command, 0x00, 0x03, len(msg), msg, 0, 0)
return buff return buff
@ -634,7 +666,9 @@ class Socks5:
return (req_type, host, port) return (req_type, host, port)
def read_connect(self): def read_connect(self):
''' connect responce: version, auth method ''' """
Connect response: version, auth method
"""
buff = self._recv() buff = self._recv()
try: try:
version, method = struct.unpack('!BB', buff) version, method = struct.unpack('!BB', buff)
@ -652,7 +686,9 @@ class Socks5:
self.idlequeue.plug_idle(self, True, False) self.idlequeue.plug_idle(self, True, False)
def _get_sha1_auth(self): def _get_sha1_auth(self):
''' get sha of sid + Initiator jid + Target jid ''' """
Get sha of sid + Initiator jid + Target jid
"""
if 'is_a_proxy' in self.file_props: if 'is_a_proxy' in self.file_props:
del(self.file_props['is_a_proxy']) del(self.file_props['is_a_proxy'])
return hashlib.sha1('%s%s%s' % (self.sid, return hashlib.sha1('%s%s%s' % (self.sid,
@ -662,9 +698,12 @@ class Socks5:
hexdigest() hexdigest()
class Socks5Sender(Socks5, IdleObject): class Socks5Sender(Socks5, IdleObject):
''' class for sending file to socket over socks5 ''' """
Class for sending file to socket over socks5
"""
def __init__(self, idlequeue, sock_hash, parent, _sock, host=None, def __init__(self, idlequeue, sock_hash, parent, _sock, host=None,
port=None): port=None):
self.queue_idx = sock_hash self.queue_idx = sock_hash
self.queue = parent self.queue = parent
Socks5.__init__(self, idlequeue, host, port, None, None, None) Socks5.__init__(self, idlequeue, host, port, None, None, None)
@ -745,7 +784,9 @@ class Socks5Sender(Socks5, IdleObject):
self.disconnect() self.disconnect()
def send_file(self): def send_file(self):
''' start sending the file over verified connection ''' """
Start sending the file over verified connection
"""
if self.file_props['started']: if self.file_props['started']:
return return
self.file_props['error'] = 0 self.file_props['error'] = 0
@ -766,7 +807,9 @@ class Socks5Sender(Socks5, IdleObject):
return self.write_next() # initial for nl byte return self.write_next() # initial for nl byte
def main(self): def main(self):
''' initial requests for verifying the connection ''' """
Initial requests for verifying the connection
"""
if self.state == 1: # initial read if self.state == 1: # initial read
buff = self.receive() buff = self.receive()
if not self.connected: if not self.connected:
@ -785,7 +828,9 @@ class Socks5Sender(Socks5, IdleObject):
return None return None
def disconnect(self, cb=True): def disconnect(self, cb=True):
''' Closes the socket. ''' """
Close the socket
"""
# close connection and remove us from the queue # close connection and remove us from the queue
Socks5.disconnect(self) Socks5.disconnect(self)
if self.file_props is not None: if self.file_props is not None:
@ -796,10 +841,12 @@ class Socks5Sender(Socks5, IdleObject):
class Socks5Listener(IdleObject): class Socks5Listener(IdleObject):
def __init__(self, idlequeue, port): def __init__(self, idlequeue, port):
''' handle all incomming connections on (0.0.0.0, port) """
Handle all incomming connections on (0.0.0.0, port)
This class implements IdleObject, but we will expect This class implements IdleObject, but we will expect
only pollin events though only pollin events though
''' """
self.port = port self.port = port
self.ais = socket.getaddrinfo(None, port, socket.AF_UNSPEC, self.ais = socket.getaddrinfo(None, port, socket.AF_UNSPEC,
socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_PASSIVE) socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_PASSIVE)
@ -843,16 +890,22 @@ class Socks5Listener(IdleObject):
self.started = True self.started = True
def pollend(self): def pollend(self):
''' called when we stop listening on (host, port) ''' """
Called when we stop listening on (host, port)
"""
self.disconnect() self.disconnect()
def pollin(self): def pollin(self):
''' accept a new incomming connection and notify queue''' """
Accept a new incomming connection and notify queue
"""
sock = self.accept_conn() sock = self.accept_conn()
self.queue.on_connection_accepted(sock) self.queue.on_connection_accepted(sock)
def disconnect(self): def disconnect(self):
''' free all resources, we are not listening anymore ''' """
Free all resources, we are not listening anymore
"""
self.idlequeue.remove_timeout(self.fd) self.idlequeue.remove_timeout(self.fd)
self.idlequeue.unplug_idle(self.fd) self.idlequeue.unplug_idle(self.fd)
self.fd = -1 self.fd = -1
@ -864,7 +917,9 @@ class Socks5Listener(IdleObject):
pass pass
def accept_conn(self): def accept_conn(self):
''' accepts a new incomming connection ''' """
Accept a new incomming connection
"""
_sock = self._serv.accept() _sock = self._serv.accept()
_sock[0].setblocking(False) _sock[0].setblocking(False)
return _sock return _sock
@ -909,7 +964,9 @@ class Socks5Receiver(Socks5, IdleObject):
self.queue.reconnect_receiver(self, self.streamhost) self.queue.reconnect_receiver(self, self.streamhost)
def connect(self): def connect(self):
''' create the socket and plug it to the idlequeue ''' """
Create the socket and plug it to the idlequeue
"""
if self.ais is None: if self.ais is None:
return None return None
@ -1018,7 +1075,9 @@ class Socks5Receiver(Socks5, IdleObject):
return 1 # we are connected return 1 # we are connected
def main(self, timeout=0): def main(self, timeout=0):
''' begin negotiation. on success 'address' != 0 ''' """
Begin negotiation. on success 'address' != 0
"""
result = 1 result = 1
buff = self.receive() buff = self.receive()
if buff == '': if buff == '':
@ -1087,7 +1146,9 @@ class Socks5Receiver(Socks5, IdleObject):
return None return None
def disconnect(self, cb=True): def disconnect(self, cb=True):
''' Closes the socket. Remove self from queue if cb is True''' """
Close the socket. Remove self from queue if cb is True
"""
# close connection # close connection
Socks5.disconnect(self) Socks5.disconnect(self)
if cb is True: if cb is True:

View File

@ -84,10 +84,11 @@ class StanzaSession(object):
return to return to
def remove_events(self, types): def remove_events(self, types):
''' """
Remove events associated with this session from the queue. Remove events associated with this session from the queue
returns True if any events were removed (unlike events.py remove_events)
''' Returns True if any events were removed (unlike events.py remove_events)
"""
any_removed = False any_removed = False
for j in (self.jid, self.jid.getStripped()): for j in (self.jid, self.jid.getStripped()):
@ -140,10 +141,10 @@ class StanzaSession(object):
self.cancelled_negotiation() self.cancelled_negotiation()
def cancelled_negotiation(self): def cancelled_negotiation(self):
''' """
A negotiation has been cancelled, so reset this session to its default A negotiation has been cancelled, so reset this session to its default
state. state
''' """
if self.control: if self.control:
self.control.on_cancel_session_negotiation() self.control.on_cancel_session_negotiation()
@ -175,7 +176,7 @@ class StanzaSession(object):
class EncryptedStanzaSession(StanzaSession): class EncryptedStanzaSession(StanzaSession):
''' """
An encrypted stanza negotiation has several states. They arerepresented as An encrypted stanza negotiation has several states. They arerepresented as
the following values in the 'status' attribute of the session object: the following values in the 'status' attribute of the session object:
@ -198,7 +199,8 @@ class EncryptedStanzaSession(StanzaSession):
The transition between these states is handled in gajim.py's The transition between these states is handled in gajim.py's
handle_session_negotiation method. handle_session_negotiation method.
''' """
def __init__(self, conn, jid, thread_id, type_='chat'): def __init__(self, conn, jid, thread_id, type_='chat'):
StanzaSession.__init__(self, conn, jid, thread_id, type_='chat') StanzaSession.__init__(self, conn, jid, thread_id, type_='chat')
@ -228,9 +230,9 @@ class EncryptedStanzaSession(StanzaSession):
return True return True
def set_kc_s(self, value): def set_kc_s(self, value):
''' """
keep the encrypter updated with my latest cipher key Keep the encrypter updated with my latest cipher key
''' """
self._kc_s = value self._kc_s = value
self.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR, self.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR,
counter=self.encryptcounter) counter=self.encryptcounter)
@ -239,9 +241,9 @@ class EncryptedStanzaSession(StanzaSession):
return self._kc_s return self._kc_s
def set_kc_o(self, value): def set_kc_o(self, value):
''' """
keep the decrypter updated with the other party's latest cipher key Keep the decrypter updated with the other party's latest cipher key
''' """
self._kc_o = value self._kc_o = value
self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR, self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR,
counter=self.decryptcounter) counter=self.decryptcounter)
@ -335,7 +337,9 @@ class EncryptedStanzaSession(StanzaSession):
return self.encrypter.encrypt(padded) return self.encrypter.encrypt(padded)
def decrypt_stanza(self, stanza): def decrypt_stanza(self, stanza):
''' delete the unencrypted explanation body, if it exists ''' """
Delete the unencrypted explanation body, if it exists
"""
orig_body = stanza.getTag('body') orig_body = stanza.getTag('body')
if orig_body: if orig_body:
stanza.delChild(orig_body) stanza.delChild(orig_body)
@ -584,7 +588,9 @@ class EncryptedStanzaSession(StanzaSession):
self.send(request) self.send(request)
def verify_options_bob(self, form): def verify_options_bob(self, form):
''' 4.3 esession response (bob) ''' """
4.3 esession response (bob)
"""
negotiated = {'recv_pubkey': None, 'send_pubkey': None} negotiated = {'recv_pubkey': None, 'send_pubkey': None}
not_acceptable = [] not_acceptable = []
ask_user = {} ask_user = {}
@ -653,7 +659,9 @@ class EncryptedStanzaSession(StanzaSession):
return (negotiated, not_acceptable, ask_user) return (negotiated, not_acceptable, ask_user)
def respond_e2e_bob(self, form, negotiated, not_acceptable): def respond_e2e_bob(self, form, negotiated, not_acceptable):
''' 4.3 esession response (bob) ''' """
4.3 esession response (bob)
"""
response = xmpp.Message() response = xmpp.Message()
feature = response.NT.feature feature = response.NT.feature
feature.setNamespace(xmpp.NS_FEATURE) feature.setNamespace(xmpp.NS_FEATURE)
@ -728,7 +736,9 @@ class EncryptedStanzaSession(StanzaSession):
self.send(response) self.send(response)
def verify_options_alice(self, form): def verify_options_alice(self, form):
''' 'Alice Accepts' ''' """
'Alice Accepts'
"""
negotiated = {} negotiated = {}
ask_user = {} ask_user = {}
not_acceptable = [] not_acceptable = []
@ -756,11 +766,13 @@ class EncryptedStanzaSession(StanzaSession):
return (negotiated, not_acceptable, ask_user) return (negotiated, not_acceptable, ask_user)
def accept_e2e_alice(self, form, negotiated): def accept_e2e_alice(self, form, negotiated):
''' 'Alice Accepts', continued ''' """
'Alice Accepts', continued
"""
self.encryptable_stanzas = ['message'] self.encryptable_stanzas = ['message']
self.sas_algs = 'sas28x5' self.sas_algs = 'sas28x5'
self.cipher = AES self.cipher = AES
self.hash_alg = sha256 self.hash_alg = sha256
self.compression = None self.compression = None
self.negotiated = negotiated self.negotiated = negotiated
@ -828,7 +840,9 @@ class EncryptedStanzaSession(StanzaSession):
self.status = 'identified-alice' self.status = 'identified-alice'
def accept_e2e_bob(self, form): def accept_e2e_bob(self, form):
''' 4.5 esession accept (bob) ''' """
4.5 esession accept (bob)
"""
response = xmpp.Message() response = xmpp.Message()
init = response.NT.init init = response.NT.init
@ -948,11 +962,11 @@ class EncryptedStanzaSession(StanzaSession):
self.control.print_esession_details() self.control.print_esession_details()
def do_retained_secret(self, k, old_srs): def do_retained_secret(self, k, old_srs):
''' """
Calculate the new retained secret. determine if the user needs to check Calculate the new retained secret. determine if the user needs to check
the remote party's identity. Set up callbacks for when the identity has the remote party's identity. Set up callbacks for when the identity has
been verified. been verified
''' """
new_srs = self.hmac(k, 'New Retained Secret') new_srs = self.hmac(k, 'New Retained Secret')
self.srs = new_srs self.srs = new_srs
@ -1017,12 +1031,12 @@ class EncryptedStanzaSession(StanzaSession):
self.enable_encryption = False self.enable_encryption = False
def fail_bad_negotiation(self, reason, fields=None): def fail_bad_negotiation(self, reason, fields=None):
''' """
Sends an error and cancels everything. Send an error and cancels everything
If fields is None, the remote party has given us a bad cryptographic If fields is None, the remote party has given us a bad cryptographic
value of some kind. Otherwise, list the fields we haven't implemented value of some kind. Otherwise, list the fields we haven't implemented.
''' """
err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED) err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
err.T.error.T.text.setData(reason) err.T.error.T.text.setData(reason)