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:
commit
26aa0382d3
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
##
|
||||
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GLib
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ detected.
|
|||
"""
|
||||
|
||||
from ..framework import CommandContainer, command, doc
|
||||
from .hosts import *
|
||||
from .hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
|
||||
|
||||
class CustomCommonCommands(CommandContainer):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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:
|
||||
|
|
387
src/common/dh.py
387
src/common/dh.py
|
@ -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))
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)]
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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=_('Couldn’t 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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
10
src/disco.py
10
src/disco.py
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue