Merge branch 'pylint' into 'master'

Add a pylint configuration file and fix a few of the issues found

See merge request !54
This commit is contained in:
Philipp Hörist 2017-02-11 17:54:19 +01:00
commit 26aa0382d3
67 changed files with 2002 additions and 1450 deletions

410
pylintrc Normal file
View File

@ -0,0 +1,410 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint.
jobs=1
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,missing-docstring,not-callable
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=_
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,future.builtins
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[BASIC]
# Naming hint for argument names
argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct argument names
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for attribute names
attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct attribute names
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class names
class-name-hint=[A-Z_][a-zA-Z0-9]+$
# Regular expression matching correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming hint for function names
function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct function names
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for method names
method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct method names
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming hint for variable names
variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct variable names
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
[IMPORTS]
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of statements in function / method body
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

View File

@ -23,26 +23,19 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
from enum import IntEnum
from gi.repository import Gtk
import gtkgui_helpers
from gi.repository import GLib
from gi.repository import Pango
from common import gajim
from common import helpers
(
OPT_TYPE,
OPT_VAL
) = range(2)
(
C_PREFNAME,
C_VALUE,
C_TYPE
) = range(3)
GTKGUI_GLADE = 'manage_accounts_window.ui'
class Column(IntEnum):
PREFERENCE_NAME = 0
VALUE = 1
TYPE = 2
def rate_limit(rate):
"""
@ -143,14 +136,15 @@ class AdvancedConfigurationWindow(object):
Check if it's boolen or holds password stuff and if yes make the
cellrenderertext not editable, else - it's editable
"""
optname = model[iter_][C_PREFNAME]
opttype = model[iter_][C_TYPE]
optname = model[iter_][Column.PREFERENCE_NAME]
opttype = model[iter_][Column.TYPE]
if opttype == self.types['boolean'] or optname == 'password':
cell.set_property('editable', False)
else:
cell.set_property('editable', True)
def get_option_path(self, model, iter_):
@staticmethod
def get_option_path(model, iter_):
# It looks like path made from reversed array
# path[0] is the true one optname
# path[1] is the key name
@ -255,7 +249,8 @@ class AdvancedConfigurationWindow(object):
modelrow[1] = text
self.check_for_restart()
def on_advanced_configuration_window_destroy(self, widget):
@staticmethod
def on_advanced_configuration_window_destroy(widget):
del gajim.interface.instances['advanced_config']
def on_reset_button_clicked(self, widget):
@ -269,8 +264,8 @@ class AdvancedConfigurationWindow(object):
elif len(opt_path) == 3:
default = gajim.config.get_default_per(opt_path[2], opt_path[0])
if model[iter_][C_TYPE] == self.types['boolean']:
if self.right_true_dict[default] == model[iter_][C_VALUE]:
if model[iter_][Column.TYPE] == self.types['boolean']:
if self.right_true_dict[default] == model[iter_][Column.VALUE]:
return
modelpath = self.modelfilter.convert_path_to_child_path(path)
modelrow = self.model[modelpath]
@ -281,15 +276,15 @@ class AdvancedConfigurationWindow(object):
keyrow = self.model[modelpath[:2]]
key = keyrow[0]
self.remember_option(option + '\n' + key + '\n' + optname,
modelrow[C_VALUE], default)
modelrow[Column.VALUE], default)
gajim.config.set_per(optname, key, option, default)
else:
self.remember_option(option, modelrow[C_VALUE], default)
self.remember_option(option, modelrow[Column.VALUE], default)
gajim.config.set(option, default)
modelrow[C_VALUE] = self.right_true_dict[default]
modelrow[Column.VALUE] = self.right_true_dict[default]
self.check_for_restart()
else:
if str(default) == model[iter_][C_VALUE]:
if str(default) == model[iter_][Column.VALUE]:
return
self.on_config_edited(None, path.to_string(), str(default))
@ -323,14 +318,14 @@ class AdvancedConfigurationWindow(object):
def visible_func(self, model, treeiter, data):
search_string = self.entry.get_text().lower()
for it in tree_model_pre_order(model, treeiter):
if model[it][C_TYPE] != '':
if model[it][Column.TYPE] != '':
opt_path = self.get_option_path(model, it)
if len(opt_path) == 3:
desc = gajim.config.get_desc_per(opt_path[2], opt_path[1],
opt_path[0])
elif len(opt_path) == 1:
desc = gajim.config.get_desc(opt_path[0])
if search_string in model[it][C_PREFNAME] or (desc and \
if search_string in model[it][Column.PREFERENCE_NAME] or (desc and \
search_string in desc.lower()):
return True
return False

View File

@ -22,7 +22,6 @@
##
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib

View File

@ -46,7 +46,7 @@ from common import ged
from common import i18n
from common.stanza_session import EncryptedStanzaSession, ArchivingStanzaSession
from common.contacts import GC_Contact
from common.logger import constants
from common.logger import KindConstant
from nbxmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC
from nbxmpp.protocol import NS_RECEIPTS, NS_ESESSION
from nbxmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO
@ -1151,7 +1151,7 @@ class ChatControl(ChatControlBase):
def print_conversation(self, text, frm='', tim=None, encrypted=False,
subject=None, xhtml=None, simple=False, xep0184_id=None,
displaymarking=None, msg_log_id=None, correct_id=None,
msg_stanza_id=None, additional_data={}):
msg_stanza_id=None, additional_data=None):
"""
Print a line in the conversation
@ -1166,6 +1166,9 @@ class ChatControl(ChatControlBase):
"""
contact = self.contact
if additional_data is None:
additional_data = {}
if frm == 'status':
if not gajim.config.get('print_status_in_chats'):
return
@ -1679,15 +1682,15 @@ class ChatControl(ChatControlBase):
additional_data = row[4]
if not msg: # message is empty, we don't print it
continue
if row[1] in (constants.KIND_CHAT_MSG_SENT,
constants.KIND_SINGLE_MSG_SENT):
if row[1] in (KindConstant.CHAT_MSG_SENT,
KindConstant.SINGLE_MSG_SENT):
kind = 'outgoing'
name = self.get_our_nick()
elif row[1] in (constants.KIND_SINGLE_MSG_RECV,
constants.KIND_CHAT_MSG_RECV):
elif row[1] in (KindConstant.SINGLE_MSG_RECV,
KindConstant.CHAT_MSG_RECV):
kind = 'incoming'
name = self.contact.get_shown_name()
elif row[1] == constants.KIND_ERROR:
elif row[1] == KindConstant.ERROR:
kind = 'status'
name = self.contact.get_shown_name()

View File

@ -737,7 +737,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
return label
def send_message(self, message, keyID='', type_='chat', chatstate=None,
msg_id=None, resource=None, xhtml=None, callback=None, callback_args=[],
msg_id=None, resource=None, xhtml=None, callback=None, callback_args=None,
process_commands=True, attention=False):
"""
Send the given message to the active tab. Doesn't return None if error
@ -745,6 +745,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
if not message or message == '\n':
return None
if callback_args is None:
callback_args = []
if process_commands and self.process_as_command(message):
return
@ -807,7 +810,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
other_tags_for_name=[], other_tags_for_time=[], other_tags_for_text=[],
count_as_new=True, subject=None, old_kind=None, xhtml=None, simple=False,
xep0184_id=None, graphics=True, displaymarking=None, msg_log_id=None,
msg_stanza_id=None, correct_id=None, additional_data={}):
msg_stanza_id=None, correct_id=None, additional_data=None):
"""
Print 'chat' type messages
correct_id = (message_id, correct_id)
@ -819,6 +822,15 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
if self.was_at_the_end or kind == 'outgoing':
end = True
if other_tags_for_name is None:
other_tags_for_name = []
if other_tags_for_time is None:
other_tags_for_time = []
if other_tags_for_text is None:
other_tags_for_text = []
if additional_data is None:
additional_data = {}
textview.print_conversation_line(text, jid, kind, name, tim,
other_tags_for_name, other_tags_for_time, other_tags_for_text,
subject, old_kind, xhtml, simple=simple, graphics=graphics,

View File

@ -19,7 +19,6 @@ architecture to implement commands in a streight and flexible,
declarative way.
"""
import re
from types import FunctionType
from inspect import getargspec, getdoc

View File

@ -35,7 +35,7 @@ detected.
"""
from ..framework import CommandContainer, command, doc
from .hosts import *
from .hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
class CustomCommonCommands(CommandContainer):
"""

View File

@ -38,7 +38,7 @@ from os.path import expanduser
from gi.repository import GLib
from ..framework import CommandContainer, command, doc
from .hosts import *
from .hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
class Execute(CommandContainer):
AUTOMATIC = True
@ -68,7 +68,7 @@ class Execute(CommandContainer):
@classmethod
def poller(cls, processor, popen):
for x in list(range(cls.POLL_COUNT)):
for _ in range(cls.POLL_COUNT):
yield cls.brush(processor, popen)
cls.overdue(processor, popen)
yield False

View File

@ -30,8 +30,7 @@ from ..errors import CommandError
from ..framework import CommandContainer, command, doc
from ..mapping import generate_usage
from .hosts import *
from . import execute
from .hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
# This holds constants fron the logger, which we'll be using in some of our
# commands.

View File

@ -24,12 +24,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from types import *
def remove(sequence, target):
if isinstance(sequence, list):
if target in sequence:
sequence.remove(target)
elif isinstance(sequence, dict):
if target in sequence:
del sequence[target]
del sequence[target]

View File

@ -94,13 +94,15 @@ def create_suitable_client_caps(node, caps_hash, hash_method, fjid=None):
client_caps = ClientCaps(caps_hash, node, hash_method)
return client_caps
def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
def compute_caps_hash(identities, features, dataforms=None, hash_method='sha-1'):
"""
Compute caps hash according to XEP-0115, V1.5
dataforms are nbxmpp.DataForms objects as common.dataforms don't allow several
values without a field type list-multi
"""
if dataforms is None:
dataforms = []
def sort_identities_func(i1, i2):
cat1 = i1['category']
cat2 = i2['category']

View File

@ -35,15 +35,15 @@
import re
from common import defs
from gi.repository import GLib
from enum import IntEnum
(
OPT_TYPE,
OPT_VAL,
OPT_DESC,
# If OPT_RESTART is True - we need restart to use our changed option
# OPT_DESC also should be there
OPT_RESTART,
) = range(4)
class Option(IntEnum):
TYPE = 0
VAL = 1
DESC = 2
# If Option.RESTART is True - we need restart to use our changed option
# Option.DESC also should be there
RESTART = 3
opt_int = [ 'integer', 0 ]
opt_str = [ 'string', 0 ]
@ -633,7 +633,7 @@ class Config:
def set(self, optname, value):
if optname not in self.__options[1]:
return
value = self.is_valid(self.__options[0][optname][OPT_TYPE], value)
value = self.is_valid(self.__options[0][optname][Option.TYPE], value)
if value is None:
return
@ -650,24 +650,24 @@ class Config:
def get_default(self, optname):
if optname not in self.__options[0]:
return None
return self.__options[0][optname][OPT_VAL]
return self.__options[0][optname][Option.VAL]
def get_type(self, optname):
if optname not in self.__options[0]:
return None
return self.__options[0][optname][OPT_TYPE][0]
return self.__options[0][optname][Option.TYPE][0]
def get_desc(self, optname):
if optname not in self.__options[0]:
return None
if len(self.__options[0][optname]) > OPT_DESC:
return self.__options[0][optname][OPT_DESC]
if len(self.__options[0][optname]) > Option.DESC:
return self.__options[0][optname][Option.DESC]
def get_restart(self, optname):
if optname not in self.__options[0]:
return None
if len(self.__options[0][optname]) > OPT_RESTART:
return self.__options[0][optname][OPT_RESTART]
if len(self.__options[0][optname]) > Option.RESTART:
return self.__options[0][optname][Option.RESTART]
def add_per(self, typename, name): # per_group_of_option
if typename not in self.__options_per_key:
@ -679,7 +679,7 @@ class Config:
return 'you already have added %s before' % name
opt[1][name] = {}
for o in opt[0]:
opt[1][name][o] = opt[0][o][OPT_VAL]
opt[1][name][o] = opt[0][o][Option.VAL]
self._timeout_save()
def del_per(self, typename, name, subname = None): # per_group_of_option
@ -705,7 +705,7 @@ class Config:
obj = dict_[key]
if subname not in obj:
return
typ = self.__options_per_key[optname][0][subname][OPT_TYPE]
typ = self.__options_per_key[optname][0][subname][Option.TYPE]
value = self.is_valid(typ, value)
if value is None:
return
@ -735,7 +735,7 @@ class Config:
dict_ = self.__options_per_key[optname][0]
if subname not in dict_:
return None
return dict_[subname][OPT_VAL]
return dict_[subname][Option.VAL]
def get_type_per(self, optname, subname):
if optname not in self.__options_per_key:
@ -743,7 +743,7 @@ class Config:
dict_ = self.__options_per_key[optname][0]
if subname not in dict_:
return None
return dict_[subname][OPT_TYPE][0]
return dict_[subname][Option.TYPE][0]
def get_desc_per(self, optname, key=None, subname=None):
if optname not in self.__options_per_key:
@ -758,8 +758,8 @@ class Config:
return None
if subname not in obj:
return None
if len(obj[subname]) > OPT_DESC:
return obj[subname][OPT_DESC]
if len(obj[subname]) > Option.DESC:
return obj[subname][Option.DESC]
return None
def get_restart_per(self, optname, key=None, subname=None):
@ -775,8 +775,8 @@ class Config:
return False
if subname not in obj:
return False
if len(obj[subname]) > OPT_RESTART:
return obj[subname][OPT_RESTART]
if len(obj[subname]) > Option.RESTART:
return obj[subname][Option.RESTART]
return False
def should_log(self, account, jid):
@ -794,7 +794,7 @@ class Config:
def _init_options(self):
for opt in self.__options[0]:
self.__options[1][opt] = self.__options[0][opt][OPT_VAL]
self.__options[1][opt] = self.__options[0][opt][Option.VAL]
def _really_save(self):
from common import gajim

View File

@ -26,12 +26,12 @@ import os
import sys
import tempfile
from common import defs
from enum import Enum
(
TYPE_CONFIG,
TYPE_CACHE,
TYPE_DATA
) = range(3)
class Type(Enum):
CONFIG = 0
CACHE = 1
DATA = 2
# Note on path and filename encodings:
#
@ -65,7 +65,7 @@ def get(key):
class ConfigPaths:
def __init__(self):
# {'name': (type, path), } type can be TYPE_CONFIG, TYPE_CACHE, TYPE_DATA
# {'name': (type, path), } type can be Type.CONFIG, Type.CACHE, Type.DATA
# or None
self.paths = {}
@ -109,11 +109,11 @@ class ConfigPaths:
def __getitem__(self, key):
type_, path = self.paths[key]
if type_ == TYPE_CONFIG:
if type_ == Type.CONFIG:
return os.path.join(self.config_root, path)
elif type_ == TYPE_CACHE:
elif type_ == Type.CACHE:
return os.path.join(self.cache_root, path)
elif type_ == TYPE_DATA:
elif type_ == Type.DATA:
return os.path.join(self.data_root, path)
return path
@ -145,26 +145,26 @@ class ConfigPaths:
'RNG_SEED': 'rng_seed'}
for name in d:
d[name] += profile
self.add(name, TYPE_DATA, windowsify(d[name]))
self.add(name, Type.DATA, windowsify(d[name]))
if len(profile):
self.add('MY_DATA', TYPE_DATA, 'data.dir')
self.add('MY_DATA', Type.DATA, 'data.dir')
else:
self.add('MY_DATA', TYPE_DATA, '')
self.add('MY_DATA', Type.DATA, '')
d = {'CACHE_DB': 'cache.db', 'VCARD': 'vcards',
'AVATAR': 'avatars'}
for name in d:
d[name] += profile
self.add(name, TYPE_CACHE, windowsify(d[name]))
self.add(name, Type.CACHE, windowsify(d[name]))
if len(profile):
self.add('MY_CACHE', TYPE_CACHE, 'cache.dir')
self.add('MY_CACHE', Type.CACHE, 'cache.dir')
else:
self.add('MY_CACHE', TYPE_CACHE, '')
self.add('MY_CACHE', Type.CACHE, '')
if len(profile):
self.add('MY_CONFIG', TYPE_CONFIG, 'config.dir')
self.add('MY_CONFIG', Type.CONFIG, 'config.dir')
else:
self.add('MY_CONFIG', TYPE_CONFIG, '')
self.add('MY_CONFIG', Type.CONFIG, '')
try:
self.add('TMP', None, tempfile.gettempdir())
@ -187,10 +187,10 @@ class ConfigPaths:
certsdir += u'.' + profile
localcertsdir += u'.' + profile
self.add('SECRETS_FILE', TYPE_DATA, secretsfile)
self.add('MY_PEER_CERTS', TYPE_DATA, certsdir)
self.add('CONFIG_FILE', TYPE_CONFIG, conffile)
self.add('PLUGINS_CONFIG_DIR', TYPE_CONFIG, pluginsconfdir)
self.add('MY_CERT', TYPE_CONFIG, localcertsdir)
self.add('SECRETS_FILE', Type.DATA, secretsfile)
self.add('MY_PEER_CERTS', Type.DATA, certsdir)
self.add('CONFIG_FILE', Type.CONFIG, conffile)
self.add('PLUGINS_CONFIG_DIR', Type.CONFIG, pluginsconfdir)
self.add('MY_CERT', Type.CONFIG, localcertsdir)
gajimpaths = ConfigPaths()

View File

@ -71,39 +71,39 @@ import logging
log = logging.getLogger('gajim.c.connection')
ssl_error = {
2: _("Unable to get issuer certificate"),
3: _("Unable to get certificate CRL"),
4: _("Unable to decrypt certificate's signature"),
5: _("Unable to decrypt CRL's signature"),
6: _("Unable to decode issuer public key"),
7: _("Certificate signature failure"),
8: _("CRL signature failure"),
9: _("Certificate is not yet valid"),
10: _("Certificate has expired"),
11: _("CRL is not yet valid"),
12: _("CRL has expired"),
13: _("Format error in certificate's notBefore field"),
14: _("Format error in certificate's notAfter field"),
15: _("Format error in CRL's lastUpdate field"),
16: _("Format error in CRL's nextUpdate field"),
17: _("Out of memory"),
18: _("Self signed certificate"),
19: _("Self signed certificate in certificate chain"),
20: _("Unable to get local issuer certificate"),
21: _("Unable to verify the first certificate"),
22: _("Certificate chain too long"),
23: _("Certificate revoked"),
24: _("Invalid CA certificate"),
25: _("Path length constraint exceeded"),
26: _("Unsupported certificate purpose"),
27: _("Certificate not trusted"),
28: _("Certificate rejected"),
29: _("Subject issuer mismatch"),
30: _("Authority and subject key identifier mismatch"),
31: _("Authority and issuer serial number mismatch"),
32: _("Key usage does not include certificate signing"),
50: _("Application verification failure")
#100 is for internal usage: host not correct
2: _("Unable to get issuer certificate"),
3: _("Unable to get certificate CRL"),
4: _("Unable to decrypt certificate's signature"),
5: _("Unable to decrypt CRL's signature"),
6: _("Unable to decode issuer public key"),
7: _("Certificate signature failure"),
8: _("CRL signature failure"),
9: _("Certificate is not yet valid"),
10: _("Certificate has expired"),
11: _("CRL is not yet valid"),
12: _("CRL has expired"),
13: _("Format error in certificate's notBefore field"),
14: _("Format error in certificate's notAfter field"),
15: _("Format error in CRL's lastUpdate field"),
16: _("Format error in CRL's nextUpdate field"),
17: _("Out of memory"),
18: _("Self signed certificate"),
19: _("Self signed certificate in certificate chain"),
20: _("Unable to get local issuer certificate"),
21: _("Unable to verify the first certificate"),
22: _("Certificate chain too long"),
23: _("Certificate revoked"),
24: _("Invalid CA certificate"),
25: _("Path length constraint exceeded"),
26: _("Unsupported certificate purpose"),
27: _("Certificate not trusted"),
28: _("Certificate rejected"),
29: _("Subject issuer mismatch"),
30: _("Authority and subject key identifier mismatch"),
31: _("Authority and issuer serial number mismatch"),
32: _("Key usage does not include certificate signing"),
50: _("Application verification failure")
#100 is for internal usage: host not correct
}
class CommonConnection:
@ -526,7 +526,9 @@ class CommonConnection:
subject, type_, msg_iq, xhtml)
def log_message(self, jid, msg, forward_from, session, original_message,
subject, type_, xhtml=None, additional_data={}):
subject, type_, xhtml=None, additional_data=None):
if additional_data is None:
additional_data = {}
if not forward_from and session and session.is_loggable():
ji = gajim.get_jid_without_resource(jid)
if gajim.config.should_log(self.name, ji):
@ -567,7 +569,7 @@ class CommonConnection:
"""
raise NotImplementedError
def request_subscription(self, jid, msg='', name='', groups=[],
def request_subscription(self, jid, msg='', name='', groups=None,
auto_auth=False):
"""
To be implemented by derivated classes
@ -2251,10 +2253,12 @@ class Connection(CommonConnection, ConnectionHandlers):
p = nbxmpp.Presence(jid, 'unsubscribe')
self.connection.send(p)
def request_subscription(self, jid, msg='', name='', groups=[],
def request_subscription(self, jid, msg='', name='', groups=None,
auto_auth=False, user_nick=''):
if not gajim.account_is_connected(self.name):
return
if groups is None:
groups = []
log.debug('subscription request for %s' % jid)
if auto_auth:
self.jids_for_auto_auth.append(jid)

View File

@ -30,14 +30,13 @@
import os
import base64
import sys
import operator
import hashlib
from gi.repository import GLib
from time import (altzone, daylight, gmtime, localtime, mktime, strftime,
from time import (altzone, daylight, gmtime, localtime, strftime,
time as time_time, timezone, tzname)
from calendar import timegm
from gi.repository import GLib
import nbxmpp
from common import caps_cache as capscache
@ -63,11 +62,6 @@ from common.nec import NetworkEvent
from common.jingle import ConnectionJingle
from common import dbus_support
if dbus_support.supported:
import dbus
from music_track_listener import MusicTrackListener
import logging
log = logging.getLogger('gajim.c.connection_handlers')
@ -603,7 +597,7 @@ class ConnectionVcard:
node = conf.getAttr('node')
form_tag = conf.getTag('x', namespace=nbxmpp.NS_DATA)
if form_tag:
form = common.dataforms.ExtendForm(node=form_tag)
form = dataforms.ExtendForm(node=form_tag)
gajim.nec.push_incoming_event(PEPConfigReceivedEvent(None,
conn=self, node=node, form=form))

View File

@ -18,33 +18,33 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
from calendar import timegm
import datetime
import hashlib
import hmac
import logging
import sys
import os
from time import (localtime, time as time_time)
from calendar import timegm
import hmac
import hashlib
from time import time as time_time
import nbxmpp
from nbxmpp.protocol import NS_CHATSTATES
from common import atom
from common import nec
from common import helpers
from common import gajim
from common import i18n
import nbxmpp
from common import dataforms
from common import exceptions
from common.zeroconf import zeroconf
from common.zeroconf.zeroconf import Constant
from common.logger import LOG_DB_PATH
from common.pep import SUPPORTED_PERSONAL_USER_EVENTS
from nbxmpp.protocol import NS_CHATSTATES
from common.jingle_transport import JingleTransportSocks5
from common.file_props import FilesProp
if gajim.HAVE_PYOPENSSL:
import OpenSSL.crypto
import logging
log = logging.getLogger('gajim.c.connection_handlers_events')
CONDITION_TO_CODE = {
@ -72,7 +72,7 @@ CONDITION_TO_CODE = {
class HelperEvent:
def get_jid_resource(self, check_fake_jid=False):
if check_fake_jid and hasattr(self, 'id_') and \
self.id_ in self.conn.groupchat_jids:
self.id_ in self.conn.groupchat_jids:
self.fjid = self.conn.groupchat_jids[self.id_]
del self.conn.groupchat_jids[self.id_]
else:
@ -84,7 +84,7 @@ class HelperEvent:
def get_gc_control(self):
self.gc_control = gajim.interface.msg_win_mgr.get_gc_control(self.jid,
self.conn.name)
self.conn.name)
# If gc_control is missing - it may be minimized. Try to get it
# from there. If it's not there - then it's missing anyway and
@ -247,7 +247,7 @@ class TimeResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
except ValueError:
try:
t = datetime.datetime.strptime(utc_time,
'%Y-%m-%dT%H:%M:%S.%f')
'%Y-%m-%dT%H:%M:%S.%f')
except ValueError as e:
log.info('Wrong time format: %s' % str(e))
return
@ -280,8 +280,7 @@ class GMailQueryReceivedEvent(nec.NetworkIncomingEvent):
gmail_messages = mb.getTags('mail-thread-info')
for gmessage in gmail_messages:
unread_senders = []
for sender in gmessage.getTag('senders').getTags(
'sender'):
for sender in gmessage.getTag('senders').getTags('sender'):
if sender.getAttr('unread') != '1':
continue
if sender.getAttr('name'):
@ -309,8 +308,7 @@ class GMailQueryReceivedEvent(nec.NetworkIncomingEvent):
self.conn.gmail_last_time = int(mb.getAttr('result-time'))
self.jid = gajim.get_jid_from_account(self.name)
log.debug(('You have %s new gmail e-mails on %s.') % (self.newmsgs,
self.jid))
log.debug('You have %s new gmail e-mails on %s.', self.newmsgs, self.jid)
return True
class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent):
@ -391,15 +389,15 @@ class RosterReceivedEvent(nec.NetworkIncomingEvent):
j = helpers.parse_jid(jid)
except Exception:
print(_('JID %s is not RFC compliant. It will not be added '
'to your roster. Use roster management tools such as '
'http://jru.jabberstudio.org/ to remove it') % jid,
file=sys.stderr)
'to your roster. Use roster management tools such as '
'http://jru.jabberstudio.org/ to remove it') % jid,
file=sys.stderr)
else:
infos = raw_roster[jid]
if jid != our_jid and (not infos['subscription'] or \
infos['subscription'] == 'none') and (not infos['ask'] or \
infos['ask'] == 'none') and not infos['name'] and \
not infos['groups']:
infos['subscription'] == 'none') and (not infos['ask'] or \
infos['ask'] == 'none') and not infos['name'] and \
not infos['groups']:
# remove this useless item, it won't be shown in roster
# anyway
self.conn.connection.getRoster().delItem(jid)
@ -777,7 +775,8 @@ PresenceHelperEvent):
sig_msg = sig_tag.getData()
self.keyID = self.conn.gpg.verify(self.status, sig_msg)
self.keyID = helpers.prepare_and_validate_gpg_keyID(self.conn.name,
self.jid, self.keyID)
self.jid,
self.keyID)
def _generate_prio(self):
self.prio = self.stanza.getPriority()
@ -844,7 +843,7 @@ PresenceHelperEvent):
# Error presences may not include sent stanza, so we don't detect
# it's a muc presence. So detect it by ID
h = hmac.new(self.conn.secret_hmac, self.jid.encode('utf-8'),
hashlib.md5).hexdigest()[:6]
hashlib.md5).hexdigest()[:6]
if self.id_.split('_')[-1] == h:
self.is_gc = True
self.status = self.stanza.getStatus() or ''
@ -856,8 +855,10 @@ PresenceHelperEvent):
self.errmsg = self.stanza.getErrorMsg()
if self.is_gc:
gajim.nec.push_incoming_event(GcPresenceReceivedEvent(None,
conn=self.conn, stanza=self.stanza, presence_obj=self))
gajim.nec.push_incoming_event(
GcPresenceReceivedEvent(
None, conn=self.conn, stanza=self.stanza,
presence_obj=self))
return
if self.ptype == 'subscribe':
@ -877,11 +878,10 @@ PresenceHelperEvent):
if not self.ptype or self.ptype == 'unavailable':
our_jid = gajim.get_jid_from_account(self.conn.name)
if self.jid == our_jid and self.resource == \
self.conn.server_resource:
if self.jid == our_jid and self.resource == self.conn.server_resource:
# We got our own presence
gajim.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
show=self.show))
show=self.show))
elif self.jid in jid_list or self.jid == our_jid:
return True
@ -947,18 +947,16 @@ class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
# we know real jid, save it in db
st += ' (%s)' % jid
try:
gajim.logger.write('gcstatus', self.fjid, st,
self.show)
gajim.logger.write('gcstatus', self.fjid, st, self.show)
except exceptions.PysqliteOperationalError as e:
self.conn.dispatch('DB_ERROR', (_('Disk Write Error'),
str(e)))
self.conn.dispatch('DB_ERROR', (_('Disk Write Error'), str(e)))
except exceptions.DatabaseMalformed:
pritext = _('Database Error')
sectext = _('The database file (%s) cannot be read. '
'Try to repair it (see '
'http://trac.gajim.org/wiki/DatabaseBackup) or '
'remove it (all history will be lost).') % \
LOG_DB_PATH
'Try to repair it (see '
'http://trac.gajim.org/wiki/DatabaseBackup) or '
'remove it (all history will be lost).') % \
LOG_DB_PATH
self.conn.dispatch('DB_ERROR', (pritext, sectext))
if self.avatar_sha == '':
# contact has no avatar
@ -1260,10 +1258,11 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
self.session = self.conn.get_latest_session(self.fjid)
if not self.session:
self.session = self.conn.make_new_session(self.fjid,
self.thread_id, type_='pm')
self.thread_id,
type_='pm')
else:
self.session = self.conn.get_or_create_session(self.fjid,
self.thread_id)
self.thread_id)
if self.thread_id and not self.session.received_thread_id:
self.session.received_thread_id = True
@ -1272,10 +1271,10 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
# check if the message is a XEP-0020 feature negotiation request
if not self.forwarded and self.stanza.getTag('feature',
namespace=nbxmpp.NS_FEATURE):
namespace=nbxmpp.NS_FEATURE):
if gajim.HAVE_PYCRYPTO:
feature = self.stanza.getTag(name='feature',
namespace=nbxmpp.NS_FEATURE)
namespace=nbxmpp.NS_FEATURE)
form = nbxmpp.DataForm(node=feature.getTag('x'))
if not form:
return
@ -1295,9 +1294,9 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
return
if not self.forwarded and self.stanza.getTag('init',
namespace=nbxmpp.NS_ESESSION_INIT):
namespace=nbxmpp.NS_ESESSION_INIT):
init = self.stanza.getTag(name='init',
namespace=nbxmpp.NS_ESESSION_INIT)
namespace=nbxmpp.NS_ESESSION_INIT)
form = nbxmpp.DataForm(node=init.getTag('x'))
self.session.handle_negotiation(form)
@ -1309,7 +1308,7 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
self.encrypted = False
xep_200_encrypted = self.stanza.getTag('c',
namespace=nbxmpp.NS_STANZA_CRYPTO)
namespace=nbxmpp.NS_STANZA_CRYPTO)
if xep_200_encrypted:
if self.forwarded:
# Ignore E2E forwarded encrypted messages
@ -1328,7 +1327,7 @@ class ZeroconfMessageReceivedEvent(MessageReceivedEvent):
if self.fjid is None:
for key in self.conn.connection.zeroconf.contacts:
if self.ip == self.conn.connection.zeroconf.contacts[key][
zeroconf.C_ADDRESS]:
Constant.ADDRESS]:
self.fjid = key
break
@ -1336,7 +1335,7 @@ class ZeroconfMessageReceivedEvent(MessageReceivedEvent):
def generate(self):
self.base_event = nec.NetworkIncomingEvent(None, conn=self.conn,
stanza=self.stanza)
stanza=self.stanza)
return super(ZeroconfMessageReceivedEvent, self).generate()
class GcInvitationReceivedEvent(nec.NetworkIncomingEvent):
@ -1350,8 +1349,8 @@ class GcInvitationReceivedEvent(nec.NetworkIncomingEvent):
try:
self.room_jid = helpers.parse_jid(invite_tag.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it' % invite_tag.getAttr(
'jid'))
log.warning('Invalid JID: %s, ignoring it', invite_tag.getAttr(
'jid'))
return
self.jid_from = self.msg_obj.fjid
self.reason = invite_tag.getAttr('reason')

View File

@ -92,12 +92,14 @@ class Contact(CommonContact):
"""
Information concerning a contact
"""
def __init__(self, jid, account, name='', groups=[], show='', status='',
def __init__(self, jid, account, name='', groups=None, show='', status='',
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
our_chatstate=None, chatstate=None, last_status_time=None, msg_log_id=None,
last_activity_time=None):
if not isinstance(jid, str):
print('no str')
if groups is None:
groups = []
CommonContact.__init__(self, jid, account, resource, show, status, name,
our_chatstate, chatstate, client_caps=client_caps)
@ -244,10 +246,12 @@ class LegacyContactsAPI:
del self._accounts[account]
self._metacontact_manager.remove_account(account)
def create_contact(self, jid, account, name='', groups=[], show='',
def create_contact(self, jid, account, name='', groups=None, show='',
status='', sub='', ask='', resource='', priority=0, keyID='',
client_caps=None, our_chatstate=None, chatstate=None, last_status_time=None,
last_activity_time=None):
if groups is None:
groups = []
# Use Account object if available
account = self._accounts.get(account, account)
return Contact(jid=jid, account=account, name=name, groups=groups,
@ -348,12 +352,14 @@ class LegacyContactsAPI:
return contact
return self.get_highest_prio_contact_from_contacts(contacts)
def get_nb_online_total_contacts(self, accounts=[], groups=[]):
def get_nb_online_total_contacts(self, accounts=None, groups=None):
"""
Return the number of online contacts and the total number of contacts
"""
if accounts == []:
if not accounts:
accounts = self.get_accounts()
if groups is None:
groups = []
nbr_online = 0
nbr_total = 0
for account in accounts:

View File

@ -28,203 +28,206 @@ These constants have been obtained from RFC2409 and RFC3526.
import string
generators = [None, # one to get the right offset
2,
2,
None,
None,
2,
None,
None,
None,
None,
None,
None,
None,
None,
2, # group 14
2,
2,
2,
2]
hex_primes = [None,
# group 1
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF''',
# group 2
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
FFFFFFFF FFFFFFFF''',
# XXX how do I obtain these?
None,
None,
# group 5
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF''',
None,
None,
None,
None,
None,
None,
None,
None,
# group 14
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AACAA68 FFFFFFFF FFFFFFFF''',
# group 15
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF''',
# group 16
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
FFFFFFFF FFFFFFFF''',
# group 17
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
6DCC4024 FFFFFFFF FFFFFFFF''',
# group 18
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD
F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831
179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B
DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF
5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6
D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3
23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328
06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C
DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE
12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4
38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300
741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568
3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9
22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B
4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A
062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36
4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1
B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92
4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47
9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
60C980DD 98EDD3DF FFFFFFFF FFFFFFFF'''
generators = [
None, # one to get the right offset
2,
2,
None,
None,
2,
None,
None,
None,
None,
None,
None,
None,
None,
2, # group 14
2,
2,
2,
2
]
all_ascii = ''.join(map(chr, range(256)))
_HEX_PRIMES = [
None,
# group 1
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF''',
# group 2
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
FFFFFFFF FFFFFFFF''',
# XXX how do I obtain these?
None,
None,
# group 5
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF''',
None,
None,
None,
None,
None,
None,
None,
None,
# group 14
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AACAA68 FFFFFFFF FFFFFFFF''',
# group 15
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF''',
# group 16
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
FFFFFFFF FFFFFFFF''',
# group 17
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
6DCC4024 FFFFFFFF FFFFFFFF''',
# group 18
'''FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD
F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831
179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B
DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF
5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6
D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3
23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328
06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C
DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE
12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4
38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300
741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568
3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9
22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B
4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A
062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36
4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1
B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92
4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47
9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
60C980DD 98EDD3DF FFFFFFFF FFFFFFFF'''
]
_ALL_ASCII = ''.join(map(chr, range(256)))
def hex_to_decimal(stripee):
if not stripee:
return None
return int(stripee.translate(all_ascii).translate(str.maketrans("", "",
string.whitespace)), 16)
return int(stripee.translate(_ALL_ASCII).translate(
str.maketrans("", "", string.whitespace)), 16)
primes = list(map(hex_to_decimal, hex_primes))
primes = list(map(hex_to_decimal, _HEX_PRIMES))

View File

@ -247,11 +247,13 @@ class Events:
event.account = account
self.fire_event_added(event)
def remove_events(self, account, jid, event = None, types = []):
def remove_events(self, account, jid, event=None, types=None):
"""
If event is not specified, remove all events from this jid, optionally
only from given type return True if no such event found
"""
if types is None:
types = []
if account not in self._events:
return True
if jid not in self._events[account]:
@ -297,15 +299,19 @@ class Events:
self._events[account][new_jid] = self._events[account][old_jid]
del self._events[account][old_jid]
def get_nb_events(self, types = [], account = None):
def get_nb_events(self, types=None, account=None):
if types is None:
types = []
return self._get_nb_events(types = types, account = account)
def get_events(self, account, jid = None, types = []):
def get_events(self, account, jid=None, types=None):
"""
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 types is None:
types = []
if account not in self._events:
return []
if not jid:
@ -342,11 +348,12 @@ class Events:
first_event = event
return first_event
def _get_nb_events(self, account = None, jid = None, attribute = None, types
= []):
def _get_nb_events(self, account=None, jid=None, attribute=None, types=None):
"""
Return the number of pending events
"""
if types is None:
types = []
nb = 0
if account:
accounts = [account]
@ -411,11 +418,13 @@ class Events:
first_event = event
return first_account, first_jid, first_event
def get_nb_systray_events(self, types = []):
def get_nb_systray_events(self, types=None):
"""
Return the number of events displayed in roster
"""
return self._get_nb_events(attribute = 'systray', types = types)
if types is None:
types = []
return self._get_nb_events(attribute='systray', types=types)
def get_systray_events(self):
"""
@ -428,12 +437,14 @@ class Events:
events = self.get_systray_events()
return self._get_first_event_with_attribute(events)
def get_nb_roster_events(self, account = None, jid = None, types = []):
def get_nb_roster_events(self, account=None, jid=None, types=None):
"""
Return the number of events displayed in roster
"""
return self._get_nb_events(attribute = 'roster', account = account,
jid = jid, types = types)
if types is None:
types = []
return self._get_nb_events(attribute='roster', account=account,
jid=jid, types=types)
def get_roster_events(self):
"""

View File

@ -35,7 +35,6 @@ import uuid
from common import config
import nbxmpp
from common import defs
from common import ged as ged_module
interface = None # The actual interface (the gtk one for the moment)
@ -143,6 +142,10 @@ SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
# zeroconf account name
ZEROCONF_ACC_NAME = 'Local'
# These will be set in gajim.gui_interface.
idlequeue = None
socks5queue = None
HAVE_ZEROCONF = True
try:
__import__('avahi')

View File

@ -45,7 +45,6 @@ import socket
import time
import datetime
from gi.repository import GObject
from encodings.punycode import punycode_encode
from string import Template

View File

@ -28,19 +28,21 @@ Handles the jingle signalling protocol
# * config:
# - codecs
import logging
import nbxmpp
from common import helpers
from common import gajim
from common.jingle_session import JingleSession, JingleStates
if gajim.HAVE_FARSTREAM:
from common.jingle_rtp import JingleAudio, JingleVideo
from common.jingle_ft import JingleFileTransfer
from common.jingle_transport import JingleTransportSocks5, JingleTransportIBB
if gajim.HAVE_FARSTREAM:
from common.jingle_rtp import JingleAudio, JingleVideo
import logging
logger = logging.getLogger('gajim.c.jingle')
class ConnectionJingle(object):
"""
This object depends on that it is a part of Connection class.
@ -80,7 +82,7 @@ class ConnectionJingle(object):
try:
jid = helpers.get_full_jid_from_iq(stanza)
except helpers.InvalidFormat:
logger.warn('Invalid JID: %s, ignoring it' % stanza.getFrom())
logger.warning('Invalid JID: %s, ignoring it', stanza.getFrom())
return
id_ = stanza.getID()
if (jid, id_) in self.__iq_responses.keys():
@ -102,14 +104,14 @@ class ConnectionJingle(object):
if sid not in self._sessions:
#TODO: tie-breaking and other things...
newjingle = JingleSession(con=self, weinitiate=False, jid=jid,
iq_id=id_, sid=sid)
iq_id=id_, sid=sid)
self._sessions[sid] = newjingle
# we already have such session in dispatcher...
self._sessions[sid].collect_iq_id(id_)
self._sessions[sid].on_stanza(stanza)
# Delete invalid/unneeded sessions
if sid in self._sessions and \
self._sessions[sid].state == JingleStates.ended:
self._sessions[sid].state == JingleStates.ENDED:
self.delete_jingle_session(sid)
raise nbxmpp.NodeProcessed
@ -132,20 +134,20 @@ class ConnectionJingle(object):
jingle = self.get_jingle_session(jid, media='audio')
if jingle:
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
out_xid=out_xid))
out_xid=out_xid))
else:
jingle = JingleSession(self, weinitiate=True, jid=jid)
self._sessions[jingle.sid] = jingle
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
out_xid=out_xid))
out_xid=out_xid))
jingle.start_session()
return jingle.sid
def start_file_transfer(self, jid, file_props, request=False):
logger.info("start file transfer with file: %s" % file_props)
logger.info("start file transfer with file: %s", file_props)
contact = gajim.contacts.get_contact_with_highest_priority(self.name,
gajim.get_jid_without_resource(jid))
if gajim.contacts.is_gc_contact(self.name,jid):
gajim.get_jid_without_resource(jid))
if gajim.contacts.is_gc_contact(self.name, jid):
gcc = jid.split('/')
if len(gcc) == 2:
contact = gajim.contacts.get_gc_contact(self.name, gcc[0], gcc[1])
@ -162,7 +164,8 @@ class ConnectionJingle(object):
elif contact.supports(nbxmpp.NS_JINGLE_IBB):
transport = JingleTransportIBB()
c = JingleFileTransfer(jingle, transport=transport,
file_props=file_props, use_security=use_security)
file_props=file_props,
use_security=use_security)
file_props.algo = self.__hash_support(contact)
jingle.add_content('file' + helpers.get_random_string_16(), c)
jingle.start_session()

View File

@ -20,7 +20,6 @@ Handles Jingle contents (XEP 0166)
import os
from common import gajim
import nbxmpp
from common.jingle_transport import JingleTransportIBB
from .jingle_xtls import SELF_SIGNED_CERTIFICATE
from .jingle_xtls import load_cert_file
@ -38,7 +37,7 @@ class JingleContentSetupException(Exception):
"""
class JingleContent(object):
class JingleContent:
"""
An abstraction of content in Jingle sessions
"""
@ -49,8 +48,8 @@ class JingleContent(object):
# will be filled by JingleSession.add_content()
# don't uncomment these lines, we will catch more buggy code then
# (a JingleContent not added to session shouldn't send anything)
#self.creator = None
#self.name = None
self.creator = None
self.name = None
self.accepted = False
self.sent = False
self.negotiated = False
@ -60,35 +59,39 @@ class JingleContent(object):
self.senders = 'both' #FIXME
self.allow_sending = True # Used for stream direction, attribute 'senders'
# These were found by the Politie
self.file_props = None
self.use_security = None
self.callbacks = {
# these are called when *we* get stanzas
'content-accept': [self.__on_transport_info,
self.__on_content_accept],
'content-add': [self.__on_transport_info],
'content-modify': [],
'content-reject': [],
'content-remove': [],
'description-info': [],
'security-info': [],
'session-accept': [self.__on_transport_info,
self.__on_content_accept],
'session-info': [],
'session-initiate': [self.__on_transport_info],
'session-terminate': [],
'transport-info': [self.__on_transport_info],
'transport-replace': [self.__on_transport_replace],
'transport-accept': [],
'transport-reject': [],
'iq-result': [],
'iq-error': [],
# these are called when *we* sent these stanzas
'content-accept-sent': [self.__fill_jingle_stanza,
self.__on_content_accept],
'content-add-sent': [self.__fill_jingle_stanza],
'session-initiate-sent': [self.__fill_jingle_stanza],
'session-accept-sent': [self.__fill_jingle_stanza,
self.__on_content_accept],
'session-terminate-sent': [],
# these are called when *we* get stanzas
'content-accept': [self.__on_transport_info,
self.__on_content_accept],
'content-add': [self.__on_transport_info],
'content-modify': [],
'content-reject': [],
'content-remove': [],
'description-info': [],
'security-info': [],
'session-accept': [self.__on_transport_info,
self.__on_content_accept],
'session-info': [],
'session-initiate': [self.__on_transport_info],
'session-terminate': [],
'transport-info': [self.__on_transport_info],
'transport-replace': [self.__on_transport_replace],
'transport-accept': [],
'transport-reject': [],
'iq-result': [],
'iq-error': [],
# these are called when *we* sent these stanzas
'content-accept-sent': [self.__fill_jingle_stanza,
self.__on_content_accept],
'content-add-sent': [self.__fill_jingle_stanza],
'session-initiate-sent': [self.__fill_jingle_stanza],
'session-accept-sent': [self.__fill_jingle_stanza,
self.__on_content_accept],
'session-terminate-sent': [],
}
def is_ready(self):
@ -128,13 +131,15 @@ class JingleContent(object):
if candidates:
self.add_remote_candidates(candidates)
def __content(self, payload=[]):
def __content(self, payload=None):
"""
Build a XML content-wrapper for our data
"""
if payload is None:
payload = []
return nbxmpp.Node('content',
attrs={'name': self.name, 'creator': self.creator},
payload=payload)
attrs={'name': self.name, 'creator': self.creator},
payload=payload)
def send_candidate(self, candidate):
"""
@ -190,17 +195,16 @@ class JingleContent(object):
file_tag.addChild(node=node)
if self.file_props.type_ == 'r':
if self.file_props.hash_:
h = file_tag.addChild('hash', attrs={
'algo': self.file_props.algo}, namespace=nbxmpp.NS_HASHES,
payload=self.file_props.hash_)
file_tag.addChild('hash', attrs={'algo': self.file_props.algo},
namespace=nbxmpp.NS_HASHES,
payload=self.file_props.hash_)
else:
# if the file is less than 10 mb, then it is small
# lets calculate it right away
if self.file_props.size < 10000000 and not \
self.file_props.hash_:
h = self._calcHash()
if h:
file_tag.addChild(node=h)
if self.file_props.size < 10000000 and not self.file_props.hash_:
hash_data = content._compute_hash()
if hash_data:
file_tag.addChild(node=hash_data)
pjid = gajim.get_jid_without_resource(self.session.peerjid)
file_info = {'name' : self.file_props.name,
'file-name' : self.file_props.file_name,
@ -222,9 +226,9 @@ class JingleContent(object):
cert = load_cert_file(certpath)
if cert:
try:
digest_algo = cert.get_signature_algorithm().decode('utf-8'
).split('With')[0]
except AttributeError as e:
digest_algo = (cert.get_signature_algorithm()
.decode('utf-8').split('With')[0])
except AttributeError:
# Old py-OpenSSL is missing get_signature_algorithm
digest_algo = "sha256"
security.addChild('fingerprint').addData(cert.digest(
@ -239,5 +243,3 @@ class JingleContent(object):
def destroy(self):
self.callbacks = None
del self.session.contents[(self.creator, self.name)]

View File

@ -19,42 +19,47 @@
Handles Jingle File Transfer (XEP 0234)
"""
import os
import hashlib
from common import gajim
import nbxmpp
from common import configpaths
from . import jingle_xtls
from common.jingle_content import contents, JingleContent
from common.jingle_transport import *
from common import helpers
from common.socks5 import Socks5ReceiverClient, Socks5SenderClient
from common.connection_handlers_events import FileRequestReceivedEvent
import threading
import logging
from common.jingle_ftstates import *
import os
import threading
from enum import IntEnum
import nbxmpp
from common import gajim
from common import configpaths
from common import jingle_xtls
from common.jingle_content import contents, JingleContent
from common.jingle_transport import JingleTransportSocks5, TransportType
from common import helpers
from common.connection_handlers_events import FileRequestReceivedEvent
from common.jingle_ftstates import (
StateInitialized, StateCandSent, StateCandReceived, StateTransfering,
StateCandSentAndRecv, StateTransportReplace)
log = logging.getLogger('gajim.c.jingle_ft')
STATE_NOT_STARTED = 0
STATE_INITIALIZED = 1
# We send the candidates and we are waiting for a reply
STATE_CAND_SENT = 2
# We received the candidates and we are waiting to reply
STATE_CAND_RECEIVED = 3
# We have sent and received the candidates
# This also includes any candidate-error received or sent
STATE_CAND_SENT_AND_RECEIVED = 4
STATE_TRANSPORT_REPLACE = 5
# We are transfering the file
STATE_TRANSFERING = 6
class State(IntEnum):
NOT_STARTED = 0
INITIALIZED = 1
# We send the candidates and we are waiting for a reply
CAND_SENT = 2
# We received the candidates and we are waiting to reply
CAND_RECEIVED = 3
# We have sent and received the candidates
# This also includes any candidate-error received or sent
CAND_SENT_AND_RECEIVED = 4
TRANSPORT_REPLACE = 5
# We are transfering the file
TRANSFERING = 6
class JingleFileTransfer(JingleContent):
def __init__(self, session, transport=None, file_props=None,
use_security=False):
use_security=False):
JingleContent.__init__(self, session, transport)
log.info("transport value: %s" % transport)
log.info("transport value: %s", transport)
# events we might be interested in
self.callbacks['session-initiate'] += [self.__on_session_initiate]
self.callbacks['session-initiate-sent'] += [
@ -86,29 +91,31 @@ class JingleFileTransfer(JingleContent):
self.file_props.sid = session.sid
self.file_props.transfered_size = []
self.file_props.transport_sid = self.transport.sid
log.info("FT request: %s" % file_props)
log.info("FT request: %s", file_props)
if transport is None:
self.transport = JingleTransportSocks5()
self.transport.set_connection(session.connection)
self.transport.set_file_props(self.file_props)
self.transport.set_our_jid(session.ourjid)
log.info('ourjid: %s' % session.ourjid)
log.info('ourjid: %s', session.ourjid)
self.session = session
self.media = 'file'
self.nominated_cand = {}
if gajim.contacts.is_gc_contact(session.connection.name,
session.peerjid):
session.peerjid):
roomjid = session.peerjid.split('/')[0]
dstaddr = hashlib.sha1(('%s%s%s' % (self.file_props.sid,
session.ourjid, roomjid)).encode('utf-8')).hexdigest()
session.ourjid, roomjid))
.encode('utf-8')).hexdigest()
self.file_props.dstaddr = dstaddr
self.state = STATE_NOT_STARTED
self.states = {STATE_INITIALIZED : StateInitialized(self),
STATE_CAND_SENT : StateCandSent(self),
STATE_CAND_RECEIVED : StateCandReceived(self),
STATE_TRANSFERING : StateTransfering(self),
STATE_TRANSPORT_REPLACE : StateTransportReplace(self),
STATE_CAND_SENT_AND_RECEIVED : StateCandSentAndRecv(self)
self.state = State.NOT_STARTED
self.states = {
State.INITIALIZED : StateInitialized(self),
State.CAND_SENT : StateCandSent(self),
State.CAND_RECEIVED : StateCandReceived(self),
State.TRANSFERING : StateTransfering(self),
State.TRANSPORT_REPLACE : StateTransportReplace(self),
State.CAND_SENT_AND_RECEIVED : StateCandSentAndRecv(self)
}
if jingle_xtls.PYOPENSSL_PRESENT:
@ -130,8 +137,10 @@ class JingleFileTransfer(JingleContent):
def __on_session_initiate(self, stanza, content, error, action):
log.debug("Jingle FT request received")
gajim.nec.push_incoming_event(FileRequestReceivedEvent(None,
conn=self.session.connection, stanza=stanza, jingle_content=content,
FT_content=self))
conn=self.session.connection,
stanza=stanza,
jingle_content=content,
FT_content=self))
if self.session.request:
# accept the request
self.session.approve_content(self.media, self.name)
@ -142,10 +151,11 @@ class JingleFileTransfer(JingleContent):
def __send_hash(self):
# Send hash in a session info
checksum = nbxmpp.Node(tag='checksum', payload=[nbxmpp.Node(tag='file',
payload=[self._calcHash()])])
checksum = nbxmpp.Node(tag='checksum',
payload=[nbxmpp.Node(tag='file',
payload=[self._compute_hash()])])
checksum.setNamespace(nbxmpp.NS_JINGLE_FILE_TRANSFER)
self.session.__session_info(checksum )
self.session.__session_info(checksum)
pjid = gajim.get_jid_without_resource(self.session.peerjid)
file_info = {'name' : self.file_props.name,
'file-name' : self.file_props.file_name,
@ -156,13 +166,13 @@ class JingleFileTransfer(JingleContent):
}
self.session.connection.set_file_info(file_info)
def _calcHash(self):
def _compute_hash(self):
# Caculates the hash and returns a xep-300 hash stanza
if self.file_props.algo == None:
if self.file_props.algo is None:
return
try:
file_ = open(self.file_props.file_name, 'rb')
except:
except IOError:
# can't open file
return
h = nbxmpp.Hashes()
@ -193,30 +203,31 @@ class JingleFileTransfer(JingleContent):
fingerprint = fingerprint.getData()
self.x509_fingerprint = fingerprint
if not jingle_xtls.check_cert(gajim.get_jid_without_resource(
self.session.responder), fingerprint):
self.session.responder), fingerprint):
id_ = jingle_xtls.send_cert_request(con,
self.session.responder)
self.session.responder)
jingle_xtls.key_exchange_pend(id_,
self.continue_session_accept, [stanza])
self.continue_session_accept,
[stanza])
raise nbxmpp.NodeProcessed
self.continue_session_accept(stanza)
def continue_session_accept(self, stanza):
con = self.session.connection
if self.state == STATE_TRANSPORT_REPLACE:
if self.state == State.TRANSPORT_REPLACE:
# If we are requesting we don't have the file
if self.session.werequest:
raise nbxmpp.NodeProcessed
# We send the file
self.__state_changed(STATE_TRANSFERING)
self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed
self.file_props.streamhosts = self.transport.remote_candidates
# Calculate file hash in a new thread
# if we haven't sent the hash already.
if self.file_props.hash_ is None and self.file_props.algo and \
not self.werequest:
self.hashThread = threading.Thread(target=self.__send_hash)
self.hashThread.start()
not self.werequest:
self.hash_thread = threading.Thread(target=self.__send_hash)
self.hash_thread.start()
for host in self.file_props.streamhosts:
host['initiator'] = self.session.initiator
host['target'] = self.session.responder
@ -226,11 +237,13 @@ class JingleFileTransfer(JingleContent):
fingerprint = 'client'
if self.transport.type_ == TransportType.SOCKS5:
gajim.socks5queue.connect_to_hosts(self.session.connection.name,
self.file_props.sid, self.on_connect,
self._on_connect_error, fingerprint=fingerprint,
receiving=False)
self.file_props.sid,
self.on_connect,
self._on_connect_error,
fingerprint=fingerprint,
receiving=False)
raise nbxmpp.NodeProcessed
self.__state_changed(STATE_TRANSFERING)
self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed
def __on_session_terminate(self, stanza, content, error, action):
@ -250,78 +263,78 @@ class JingleFileTransfer(JingleContent):
def __on_transport_info(self, stanza, content, error, action):
log.info("__on_transport_info")
candError = content.getTag('transport').getTag('candidate-error')
candUsed = content.getTag('transport').getTag('candidate-used')
if (candError or candUsed) and \
self.state >= STATE_CAND_SENT_AND_RECEIVED:
cand_error = content.getTag('transport').getTag('candidate-error')
cand_used = content.getTag('transport').getTag('candidate-used')
if (cand_error or cand_used) and \
self.state >= State.CAND_SENT_AND_RECEIVED:
raise nbxmpp.NodeProcessed
if candError:
if cand_error:
if not gajim.socks5queue.listener.connections:
gajim.socks5queue.listener.disconnect()
self.nominated_cand['peer-cand'] = False
if self.state == STATE_CAND_SENT:
if self.state == State.CAND_SENT:
if not self.nominated_cand['our-cand'] and \
not self.nominated_cand['peer-cand']:
if not self.weinitiate:
return
self.__state_changed(STATE_TRANSPORT_REPLACE)
self.__state_changed(State.TRANSPORT_REPLACE)
else:
response = stanza.buildReply('result')
response.delChild(response.getQuery())
self.session.connection.connection.send(response)
self.__state_changed(STATE_TRANSFERING)
self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed
else:
args = {'candError' : True}
self.__state_changed(STATE_CAND_RECEIVED, args)
args = {'cand_error' : True}
self.__state_changed(State.CAND_RECEIVED, args)
return
if candUsed:
streamhost_cid = candUsed.getAttr('cid')
if cand_used:
streamhost_cid = cand_used.getAttr('cid')
streamhost_used = None
for cand in self.transport.candidates:
if cand['candidate_id'] == streamhost_cid:
streamhost_used = cand
break
if streamhost_used == None or streamhost_used['type'] == 'proxy':
if streamhost_used is None or streamhost_used['type'] == 'proxy':
if gajim.socks5queue.listener and \
not gajim.socks5queue.listener.connections:
gajim.socks5queue.listener.disconnect()
if content.getTag('transport').getTag('activated'):
self.state = STATE_TRANSFERING
self.state = State.TRANSFERING
jid = gajim.get_jid_without_resource(self.session.ourjid)
gajim.socks5queue.send_file(self.file_props,
self.session.connection.name, 'client')
self.session.connection.name, 'client')
return
args = {'content' : content,
'sendCand' : False}
if self.state == STATE_CAND_SENT:
self.__state_changed(STATE_CAND_SENT_AND_RECEIVED, args)
self.__state_changed(STATE_TRANSFERING)
args = {'content': content,
'sendCand': False}
if self.state == State.CAND_SENT:
self.__state_changed(State.CAND_SENT_AND_RECEIVED, args)
self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed
else:
self.__state_changed(STATE_CAND_RECEIVED, args)
self.__state_changed(State.CAND_RECEIVED, args)
def __on_iq_result(self, stanza, content, error, action):
log.info("__on_iq_result")
if self.state == STATE_NOT_STARTED:
self.__state_changed(STATE_INITIALIZED)
elif self.state == STATE_CAND_SENT_AND_RECEIVED:
if self.state == State.NOT_STARTED:
self.__state_changed(State.INITIALIZED)
elif self.state == State.CAND_SENT_AND_RECEIVED:
if not self.nominated_cand['our-cand'] and \
not self.nominated_cand['peer-cand']:
if not self.weinitiate:
return
self.__state_changed(STATE_TRANSPORT_REPLACE)
self.__state_changed(State.TRANSPORT_REPLACE)
return
# initiate transfer
self.__state_changed(STATE_TRANSFERING)
self.__state_changed(State.TRANSFERING)
def __transport_setup(self, stanza=None, content=None, error=None,
action=None):
action=None):
# Sets up a few transport specific things for the file transfer
if self.transport.type_ == TransportType.IBB:
# No action required, just set the state to transfering
self.state = STATE_TRANSFERING
self.state = State.TRANSFERING
else:
self._listen_host()
@ -335,19 +348,19 @@ class JingleFileTransfer(JingleContent):
args = {'streamhost' : streamhost,
'sendCand' : True}
self.nominated_cand['our-cand'] = streamhost
self.__sendCand(args)
self.__send_candidate(args)
def _on_connect_error(self, sid):
log.info('connect error, sid=' + sid)
args = {'candError' : True,
'sendCand' : True}
self.__sendCand(args)
self.__send_candidate(args)
def __sendCand(self, args):
if self.state == STATE_CAND_RECEIVED:
self.__state_changed(STATE_CAND_SENT_AND_RECEIVED, args)
def __send_candidate(self, args):
if self.state == State.CAND_RECEIVED:
self.__state_changed(State.CAND_SENT_AND_RECEIVED, args)
else:
self.__state_changed(STATE_CAND_SENT, args)
self.__state_changed(State.CAND_SENT, args)
def _store_socks5_sid(self, sid, hash_id):
# callback from socsk5queue.start_listener
@ -357,44 +370,40 @@ class JingleFileTransfer(JingleContent):
receiver = self.file_props.receiver
sender = self.file_props.sender
sha_str = helpers.get_auth_sha(self.file_props.sid, sender,
receiver)
receiver)
self.file_props.sha_str = sha_str
port = gajim.config.get('file_transfers_port')
fingerprint = None
if self.use_security:
fingerprint = 'server'
if self.weinitiate:
listener = gajim.socks5queue.start_listener(port, sha_str,
self._store_socks5_sid, self.file_props,
fingerprint=fingerprint, typ='sender')
else:
listener = gajim.socks5queue.start_listener(port, sha_str,
self._store_socks5_sid, self.file_props,
fingerprint=fingerprint, typ='receiver')
listener = gajim.socks5queue.start_listener(port, sha_str,
self._store_socks5_sid,
self.file_props,
fingerprint=fingerprint,
typ='sender' if self.weinitiate else 'receiver')
if not listener:
# send error message, notify the user
return
def isOurCandUsed(self):
def is_our_candidate_used(self):
'''
If this method returns true then the candidate we nominated will be
used, if false, the candidate nominated by peer will be used
'''
if self.nominated_cand['peer-cand'] == False:
if not self.nominated_cand['peer-cand']:
return True
if self.nominated_cand['our-cand'] == False:
if not self.nominated_cand['our-cand']:
return False
peer_pr = int(self.nominated_cand['peer-cand']['priority'])
our_pr = int(self.nominated_cand['our-cand']['priority'])
if peer_pr != our_pr:
return our_pr > peer_pr
else:
return self.weinitiate
return self.weinitiate
def start_IBB_transfer(self):
def start_ibb_transfer(self):
if self.file_props.type_ == 's':
self.__state_changed(STATE_TRANSFERING)
self.__state_changed(State.TRANSFERING)
def get_content(desc):

View File

@ -13,11 +13,14 @@
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from common import gajim
import nbxmpp
from common.jingle_transport import *
from common import gajim
from common.jingle_transport import TransportType
from common.socks5 import Socks5ReceiverClient, Socks5SenderClient
import logging
log = logging.getLogger('gajim.c.jingle_ftstates')
class JingleFileTransferStates:
'''
@ -31,7 +34,7 @@ class JingleFileTransferStates:
'''
This method MUST be overriden by a subclass
'''
raise Exception('This is an abstract method!!')
raise NotImplementedError('This is an abstract method!')
class StateInitialized(JingleFileTransferStates):
@ -50,8 +53,10 @@ class StateInitialized(JingleFileTransferStates):
fingerprint = 'client'
# Connect to the candidate host, on success call on_connect method
gajim.socks5queue.connect_to_hosts(self.jft.session.connection.name,
self.jft.file_props.sid, self.jft.on_connect,
self.jft._on_connect_error, fingerprint=fingerprint)
self.jft.file_props.sid,
self.jft.on_connect,
self.jft._on_connect_error,
fingerprint=fingerprint)
class StateCandSent(JingleFileTransferStates):
@ -59,7 +64,7 @@ class StateCandSent(JingleFileTransferStates):
This state sends our nominated candidate
'''
def _sendCand(self, args):
def _send_candidate(self, args):
if 'candError' in args:
self.jft.nominated_cand['our-cand'] = False
self.jft.send_error_candidate()
@ -80,16 +85,16 @@ class StateCandSent(JingleFileTransferStates):
self.jft.session.send_transport_info(content)
def action(self, args=None):
self._sendCand(args)
self._send_candidate(args)
class StateCandReceived(JingleFileTransferStates):
class StateCandReceived(JingleFileTransferStates):
'''
This state happens when we receive a candidate.
It takes the arguments: canError if we receive a candidate-error
'''
def _recvCand(self, args):
def _recv_candidate(self, args):
if 'candError' in args:
return
content = args['content']
@ -100,17 +105,17 @@ class StateCandReceived(JingleFileTransferStates):
if cand['candidate_id'] == streamhost_cid:
streamhost_used = cand
break
if streamhost_used == None:
if streamhost_used is None:
log.info("unknow streamhost")
return
# We save the candidate nominated by peer
self.jft.nominated_cand['peer-cand'] = streamhost_used
def action(self, args=None):
self._recvCand(args)
self._recv_candidate(args)
class StateCandSentAndRecv( StateCandSent, StateCandReceived):
class StateCandSentAndRecv(StateCandSent, StateCandReceived):
'''
This state happens when we have received and sent the candidates.
It takes the boolean argument: sendCand in order to decide whether
@ -119,9 +124,9 @@ class StateCandSentAndRecv( StateCandSent, StateCandReceived):
def action(self, args=None):
if args['sendCand']:
self._sendCand(args)
self._send_candidate(args)
else:
self._recvCand(args)
self._recv_candidate(args)
class StateTransportReplace(JingleFileTransferStates):
@ -139,16 +144,16 @@ class StateTransfering(JingleFileTransferStates):
we have.
'''
def __start_IBB_transfer(self, con):
def _start_ibb_transfer(self, con):
self.jft.file_props.transport_sid = self.jft.transport.sid
fp = open(self.jft.file_props.file_name, 'r')
con.OpenStream( self.jft.file_props.sid, self.jft.session.peerjid, fp,
blocksize=4096)
con.OpenStream(self.jft.file_props.sid, self.jft.session.peerjid, fp,
blocksize=4096)
def __start_SOCK5_transfer(self):
def _start_sock5_transfer(self):
# It tells wether we start the transfer as client or server
mode = None
if self.jft.isOurCandUsed():
if self.jft.is_our_candidate_used():
mode = 'client'
streamhost_used = self.jft.nominated_cand['our-cand']
gajim.socks5queue.remove_server(self.jft.file_props.sid)
@ -191,34 +196,34 @@ class StateTransfering(JingleFileTransferStates):
gajim.socks5queue.idx += 1
idx = gajim.socks5queue.idx
sockobj = Socks5SenderClient(gajim.idlequeue, idx,
gajim.socks5queue, _sock=None,
host=str(streamhost_used['host']),
port=int(streamhost_used['port']), fingerprint=None,
connected=False, file_props=self.jft.file_props)
gajim.socks5queue, _sock=None,
host=str(streamhost_used['host']),
port=int(streamhost_used['port']),
fingerprint=None, connected=False,
file_props=self.jft.file_props)
else:
sockobj = Socks5ReceiverClient(gajim.idlequeue, streamhost_used,
sid=self.jft.file_props.sid,
file_props=self.jft.file_props, fingerprint=None)
sid=self.jft.file_props.sid,
file_props=self.jft.file_props,
fingerprint=None)
sockobj.proxy = True
sockobj.streamhost = streamhost_used
gajim.socks5queue.add_sockobj(self.jft.session.connection.name,
sockobj)
sockobj)
streamhost_used['idx'] = sockobj.queue_idx
# If we offered the nominated candidate used, we activate
# the proxy
if not self.jft.isOurCandUsed():
if not self.jft.is_our_candidate_used():
gajim.socks5queue.on_success[self.jft.file_props.sid] = \
self.jft.transport._on_proxy_auth_ok
# TODO: add on failure
else:
jid = gajim.get_jid_without_resource(self.jft.session.ourjid)
gajim.socks5queue.send_file(self.jft.file_props,
self.jft.session.connection.name, mode)
self.jft.session.connection.name, mode)
def action(self, args=None):
if self.jft.transport.type_ == TransportType.IBB:
self.__start_IBB_transfer(self.jft.session.connection)
self._start_ibb_transfer(self.jft.session.connection)
elif self.jft.transport.type_ == TransportType.SOCKS5:
self.__start_SOCK5_transfer()
self._start_sock5_transfer()

View File

@ -17,6 +17,7 @@
Handles Jingle RTP sessions (XEP 0167)
"""
import logging
import socket
import nbxmpp
import gi
@ -35,7 +36,6 @@ from common.jingle_session import FailedApplication
from collections import deque
import logging
log = logging.getLogger('gajim.c.jingle_rtp')
@ -46,7 +46,8 @@ class JingleRTPContent(JingleContent):
JingleContent.__init__(self, session, transport)
self.media = media
self._dtmf_running = False
self.farstream_media = {'audio': Farstream.MediaType.AUDIO,
self.farstream_media = {
'audio': Farstream.MediaType.AUDIO,
'video': Farstream.MediaType.VIDEO}[media]
self.pipeline = None
@ -55,6 +56,12 @@ class JingleRTPContent(JingleContent):
self.candidates_ready = False # True when local candidates are prepared
# TODO
self.conference = None
self.funnel = None
self.p2psession = None
self.p2pstream = None
self.callbacks['session-initiate'] += [self.__on_remote_codecs]
self.callbacks['content-add'] += [self.__on_remote_codecs]
self.callbacks['description-info'] += [self.__on_remote_codecs]
@ -90,32 +97,32 @@ class JingleRTPContent(JingleContent):
if stun_server:
try:
ip = socket.getaddrinfo(stun_server, 0, socket.AF_UNSPEC,
socket.SOCK_STREAM)[0][4][0]
socket.SOCK_STREAM)[0][4][0]
except socket.gaierror as e:
log.warning('Lookup of stun ip failed: %s' % str(e))
log.warning('Lookup of stun ip failed: %s', str(e))
else:
params['stun-ip'] = ip
self.p2pstream = self.p2psession.new_stream(participant,
Farstream.StreamDirection.BOTH)
Farstream.StreamDirection.BOTH)
self.p2pstream.connect('src-pad-added', on_src_pad_added)
self.p2pstream.set_transmitter_ht('nice', params)
def is_ready(self):
return (JingleContent.is_ready(self) and self.candidates_ready)
return JingleContent.is_ready(self) and self.candidates_ready
def make_bin_from_config(self, config_key, pipeline, text):
pipeline = pipeline % gajim.config.get(config_key)
try:
bin = Gst.parse_bin_from_description(pipeline, True)
return bin
gst_bin = Gst.parse_bin_from_description(pipeline, True)
return gst_bin
except GLib.GError as e:
gajim.nec.push_incoming_event(InformationEvent(None,
conn=self.session.connection, level='error',
pri_txt=_('%s configuration error') % text.capitalize(),
sec_txt=_("Couldn't setup %s. Check your configuration.\n\n"
"Pipeline was:\n%s\n\nError was:\n%s") % (text, pipeline,
str(e))))
gajim.nec.push_incoming_event(
InformationEvent(
None, conn=self.session.connection, level='error',
pri_txt=_('%s configuration error') % text.capitalize(),
sec_txt=_('Couldnt setup %s. Check your configuration.\n\n'
'Pipeline was:\n%s\n\nError was:\n%s') % (text, pipeline, str(e))))
raise JingleContentSetupException
def add_remote_candidates(self, candidates):
@ -147,7 +154,7 @@ class JingleRTPContent(JingleContent):
def _start_dtmf(self, event):
if event in ('*', '#'):
event = {'*': Farstream.DTMFEvent.STAR,
'#': Farstream.DTMFEvent.POUND}[event]
'#': Farstream.DTMFEvent.POUND}[event]
else:
event = int(event)
self.p2psession.start_telephony_event(event, 2)
@ -157,7 +164,8 @@ class JingleRTPContent(JingleContent):
def _fill_content(self, content):
content.addChild(nbxmpp.NS_JINGLE_RTP + ' description',
attrs={'media': self.media}, payload=list(self.iter_codecs()))
attrs={'media': self.media},
payload=list(self.iter_codecs()))
def _setup_funnel(self):
self.funnel = Gst.ElementFactory.make('funnel', None)
@ -174,7 +182,7 @@ class JingleRTPContent(JingleContent):
def _on_gst_message(self, bus, message):
if message.type == Gst.MessageType.ELEMENT:
name = message.get_structure().get_name()
log.debug('gst element message: %s: %s' % (name, message))
log.debug('gst element message: %s: %s', name, message)
if name == 'farstream-new-active-candidate-pair':
pass
elif name == 'farstream-recv-codecs-changed':
@ -189,7 +197,7 @@ class JingleRTPContent(JingleContent):
self.transport.remote_candidates)
self.transport.remote_candidates = []
self.p2pstream.set_property('direction',
Farstream.StreamDirection.BOTH)
Farstream.StreamDirection.BOTH)
elif name == 'farstream-local-candidates-prepared':
self.candidates_ready = True
@ -208,19 +216,21 @@ class JingleRTPContent(JingleContent):
reason.setTag('failed-transport')
self.session.remove_content(self.creator, self.name, reason)
elif name == 'farstream-error':
log.error('Farstream error #%d!\nMessage: %s' % (
message.get_structure().get_value('error-no'),
message.get_structure().get_value('error-msg')))
log.error('Farstream error #%d!\nMessage: %s',
message.get_structure().get_value('error-no'),
message.get_structure().get_value('error-msg'))
elif message.type == Gst.MessageType.ERROR:
# TODO: Fix it to fallback to videotestsrc anytime an error occur,
# or raise an error, Jingle way
# or maybe one-sided stream?
if not self.stream_failed_once:
gajim.nec.push_incoming_event(InformationEvent(None,
conn=self.session.connection, level='error',
pri_txt=_('GStreamer error'), sec_txt=_('Error: %s\nDebug: '
'%s' % (message.get_structure().get_value('gerror'),
message.get_structure().get_value('debug')))))
gajim.nec.push_incoming_event(
InformationEvent(
None, conn=self.session.connection, level='error',
pri_txt=_('GStreamer error'),
sec_txt=_('Error: %s\nDebug: %s' %
(message.get_structure().get_value('gerror'),
message.get_structure().get_value('debug')))))
sink_pad = self.p2psession.get_property('sink-pad')
@ -243,7 +253,8 @@ class JingleRTPContent(JingleContent):
# Start playing again
self.pipeline.set_state(Gst.State.PLAYING)
def get_fallback_src(self):
@staticmethod
def get_fallback_src():
return Gst.ElementFactory.make('fakesrc', None)
def on_negotiated(self):
@ -270,7 +281,7 @@ class JingleRTPContent(JingleContent):
# ignore invalid payload-types
continue
c = Farstream.Codec.new(int(codec['id']), codec['name'],
self.farstream_media, int(codec['clockrate']))
self.farstream_media, int(codec['clockrate']))
if 'channels' in codec:
c.channels = int(codec['channels'])
else:
@ -288,14 +299,17 @@ class JingleRTPContent(JingleContent):
def iter_codecs(self):
codecs = self.p2psession.props.codecs_without_config
for codec in codecs:
attrs = {'name': codec.encoding_name,
attrs = {
'name': codec.encoding_name,
'id': codec.id,
'channels': codec.channels}
'channels': codec.channels
}
if codec.clock_rate:
attrs['clockrate'] = codec.clock_rate
if codec.optional_params:
payload = list(nbxmpp.Node('parameter', {'name': p.name,
'value': p.value}) for p in codec.optional_params)
payload = [nbxmpp.Node('parameter',
{'name': p.name, 'value': p.value})
for p in codec.optional_params]
else:
payload = []
yield nbxmpp.Node('payload-type', attrs, payload)
@ -343,19 +357,22 @@ class JingleAudio(JingleRTPContent):
# place 16kHz before 8kHz, as buggy psi versions will take in
# account only the first codec
codecs = [Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
Farstream.MediaType.AUDIO, 16000),
codecs = [
Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
Farstream.MediaType.AUDIO, 8000)]
Farstream.MediaType.AUDIO, 16000),
Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
Farstream.MediaType.AUDIO, 8000)]
self.p2psession.set_codec_preferences(codecs)
# the local parts
# TODO: Add queues?
self.src_bin = self.make_bin_from_config('audio_input_device',
'%s ! audioconvert', _("audio input"))
'%s ! audioconvert',
_("audio input"))
self.sink = self.make_bin_from_config('audio_output_device',
'audioconvert ! volume name=gajim_out_vol ! %s', _("audio output"))
'audioconvert ! volume name=gajim_out_vol ! %s',
_("audio output"))
self.mic_volume = self.src_bin.get_by_name('gajim_vol')
self.out_volume = self.sink.get_by_name('gajim_out_vol')
@ -411,15 +428,16 @@ class JingleVideo(JingleRTPContent):
tee = ''
self.src_bin = self.make_bin_from_config('video_input_device',
'%%s %s! %svideoscale ! %svideoconvert' % (tee, framerate,
video_size), _("video input"))
'%%s %s! %svideoscale ! %svideoconvert' %
(tee, framerate, video_size),
_("video input"))
self.pipeline.add(self.src_bin)
self.pipeline.set_state(Gst.State.PLAYING)
self.sink = self.make_bin_from_config('video_output_device',
'videoscale ! videoconvert ! %s',
_("video output"))
'videoscale ! videoconvert ! %s',
_("video output"))
self.pipeline.add(self.sink)

View File

@ -28,24 +28,28 @@ Handles Jingle sessions (XEP 0166)
# - Tie-breaking
# * timeout
from common import gajim
import nbxmpp
from common.jingle_transport import get_jingle_transport, JingleTransportIBB
from common.jingle_content import get_jingle_content, JingleContentSetupException
from common.jingle_content import JingleContent
from common.jingle_ft import STATE_TRANSPORT_REPLACE
from common.connection_handlers_events import *
import logging
from enum import Enum
import nbxmpp
from common import gajim
from common.jingle_transport import get_jingle_transport, JingleTransportIBB
from common.jingle_content import get_jingle_content, JingleContentSetupException, JingleContent
from common.jingle_ft import State
from common.connection_handlers_events import (
FilesProp, JingleRequestReceivedEvent, JingleDisconnectedReceivedEvent,
JingleTransferCancelledEvent, JingleConnectedReceivedEvent,
JingleErrorReceivedEvent)
log = logging.getLogger("gajim.c.jingle_session")
# FIXME: Move it to JingleSession.States?
class JingleStates(object):
class JingleStates(Enum):
"""
States in which jingle session may exist
"""
ended = 0
pending = 1
active = 2
ENDED = 0
PENDING = 1
ACTIVE = 2
class OutOfOrder(Exception):
"""
@ -61,17 +65,18 @@ class TieBreak(Exception):
class FailedApplication(Exception):
"""
Exception that should be raised in case responder supports none of the payload-types offered by the initiator
Exception that should be raised in case responder supports none of the
payload-types offered by the initiator
"""
class JingleSession(object):
class JingleSession:
"""
This represents one jingle session, that is, one or more content types
negotiated between an initiator and a responder.
"""
def __init__(self, con, weinitiate, jid, iq_id=None, sid=None,
werequest=False):
werequest=False):
"""
con -- connection object,
weinitiate -- boolean, are we the initiator?
@ -85,16 +90,16 @@ class JingleSession(object):
self.ourjid = self.ourjid + '/' + con.server_resource
self.peerjid = jid # jid we connect to
# jid we use as the initiator
self.initiator = weinitiate and self.ourjid or self.peerjid
self.initiator = self.ourjid if weinitiate else self.peerjid
# jid we use as the responder
self.responder = weinitiate and self.peerjid or self.ourjid
self.responder = self.peerjid if weinitiate else self.ourjid
# are we an initiator?
self.weinitiate = weinitiate
# Are we requesting or offering a file?
self.werequest = werequest
self.request = False
# what state is session in? (one from JingleStates)
self.state = JingleStates.ended
self.state = JingleStates.ENDED
if not sid:
sid = con.connection.getAnID()
self.sid = sid # sessionid
@ -106,37 +111,37 @@ class JingleSession(object):
self.iq_ids = []
self.accepted = True # is this session accepted by user
# Tells whether this session is a file transfer or not
self.session_type_FT = False
self.session_type_ft = False
# callbacks to call on proper contents
# use .prepend() to add new callbacks, especially when you're going
# to send error instead of ack
self.callbacks = {
'content-accept': [self.__ack, self.__on_content_accept,
self.__broadcast],
'content-add': [self.__ack,
self.__on_content_add, self.__broadcast
], #TODO
'content-modify': [self.__ack], #TODO
'content-reject': [self.__ack, self.__on_content_remove],
'content-remove': [self.__ack, self.__on_content_remove],
'description-info': [self.__ack, self.__broadcast], #TODO
'security-info': [self.__ack], #TODO
'session-accept': [self.__ack, self.__on_session_accept,
self.__on_content_accept,
self.__broadcast],
'session-info': [self.__ack, self.__broadcast,
self.__on_session_info ],
'session-initiate': [self.__ack, self.__on_session_initiate,
self.__broadcast],
'session-terminate': [self.__ack,self.__on_session_terminate,
self.__broadcast_all],
'transport-info': [self.__ack, self.__broadcast],
'transport-replace': [self.__ack, self.__broadcast,
self.__on_transport_replace], #TODO
'transport-accept': [self.__ack], #TODO
'transport-reject': [self.__ack], #TODO
'iq-result': [self.__broadcast],
'iq-error': [self.__on_error],
'content-accept': [self.__ack, self.__on_content_accept,
self.__broadcast],
'content-add': [self.__ack,
self.__on_content_add, self.__broadcast
], #TODO
'content-modify': [self.__ack], #TODO
'content-reject': [self.__ack, self.__on_content_remove],
'content-remove': [self.__ack, self.__on_content_remove],
'description-info': [self.__ack, self.__broadcast], #TODO
'security-info': [self.__ack], #TODO
'session-accept': [self.__ack, self.__on_session_accept,
self.__on_content_accept,
self.__broadcast],
'session-info': [self.__ack, self.__broadcast,
self.__on_session_info],
'session-initiate': [self.__ack, self.__on_session_initiate,
self.__broadcast],
'session-terminate': [self.__ack, self.__on_session_terminate,
self.__broadcast_all],
'transport-info': [self.__ack, self.__broadcast],
'transport-replace': [self.__ack, self.__broadcast,
self.__on_transport_replace], #TODO
'transport-accept': [self.__ack], #TODO
'transport-reject': [self.__ack], #TODO
'iq-result': [self.__broadcast],
'iq-error': [self.__on_error],
}
def collect_iq_id(self, iq_id):
@ -174,7 +179,7 @@ class JingleSession(object):
def reject_content(self, media, name=None):
content = self.get_content(media, name)
if content:
if self.state == JingleStates.active:
if self.state == JingleStates.ACTIVE:
self.__content_reject(content)
content.destroy()
self.on_session_state_changed()
@ -184,7 +189,7 @@ class JingleSession(object):
Called when user stops or cancel session in UI
"""
reason = nbxmpp.Node('reason')
if self.state == JingleStates.active:
if self.state == JingleStates.ACTIVE:
reason.addChild('success')
else:
reason.addChild('cancel')
@ -232,11 +237,11 @@ class JingleSession(object):
if not self.contents:
self.end_session()
def modify_content(self, creator, name, transport = None):
def modify_content(self, creator, name, transport=None):
'''
Currently used for transport replacement
'''
content = self.contents[(creator,name)]
content = self.contents[(creator, name)]
transport.set_sid(content.transport.sid)
transport.set_file_props(content.transport.file_props)
content.transport = transport
@ -245,11 +250,11 @@ class JingleSession(object):
content.accepted = True
def on_session_state_changed(self, content=None):
if self.state == JingleStates.ended:
if self.state == JingleStates.ENDED:
# Session not yet started, only one action possible: session-initiate
if self.is_ready() and self.weinitiate:
self.__session_initiate()
elif self.state == JingleStates.pending:
elif self.state == JingleStates.PENDING:
# We can either send a session-accept or a content-add
if self.is_ready() and not self.weinitiate:
self.__session_accept()
@ -257,7 +262,7 @@ class JingleSession(object):
self.__content_add(content)
elif content and self.weinitiate:
self.__content_accept(content)
elif self.state == JingleStates.active:
elif self.state == JingleStates.ACTIVE:
# We can either send a content-add or a content-accept. However, if
# we are sending a file we can only use session_initiate.
if not content:
@ -278,7 +283,7 @@ class JingleSession(object):
Return True when all codecs and candidates are ready (for all contents)
"""
return (any((content.is_ready() for content in self.contents.values()))
and self.accepted)
and self.accepted)
def accept_session(self):
"""
@ -298,20 +303,20 @@ class JingleSession(object):
pass
def send_content_accept(self, content):
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('content-accept')
jingle.addChild(node=content)
self.connection.connection.send(stanza)
def send_transport_info(self, content):
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('transport-info')
jingle.addChild(node=content)
self.connection.connection.send(stanza)
self.collect_iq_id(stanza.getID())
def send_description_info(self, content):
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('description-info')
jingle.addChild(node=content)
self.connection.connection.send(stanza)
@ -334,8 +339,8 @@ class JingleSession(object):
self.__send_error(stanza, 'bad-request')
return
# FIXME: If we aren't initiated and it's not a session-initiate...
if action not in ['session-initiate','session-terminate'] \
and self.state == JingleStates.ended:
if action not in ['session-initiate', 'session-terminate'] \
and self.state == JingleStates.ENDED:
self.__send_error(stanza, 'item-not-found', 'unknown-session')
return
else:
@ -381,7 +386,7 @@ class JingleSession(object):
transport = JingleTransportIBB()
# For debug only, delete this and replace for a function
# that will identify contents by its sid
for creator, name in self.contents.keys():
for creator, name in self.contents:
self.modify_content(creator, name, transport)
cont = self.contents[(creator, name)]
cont.transport = transport
@ -389,7 +394,7 @@ class JingleSession(object):
self.__append_contents(jingle)
self.__broadcast(stanza, jingle, None, 'transport-replace')
self.connection.connection.send(stanza)
self.state = JingleStates.pending
self.state = JingleStates.PENDING
def __on_transport_replace(self, stanza, jingle, error, action):
for content in jingle.iterTags('content'):
@ -405,15 +410,15 @@ class JingleSession(object):
elif transport_ns == nbxmpp.NS_JINGLE_IBB:
transport = JingleTransportIBB()
self.modify_content(creator, name, transport)
self.state = JingleStates.pending
self.contents[(creator,name)].state = STATE_TRANSPORT_REPLACE
self.state = JingleStates.PENDING
self.contents[(creator, name)].state = State.TRANSPORT_REPLACE
self.__ack(stanza, jingle, error, action)
self.__session_accept()
self.contents[(creator,name)].start_IBB_transfer()
self.contents[(creator, name)].start_IBB_transfer()
else:
stanza, jingle = self.__make_jingle('transport-reject')
content = jingle.setTag('content', attrs={'creator': creator,
'name': name})
'name': name})
content.setTag('transport', namespace=transport_ns)
self.connection.connection.send(stanza)
raise nbxmpp.NodeProcessed
@ -421,9 +426,9 @@ class JingleSession(object):
# 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})
c.setTag('transport', namespace=transport_ns)
content = jingle.setTag('content', attrs={'creator': creator,
'name': name})
content.setTag('transport', namespace=transport_ns)
self.connection.connection.send(stanza)
raise nbxmpp.NodeProcessed
@ -433,12 +438,12 @@ class JingleSession(object):
if payload[0].getName() == 'ringing':
# ignore ringing
raise nbxmpp.NodeProcessed
if self.state != JingleStates.active:
if self.state != JingleStates.ACTIVE:
raise OutOfOrder
for p in payload:
if p.getName() == 'checksum':
hash_ = p.getTag('file').getTag(name='hash',
namespace=nbxmpp.NS_HASHES)
for child in payload:
if child.getName() == 'checksum':
hash_ = child.getTag('file').getTag(name='hash',
namespace=nbxmpp.NS_HASHES)
algo = hash_.getAttr('algo')
if algo in nbxmpp.Hashes.supported:
file_props = FilesProp.getFileProp(self.connection.name,
@ -468,11 +473,12 @@ class JingleSession(object):
def __on_session_accept(self, stanza, jingle, error, action):
# FIXME
if self.state != JingleStates.pending:
if self.state != JingleStates.PENDING:
raise OutOfOrder
self.state = JingleStates.active
self.state = JingleStates.ACTIVE
def __on_content_accept(self, stanza, jingle, error, action):
@staticmethod
def __on_content_accept(stanza, jingle, error, action):
"""
Called when we get content-accept stanza or equivalent one (like
session-accept)
@ -484,7 +490,7 @@ class JingleSession(object):
name = content['name']
def __on_content_add(self, stanza, jingle, error, action):
if self.state == JingleStates.ended:
if self.state == JingleStates.ENDED:
raise OutOfOrder
parse_result = self.__parse_contents(jingle)
contents = parse_result[0]
@ -496,14 +502,16 @@ class JingleSession(object):
self.__content_reject(content)
self.contents[(content.creator, content.name)].destroy()
gajim.nec.push_incoming_event(JingleRequestReceivedEvent(None,
conn=self.connection, jingle_session=self, contents=contents))
conn=self.connection,
jingle_session=self,
contents=contents))
def __on_session_initiate(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
"""
if self.state != JingleStates.ended:
if self.state != JingleStates.ENDED:
raise OutOfOrder
self.initiator = jingle['initiator']
self.responder = self.ourjid
@ -519,7 +527,7 @@ class JingleSession(object):
# Check if there's already a session with this user:
if contents[0][0] != 'file':
for session in self.connection.iter_jingle_sessions(self.peerjid):
if not session is self:
if session is not self:
reason = nbxmpp.Node('reason')
alternative_session = reason.setTag('alternative-session')
alternative_session.setTagData('sid', session.sid)
@ -533,18 +541,17 @@ class JingleSession(object):
jingle.getTag('content').getTag('description').getTag('request')
if request:
self.request = True
h = request.getTag('file').getTag('hash')
h = h.getData() if h else None
hash_tag = request.getTag('file').getTag('hash')
hash_data = hash_tag.getData() if hash_tag else None
n = request.getTag('file').getTag('name')
n = n.getData() if n else None
pjid = gajim.get_jid_without_resource(self.peerjid)
file_info = self.connection.get_file_info(pjid, h, n,
self.connection.name)
file_info = self.connection.get_file_info(pjid, hash_data, n,
self.connection.name)
if not file_info:
log.warning('The peer ' + pjid + \
' is requesting a ' + \
'file that we dont have or ' + \
'it is not allowed to request')
log.warning('The peer %s is requesting a ' \
'file that we dont have or ' \
'it is not allowed to request', pjid)
self.decline_session()
raise nbxmpp.NodeProcessed
# If there's no content we understand...
@ -555,10 +562,12 @@ class JingleSession(object):
self.__ack(stanza, jingle, error, action)
self._session_terminate(reason)
raise nbxmpp.NodeProcessed
self.state = JingleStates.pending
self.state = JingleStates.PENDING
# Send event about starting a session
gajim.nec.push_incoming_event(JingleRequestReceivedEvent(None,
conn=self.connection, jingle_session=self, contents=contents))
conn=self.connection,
jingle_session=self,
contents=contents))
def __broadcast(self, stanza, jingle, error, action):
"""
@ -594,10 +603,12 @@ class JingleSession(object):
else:
# TODO
text = reason
if reason == 'cancel' and self.session_type_FT:
if reason == 'cancel' and self.session_type_ft:
gajim.nec.push_incoming_event(JingleTransferCancelledEvent(None,
conn=self.connection, jingle_session=self, media=None,
reason=text))
conn=self.connection,
jingle_session=self,
media=None,
reason=text))
def __broadcast_all(self, stanza, jingle, error, action):
"""
@ -621,7 +632,7 @@ class JingleSession(object):
if transport:
content = content_type(self, transport)
self.add_content(element['name'],
content, 'peer')
content, 'peer')
contents.append((content.media,))
else:
reasons.add('unsupported-transports')
@ -634,7 +645,7 @@ class JingleSession(object):
failure_reason = None
# Store the first reason of failure
for reason in ('failed-application', 'unsupported-transports',
'unsupported-applications'):
'unsupported-applications'):
if reason in reasons:
failure_reason = reason
break
@ -645,17 +656,21 @@ class JingleSession(object):
text = '%s (%s)' % (error, text)
if type_ != 'modify':
gajim.nec.push_incoming_event(JingleErrorReceivedEvent(None,
conn=self.connection, jingle_session=self,
reason=text or error))
conn=self.connection,
jingle_session=self,
reason=text or error))
def __reason_from_stanza(self, stanza):
@staticmethod
def __reason_from_stanza(stanza):
# TODO: Move to GUI?
reason = 'success'
reasons = ['success', 'busy', 'cancel', 'connectivity-error',
'decline', 'expired', 'failed-application', 'failed-transport',
'general-error', 'gone', 'incompatible-parameters', 'media-error',
'security-error', 'timeout', 'unsupported-applications',
'unsupported-transports']
reasons = [
'success', 'busy', 'cancel', 'connectivity-error', 'decline',
'expired', 'failed-application', 'failed-transport',
'general-error', 'gone', 'incompatible-parameters', 'media-error',
'security-error', 'timeout', 'unsupported-applications',
'unsupported-transports'
]
tag = stanza.getTag('reason')
text = ''
if tag:
@ -668,12 +683,14 @@ class JingleSession(object):
def __make_jingle(self, action, reason=None):
stanza = nbxmpp.Iq(typ='set', to=nbxmpp.JID(self.peerjid),
frm=self.ourjid)
attrs = {'action': action,
'sid': self.sid,
'initiator' : self.initiator}
frm=self.ourjid)
attrs = {
'action': action,
'sid': self.sid,
'initiator' : self.initiator
}
jingle = stanza.addChild('jingle', attrs=attrs,
namespace=nbxmpp.NS_JINGLE)
namespace=nbxmpp.NS_JINGLE)
if reason is not None:
jingle.addChild(node=reason)
return stanza, jingle
@ -690,13 +707,15 @@ class JingleSession(object):
self.connection.connection.send(err_stanza)
self.__dispatch_error(jingle_error or error, text, type_)
def __append_content(self, jingle, content):
@staticmethod
def __append_content(jingle, content):
"""
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})
attrs={'name': content.name,
'creator': content.creator})
def __append_contents(self, jingle):
"""
@ -709,36 +728,36 @@ class JingleSession(object):
self.__append_content(jingle, content)
def __session_initiate(self):
assert self.state == JingleStates.ended
assert self.state == JingleStates.ENDED
stanza, jingle = self.__make_jingle('session-initiate')
self.__append_contents(jingle)
self.__broadcast(stanza, jingle, None, 'session-initiate-sent')
self.connection.connection.send(stanza)
self.collect_iq_id(stanza.getID())
self.state = JingleStates.pending
self.state = JingleStates.PENDING
def __session_accept(self):
assert self.state == JingleStates.pending
assert self.state == JingleStates.PENDING
stanza, jingle = self.__make_jingle('session-accept')
self.__append_contents(jingle)
self.__broadcast(stanza, jingle, None, 'session-accept-sent')
self.connection.connection.send(stanza)
self.collect_iq_id(stanza.getID())
self.state = JingleStates.active
self.state = JingleStates.ACTIVE
def __session_info(self, payload=None):
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('session-info')
if payload:
jingle.addChild(node=payload)
self.connection.connection.send(stanza)
def _JingleFileTransfer__session_info(self, p):
def _JingleFileTransfer__session_info(self, payload):
# For some strange reason when I call
# self.session.__session_info(h) from the jingleFileTransfer object
# self.session.__session_info(payload) from the jingleFileTransfer object
# within a thread, this method gets called instead. Even though, it
# isn't being called explicitly.
self.__session_info(p)
self.__session_info(payload)
def _session_terminate(self, reason=None):
stanza, jingle = self.__make_jingle('session-terminate', reason=reason)
@ -755,12 +774,14 @@ class JingleSession(object):
text = reason
self.connection.delete_jingle_session(self.sid)
gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=None,
reason=text))
conn=self.connection,
jingle_session=self,
media=None,
reason=text))
def __content_add(self, content):
# TODO: test
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('content-add')
self.__append_content(jingle, content)
self.__broadcast(stanza, jingle, None, 'content-add-sent')
@ -769,7 +790,7 @@ class JingleSession(object):
def __content_accept(self, content):
# TODO: test
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('content-accept')
self.__append_content(jingle, content)
self.__broadcast(stanza, jingle, None, 'content-accept-sent')
@ -777,31 +798,35 @@ class JingleSession(object):
self.collect_iq_id(id_)
def __content_reject(self, content):
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
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
gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=content.media,
reason='rejected'))
conn=self.connection,
jingle_session=self,
media=content.media,
reason='rejected'))
def __content_modify(self):
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
def __content_remove(self, content, reason=None):
assert self.state != JingleStates.ended
assert self.state != JingleStates.ENDED
if self.connection.connection and self.connection.connected > 1:
stanza, jingle = self.__make_jingle('content-remove', reason=reason)
self.__append_content(jingle, content)
self.connection.connection.send(stanza)
# TODO: this will fail if content is not an RTP content
gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=content.media,
reason='removed'))
conn=self.connection,
jingle_session=self,
media=content.media,
reason='removed'))
def content_negotiated(self, media):
gajim.nec.push_incoming_event(JingleConnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=media))
conn=self.connection,
jingle_session=self,
media=media))

View File

@ -17,11 +17,11 @@
Handles Jingle Transports (currently only ICE-UDP)
"""
import nbxmpp
import socket
from common import gajim
from common.protocol.bytestream import ConnectionSocks5Bytestream
import logging
import socket
from enum import IntEnum
import nbxmpp
from common import gajim
log = logging.getLogger('gajim.c.jingle_transport')
@ -34,7 +34,7 @@ def get_jingle_transport(node):
return transports[namespace](node)
class TransportType(object):
class TransportType(IntEnum):
"""
Possible types of a JingleTransport
"""
@ -43,16 +43,24 @@ class TransportType(object):
IBB = 3
class JingleTransport(object):
class JingleTransport:
"""
An abstraction of a transport in Jingle sessions
"""
__slots__ = ['type_', 'candidates', 'remote_candidates', 'connection',
'file_props', 'ourjid', 'sid']
def __init__(self, type_):
self.type_ = type_
self.candidates = []
self.remote_candidates = []
self.connection = None
self.file_props = None
self.ourjid = None
self.sid = None
def _iter_candidates(self):
for candidate in self.candidates:
yield self.make_candidate(candidate)
@ -110,9 +118,7 @@ class JingleTransportSocks5(JingleTransport):
def make_candidate(self, candidate):
import logging
log = logging.getLogger()
log.info('candidate dict, %s' % candidate)
log.info('candidate dict, %s', candidate)
attrs = {
'cid': candidate['candidate_id'],
'host': candidate['host'],
@ -124,8 +130,8 @@ class JingleTransportSocks5(JingleTransport):
return nbxmpp.Node('candidate', attrs=attrs)
def make_transport(self, candidates=None, add_candidates = True):
if add_candidates:
def make_transport(self, candidates=None, add_candidates=True):
if add_candidates:
self._add_local_ips_as_candidates()
self._add_additional_candidates()
self._add_proxy_candidates()
@ -174,7 +180,7 @@ class JingleTransportSocks5(JingleTransport):
def _add_local_ips_as_candidates(self):
if not gajim.config.get_per('accounts', self.connection.name,
'ft_send_local_ips'):
'ft_send_local_ips'):
return
if not self.connection:
return
@ -186,30 +192,34 @@ class JingleTransportSocks5(JingleTransport):
hosts = set()
local_ip_cand = []
c = {'host': self.connection.peerhost[0],
'candidate_id': self.connection.connection.getAnID(),
'port': port,
'type': 'direct',
'jid': self.ourjid,
'priority': priority}
candidate = {
'host': self.connection.peerhost[0],
'candidate_id': self.connection.connection.getAnID(),
'port': port,
'type': 'direct',
'jid': self.ourjid,
'priority': priority
}
hosts.add(self.connection.peerhost[0])
local_ip_cand.append(c)
local_ip_cand.append(candidate)
try:
for addrinfo in socket.getaddrinfo(socket.gethostname(), None):
addr = addrinfo[4][0]
if not addr in hosts and not addr.startswith('127.') and \
addr != '::1':
c = {'host': addr,
'candidate_id': self.connection.connection.getAnID(),
'port': port,
'type': 'direct',
'jid': self.ourjid,
'priority': priority,
'initiator': self.file_props.sender,
'target': self.file_props.receiver}
candidate = {
'host': addr,
'candidate_id': self.connection.connection.getAnID(),
'port': port,
'type': 'direct',
'jid': self.ourjid,
'priority': priority,
'initiator': self.file_props.sender,
'target': self.file_props.receiver
}
hosts.add(addr)
local_ip_cand.append(c)
local_ip_cand.append(candidate)
except socket.gaierror:
pass # ignore address-related errors for getaddrinfo
@ -226,16 +236,18 @@ class JingleTransportSocks5(JingleTransport):
if ft_add_hosts:
hosts = [e.strip() for e in ft_add_hosts.split(',')]
for h in hosts:
c = {'host': h,
'candidate_id': self.connection.connection.getAnID(),
'port': port,
'type': 'direct',
'jid': self.ourjid,
'priority': priority,
'initiator': self.file_props.sender,
'target': self.file_props.receiver}
additional_ip_cand.append(c)
for host in hosts:
candidate = {
'host': host,
'candidate_id': self.connection.connection.getAnID(),
'port': port,
'type': 'direct',
'jid': self.ourjid,
'priority': priority,
'initiator': self.file_props.sender,
'target': self.file_props.receiver
}
additional_ip_cand.append(candidate)
self._add_candidates(additional_ip_cand)
@ -252,21 +264,23 @@ class JingleTransportSocks5(JingleTransport):
self.file_props.proxyhosts = proxyhosts
for proxyhost in proxyhosts:
c = {'host': proxyhost['host'],
'candidate_id': self.connection.connection.getAnID(),
'port': int(proxyhost['port']),
'type': 'proxy',
'jid': proxyhost['jid'],
'priority': priority,
'initiator': self.file_props.sender,
'target': self.file_props.receiver}
proxy_cand.append(c)
candidate = {
'host': proxyhost['host'],
'candidate_id': self.connection.connection.getAnID(),
'port': int(proxyhost['port']),
'type': 'proxy',
'jid': proxyhost['jid'],
'priority': priority,
'initiator': self.file_props.sender,
'target': self.file_props.receiver
}
proxy_cand.append(candidate)
self._add_candidates(proxy_cand)
def get_content(self):
sesn = self.connection.get_jingle_session(self.ourjid,
self.file_props.sid)
self.file_props.sid)
for content in sesn.contents.values():
if content.transport == self:
return content
@ -277,7 +291,7 @@ class JingleTransportSocks5(JingleTransport):
if not self.connection:
return
sesn = self.connection.get_jingle_session(self.ourjid,
self.file_props.sid)
self.file_props.sid)
if sesn is None:
return
@ -294,8 +308,8 @@ class JingleTransportSocks5(JingleTransport):
content = nbxmpp.Node('content')
content.setAttr('creator', 'initiator')
c = self.get_content()
content.setAttr('name', c.name)
content_object = self.get_content()
content.setAttr('name', content_object.name)
transport = nbxmpp.Node('transport')
transport.setNamespace(nbxmpp.NS_JINGLE_BYTESTREAM)
transport.setAttr('sid', proxy['sid'])
@ -307,7 +321,7 @@ class JingleTransportSocks5(JingleTransport):
else:
for host in self.candidates:
if host['host'] == proxy['host'] and host['jid'] == proxy['jid'] \
and host['port'] == proxy['port']:
and host['port'] == proxy['port']:
cid = host['candidate_id']
break
if cid is None:
@ -345,7 +359,7 @@ class JingleTransportIBB(JingleTransport):
try:
from gi.repository import Farstream
except Exception:
except ImportError:
pass
class JingleTransportICEUDP(JingleTransport):
@ -353,11 +367,13 @@ class JingleTransportICEUDP(JingleTransport):
JingleTransport.__init__(self, TransportType.ICEUDP)
def make_candidate(self, candidate):
types = {Farstream.CandidateType.HOST: 'host',
types = {
Farstream.CandidateType.HOST: 'host',
Farstream.CandidateType.SRFLX: 'srflx',
Farstream.CandidateType.PRFLX: 'prflx',
Farstream.CandidateType.RELAY: 'relay',
Farstream.CandidateType.MULTICAST: 'multicast'}
Farstream.CandidateType.MULTICAST: 'multicast'
}
attrs = {
'component': candidate.component_id,
'foundation': '1', # hack
@ -402,23 +418,26 @@ class JingleTransportICEUDP(JingleTransport):
# jingle
proto = Farstream.NetworkProtocol.TCP
priority = int(candidate['priority'])
types = {'host': Farstream.CandidateType.HOST,
types = {
'host': Farstream.CandidateType.HOST,
'srflx': Farstream.CandidateType.SRFLX,
'prflx': Farstream.CandidateType.PRFLX,
'relay': Farstream.CandidateType.RELAY,
'multicast': Farstream.CandidateType.MULTICAST}
'multicast': Farstream.CandidateType.MULTICAST
}
if 'type' in candidate and candidate['type'] in types:
type_ = types[candidate['type']]
else:
log.warning('Unknown type %s' % candidate['type'])
log.warning('Unknown type %s', candidate['type'])
type_ = Farstream.CandidateType.HOST
username = str(transport['ufrag'])
password = str(transport['pwd'])
ttl = 0
cand = Farstream.Candidate.new_full(foundation, component_id, ip,
port, base_ip, base_port, proto, priority, type_, username,
password, ttl)
port, base_ip, base_port,
proto, priority, type_,
username, password, ttl)
candidates.append(cand)
self.remote_candidates.extend(candidates)

View File

@ -16,13 +16,15 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import os
import nbxmpp
import logging
import os
import nbxmpp
from common import gajim
log = logging.getLogger('gajim.c.jingle_xtls')
PYOPENSSL_PRESENT = False
# key-exchange id -> [callback, args], accept that session once key-exchange completes
@ -44,9 +46,7 @@ except ImportError:
log.info("PyOpenSSL not available")
if PYOPENSSL_PRESENT:
from OpenSSL import SSL
from OpenSSL.SSL import Context
from OpenSSL import crypto
from OpenSSL import SSL, crypto
TYPE_RSA = crypto.TYPE_RSA
TYPE_DSA = crypto.TYPE_DSA
@ -55,7 +55,7 @@ DH_PARAMS = 'dh_params.pem'
DEFAULT_DH_PARAMS = 'dh4096.pem'
def default_callback(connection, certificate, error_num, depth, return_code):
log.info("certificate: %s" % certificate)
log.info("certificate: %s", certificate)
return return_code
def load_cert_file(cert_path, cert_store=None):
@ -67,8 +67,8 @@ def load_cert_file(cert_path, cert_store=None):
try:
f = open(cert_path)
except IOError as e:
log.warning('Unable to open certificate file %s: %s' % (cert_path,
str(e)))
log.warning('Unable to open certificate file %s: %s', cert_path,
str(e))
return None
lines = f.readlines()
i = 0
@ -86,11 +86,11 @@ def load_cert_file(cert_path, cert_store=None):
f.close()
return x509cert
except OpenSSL.crypto.Error as exception_obj:
log.warning('Unable to load a certificate from file %s: %s' %\
(cert_path, exception_obj.args[0][0][2]))
log.warning('Unable to load a certificate from file %s: %s',
cert_path, exception_obj.args[0][0][2])
except:
log.warning('Unknown error while loading certificate from file '
'%s' % cert_path)
'%s', cert_path)
begin = -1
i += 1
f.close()
@ -107,7 +107,7 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None):
if fingerprint == 'server': # for testing purposes only
ctx.set_verify(SSL.VERIFY_NONE|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
verify_cb or default_callback)
verify_cb or default_callback)
elif fingerprint == 'client':
ctx.set_verify(SSL.VERIFY_PEER, verify_cb or default_callback)
@ -123,23 +123,23 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None):
ctx.load_tmp_dh(dh_params_name.encode('utf-8'))
except FileNotFoundError as err:
default_dh_params_name = os.path.join(gajim.DATA_DIR,
'other', DEFAULT_DH_PARAMS)
'other', DEFAULT_DH_PARAMS)
try:
with open(default_dh_params_name, "r") as default_dh_params_file:
ctx.load_tmp_dh(default_dh_params_name.encode('utf-8'))
except FileNotFoundError as err:
log.error('Unable to load default DH parameter file: %s , %s'
% (default_dh_params_name, err))
log.error('Unable to load default DH parameter file: %s, %s',
default_dh_params_name, err)
raise
if remote_jid:
store = ctx.get_cert_store()
path = os.path.join(os.path.expanduser(gajim.MY_PEER_CERTS_PATH),
remote_jid) + '.cert'
remote_jid) + '.cert'
if os.path.exists(path):
load_cert_file(path, cert_store=store)
log.debug('certificate file ' + path + ' loaded fingerprint ' + \
fingerprint)
log.debug('certificate file %s loaded fingerprint %s',
path, fingerprint)
return ctx
def read_cert(certpath):
@ -154,7 +154,7 @@ def send_cert(con, jid_from, sid):
certpath = os.path.join(gajim.MY_CERT_DIR, SELF_SIGNED_CERTIFICATE) + \
'.cert'
certificate = read_cert(certpath)
iq = nbxmpp.Iq('result', to=jid_from);
iq = nbxmpp.Iq('result', to=jid_from)
iq.setAttr('id', sid)
pubkey = iq.setTag('pubkeys')
@ -214,16 +214,16 @@ def send_cert_request(con, to_jid):
# the following code is partly due to pyopenssl examples
def createKeyPair(type, bits):
def createKeyPair(type_, bits):
"""
Create a public/private key pair.
Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA
Arguments: type_ - Key type, must be one of TYPE_RSA and TYPE_DSA
bits - Number of bits to use in the key
Returns: The public/private key pair in a PKey object
"""
pkey = crypto.PKey()
pkey.generate_key(type, bits)
pkey.generate_key(type_, bits)
return pkey
def createCertRequest(pkey, digest="sha256", **name):
@ -246,7 +246,7 @@ def createCertRequest(pkey, digest="sha256", **name):
req = crypto.X509Req()
subj = req.get_subject()
for (key,value) in name.items():
for (key, value) in name.items():
setattr(subj, key, value)
req.set_pubkey(pkey)

View File

@ -21,7 +21,6 @@
from datetime import datetime
from common import gajim
from common import pep
from common import dbus_support
if dbus_support.supported:
import dbus
@ -90,9 +89,11 @@ class LocationListener:
def shut_down(self):
pass
def _on_geoclue_address_changed(self, timestamp=None, address={},
def _on_geoclue_address_changed(self, timestamp=None, address=None,
accuracy=None):
# update data with info we just received
if address is None:
address = {}
for field in ['country', 'countrycode', 'locality', 'postalcode',
'region', 'street']:
self._data[field] = address.get(field, None)
@ -103,8 +104,10 @@ class LocationListener:
self._data['accuracy'] = accuracy[1]
self._send_location()
def _on_geoclue_position_changed(self, fields=[], timestamp=None, lat=None,
def _on_geoclue_position_changed(self, fields=None, timestamp=None, lat=None,
lon=None, alt=None, accuracy=None):
if fields is None:
fields = []
# update data with info we just received
_dict = {'lat': lat, 'lon': lon, 'alt': alt}
for field in _dict:

View File

@ -36,6 +36,7 @@ import json
from gzip import GzipFile
from io import BytesIO
from gi.repository import GLib
from enum import IntEnum
from common import exceptions
from common import gajim
@ -50,59 +51,50 @@ CACHE_DB_PATH = gajim.gajimpaths['CACHE_DB']
import logging
log = logging.getLogger('gajim.c.logger')
class Constants:
def __init__(self):
(
self.JID_NORMAL_TYPE,
self.JID_ROOM_TYPE
) = range(2)
class JIDConstant(IntEnum):
NORMAL_TYPE = 0
ROOM_TYPE = 1
(
self.KIND_STATUS,
self.KIND_GCSTATUS,
self.KIND_GC_MSG,
self.KIND_SINGLE_MSG_RECV,
self.KIND_CHAT_MSG_RECV,
self.KIND_SINGLE_MSG_SENT,
self.KIND_CHAT_MSG_SENT,
self.KIND_ERROR
) = range(8)
class KindConstant(IntEnum):
STATUS = 0
GCSTATUS = 1
GC_MSG = 2
SINGLE_MSG_RECV = 3
CHAT_MSG_RECV = 4
SINGLE_MSG_SENT = 5
CHAT_MSG_SENT = 6
ERROR = 7
(
self.SHOW_ONLINE,
self.SHOW_CHAT,
self.SHOW_AWAY,
self.SHOW_XA,
self.SHOW_DND,
self.SHOW_OFFLINE
) = range(6)
class ShowConstant(IntEnum):
ONLINE = 0
CHAT = 1
AWAY = 2
XA = 3
DND = 4
OFFLINE = 5
(
self.TYPE_AIM,
self.TYPE_GG,
self.TYPE_HTTP_WS,
self.TYPE_ICQ,
self.TYPE_MSN,
self.TYPE_QQ,
self.TYPE_SMS,
self.TYPE_SMTP,
self.TYPE_TLEN,
self.TYPE_YAHOO,
self.TYPE_NEWMAIL,
self.TYPE_RSS,
self.TYPE_WEATHER,
self.TYPE_MRIM,
self.TYPE_NO_TRANSPORT,
) = range(15)
class TypeConstant(IntEnum):
AIM = 0
GG = 1
HTTP_WS = 2
ICQ = 3
MSN = 4
QQ = 5
SMS = 6
SMTP = 7
TLEN = 8
YAHOO = 9
NEWMAIL = 10
RSS = 11
WEATHER = 12
MRIM = 13
NO_TRANSPORT = 14
(
self.SUBSCRIPTION_NONE,
self.SUBSCRIPTION_TO,
self.SUBSCRIPTION_FROM,
self.SUBSCRIPTION_BOTH,
) = range(4)
constants = Constants()
class SubscriptionConstant(IntEnum):
NONE = 0
TO = 1
FROM = 2
BOTH = 3
class Logger:
def __init__(self):
@ -231,7 +223,7 @@ class Logger:
if row is None:
return None
else:
if row[0] == constants.JID_ROOM_TYPE:
if row[0] == JIDConstant.ROOM_TYPE:
return True
return False
@ -255,9 +247,9 @@ class Logger:
return row[0]
# oh! a new jid :), we add it now
if typestr == 'ROOM':
typ = constants.JID_ROOM_TYPE
typ = JIDConstant.ROOM_TYPE
else:
typ = constants.JID_NORMAL_TYPE
typ = JIDConstant.NORMAL_TYPE
try:
self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid,
typ))
@ -277,34 +269,34 @@ class Logger:
Convert from string style to constant ints for db
"""
if kind == 'status':
kind_col = constants.KIND_STATUS
kind_col = KindConstant.STATUS
elif kind == 'gcstatus':
kind_col = constants.KIND_GCSTATUS
kind_col = KindConstant.GCSTATUS
elif kind == 'gc_msg':
kind_col = constants.KIND_GC_MSG
kind_col = KindConstant.GC_MSG
elif kind == 'single_msg_recv':
kind_col = constants.KIND_SINGLE_MSG_RECV
kind_col = KindConstant.SINGLE_MSG_RECV
elif kind == 'single_msg_sent':
kind_col = constants.KIND_SINGLE_MSG_SENT
kind_col = KindConstant.SINGLE_MSG_SENT
elif kind == 'chat_msg_recv':
kind_col = constants.KIND_CHAT_MSG_RECV
kind_col = KindConstant.CHAT_MSG_RECV
elif kind == 'chat_msg_sent':
kind_col = constants.KIND_CHAT_MSG_SENT
kind_col = KindConstant.CHAT_MSG_SENT
elif kind == 'error':
kind_col = constants.KIND_ERROR
kind_col = KindConstant.ERROR
if show == 'online':
show_col = constants.SHOW_ONLINE
show_col = ShowConstant.ONLINE
elif show == 'chat':
show_col = constants.SHOW_CHAT
show_col = ShowConstant.CHAT
elif show == 'away':
show_col = constants.SHOW_AWAY
show_col = ShowConstant.AWAY
elif show == 'xa':
show_col = constants.SHOW_XA
show_col = ShowConstant.XA
elif show == 'dnd':
show_col = constants.SHOW_DND
show_col = ShowConstant.DND
elif show == 'offline':
show_col = constants.SHOW_OFFLINE
show_col = ShowConstant.OFFLINE
elif show is None:
show_col = None
else: # invisible in GC when someone goes invisible
@ -318,70 +310,70 @@ class Logger:
Convert from string style to constant ints for db
"""
if type_ == 'aim':
return constants.TYPE_AIM
return TypeConstant.AIM
if type_ == 'gadu-gadu':
return constants.TYPE_GG
return TypeConstant.GG
if type_ == 'http-ws':
return constants.TYPE_HTTP_WS
return TypeConstant.HTTP_WS
if type_ == 'icq':
return constants.TYPE_ICQ
return TypeConstant.ICQ
if type_ == 'msn':
return constants.TYPE_MSN
return TypeConstant.MSN
if type_ == 'qq':
return constants.TYPE_QQ
return TypeConstant.QQ
if type_ == 'sms':
return constants.TYPE_SMS
return TypeConstant.SMS
if type_ == 'smtp':
return constants.TYPE_SMTP
return TypeConstant.SMTP
if type_ in ('tlen', 'x-tlen'):
return constants.TYPE_TLEN
return TypeConstant.TLEN
if type_ == 'yahoo':
return constants.TYPE_YAHOO
return TypeConstant.YAHOO
if type_ == 'newmail':
return constants.TYPE_NEWMAIL
return TypeConstant.NEWMAIL
if type_ == 'rss':
return constants.TYPE_RSS
return TypeConstant.RSS
if type_ == 'weather':
return constants.TYPE_WEATHER
return TypeConstant.WEATHER
if type_ == 'mrim':
return constants.TYPE_MRIM
return TypeConstant.MRIM
if type_ == 'jabber':
return constants.TYPE_NO_TRANSPORT
return TypeConstant.NO_TRANSPORT
return None
def convert_api_values_to_human_transport_type(self, type_id):
"""
Convert from constant ints for db to string style
"""
if type_id == constants.TYPE_AIM:
if type_id == TypeConstant.AIM:
return 'aim'
if type_id == constants.TYPE_GG:
if type_id == TypeConstant.GG:
return 'gadu-gadu'
if type_id == constants.TYPE_HTTP_WS:
if type_id == TypeConstant.HTTP_WS:
return 'http-ws'
if type_id == constants.TYPE_ICQ:
if type_id == TypeConstant.ICQ:
return 'icq'
if type_id == constants.TYPE_MSN:
if type_id == TypeConstant.MSN:
return 'msn'
if type_id == constants.TYPE_QQ:
if type_id == TypeConstant.QQ:
return 'qq'
if type_id == constants.TYPE_SMS:
if type_id == TypeConstant.SMS:
return 'sms'
if type_id == constants.TYPE_SMTP:
if type_id == TypeConstant.SMTP:
return 'smtp'
if type_id == constants.TYPE_TLEN:
if type_id == TypeConstant.TLEN:
return 'tlen'
if type_id == constants.TYPE_YAHOO:
if type_id == TypeConstant.YAHOO:
return 'yahoo'
if type_id == constants.TYPE_NEWMAIL:
if type_id == TypeConstant.NEWMAIL:
return 'newmail'
if type_id == constants.TYPE_RSS:
if type_id == TypeConstant.RSS:
return 'rss'
if type_id == constants.TYPE_WEATHER:
if type_id == TypeConstant.WEATHER:
return 'weather'
if type_id == constants.TYPE_MRIM:
if type_id == TypeConstant.MRIM:
return 'mrim'
if type_id == constants.TYPE_NO_TRANSPORT:
if type_id == TypeConstant.NO_TRANSPORT:
return 'jabber'
def convert_human_subscription_values_to_db_api_values(self, sub):
@ -389,25 +381,25 @@ class Logger:
Convert from string style to constant ints for db
"""
if sub == 'none':
return constants.SUBSCRIPTION_NONE
return SubscriptionConstant.NONE
if sub == 'to':
return constants.SUBSCRIPTION_TO
return SubscriptionConstant.TO
if sub == 'from':
return constants.SUBSCRIPTION_FROM
return SubscriptionConstant.FROM
if sub == 'both':
return constants.SUBSCRIPTION_BOTH
return SubscriptionConstant.BOTH
def convert_db_api_values_to_human_subscription_values(self, sub):
"""
Convert from constant ints for db to string style
"""
if sub == constants.SUBSCRIPTION_NONE:
if sub == SubscriptionConstant.NONE:
return 'none'
if sub == constants.SUBSCRIPTION_TO:
if sub == SubscriptionConstant.TO:
return 'to'
if sub == constants.SUBSCRIPTION_FROM:
if sub == SubscriptionConstant.FROM:
return 'from'
if sub == constants.SUBSCRIPTION_BOTH:
if sub == SubscriptionConstant.BOTH:
return 'both'
def commit_to_db(self, values, write_unread=False):
@ -498,7 +490,7 @@ class Logger:
all_messages.append(results[0])
return all_messages
def write(self, kind, jid, message=None, show=None, tim=None, subject=None, additional_data={}):
def write(self, kind, jid, message=None, show=None, tim=None, subject=None, additional_data=None):
"""
Write a row (status, gcstatus, message etc) to logs database
@ -512,6 +504,8 @@ class Logger:
ROOM_JID/nick if pm-related.
"""
if additional_data is None:
additional_data = {}
if self.jids_already_in == []: # only happens if we just created the db
self.open_db()
@ -539,12 +533,12 @@ class Logger:
except exceptions.PysqliteOperationalError as e:
raise exceptions.PysqliteOperationalError(str(e))
if show is None: # show is None (xmpp), but we say that 'online'
show_col = constants.SHOW_ONLINE
show_col = ShowConstant.ONLINE
elif kind == 'gcstatus':
# status in ROOM (for pm status see status)
if show is None: # show is None (xmpp), but we say that 'online'
show_col = constants.SHOW_ONLINE
show_col = ShowConstant.ONLINE
jid, nick = jid.split('/', 1)
try:
# re-get jid_id for the new jid
@ -608,9 +602,9 @@ class Logger:
SELECT time, kind, message, subject, additional_data FROM logs
WHERE (%s) AND kind IN (%d, %d, %d, %d, %d) AND time > %d
ORDER BY time DESC LIMIT %d OFFSET %d
''' % (where_sql, constants.KIND_SINGLE_MSG_RECV,
constants.KIND_CHAT_MSG_RECV, constants.KIND_SINGLE_MSG_SENT,
constants.KIND_CHAT_MSG_SENT, constants.KIND_ERROR, timed_out,
''' % (where_sql, KindConstant.SINGLE_MSG_RECV,
KindConstant.CHAT_MSG_RECV, KindConstant.SINGLE_MSG_SENT,
KindConstant.CHAT_MSG_SENT, KindConstant.ERROR, timed_out,
restore_how_many_rows, pending_how_many), jid_tuple)
results = self.cur.fetchall()
@ -736,7 +730,7 @@ class Logger:
AND kind NOT IN (%d, %d)
ORDER BY time
''' % (where_sql, start_of_month, last_second_of_month,
constants.KIND_STATUS, constants.KIND_GCSTATUS), jid_tuple)
KindConstant.STATUS, KindConstant.GCSTATUS), jid_tuple)
result = self.cur.fetchall()
# convert timestamps to day of month
@ -765,7 +759,7 @@ class Logger:
SELECT MAX(time) FROM logs
WHERE (%s)
AND kind NOT IN (%d, %d)
''' % (where_sql, constants.KIND_STATUS, constants.KIND_GCSTATUS),
''' % (where_sql, KindConstant.STATUS, KindConstant.GCSTATUS),
jid_tuple)
results = self.cur.fetchone()
@ -1109,7 +1103,9 @@ class Logger:
(account_jid_id,))
self._timeout_commit()
def save_if_not_exists(self, with_, direction, tim, msg='', nick=None, additional_data={}):
def save_if_not_exists(self, with_, direction, tim, msg='', nick=None, additional_data=None):
if additional_data is None:
additional_data = {}
if tim:
time_col = float(tim)
else:

View File

@ -241,7 +241,8 @@ class OptionsParser:
caps_cache.capscache.initialize_from_db()
def assert_unread_msgs_table_exists(self):
@staticmethod
def assert_unread_msgs_table_exists():
"""
Create table unread_messages if there is no such table
"""
@ -265,7 +266,12 @@ class OptionsParser:
pass
con.close()
def update_ft_proxies(self, to_remove=[], to_add=[]):
@staticmethod
def update_ft_proxies(to_remove=None, to_add=None):
if to_remove is None:
to_remove = []
if to_add is None:
to_add = []
for account in gajim.config.get_per('accounts'):
proxies_str = gajim.config.get_per('accounts', account,
'file_transfer_proxies')

View File

@ -216,13 +216,10 @@ LOCATION_DATA = {
'uri': _('uri')}
from gi.repository import GLib
from gi.repository import Gtk
from gi.repository import GdkPixbuf
import logging
log = logging.getLogger('gajim.c.pep')
from common import helpers
import nbxmpp
from common import gajim

View File

@ -36,7 +36,6 @@ import time
import nbxmpp
from common import gajim
from common import helpers
from common import dataforms
from common import ged
from common import jingle_xtls
from common.file_props import FilesProp
@ -436,8 +435,8 @@ class ConnectionSocks5Bytestream(ConnectionBytestream):
# it's an IPv6
return True
ip_s = ip.split('.')
ip_l = long(ip_s[0])<<24 | long(ip_s[1])<<16 | long(ip_s[2])<<8 | \
long(ip_s[3])
ip_l = int(ip_s[0])<<24 | int(ip_s[1])<<16 | int(ip_s[2])<<8 | \
int(ip_s[3])
# 10/8
if ip_l & (255<<24) == 10<<24:
return True

View File

@ -27,7 +27,6 @@ log = logging.getLogger('gajim.c.p.caps')
from common import gajim
from common import ged
from common import helpers
from common.connection_handlers_events import CapsPresenceReceivedEvent, \
CapsDiscoReceivedEvent, CapsReceivedEvent

View File

@ -62,7 +62,9 @@ else:
should return the validated text, or raise ValueError
"""
def uri_reference_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
options=None, content=None):
if options is None:
options = {}
try:
valid_text = validator(text)
except ValueError as e:
@ -102,9 +104,10 @@ else:
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=None,
config_section='general'):
if settings_overrides is None:
settings_overrides = {'report_level': 5, 'halt_level': 5}
self.pub = Publisher(reader=None, parser=None, writer=None,
settings=None,
source_class=io.StringInput,

View File

@ -485,7 +485,7 @@ class Socks5(object):
for ai in self.ais:
try:
self._sock = socket.socket(*ai[:3])
if not self.fingerprint is None:
if self.fingerprint is not None:
if self.file_props.type_ == 's':
remote_jid = gajim.get_jid_without_resource(
self.file_props.receiver)
@ -825,7 +825,7 @@ class Socks5(object):
auth_mechanisms = []
try:
num_auth = struct.unpack('!xB', buff[:2])[0]
for i in list(range(num_auth)):
for i in range(num_auth):
mechanism, = struct.unpack('!B', buff[1 + i])
auth_mechanisms.append(mechanism)
except Exception:

View File

@ -553,7 +553,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
n, e = (crypto.decode_mpi(base64.b64decode(
keyvalue.getTagData(x))) for x in ('Modulus', 'Exponent'))
eir_pubkey = RSA.construct((n, long(e)))
eir_pubkey = RSA.construct((n, int(e)))
pubkey_o = nbxmpp.c14n.c14n(keyvalue, self._is_buggy_gajim())
else:

View File

@ -22,7 +22,6 @@ import nbxmpp
from nbxmpp.idlequeue import IdleObject
from nbxmpp import dispatcher_nb, simplexml
from nbxmpp.plugin import *
from nbxmpp.simplexml import ustr
from nbxmpp.transports_nb import DATA_RECEIVED, DATA_SENT, DATA_ERROR
from common.zeroconf import zeroconf

View File

@ -23,13 +23,9 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import time
import nbxmpp
from common import helpers
from common import gajim
from common.zeroconf import zeroconf
from common.commands import ConnectionCommands
from common.protocol.bytestream import ConnectionSocks5BytestreamZeroconf
from common.connection_handlers_events import ZeroconfMessageReceivedEvent
@ -51,7 +47,6 @@ except Exception:
HAS_IDLE = False
from common import connection_handlers
from session import ChatControlSession
class ConnectionVcard(connection_handlers.ConnectionVcard):
def add_sha(self, p, send_caps = True):

View File

@ -50,8 +50,6 @@ from common.zeroconf import zeroconf
from common.zeroconf.connection_handlers_zeroconf import *
from common.connection_handlers_events import *
import locale
class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
def __init__(self, name):
ConnectionHandlersZeroconf.__init__(self)

View File

@ -19,6 +19,7 @@
from common.zeroconf import zeroconf
from common.zeroconf.zeroconf import Constant, ConstantRI
class Roster:
def __init__(self, zeroconf):
@ -29,7 +30,7 @@ class Roster:
def update_roster(self):
for val in self.zeroconf.contacts.values():
self.setItem(val[zeroconf.C_NAME])
self.setItem(val[Constant.NAME])
def getRoster(self):
if self._data is None:
@ -58,13 +59,13 @@ class Roster:
addresses = []
i = 0
for ri in contact[zeroconf.C_RESOLVED_INFO]:
for ri in contact[Constant.RESOLVED_INFO]:
addresses += [{}]
addresses[i]['host'] = ri[zeroconf.C_RI_HOST]
addresses[i]['address'] = ri[zeroconf.C_RI_ADDRESS]
addresses[i]['port'] = ri[zeroconf.C_RI_PORT]
addresses[i]['host'] = ri[ConstantRI.HOST]
addresses[i]['address'] = ri[ConstantRI.ADDRESS]
addresses[i]['port'] = ri[ConstantRI.PORT]
i += 1
txt = contact[zeroconf.C_TXT]
txt = contact[Constant.TXT]
self._data[jid]={}
self._data[jid]['ask'] = 'none'

View File

@ -17,8 +17,22 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
C_NAME, C_DOMAIN, C_RESOLVED_INFO, C_BARE_NAME, C_TXT = range(5)
C_RI_INTERFACE, C_RI_PROTOCOL, C_RI_HOST, C_RI_APROTOCOL, C_RI_ADDRESS, C_RI_PORT = range(6)
from enum import IntEnum
class Constant(IntEnum):
NAME = 0
DOMAIN = 1
RESOLVED_INFO = 2
BARE_NAME = 3
TXT = 4
class ConstantRI(IntEnum):
INTERFACE = 0
PROTOCOL = 1
HOST = 2
APROTOCOL = 3
ADDRESS = 4
PORT = 5
def test_avahi():
try:

View File

@ -25,8 +25,7 @@ try:
except ImportError:
pass
from common.zeroconf.zeroconf import C_BARE_NAME, C_RESOLVED_INFO, \
C_RI_INTERFACE, C_RI_PROTOCOL, C_RI_APROTOCOL, C_DOMAIN, C_TXT
from common.zeroconf.zeroconf import Constant, ConstantRI
class Zeroconf:
def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB,
@ -95,15 +94,15 @@ class Zeroconf:
if name != self.name:
for key in self.contacts.keys():
val = self.contacts[key]
if val[C_BARE_NAME] == name:
if val[Constant.BARE_NAME] == name:
# try to reduce instead of delete first
resolved_info = val[C_RESOLVED_INFO]
resolved_info = val[Constant.RESOLVED_INFO]
if len(resolved_info) > 1:
for i in range(len(resolved_info)):
if resolved_info[i][C_RI_INTERFACE] == interface and resolved_info[i][C_RI_PROTOCOL] == protocol:
del self.contacts[key][C_RESOLVED_INFO][i]
if resolved_info[i][ConstantRI.INTERFACE] == interface and resolved_info[i][ConstantRI.PROTOCOL] == protocol:
del self.contacts[key][Constant.RESOLVED_INFO][i]
# if still something left, don't remove
if len(self.contacts[key][C_RESOLVED_INFO]) > 1: return
if len(self.contacts[key][Constant.RESOLVED_INFO]) > 1: return
del self.contacts[key]
self.remove_serviceCB(key)
return
@ -201,7 +200,7 @@ class Zeroconf:
name = name + '@' + name
# update TXT data only, as intended according to resolve_all comment
old_contact = self.contacts[name]
self.contacts[name] = old_contact[0:C_TXT] + (txt,) + old_contact[C_TXT+1:]
self.contacts[name] = old_contact[0:Constant.TXT] + (txt,) + old_contact[Constant.TXT+1:]
def service_added_callback(self):
log.debug('Service successfully added')
@ -450,9 +449,9 @@ class Zeroconf:
for val in self.contacts.values():
# get txt data from last recorded resolved info
# TODO: Better try to get it from last IPv6 mDNS, then last IPv4?
ri = val[C_RESOLVED_INFO][0]
self.server.ResolveService(int(ri[C_RI_INTERFACE]), int(ri[C_RI_PROTOCOL]),
val[C_BARE_NAME], self.stype, val[C_DOMAIN],
ri = val[Constant.RESOLVED_INFO][0]
self.server.ResolveService(int(ri[ConstantRI.INTERFACE]), int(ri[ConstantRI.PROTOCOL]),
val[Constant.BARE_NAME], self.stype, val[Constant.DOMAIN],
self.avahi.PROTO_UNSPEC, dbus.UInt32(0),
reply_handler=self.service_resolved_all_callback,
error_handler=self.error_callback)

View File

@ -20,7 +20,7 @@
from common import gajim
import select
import re
from common.zeroconf.zeroconf import C_BARE_NAME, C_DOMAIN, C_TXT
from common.zeroconf.zeroconf import Constant
try:
import pybonjour
@ -86,7 +86,7 @@ class Zeroconf:
return
if name != self.name:
for key in self.contacts.keys():
if self.contacts[key][C_BARE_NAME] == name:
if self.contacts[key][Constant.BARE_NAME] == name:
del self.contacts[key]
self.remove_serviceCB(key)
return
@ -171,7 +171,7 @@ class Zeroconf:
if name != self.name:
# update TXT data only, as intended according to resolve_all comment
old_contact = self.contacts[name]
self.contacts[name] = old_contact[0:C_TXT] + (self.txt,) + old_contact[C_TXT+1:]
self.contacts[name] = old_contact[0:Constant.TXT] + (self.txt,) + old_contact[Constant.TXT+1:]
def service_added_callback(self, sdRef, flags, errorCode, name, regtype, domain):
@ -303,8 +303,8 @@ class Zeroconf:
for val in self.contacts.values():
resolve_sdRef = pybonjour.DNSServiceResolve(0,
pybonjour.kDNSServiceInterfaceIndexAny, val[C_BARE_NAME],
self.stype + '.', val[C_DOMAIN] + '.',
pybonjour.kDNSServiceInterfaceIndexAny, val[Constant.BARE_NAME],
self.stype + '.', val[Constant.DOMAIN] + '.',
self.service_resolved_all_callback)
try:

View File

@ -51,7 +51,6 @@ from common.fuzzyclock import FuzzyClock
from htmltextview import HtmlTextView
from common.exceptions import GajimGeneralException
from encodings.punycode import punycode_encode as puny_encode
NOT_SHOWN = 0
ALREADY_RECEIVED = 1
@ -449,9 +448,10 @@ class ConversationTextview(GObject.GObject):
self.auto_scrolling = False
return False # when called in an idle_add, just do it once
def bring_scroll_to_end(self, diff_y = 0,
use_smooth=gajim.config.get('use_smooth_scrolling')):
def bring_scroll_to_end(self, diff_y=0, use_smooth=None):
''' scrolls to the end of textview if end is not visible '''
if use_smooth is None:
use_smooth = gajim.config.get('use_smooth_scrolling')
buffer_ = self.tv.get_buffer()
end_iter = buffer_.get_end_iter()
end_rect = self.tv.get_iter_location(end_iter)
@ -1015,7 +1015,7 @@ class ConversationTextview(GObject.GObject):
helpers.launch_browser_mailer(kind, word)
def detect_and_print_special_text(self, otext, other_tags, graphics=True,
iter_=None, additional_data={}):
iter_=None, additional_data=None):
"""
Detect special text (emots & links & formatting), print normal text
before any special text it founds, then print special text (that happens
@ -1025,6 +1025,8 @@ class ConversationTextview(GObject.GObject):
"""
if not otext:
return
if additional_data is None:
additional_data = {}
buffer_ = self.tv.get_buffer()
if other_tags:
insert_tags_func = buffer_.insert_with_tags_by_name
@ -1080,11 +1082,13 @@ class ConversationTextview(GObject.GObject):
return end_iter
def print_special_text(self, special_text, other_tags, graphics=True,
iter_=None, additional_data={}):
iter_=None, additional_data=None):
"""
Is called by detect_and_print_special_text and prints special text
(emots, links, formatting)
"""
if additional_data is None:
additional_data = {}
# PluginSystem: adding GUI extension point for ConversationTextview
self.plugin_modified = False
@ -1539,10 +1543,12 @@ class ConversationTextview(GObject.GObject):
self.print_empty_line(end_iter)
def print_real_text(self, text, text_tags=[], name=None, xhtml=None,
graphics=True, mark=None, additional_data={}):
graphics=True, mark=None, additional_data=None):
"""
Add normal and special text. call this to add text
"""
if additional_data is None:
additional_data = {}
buffer_ = self.tv.get_buffer()
if not mark:
iter_ = buffer_.get_end_iter()

View File

@ -279,7 +279,7 @@ class DataFormWidget(Gtk.Alignment, object):
selection = self.records_treeview.get_selection()
model, rowrefs = selection.get_selected_rows()
# rowref is a list of paths
for i in list(range(len(rowrefs))):
for i in range(len(rowrefs)):
rowrefs[i] = Gtk.TreeRowReference.new(model, rowrefs[i])
# rowref is a list of row references; need to convert because we will
# modify the model, paths would change

View File

@ -299,10 +299,12 @@ class ServicesCache:
if not self._cbs[cbkey]:
del self._cbs[cbkey]
def get_icon(self, identities = [], addr=''):
def get_icon(self, identities=None, addr=''):
"""
Return the icon for an agent
"""
if identities is None:
identities = []
# Grab the first identity with an icon
quiet = False
for identity in identities:
@ -334,10 +336,14 @@ class ServicesCache:
_icon_cache['jabber'] = pix
return pix
def get_browser(self, identities=[], features=[]):
def get_browser(self, identities=None, features=None):
"""
Return the browser class for an agent
"""
if identities is None:
identities = []
if features is None:
features = []
# First pass, we try to find a ToplevelAgentBrowser
for identity in identities:
try:

View File

@ -29,7 +29,6 @@ from gi.repository import Gtk, Gdk
import gtkgui_helpers
from common import gajim
from common import helpers
from common.i18n import Q_
class FeaturesWindow:

View File

@ -29,6 +29,8 @@ from gi.repository import Pango
import os
import time
from enum import IntEnum
import gtkgui_helpers
import tooltips
import dialogs
@ -42,14 +44,15 @@ from nbxmpp.protocol import NS_JINGLE_FILE_TRANSFER
import logging
log = logging.getLogger('gajim.filetransfer_window')
C_IMAGE = 0
C_LABELS = 1
C_FILE = 2
C_TIME = 3
C_PROGRESS = 4
C_PERCENT = 5
C_PULSE = 6
C_SID = 7
class Column(IntEnum):
IMAGE = 0
LABELS = 1
FILE = 2
TIME = 3
PROGRESS = 4
PERCENT = 5
PULSE = 6
SID = 7
class FileTransfersWindow:
@ -85,11 +88,11 @@ class FileTransfersWindow:
col = Gtk.TreeViewColumn(_('File'))
renderer = Gtk.CellRendererText()
col.pack_start(renderer, False)
col.add_attribute(renderer, 'markup', C_LABELS)
col.add_attribute(renderer, 'markup', Column.LABELS)
renderer.set_property('yalign', 0.)
renderer = Gtk.CellRendererText()
col.pack_start(renderer, True)
col.add_attribute(renderer, 'markup', C_FILE)
col.add_attribute(renderer, 'markup', Column.FILE)
renderer.set_property('xalign', 0.)
renderer.set_property('yalign', 0.)
renderer.set_property('ellipsize', Pango.EllipsizeMode.END)
@ -100,7 +103,7 @@ class FileTransfersWindow:
col = Gtk.TreeViewColumn(_('Time'))
renderer = Gtk.CellRendererText()
col.pack_start(renderer, False)
col.add_attribute(renderer, 'markup', C_TIME)
col.add_attribute(renderer, 'markup', Column.TIME)
renderer.set_property('yalign', 0.5)
renderer.set_property('xalign', 0.5)
renderer = Gtk.CellRendererText()
@ -114,9 +117,9 @@ class FileTransfersWindow:
renderer.set_property('yalign', 0.5)
renderer.set_property('xalign', 0.5)
col.pack_start(renderer, False)
col.add_attribute(renderer, 'text', C_PROGRESS)
col.add_attribute(renderer, 'value', C_PERCENT)
col.add_attribute(renderer, 'pulse', C_PULSE)
col.add_attribute(renderer, 'text', Column.PROGRESS)
col.add_attribute(renderer, 'value', Column.PERCENT)
col.add_attribute(renderer, 'pulse', Column.PULSE)
col.set_resizable(True)
col.set_expand(False)
self.tree.append_column(col)
@ -480,7 +483,7 @@ class FileTransfersWindow:
iter_ = self.get_iter_by_sid(file_props.type_, file_props.sid)
if iter_ is None:
return
self.model[iter_][C_SID]
self.model[iter_][Column.SID]
if status == 'stop':
file_props.stopped = True
elif status == 'ok':
@ -490,21 +493,21 @@ class FileTransfersWindow:
full_size = file_props.size
text += helpers.convert_bytes(received_size) + '/' + \
helpers.convert_bytes(full_size)
self.model.set(iter_, C_PROGRESS, text)
self.model.set(iter_, C_PULSE, GLib.MAXINT32)
self.model.set(iter_, Column.PROGRESS, text)
self.model.set(iter_, Column.PULSE, GLib.MAXINT32)
elif status == 'computing':
self.model.set(iter_, C_PULSE, 1)
self.model.set(iter_, Column.PULSE, 1)
text = _('Checking file…') + '\n'
received_size = int(file_props.received_len)
full_size = file_props.size
text += helpers.convert_bytes(received_size) + '/' + \
helpers.convert_bytes(full_size)
self.model.set(iter_, C_PROGRESS, text)
self.model.set(iter_, Column.PROGRESS, text)
def pulse():
p = self.model.get(iter_, C_PULSE)[0]
p = self.model.get(iter_, Column.PULSE)[0]
if p == GLib.MAXINT32:
return False
self.model.set(iter_, C_PULSE, p + 1)
self.model.set(iter_, Column.PULSE, p + 1)
return True
GLib.timeout_add(100, pulse)
elif status == 'hash_error':
@ -513,9 +516,9 @@ class FileTransfersWindow:
full_size = file_props.size
text += helpers.convert_bytes(received_size) + '/' + \
helpers.convert_bytes(full_size)
self.model.set(iter_, C_PROGRESS, text)
self.model.set(iter_, C_PULSE, GLib.MAXINT32)
self.model.set(iter_, C_IMAGE, self.get_icon(status))
self.model.set(iter_, Column.PROGRESS, text)
self.model.set(iter_, Column.PULSE, GLib.MAXINT32)
self.model.set(iter_, Column.IMAGE, self.get_icon(status))
path = self.model.get_path(iter_)
self.select_func(path)
@ -609,7 +612,7 @@ class FileTransfersWindow:
iter_ = self.get_iter_by_sid(typ, sid)
if iter_ is not None:
just_began = False
if self.model[iter_][C_PERCENT] == 0 and int(percent > 0):
if self.model[iter_][Column.PERCENT] == 0 and int(percent > 0):
just_began = True
text = self._format_percent(percent)
if transfered_size == 0:
@ -631,8 +634,8 @@ class FileTransfersWindow:
eta, speed = self._get_eta_and_speed(full_size, transfered_size,
file_props)
self.model.set(iter_, C_PROGRESS, text)
self.model.set(iter_, C_PERCENT, int(percent))
self.model.set(iter_, Column.PROGRESS, text)
self.model.set(iter_, Column.PERCENT, int(percent))
text = self._format_time(eta)
text += '\n'
#This should make the string Kb/s,
@ -640,7 +643,7 @@ class FileTransfersWindow:
#Only the 's' after / (which means second) should be translated.
text += _('(%(filesize_unit)s/s)') % {'filesize_unit':
helpers.convert_bytes(speed)}
self.model.set(iter_, C_TIME, text)
self.model.set(iter_, Column.TIME, text)
# try to guess what should be the status image
if file_props.type_ == 'r':
@ -673,7 +676,7 @@ class FileTransfersWindow:
"""
iter_ = self.model.get_iter_first()
while iter_:
if typ + sid == self.model[iter_][C_SID]:
if typ + sid == self.model[iter_][Column.SID]:
return iter_
iter_ = self.model.iter_next(iter_)
@ -736,7 +739,7 @@ class FileTransfersWindow:
file_name = file_props.name
text_props = GLib.markup_escape_text(file_name) + '\n'
text_props += contact.get_shown_name()
self.model.set(iter_, 1, text_labels, 2, text_props, C_PULSE, -1, C_SID,
self.model.set(iter_, 1, text_labels, 2, text_props, Column.PULSE, -1, Column.SID,
file_props.type_ + file_props.sid)
self.set_progress(file_props.type_, file_props.sid, 0, iter_)
if file_props.started is False:
@ -767,7 +770,7 @@ class FileTransfersWindow:
except Exception:
self.tooltip.hide_tooltip()
return
sid = self.model[iter_][C_SID]
sid = self.model[iter_][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
if file_props is not None:
if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
@ -825,7 +828,7 @@ class FileTransfersWindow:
self.set_all_insensitive()
return
current_iter = self.model.get_iter(path)
sid = self.model[current_iter][C_SID]
sid = self.model[current_iter][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
self.remove_menuitem.set_sensitive(is_row_selected)
self.open_folder_menuitem.set_sensitive(is_row_selected)
@ -883,7 +886,7 @@ class FileTransfersWindow:
i = len(self.model) - 1
while i >= 0:
iter_ = self.model.get_iter((i))
sid = self.model[iter_][C_SID]
sid = self.model[iter_][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
if is_transfer_stopped(file_props):
self._remove_transfer(iter_, sid, file_props)
@ -918,7 +921,7 @@ class FileTransfersWindow:
if selected is None or selected[1] is None:
return
s_iter = selected[1]
sid = self.model[s_iter][C_SID]
sid = self.model[s_iter][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
if is_transfer_paused(file_props):
file_props.last_time = time.time()
@ -940,7 +943,7 @@ class FileTransfersWindow:
if selected is None or selected[1] is None:
return
s_iter = selected[1]
sid = self.model[s_iter][C_SID]
sid = self.model[s_iter][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
account = file_props.tt_account
if account not in gajim.connections:
@ -966,7 +969,7 @@ class FileTransfersWindow:
# as it was before setting the timeout
if props and self.tooltip.id == props[0]:
iter_ = self.model.get_iter(props[0])
sid = self.model[iter_][C_SID]
sid = self.model[iter_][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
# bounding rectangle of coordinates for the cell within the treeview
rect = self.tree.get_cell_area(props[0], props[1])
@ -1054,7 +1057,7 @@ class FileTransfersWindow:
if not selected or not selected[1]:
return
s_iter = selected[1]
sid = self.model[s_iter][C_SID]
sid = self.model[s_iter][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
if not file_props.file_name:
return
@ -1076,7 +1079,7 @@ class FileTransfersWindow:
if not selected or not selected[1]:
return
s_iter = selected[1]
sid = self.model[s_iter][C_SID]
sid = self.model[s_iter][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
self._remove_transfer(s_iter, sid, file_props)
self.set_all_insensitive()

View File

@ -46,6 +46,8 @@ import cell_renderer_image
import dataforms_widget
import nbxmpp
from enum import IntEnum
from common import events
from common import gajim
from common import helpers
@ -55,7 +57,6 @@ from common import i18n
from chat_control import ChatControl
from chat_control_base import ChatControlBase
from common.exceptions import GajimGeneralException
from command_system.implementation.hosts import PrivateChatCommands
from command_system.implementation.hosts import GroupChatCommands
@ -64,14 +65,12 @@ from common.connection_handlers_events import GcMessageOutgoingEvent
import logging
log = logging.getLogger('gajim.groupchat_control')
#(status_image, type, nick, shown_nick)
(
C_IMG, # image to show state (online, new message etc)
C_NICK, # contact nickame or ROLE name
C_TYPE, # type of the row ('contact' or 'role')
C_TEXT, # text shown in the cellrenderer
C_AVATAR, # avatar of the contact
) = range(5)
class Column(IntEnum):
IMG = 0 # image to show state (online, new message etc)
NICK = 1 # contact nickame or ROLE name
TYPE = 2 # type of the row ('contact' or 'role')
TEXT = 3 # text shown in the cellrenderer
AVATAR = 4 # avatar of the contact
empty_pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, 1, 1)
empty_pixbuf.fill(0xffffff00)
@ -100,7 +99,7 @@ def tree_cell_data_func(column, renderer, model, iter_, tv=None):
renderer.set_property('xalign', 1) # align pixbuf to the right
else:
renderer.set_property('xalign', 0.5)
if parent_iter and (model[iter_][C_AVATAR] or avatar_position == \
if parent_iter and (model[iter_][Column.AVATAR] or avatar_position == \
'left'):
renderer.set_property('visible', True)
renderer.set_property('width', gajim.config.get(
@ -449,8 +448,8 @@ class GroupchatControl(ChatControlBase):
#status_image, shown_nick, type, nickname, avatar
self.columns = [Gtk.Image, str, str, str, GdkPixbuf.Pixbuf]
self.model = Gtk.TreeStore(*self.columns)
self.model.set_sort_func(C_NICK, self.tree_compare_iters)
self.model.set_sort_column_id(C_NICK, Gtk.SortType.ASCENDING)
self.model.set_sort_func(Column.NICK, self.tree_compare_iters)
self.model.set_sort_column_id(Column.NICK, Gtk.SortType.ASCENDING)
# columns
column = Gtk.TreeViewColumn()
@ -470,14 +469,14 @@ class GroupchatControl(ChatControlBase):
self.renderers_list += (
# status img
('icon', renderer_image, False,
'image', C_IMG, tree_cell_data_func, self.list_treeview),
'image', Column.IMG, tree_cell_data_func, self.list_treeview),
# contact name
('name', renderer_text, True,
'markup', C_TEXT, tree_cell_data_func, self.list_treeview))
'markup', Column.TEXT, tree_cell_data_func, self.list_treeview))
# avatar img
avater_renderer = ('avatar', Gtk.CellRendererPixbuf(),
False, 'pixbuf', C_AVATAR,
False, 'pixbuf', Column.AVATAR,
tree_cell_data_func, self.list_treeview)
if gajim.config.get('avatar_position_in_roster') == 'right':
@ -550,8 +549,8 @@ class GroupchatControl(ChatControlBase):
except Exception:
return False
typ = self.model[iter_][C_TYPE]
nick = self.model[iter_][C_NICK]
typ = self.model[iter_][Column.TYPE]
nick = self.model[iter_][Column.NICK]
if typ != 'contact':
return False
@ -590,12 +589,12 @@ class GroupchatControl(ChatControlBase):
"""
Compare two iters to sort them
"""
type1 = model[iter1][C_TYPE]
type2 = model[iter2][C_TYPE]
type1 = model[iter1][Column.TYPE]
type2 = model[iter2][Column.TYPE]
if not type1 or not type2:
return 0
nick1 = model[iter1][C_NICK]
nick2 = model[iter2][C_NICK]
nick1 = model[iter1][Column.NICK]
nick2 = model[iter2][Column.NICK]
if not nick1 or not nick2:
return 0
if type1 == 'role':
@ -701,7 +700,7 @@ class GroupchatControl(ChatControlBase):
"""
# Get the room_jid from treeview
for contact in self.iter_contact_rows():
nick = contact[C_NICK]
nick = contact[Column.NICK]
self.draw_contact(nick)
def on_list_treeview_selection_changed(self, selection):
@ -713,9 +712,9 @@ class GroupchatControl(ChatControlBase):
self._last_selected_contact = None
return
contact = model[selected_iter]
nick = contact[C_NICK]
nick = contact[Column.NICK]
self._last_selected_contact = nick
if contact[C_TYPE] != 'contact':
if contact[Column.TYPE] != 'contact':
return
self.draw_contact(nick, selected=True, focus=True)
@ -783,7 +782,7 @@ class GroupchatControl(ChatControlBase):
self.draw_contact(nick)
def _change_style(self, model, path, iter_, option):
model[iter_][C_NICK] = model[iter_][C_NICK]
model[iter_][Column.NICK] = model[iter_][Column.NICK]
def change_roster_style(self):
self.model.foreach(self._change_style, None)
@ -1101,7 +1100,7 @@ class GroupchatControl(ChatControlBase):
gajim.interface.roster.get_appropriate_state_images(
self.room_jid, icon_name='event')
image = state_images['event']
self.model[iter_][C_IMG] = image
self.model[iter_][Column.IMG] = image
if self.parent_win:
self.parent_win.show_title()
self.parent_win.redraw_tab(self)
@ -1123,7 +1122,7 @@ class GroupchatControl(ChatControlBase):
while role_iter:
user_iter = self.model.iter_children(role_iter)
while user_iter:
if nick == self.model[user_iter][C_NICK]:
if nick == self.model[user_iter][Column.NICK]:
return user_iter
else:
user_iter = self.model.iter_next(user_iter)
@ -1487,7 +1486,7 @@ class GroupchatControl(ChatControlBase):
contact in a room
"""
if nick is None:
nick = model[iter_][C_NICK]
nick = model[iter_][Column.NICK]
ctrl = self._start_private_message(nick)
if ctrl and msg:
@ -1549,8 +1548,8 @@ class GroupchatControl(ChatControlBase):
pixbuf2.get_property('height'), 0, 0, 1.0, 1.0,
GdkPixbuf.InterpType.HYPER, 127)
image = Gtk.Image.new_from_pixbuf(pixbuf1)
self.model[iter_][C_IMG] = image
self.model[iter_][C_TEXT] = name
self.model[iter_][Column.IMG] = image
self.model[iter_][Column.TEXT] = name
def draw_avatar(self, nick):
if not gajim.config.get('show_avatars_in_roster'):
@ -1566,7 +1565,7 @@ class GroupchatControl(ChatControlBase):
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'roster')
if not scaled_pixbuf:
scaled_pixbuf = empty_pixbuf
self.model[iter_][C_AVATAR] = scaled_pixbuf
self.model[iter_][Column.AVATAR] = scaled_pixbuf
def draw_role(self, role):
role_iter = self.get_role_iter(role)
@ -1577,7 +1576,7 @@ class GroupchatControl(ChatControlBase):
nbr_role, nbr_total = gajim.contacts.get_nb_role_total_gc_contacts(
self.account, self.room_jid, role)
role_name += ' (%s/%s)' % (repr(nbr_role), repr(nbr_total))
self.model[role_iter][C_TEXT] = role_name
self.model[role_iter][Column.TEXT] = role_name
def draw_all_roles(self):
for role in ('visitor', 'participant', 'moderator'):
@ -1968,7 +1967,7 @@ class GroupchatControl(ChatControlBase):
def get_role_iter(self, role):
role_iter = self.model.get_iter_first()
while role_iter:
role_name = self.model[role_iter][C_NICK]
role_name = self.model[role_iter][Column.NICK]
if role == role_name:
return role_iter
role_iter = self.model.iter_next(role_iter)
@ -2430,7 +2429,7 @@ class GroupchatControl(ChatControlBase):
"""
model = widget.get_model()
image = gajim.interface.jabber_state_images['16']['opened']
model[iter_][C_IMG] = image
model[iter_][Column.IMG] = image
def on_list_treeview_row_collapsed(self, widget, iter_, path):
"""
@ -2438,7 +2437,7 @@ class GroupchatControl(ChatControlBase):
"""
model = widget.get_model()
image = gajim.interface.jabber_state_images['16']['closed']
model[iter_][C_IMG] = image
model[iter_][Column.IMG] = image
def kick(self, widget, nick):
"""
@ -2457,7 +2456,7 @@ class GroupchatControl(ChatControlBase):
"""
Make contact's popup menu
"""
nick = self.model[iter_][C_NICK]
nick = self.model[iter_][Column.NICK]
c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
fjid = self.room_jid + '/' + nick
jid = c.jid
@ -2620,7 +2619,7 @@ class GroupchatControl(ChatControlBase):
else:
widget.expand_row(path, False)
else: # We want to send a private message
nick = self.model[path][C_NICK]
nick = self.model[path][Column.NICK]
self._start_private_message(nick)
def on_list_treeview_row_activated(self, widget, path, col=0):
@ -2651,7 +2650,7 @@ class GroupchatControl(ChatControlBase):
widget.get_selection().select_path(path)
iter_ = self.model.get_iter(path)
if path.get_depth() == 2:
nick = self.model[iter_][C_NICK]
nick = self.model[iter_][Column.NICK]
self._start_private_message(nick)
return True
@ -2661,7 +2660,7 @@ class GroupchatControl(ChatControlBase):
return True
else:
iter_ = self.model.get_iter(path)
nick = self.model[iter_][C_NICK]
nick = self.model[iter_][Column.NICK]
if not nick in gajim.contacts.get_nick_list(self.account,
self.room_jid):
# it's a group

View File

@ -362,7 +362,7 @@ class HashDigest:
def __str__(self):
prettydigest = ''
for i in list(range(0, len(self.digest), 2)):
for i in range(0, len(self.digest), 2):
prettydigest += self.digest[i:i + 2] + ':'
return prettydigest[:-1]

View File

@ -80,13 +80,11 @@ from common import caps_cache
from common import proxy65_manager
from common import socks5
from common import helpers
from common import dataforms
from common import passwords
from common import logging_helpers
from common.connection_handlers_events import OurShowEvent, \
FileRequestErrorEvent, FileTransferCompletedEvent, InformationEvent
FileRequestErrorEvent, FileTransferCompletedEvent
from common.connection import Connection
from common import jingle
from common.file_props import FilesProp
from common import pep
@ -120,7 +118,8 @@ class Interface:
self.db_error_dialog = None
self.db_error_dialog.connect('destroy', destroyed)
def handle_event_information(self, obj):
@staticmethod
def handle_event_information(obj):
if obj.popup:
if obj.level == 'error':
cls = dialogs.ErrorDialog
@ -147,7 +146,8 @@ class Interface:
self.instances['change_nick_dialog'] = dialogs.ChangeNickDialog(
account, room_jid, title, prompt, transient_for=parent_win)
def handle_event_http_auth(self, obj):
@staticmethod
def handle_event_http_auth(obj):
#('HTTP_AUTH', account, (method, url, transaction_id, iq_obj, msg))
def response(account, answer):
obj.conn.build_http_auth_answer(obj.stanza, answer)
@ -200,14 +200,16 @@ class Interface:
if ctrl and ctrl.type_id == message_control.TYPE_GC:
ctrl.print_conversation('Error %s: %s' % (obj.errcode, obj.errmsg))
def handle_event_connection_lost(self, obj):
@staticmethod
def handle_event_connection_lost(obj):
# ('CONNECTION_LOST', account, [title, text])
path = gtkgui_helpers.get_icon_path('gajim-connection_lost', 48)
account = obj.conn.name
notify.popup(_('Connection Failed'), account, account,
'', path, obj.title, obj.msg)
def unblock_signed_in_notifications(self, account):
@staticmethod
def unblock_signed_in_notifications(account):
gajim.block_signed_in_notifications[account] = False
def handle_event_status(self, obj): # OUR status
@ -247,7 +249,8 @@ class Interface:
profile_window.ProfileWindow(account, gajim.interface.roster.window)
gajim.connections[account].request_vcard(jid)
def handle_gc_error(self, gc_control, pritext, sectext):
@staticmethod
def handle_gc_error(gc_control, pritext, sectext):
if gc_control and gc_control.autorejoin is not None:
if gc_control.error_dialog:
gc_control.error_dialog.destroy()
@ -339,7 +342,8 @@ class Interface:
if gc_control and gc_control.autorejoin:
gc_control.autorejoin = False
def handle_event_gc_message(self, obj):
@staticmethod
def handle_event_gc_message(obj):
if not obj.stanza.getTag('body'): # no <body>
# It could be a voice request. See
# http://www.xmpp.org/extensions/xep-0045.html#voiceapprove
@ -443,14 +447,16 @@ class Interface:
if session:
session.roster_message(jid, msg, obj.time_, msg_type='error')
def handle_event_msgsent(self, obj):
@staticmethod
def handle_event_msgsent(obj):
#('MSGSENT', account, (jid, msg, keyID))
# do not play sound when standalone chatstate message (eg no msg)
if obj.message and gajim.config.get_per('soundevents', 'message_sent',
'enabled'):
helpers.play_sound('message_sent')
def handle_event_msgnotsent(self, obj):
@staticmethod
def handle_event_msgnotsent(obj):
#('MSGNOTSENT', account, (jid, ierror_msg, msg, time, session))
msg = _('error while sending %(message)s ( %(error)s )') % {
'message': obj.message, 'error': obj.error}
@ -540,7 +546,8 @@ class Interface:
notify.popup(event_type, obj.jid, account, 'unsubscribed', path,
event_type, obj.jid)
def handle_event_register_agent_info(self, obj):
@staticmethod
def handle_event_register_agent_info(obj):
# ('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
# info in a dataform if is_form is True
if obj.is_form or 'instructions' in obj.config:
@ -688,7 +695,8 @@ class Interface:
_('You are currently connected without your OpenPGP key.'))
self.forget_gpg_passphrase(obj.keyID)
def handle_event_client_cert_passphrase(self, obj):
@staticmethod
def handle_event_client_cert_passphrase(obj):
def on_ok(passphrase, checked):
obj.conn.on_client_cert_passphrase(passphrase, obj.con, obj.port,
obj.secure_tuple)
@ -710,7 +718,8 @@ class Interface:
self.gpg_passphrase[obj.keyid] = request
request.add_callback(obj.conn.name, obj.callback)
def handle_event_gpg_trust_key(self, obj):
@staticmethod
def handle_event_gpg_trust_key(obj):
#('GPG_ALWAYS_TRUST', account, callback)
def on_yes(checked):
if checked:
@ -861,7 +870,8 @@ class Interface:
notify.popup(event_type, jid, account, 'file-send-error', path,
event_type, file_props.name)
def handle_event_gmail_notify(self, obj):
@staticmethod
def handle_event_gmail_notify(obj):
jid = obj.jid
gmail_new_messages = int(obj.newmsgs)
gmail_messages_list = obj.gmail_messages_list
@ -963,7 +973,8 @@ class Interface:
notify.popup(event_type, obj.jid, account, 'file-request',
path_to_image=path, title=event_type, text=txt)
def handle_event_file_error(self, title, message):
@staticmethod
def handle_event_file_error(title, message):
dialogs.ErrorDialog(title, message)
def handle_event_file_progress(self, account, file_props):
@ -1129,7 +1140,8 @@ class Interface:
notify.popup(event_type, jid, account, msg_type, path_to_image=path,
title=event_type, text=txt)
def ask_offline_status(self, account):
@staticmethod
def ask_offline_status(account):
for contact in gajim.contacts.iter_contacts(account):
gajim.connections[account].request_last_status_time(contact.jid,
contact.resource)
@ -1187,13 +1199,16 @@ class Interface:
location_listener.enable()
def handle_event_metacontacts(self, obj):
@staticmethod
def handle_event_metacontacts(obj):
gajim.contacts.define_metacontacts(obj.conn.name, obj.meta_list)
def handle_atom_entry(self, obj):
@staticmethod
def handle_atom_entry(obj):
AtomWindow.newAtomEntry(obj.atom_entry)
def handle_event_failed_decrypt(self, obj):
@staticmethod
def handle_event_failed_decrypt(obj):
details = _('Unable to decrypt message from %s\nIt may have been '
'tampered with.') % obj.fjid
dialogs.WarningDialog(_('Unable to decrypt message'), details)
@ -1328,7 +1343,8 @@ class Interface:
if ctrl:
ctrl.set_audio_state('error', reason=obj.reason)
def handle_event_roster_item_exchange(self, obj):
@staticmethod
def handle_event_roster_item_exchange(obj):
# data = (action in [add, delete, modify], exchange_list, jid_from)
dialogs.RosterItemExchangeWindow(obj.conn.name, obj.action,
obj.exchange_items_list, obj.fjid)
@ -1821,7 +1837,8 @@ class Interface:
### Methods dealing with emoticons
################################################################################
def image_is_ok(self, image):
@staticmethod
def image_is_ok(image):
if not os.path.exists(image):
return False
img = Gtk.Image()
@ -2317,7 +2334,8 @@ class Interface:
### Other Methods
################################################################################
def change_awn_icon_status(self, status):
@staticmethod
def change_awn_icon_status(status):
if not dbus_support.supported:
# do nothing if user doesn't have D-Bus bindings
return
@ -2356,8 +2374,8 @@ class Interface:
listener.disconnect(self.music_track_changed_signal)
self.music_track_changed_signal = None
def music_track_changed(self, unused_listener, music_track_info,
account=None):
@staticmethod
def music_track_changed(unused_listener, music_track_info, account=None):
if not account:
accounts = gajim.connections.keys()
else:
@ -2478,7 +2496,8 @@ class Interface:
self.systray_enabled = False
self.systray.hide_icon()
def on_launch_browser_mailer(self, widget, url, kind):
@staticmethod
def on_launch_browser_mailer(widget, url, kind):
helpers.launch_browser_mailer(kind, url)
def process_connections(self):
@ -2497,7 +2516,8 @@ class Interface:
raise
return True # renew timeout (loop for ever)
def save_config(self):
@staticmethod
def save_config():
err_str = parser.write()
if err_str is not None:
print(err_str, file=sys.stderr)
@ -2507,7 +2527,8 @@ class Interface:
'preferences'), err_str)
sys.exit()
def save_avatar_files(self, jid, photo, puny_nick = None, local = False):
@staticmethod
def save_avatar_files(jid, photo, puny_nick = None, local = False):
"""
Save an avatar to a separate file, and generate files for dbus
notifications. An avatar can be given as a pixmap directly or as an
@ -2567,7 +2588,8 @@ class Interface:
log.error('Error writing avatar file %s: %s' % \
(path_to_original_file, str(e)))
def remove_avatar_files(self, jid, puny_nick = None, local = False):
@staticmethod
def remove_avatar_files(jid, puny_nick = None, local = False):
"""
Remove avatar files of a jid
"""
@ -2657,7 +2679,8 @@ class Interface:
return gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC
def get_pep_icon(self, pep_obj):
@staticmethod
def get_pep_icon(pep_obj):
if isinstance(pep_obj, pep.UserMoodPEP):
received_mood = pep_obj._pep_specific_data['mood']
mood = received_mood if received_mood in pep.MOODS else 'unknown'
@ -2688,7 +2711,8 @@ class Interface:
quiet=True)
return icon
def create_ipython_window(self):
@staticmethod
def create_ipython_window():
try:
from ipython_view import IPythonView
except ImportError:

View File

@ -59,13 +59,15 @@ def build_resources_submenu(contacts, account, action, room_jid=None,
return sub_menu
def build_invite_submenu(invite_menuitem, list_, ignore_rooms=[],
def build_invite_submenu(invite_menuitem, list_, ignore_rooms=None,
show_bookmarked=False, force_resource=False):
"""
list_ in a list of (contact, account)
force_resource means we want to send invitation even if there is only one
resource
"""
if ignore_rooms is None:
ignore_rooms = []
roster = gajim.interface.roster
# used if we invite only one contact with several resources
contact_list = []

View File

@ -89,7 +89,6 @@ del parseOpts
import common.configpaths
common.configpaths.gajimpaths.init(config_path)
del config_path
from common import exceptions
from common import gajim
import gtkgui_helpers
from common.logger import LOG_DB_PATH, constants
@ -100,13 +99,13 @@ status = dict((constants.__dict__[i], i[5:].lower()) for i in \
from common import helpers
import dialogs
# time, message, subject
(
C_UNIXTIME,
C_MESSAGE,
C_SUBJECT,
C_NICKNAME
) = range(2, 6)
from enum import IntEnum
class Column(IntEnum):
UNIXTIME = 2
MESSAGE = 3
SUBJECT = 4
NICKNAME = 5
import sqlite3 as sqlite
@ -177,32 +176,32 @@ class HistoryManager:
self.logs_listview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
renderer_text = Gtk.CellRendererText() # holds time
col = Gtk.TreeViewColumn(_('Date'), renderer_text, text=C_UNIXTIME)
col = Gtk.TreeViewColumn(_('Date'), renderer_text, text=Column.UNIXTIME)
# user can click this header and sort
col.set_sort_column_id(C_UNIXTIME)
col.set_sort_column_id(Column.UNIXTIME)
col.set_resizable(True)
self.logs_listview.append_column(col)
renderer_text = Gtk.CellRendererText() # holds nickname
col = Gtk.TreeViewColumn(_('Nickname'), renderer_text, text=C_NICKNAME)
col = Gtk.TreeViewColumn(_('Nickname'), renderer_text, text=Column.NICKNAME)
# user can click this header and sort
col.set_sort_column_id(C_NICKNAME)
col.set_sort_column_id(Column.NICKNAME)
col.set_resizable(True)
col.set_visible(False)
self.nickname_col_for_logs = col
self.logs_listview.append_column(col)
renderer_text = Gtk.CellRendererText() # holds message
col = Gtk.TreeViewColumn(_('Message'), renderer_text, markup=C_MESSAGE)
col = Gtk.TreeViewColumn(_('Message'), renderer_text, markup=Column.MESSAGE)
# user can click this header and sort
col.set_sort_column_id(C_MESSAGE)
col.set_sort_column_id(Column.MESSAGE)
col.set_resizable(True)
self.message_col_for_logs = col
self.logs_listview.append_column(col)
renderer_text = Gtk.CellRendererText() # holds subject
col = Gtk.TreeViewColumn(_('Subject'), renderer_text, text=C_SUBJECT)
col.set_sort_column_id(C_SUBJECT) # user can click this header and sort
col = Gtk.TreeViewColumn(_('Subject'), renderer_text, text=Column.SUBJECT)
col.set_sort_column_id(Column.SUBJECT) # user can click this header and sort
col.set_resizable(True)
col.set_visible(False)
self.subject_col_for_logs = col
@ -221,28 +220,28 @@ class HistoryManager:
self.search_results_listview.append_column(col)
renderer_text = Gtk.CellRendererText() # holds time
col = Gtk.TreeViewColumn(_('Date'), renderer_text, text=C_UNIXTIME)
col = Gtk.TreeViewColumn(_('Date'), renderer_text, text=Column.UNIXTIME)
# user can click this header and sort
col.set_sort_column_id(C_UNIXTIME)
col.set_sort_column_id(Column.UNIXTIME)
col.set_resizable(True)
self.search_results_listview.append_column(col)
renderer_text = Gtk.CellRendererText() # holds message
col = Gtk.TreeViewColumn(_('Message'), renderer_text, text=C_MESSAGE)
col.set_sort_column_id(C_MESSAGE) # user can click this header and sort
col = Gtk.TreeViewColumn(_('Message'), renderer_text, text=Column.MESSAGE)
col.set_sort_column_id(Column.MESSAGE) # user can click this header and sort
col.set_resizable(True)
self.search_results_listview.append_column(col)
renderer_text = Gtk.CellRendererText() # holds subject
col = Gtk.TreeViewColumn(_('Subject'), renderer_text, text=C_SUBJECT)
col.set_sort_column_id(C_SUBJECT) # user can click this header and sort
col = Gtk.TreeViewColumn(_('Subject'), renderer_text, text=Column.SUBJECT)
col.set_sort_column_id(Column.SUBJECT) # user can click this header and sort
col.set_resizable(True)
self.search_results_listview.append_column(col)
renderer_text = Gtk.CellRendererText() # holds nickname
col = Gtk.TreeViewColumn(_('Nickname'), renderer_text, text=C_NICKNAME)
col = Gtk.TreeViewColumn(_('Nickname'), renderer_text, text=Column.NICKNAME)
# user can click this header and sort
col.set_sort_column_id(C_NICKNAME)
col.set_sort_column_id(Column.NICKNAME)
col.set_resizable(True)
self.search_results_listview.append_column(col)

View File

@ -31,6 +31,8 @@ from gi.repository import GLib
import time
import calendar
from enum import IntEnum
import gtkgui_helpers
import conversation_textview
import dialogs
@ -39,26 +41,21 @@ from common import gajim
from common import helpers
from common import exceptions
from common.logger import Constants
from common.logger import ShowConstant, KindConstant
constants = Constants()
class InfoColumn(IntEnum):
'''Completion dict'''
JID = 0
ACCOUNT = 1
NAME = 2
COMPLETION = 3
# Completion dict
(
C_INFO_JID,
C_INFO_ACCOUNT,
C_INFO_NAME,
C_INFO_COMPLETION
) = range(4)
# contact_name, date, message, time
(
C_LOG_JID,
C_CONTACT_NAME,
C_UNIXTIME,
C_MESSAGE,
C_TIME
) = range(5)
class Column(IntEnum):
LOG_JID = 0
CONTACT_NAME = 1
UNIXTIME = 2
MESSAGE = 3
TIME = 4
class HistoryWindow:
"""
@ -95,23 +92,23 @@ class HistoryWindow:
self.results_treeview.append_column(col)
renderer = Gtk.CellRendererText()
col.pack_start(renderer, True)
col.add_attribute(renderer, 'text', C_CONTACT_NAME)
col.set_sort_column_id(C_CONTACT_NAME) # user can click this header and sort
col.add_attribute(renderer, 'text', Column.CONTACT_NAME)
col.set_sort_column_id(Column.CONTACT_NAME) # user can click this header and sort
col.set_resizable(True)
col = Gtk.TreeViewColumn(_('Date'))
self.results_treeview.append_column(col)
renderer = Gtk.CellRendererText()
col.pack_start(renderer, True)
col.add_attribute(renderer, 'text', C_UNIXTIME)
col.set_sort_column_id(C_UNIXTIME) # user can click this header and sort
col.add_attribute(renderer, 'text', Column.UNIXTIME)
col.set_sort_column_id(Column.UNIXTIME) # user can click this header and sort
col.set_resizable(True)
col = Gtk.TreeViewColumn(_('Message'))
self.results_treeview.append_column(col)
renderer = Gtk.CellRendererText()
col.pack_start(renderer, True)
col.add_attribute(renderer, 'text', C_MESSAGE)
col.add_attribute(renderer, 'text', Column.MESSAGE)
col.set_resizable(True)
self.jid = None # The history we are currently viewing
@ -372,17 +369,17 @@ class HistoryWindow:
widget.mark_day(day)
def _get_string_show_from_constant_int(self, show):
if show == constants.SHOW_ONLINE:
if show == ShowConstant.ONLINE:
show = 'online'
elif show == constants.SHOW_CHAT:
elif show == ShowConstant.CHAT:
show = 'chat'
elif show == constants.SHOW_AWAY:
elif show == ShowConstant.AWAY:
show = 'away'
elif show == constants.SHOW_XA:
elif show == ShowConstant.XA:
show = 'xa'
elif show == constants.SHOW_DND:
elif show == ShowConstant.DND:
show = 'dnd'
elif show == constants.SHOW_OFFLINE:
elif show == ShowConstant.OFFLINE:
show = 'offline'
return show
@ -402,8 +399,8 @@ class HistoryWindow:
# line[0] is contact_name, line[1] is time of message
# line[2] is kind, line[3] is show, line[4] is message, line[5] is subject
# line[6] is additional_data
if not show_status and line[2] in (constants.KIND_GCSTATUS,
constants.KIND_STATUS):
if not show_status and line[2] in (KindConstant.GCSTATUS,
KindConstant.STATUS):
continue
self._add_new_line(line[0], line[1], line[2], line[3], line[4],
line[5], line[6])
@ -412,8 +409,8 @@ class HistoryWindow:
"""
Add a new line in textbuffer
"""
if not message and kind not in (constants.KIND_STATUS,
constants.KIND_GCSTATUS):
if not message and kind not in (KindConstant.STATUS,
KindConstant.GCSTATUS):
return
buf = self.history_buffer
end_iter = buf.get_end_iter()
@ -444,15 +441,15 @@ class HistoryWindow:
show = self._get_string_show_from_constant_int(show)
if kind == constants.KIND_GC_MSG:
if kind == KindConstant.GC_MSG:
tag_name = 'incoming'
elif kind in (constants.KIND_SINGLE_MSG_RECV,
constants.KIND_CHAT_MSG_RECV):
contact_name = self.completion_dict[self.jid][C_INFO_NAME]
elif kind in (KindConstant.SINGLE_MSG_RECV,
KindConstant.CHAT_MSG_RECV):
contact_name = self.completion_dict[self.jid][InfoColumn.NAME]
tag_name = 'incoming'
tag_msg = 'incomingtxt'
elif kind in (constants.KIND_SINGLE_MSG_SENT,
constants.KIND_CHAT_MSG_SENT):
elif kind in (KindConstant.SINGLE_MSG_SENT,
KindConstant.CHAT_MSG_SENT):
if self.account:
contact_name = gajim.nicks[self.account]
else:
@ -462,7 +459,7 @@ class HistoryWindow:
contact_name = gajim.nicks[account]
tag_name = 'outgoing'
tag_msg = 'outgoingtxt'
elif kind == constants.KIND_GCSTATUS:
elif kind == KindConstant.GCSTATUS:
# message here (if not None) is status message
if message:
message = _('%(nick)s is now %(status)s: %(status_msg)s') %\
@ -492,7 +489,7 @@ class HistoryWindow:
else:
# do not do this if gcstats, avoid dupping contact_name
# eg. nkour: nkour is now Offline
if contact_name and kind != constants.KIND_GCSTATUS:
if contact_name and kind != KindConstant.GCSTATUS:
# add stuff before and after contact name
before_str = gajim.config.get('before_nickname')
before_str = helpers.from_one_line(before_str)
@ -532,7 +529,7 @@ class HistoryWindow:
# perform search in preselected jids
# jids are preselected with the query_entry
for jid in self.jids_to_search:
account = self.completion_dict[jid][C_INFO_ACCOUNT]
account = self.completion_dict[jid][InfoColumn.ACCOUNT]
if account is None:
# We do not know an account. This can only happen if the contact is offine,
# or if we browse a groupchat history. The account is not needed, a dummy can
@ -554,16 +551,16 @@ class HistoryWindow:
# add "subject: | message: " in message column if kind is single
# also do we need show at all? (we do not search on subject)
for row in results:
if not show_status and row[2] in (constants.KIND_GCSTATUS,
constants.KIND_STATUS):
if not show_status and row[2] in (KindConstant.GCSTATUS,
KindConstant.STATUS):
continue
contact_name = row[0]
if not contact_name:
kind = row[2]
if kind == constants.KIND_CHAT_MSG_SENT: # it's us! :)
if kind == KindConstant.CHAT_MSG_SENT: # it's us! :)
contact_name = gajim.nicks[account]
else:
contact_name = self.completion_dict[jid][C_INFO_NAME]
contact_name = self.completion_dict[jid][InfoColumn.NAME]
tim = row[1]
message = row[4]
local_time = time.localtime(tim)
@ -583,14 +580,14 @@ class HistoryWindow:
cur_month = gtkgui_helpers.make_gtk_month_python_month(cur_month)
model = widget.get_model()
# make it a tupple (Y, M, D, 0, 0, 0...)
tim = time.strptime(model[path][C_UNIXTIME], '%Y-%m-%d')
tim = time.strptime(model[path][Column.UNIXTIME], '%Y-%m-%d')
year = tim[0]
gtk_month = tim[1]
month = gtkgui_helpers.make_python_month_gtk_month(gtk_month)
day = tim[2]
# switch to belonging logfile if necessary
log_jid = model[path][C_LOG_JID]
log_jid = model[path][Column.LOG_JID]
if log_jid != self.jid:
self._load_history(log_jid, None)
@ -599,7 +596,7 @@ class HistoryWindow:
self.calendar.select_month(month, year)
self.calendar.select_day(day)
unix_time = model[path][C_TIME]
unix_time = model[path][Column.TIME]
self._scroll_to_result(unix_time)
#FIXME: one day do not search just for unix_time but the whole and user
# specific format of the textbuffer line [time] nick: message
@ -666,5 +663,5 @@ class HistoryWindow:
gajim.config.set('history_window_x-position', x)
gajim.config.set('history_window_y-position', y)
gajim.config.set('history_window_width', width);
gajim.config.set('history_window_height', height);
gajim.config.set('history_window_width', width)
gajim.config.set('history_window_height', height)

View File

@ -51,7 +51,6 @@ if __name__ == '__main__':
from common import i18n
import common.configpaths
common.configpaths.gajimpaths.init(None)
import gtkgui_helpers
from common import gajim
from gtkgui_helpers import get_icon_pixmap
from common import helpers
@ -1130,8 +1129,6 @@ class HtmlTextView(Gtk.TextView):
change_cursor = None
if __name__ == '__main__':
import os
from conversation_textview import ConversationTextview
import gajim as gaj

View File

@ -166,7 +166,7 @@ class IterableIPShell:
sys.stdout = IPython.utils.io.stdout
orig_stdin = sys.stdin
sys.stdin = IPython.utils.io.stdin;
sys.stdin = IPython.utils.io.stdin
self.prompt = self.generatePrompt(self.iter_more)
self.IP.hooks.pre_prompt_hook()

View File

@ -57,7 +57,7 @@ def on_suspend(active):
# close the file descriptor and let the computer suspend
if fd >= 0:
os.close(fd)
fd = -1;
fd = -1
else:
# something is wrong, the system is suspending but we don't have
# a lock file

View File

@ -120,7 +120,7 @@ class MessageWindow(object):
'<Control>b', '<Control>F4',
'<Control>w', '<Control>Page_Up', '<Control>Page_Down', '<Alt>Right',
'<Alt>Left', '<Alt>d', '<Alt>c', '<Alt>m', '<Alt>t', 'Escape'] + \
['<Alt>'+str(i) for i in list(range(10))]
['<Alt>'+str(i) for i in range(10)]
accel_group = Gtk.AccelGroup()
for key in keys:
keyval, mod = Gtk.accelerator_parse(key)
@ -877,7 +877,7 @@ class MessageWindow(object):
to_right = False
horiz = self.notebook.get_tab_pos() == Gtk.PositionType.TOP or \
self.notebook.get_tab_pos() == Gtk.PositionType.BOTTOM
for i in list(range(self.notebook.get_n_pages())):
for i in range(self.notebook.get_n_pages()):
page = self.notebook.get_nth_page(i)
tab = self.notebook.get_tab_label(page)
tab_alloc = tab.get_allocation()
@ -903,7 +903,7 @@ class MessageWindow(object):
Find the page num of the tab label
"""
page_num = -1
for i in list(range(self.notebook.get_n_pages())):
for i in range(self.notebook.get_n_pages()):
page = self.notebook.get_nth_page(i)
tab = self.notebook.get_tab_label(page)
if tab == tab_label:

View File

@ -32,22 +32,23 @@ from gi.repository import GdkPixbuf
from gi.repository import GLib, Gdk
import os
from enum import IntEnum
import gtkgui_helpers
from dialogs import WarningDialog, YesNoDialog, ArchiveChooserDialog
from htmltextview import HtmlTextView
from common import gajim
from plugins.helpers import log_calls, log
from plugins.helpers import log_calls
from plugins.helpers import GajimPluginActivateException
from plugins.plugins_i18n import _
from common.exceptions import PluginsystemError
(
PLUGIN,
NAME,
ACTIVE,
ACTIVATABLE,
ICON,
) = range(5)
class Column(IntEnum):
PLUGIN = 0
NAME = 1
ACTIVE = 2
ACTIVATABLE = 3
ICON = 4
class PluginsWindow(object):
@ -78,19 +79,19 @@ class PluginsWindow(object):
self.installed_plugins_treeview.set_rules_hint(True)
renderer = Gtk.CellRendererText()
col = Gtk.TreeViewColumn(_('Plugin'))#, renderer, text=NAME)
col = Gtk.TreeViewColumn(_('Plugin'))#, renderer, text=Column.NAME)
cell = Gtk.CellRendererPixbuf()
col.pack_start(cell, False)
col.add_attribute(cell, 'pixbuf', ICON)
col.add_attribute(cell, 'pixbuf', Column.ICON)
col.pack_start(renderer, True)
col.add_attribute(renderer, 'text', NAME)
col.add_attribute(renderer, 'text', Column.NAME)
col.set_property('expand', True)
self.installed_plugins_treeview.append_column(col)
renderer = Gtk.CellRendererToggle()
renderer.connect('toggled', self.installed_plugins_toggled_cb)
col = Gtk.TreeViewColumn(_('Active'), renderer, active=ACTIVE,
activatable=ACTIVATABLE)
col = Gtk.TreeViewColumn(_('Active'), renderer, active=Column.ACTIVE,
activatable=Column.ACTIVATABLE)
self.installed_plugins_treeview.append_column(col)
self.def_icon = gtkgui_helpers.get_icon_pixmap('preferences-desktop')
@ -127,9 +128,9 @@ class PluginsWindow(object):
def installed_plugins_treeview_selection_changed(self, treeview_selection):
model, iter = treeview_selection.get_selected()
if iter:
plugin = model.get_value(iter, PLUGIN)
plugin_name = model.get_value(iter, NAME)
is_active = model.get_value(iter, ACTIVE)
plugin = model.get_value(iter, Column.PLUGIN)
plugin_name = model.get_value(iter, Column.NAME)
is_active = model.get_value(iter, Column.ACTIVE)
self._display_installed_plugin_info(plugin)
else:
@ -161,7 +162,7 @@ class PluginsWindow(object):
self.uninstall_plugin_button.set_property('sensitive',
gajim.PLUGINS_DIRS[1] in plugin.__path__)
self.configure_plugin_button.set_property(
'sensitive', not plugin.config_dialog is None)
'sensitive', plugin.config_dialog is not None)
def _clear_installed_plugin_info(self):
self.plugin_name_label.set_text('')
@ -198,8 +199,8 @@ class PluginsWindow(object):
@log_calls('PluginsWindow')
def installed_plugins_toggled_cb(self, cell, path):
is_active = self.installed_plugins_model[path][ACTIVE]
plugin = self.installed_plugins_model[path][PLUGIN]
is_active = self.installed_plugins_model[path][Column.ACTIVE]
plugin = self.installed_plugins_model[path][Column.PLUGIN]
if is_active:
gajim.plugin_manager.deactivate_plugin(plugin)
@ -211,7 +212,7 @@ class PluginsWindow(object):
transient_for=self.window)
return
self.installed_plugins_model[path][ACTIVE] = not is_active
self.installed_plugins_model[path][Column.ACTIVE] = not is_active
@log_calls('PluginsWindow')
def on_plugins_window_destroy(self, widget):
@ -224,13 +225,12 @@ class PluginsWindow(object):
@log_calls('PluginsWindow')
def on_configure_plugin_button_clicked(self, widget):
#log.debug('widget: %s'%(widget))
selection = self.installed_plugins_treeview.get_selection()
model, iter = selection.get_selected()
if iter:
plugin = model.get_value(iter, PLUGIN)
plugin_name = model.get_value(iter, NAME)
is_active = model.get_value(iter, ACTIVE)
plugin = model.get_value(iter, Column.PLUGIN)
plugin_name = model.get_value(iter, Column.NAME)
is_active = model.get_value(iter, Column.ACTIVE)
result = plugin.config_dialog.run(self.window)
@ -246,9 +246,9 @@ class PluginsWindow(object):
selection = self.installed_plugins_treeview.get_selection()
model, iter = selection.get_selected()
if iter:
plugin = model.get_value(iter, PLUGIN)
plugin_name = model.get_value(iter, NAME)
is_active = model.get_value(iter, ACTIVE)
plugin = model.get_value(iter, Column.PLUGIN)
plugin_name = model.get_value(iter, Column.NAME)
is_active = model.get_value(iter, Column.ACTIVE)
try:
gajim.plugin_manager.remove_plugin(plugin)
except PluginsystemError as e:
@ -274,9 +274,9 @@ class PluginsWindow(object):
return
model = self.installed_plugins_model
for row in list(range(len(model))):
if plugin == model[row][PLUGIN]:
model.remove(model.get_iter((row, PLUGIN)))
for i, row in enumerate(model):
if plugin == row[Column.PLUGIN]:
model.remove(model.get_iter((i, Column.PLUGIN)))
break
iter_ = model.append([plugin, plugin.name, False,

View File

@ -698,7 +698,7 @@ class SignalObject(dbus.service.Object):
if self._is_first():
win.window.focus(Gtk.get_current_event_time())
else:
win.window.focus(long(time()))
win.window.focus(int(time()))
@dbus.service.method(INTERFACE, in_signature='', out_signature='')
def show_roster(self):
@ -711,7 +711,7 @@ class SignalObject(dbus.service.Object):
if self._is_first():
win.window.focus(Gtk.get_current_event_time())
else:
win.window.focus(long(time()))
win.window.focus(int(time()))
@dbus.service.method(INTERFACE, in_signature='', out_signature='')
def toggle_ipython(self):

File diff suppressed because it is too large Load Diff

View File

@ -267,13 +267,16 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
def roster_message(self, jid, msg, tim, encrypted=False, msg_type='',
subject=None, resource='', msg_log_id=None, user_nick='', xhtml=None,
form_node=None, displaymarking=None, additional_data={}):
form_node=None, displaymarking=None, additional_data=None):
"""
Display the message or show notification in the roster
"""
contact = None
fjid = jid
if additional_data is None:
additional_data = {}
# Try to catch the contact with correct resource
if resource:
fjid = jid + '/' + resource

View File

@ -25,7 +25,6 @@
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
import os
import dialogs
@ -35,7 +34,6 @@ import gtkgui_helpers
from common import gajim
from common import helpers
from common import pep
class StatusIcon:
"""

View File

@ -40,7 +40,6 @@ import gtkgui_helpers
from common import gajim
from common import helpers
from common.pep import MOODS, ACTIVITIES
from common.i18n import Q_
class BaseTooltip:
@ -920,4 +919,4 @@ def colorize_status(status):
color = gajim.config.get('tooltip_status_offline_color')
if color:
status = formatted % (color, status)
return status
return status

View File

@ -33,8 +33,6 @@ def on_suspend(*args, **kwargs):
conn.time_to_reconnect = 5
if dbus_support.supported:
import dbus
try:
from common.dbus_support import system_bus
bus = system_bus.bus()