A big portion of doc-string refactoring
This commit is contained in:
parent
cea7c66f75
commit
6bf2246de5
|
@ -21,8 +21,10 @@
|
|||
## 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
|
||||
|
||||
|
@ -72,7 +74,9 @@ def Field(typ, **attrs):
|
|||
return f
|
||||
|
||||
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
|
||||
# like DateTimeField - so that dicts in Field() and ExtendField() will
|
||||
# be different...
|
||||
|
@ -94,19 +98,23 @@ def ExtendField(node):
|
|||
return f[typ](extend=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:
|
||||
return MultipleDataForm(extend=node)
|
||||
else:
|
||||
return SimpleDataForm(extend=node)
|
||||
|
||||
class DataField(ExtendedNode):
|
||||
""" Keeps data about one field - var, field type, labels, instructions...
|
||||
Base class for different kinds of fields. Use Field() function to
|
||||
construct one of these. """
|
||||
"""
|
||||
Keeps data about one field - var, field type, labels, instructions... Base
|
||||
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,
|
||||
required=False, options=None, extend=None):
|
||||
required=False, options=None, extend=None):
|
||||
|
||||
if extend is None:
|
||||
ExtendedNode.__init__(self, 'field')
|
||||
|
@ -124,10 +132,12 @@ class DataField(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
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',
|
||||
'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):
|
||||
t = self.getAttr('type')
|
||||
if t is None:
|
||||
|
@ -142,7 +152,9 @@ class DataField(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
def var():
|
||||
'''Field identifier.'''
|
||||
"""
|
||||
Field identifier
|
||||
"""
|
||||
def fget(self):
|
||||
return self.getAttr('var')
|
||||
|
||||
|
@ -157,7 +169,9 @@ class DataField(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
def label():
|
||||
'''Human-readable field name.'''
|
||||
"""
|
||||
Human-readable field name
|
||||
"""
|
||||
def fget(self):
|
||||
l = self.getAttr('label')
|
||||
if not l:
|
||||
|
@ -176,7 +190,9 @@ class DataField(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
def description():
|
||||
'''Human-readable description of field meaning.'''
|
||||
"""
|
||||
Human-readable description of field meaning
|
||||
"""
|
||||
def fget(self):
|
||||
return self.getTagData('desc') or u''
|
||||
|
||||
|
@ -196,7 +212,9 @@ class DataField(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
def required():
|
||||
'''Controls whether this field required to fill. Boolean.'''
|
||||
"""
|
||||
Controls whether this field required to fill. Boolean
|
||||
"""
|
||||
def fget(self):
|
||||
return bool(self.getTag('required'))
|
||||
|
||||
|
@ -212,7 +230,9 @@ class DataField(ExtendedNode):
|
|||
class BooleanField(DataField):
|
||||
@nested_property
|
||||
def value():
|
||||
'''Value of field. May contain True, False or None.'''
|
||||
"""
|
||||
Value of field. May contain True, False or None
|
||||
"""
|
||||
def fget(self):
|
||||
v = self.getTagData('value')
|
||||
if v in ('0', 'false'):
|
||||
|
@ -234,10 +254,15 @@ class BooleanField(DataField):
|
|||
return locals()
|
||||
|
||||
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
|
||||
def value():
|
||||
'''Value of field. May be any unicode string.'''
|
||||
"""
|
||||
Value of field. May be any unicode string
|
||||
"""
|
||||
def fget(self):
|
||||
return self.getTagData('value') or u''
|
||||
|
||||
|
@ -256,10 +281,15 @@ class StringField(DataField):
|
|||
return locals()
|
||||
|
||||
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
|
||||
def options():
|
||||
'''Options.'''
|
||||
"""
|
||||
Options
|
||||
"""
|
||||
def fget(self):
|
||||
options = []
|
||||
for element in self.getTags('option'):
|
||||
|
@ -294,14 +324,21 @@ class ListField(DataField):
|
|||
yield (v, l)
|
||||
|
||||
class ListSingleField(ListField, StringField):
|
||||
'''Covers list-single and jid-single fields.'''
|
||||
"""
|
||||
Covers list-single and jid-single fields
|
||||
"""
|
||||
pass
|
||||
|
||||
class ListMultiField(ListField):
|
||||
'''Covers list-multi and jid-multi fields.'''
|
||||
"""
|
||||
Covers list-multi and jid-multi fields
|
||||
"""
|
||||
|
||||
@nested_property
|
||||
def values():
|
||||
'''Values held in field.'''
|
||||
"""
|
||||
Values held in field
|
||||
"""
|
||||
def fget(self):
|
||||
values = []
|
||||
for element in self.getTags('value'):
|
||||
|
@ -326,7 +363,9 @@ class ListMultiField(ListField):
|
|||
class TextMultiField(DataField):
|
||||
@nested_property
|
||||
def value():
|
||||
'''Value held in field.'''
|
||||
"""
|
||||
Value held in field
|
||||
"""
|
||||
def fget(self):
|
||||
value = u''
|
||||
for element in self.iterTags('value'):
|
||||
|
@ -347,8 +386,10 @@ class TextMultiField(DataField):
|
|||
return locals()
|
||||
|
||||
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):
|
||||
self.associated = associated
|
||||
self.vars = {}
|
||||
|
@ -373,7 +414,9 @@ class DataRecord(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
def fields():
|
||||
'''List of fields in this record.'''
|
||||
"""
|
||||
List of fields in this record
|
||||
"""
|
||||
def fget(self):
|
||||
return self.getTags('field')
|
||||
|
||||
|
@ -391,14 +434,17 @@ class DataRecord(ExtendedNode):
|
|||
return locals()
|
||||
|
||||
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'):
|
||||
yield field
|
||||
|
||||
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():
|
||||
yield self[field.var], field
|
||||
|
||||
|
@ -420,9 +466,11 @@ class DataForm(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
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:
|
||||
filledform = DataForm(replyto=thisform)...'''
|
||||
filledform = DataForm(replyto=thisform)
|
||||
"""
|
||||
def fget(self):
|
||||
return self.getAttr('type')
|
||||
|
||||
|
@ -434,7 +482,11 @@ class DataForm(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
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):
|
||||
return self.getTagData('title')
|
||||
|
||||
|
@ -451,7 +503,11 @@ class DataForm(ExtendedNode):
|
|||
|
||||
@nested_property
|
||||
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
|
||||
def fget(self):
|
||||
value = u''
|
||||
|
@ -520,7 +576,9 @@ class MultipleDataForm(DataForm):
|
|||
|
||||
@nested_property
|
||||
def items():
|
||||
''' A list of all records. '''
|
||||
"""
|
||||
A list of all records
|
||||
"""
|
||||
def fget(self):
|
||||
return list(self.iter_records())
|
||||
|
||||
|
@ -543,7 +601,9 @@ class MultipleDataForm(DataForm):
|
|||
|
||||
# @nested_property
|
||||
# def reported():
|
||||
# ''' DataRecord that contains descriptions of fields in records.'''
|
||||
# """
|
||||
# DataRecord that contains descriptions of fields in records
|
||||
# """
|
||||
# def fget(self):
|
||||
# return self.getTag('reported')
|
||||
# def fset(self, record):
|
||||
|
|
|
@ -51,7 +51,10 @@ else:
|
|||
print _('D-Bus capabilities of Gajim cannot be used')
|
||||
|
||||
class SystemBus:
|
||||
'''A Singleton for the DBus SystemBus'''
|
||||
"""
|
||||
A Singleton for the DBus SystemBus
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.system_bus = None
|
||||
|
||||
|
@ -84,7 +87,10 @@ class SystemBus:
|
|||
system_bus = SystemBus()
|
||||
|
||||
class SessionBus:
|
||||
'''A Singleton for the D-Bus SessionBus'''
|
||||
"""
|
||||
A Singleton for the D-Bus SessionBus
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.session_bus = None
|
||||
|
||||
|
@ -115,8 +121,10 @@ class SessionBus:
|
|||
session_bus = SessionBus()
|
||||
|
||||
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:
|
||||
return None
|
||||
if session_bus.present():
|
||||
|
@ -144,9 +152,11 @@ def get_interface(interface, path, start_service=True):
|
|||
|
||||
|
||||
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
|
||||
iface = get_interface('org.kde.VisualNotifications', '/VisualNotifications',
|
||||
start_service=False)
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
"""
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
|
||||
import string
|
||||
|
||||
generators = [ None, # one to get the right offset
|
||||
|
|
|
@ -27,13 +27,18 @@
|
|||
import time
|
||||
|
||||
class Event:
|
||||
'''Information concerning each event'''
|
||||
"""
|
||||
Information concerning each event
|
||||
"""
|
||||
|
||||
def __init__(self, type_, time_, parameters, show_in_roster=False,
|
||||
show_in_systray=True):
|
||||
''' type_ in chat, normal, file-request, file-error, file-completed,
|
||||
show_in_systray=True):
|
||||
"""
|
||||
type_ in chat, normal, file-request, file-error, file-completed,
|
||||
file-request-error, file-send-error, file-stopped, gc_msg, pm,
|
||||
printed_chat, printed_gc_msg, printed_marked_gc_msg, printed_pm,
|
||||
gc-invitation, subscription_request, unsubscribedm jingle-incoming
|
||||
|
||||
parameters is (per type_):
|
||||
chat, normal, pm: [message, subject, kind, time, encrypted, resource,
|
||||
msg_id]
|
||||
|
@ -47,7 +52,7 @@ class Event:
|
|||
subscription_request: [text, nick]
|
||||
unsubscribed: contact
|
||||
jingle-incoming: (fulljid, sessionid, content_types)
|
||||
'''
|
||||
"""
|
||||
self.type_ = type_
|
||||
self.time_ = time_
|
||||
self.parameters = parameters
|
||||
|
@ -58,29 +63,40 @@ class Event:
|
|||
self.account = None
|
||||
|
||||
class Events:
|
||||
'''Information concerning all events'''
|
||||
"""
|
||||
Information concerning all events
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._events = {} # list of events {acct: {jid1: [E1, E2]}, }
|
||||
self._event_added_listeners = []
|
||||
self._event_removed_listeners = []
|
||||
|
||||
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:
|
||||
self._event_added_listeners.append(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:
|
||||
self._event_added_listeners.remove(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:
|
||||
self._event_removed_listeners.append(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:
|
||||
self._event_removed_listeners.remove(listener)
|
||||
|
||||
|
@ -125,9 +141,10 @@ class Events:
|
|||
self.fire_event_added(event)
|
||||
|
||||
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
|
||||
return True if no such event found'''
|
||||
"""
|
||||
If event is not specified, remove all events from this jid, optionally
|
||||
only from given type return True if no such event found
|
||||
"""
|
||||
if account not in self._events:
|
||||
return True
|
||||
if jid not in self._events[account]:
|
||||
|
@ -177,10 +194,11 @@ class Events:
|
|||
return self._get_nb_events(types = types, account = account)
|
||||
|
||||
def get_events(self, account, jid = None, types = []):
|
||||
'''returns 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: []
|
||||
optionally only from given type'''
|
||||
"""
|
||||
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: []
|
||||
optionally only from given type
|
||||
"""
|
||||
if account not in self._events:
|
||||
return []
|
||||
if not jid:
|
||||
|
@ -202,7 +220,9 @@ class Events:
|
|||
return events_list
|
||||
|
||||
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_)
|
||||
# be sure it's bigger than latest event
|
||||
first_event_time = time.time() + 1
|
||||
|
@ -213,9 +233,11 @@ class Events:
|
|||
first_event = event
|
||||
return first_event
|
||||
|
||||
def _get_nb_events(self, account = None, jid = None, attribute = None,
|
||||
types = []):
|
||||
'''return the number of pending events'''
|
||||
def _get_nb_events(self, account = None, jid = None, attribute = None, types
|
||||
= []):
|
||||
"""
|
||||
Return the number of pending events
|
||||
"""
|
||||
nb = 0
|
||||
if account:
|
||||
accounts = [account]
|
||||
|
@ -241,7 +263,9 @@ class Events:
|
|||
return nb
|
||||
|
||||
def _get_some_events(self, attribute):
|
||||
'''attribute in systray, roster'''
|
||||
"""
|
||||
Attribute in systray, roster
|
||||
"""
|
||||
events = {}
|
||||
for account in self._events:
|
||||
events[account] = {}
|
||||
|
@ -258,8 +282,11 @@ class Events:
|
|||
return 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
|
||||
first_event_time = time.time() + 1
|
||||
first_account = None
|
||||
|
@ -276,12 +303,16 @@ class Events:
|
|||
return first_account, first_jid, first_event
|
||||
|
||||
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)
|
||||
|
||||
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')
|
||||
|
||||
def get_first_systray_event(self):
|
||||
|
@ -289,13 +320,17 @@ class Events:
|
|||
return self._get_first_event_with_attribute(events)
|
||||
|
||||
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,
|
||||
jid = jid, types = types)
|
||||
|
||||
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')
|
||||
|
||||
# vim: se ts=3:
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
##
|
||||
|
||||
class PysqliteNotAvailable(Exception):
|
||||
'''sqlite2 is not installed or python bindings are missing'''
|
||||
"""
|
||||
Sqlite2 is not installed or python bindings are missing
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Exception.__init__(self)
|
||||
|
||||
|
@ -30,7 +33,10 @@ class PysqliteNotAvailable(Exception):
|
|||
return _('pysqlite2 (aka python-pysqlite2) dependency is missing. Exiting...')
|
||||
|
||||
class PysqliteOperationalError(Exception):
|
||||
'''sqlite2 raised pysqlite2.dbapi2.OperationalError'''
|
||||
"""
|
||||
Sqlite2 raised pysqlite2.dbapi2.OperationalError
|
||||
"""
|
||||
|
||||
def __init__(self, text=''):
|
||||
Exception.__init__(self)
|
||||
self.text = text
|
||||
|
@ -39,7 +45,10 @@ class PysqliteOperationalError(Exception):
|
|||
return self.text
|
||||
|
||||
class DatabaseMalformed(Exception):
|
||||
'''The databas can't be read'''
|
||||
"""
|
||||
The databas can't be read
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Exception.__init__(self)
|
||||
|
||||
|
@ -47,7 +56,10 @@ class DatabaseMalformed(Exception):
|
|||
return _('Database cannot be read.')
|
||||
|
||||
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):
|
||||
Exception.__init__(self)
|
||||
|
||||
|
@ -55,7 +67,10 @@ class ServiceNotAvailable(Exception):
|
|||
return _('Service not available: Gajim is not running, or remote_control is False')
|
||||
|
||||
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):
|
||||
Exception.__init__(self)
|
||||
|
||||
|
@ -63,7 +78,10 @@ class DbusNotSupported(Exception):
|
|||
return _('D-Bus is not present on this machine or python module is missing')
|
||||
|
||||
class SessionBusNotPresent(Exception):
|
||||
'''This exception indicates that there is no session daemon'''
|
||||
"""
|
||||
This exception indicates that there is no session daemon
|
||||
"""
|
||||
|
||||
def __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')
|
||||
|
||||
class NegotiationError(Exception):
|
||||
'''A session negotiation failed'''
|
||||
"""
|
||||
A session negotiation failed
|
||||
"""
|
||||
pass
|
||||
|
||||
class DecryptionError(Exception):
|
||||
'''A message couldn't be decrypted into usable XML'''
|
||||
"""
|
||||
A message couldn't be decrypted into usable XML
|
||||
"""
|
||||
pass
|
||||
|
||||
class Cancelled(Exception):
|
||||
'''The user cancelled an operation'''
|
||||
"""
|
||||
The user cancelled an operation
|
||||
"""
|
||||
pass
|
||||
|
||||
class LatexError(Exception):
|
||||
'''LaTeX processing failed for some reason'''
|
||||
"""
|
||||
LaTeX processing failed for some reason
|
||||
"""
|
||||
|
||||
def __init__(self, text=''):
|
||||
Exception.__init__(self)
|
||||
self.text = text
|
||||
|
@ -92,7 +119,10 @@ class LatexError(Exception):
|
|||
return self.text
|
||||
|
||||
class GajimGeneralException(Exception):
|
||||
'''This exception is our general exception'''
|
||||
"""
|
||||
This exception is our general exception
|
||||
"""
|
||||
|
||||
def __init__(self, text=''):
|
||||
Exception.__init__(self)
|
||||
self.text = text
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
"""
|
||||
Python class to show a "fuzzy clock".
|
||||
Homepage of the original: http://home.gna.org/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
|
||||
in turn based on the Fuzzy Clock Applet of Frerich Raabe (KDE).
|
||||
So most of the credit goes to this guys, thanks :-)
|
||||
'''
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
|
@ -46,7 +46,7 @@ class FuzzyClock:
|
|||
_('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") ]
|
||||
|
||||
FUZZY_DAYTIME = [ _('Night'), _('Early morning'), _('Morning'),
|
||||
FUZZY_DAYTIME = [ _('Night'), _('Early morning'), _('Morning'),
|
||||
_('Almost noon'), _('Noon'), _('Afternoon'), _('Evening'),
|
||||
_('Late evening'), _('Night') ]
|
||||
|
||||
|
|
|
@ -241,8 +241,9 @@ def get_room_and_nick_from_fjid(jid):
|
|||
return l
|
||||
|
||||
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)
|
||||
if not nick: # It's not a fake_jid, it is a real jid
|
||||
return fjid # we return the real jid
|
||||
|
@ -267,7 +268,9 @@ def get_jid_without_resource(jid):
|
|||
return jid.split('/')[0]
|
||||
|
||||
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
|
||||
# gaim@conference.jabber.org/nick
|
||||
if isinstance(nick, str):
|
||||
|
@ -281,19 +284,17 @@ def get_resource_from_jid(jid):
|
|||
else:
|
||||
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():
|
||||
'''returns the number of ALL accounts'''
|
||||
"""
|
||||
Return the number of ALL accounts
|
||||
"""
|
||||
return len(connections.keys())
|
||||
|
||||
def get_number_of_connected_accounts(accounts_list = None):
|
||||
'''returns the number of CONNECTED accounts
|
||||
you can optionally pass an accounts_list
|
||||
and if you do those will be checked, else all will be checked'''
|
||||
"""
|
||||
Returns the number of CONNECTED accounts. Uou can optionally pass an
|
||||
accounts_list and if you do those will be checked, else all will be checked
|
||||
"""
|
||||
connected_accounts = 0
|
||||
if accounts_list is None:
|
||||
accounts = connections.keys()
|
||||
|
@ -320,7 +321,9 @@ def zeroconf_is_connected():
|
|||
config.get_per('accounts', ZEROCONF_ACC_NAME, 'is_zeroconf')
|
||||
|
||||
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
|
||||
for account in connections.keys():
|
||||
if account_is_securely_connected(account):
|
||||
|
@ -335,8 +338,11 @@ def account_is_securely_connected(account):
|
|||
return False
|
||||
|
||||
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:
|
||||
# in the code block # it is a groupchat presence in handle_event_notify
|
||||
# jid was None. Yann why?
|
||||
|
@ -372,21 +378,27 @@ def jid_is_transport(jid):
|
|||
return False
|
||||
|
||||
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')
|
||||
hostname = config.get_per('accounts', account_name, 'hostname')
|
||||
jid = name + '@' + hostname
|
||||
return jid
|
||||
|
||||
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()
|
||||
for account in contacts.get_accounts():
|
||||
our_jids.append(get_jid_from_account(account))
|
||||
return our_jids
|
||||
|
||||
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:
|
||||
return connections[account_name].connected_hostname
|
||||
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')
|
||||
|
||||
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)
|
||||
if transport_name in ('aim', 'icq', 'msn', 'yahoo', 'facebook'):
|
||||
prefix = transport_name
|
||||
|
@ -403,7 +417,9 @@ def get_notification_image_prefix(jid):
|
|||
return prefix
|
||||
|
||||
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)
|
||||
if contact:
|
||||
actor = contact.get_shown_name()
|
||||
|
@ -412,7 +428,9 @@ def get_name_from_jid(account, jid):
|
|||
return actor
|
||||
|
||||
def get_priority(account, show):
|
||||
'''return the priority an account must have'''
|
||||
"""
|
||||
Return the priority an account must have
|
||||
"""
|
||||
if not show:
|
||||
show = 'online'
|
||||
|
||||
|
|
|
@ -92,15 +92,19 @@ def decompose_jid(jidstring):
|
|||
return user, server, resource
|
||||
|
||||
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
|
||||
|
||||
return prep(*decompose_jid(jidstring))
|
||||
|
||||
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
|
||||
labels = idna.dots.split(host)
|
||||
converted_labels = []
|
||||
|
@ -109,8 +113,10 @@ def idn_to_ascii(host):
|
|||
return ".".join(converted_labels)
|
||||
|
||||
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
|
||||
labels = idna.dots.split(host)
|
||||
converted_labels = []
|
||||
|
@ -119,7 +125,9 @@ def ascii_to_idn(host):
|
|||
return ".".join(converted_labels)
|
||||
|
||||
def parse_resource(resource):
|
||||
'''Perform stringprep on resource and return it'''
|
||||
"""
|
||||
Perform stringprep on resource and return it
|
||||
"""
|
||||
if resource:
|
||||
try:
|
||||
from xmpp.stringprepare import resourceprep
|
||||
|
@ -128,10 +136,11 @@ def parse_resource(resource):
|
|||
raise InvalidFormat, 'Invalid character in 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
|
||||
#http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
|
||||
|
||||
if user:
|
||||
try:
|
||||
from xmpp.stringprepare import nodeprep
|
||||
|
@ -186,10 +195,13 @@ def temp_failure_retry(func, *args, **kwargs):
|
|||
raise
|
||||
|
||||
def get_uf_show(show, use_mnemonic = False):
|
||||
'''returns a userfriendly string for dnd/xa/chat
|
||||
and makes all strings translatable
|
||||
if use_mnemonic is True, it adds _ so GUI should call with True
|
||||
for accessibility issues'''
|
||||
"""
|
||||
Return a userfriendly string for dnd/xa/chat and make all strings
|
||||
translatable
|
||||
|
||||
If use_mnemonic is True, it adds _ so GUI should call with True for
|
||||
accessibility issues
|
||||
"""
|
||||
if show == 'dnd':
|
||||
if use_mnemonic:
|
||||
uf_show = _('_Busy')
|
||||
|
@ -324,7 +336,9 @@ def from_one_line(msg):
|
|||
return msg
|
||||
|
||||
def get_uf_chatstate(chatstate):
|
||||
'''removes chatstate jargon and returns user friendly messages'''
|
||||
"""
|
||||
Remove chatstate jargon and returns user friendly messages
|
||||
"""
|
||||
if chatstate == 'active':
|
||||
return _('is paying attention to the conversation')
|
||||
elif chatstate == 'inactive':
|
||||
|
@ -339,10 +353,11 @@ def get_uf_chatstate(chatstate):
|
|||
return ''
|
||||
|
||||
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
|
||||
the first found command instead. Returns False otherwise and on errors.'''
|
||||
|
||||
"""
|
||||
Return True if 'command' is found in one of the directories in the user's
|
||||
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):
|
||||
try:
|
||||
if command in os.listdir(directory):
|
||||
|
@ -405,7 +420,9 @@ def get_output_of_command(command):
|
|||
return output
|
||||
|
||||
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):
|
||||
return string
|
||||
# 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
|
||||
|
||||
def ensure_utf8_string(string):
|
||||
'''make sure string is in UTF-8'''
|
||||
"""
|
||||
Make sure string is in UTF-8
|
||||
"""
|
||||
try:
|
||||
string = decode_string(string).encode('utf-8')
|
||||
except Exception:
|
||||
|
@ -428,28 +447,28 @@ def ensure_utf8_string(string):
|
|||
return string
|
||||
|
||||
def get_windows_reg_env(varname, default=''):
|
||||
'''asks for paths commonly used but not exposed as ENVs
|
||||
in english Windows 2003 those are:
|
||||
'AppData' = %USERPROFILE%\Application Data (also an ENV)
|
||||
'Desktop' = %USERPROFILE%\Desktop
|
||||
'Favorites' = %USERPROFILE%\Favorites
|
||||
'NetHood' = %USERPROFILE%\NetHood
|
||||
'Personal' = D:\My Documents (PATH TO MY DOCUMENTS)
|
||||
'PrintHood' = %USERPROFILE%\PrintHood
|
||||
'Programs' = %USERPROFILE%\Start Menu\Programs
|
||||
'Recent' = %USERPROFILE%\Recent
|
||||
'SendTo' = %USERPROFILE%\SendTo
|
||||
'Start Menu' = %USERPROFILE%\Start Menu
|
||||
'Startup' = %USERPROFILE%\Start Menu\Programs\Startup
|
||||
'Templates' = %USERPROFILE%\Templates
|
||||
'My Pictures' = D:\My Documents\My Pictures
|
||||
'Local Settings' = %USERPROFILE%\Local Settings
|
||||
'Local AppData' = %USERPROFILE%\Local Settings\Application Data
|
||||
'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files
|
||||
'Cookies' = %USERPROFILE%\Cookies
|
||||
'History' = %USERPROFILE%\Local Settings\History
|
||||
'''
|
||||
|
||||
"""
|
||||
Ask for paths commonly used but not exposed as ENVs in english Windows 2003
|
||||
those are:
|
||||
'AppData' = %USERPROFILE%\Application Data (also an ENV)
|
||||
'Desktop' = %USERPROFILE%\Desktop
|
||||
'Favorites' = %USERPROFILE%\Favorites
|
||||
'NetHood' = %USERPROFILE%\NetHood
|
||||
'Personal' = D:\My Documents (PATH TO MY DOCUMENTS)
|
||||
'PrintHood' = %USERPROFILE%\PrintHood
|
||||
'Programs' = %USERPROFILE%\Start Menu\Programs
|
||||
'Recent' = %USERPROFILE%\Recent
|
||||
'SendTo' = %USERPROFILE%\SendTo
|
||||
'Start Menu' = %USERPROFILE%\Start Menu
|
||||
'Startup' = %USERPROFILE%\Start Menu\Programs\Startup
|
||||
'Templates' = %USERPROFILE%\Templates
|
||||
'My Pictures' = D:\My Documents\My Pictures
|
||||
'Local Settings' = %USERPROFILE%\Local Settings
|
||||
'Local AppData' = %USERPROFILE%\Local Settings\Application Data
|
||||
'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files
|
||||
'Cookies' = %USERPROFILE%\Cookies
|
||||
'History' = %USERPROFILE%\Local Settings\History
|
||||
"""
|
||||
if os.name != 'nt':
|
||||
return ''
|
||||
|
||||
|
@ -467,7 +486,9 @@ r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders')
|
|||
return val
|
||||
|
||||
def get_my_pictures_path():
|
||||
'''windows-only atm. [Unix lives in the past]'''
|
||||
"""
|
||||
Windows-only atm
|
||||
"""
|
||||
return get_windows_reg_env('My Pictures')
|
||||
|
||||
def get_desktop_path():
|
||||
|
@ -485,8 +506,10 @@ def get_documents_path():
|
|||
return path
|
||||
|
||||
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
|
||||
if len(filename) > 48:
|
||||
hash = hashlib.md5(filename)
|
||||
|
@ -502,11 +525,13 @@ def sanitize_filename(filename):
|
|||
return filename
|
||||
|
||||
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'.
|
||||
If any of the params is not present (None or 0) the action
|
||||
on it is not performed'''
|
||||
"""
|
||||
Cut the chars after 'max_chars' on each line and show only the first
|
||||
'max_lines'
|
||||
|
||||
If any of the params is not present (None or 0) the action on it is not
|
||||
performed
|
||||
"""
|
||||
def _cut_if_long(string):
|
||||
if len(string) > max_chars:
|
||||
string = string[:max_chars - 3] + '...'
|
||||
|
@ -535,10 +560,11 @@ def get_account_status(account):
|
|||
return status
|
||||
|
||||
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.
|
||||
prefix is the path to the requested avatar just before the ".png" or
|
||||
".jpeg".'''
|
||||
"""
|
||||
Return the filename of the avatar, distinguishes between user- and contact-
|
||||
provided one. Return None if no avatar was found at all. prefix is the path
|
||||
to the requested avatar just before the ".png" or ".jpeg"
|
||||
"""
|
||||
# First, scan for a local, user-set avatar
|
||||
for type_ in ('jpeg', 'png'):
|
||||
file_ = prefix + '_local.' + type_
|
||||
|
@ -552,13 +578,16 @@ def get_avatar_path(prefix):
|
|||
return None
|
||||
|
||||
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
|
||||
are handled:
|
||||
- Optional milliseconds 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
|
||||
the above format.'''
|
||||
the above format.
|
||||
"""
|
||||
timestamp = timestamp.split('.')[0]
|
||||
timestamp = timestamp.replace('-', '')
|
||||
timestamp = timestamp.replace('z', '')
|
||||
|
@ -609,8 +638,11 @@ def convert_bytes(string):
|
|||
return suffix % unicode(bytes)
|
||||
|
||||
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 = {}
|
||||
for jid in gajim.contacts.get_jid_list(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')
|
||||
play_sound_file(path_to_soundfile)
|
||||
|
||||
def check_soundfile_path(file,
|
||||
dirs=(gajim.gajimpaths.root, gajim.DATA_DIR)):
|
||||
'''Check if the sound file exists.
|
||||
def check_soundfile_path(file, dirs=(gajim.gajimpaths.root, gajim.DATA_DIR)):
|
||||
"""
|
||||
Check if the sound file exists
|
||||
|
||||
: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
|
||||
(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:
|
||||
return None
|
||||
elif os.path.exists(file):
|
||||
|
@ -710,16 +744,17 @@ def check_soundfile_path(file,
|
|||
return d
|
||||
return None
|
||||
|
||||
def strip_soundfile_path(file,
|
||||
dirs=(gajim.gajimpaths.root, gajim.DATA_DIR),
|
||||
abs=True):
|
||||
'''Remove knowns paths from a sound file:
|
||||
def strip_soundfile_path(file, dirs=(gajim.gajimpaths.root, gajim.DATA_DIR),
|
||||
abs=True):
|
||||
"""
|
||||
Remove knowns paths from a sound file
|
||||
|
||||
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.
|
||||
param: file: the filename to strip.
|
||||
param: dirs: list of knowns paths from which the filename should be stripped.
|
||||
param: abs: force absolute path on dirs
|
||||
'''
|
||||
"""
|
||||
if not file:
|
||||
return None
|
||||
|
||||
|
@ -777,7 +812,9 @@ def get_global_status():
|
|||
|
||||
|
||||
def statuses_unified():
|
||||
'''testing if all statuses are the same.'''
|
||||
"""
|
||||
Test if all statuses are the same
|
||||
"""
|
||||
reference = None
|
||||
for account in gajim.connections:
|
||||
if not gajim.config.get_per('accounts', account,
|
||||
|
@ -790,7 +827,9 @@ def statuses_unified():
|
|||
return True
|
||||
|
||||
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):
|
||||
return 'event'
|
||||
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'
|
||||
|
||||
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()))
|
||||
|
||||
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)
|
||||
return gajim.get_jid_without_resource(jid)
|
||||
|
||||
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()
|
||||
|
||||
def remove_invalid_xml_chars(string):
|
||||
|
@ -861,7 +906,9 @@ distro_info = {
|
|||
}
|
||||
|
||||
def get_random_string_16():
|
||||
''' create random string of length 16'''
|
||||
"""
|
||||
Create random string of length 16
|
||||
"""
|
||||
rng = range(65, 90)
|
||||
rng.extend(range(48, 57))
|
||||
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',
|
||||
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
|
||||
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'''
|
||||
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 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:
|
||||
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
|
||||
'popup')
|
||||
|
@ -967,7 +1017,9 @@ advanced_notif_num = None, is_first_message = True):
|
|||
return False
|
||||
|
||||
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:
|
||||
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
|
||||
'auto_open')
|
||||
|
@ -1018,8 +1070,10 @@ def get_chat_control(account, contact):
|
|||
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
|
||||
|
||||
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
|
||||
max_ungrouped_events = 10
|
||||
|
||||
|
@ -1131,7 +1185,9 @@ def get_notification_icon_tooltip_text():
|
|||
return text
|
||||
|
||||
def get_accounts_info():
|
||||
'''helper for notification icon tooltip'''
|
||||
"""
|
||||
Helper for notification icon tooltip
|
||||
"""
|
||||
accounts = []
|
||||
accounts_list = sorted(gajim.contacts.get_accounts())
|
||||
for account in accounts_list:
|
||||
|
@ -1182,9 +1238,14 @@ def get_transport_path(transport):
|
|||
return get_iconset_path(gajim.config.get('iconset'))
|
||||
|
||||
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
|
||||
XXXXXXXXMISMATCH is returned. If the key is trusted and not yet assigned, assign it'''
|
||||
"""
|
||||
Return 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 XXXXXXXXMISMATCH is returned. If the key is trusted and not yet
|
||||
assigned, assign it.
|
||||
"""
|
||||
if gajim.connections[account].USE_GPG:
|
||||
if keyID and len(keyID) == 16:
|
||||
keyID = keyID[8:]
|
||||
|
|
|
@ -32,7 +32,7 @@ def paragraph_direction_mark(text):
|
|||
"""
|
||||
Determine paragraph writing direction according to
|
||||
http://www.unicode.org/reports/tr9/#The_Paragraph_Level
|
||||
|
||||
|
||||
Returns either Unicode LTR mark or RTL mark.
|
||||
"""
|
||||
for char in text:
|
||||
|
@ -84,11 +84,12 @@ def Q_(s):
|
|||
return s
|
||||
|
||||
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)
|
||||
if n == 1 and replace_sing is not None:
|
||||
text = text % replace_sing
|
||||
|
|
|
@ -73,7 +73,9 @@ except OSError, e:
|
|||
xss_available = False
|
||||
|
||||
def getIdleSec():
|
||||
"""Returns the idle time in seconds"""
|
||||
"""
|
||||
Return the idle time in seconds
|
||||
"""
|
||||
if not xss_available:
|
||||
return 0
|
||||
if libXss.XScreenSaverQueryInfo(dpy_p, rootwindow, xss_info_p) == 0:
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
''' Handles the jingle signalling protocol. '''
|
||||
"""
|
||||
Handles the jingle signalling protocol
|
||||
"""
|
||||
|
||||
#TODO:
|
||||
# * things in XEP 0176, including:
|
||||
|
@ -37,7 +39,10 @@ from jingle_rtp import JingleAudio, JingleVideo
|
|||
|
||||
|
||||
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):
|
||||
# dictionary: (jid, sessionid) => JingleSession object
|
||||
self.__sessions = {}
|
||||
|
@ -47,13 +52,17 @@ class ConnectionJingle(object):
|
|||
self.__iq_responses = {}
|
||||
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
self.__sessions[(jingle.peerjid, jingle.sid)] = jingle
|
||||
|
||||
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)
|
||||
if key in self.__sessions:
|
||||
#FIXME: Move this elsewhere?
|
||||
|
@ -63,12 +72,15 @@ class ConnectionJingle(object):
|
|||
del self.__sessions[key]
|
||||
|
||||
def _JingleCB(self, con, stanza):
|
||||
''' The jingle stanza dispatcher.
|
||||
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.'''
|
||||
"""
|
||||
The jingle stanza dispatcher
|
||||
|
||||
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
|
||||
jid = helpers.get_full_jid_from_iq(stanza)
|
||||
id = stanza.getID()
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
''' Handles Jingle contents (XEP 0166). '''
|
||||
|
||||
"""
|
||||
Handles Jingle contents (XEP 0166)
|
||||
"""
|
||||
|
||||
contents = {}
|
||||
|
||||
|
@ -23,7 +26,10 @@ def get_jingle_content(node):
|
|||
|
||||
|
||||
class JingleContent(object):
|
||||
''' An abstraction of content in Jingle sessions. '''
|
||||
"""
|
||||
An abstraction of content in Jingle sessions
|
||||
"""
|
||||
|
||||
def __init__(self, session, transport):
|
||||
self.session = session
|
||||
self.transport = transport
|
||||
|
@ -71,24 +77,32 @@ class JingleContent(object):
|
|||
return (self.accepted and not self.sent)
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
for callback in self.callbacks[action]:
|
||||
callback(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(
|
||||
content.getTag('transport'))
|
||||
if candidates:
|
||||
self.add_remote_candidates(candidates)
|
||||
|
||||
def __content(self, payload=[]):
|
||||
''' Build a XML content-wrapper for our data. '''
|
||||
"""
|
||||
Build a XML content-wrapper for our data
|
||||
"""
|
||||
return xmpp.Node('content',
|
||||
attrs={'name': self.name, 'creator': self.creator},
|
||||
payload=payload)
|
||||
|
@ -99,7 +113,9 @@ class JingleContent(object):
|
|||
self.session.send_transport_info(content)
|
||||
|
||||
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.sent = True
|
||||
content.addChild(node=self.transport.make_transport())
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
''' Handles Jingle RTP sessions (XEP 0167). '''
|
||||
|
||||
"""
|
||||
Handles Jingle RTP sessions (XEP 0167)
|
||||
"""
|
||||
|
||||
import gobject
|
||||
|
||||
|
@ -23,7 +25,9 @@ from jingle_content import contents, JingleContent
|
|||
|
||||
# TODO: Will that be even used?
|
||||
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:
|
||||
factory = gst.element_factory_find(name)
|
||||
if factory:
|
||||
|
@ -70,7 +74,7 @@ class JingleRTPContent(JingleContent):
|
|||
self.p2psession = self.conference.new_session(self.farsight_media)
|
||||
|
||||
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
|
||||
# due to bad controlling-mode
|
||||
params = {'controlling-mode': self.session.weinitiate,# 'debug': False}
|
||||
|
@ -85,14 +89,14 @@ class JingleRTPContent(JingleContent):
|
|||
|
||||
def 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!
|
||||
if self.sent:
|
||||
self.p2pstream.set_remote_candidates(candidates)
|
||||
|
||||
def batch_dtmf(self, events):
|
||||
if self._dtmf_running:
|
||||
raise Exception #TODO: Proper exception
|
||||
raise Exception # TODO: Proper exception
|
||||
self._dtmf_running = True
|
||||
self._start_dtmf(events.pop(0))
|
||||
gobject.timeout_add(500, self._next_dtmf, events)
|
||||
|
@ -143,7 +147,7 @@ class JingleRTPContent(JingleContent):
|
|||
elif name == 'farsight-codecs-changed':
|
||||
if self.is_ready():
|
||||
self.session.on_session_state_changed(self)
|
||||
#TODO: description-info
|
||||
# TODO: description-info
|
||||
elif name == 'farsight-local-candidates-prepared':
|
||||
self.candidates_ready = True
|
||||
if self.is_ready():
|
||||
|
@ -152,7 +156,7 @@ class JingleRTPContent(JingleContent):
|
|||
candidate = message.structure['candidate']
|
||||
self.transport.candidates.append(candidate)
|
||||
if self.candidates_ready:
|
||||
#FIXME: Is this case even possible?
|
||||
# FIXME: Is this case even possible?
|
||||
self.send_candidate(candidate)
|
||||
elif name == 'farsight-component-state-changed':
|
||||
state = message.structure['state']
|
||||
|
@ -173,7 +177,7 @@ class JingleRTPContent(JingleContent):
|
|||
if self.transport.remote_candidates:
|
||||
self.p2pstream.set_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.session.content_negociated(self.media)
|
||||
|
||||
|
@ -195,7 +199,7 @@ class JingleRTPContent(JingleContent):
|
|||
codecs.append(c)
|
||||
|
||||
if len(codecs) > 0:
|
||||
#FIXME: Handle this case:
|
||||
# FIXME: Handle this case:
|
||||
# glib.GError: There was no intersection between the remote codecs and
|
||||
# the local ones
|
||||
self.p2pstream.set_remote_codecs(codecs)
|
||||
|
@ -228,14 +232,15 @@ class JingleRTPContent(JingleContent):
|
|||
|
||||
|
||||
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):
|
||||
JingleRTPContent.__init__(self, session, 'audio', transport)
|
||||
self.setup_stream()
|
||||
|
||||
|
||||
''' Things to control the gstreamer's pipeline '''
|
||||
def setup_stream(self):
|
||||
JingleRTPContent.setup_stream(self)
|
||||
|
||||
|
@ -283,9 +288,8 @@ class JingleVideo(JingleRTPContent):
|
|||
JingleRTPContent.__init__(self, session, 'video', transport)
|
||||
self.setup_stream()
|
||||
|
||||
''' Things to control the gstreamer's pipeline '''
|
||||
def setup_stream(self):
|
||||
#TODO: Everything is not working properly:
|
||||
# TODO: Everything is not working properly:
|
||||
# sometimes, one window won't show up,
|
||||
# sometimes it'll freeze...
|
||||
JingleRTPContent.setup_stream(self)
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
''' Handles Jingle sessions (XEP 0166). '''
|
||||
|
||||
"""
|
||||
Handles Jingle sessions (XEP 0166)
|
||||
"""
|
||||
|
||||
#TODO:
|
||||
# * Have JingleContent here
|
||||
|
@ -29,25 +32,38 @@ import xmpp
|
|||
from jingle_transport import get_jingle_transport
|
||||
from jingle_content import get_jingle_content
|
||||
|
||||
#FIXME: Move it to JingleSession.States?
|
||||
# FIXME: Move it to JingleSession.States?
|
||||
class JingleStates(object):
|
||||
''' States in which jingle session may exist. '''
|
||||
"""
|
||||
States in which jingle session may exist
|
||||
"""
|
||||
ended = 0
|
||||
pending = 1
|
||||
active = 2
|
||||
|
||||
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):
|
||||
''' 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):
|
||||
''' This represents one jingle session. '''
|
||||
"""
|
||||
This represents one jingle session
|
||||
"""
|
||||
|
||||
def __init__(self, con, weinitiate, jid, sid=None):
|
||||
''' con -- connection object,
|
||||
weinitiate -- boolean, are we the initiator?
|
||||
jid - jid of the other entity'''
|
||||
"""
|
||||
con -- connection object,
|
||||
weinitiate -- boolean, are we the initiator?
|
||||
jid - jid of the other entity
|
||||
"""
|
||||
self.contents = {} # negotiated contents
|
||||
self.connection = con # connection to use
|
||||
# our full jid
|
||||
|
@ -97,15 +113,16 @@ class JingleSession(object):
|
|||
'iq-error': [self.__errorCB],
|
||||
}
|
||||
|
||||
''' Interaction with user '''
|
||||
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()
|
||||
|
||||
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.addChild('decline')
|
||||
self._session_terminate(reason)
|
||||
|
@ -125,7 +142,9 @@ class JingleSession(object):
|
|||
self.on_session_state_changed()
|
||||
|
||||
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')
|
||||
if self.state == JingleStates.active:
|
||||
reason.addChild('success')
|
||||
|
@ -133,8 +152,6 @@ class JingleSession(object):
|
|||
reason.addChild('cancel')
|
||||
self._session_terminate(reason)
|
||||
|
||||
''' Middle-level functions to manage contents. Handle local content
|
||||
cache and send change notifications. '''
|
||||
def get_content(self, media=None):
|
||||
if media is None:
|
||||
return None
|
||||
|
@ -144,9 +161,12 @@ class JingleSession(object):
|
|||
return content
|
||||
|
||||
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.
|
||||
Creator must be one of ('we', 'peer', 'initiator', 'responder')'''
|
||||
"""
|
||||
Add new content to session. If the session is active, this will send
|
||||
proper stanza to update session
|
||||
|
||||
Creator must be one of ('we', 'peer', 'initiator', 'responder')
|
||||
"""
|
||||
assert creator in ('we', 'peer', 'initiator', 'responder')
|
||||
|
||||
if (creator == 'we' and self.weinitiate) or (creator == 'peer' and \
|
||||
|
@ -164,7 +184,9 @@ class JingleSession(object):
|
|||
content.accepted = True
|
||||
|
||||
def remove_content(self, creator, name):
|
||||
''' We do not need this now '''
|
||||
"""
|
||||
We do not need this now
|
||||
"""
|
||||
#TODO:
|
||||
if (creator, name) in self.contents:
|
||||
content = self.contents[(creator, name)]
|
||||
|
@ -175,7 +197,9 @@ class JingleSession(object):
|
|||
self.end_session()
|
||||
|
||||
def modify_content(self, creator, name, *someother):
|
||||
''' We do not need this now '''
|
||||
"""
|
||||
We do not need this now
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_session_state_changed(self, content=None):
|
||||
|
@ -203,19 +227,23 @@ class JingleSession(object):
|
|||
self.__content_accept(content)
|
||||
|
||||
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()))
|
||||
and self.accepted)
|
||||
|
||||
''' Middle-level function to do stanza exchange. '''
|
||||
def accept_session(self):
|
||||
''' Mark the session as accepted. '''
|
||||
"""
|
||||
Mark the session as accepted
|
||||
"""
|
||||
self.accepted = True
|
||||
self.on_session_state_changed()
|
||||
|
||||
def start_session(self):
|
||||
''' Mark the session as ready to be started. '''
|
||||
"""
|
||||
Mark the session as ready to be started
|
||||
"""
|
||||
self.accepted = True
|
||||
self.on_session_state_changed()
|
||||
|
||||
|
@ -234,11 +262,12 @@ class JingleSession(object):
|
|||
jingle.addChild(node=content)
|
||||
self.connection.connection.send(stanza)
|
||||
|
||||
''' Session callbacks. '''
|
||||
def stanzaCB(self, stanza):
|
||||
''' A callback for ConnectionJingle. It gets stanza, then
|
||||
tries to send it to all internally registered callbacks.
|
||||
First one to raise xmpp.NodeProcessed breaks function.'''
|
||||
"""
|
||||
A callback for ConnectionJingle. It gets stanza, then tries to send it to
|
||||
all internally registered callbacks. First one to raise
|
||||
xmpp.NodeProcessed breaks function
|
||||
"""
|
||||
jingle = stanza.getTag('jingle')
|
||||
error = stanza.getTag('error')
|
||||
if error:
|
||||
|
@ -250,7 +279,7 @@ class JingleSession(object):
|
|||
if action not in self.callbacks:
|
||||
self.__send_error(stanza, 'bad_request')
|
||||
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:
|
||||
self.__send_error(stanza, 'item-not-found', 'unknown-session')
|
||||
return
|
||||
|
@ -268,16 +297,18 @@ class JingleSession(object):
|
|||
except TieBreak:
|
||||
self.__send_error(stanza, 'conflict', 'tiebreak')
|
||||
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):
|
||||
''' Default callback for action stanzas -- simple ack
|
||||
and stop processing. '''
|
||||
"""
|
||||
Default callback for action stanzas -- simple ack and stop processing
|
||||
"""
|
||||
response = stanza.buildReply('result')
|
||||
self.connection.connection.send(response)
|
||||
|
||||
def __errorCB(self, stanza, jingle, error, action):
|
||||
#FIXME
|
||||
# FIXME
|
||||
text = error.getTagData('text')
|
||||
jingle_error = None
|
||||
xmpp_error = None
|
||||
|
@ -287,7 +318,7 @@ class JingleSession(object):
|
|||
elif child.getNamespace() == xmpp.NS_STANZAS:
|
||||
xmpp_error = child.getName()
|
||||
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':
|
||||
self.connection.delete_jingle_session(self.peerjid, self.sid)
|
||||
|
||||
|
@ -298,9 +329,9 @@ class JingleSession(object):
|
|||
if (creator, name) in self.contents:
|
||||
transport_ns = content.getTag('transport').getNamespace()
|
||||
if transport_ns == xmpp.JINGLE_ICE_UDP:
|
||||
#FIXME: We don't manage anything else than ICE-UDP now...
|
||||
#What was the previous transport?!?
|
||||
#Anyway, content's transport is not modifiable yet
|
||||
# FIXME: We don't manage anything else than ICE-UDP now...
|
||||
# What was the previous transport?!?
|
||||
# Anyway, content's transport is not modifiable yet
|
||||
pass
|
||||
else:
|
||||
stanza, jingle = self.__make_jingle('transport-reject')
|
||||
|
@ -310,8 +341,8 @@ class JingleSession(object):
|
|||
self.connection.connection.send(stanza)
|
||||
raise xmpp.NodeProcessed
|
||||
else:
|
||||
#FIXME: This ressource is unknown to us, what should we do?
|
||||
#For now, reject the transport
|
||||
# FIXME: This ressource is unknown to us, what should we do?
|
||||
# For now, reject the transport
|
||||
stanza, jingle = self.__make_jingle('transport-reject')
|
||||
c = jingle.setTag('content', attrs={'creator': creator,
|
||||
'name': name})
|
||||
|
@ -320,7 +351,7 @@ class JingleSession(object):
|
|||
raise xmpp.NodeProcessed
|
||||
|
||||
def __sessionInfoCB(self, stanza, jingle, error, action):
|
||||
#TODO: ringing, active, (un)hold, (un)mute
|
||||
# TODO: ringing, active, (un)hold, (un)mute
|
||||
payload = jingle.getPayload()
|
||||
if len(payload) > 0:
|
||||
self.__send_error(stanza, 'feature-not-implemented', 'unsupported-info')
|
||||
|
@ -332,7 +363,7 @@ class JingleSession(object):
|
|||
name = content['name']
|
||||
if (creator, name) in self.contents:
|
||||
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.peerjid, self.sid, content.media, 'removed'))
|
||||
content.destroy()
|
||||
|
@ -342,17 +373,21 @@ class JingleSession(object):
|
|||
self._session_terminate(reason)
|
||||
|
||||
def __sessionAcceptCB(self, stanza, jingle, error, action):
|
||||
if self.state != JingleStates.pending: #FIXME
|
||||
# FIXME
|
||||
if self.state != JingleStates.pending:
|
||||
raise OutOfOrder
|
||||
self.state = JingleStates.active
|
||||
|
||||
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
|
||||
for content in jingle.iterTags('content'):
|
||||
creator = content['creator']
|
||||
name = content['name']#TODO...
|
||||
# TODO
|
||||
name = content['name']
|
||||
|
||||
def __contentAddCB(self, stanza, jingle, error, action):
|
||||
if self.state == JingleStates.ended:
|
||||
|
@ -363,7 +398,7 @@ class JingleSession(object):
|
|||
rejected_contents = parse_result[3]
|
||||
|
||||
for name, creator in rejected_contents:
|
||||
#TODO:
|
||||
# TODO
|
||||
content = JingleContent()
|
||||
self.add_content(name, content, creator)
|
||||
self.__content_reject(content)
|
||||
|
@ -373,10 +408,10 @@ class JingleSession(object):
|
|||
contents))
|
||||
|
||||
def __sessionInitiateCB(self, stanza, jingle, error, action):
|
||||
''' We got a jingle session request from other entity,
|
||||
therefore we are the receiver... Unpack the data,
|
||||
inform the user. '''
|
||||
|
||||
"""
|
||||
We got a jingle session request from other entity, therefore we are the
|
||||
receiver... Unpack the data, inform the user
|
||||
"""
|
||||
if self.state != JingleStates.ended:
|
||||
raise OutOfOrder
|
||||
|
||||
|
@ -417,7 +452,9 @@ class JingleSession(object):
|
|||
contents))
|
||||
|
||||
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'):
|
||||
name = content['name']
|
||||
creator = content['creator']
|
||||
|
@ -437,11 +474,12 @@ class JingleSession(object):
|
|||
(self.peerjid, self.sid, None, text))
|
||||
|
||||
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():
|
||||
content.stanzaCB(stanza, None, error, action)
|
||||
|
||||
''' Internal methods. '''
|
||||
def __parse_contents(self, jingle):
|
||||
#TODO: Needs some reworking
|
||||
contents = []
|
||||
|
@ -492,8 +530,6 @@ class JingleSession(object):
|
|||
break
|
||||
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):
|
||||
stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.peerjid))
|
||||
attrs = {'action': action,
|
||||
|
@ -516,14 +552,17 @@ class JingleSession(object):
|
|||
self.__dispatch_error(error, jingle_error, text)
|
||||
|
||||
def __append_content(self, jingle, content):
|
||||
''' Append <content/> element to <jingle/> element,
|
||||
with (full=True) or without (full=False) <content/>
|
||||
children. '''
|
||||
"""
|
||||
Append <content/> element to <jingle/> element, with (full=True) or
|
||||
without (full=False) <content/> children
|
||||
"""
|
||||
jingle.addChild('content',
|
||||
attrs={'name': content.name, 'creator': content.creator})
|
||||
|
||||
def __append_contents(self, jingle):
|
||||
''' Append all <content/> elements to <jingle/>.'''
|
||||
"""
|
||||
Append all <content/> elements to <jingle/>
|
||||
"""
|
||||
# TODO: integrate with __appendContent?
|
||||
# TODO: parameters 'name', 'content'?
|
||||
for content in self.contents.values():
|
||||
|
@ -571,7 +610,7 @@ class JingleSession(object):
|
|||
(self.peerjid, self.sid, None, text))
|
||||
|
||||
def __content_add(self, content):
|
||||
#TODO: test
|
||||
# TODO: test
|
||||
assert self.state != JingleStates.ended
|
||||
stanza, jingle = self.__make_jingle('content-add')
|
||||
self.__append_content(jingle, content)
|
||||
|
@ -579,7 +618,7 @@ class JingleSession(object):
|
|||
self.connection.connection.send(stanza)
|
||||
|
||||
def __content_accept(self, content):
|
||||
#TODO: test
|
||||
# TODO: test
|
||||
assert self.state != JingleStates.ended
|
||||
stanza, jingle = self.__make_jingle('content-accept')
|
||||
self.__append_content(jingle, content)
|
||||
|
@ -591,7 +630,7 @@ class JingleSession(object):
|
|||
stanza, jingle = self.__make_jingle('content-reject')
|
||||
self.__append_content(jingle, content)
|
||||
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.peerjid, self.sid, content.media, 'rejected'))
|
||||
|
||||
|
@ -603,7 +642,7 @@ class JingleSession(object):
|
|||
stanza, jingle = self.__make_jingle('content-remove')
|
||||
self.__append_content(jingle, content)
|
||||
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.peerjid, self.sid, content.media, 'removed'))
|
||||
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
''' Handles Jingle Transports (currently only ICE-UDP). '''
|
||||
|
||||
"""
|
||||
Handles Jingle Transports (currently only ICE-UDP)
|
||||
"""
|
||||
|
||||
import xmpp
|
||||
|
||||
|
@ -25,13 +28,18 @@ def get_jingle_transport(node):
|
|||
|
||||
|
||||
class TransportType(object):
|
||||
''' Possible types of a JingleTransport '''
|
||||
"""
|
||||
Possible types of a JingleTransport
|
||||
"""
|
||||
datagram = 1
|
||||
streaming = 2
|
||||
|
||||
|
||||
class JingleTransport(object):
|
||||
''' An abstraction of a transport in Jingle sessions. '''
|
||||
"""
|
||||
An abstraction of a transport in Jingle sessions
|
||||
"""
|
||||
|
||||
def __init__(self, type_):
|
||||
self.type = type_
|
||||
self.candidates = []
|
||||
|
@ -42,19 +50,25 @@ class JingleTransport(object):
|
|||
yield self.make_candidate(candidate)
|
||||
|
||||
def make_candidate(self, candidate):
|
||||
''' Build a candidate stanza for the given candidate. '''
|
||||
"""
|
||||
Build a candidate stanza for the given candidate
|
||||
"""
|
||||
pass
|
||||
|
||||
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:
|
||||
candidates = self._iter_candidates()
|
||||
transport = xmpp.Node('transport', payload=candidates)
|
||||
return 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 []
|
||||
|
||||
|
||||
|
@ -132,4 +146,3 @@ class JingleTransportICEUDP(JingleTransport):
|
|||
return candidates
|
||||
|
||||
transports[xmpp.NS_JINGLE_ICE_UDP] = JingleTransportICEUDP
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ import subprocess
|
|||
|
||||
|
||||
def kwallet_available():
|
||||
"""Return True if kwalletcli can be run, False otherwise."""
|
||||
"""
|
||||
Return True if kwalletcli can be run, False otherwise
|
||||
"""
|
||||
try:
|
||||
p = subprocess.Popen(["kwalletcli", "-qV"])
|
||||
except Exception:
|
||||
|
@ -37,7 +39,8 @@ def kwallet_available():
|
|||
|
||||
|
||||
def kwallet_get(folder, entry):
|
||||
"""Retrieve a passphrase from the KDE Wallet via kwalletcli.
|
||||
"""
|
||||
Retrieve a passphrase from the KDE Wallet via kwalletcli
|
||||
|
||||
Arguments:
|
||||
• 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,
|
||||
or None if an error occured.
|
||||
|
||||
"""
|
||||
p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'),
|
||||
"-e", entry.encode('utf-8')], stdout=subprocess.PIPE)
|
||||
|
@ -60,7 +62,8 @@ def kwallet_get(folder, entry):
|
|||
|
||||
|
||||
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:
|
||||
• 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
|
||||
|
||||
Returns True on success, False otherwise.
|
||||
|
||||
"""
|
||||
p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'),
|
||||
"-e", entry.encode('utf-8'), "-P"], stdin=subprocess.PIPE)
|
||||
|
|
|
@ -86,8 +86,9 @@ def popen_nt_friendly(command):
|
|||
return Popen(command, cwd=gettempdir(), stdout=PIPE)
|
||||
|
||||
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:
|
||||
filename = latex_to_image("test")
|
||||
if filename:
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
## 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 sys
|
||||
|
@ -150,7 +152,9 @@ class Logger:
|
|||
self.get_jids_already_in_db()
|
||||
|
||||
def simple_commit(self, sql_to_commit):
|
||||
'''helper to commit'''
|
||||
"""
|
||||
Helper to commit
|
||||
"""
|
||||
self.cur.execute(sql_to_commit)
|
||||
try:
|
||||
self.con.commit()
|
||||
|
@ -177,14 +181,14 @@ class Logger:
|
|||
return self.jids_already_in
|
||||
|
||||
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?
|
||||
we ask if gajim@conf is already in jids (with type room jid)
|
||||
this fails if user disables logging for room and only enables for
|
||||
pm (so higly unlikely) and if we fail we do not go chaos
|
||||
(user will see the first pm as if it was message in room's public chat)
|
||||
and after that all okay'''
|
||||
|
||||
"""
|
||||
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? we ask if gajim@conf
|
||||
is already in jids (with type room jid) this fails if user disables
|
||||
logging for room and only enables for pm (so higly unlikely) and if we
|
||||
fail we do not go chaos (user will see the first pm as if it was message
|
||||
in room's public chat) and after that all okay
|
||||
"""
|
||||
if jid.find('/') > -1:
|
||||
possible_room_jid = jid.split('/', 1)[0]
|
||||
return self.jid_is_room_jid(possible_room_jid)
|
||||
|
@ -202,13 +206,14 @@ class Logger:
|
|||
return True
|
||||
|
||||
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
|
||||
so to ask logs we need jid_id that matches our jid in jids table
|
||||
this method wants jid and returns the jid_id for later sql-ing on logs
|
||||
typestr can be 'ROOM' or anything else depending on the type of JID
|
||||
and is only needed to be specified when the JID is new in DB
|
||||
'''
|
||||
"""
|
||||
jids table has jid and jid_id logs table has log_id, jid_id,
|
||||
contact_name, time, kind, show, message so to ask logs we need jid_id
|
||||
that matches our jid in jids table this method wants jid and returns the
|
||||
jid_id for later sql-ing on logs typestr can be 'ROOM' or anything else
|
||||
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 /
|
||||
jid_is_from_pm = self.jid_is_from_pm(jid)
|
||||
if not jid_is_from_pm: # it's normal jid with resource
|
||||
|
@ -238,7 +243,9 @@ class Logger:
|
|||
return jid_id
|
||||
|
||||
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':
|
||||
kind_col = constants.KIND_STATUS
|
||||
elif kind == 'gcstatus':
|
||||
|
@ -277,7 +284,9 @@ class Logger:
|
|||
return kind_col, show_col
|
||||
|
||||
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':
|
||||
return constants.TYPE_AIM
|
||||
if type_ == 'gadu-gadu':
|
||||
|
@ -309,7 +318,9 @@ class Logger:
|
|||
return None
|
||||
|
||||
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:
|
||||
return 'aim'
|
||||
if type_id == constants.TYPE_GG:
|
||||
|
@ -340,7 +351,9 @@ class Logger:
|
|||
return 'mrim'
|
||||
|
||||
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':
|
||||
return constants.SUBSCRIPTION_NONE
|
||||
if sub == 'to':
|
||||
|
@ -351,7 +364,9 @@ class Logger:
|
|||
return constants.SUBSCRIPTION_BOTH
|
||||
|
||||
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:
|
||||
return 'none'
|
||||
if sub == constants.SUBSCRIPTION_TO:
|
||||
|
@ -382,30 +397,40 @@ class Logger:
|
|||
return message_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,
|
||||
jid_id)
|
||||
self.simple_commit(sql)
|
||||
|
||||
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])
|
||||
sql = 'DELETE FROM unread_messages WHERE message_id IN (%s)' % ids
|
||||
self.simple_commit(sql)
|
||||
|
||||
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' % \
|
||||
msg_id
|
||||
self.simple_commit(sql)
|
||||
|
||||
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'
|
||||
self.simple_commit(sql)
|
||||
|
||||
def get_unread_msgs(self):
|
||||
''' get all unread messages '''
|
||||
"""
|
||||
Get all unread messages
|
||||
"""
|
||||
all_messages = []
|
||||
try:
|
||||
self.cur.execute(
|
||||
|
@ -435,15 +460,18 @@ class Logger:
|
|||
return all_messages
|
||||
|
||||
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),
|
||||
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()
|
||||
"""
|
||||
Write a row (status, gcstatus, message etc) to logs database
|
||||
|
||||
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.'''
|
||||
kind can be status, gcstatus, gc_msg, (we only recv for those 3),
|
||||
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:
|
||||
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
|
||||
self.open_db()
|
||||
|
@ -516,12 +544,14 @@ class Logger:
|
|||
return self.commit_to_db(values, write_unread)
|
||||
|
||||
def get_last_conversation_lines(self, jid, restore_how_many_rows,
|
||||
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
|
||||
and are already logged but pending to be viewed,
|
||||
returns a list of tupples containg time, kind, message,
|
||||
list with empty tupple if nothing found to meet our demands'''
|
||||
pending_how_many, timeout, account):
|
||||
"""
|
||||
Accept 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 and are
|
||||
already logged but pending to be viewed, returns a list of tupples
|
||||
containg time, kind, message, list with empty tupple if nothing found to
|
||||
meet our demands
|
||||
"""
|
||||
try:
|
||||
self.get_jid_id(jid)
|
||||
except exceptions.PysqliteOperationalError, e:
|
||||
|
@ -561,9 +591,12 @@ class Logger:
|
|||
return start_of_day
|
||||
|
||||
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,
|
||||
returns list with empty tupple if we found nothing to meet our demands'''
|
||||
"""
|
||||
Return contact_name, time, kind, show, message, subject
|
||||
|
||||
For each row in a list of tupples, returns list with empty tupple if we
|
||||
found nothing to meet our demands
|
||||
"""
|
||||
try:
|
||||
self.get_jid_id(jid)
|
||||
except exceptions.PysqliteOperationalError, e:
|
||||
|
@ -586,9 +619,12 @@ class Logger:
|
|||
return results
|
||||
|
||||
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 list with empty tupple if we found nothing to meet our demands'''
|
||||
"""
|
||||
Returns contact_name, time, kind, show, message
|
||||
|
||||
For each row in a list of tupples, returns list with empty tupple if we
|
||||
found nothing to meet our demands
|
||||
"""
|
||||
try:
|
||||
self.get_jid_id(jid)
|
||||
except exceptions.PysqliteOperationalError, e:
|
||||
|
@ -615,7 +651,9 @@ class Logger:
|
|||
return results
|
||||
|
||||
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:
|
||||
self.get_jid_id(jid)
|
||||
except exceptions.PysqliteOperationalError, e:
|
||||
|
@ -650,8 +688,10 @@ class Logger:
|
|||
return days_with_logs
|
||||
|
||||
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 = ''
|
||||
if not is_room:
|
||||
where_sql = self._build_contact_where(account, jid)
|
||||
|
@ -676,8 +716,10 @@ class Logger:
|
|||
return result
|
||||
|
||||
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:
|
||||
jid_id = self.get_jid_id(jid, 'ROOM')
|
||||
except exceptions.PysqliteOperationalError, e:
|
||||
|
@ -697,8 +739,10 @@ class Logger:
|
|||
return result
|
||||
|
||||
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 is unique in this table, create or update :
|
||||
sql = 'REPLACE INTO rooms_last_message_time VALUES (%d, %d)' % \
|
||||
|
@ -706,8 +750,9 @@ class Logger:
|
|||
self.simple_commit(sql)
|
||||
|
||||
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 = ''
|
||||
# will return empty list if jid is not associated with
|
||||
# any metacontacts
|
||||
|
@ -727,7 +772,9 @@ class Logger:
|
|||
return where_sql
|
||||
|
||||
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_)
|
||||
if not type_id:
|
||||
# unknown type
|
||||
|
@ -747,7 +794,9 @@ class Logger:
|
|||
self.simple_commit(sql)
|
||||
|
||||
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(
|
||||
'SELECT * from transports_cache')
|
||||
results = self.cur.fetchall()
|
||||
|
@ -767,11 +816,13 @@ class Logger:
|
|||
# (2)
|
||||
# GzipFile needs a file-like object, StringIO emulates file for plain strings
|
||||
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):
|
||||
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
|
||||
# the data field contains binary object (gzipped data), this is a hack
|
||||
# to get that data without trying to convert it to unicode
|
||||
|
@ -854,16 +905,21 @@ class Logger:
|
|||
self.simple_commit(sql)
|
||||
|
||||
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''' % \
|
||||
int(time.time() - 3*30*24*3600)
|
||||
self.simple_commit(sql)
|
||||
|
||||
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
|
||||
roster_version is the version of the new roster
|
||||
roster is the new version '''
|
||||
"""
|
||||
Replace current roster in DB by a new one
|
||||
|
||||
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
|
||||
# sends back all the roster at the next connexion if the replacement
|
||||
# didn't work properly.
|
||||
|
@ -887,7 +943,9 @@ class Logger:
|
|||
roster_version)
|
||||
|
||||
def del_contact(self, account_jid, jid):
|
||||
''' Remove jid from account_jid roster. '''
|
||||
"""
|
||||
Remove jid from account_jid roster
|
||||
"""
|
||||
try:
|
||||
account_jid_id = self.get_jid_id(account_jid)
|
||||
jid_id = self.get_jid_id(jid)
|
||||
|
@ -902,7 +960,9 @@ class Logger:
|
|||
self.con.commit()
|
||||
|
||||
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':
|
||||
self.del_contact(account_jid, jid)
|
||||
return
|
||||
|
@ -933,7 +993,9 @@ class Logger:
|
|||
self.con.commit()
|
||||
|
||||
def get_roster(self, account_jid):
|
||||
''' Return the accound_jid roster in NonBlockingRoster format. '''
|
||||
"""
|
||||
Return the accound_jid roster in NonBlockingRoster format
|
||||
"""
|
||||
data = {}
|
||||
account_jid_id = self.get_jid_id(account_jid)
|
||||
|
||||
|
@ -972,7 +1034,9 @@ class Logger:
|
|||
return data
|
||||
|
||||
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)
|
||||
|
||||
self.cur.execute('DELETE FROM roster_entry WHERE account_jid_id=?',
|
||||
|
|
|
@ -23,7 +23,7 @@ import i18n
|
|||
|
||||
def parseLogLevel(arg):
|
||||
"""
|
||||
eiter numeric value or level name from logging module
|
||||
Eiter numeric value or level name from logging module
|
||||
"""
|
||||
if arg.isdigit():
|
||||
return int(arg)
|
||||
|
@ -98,7 +98,7 @@ def colorize(text, color):
|
|||
|
||||
class FancyFormatter(logging.Formatter):
|
||||
"""
|
||||
A Eye-candy formatter with colors
|
||||
An eye-candy formatter with colors
|
||||
"""
|
||||
colors_mapping = {
|
||||
'DEBUG': colors.BLUE,
|
||||
|
@ -134,7 +134,7 @@ class FancyFormatter(logging.Formatter):
|
|||
|
||||
def init(use_color=False):
|
||||
"""
|
||||
initialize the logging system
|
||||
Iinitialize the logging system
|
||||
"""
|
||||
consoleloghandler = logging.StreamHandler()
|
||||
consoleloghandler.setFormatter(
|
||||
|
|
|
@ -228,7 +228,9 @@ class OptionsParser:
|
|||
caps.capscache.initialize_from_db()
|
||||
|
||||
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()
|
||||
os.chdir(logger.LOG_DB_FOLDER)
|
||||
con = sqlite.connect(logger.LOG_DB_FILE)
|
||||
|
@ -346,8 +348,10 @@ class OptionsParser:
|
|||
gajim.config.set('version', '0.10.1.2')
|
||||
|
||||
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()
|
||||
os.chdir(logger.LOG_DB_FOLDER)
|
||||
con = sqlite.connect(logger.LOG_DB_FILE)
|
||||
|
@ -369,9 +373,11 @@ class OptionsParser:
|
|||
gajim.config.set('version', '0.10.1.3')
|
||||
|
||||
def update_config_to_01014(self):
|
||||
'''apply indeces to the logs database'''
|
||||
"""
|
||||
Apply indeces to the logs database
|
||||
"""
|
||||
print _('migrating logs database to indices')
|
||||
#FIXME see #2812
|
||||
# FIXME see #2812
|
||||
back = os.getcwd()
|
||||
os.chdir(logger.LOG_DB_FOLDER)
|
||||
con = sqlite.connect(logger.LOG_DB_FILE)
|
||||
|
@ -393,7 +399,9 @@ class OptionsParser:
|
|||
gajim.config.set('version', '0.10.1.4')
|
||||
|
||||
def update_config_to_01015(self):
|
||||
'''clean show values in logs database'''
|
||||
"""
|
||||
Clean show values in logs database
|
||||
"""
|
||||
#FIXME see #2812
|
||||
back = os.getcwd()
|
||||
os.chdir(logger.LOG_DB_FOLDER)
|
||||
|
@ -412,8 +420,10 @@ class OptionsParser:
|
|||
gajim.config.set('version', '0.10.1.5')
|
||||
|
||||
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 \
|
||||
self.old_values['notify_on_all_muc_messages'] == 'False' and \
|
||||
gajim.config.get_per('soundevents', 'muc_message_received', 'enabled'):
|
||||
|
@ -422,36 +432,45 @@ class OptionsParser:
|
|||
gajim.config.set('version', '0.10.1.6')
|
||||
|
||||
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:
|
||||
gajim.config.set('trayicon_notification_on_events',
|
||||
self.old_values['trayicon_notification_on_new_messages'])
|
||||
gajim.config.set('version', '0.10.1.7')
|
||||
|
||||
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:
|
||||
gajim.config.set('outgoing_chat_state_notifications',
|
||||
self.old_values['chat_state_notifications'])
|
||||
gajim.config.set('version', '0.10.1.8')
|
||||
|
||||
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:
|
||||
gajim.config.set('time_stamp', '%s%%X%s ' % (
|
||||
self.old_values['before_time'], self.old_values['after_time']))
|
||||
gajim.config.set('version', '0.11.0.1')
|
||||
|
||||
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:
|
||||
gajim.config.set('ft_add_hosts_to_send',
|
||||
self.old_values['ft_override_host_to_send'])
|
||||
gajim.config.set('version', '0.11.0.2')
|
||||
|
||||
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 \
|
||||
'always_hide_chat_buttons' in self.old_values:
|
||||
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')
|
||||
|
||||
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 \
|
||||
self.old_values['roster_theme'] == 'gtk+':
|
||||
gajim.config.set('roster_theme', _('default'))
|
||||
|
@ -568,7 +589,9 @@ class OptionsParser:
|
|||
gajim.config.set('version', '0.11.4.1')
|
||||
|
||||
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_unfocused')
|
||||
if gajim.config.get_per('soundevents', 'next_message_received'):
|
||||
|
@ -681,7 +704,9 @@ class OptionsParser:
|
|||
gajim.config.set('version', '0.12.1.4')
|
||||
|
||||
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)
|
||||
for evt in gajim.config.get_per('soundevents'):
|
||||
path = gajim.config.get_per('soundevents', evt ,'path')
|
||||
|
|
|
@ -206,64 +206,64 @@ import gtkgui_helpers
|
|||
|
||||
|
||||
class AbstractPEP(object):
|
||||
|
||||
|
||||
type = ''
|
||||
namespace = ''
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_tag_as_PEP(cls, jid, account, event_tag):
|
||||
items = event_tag.getTag('items', {'node': cls.namespace})
|
||||
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)
|
||||
else:
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
def __init__(self, jid, account, items):
|
||||
self._pep_specific_data, self._retracted = self._extract_info(items)
|
||||
|
||||
|
||||
self._update_contacts(jid, account)
|
||||
if jid == gajim.get_jid_from_account(account):
|
||||
self._update_account(account)
|
||||
|
||||
|
||||
def _extract_info(self, items):
|
||||
'''To be implemented by subclasses'''
|
||||
raise NotImplementedError
|
||||
|
||||
def _update_contacts(self, jid, account):
|
||||
for contact in gajim.contacts.get_contacts(account, jid):
|
||||
|
||||
def _update_contacts(self, jid, account):
|
||||
for contact in gajim.contacts.get_contacts(account, jid):
|
||||
if self._retracted:
|
||||
if self.type in contact.pep:
|
||||
del contact.pep[self.type]
|
||||
else:
|
||||
contact.pep[self.type] = self
|
||||
|
||||
def _update_account(self, account):
|
||||
acc = gajim.connections[account]
|
||||
|
||||
def _update_account(self, account):
|
||||
acc = gajim.connections[account]
|
||||
if self._retracted:
|
||||
if self.type in acc.pep:
|
||||
del acc.pep[self.type]
|
||||
else:
|
||||
acc.pep[self.type] = self
|
||||
|
||||
|
||||
def asPixbufIcon(self):
|
||||
'''SHOULD be implemented by subclasses'''
|
||||
return None
|
||||
|
||||
|
||||
def asMarkupText(self):
|
||||
'''SHOULD be implemented by subclasses'''
|
||||
return ''
|
||||
|
||||
|
||||
|
||||
|
||||
class UserMoodPEP(AbstractPEP):
|
||||
'''XEP-0107: User Mood'''
|
||||
|
||||
|
||||
type = 'mood'
|
||||
namespace = xmpp.NS_MOOD
|
||||
|
||||
|
||||
def _extract_info(self, items):
|
||||
mood_dict = {}
|
||||
|
||||
|
||||
for item in items.getTags('item'):
|
||||
mood_tag = item.getTag('mood')
|
||||
if mood_tag:
|
||||
|
@ -273,22 +273,22 @@ class UserMoodPEP(AbstractPEP):
|
|||
mood_dict['text'] = child.getData()
|
||||
else:
|
||||
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)
|
||||
|
||||
|
||||
def asPixbufIcon(self):
|
||||
assert not self._retracted
|
||||
received_mood = self._pep_specific_data['mood']
|
||||
mood = received_mood if received_mood in MOODS else 'unknown'
|
||||
pixbuf = gtkgui_helpers.load_mood_icon(mood).get_pixbuf()
|
||||
return pixbuf
|
||||
|
||||
|
||||
def asMarkupText(self):
|
||||
assert not self._retracted
|
||||
untranslated_mood = self._pep_specific_data['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:
|
||||
text = self._pep_specific_data['text']
|
||||
markuptext += ' (%s)' % gobject.markup_escape_text(text)
|
||||
|
@ -303,13 +303,13 @@ class UserMoodPEP(AbstractPEP):
|
|||
|
||||
class UserTunePEP(AbstractPEP):
|
||||
'''XEP-0118: User Tune'''
|
||||
|
||||
|
||||
type = 'tune'
|
||||
namespace = xmpp.NS_TUNE
|
||||
|
||||
def _extract_info(self, items):
|
||||
|
||||
def _extract_info(self, items):
|
||||
tune_dict = {}
|
||||
|
||||
|
||||
for item in items.getTags('item'):
|
||||
tune_tag = item.getTag('tune')
|
||||
if tune_tag:
|
||||
|
@ -318,23 +318,23 @@ class UserTunePEP(AbstractPEP):
|
|||
data = child.getData().strip()
|
||||
if child.getName() in TUNE_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)
|
||||
return (tune_dict, retracted)
|
||||
|
||||
|
||||
def asPixbufIcon(self):
|
||||
import os
|
||||
path = os.path.join(gajim.DATA_DIR, 'emoticons', 'static', 'music.png')
|
||||
return gtk.gdk.pixbuf_new_from_file(path)
|
||||
|
||||
|
||||
def asMarkupText(self):
|
||||
assert not self._retracted
|
||||
tune = self._pep_specific_data
|
||||
|
||||
artist = tune.get('artist', _('Unknown Artist'))
|
||||
artist = gobject.markup_escape_text(artist)
|
||||
|
||||
|
||||
title = tune.get('title', _('Unknown Title'))
|
||||
title = gobject.markup_escape_text(title)
|
||||
|
||||
|
@ -345,17 +345,17 @@ class UserTunePEP(AbstractPEP):
|
|||
'from <i>%(source)s</i>') % {'title': title,
|
||||
'artist': artist, 'source': source}
|
||||
return tune_string
|
||||
|
||||
|
||||
|
||||
class UserActivityPEP(AbstractPEP):
|
||||
'''XEP-0108: User Activity'''
|
||||
|
||||
|
||||
type = 'activity'
|
||||
namespace = xmpp.NS_ACTIVITY
|
||||
|
||||
|
||||
def _extract_info(self, items):
|
||||
activity_dict = {}
|
||||
|
||||
|
||||
for item in items.getTags('item'):
|
||||
activity_tag = item.getTag('activity')
|
||||
if activity_tag:
|
||||
|
@ -369,28 +369,28 @@ class UserActivityPEP(AbstractPEP):
|
|||
for subactivity in child.getChildren():
|
||||
subactivity_name = subactivity.getName().strip()
|
||||
activity_dict['subactivity'] = subactivity_name
|
||||
|
||||
|
||||
retracted = items.getTag('retract') or not 'activity' in activity_dict
|
||||
return (activity_dict, retracted)
|
||||
|
||||
|
||||
def asPixbufIcon(self):
|
||||
assert not self._retracted
|
||||
pep = self._pep_specific_data
|
||||
activity = pep['activity']
|
||||
|
||||
|
||||
has_known_activity = activity in ACTIVITIES
|
||||
has_known_subactivity = (has_known_activity and ('subactivity' in pep)
|
||||
and (pep['subactivity'] in ACTIVITIES[activity]))
|
||||
|
||||
|
||||
if has_known_activity:
|
||||
if has_known_subactivity:
|
||||
subactivity = pep['subactivity']
|
||||
return gtkgui_helpers.load_activity_icon(activity, subactivity).get_pixbuf()
|
||||
else:
|
||||
return gtkgui_helpers.load_activity_icon(activity).get_pixbuf()
|
||||
else:
|
||||
else:
|
||||
return gtkgui_helpers.load_activity_icon('unknown').get_pixbuf()
|
||||
|
||||
|
||||
def asMarkupText(self):
|
||||
assert not self._retracted
|
||||
pep = self._pep_specific_data
|
||||
|
@ -403,45 +403,45 @@ class UserActivityPEP(AbstractPEP):
|
|||
if subactivity in ACTIVITIES[activity]:
|
||||
subactivity = ACTIVITIES[activity][subactivity]
|
||||
activity = ACTIVITIES[activity]['category']
|
||||
|
||||
|
||||
markuptext = '<b>' + gobject.markup_escape_text(activity)
|
||||
if subactivity:
|
||||
markuptext += ': ' + gobject.markup_escape_text(subactivity)
|
||||
markuptext += '</b>'
|
||||
if text:
|
||||
markuptext += ' (%s)' % gobject.markup_escape_text(text)
|
||||
return markuptext
|
||||
|
||||
|
||||
return markuptext
|
||||
|
||||
|
||||
class UserNicknamePEP(AbstractPEP):
|
||||
'''XEP-0172: User Nickname'''
|
||||
|
||||
|
||||
type = 'nickname'
|
||||
namespace = xmpp.NS_NICK
|
||||
|
||||
def _extract_info(self, items):
|
||||
|
||||
def _extract_info(self, items):
|
||||
nick = ''
|
||||
for item in items.getTags('item'):
|
||||
child = item.getTag('nick')
|
||||
if child:
|
||||
nick = child.getData()
|
||||
break
|
||||
|
||||
|
||||
retracted = items.getTag('retract') or not nick
|
||||
return (nick, retracted)
|
||||
|
||||
return (nick, retracted)
|
||||
|
||||
def _update_contacts(self, jid, account):
|
||||
nick = '' if self._retracted else self._pep_specific_data
|
||||
for contact in gajim.contacts.get_contacts(account, jid):
|
||||
contact.contact_name = nick
|
||||
|
||||
|
||||
def _update_account(self, account):
|
||||
if self._retracted:
|
||||
gajim.nicks[account] = gajim.config.get_per('accounts', account, 'name')
|
||||
else:
|
||||
gajim.nicks[account] = self._pep_specific_data
|
||||
|
||||
|
||||
|
||||
SUPPORTED_PERSONAL_USER_EVENTS = [UserMoodPEP, UserTunePEP, UserActivityPEP,
|
||||
UserNicknamePEP]
|
||||
|
||||
|
@ -451,7 +451,7 @@ class ConnectionPEP(object):
|
|||
self._account = account
|
||||
self._dispatcher = dispatcher
|
||||
self._pubsub_connection = pubsub_connection
|
||||
|
||||
|
||||
def _pubsubEventCB(self, xmpp_dispatcher, msg):
|
||||
''' Called when we receive <message /> with pubsub event. '''
|
||||
if not msg.getTag('event'):
|
||||
|
@ -459,7 +459,7 @@ class ConnectionPEP(object):
|
|||
if msg.getTag('error'):
|
||||
log.debug('PubsubEventCB received error stanza. Ignoring')
|
||||
raise xmpp.NodeProcessed
|
||||
|
||||
|
||||
jid = helpers.get_full_jid_from_iq(msg)
|
||||
event_tag = msg.getTag('event')
|
||||
|
||||
|
@ -467,7 +467,7 @@ class ConnectionPEP(object):
|
|||
pep = pep_class.get_tag_as_PEP(jid, self._account, event_tag)
|
||||
if pep:
|
||||
self._dispatcher.dispatch('PEP_RECEIVED', (jid, pep.type))
|
||||
|
||||
|
||||
items = event_tag.getTag('items')
|
||||
if items:
|
||||
for item in items.getTags('item'):
|
||||
|
@ -477,9 +477,9 @@ class ConnectionPEP(object):
|
|||
# but to be sure...
|
||||
self._dispatcher.dispatch('ATOM_ENTRY',
|
||||
(atom.OldEntry(node=entry),))
|
||||
|
||||
|
||||
raise xmpp.NodeProcessed
|
||||
|
||||
|
||||
def send_activity(self, activity, subactivity=None, message=None):
|
||||
if not self.pep_supported:
|
||||
return
|
||||
|
@ -492,7 +492,7 @@ class ConnectionPEP(object):
|
|||
i = item.addChild('text')
|
||||
i.addData(message)
|
||||
self._pubsub_connection.send_pb_publish('', xmpp.NS_ACTIVITY, item, '0')
|
||||
|
||||
|
||||
def retract_activity(self):
|
||||
if not self.pep_supported:
|
||||
return
|
||||
|
@ -510,13 +510,13 @@ class ConnectionPEP(object):
|
|||
i = item.addChild('text')
|
||||
i.addData(message)
|
||||
self._pubsub_connection.send_pb_publish('', xmpp.NS_MOOD, item, '0')
|
||||
|
||||
|
||||
def retract_mood(self):
|
||||
if not self.pep_supported:
|
||||
return
|
||||
self.send_mood(None)
|
||||
self._pubsub_connection.send_pb_retract('', xmpp.NS_MOOD, '0')
|
||||
|
||||
|
||||
def send_tune(self, artist='', title='', source='', track=0, length=0,
|
||||
items=None):
|
||||
if not self.pep_supported:
|
||||
|
|
|
@ -41,10 +41,13 @@ S_FINISHED = 4
|
|||
CONNECT_TIMEOUT = 20
|
||||
|
||||
class Proxy65Manager:
|
||||
''' keep records for file transfer proxies. Each time account
|
||||
establishes a connection to its server call proxy65manger.resolve(proxy)
|
||||
for every proxy that is convigured within the account. The class takes
|
||||
care to resolve and test each proxy only once.'''
|
||||
"""
|
||||
Keep records for file transfer proxies. Each time account establishes a
|
||||
connection to its server call proxy65manger.resolve(proxy) for every proxy
|
||||
that is convigured within the account. The class takes care to resolve and
|
||||
test each proxy only once
|
||||
"""
|
||||
|
||||
def __init__(self, idlequeue):
|
||||
# dict {proxy: proxy properties}
|
||||
self.idlequeue = idlequeue
|
||||
|
@ -53,7 +56,9 @@ class Proxy65Manager:
|
|||
self.default_proxies = {}
|
||||
|
||||
def resolve(self, proxy, connection, sender_jid, default=None):
|
||||
''' start '''
|
||||
"""
|
||||
Start
|
||||
"""
|
||||
if proxy in self.proxies:
|
||||
resolver = self.proxies[proxy]
|
||||
else:
|
||||
|
@ -102,7 +107,9 @@ class Proxy65Manager:
|
|||
|
||||
class ProxyResolver:
|
||||
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.port = int(port)
|
||||
self.jid = unicode(jid)
|
||||
|
@ -175,19 +182,25 @@ class ProxyResolver:
|
|||
self.try_next_connection()
|
||||
|
||||
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:
|
||||
connection = self.connections.pop(0)
|
||||
self.start_resolve(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)
|
||||
if self.state == S_INITIAL:
|
||||
self.start_resolve(connection)
|
||||
|
||||
def start_resolve(self, connection):
|
||||
''' request network address from proxy '''
|
||||
"""
|
||||
Request network address from proxy
|
||||
"""
|
||||
self.state = S_STARTED
|
||||
self.active_connection = connection
|
||||
iq = common.xmpp.Protocol(name='iq', to=self.proxy, typ='get')
|
||||
|
@ -209,10 +222,16 @@ class ProxyResolver:
|
|||
self.sender_jid = sender_jid
|
||||
|
||||
class HostTester(Socks5, IdleObject):
|
||||
''' fake proxy tester. '''
|
||||
"""
|
||||
Fake proxy tester
|
||||
"""
|
||||
|
||||
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.port = port
|
||||
self.jid = jid
|
||||
|
@ -226,7 +245,9 @@ class HostTester(Socks5, IdleObject):
|
|||
self.sid = sid
|
||||
|
||||
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:
|
||||
self.on_failure()
|
||||
return None
|
||||
|
@ -320,10 +341,16 @@ class HostTester(Socks5, IdleObject):
|
|||
return
|
||||
|
||||
class ReceiverTester(Socks5, IdleObject):
|
||||
''' fake proxy tester. '''
|
||||
"""
|
||||
Fake proxy tester
|
||||
"""
|
||||
|
||||
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.port = port
|
||||
self.jid = jid
|
||||
|
@ -337,7 +364,9 @@ class ReceiverTester(Socks5, IdleObject):
|
|||
self.sid = sid
|
||||
|
||||
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:
|
||||
self.on_failure()
|
||||
return None
|
||||
|
|
|
@ -67,7 +67,9 @@ class ConnectionPubSub:
|
|||
self.__callbacks[id_]=(cb, args, kwargs)
|
||||
|
||||
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:
|
||||
return
|
||||
query = xmpp.Iq('set', to=jid)
|
||||
|
@ -80,20 +82,24 @@ class ConnectionPubSub:
|
|||
|
||||
self.connection.send(query)
|
||||
|
||||
def send_pb_retrieve(self, jid, node, cb=None, *args, **kwargs):
|
||||
'''Get items from a node'''
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
query = xmpp.Iq('get', to=jid)
|
||||
r = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
|
||||
r = r.addChild('items', {'node': node})
|
||||
def send_pb_retrieve(self, jid, node, cb=None, *args, **kwargs):
|
||||
"""
|
||||
Get items from a node
|
||||
"""
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
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)
|
||||
|
||||
if cb:
|
||||
self.__callbacks[id_]=(cb, args, kwargs)
|
||||
|
||||
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:
|
||||
return
|
||||
query = xmpp.Iq('set', to=jid)
|
||||
|
@ -104,7 +110,9 @@ class ConnectionPubSub:
|
|||
self.connection.send(query)
|
||||
|
||||
def send_pb_delete(self, jid, node):
|
||||
'''Deletes node.'''
|
||||
"""
|
||||
Delete node
|
||||
"""
|
||||
if not self.connection or self.connected < 2:
|
||||
return
|
||||
query = xmpp.Iq('set', to=jid)
|
||||
|
@ -122,7 +130,9 @@ class ConnectionPubSub:
|
|||
'node': node})
|
||||
|
||||
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:
|
||||
return
|
||||
query = xmpp.Iq('set', to=jid)
|
||||
|
|
|
@ -88,11 +88,12 @@ class CommonResolver():
|
|||
|
||||
# FIXME: API usage is not consistent! This one requires that process is called
|
||||
class LibAsyncNSResolver(CommonResolver):
|
||||
'''
|
||||
"""
|
||||
Asynchronous resolver using libasyncns-python. process() method has to be
|
||||
called in order to proceed the pending requests.
|
||||
Based on patch submitted by Damien Thebault.
|
||||
'''
|
||||
called in order to proceed the pending requests. Based on patch submitted by
|
||||
Damien Thebault.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.asyncns = libasyncns.Asyncns()
|
||||
CommonResolver.__init__(self)
|
||||
|
@ -146,20 +147,22 @@ class LibAsyncNSResolver(CommonResolver):
|
|||
|
||||
|
||||
class NSLookupResolver(CommonResolver):
|
||||
'''
|
||||
"""
|
||||
Asynchronous DNS resolver calling nslookup. Processing of pending requests
|
||||
is invoked from idlequeue which is watching file descriptor of pipe of stdout
|
||||
of nslookup process.
|
||||
'''
|
||||
is invoked from idlequeue which is watching file descriptor of pipe of
|
||||
stdout of nslookup process.
|
||||
"""
|
||||
|
||||
def __init__(self, idlequeue):
|
||||
self.idlequeue = idlequeue
|
||||
self.process = False
|
||||
CommonResolver.__init__(self)
|
||||
|
||||
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
|
||||
srv hosts '''
|
||||
"""
|
||||
Parse the output of nslookup command and return list of properties:
|
||||
'host', 'port','weight', 'priority' corresponding to the found srv hosts
|
||||
"""
|
||||
if os.name == 'nt':
|
||||
return self._parse_srv_result_nt(fqdn, result)
|
||||
elif os.name == 'posix':
|
||||
|
@ -260,7 +263,9 @@ class NSLookupResolver(CommonResolver):
|
|||
CommonResolver._on_ready(self, host, type, result_list)
|
||||
|
||||
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.set_idlequeue(self.idlequeue)
|
||||
ns.commandtimeout = 10
|
||||
|
|
|
@ -33,17 +33,19 @@ except ImportError:
|
|||
return None
|
||||
else:
|
||||
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)
|
||||
if result < 0:
|
||||
raise ValueError("Error: value '%(text)s' "
|
||||
"must be a positive integer")
|
||||
return result
|
||||
|
||||
def generate_uri_role( role_name, aliases,
|
||||
anchor_text, base_url,
|
||||
interpret_url, validator):
|
||||
'''Creates and register a uri based "interpreted role".
|
||||
def generate_uri_role( role_name, aliases, anchor_text, base_url,
|
||||
interpret_url, validator):
|
||||
"""
|
||||
Create and register a uri based "interpreted role"
|
||||
|
||||
Those are similar to the RFC, and PEP ones, and take
|
||||
role_name:
|
||||
|
@ -58,7 +60,7 @@ else:
|
|||
this, modulo the validated text, will be added to it
|
||||
validator:
|
||||
should return the validated text, or raise ValueError
|
||||
'''
|
||||
"""
|
||||
def uri_reference_role(role, rawtext, text, lineno, inliner,
|
||||
options={}, content=[]):
|
||||
try:
|
||||
|
@ -94,15 +96,15 @@ else:
|
|||
pos_int_validator)
|
||||
|
||||
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*
|
||||
threadsafe.
|
||||
'''
|
||||
def __init__(self,
|
||||
settings_spec=None,
|
||||
settings_overrides=dict(report_level=5, halt_level=5),
|
||||
config_section='general'):
|
||||
"""
|
||||
def __init__(self, settings_spec=None,
|
||||
settings_overrides=dict(report_level=5, halt_level=5),
|
||||
config_section='general'):
|
||||
self.pub = Publisher(reader=None, parser=None, writer=None,
|
||||
settings=None,
|
||||
source_class=io.StringInput,
|
||||
|
@ -124,13 +126,12 @@ else:
|
|||
config_section)
|
||||
|
||||
|
||||
def create_xhtml(self, text,
|
||||
destination=None,
|
||||
destination_path=None,
|
||||
enable_exit_status=None):
|
||||
''' Create xhtml for a fragment of IM dialog.
|
||||
We can use the source_name to store info about
|
||||
the message.'''
|
||||
def create_xhtml(self, text, destination=None, destination_path=None,
|
||||
enable_exit_status=None):
|
||||
"""
|
||||
Create xhtml for a fragment of IM dialog. We can use the source_name
|
||||
to store info about the message
|
||||
"""
|
||||
self.pub.set_source(text, None)
|
||||
self.pub.set_destination(destination, destination_path)
|
||||
output = self.pub.publish(enable_exit_status=enable_exit_status)
|
||||
|
|
|
@ -66,7 +66,9 @@ class SleepyWindows:
|
|||
return idleDelta
|
||||
|
||||
def poll(self):
|
||||
'''checks to see if we should change state'''
|
||||
"""
|
||||
Check to see if we should change state
|
||||
"""
|
||||
if not SUPPORTED:
|
||||
return False
|
||||
|
||||
|
@ -113,7 +115,9 @@ class SleepyUnix:
|
|||
return idle.getIdleSec()
|
||||
|
||||
def poll(self):
|
||||
'''checks to see if we should change state'''
|
||||
"""
|
||||
Check to see if we should change state
|
||||
"""
|
||||
if not SUPPORTED:
|
||||
return False
|
||||
|
||||
|
|
|
@ -52,9 +52,12 @@ READ_TIMEOUT = 180
|
|||
SEND_TIMEOUT = 180
|
||||
|
||||
class SocksQueue:
|
||||
''' queue for all file requests objects '''
|
||||
"""
|
||||
Queue for all file requests objects
|
||||
"""
|
||||
|
||||
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.readers = {}
|
||||
self.files_props = {}
|
||||
|
@ -72,9 +75,10 @@ class SocksQueue:
|
|||
self.on_failure = None
|
||||
|
||||
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)
|
||||
if self.listener is None:
|
||||
self.listener = Socks5Listener(self.idlequeue, port)
|
||||
|
@ -121,8 +125,10 @@ class SocksQueue:
|
|||
streamhost['idx'] = receiver.queue_idx
|
||||
|
||||
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']:
|
||||
if host != streamhost and 'idx' in host:
|
||||
if host['state'] == 1:
|
||||
|
@ -137,10 +143,11 @@ class SocksQueue:
|
|||
host['state'] = -2
|
||||
|
||||
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
|
||||
not connected try to establish connection to one of them.
|
||||
'''
|
||||
"""
|
||||
Check the state of all streamhosts and if all has failed, then emit
|
||||
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.unplug_idle(receiver.fd)
|
||||
file_props = receiver.file_props
|
||||
|
@ -173,7 +180,9 @@ class SocksQueue:
|
|||
self.process_result(-1, receiver)
|
||||
|
||||
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:
|
||||
return
|
||||
streamhost['state'] = -1
|
||||
|
@ -189,7 +198,9 @@ class SocksQueue:
|
|||
del(file_props['failure_cb'])
|
||||
|
||||
def add_receiver(self, account, sock5_receiver):
|
||||
''' add new file request '''
|
||||
"""
|
||||
Add new file request
|
||||
"""
|
||||
self.readers[self.idx] = sock5_receiver
|
||||
sock5_receiver.queue_idx = self.idx
|
||||
sock5_receiver.queue = self
|
||||
|
@ -259,9 +270,10 @@ class SocksQueue:
|
|||
sender.file_props = 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:
|
||||
return
|
||||
_id = file_props['sid']
|
||||
|
@ -279,7 +291,9 @@ class SocksQueue:
|
|||
self.connected = 0
|
||||
|
||||
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:
|
||||
fl_props = self.files_props[account]
|
||||
if sid in fl_props:
|
||||
|
@ -294,11 +308,12 @@ class SocksQueue:
|
|||
self.connected += 1
|
||||
|
||||
def process_result(self, result, actor):
|
||||
''' Take appropriate actions upon the result:
|
||||
[ 0, - 1 ] complete/end transfer
|
||||
[ > 0 ] send progress message
|
||||
[ None ] do nothing
|
||||
'''
|
||||
"""
|
||||
Take appropriate actions upon the result:
|
||||
[ 0, - 1 ] complete/end transfer
|
||||
[ > 0 ] send progress message
|
||||
[ None ] do nothing
|
||||
"""
|
||||
if result is None:
|
||||
return
|
||||
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)
|
||||
|
||||
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 in self.readers:
|
||||
reader = self.readers[idx]
|
||||
|
@ -325,8 +342,10 @@ class SocksQueue:
|
|||
del(self.readers[idx])
|
||||
|
||||
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 in self.senders:
|
||||
if do_disconnect:
|
||||
|
@ -386,9 +405,10 @@ class Socks5:
|
|||
self.file = None
|
||||
|
||||
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:
|
||||
fd = self.file_props['fd']
|
||||
else:
|
||||
|
@ -413,8 +433,10 @@ class Socks5:
|
|||
pass
|
||||
|
||||
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 = ''
|
||||
try:
|
||||
add = self._recv(64)
|
||||
|
@ -426,7 +448,9 @@ class Socks5:
|
|||
return add
|
||||
|
||||
def send_raw(self,raw_data):
|
||||
''' Writes raw outgoing data. '''
|
||||
"""
|
||||
Write raw outgoing data
|
||||
"""
|
||||
try:
|
||||
self._send(raw_data)
|
||||
except Exception:
|
||||
|
@ -483,7 +507,9 @@ class Socks5:
|
|||
return -1
|
||||
|
||||
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:
|
||||
self.file_props['error'] = -2
|
||||
return None
|
||||
|
@ -557,7 +583,9 @@ class Socks5:
|
|||
return None
|
||||
|
||||
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
|
||||
self.close_file()
|
||||
self.idlequeue.remove_timeout(self.fd)
|
||||
|
@ -573,13 +601,15 @@ class Socks5:
|
|||
self.state = -1
|
||||
|
||||
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)
|
||||
|
||||
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 = []
|
||||
try:
|
||||
num_auth = struct.unpack('!xB', buff[:2])[0]
|
||||
|
@ -591,9 +621,9 @@ class Socks5:
|
|||
return auth_mechanisms
|
||||
|
||||
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)
|
||||
|
||||
def _get_connect_buff(self):
|
||||
|
@ -604,8 +634,10 @@ class Socks5:
|
|||
return buff
|
||||
|
||||
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),
|
||||
0x05, command, 0x00, 0x03, len(msg), msg, 0, 0)
|
||||
return buff
|
||||
|
@ -634,7 +666,9 @@ class Socks5:
|
|||
return (req_type, host, port)
|
||||
|
||||
def read_connect(self):
|
||||
''' connect responce: version, auth method '''
|
||||
"""
|
||||
Connect response: version, auth method
|
||||
"""
|
||||
buff = self._recv()
|
||||
try:
|
||||
version, method = struct.unpack('!BB', buff)
|
||||
|
@ -652,7 +686,9 @@ class Socks5:
|
|||
self.idlequeue.plug_idle(self, True, False)
|
||||
|
||||
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:
|
||||
del(self.file_props['is_a_proxy'])
|
||||
return hashlib.sha1('%s%s%s' % (self.sid,
|
||||
|
@ -662,9 +698,12 @@ class Socks5:
|
|||
hexdigest()
|
||||
|
||||
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,
|
||||
port=None):
|
||||
port=None):
|
||||
self.queue_idx = sock_hash
|
||||
self.queue = parent
|
||||
Socks5.__init__(self, idlequeue, host, port, None, None, None)
|
||||
|
@ -745,7 +784,9 @@ class Socks5Sender(Socks5, IdleObject):
|
|||
self.disconnect()
|
||||
|
||||
def send_file(self):
|
||||
''' start sending the file over verified connection '''
|
||||
"""
|
||||
Start sending the file over verified connection
|
||||
"""
|
||||
if self.file_props['started']:
|
||||
return
|
||||
self.file_props['error'] = 0
|
||||
|
@ -766,7 +807,9 @@ class Socks5Sender(Socks5, IdleObject):
|
|||
return self.write_next() # initial for nl byte
|
||||
|
||||
def main(self):
|
||||
''' initial requests for verifying the connection '''
|
||||
"""
|
||||
Initial requests for verifying the connection
|
||||
"""
|
||||
if self.state == 1: # initial read
|
||||
buff = self.receive()
|
||||
if not self.connected:
|
||||
|
@ -785,7 +828,9 @@ class Socks5Sender(Socks5, IdleObject):
|
|||
return None
|
||||
|
||||
def disconnect(self, cb=True):
|
||||
''' Closes the socket. '''
|
||||
"""
|
||||
Close the socket
|
||||
"""
|
||||
# close connection and remove us from the queue
|
||||
Socks5.disconnect(self)
|
||||
if self.file_props is not None:
|
||||
|
@ -796,10 +841,12 @@ class Socks5Sender(Socks5, IdleObject):
|
|||
|
||||
class Socks5Listener(IdleObject):
|
||||
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
|
||||
only pollin events though
|
||||
'''
|
||||
"""
|
||||
self.port = port
|
||||
self.ais = socket.getaddrinfo(None, port, socket.AF_UNSPEC,
|
||||
socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_PASSIVE)
|
||||
|
@ -843,16 +890,22 @@ class Socks5Listener(IdleObject):
|
|||
self.started = True
|
||||
|
||||
def pollend(self):
|
||||
''' called when we stop listening on (host, port) '''
|
||||
"""
|
||||
Called when we stop listening on (host, port)
|
||||
"""
|
||||
self.disconnect()
|
||||
|
||||
def pollin(self):
|
||||
''' accept a new incomming connection and notify queue'''
|
||||
"""
|
||||
Accept a new incomming connection and notify queue
|
||||
"""
|
||||
sock = self.accept_conn()
|
||||
self.queue.on_connection_accepted(sock)
|
||||
|
||||
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.unplug_idle(self.fd)
|
||||
self.fd = -1
|
||||
|
@ -864,7 +917,9 @@ class Socks5Listener(IdleObject):
|
|||
pass
|
||||
|
||||
def accept_conn(self):
|
||||
''' accepts a new incomming connection '''
|
||||
"""
|
||||
Accept a new incomming connection
|
||||
"""
|
||||
_sock = self._serv.accept()
|
||||
_sock[0].setblocking(False)
|
||||
return _sock
|
||||
|
@ -909,7 +964,9 @@ class Socks5Receiver(Socks5, IdleObject):
|
|||
self.queue.reconnect_receiver(self, self.streamhost)
|
||||
|
||||
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:
|
||||
return None
|
||||
|
||||
|
@ -1018,7 +1075,9 @@ class Socks5Receiver(Socks5, IdleObject):
|
|||
return 1 # we are connected
|
||||
|
||||
def main(self, timeout=0):
|
||||
''' begin negotiation. on success 'address' != 0 '''
|
||||
"""
|
||||
Begin negotiation. on success 'address' != 0
|
||||
"""
|
||||
result = 1
|
||||
buff = self.receive()
|
||||
if buff == '':
|
||||
|
@ -1087,7 +1146,9 @@ class Socks5Receiver(Socks5, IdleObject):
|
|||
return None
|
||||
|
||||
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
|
||||
Socks5.disconnect(self)
|
||||
if cb is True:
|
||||
|
|
|
@ -84,10 +84,11 @@ class StanzaSession(object):
|
|||
return to
|
||||
|
||||
def remove_events(self, types):
|
||||
'''
|
||||
Remove events associated with this session from the queue.
|
||||
returns True if any events were removed (unlike events.py remove_events)
|
||||
'''
|
||||
"""
|
||||
Remove events associated with this session from the queue
|
||||
|
||||
Returns True if any events were removed (unlike events.py remove_events)
|
||||
"""
|
||||
any_removed = False
|
||||
|
||||
for j in (self.jid, self.jid.getStripped()):
|
||||
|
@ -140,10 +141,10 @@ class StanzaSession(object):
|
|||
self.cancelled_negotiation()
|
||||
|
||||
def cancelled_negotiation(self):
|
||||
'''
|
||||
"""
|
||||
A negotiation has been cancelled, so reset this session to its default
|
||||
state.
|
||||
'''
|
||||
state
|
||||
"""
|
||||
if self.control:
|
||||
self.control.on_cancel_session_negotiation()
|
||||
|
||||
|
@ -175,7 +176,7 @@ class StanzaSession(object):
|
|||
|
||||
|
||||
class EncryptedStanzaSession(StanzaSession):
|
||||
'''
|
||||
"""
|
||||
An encrypted stanza negotiation has several states. They arerepresented as
|
||||
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
|
||||
handle_session_negotiation method.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __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
|
||||
|
||||
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.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR,
|
||||
counter=self.encryptcounter)
|
||||
|
@ -239,9 +241,9 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
return self._kc_s
|
||||
|
||||
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.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR,
|
||||
counter=self.decryptcounter)
|
||||
|
@ -335,7 +337,9 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
return self.encrypter.encrypt(padded)
|
||||
|
||||
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')
|
||||
if orig_body:
|
||||
stanza.delChild(orig_body)
|
||||
|
@ -584,7 +588,9 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.send(request)
|
||||
|
||||
def verify_options_bob(self, form):
|
||||
''' 4.3 esession response (bob) '''
|
||||
"""
|
||||
4.3 esession response (bob)
|
||||
"""
|
||||
negotiated = {'recv_pubkey': None, 'send_pubkey': None}
|
||||
not_acceptable = []
|
||||
ask_user = {}
|
||||
|
@ -653,7 +659,9 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
return (negotiated, not_acceptable, ask_user)
|
||||
|
||||
def respond_e2e_bob(self, form, negotiated, not_acceptable):
|
||||
''' 4.3 esession response (bob) '''
|
||||
"""
|
||||
4.3 esession response (bob)
|
||||
"""
|
||||
response = xmpp.Message()
|
||||
feature = response.NT.feature
|
||||
feature.setNamespace(xmpp.NS_FEATURE)
|
||||
|
@ -728,7 +736,9 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.send(response)
|
||||
|
||||
def verify_options_alice(self, form):
|
||||
''' 'Alice Accepts' '''
|
||||
"""
|
||||
'Alice Accepts'
|
||||
"""
|
||||
negotiated = {}
|
||||
ask_user = {}
|
||||
not_acceptable = []
|
||||
|
@ -756,11 +766,13 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
return (negotiated, not_acceptable, ask_user)
|
||||
|
||||
def accept_e2e_alice(self, form, negotiated):
|
||||
''' 'Alice Accepts', continued '''
|
||||
"""
|
||||
'Alice Accepts', continued
|
||||
"""
|
||||
self.encryptable_stanzas = ['message']
|
||||
self.sas_algs = 'sas28x5'
|
||||
self.cipher = AES
|
||||
self.hash_alg = sha256
|
||||
self.hash_alg = sha256
|
||||
self.compression = None
|
||||
|
||||
self.negotiated = negotiated
|
||||
|
@ -828,7 +840,9 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.status = 'identified-alice'
|
||||
|
||||
def accept_e2e_bob(self, form):
|
||||
''' 4.5 esession accept (bob) '''
|
||||
"""
|
||||
4.5 esession accept (bob)
|
||||
"""
|
||||
response = xmpp.Message()
|
||||
|
||||
init = response.NT.init
|
||||
|
@ -948,11 +962,11 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.control.print_esession_details()
|
||||
|
||||
def do_retained_secret(self, k, old_srs):
|
||||
'''
|
||||
"""
|
||||
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
|
||||
been verified.
|
||||
'''
|
||||
been verified
|
||||
"""
|
||||
new_srs = self.hmac(k, 'New Retained Secret')
|
||||
self.srs = new_srs
|
||||
|
||||
|
@ -1017,12 +1031,12 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.enable_encryption = False
|
||||
|
||||
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
|
||||
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.T.error.T.text.setData(reason)
|
||||
|
||||
|
|
Loading…
Reference in New Issue