Remove support for ESessions

Fixes #5294
This commit is contained in:
Philipp Hörist 2018-05-27 01:01:35 +02:00
parent 56fbe32b11
commit 32b74b459e
23 changed files with 42 additions and 1957 deletions

View File

@ -15,7 +15,6 @@
- python3-keyring for saving your password to your system keyring
- python3-pil (pillow) for support of webp avatars
- python3-crypto to enable End to end encryption
- python3-gnupg to enable GPG encryption
- For zeroconf (bonjour) you need python3-dbus
- gir1.2-gspell-1 and hunspell-LANG where lang is your locale eg. en, fr etc

View File

@ -42,7 +42,6 @@ from gajim.common import app
from gajim.common import helpers
from gajim.common import ged
from gajim.common import i18n
from gajim.common.stanza_session import EncryptedStanzaSession, ArchivingStanzaSession
from gajim.common.contacts import GC_Contact
from nbxmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC
from nbxmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO
@ -207,9 +206,6 @@ class ChatControl(ChatControlBase):
session.control = self
self.session = session
if session.enable_encryption:
self.print_esession_details()
# Enable encryption if needed
self.no_autonegotiation = False
self.add_actions()
@ -231,8 +227,6 @@ class ChatControl(ChatControlBase):
# Dont connect this when PrivateChatControl is used
app.ged.register_event_handler('update-roster-avatar', ged.GUI1,
self._nec_update_avatar)
app.ged.register_event_handler('failed-decrypt', ged.GUI1,
self._nec_failed_decrypt)
app.ged.register_event_handler('chatstate-received', ged.GUI1,
self._nec_chatstate_received)
app.ged.register_event_handler('caps-received', ged.GUI1,
@ -920,53 +914,6 @@ class ChatControl(ChatControlBase):
chatstate=chatstate_to_send, xhtml=xhtml,
process_commands=process_commands, attention=attention)
def on_cancel_session_negotiation(self):
msg = _('Session negotiation cancelled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
def print_archiving_session_details(self):
"""
Print esession settings to textview
"""
archiving = bool(self.session) and isinstance(self.session,
ArchivingStanzaSession) and self.session.archiving
if archiving:
msg = _('This session WILL be archived on server')
else:
msg = _('This session WILL NOT be archived on server')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
def print_esession_details(self):
"""
Print esession settings to textview
"""
e2e_is_active = bool(self.session) and self.session.enable_encryption
if e2e_is_active:
msg = _('This session is encrypted')
if self.session.is_loggable():
msg += _(' and WILL be logged')
else:
msg += _(' and WILL NOT be logged')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if not self.session.verified_identity:
ChatControlBase.print_conversation_line(self, _("Remote contact's identity not verified. Click the shield button for more details."), 'status', '', None)
else:
msg = _('end-to-end encryption disabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
self._show_lock_image(e2e_is_active, 'E2E',
self.session and self.session.verified_identity)
def print_session_details(self, old_session=None):
if isinstance(self.session, EncryptedStanzaSession) or \
(old_session and isinstance(old_session, EncryptedStanzaSession)):
self.print_esession_details()
elif isinstance(self.session, ArchivingStanzaSession):
self.print_archiving_session_details()
def get_our_nick(self):
return app.nicks[self.account]
@ -1186,8 +1133,6 @@ class ChatControl(ChatControlBase):
if self.TYPE_ID == message_control.TYPE_CHAT:
app.ged.remove_event_handler('update-roster-avatar', ged.GUI1,
self._nec_update_avatar)
app.ged.remove_event_handler('failed-decrypt', ged.GUI1,
self._nec_failed_decrypt)
app.ged.remove_event_handler('chatstate-received', ged.GUI1,
self._nec_chatstate_received)
app.ged.remove_event_handler('caps-received', ged.GUI1,
@ -1482,57 +1427,6 @@ class ChatControl(ChatControlBase):
"""
dialogs.TransformChatToMUC(self.account, [self.contact.jid])
def activate_esessions(self):
if not (self.session and self.session.enable_encryption):
self.begin_e2e_negotiation()
def terminate_esessions(self):
if not (self.session and self.session.enable_encryption):
return
# e2e was enabled, disable it
jid = str(self.session.jid)
thread_id = self.session.thread_id
self.session.terminate_e2e()
app.connections[self.account].delete_session(jid, thread_id)
# presumably the user had a good reason to shut it off, so
# disable autonegotiation too
self.no_autonegotiation = True
def begin_negotiation(self):
self.no_autonegotiation = True
if not self.session:
fjid = self.contact.get_full_jid()
new_sess = app.connections[self.account].make_new_session(fjid, type_=self.type_id)
self.set_session(new_sess)
def begin_e2e_negotiation(self):
self.begin_negotiation()
self.session.resource = self.contact.resource
self.session.negotiate_e2e(False)
def _nec_failed_decrypt(self, obj):
if obj.session != self.session:
return
details = _('Unable to decrypt message from %s\nIt may have been '
'tampered with.') % obj.fjid
self.print_conversation_line(details, 'status', '', obj.timestamp)
# terminate the session
thread_id = self.session.thread_id
self.session.terminate_e2e()
obj.conn.delete_session(obj.fjid, thread_id)
# restart the session
self.begin_e2e_negotiation()
# Stop emission so it doesn't go to gui_interface
return True
def got_connected(self):
ChatControlBase.got_connected(self)
# Refreshing contact

View File

@ -451,9 +451,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
plugin = app.plugin_manager.encryption_plugins[encryption]
if not plugin.activate_encryption(self):
return
else:
if not self.widget_name == 'groupchat_control':
self.terminate_esessions()
action.set_state(param)
self.set_encryption_state(encryption)
self.set_encryption_menu_icon()

View File

@ -147,7 +147,6 @@ caps_hash = {}
_dependencies = {
'PYTHON-DBUS': False,
'PYBONJOUR': False,
'PYCRYPTO': False,
'PYGPG': False,
'GPG_BINARY': False,
'FARSTREAM': False,
@ -188,13 +187,6 @@ def detect_dependencies():
except Exception:
pass
# PYCRYPTO
try:
import Crypto
_dependencies['PYCRYPTO'] = True
except ImportError:
pass
# python-gnupg
try:
import gnupg

View File

@ -338,8 +338,6 @@ class Config:
'keyid': [ opt_str, '', '', True ],
'gpg_sign_presence': [ opt_bool, True, _('If disabled, don\'t sign presences with GPG key, even if GPG is configured.') ],
'keyname': [ opt_str, '', '', True ],
'enable_esessions': [opt_bool, True, _('Enable ESessions encryption for this account.'), True],
'autonegotiate_esessions': [opt_bool, False, _('Should Gajim automatically start an encrypted session when possible?')],
'allow_plaintext_connection': [ opt_bool, False, _('Allow plaintext connections')],
'tls_version': [ opt_str, '1.2', '' ],
'cipher_list': [ opt_str, 'HIGH:!aNULL:RC4-SHA', '' ],

View File

@ -340,10 +340,7 @@ class CommonConnection:
# <body> tag is the active event
if obj.chatstate and contact and contact.supports(nbxmpp.NS_CHATSTATES):
msg_iq.setTag(obj.chatstate, namespace=nbxmpp.NS_CHATSTATES)
only_chatste = False
if not obj.message:
only_chatste = True
if only_chatste and not obj.session.enable_encryption:
msg_iq.setTag('no-store',
namespace=nbxmpp.NS_MSG_HINTS)

View File

@ -830,8 +830,6 @@ class ConnectionHandlersBase:
self._nec_iq_error_received)
app.ged.register_event_handler('presence-received', ged.CORE,
self._nec_presence_received)
app.ged.register_event_handler('gc-presence-received', ged.CORE,
self._nec_gc_presence_received)
app.ged.register_event_handler('message-received', ged.CORE,
self._nec_message_received)
app.ged.register_event_handler('mam-message-received', ged.CORE,
@ -848,8 +846,6 @@ class ConnectionHandlersBase:
self._nec_iq_error_received)
app.ged.remove_event_handler('presence-received', ged.CORE,
self._nec_presence_received)
app.ged.remove_event_handler('gc-presence-received', ged.CORE,
self._nec_gc_presence_received)
app.ged.remove_event_handler('message-received', ged.CORE,
self._nec_message_received)
app.ged.remove_event_handler('mam-message-received', ged.CORE,
@ -975,24 +971,6 @@ class ConnectionHandlersBase:
# resource signs off!
self.stop_all_active_file_transfers(obj.contact)
# disable encryption, since if any messages are
# lost they'll be not decryptable (note that
# this contradicts XEP-0201 - trying to get that
# in the XEP, though)
# there won't be any sessions here if the contact terminated
# their sessions before going offline (which we do)
for sess in self.get_sessions(jid):
sess_fjid = sess.jid.getStripped()
if sess.resource:
sess_fjid += '/' + sess.resource
if obj.fjid != sess_fjid:
continue
if sess.control:
sess.control.no_autonegotiation = False
if sess.enable_encryption:
sess.terminate_e2e()
if app.config.get('log_contact_status_changes') and \
app.config.should_log(self.name, obj.jid):
show = app.logger.convert_show_values_to_db_api_values(obj.show)
@ -1004,15 +982,6 @@ class ConnectionHandlersBase:
message=obj.status,
show=show)
def _nec_gc_presence_received(self, obj):
if obj.conn.name != self.name:
return
for sess in self.get_sessions(obj.fjid):
if obj.fjid != sess.jid:
continue
if sess.enable_encryption:
sess.terminate_e2e()
def _nec_message_received(self, obj):
if obj.conn.name != self.name:
return
@ -1210,14 +1179,7 @@ class ConnectionHandlersBase:
except KeyError:
return None
def terminate_sessions(self, send_termination=False):
"""
Send termination messages and delete all active sessions
"""
for jid in self.sessions:
for thread_id in self.sessions[jid]:
self.sessions[jid][thread_id].terminate(send_termination)
def terminate_sessions(self):
self.sessions = {}
def delete_session(self, jid, thread_id):

View File

@ -2309,17 +2309,6 @@ class Oauth2CredentialsRequiredEvent(nec.NetworkIncomingEvent):
name = 'oauth2-credentials-required'
base_network_events = []
class FailedDecryptEvent(nec.NetworkIncomingEvent):
name = 'failed-decrypt'
base_network_events = []
def generate(self):
self.conn = self.msg_obj.conn
self.fjid = self.msg_obj.fjid
self.timestamp = self.msg_obj.timestamp
self.session = self.msg_obj.session
return True
class SignedInEvent(nec.NetworkIncomingEvent):
name = 'signed-in'
base_network_events = []

View File

@ -1,105 +0,0 @@
# common crypto functions (mostly specific to XEP-0116, but useful elsewhere)
# -*- coding:utf-8 -*-
## src/common/crypto.py
##
## Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import sys
import os
import math
from hashlib import sha256 as SHA256
# convert a large integer to a big-endian bitstring
def encode_mpi(n):
if n >= 256:
return encode_mpi(n // 256) + bytes([n % 256])
else:
return bytes([n])
# convert a large integer to a big-endian bitstring, padded with \x00s to
# a multiple of 16 bytes
def encode_mpi_with_padding(n):
return pad_to_multiple(encode_mpi(n), 16, '\x00', True)
# pad 'string' to a multiple of 'multiple_of' with 'char'.
# pad on the left if 'left', otherwise pad on the right.
def pad_to_multiple(string, multiple_of, char, left):
mod = len(string) % multiple_of
if mod == 0:
return string
else:
padding = (multiple_of - mod) * char
if left:
return padding + string
else:
return string + padding
# convert a big-endian bitstring to an integer
def decode_mpi(s):
if len(s) == 0:
return 0
else:
return 256 * decode_mpi(s[:-1]) + s[-1]
def sha256(string):
sh = SHA256()
sh.update(string)
return sh.digest()
base28_chr = "acdefghikmopqruvwxy123456789"
def sas_28x5(m_a, form_b):
sha = sha256(m_a + form_b + b'Short Authentication String')
lsb24 = decode_mpi(sha[-3:])
return base28(lsb24)
def base28(n):
if n >= 28:
return base28(n // 28) + base28_chr[n % 28]
else:
return base28_chr[n]
def random_bytes(bytes_):
return os.urandom(bytes_)
def generate_nonce():
return random_bytes(8)
# generate a random number between 'bottom' and 'top'
def srand(bottom, top):
# minimum number of bytes needed to represent that range
bytes = int(math.ceil(math.log(top - bottom, 256)))
# in retrospect, this is horribly inadequate.
return (decode_mpi(random_bytes(bytes)) % (top - bottom)) + bottom
# a faster version of (base ** exp) % mod
# taken from <http://lists.danga.com/pipermail/yadis/2005-September/001445.html>
def powmod(base, exp, mod):
square = base % mod
result = 1
while exp > 0:
if exp & 1: # exponent is odd
result = (result * square) % mod
square = (square * square) % mod
exp //= 2
return result

View File

@ -100,12 +100,6 @@ class NegotiationError(Exception):
"""
pass
class DecryptionError(Exception):
"""
A message couldn't be decrypted into usable XML
"""
pass
class Cancelled(Exception):
"""
The user cancelled an operation

View File

@ -1367,9 +1367,6 @@ def update_optional_features(account = None):
app.gajim_optional_features[a].append(nbxmpp.NS_CHATSTATES)
if not app.config.get('ignore_incoming_xhtml'):
app.gajim_optional_features[a].append(nbxmpp.NS_XHTML_IM)
if app.is_installed('PYCRYPTO') \
and app.config.get_per('accounts', a, 'enable_esessions'):
app.gajim_optional_features[a].append(nbxmpp.NS_ESESSION)
if app.config.get_per('accounts', a, 'answer_receipts'):
app.gajim_optional_features[a].append(nbxmpp.NS_RECEIPTS)
app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE)

File diff suppressed because it is too large Load Diff

View File

@ -69,10 +69,6 @@ class FeaturesWindow:
_('Ability to measure idle time, in order to set auto status.'),
_('Requires libxss library.'),
_('Requires python2.5.')),
_('End to End message encryption'): (self.pycrypto_available,
_('Encrypting chat messages.'),
_('Requires python-crypto.'),
_('Requires python-crypto.')),
_('RST Generator'): (self.docutils_available,
_('Generate XHTML output from RST code (see http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html).'),
_('Requires python-docutils.'),
@ -166,9 +162,6 @@ class FeaturesWindow:
from gajim.common import idle
return idle.Monitor.is_available()
def pycrypto_available(self):
return app.is_installed('PYCRYPTO')
def docutils_available(self):
try:
__import__('docutils')

View File

@ -200,13 +200,6 @@ class PrivateChatControl(ChatControl):
c = gc_c.as_contact()
self.gc_contact = gc_c
self.contact = c
if self.session:
# stop e2e
if self.session.enable_encryption:
thread_id = self.session.thread_id
self.session.terminate_e2e()
obj.conn.delete_session(obj.fjid, thread_id)
self.no_autonegotiation = False
self.draw_banner()
old_jid = obj.room_jid + '/' + obj.nick
new_jid = obj.room_jid + '/' + obj.new_nick
@ -282,17 +275,6 @@ class PrivateChatControl(ChatControl):
def update_contact(self):
self.contact = self.gc_contact.as_contact()
def begin_e2e_negotiation(self):
self.no_autonegotiation = True
if not self.session:
fjid = self.gc_contact.get_full_jid()
new_sess = app.connections[self.account].make_new_session(fjid,
type_=self.type_id)
self.set_session(new_sess)
self.session.negotiate_e2e(False)
def got_disconnected(self):
ChatControl.got_disconnected(self)
@ -1859,20 +1841,6 @@ class GroupchatControl(ChatControlBase):
self.nick = obj.new_nick
self.new_nick = ''
s = _('You are now known as %s') % self.nick
# Stop all E2E sessions
nick_list = app.contacts.get_nick_list(self.account,
self.room_jid)
for nick_ in nick_list:
fjid_ = self.room_jid + '/' + nick_
ctrl = app.interface.msg_win_mgr.get_control(
fjid_, self.account)
if ctrl and ctrl.session and \
ctrl.session.enable_encryption:
thread_id = ctrl.session.thread_id
ctrl.session.terminate_e2e()
app.connections[self.account].delete_session(
fjid_, thread_id)
ctrl.no_autonegotiation = False
else:
s = _('%(nick)s is now known as %(new_nick)s') % {
'nick': nick, 'new_nick': obj.new_nick}
@ -2303,13 +2271,6 @@ class GroupchatControl(ChatControlBase):
contact.status = ''
ctrl.update_ui()
ctrl.parent_win.redraw_tab(ctrl)
for sess in app.connections[self.account].get_sessions(fjid):
if sess.control:
sess.control.no_autonegotiation = False
if sess.enable_encryption:
sess.terminate_e2e()
app.connections[self.account].delete_session(fjid,
sess.thread_id)
# They can already be removed by the destroy function
if self.room_jid in app.contacts.get_gc_list(self.account):
app.contacts.remove_room(self.account, self.room_jid)

View File

@ -1184,12 +1184,6 @@ class Interface:
def handle_atom_entry(obj):
AtomWindow.newAtomEntry(obj.atom_entry)
@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)
def handle_event_zc_name_conflict(self, obj):
def on_ok(new_name):
app.config.set_per('accounts', obj.conn.name, 'name', new_name)
@ -1531,7 +1525,6 @@ class Interface:
'client-cert-passphrase': [
self.handle_event_client_cert_passphrase],
'connection-lost': [self.handle_event_connection_lost],
'failed-decrypt': [(self.handle_event_failed_decrypt, ged.GUI2)],
'file-request-error': [self.handle_event_file_request_error],
'file-request-received': [self.handle_event_file_request],
'gc-invitation-received': [self.handle_event_gc_invitation],

View File

@ -32,7 +32,6 @@ import uuid
from gajim.common import app
from gajim.common import helpers
from gajim.common import ged
from gajim.common.stanza_session import EncryptedStanzaSession, ArchivingStanzaSession
# Derived types MUST register their type IDs here if custom behavor is required
TYPE_CHAT = 'chat'
@ -200,19 +199,6 @@ class MessageControl(object):
if session and oldsession:
oldsession.control = None
crypto_changed = bool(session and isinstance(session,
EncryptedStanzaSession) and session.enable_encryption) != \
bool(oldsession and isinstance(oldsession, EncryptedStanzaSession) \
and oldsession.enable_encryption)
archiving_changed = bool(session and isinstance(session,
ArchivingStanzaSession) and session.archiving) != \
bool(oldsession and isinstance(oldsession,
ArchivingStanzaSession) and oldsession.archiving)
if crypto_changed or archiving_changed:
self.print_session_details(oldsession)
def remove_session(self, session):
if session != self.session:
return

View File

@ -1,128 +0,0 @@
# -*- coding:utf-8 -*-
## src/secrets.py
##
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
from gajim.common import configpaths
import Crypto
from gajim.common import crypto
from gajim.common import exceptions
import os
import pickle
secrets_filename = configpaths.get('SECRETS_FILE')
secrets_cache = None
class Secrets():
def __init__(self, filename):
self.filename = filename
self.srs = {}
self.pubkeys = {}
self.privkeys = {}
def cancel(self):
raise exceptions.Cancelled
def save(self):
f = open(secrets_filename, 'wb')
pickle.dump(self, f, protocol=2)
f.close()
def retained_secrets(self, account, bare_jid):
try:
return self.srs[account][bare_jid]
except KeyError:
return []
# retained secrets are stored as a tuple of the secret and whether the user
# has verified it
def save_new_srs(self, account, jid, secret, verified):
if not account in self.srs:
self.srs[account] = {}
if not jid in self.srs[account]:
self.srs[account][jid] = []
self.srs[account][jid].append((secret, verified))
self.save()
def find_srs(self, account, jid, srs):
our_secrets = self.srs[account][jid]
return [(x, y) for x, y in our_secrets if x == srs][0]
# has the user verified this retained secret?
def srs_verified(self, account, jid, srs):
return self.find_srs(account, jid, srs)[1]
def replace_srs(self, account, jid, old_secret, new_secret, verified):
our_secrets = self.srs[account][jid]
idx = our_secrets.index(self.find_srs(account, jid, old_secret))
our_secrets[idx] = (new_secret, verified)
self.save()
# the public key associated with 'account'
def my_pubkey(self, account):
try:
pk = self.privkeys[account]
except KeyError:
pk = Crypto.PublicKey.RSA.generate(2048, crypto.random_bytes)
self.privkeys[account] = pk
self.save()
return pk
def load_secrets(filename):
f = open(filename, 'rb')
try:
secrets = pickle.load(f, encoding='latin1')
# We do that to be able to read files written in py2
for acct in secrets.srs:
for jid in secrets.srs[acct]:
for (secret, verified) in list(secrets.srs[acct][jid]):
if type(secret) is str:
secrets.srs[acct][jid].remove((secret, verified))
secrets.srs[acct][jid].append((secret.encode('latin1'), verified))
except (KeyError, EOFError, ImportError):
f.close()
secrets = Secrets(filename)
f.close()
return secrets
def secrets():
global secrets_cache
if secrets_cache:
return secrets_cache
if os.path.exists(secrets_filename):
secrets_cache = load_secrets(secrets_filename)
else:
secrets_cache = Secrets(secrets_filename)
return secrets_cache

View File

@ -21,11 +21,13 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import string
import random
import itertools
from gajim.common import helpers
from gajim.common import events
from gajim.common import exceptions
from gajim.common import app
from gajim.common import stanza_session
from gajim.common import contacts
from gajim.common import ged
from gajim.common.connection_handlers_events import ChatstateReceivedEvent, \
@ -34,28 +36,48 @@ from gajim.common.const import KindConstant
from gajim import message_control
from gajim import notify
from gajim import dialogs
from gajim import negotiation
class ChatControlSession(stanza_session.EncryptedStanzaSession):
class ChatControlSession(object):
def __init__(self, conn, jid, thread_id, type_='chat'):
stanza_session.EncryptedStanzaSession.__init__(self, conn, jid, thread_id,
type_='chat')
app.ged.register_event_handler('decrypted-message-received', ged.PREGUI,
self._nec_decrypted_message_received)
self.conn = conn
self.jid = jid
self.type_ = type_
self.resource = jid.getResource()
self.control = None
def detach_from_control(self):
if self.control:
self.control.set_session(None)
if thread_id:
self.received_thread_id = True
self.thread_id = thread_id
else:
self.received_thread_id = False
if type_ == 'normal':
self.thread_id = None
else:
self.thread_id = self.generate_thread_id()
def acknowledge_termination(self):
self.detach_from_control()
stanza_session.EncryptedStanzaSession.acknowledge_termination(self)
self.loggable = True
def terminate(self, send_termination = True):
stanza_session.EncryptedStanzaSession.terminate(self, send_termination)
self.detach_from_control()
self.last_send = 0
self.last_receive = 0
app.ged.register_event_handler('decrypted-message-received',
ged.PREGUI,
self._nec_decrypted_message_received)
def generate_thread_id(self):
return ''.join(
[f(string.ascii_letters) for f in itertools.repeat(
random.choice, 32)]
)
def is_loggable(self):
return app.config.should_log(self.conn.name,
self.jid.getStripped())
def get_to(self):
to = str(self.jid)
return app.get_jid_without_resource(to) + '/' + self.resource
def _nec_decrypted_message_received(self, obj):
"""
@ -400,177 +422,3 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
else:
bb_jid, bb_account = jid, self.conn.name
app.interface.roster.select_contact(bb_jid, bb_account)
# ---- ESessions stuff ---
def handle_negotiation(self, form):
if form.getField('accept') and not form['accept'] in ('1', 'true'):
self.cancelled_negotiation()
return
# encrypted session states. these are described in stanza_session.py
try:
if form.getType() == 'form' and 'security' in form.asDict():
security_options = [x[1] for x in form.getField('security').\
getOptions()]
if security_options == ['none']:
self.respond_archiving(form)
else:
# bob responds
# we don't support 3-message negotiation as the responder
if 'dhkeys' in form.asDict():
self.fail_bad_negotiation('3 message negotiation not '
'supported when responding', ('dhkeys',))
return
negotiated, not_acceptable, ask_user = \
self.verify_options_bob(form)
if ask_user:
def accept_nondefault_options(is_checked):
self.dialog.destroy()
negotiated.update(ask_user)
self.respond_e2e_bob(form, negotiated,
not_acceptable)
def reject_nondefault_options():
self.dialog.destroy()
for key in ask_user.keys():
not_acceptable.append(key)
self.respond_e2e_bob(form, negotiated,
not_acceptable)
self.dialog = dialogs.YesNoDialog(_('Confirm these '
'session options'),
_('The remote client wants to negotiate a session '
'with these features:\n\n%s\n\nAre these options '
'acceptable?''') % (
negotiation.describe_features(ask_user)),
on_response_yes=accept_nondefault_options,
on_response_no=reject_nondefault_options,
transient_for=self.control.parent_win.window)
else:
self.respond_e2e_bob(form, negotiated, not_acceptable)
return
elif self.status == 'requested-archiving' and form.getType() == \
'submit':
try:
self.archiving_accepted(form)
except exceptions.NegotiationError as details:
self.fail_bad_negotiation(details)
return
# alice accepts
elif self.status == 'requested-e2e' and form.getType() == 'submit':
negotiated, not_acceptable, ask_user = self.verify_options_alice(
form)
if ask_user:
def accept_nondefault_options(is_checked):
if dialog:
dialog.destroy()
if is_checked:
allow_no_log_for = app.config.get_per(
'accounts', self.conn.name,
'allow_no_log_for').split()
jid = str(self.jid)
if jid not in allow_no_log_for:
allow_no_log_for.append(jid)
app.config.set_per('accounts', self.conn.name,
'allow_no_log_for', ' '.join(allow_no_log_for))
negotiated.update(ask_user)
try:
self.accept_e2e_alice(form, negotiated)
except exceptions.NegotiationError as details:
self.fail_bad_negotiation(details)
def reject_nondefault_options():
self.reject_negotiation()
dialog.destroy()
allow_no_log_for = app.config.get_per('accounts',
self.conn.name, 'allow_no_log_for').split()
if str(self.jid) in allow_no_log_for:
dialog = None
accept_nondefault_options(False)
else:
dialog = dialogs.YesNoDialog(_('Confirm these session '
'options'),
_('The remote client selected these options:\n\n%s'
'\n\nContinue with the session?') % (
negotiation.describe_features(ask_user)),
_('Always accept for this contact'),
on_response_yes = accept_nondefault_options,
on_response_no = reject_nondefault_options,
transient_for=self.control.parent_win.window)
else:
try:
self.accept_e2e_alice(form, negotiated)
except exceptions.NegotiationError as details:
self.fail_bad_negotiation(details)
return
elif self.status == 'responded-archiving' and form.getType() == \
'result':
try:
self.we_accept_archiving(form)
except exceptions.NegotiationError as details:
self.fail_bad_negotiation(details)
return
elif self.status == 'responded-e2e' and form.getType() == 'result':
try:
self.accept_e2e_bob(form)
except exceptions.NegotiationError as details:
self.fail_bad_negotiation(details)
return
elif self.status == 'identified-alice' and form.getType() == 'result':
try:
self.final_steps_alice(form)
except exceptions.NegotiationError as details:
self.fail_bad_negotiation(details)
return
except exceptions.Cancelled:
# user cancelled the negotiation
self.reject_negotiation()
return
if form.getField('terminate') and\
form.getField('terminate').getValue() in ('1', 'true'):
self.acknowledge_termination()
self.conn.delete_session(str(self.jid), self.thread_id)
return
# non-esession negotiation. this isn't very useful, but i'm keeping it
# around to test my test suite.
if form.getType() == 'form':
if not self.control:
jid, resource = app.get_room_and_nick_from_fjid(str(self.jid))
account = self.conn.name
contact = app.contacts.get_contact(account, str(self.jid),
resource)
if not contact:
contact = app.contacts.create_contact(jid=jid, account=account,
resource=resource, show=self.conn.get_status())
app.interface.new_chat(contact, account, resource=resource,
session=self)
negotiation.FeatureNegotiationWindow(account, str(self.jid), self,
form)

View File

@ -212,25 +212,6 @@
}
]
},
/* ESession support */
{
"name": "python3-pycrypto",
"buildsystem": "simple",
"build-commands": [
"python3 setup.py install --prefix=/app"
],
"sources": [
{
"type": "archive",
"url": "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz",
"sha256": "f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"
},
{
"type": "patch",
"path": "flatpak/CVE-2013-7459.patch"
}
]
},
/* gnupg support */
{
"name": "python3-python-gnupg",

View File

@ -57,8 +57,6 @@ configpaths.init()
configpaths.override_path('DATA', gajim_root + '/gajim/data')
from common.stanza_session import StanzaSession
# name to use for the test account
account_name = 'test'

View File

@ -16,7 +16,6 @@ from gajim.common.connection_handlers_events import MessageReceivedEvent
from gajim.common.connection_handlers_events import DecryptedMessageReceivedEvent
import nbxmpp
from gajim.common.stanza_session import StanzaSession
from gajim.session import ChatControlSession
from gajim.roster_window import RosterWindow
@ -30,59 +29,6 @@ app.interface = MockInterface()
# name to use for the test account
account_name = account1
class TestStanzaSession(unittest.TestCase):
''' Testclass for common/stanzasession.py '''
def setUp(self):
self.jid = nbxmpp.JID('test@example.org/Gajim')
self.conn = MockConnection(account_name, {'send_stanza': None})
self.sess = StanzaSession(self.conn, self.jid, None, 'chat')
def test_generate_thread_id(self):
# thread_id is a string
self.assertTrue(isinstance(self.sess.thread_id, str))
# it should be somewhat long, to avoid clashes
self.assertTrue(len(self.sess.thread_id) >= 32)
def test_is_loggable(self):
# by default a session should be loggable
# (unless the no_log_for setting says otherwise)
self.assertTrue(self.sess.is_loggable())
def test_terminate(self):
# termination is sent by default
self.sess.last_send = time.time()
self.sess.terminate()
self.assertEqual(None, self.sess.status)
calls = self.conn.mockGetNamedCalls('send_stanza')
msg = calls[0].getParam(0)
self.assertEqual(msg.getThread(), self.sess.thread_id)
def test_terminate_without_sending(self):
# no termination is sent if no messages have been sent in the session
self.sess.terminate()
self.assertEqual(None, self.sess.status)
calls = self.conn.mockGetNamedCalls('send_stanza')
self.assertEqual(0, len(calls))
def test_terminate_no_remote_xep_201(self):
# no termination is sent if only messages without thread ids have been
# received
self.sess.last_send = time.time()
self.sess.last_receive = time.time()
self.sess.terminate()
self.assertEqual(None, self.sess.status)
calls = self.conn.mockGetNamedCalls('send_stanza')
self.assertEqual(0, len(calls))
class TestChatControlSession(unittest.TestCase):
''' Testclass for session.py '''

View File

@ -91,7 +91,6 @@ function install_deps {
PIP_REQUIREMENTS="\
git+https://dev.gajim.org/gajim/python-nbxmpp.git
git+https://github.com/dlitz/pycrypto.git
git+https://dev.gajim.org/lovetox/pybonjour-python3.git
keyring
python-gnupg

View File

@ -23,7 +23,6 @@ certifi
git+https://dev.gajim.org/gajim/python-nbxmpp.git
git+https://dev.gajim.org/lovetox/pybonjour-python3.git
protobuf
git+https://github.com/dlitz/pycrypto.git
cryptography
pyopenssl
python-gnupg