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:13:03 +0300
committerPhilipp Hörist <forenjunkie@chello.at>2017-05-08 00:13:03 +0300
commit3242de357468784087d9094691f78bf3c292a144 (patch)
tree6d70cc2237bf7236e59fa48b15bd75c0b44aa848
parent8b85e8a0ae33d967b0e0bee0a28ee5359e31376f (diff)
parenta319b1f96d14227dd53faa132ec27ae64c4c815f (diff)
Merge branch 'newplugins' into 'gtk3'
PGP Plugin version 1.0.0 See merge request !37
-rw-r--r--pgp/__init__.py1
-rw-r--r--pgp/manifest.ini8
-rw-r--r--pgp/pgpplugin.py291
3 files changed, 300 insertions, 0 deletions
diff --git a/pgp/__init__.py b/pgp/__init__.py
new file mode 100644
index 0000000..bd90642
--- /dev/null
+++ b/pgp/__init__.py
@@ -0,0 +1 @@
+from .pgpplugin import OldPGPPlugin
diff --git a/pgp/manifest.ini b/pgp/manifest.ini
new file mode 100644
index 0000000..70f6377
--- /dev/null
+++ b/pgp/manifest.ini
@@ -0,0 +1,8 @@
+[info]
+name: PGP
+short_name: PGP
+version: 1.0.0
+description: PGP encryption as per XEP-0027
+authors: Philipp Hörist <philipp@hoerist.com>
+homepage: https://dev.gajim.org/gajim/gajim-plugins/wikis/pgp
+min_gajim_version: 0.16.10
diff --git a/pgp/pgpplugin.py b/pgp/pgpplugin.py
new file mode 100644
index 0000000..4f03601
--- /dev/null
+++ b/pgp/pgpplugin.py
@@ -0,0 +1,291 @@
+# -*- 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
+from gi.repository import GLib
+
+import dialogs
+from common import gajim
+from common.connection_handlers_events import (
+ MessageNotSentEvent, MessageReceivedEvent)
+from plugins import GajimPlugin
+
+log = logging.getLogger('gajim.plugin_system.oldpgp')
+
+ERROR_MSG = ''
+if not gajim.HAVE_GPG:
+ ERROR_MSG = 'Please install python-gnupg'
+
+ALLOWED_TAGS = [('request', nbxmpp.NS_RECEIPTS),
+ ('active', nbxmpp.NS_CHATSTATES),
+ ('gone', nbxmpp.NS_CHATSTATES),
+ ('inactive', nbxmpp.NS_CHATSTATES),
+ ('paused', nbxmpp.NS_CHATSTATES),
+ ('composing', nbxmpp.NS_CHATSTATES),
+ ('no-store', nbxmpp.NS_MSG_HINTS),
+ ('store', nbxmpp.NS_MSG_HINTS),
+ ('no-copy', nbxmpp.NS_MSG_HINTS),
+ ('no-permanent-store', nbxmpp.NS_MSG_HINTS),
+ ('replace', nbxmpp.NS_CORRECT)]
+
+
+class OldPGPPlugin(GajimPlugin):
+
+ def init(self):
+ if ERROR_MSG:
+ self.activatable = False
+ self.available_text = ERROR_MSG
+ return
+ self.config_dialog = None
+ self.encryption_name = 'PGP'
+ self.config_dialog = None
+ self.gui_extension_points = {
+ 'encrypt' + self.encryption_name: (self._encrypt_message, None),
+ 'decrypt': (self._message_received, None),
+ 'send_message' + self.encryption_name: (
+ self._before_sendmessage, None),
+ 'encryption_dialog' + self.encryption_name: (
+ self.on_encryption_button_clicked, None),
+ 'encryption_state' + self.encryption_name: (
+ self.encryption_state, None)}
+
+ self.gpg_instances = {}
+ self.decrypt_queue = queue.Queue()
+ self.thread = None
+
+ def get_gpg(self, account):
+ return self.gpg_instances[account]
+
+ def activate(self):
+ for account in gajim.connections:
+ self.gpg_instances[account] = gajim.connections[account].gpg
+
+ def deactivate(self):
+ pass
+
+ @staticmethod
+ def activate_encryption(chat_control):
+ return True
+
+ @staticmethod
+ def encryption_state(chat_control, state):
+ key_id = chat_control.contact.keyID
+ account = chat_control.account
+ authenticated, _ = check_state(key_id, account)
+ state['visible'] = True
+ state['authenticated'] = authenticated
+
+ @staticmethod
+ def on_encryption_button_clicked(chat_control):
+ account = chat_control.account
+ key_id = chat_control.contact.keyID
+ transient = chat_control.parent_win.window
+ authenticated, info = check_state(key_id, account)
+ dialogs.InformationDialog(authenticated, info, transient)
+
+ @staticmethod
+ def _before_sendmessage(chat_control):
+ account = chat_control.account
+ if not chat_control.contact.keyID:
+ dialogs.ErrorDialog(
+ _('No OpenPGP key assigned'),
+ _('No OpenPGP key is assigned to this contact. So you cannot '
+ 'encrypt messages with OpenPGP.'))
+ chat_control.sendmessage = False
+ elif not gajim.config.get_per('accounts', account, 'keyid'):
+ dialogs.ErrorDialog(
+ _('No OpenPGP key assigned'),
+ _('No OpenPGP key is assigned to your account. So you cannot '
+ 'encrypt messages with OpenPGP.'))
+ chat_control.sendmessage = False
+
+ @staticmethod
+ def _get_info_message():
+ msg = '[This message is *encrypted* (See :XEP:`27`]'
+ lang = os.getenv('LANG')
+ if lang is not None and not lang.startswith('en'):
+ # we're not english: one in locale and one en
+ msg = _('[This message is *encrypted* (See :XEP:`27`]') + \
+ ' (' + msg + ')'
+ return msg
+
+ def _message_received(self, conn, obj, callback):
+ if obj.encrypted:
+ # Another Plugin already decrypted the message
+ return
+ account = conn.name
+ if isinstance(obj, MessageReceivedEvent):
+ enc_tag = obj.stanza.getTag('x', namespace=nbxmpp.NS_ENCRYPTED)
+ else:
+ enc_tag = obj.msg_.getTag('x', namespace=nbxmpp.NS_ENCRYPTED)
+ if enc_tag:
+ encmsg = enc_tag.getData()
+ key_id = gajim.config.get_per('accounts', account, 'keyid')
+ if key_id:
+ obj.encrypted = 'xep27'
+ self.decrypt_queue.put([encmsg, key_id, obj, conn, callback])
+ if not self.thread:
+ self.thread = threading.Thread(target=self.worker)
+ self.thread.start()
+ return
+
+ def worker(self):
+ while True:
+ try:
+ item = self.decrypt_queue.get(block=False)
+ encmsg, key_id, obj, conn, callback = item
+ account = conn.name
+ decmsg = self.get_gpg(account).decrypt(encmsg, key_id)
+ decmsg = conn.connection.Dispatcher. \
+ replace_non_character(decmsg)
+ # \x00 chars are not allowed in C (so in GTK)
+ msg = decmsg.replace('\x00', '')
+ obj.msgtxt = msg
+ GLib.idle_add(callback, obj)
+ except queue.Empty:
+ self.thread = None
+ break
+
+ def _encrypt_message(self, conn, obj, callback):
+ account = conn.name
+ if not obj.message:
+ # We only encrypt the actual message
+ self._finished_encrypt(obj, callback=callback)
+ return
+
+ if obj.keyID == 'UNKNOWN':
+ error = _('Neither the remote presence is signed, nor a key was '
+ 'assigned.')
+ elif obj.keyID.endswith('MISMATCH'):
+ error = _('The contact\'s key (%s) does not match the key assigned '
+ 'in Gajim.' % obj.keyID[:8])
+ else:
+ my_key_id = gajim.config.get_per('accounts', account, 'keyid')
+ key_list = [obj.keyID, my_key_id]
+
+ def _on_encrypted(output):
+ msgenc, error = output
+ if error.startswith('NOT_TRUSTED'):
+ def on_yes(checked):
+ if checked:
+ obj.conn.gpg.always_trust.append(obj.keyID)
+ gajim.thread_interface(
+ self.get_gpg(account).encrypt,
+ [obj.message, key_list, True],
+ _on_encrypted, [])
+
+ def on_no():
+ self._finished_encrypt(
+ obj, msgenc=msgenc, error=error, conn=conn)
+
+ dialogs.YesNoDialog(
+ _('Untrusted OpenPGP key'),
+ _('The OpenPGP key used to encrypt this chat is not '
+ 'trusted. Do you really want to encrypt this '
+ 'message?'),
+ checktext=_('_Do not ask me again'),
+ on_response_yes=on_yes,
+ on_response_no=on_no)
+ else:
+ self._finished_encrypt(
+ obj, msgenc=msgenc, error=error, conn=conn,
+ callback=callback)
+ gajim.thread_interface(
+ self.get_gpg(account).encrypt,
+ [obj.message, key_list, False],
+ _on_encrypted, [])
+ return
+ self._finished_encrypt(conn, obj, error=error)
+
+ def _finished_encrypt(self, obj, msgenc=None, error=None,
+ conn=None, callback=None):
+ if error:
+ log.error('python-gnupg error: %s', error)
+ gajim.nec.push_incoming_event(
+ MessageNotSentEvent(
+ None, conn=conn, jid=obj.jid, message=obj.message,
+ error=error, time_=time.time(), session=obj.session))
+ return
+ self.cleanup_stanza(obj)
+
+ if msgenc:
+ obj.msg_iq.setBody(self._get_info_message())
+ obj.msg_iq.setTag(
+ 'x', namespace=nbxmpp.NS_ENCRYPTED).setData(msgenc)
+ eme_node = nbxmpp.Node('encryption',
+ attrs={'xmlns': nbxmpp.NS_EME,
+ 'namespace': nbxmpp.NS_ENCRYPTED})
+ obj.msg_iq.addChild(node=eme_node)
+
+ # Set xhtml to None so it doesnt get logged
+ obj.xhtml = None
+ print_msg_to_log(obj.msg_iq)
+ callback(obj)
+
+ @staticmethod
+ def cleanup_stanza(obj):
+ ''' We make sure only allowed tags are in the stanza '''
+ stanza = nbxmpp.Message(
+ to=obj.msg_iq.getTo(),
+ typ=obj.msg_iq.getType())
+ stanza.setThread(obj.msg_iq.getThread())
+ for tag, ns in ALLOWED_TAGS:
+ node = obj.msg_iq.getTag(tag, namespace=ns)
+ if node:
+ stanza.addChild(node=node)
+ obj.msg_iq = stanza
+
+
+def print_msg_to_log(stanza):
+ """ Prints a stanza in a fancy way to the log """
+ stanzastr = '\n' + stanza.__str__(fancy=True) + '\n'
+ stanzastr = stanzastr[0:-1]
+ log.debug('\n' + '-'*15 + stanzastr + '-'*15)
+
+
+def check_state(key_id, account):
+ error = None
+ if key_id.endswith('MISMATCH'):
+ verification_status = _('''Contact's identity NOT verified''')
+ info = _('The contact\'s key (%s) <b>does not match</b> the key '
+ 'assigned in Gajim.') % key_id[:8]
+ elif not key_id:
+ # No key assigned nor a key is used by remote contact
+ verification_status = _('No OpenPGP key assigned')
+ info = _('No OpenPGP key is assigned to this contact. So you cannot'
+ ' encrypt messages.')
+ else:
+ error = gajim.connections[account].gpg.encrypt('test', [key_id])[1]
+ if error:
+ verification_status = _('''Contact's identity NOT verified''')
+ info = _('OpenPGP key is assigned to this contact, but <b>you '
+ 'do not trust their key</b>, so message <b>cannot</b> be '
+ 'encrypted. Use your OpenPGP client to trust their key.')
+ else:
+ verification_status = _('''Contact's identity verified''')
+ info = _('OpenPGP Key is assigned to this contact, and you '
+ 'trust their key, so messages will be encrypted.')
+ return (verification_status, info)