Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim-plugins.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Hörist <forenjunkie@chello.at>2017-05-08 00:14:53 +0300
committerPhilipp Hörist <forenjunkie@chello.at>2017-05-08 00:14:53 +0300
commit7de642625c78625fae4e7eea244f2e4ea9d9f335 (patch)
tree25e5f08cb8248c99646b9d37e123ffb3c55f1a3a
parent3242de357468784087d9094691f78bf3c292a144 (diff)
parent97e6806c9ba77e08129cec27bd9f1522b8043a57 (diff)
Merge branch 'esessions' into 'gtk3'
ESessions Plugin version 1.0.0 See merge request !36
-rw-r--r--esessions/__init__.py1
-rw-r--r--esessions/esessions.py276
-rw-r--r--esessions/manifest.ini8
3 files changed, 285 insertions, 0 deletions
diff --git a/esessions/__init__.py b/esessions/__init__.py
new file mode 100644
index 0000000..98bc419
--- /dev/null
+++ b/esessions/__init__.py
@@ -0,0 +1 @@
+from .esessions import ESessionsPlugin
diff --git a/esessions/esessions.py b/esessions/esessions.py
new file mode 100644
index 0000000..9403a43
--- /dev/null
+++ b/esessions/esessions.py
@@ -0,0 +1,276 @@
+# -*- coding: utf-8 -*-
+
+'''
+Copyright 2017 Philipp Hörist <philipp@hoerist.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 os
+import logging
+import time
+import threading
+import queue
+
+import nbxmpp
+
+import dialogs
+import gtkgui_helpers
+
+from common import gajim
+from common.connection_handlers_events import (
+ FailedDecryptEvent, MamMessageReceivedEvent)
+from plugins import GajimPlugin
+
+log = logging.getLogger('gajim.plugin_system.esessions')
+
+ERROR_MSG = ''
+if not gajim.HAVE_PYCRYPTO:
+ ERROR_MSG = 'Please install pycrypto'
+
+
+class ESessionsPlugin(GajimPlugin):
+
+ def init(self):
+ """ Init """
+ if ERROR_MSG:
+ # self.activatable = False
+ self.available_text = ERROR_MSG
+ self.config_dialog = None
+ self.encryption_name = 'ESessions'
+ self.config_dialog = None
+ self.gui_extension_points = {
+ 'decrypt': (self._message_received, None),
+ 'encrypt' + self.encryption_name: (self._encrypt_message, None),
+ 'send_message' + self.encryption_name: (
+ self._before_sendmessage, None),
+ 'encryption_dialog' + self.encryption_name: (
+ self.encryption_dialog, None),
+ 'encryption_state' + self.encryption_name: (
+ self.encryption_state, None),
+ 'typing' + self.encryption_name: (
+ self.typing, None)}
+
+ def activate(self):
+ pass
+
+ def deactivate(self):
+ pass
+
+ @staticmethod
+ def activate_encryption(chat_control):
+ contact = chat_control.contact
+ useable = contact.supports(nbxmpp.NS_ESESSION)
+ if useable:
+ chat_control.activate_esessions()
+ return bool(useable)
+
+ @staticmethod
+ def encryption_dialog(chat_control):
+ ESessionInfoWindow(chat_control.session,
+ chat_control.parent_win.window)
+
+ @staticmethod
+ def encryption_state(chat_control, state):
+ session = chat_control.session
+ if not session:
+ return
+ authenticated = chat_control.session.verified_identity
+ state['authenticated'] = session and authenticated
+ state['visible'] = True
+
+ @staticmethod
+ def typing(chat_control):
+ contact = chat_control.contact
+ enabled = chat_control.session.enable_encryption
+ if contact.supports(nbxmpp.NS_ESESSION):
+ if not (chat_control.session and enabled):
+ if not chat_control.no_autonegotiation:
+ chat_control.activate_esessions()
+
+ @staticmethod
+ def _before_sendmessage(chat_control):
+ enabled = chat_control.session.enable_encryption
+ if not (chat_control.session and enabled):
+ dialogs.ErrorDialog(
+ _('No Session'),
+ _('You have no valid Session with that contact.'))
+ chat_control.sendmessage = False
+
+ def _message_received(self, conn, obj, callback):
+ if obj.encrypted:
+ # Another Plugin already decrypted the message
+ return
+ if isinstance(obj, MamMessageReceivedEvent):
+ # Esessions doesnt support MAM Messages
+ return
+ if obj.forwarded:
+ # Esessions cant decrypt Carbon Copys
+ return
+ if obj.stanza.getTag('feature', namespace=nbxmpp.NS_FEATURE):
+ if gajim.HAVE_PYCRYPTO:
+ feature = obj.stanza.getTag(name='feature',
+ namespace=nbxmpp.NS_FEATURE)
+ form = nbxmpp.DataForm(node=feature.getTag('x'))
+ if not form:
+ return
+
+ if not form.getField('FORM_TYPE'):
+ return
+
+ if form['FORM_TYPE'] == 'urn:xmpp:ssn':
+ obj.session.handle_negotiation(form)
+ else:
+ reply = obj.stanza.buildReply()
+ reply.setType('error')
+ reply.addChild(feature)
+ err = nbxmpp.ErrorNode('service-unavailable', typ='cancel')
+ reply.addChild(node=err)
+ conn.connection.send(reply)
+ return
+
+ if obj.stanza.getTag('init', namespace=nbxmpp.NS_ESESSION_INIT):
+ init = obj.stanza.getTag(name='init',
+ namespace=nbxmpp.NS_ESESSION_INIT)
+ form = nbxmpp.DataForm(node=init.getTag('x'))
+
+ obj.session.handle_negotiation(form)
+ return
+
+ encrypted = obj.stanza.getTag(
+ 'c', namespace=nbxmpp.NS_STANZA_CRYPTO)
+ if encrypted:
+ try:
+ obj.stanza = obj.session.decrypt_stanza(obj.stanza)
+ obj.msgtxt = obj.stanza.getBody()
+ obj.encrypted = 'ESessions'
+ callback(obj)
+ except Exception:
+ gajim.nec.push_incoming_event(
+ FailedDecryptEvent(None, conn=conn, msg_obj=obj))
+ return
+
+ def _encrypt_message(self, conn, obj, callback):
+ if obj.session.enable_encryption:
+ obj.msg_iq = obj.session.encrypt_stanza(obj.msg_iq)
+ obj.msg_iq.addChild(name='private', namespace=nbxmpp.NS_CARBONS)
+ obj.msg_iq.addChild(name='no-permanent-store',
+ namespace=nbxmpp.NS_MSG_HINTS)
+ obj.msg_iq.addChild(name='no-copy',
+ namespace=nbxmpp.NS_MSG_HINTS)
+ eme_node = nbxmpp.Node('encryption',
+ attrs={'xmlns': nbxmpp.NS_EME,
+ 'name': 'ESessions',
+ 'namespace': nbxmpp.NS_STANZA_CRYPTO})
+ obj.msg_iq.addChild(node=eme_node)
+ callback(obj)
+
+
+class ESessionInfoWindow:
+ """
+ Class for displaying information about a XEP-0116 encrypted session
+ """
+ def __init__(self, session, transient_for=None):
+ self.session = session
+
+ self.xml = gtkgui_helpers.get_gtk_builder('esession_info_window.ui')
+ self.xml.connect_signals(self)
+
+ self.security_image = self.xml.get_object('security_image')
+ self.verify_now_button = self.xml.get_object('verify_now_button')
+ self.button_label = self.xml.get_object('verification_status_label')
+ self.window = self.xml.get_object('esession_info_window')
+ self.update_info()
+ self.window.set_transient_for(transient_for)
+
+ self.window.show_all()
+
+ def get_sas(self):
+ sas = ''
+ if hasattr(self.session, 'sas'):
+ sas = self.session.sas
+ return sas
+
+ def update_info(self):
+
+ labeltext = _(
+ '''Your chat session with <b>%(jid)s</b> is encrypted.\n\n
+ This session's Short Authentication String is <b>%(sas)s</b>.
+ ''') % {'jid': self.session.jid, 'sas': self.get_sas()}
+
+ if self.session.verified_identity:
+ labeltext += '\n\n' + _(
+ '''You have already verified this contact's identity.''')
+ security_image = 'security-high'
+ if self.session.control:
+ self.session.control.set_lock_image()
+
+ verification_status = _('''Contact's identity verified''')
+ self.window.set_title(verification_status)
+ self.xml.get_object('verification_status_label').set_markup(
+ '<b><span size="x-large">%s</span></b>' % verification_status)
+
+ self.xml.get_object('dialog-action_area1').set_no_show_all(True)
+ self.button_label.set_text(_('Verify again…'))
+ else:
+ if self.session.control:
+ self.session.control.set_lock_image()
+ labeltext += '\n\n' + _(
+ '''To be certain that <b>only</b> the expected person can read
+ your messages or send you messages, you need to verify their
+ identity by clicking the button below.''')
+ security_image = 'security-low'
+
+ verification_status = _('''Contact's identity NOT verified''')
+ self.window.set_title(verification_status)
+ self.xml.get_object('verification_status_label').set_markup(
+ '<b><span size="x-large">%s</span></b>' % verification_status)
+
+ self.button_label.set_text(_('Verify…'))
+
+ path = gtkgui_helpers.get_icon_path(security_image, 32)
+ self.security_image.set_from_file(path)
+
+ self.xml.get_object('info_display').set_markup(labeltext)
+
+ def on_close_button_clicked(self, widget):
+ self.window.destroy()
+
+ def on_verify_now_button_clicked(self, widget):
+ pritext = _('''Have you verified the contact's identity?''')
+ sectext = _('''
+ To prevent talking to an unknown person, you should
+ speak to <b>%(jid)s</b> directly (in person or on the phone)
+ and verify that they see the same Short Authentication String (SAS)
+ as you.\n\n
+ This session's Short Authentication String is <b>%(sas)s</b>.
+ ''') % {'jid': self.session.jid, 'sas': self.get_sas()}
+ sectext += '\n\n' + _(
+ 'Did you talk to the remote contact and verify the SAS?')
+
+ def on_yes(checked):
+ self.session._verified_srs_cb()
+ self.session.verified_identity = True
+ self.update_info()
+
+ def on_no():
+ self.session._unverified_srs_cb()
+ self.session.verified_identity = False
+ self.update_info()
+
+ dialogs.YesNoDialog(pritext, sectext,
+ on_response_yes=on_yes,
+ on_response_no=on_no,
+ transient_for=self.window)
diff --git a/esessions/manifest.ini b/esessions/manifest.ini
new file mode 100644
index 0000000..ebcab11
--- /dev/null
+++ b/esessions/manifest.ini
@@ -0,0 +1,8 @@
+[info]
+name: ESessions
+short_name: esessions
+version: 1.0.0
+description: Encryption as per XEP-0200
+authors: Philipp Hörist <philipp@hoerist.com>
+homepage: https://dev.gajim.org/gajim/gajim-plugins/wikis/esessions
+min_gajim_version: 0.16.10