From 2b9780a9f92e1e77cd80c90cfb868afda04d9ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20H=C3=B6rist?= Date: Sat, 9 Feb 2019 20:38:20 +0100 Subject: [PATCH] [openpgp] Refactor Plugin - Adapt to nbxmpp now supporting openpgp --- openpgp/backend/gpgme.py | 12 +- openpgp/backend/pygpg.py | 17 +- openpgp/backend/sql.py | 36 ++- openpgp/gtk/key.py | 18 +- openpgp/gtk/wizard.py | 16 +- openpgp/modules/key_store.py | 257 ++++++++++++++++ openpgp/modules/openpgp.py | 518 ++++++++------------------------- openpgp/modules/pgp_keylist.py | 116 -------- openpgp/modules/util.py | 201 ++----------- openpgp/pgpplugin.py | 62 ++-- 10 files changed, 484 insertions(+), 769 deletions(-) create mode 100644 openpgp/modules/key_store.py delete mode 100644 openpgp/modules/pgp_keylist.py diff --git a/openpgp/backend/gpgme.py b/openpgp/backend/gpgme.py index 238d256..4058e27 100644 --- a/openpgp/backend/gpgme.py +++ b/openpgp/backend/gpgme.py @@ -1,20 +1,18 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 . - -# XEP-0373: OpenPGP for XMPP +# along with OpenPGP Gajim Plugin. If not, see . import io diff --git a/openpgp/backend/pygpg.py b/openpgp/backend/pygpg.py index 8868703..fa295aa 100644 --- a/openpgp/backend/pygpg.py +++ b/openpgp/backend/pygpg.py @@ -1,20 +1,18 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 . - -# XEP-0373: OpenPGP for XMPP +# along with OpenPGP Gajim Plugin. If not, see . import os import logging @@ -27,7 +25,6 @@ from gajim.common import app from openpgp.modules.util import DecryptionFailed log = logging.getLogger('gajim.plugin_system.openpgp.pygnupg') -# gnupg.logger = log KeyringItem = namedtuple('KeyringItem', 'jid keyid fingerprint') @@ -38,7 +35,7 @@ class PGPContext(gnupg.GPG): self, gpgbinary=app.get_gpg_binary(), gnupghome=str(gnupghome)) self._passphrase = 'gajimopenpgppassphrase' - self._jid = jid + self._jid = jid.getBare() self._own_fingerprint = None def _get_key_params(self, jid, passphrase): @@ -122,7 +119,7 @@ class PGPContext(gnupg.GPG): log.error(result.results[0]) return - if not self.validate_key(data, jid): + if not self.validate_key(data, str(jid)): return None key = self.get_key(result.results[0]['fingerprint']) return self._make_keyring_item(key[0]) diff --git a/openpgp/backend/sql.py b/openpgp/backend/sql.py index 562629e..9ebe9ce 100644 --- a/openpgp/backend/sql.py +++ b/openpgp/backend/sql.py @@ -1,30 +1,30 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 . - -# XEP-0373: OpenPGP for XMPP +# along with OpenPGP Gajim Plugin. If not, see . import sqlite3 import logging from collections import namedtuple +from nbxmpp import JID + log = logging.getLogger('gajim.plugin_system.openpgp.sql') TABLE_LAYOUT = ''' CREATE TABLE contacts ( - jid TEXT, + jid JID, fingerprint TEXT, active BOOLEAN, trust INTEGER, @@ -34,10 +34,25 @@ TABLE_LAYOUT = ''' CREATE UNIQUE INDEX jid_fingerprint ON contacts (jid, fingerprint);''' +def _jid_adapter(jid): + return str(jid) + + +def _jid_converter(jid): + return JID(jid.decode()) + + +sqlite3.register_adapter(JID, _jid_adapter) +sqlite3.register_converter('JID', _jid_converter) + + class Storage: def __init__(self, folder_path): self._con = sqlite3.connect(str(folder_path / 'contacts.db'), detect_types=sqlite3.PARSE_DECLTYPES) + + + self._con.row_factory = self._namedtuple_factory self._create_database() self._migrate_database() @@ -72,10 +87,7 @@ class Storage: pass def load_contacts(self): - sql = 'SELECT * from contacts' - rows = self._con.execute(sql).fetchall() - if rows is not None: - return rows + return self._con.execute('SELECT * from contacts').fetchall() def save_contact(self, db_values): sql = '''REPLACE INTO diff --git a/openpgp/gtk/key.py b/openpgp/gtk/key.py index 54d603b..be66e2c 100644 --- a/openpgp/gtk/key.py +++ b/openpgp/gtk/key.py @@ -1,20 +1,18 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 . - -# XEP-0373: OpenPGP for XMPP +# along with OpenPGP Gajim Plugin. If not, see . import logging import time @@ -24,7 +22,7 @@ from gi.repository import Gtk from gajim.common import app from gajim.common.const import DialogButton, ButtonAction -from gajim.gtk import NewConfirmationDialog +from gajim.gtk.dialogs import NewConfirmationDialog from gajim.plugins.plugins_i18n import _ from openpgp.modules.util import Trust @@ -49,8 +47,8 @@ TRUST_DATA = { class KeyDialog(Gtk.Dialog): def __init__(self, account, jid, transient): - flags = Gtk.DialogFlags.DESTROY_WITH_PARENT - super().__init__(_('Public Keys for %s') % jid, None, flags) + super().__init__(title=_('Public Keys for %s') % jid, + destroy_with_parent=True) self.set_transient_for(transient) self.set_resizable(True) diff --git a/openpgp/gtk/wizard.py b/openpgp/gtk/wizard.py index 9535713..0cff1c8 100644 --- a/openpgp/gtk/wizard.py +++ b/openpgp/gtk/wizard.py @@ -1,20 +1,18 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 . - -# XEP-0373: OpenPGP for XMPP +# along with OpenPGP Gajim Plugin. If not, see . import logging import threading @@ -177,8 +175,8 @@ class NewKeyPage(RequestPage): error = e else: self._con.get_module('OpenPGP').get_own_key_details() - self._con.get_module('OpenPGP').publish_key() - self._con.get_module('OpenPGP').query_key_list() + self._con.get_module('OpenPGP').set_public_key() + self._con.get_module('OpenPGP').request_keylist() GLib.idle_add(self.finished, error) def finished(self, error): diff --git a/openpgp/modules/key_store.py b/openpgp/modules/key_store.py new file mode 100644 index 0000000..0d3c640 --- /dev/null +++ b/openpgp/modules/key_store.py @@ -0,0 +1,257 @@ +# Copyright (C) 2019 Philipp Hörist +# +# This file is part of the OpenPGP Gajim Plugin. +# +# OpenPGP Gajim Plugin 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. +# +# OpenPGP Gajim Plugin 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 OpenPGP Gajim Plugin. If not, see . + +import logging + +from openpgp.modules.util import Trust + +log = logging.getLogger('gajim.plugin_system.openpgp.store') + + +class KeyData: + ''' + Holds all data related to a certain key + ''' + def __init__(self, contact_data): + self._contact_data = contact_data + self.fingerprint = None + self.active = False + self._trust = Trust.UNKNOWN + self.timestamp = None + self.comment = None + self.has_pubkey = False + + @property + def trust(self): + return self._trust + + @trust.setter + def trust(self, value): + if value not in (Trust.NOT_TRUSTED, + Trust.UNKNOWN, + Trust.BLIND, + Trust.VERIFIED): + raise ValueError('Trust value not allowed: %s' % value) + self._trust = value + self._contact_data.set_trust(self.fingerprint, self._trust) + + @classmethod + def from_key(cls, contact_data, key, trust): + keydata = cls(contact_data) + keydata.fingerprint = key.fingerprint + keydata.timestamp = key.date + keydata.active = True + keydata._trust = trust + return keydata + + @classmethod + def from_row(cls, contact_data, row): + keydata = cls(contact_data) + keydata.fingerprint = row.fingerprint + keydata.timestamp = row.timestamp + keydata.comment = row.comment + keydata._trust = row.trust + keydata.active = row.active + return keydata + + def delete(self): + self._contact_data.delete_key(self.fingerprint) + + +class ContactData: + ''' + Holds all data related to a contact + ''' + def __init__(self, jid, storage, pgp): + self.jid = jid + self._key_store = {} + self._storage = storage + self._pgp = pgp + + @property + def userid(self): + if self.jid is None: + raise ValueError('JID not set') + return 'xmpp:%s' % self.jid + + @property + def default_trust(self): + for key in self._key_store.values(): + if key.trust in (Trust.NOT_TRUSTED, Trust.BLIND): + return Trust.UNKNOWN + return Trust.BLIND + + def db_values(self): + for key in self._key_store.values(): + yield (self.jid, + key.fingerprint, + key.active, + key.trust, + key.timestamp, + key.comment) + + def add_from_key(self, key): + try: + keydata = self._key_store[key.fingerprint] + except KeyError: + keydata = KeyData.from_key(self, key, self.default_trust) + self._key_store[key.fingerprint] = keydata + log.info('Add from key: %s %s', self.jid, keydata.fingerprint) + return keydata + + def add_from_db(self, row): + try: + keydata = self._key_store[row.fingerprint] + except KeyError: + keydata = KeyData.from_row(self, row) + self._key_store[row.fingerprint] = keydata + log.info('Add from row: %s %s', self.jid, row.fingerprint) + return keydata + + def process_keylist(self, keylist): + log.info('Process keylist: %s %s', self.jid, keylist) + + if keylist is None: + for keydata in self._key_store.values(): + keydata.active = False + self._storage.save_contact(self.db_values()) + return [] + + missing_pub_keys = [] + fingerprints = set([key.fingerprint for key in keylist]) + if fingerprints == self._key_store.keys(): + log.info('No updates found') + for key in self._key_store.values(): + if not key.has_pubkey: + missing_pub_keys.append(key.fingerprint) + return missing_pub_keys + + for keydata in self._key_store.values(): + keydata.active = False + + for key in keylist: + try: + keydata = self._key_store[key.fingerprint] + keydata.active = True + if not keydata.has_pubkey: + missing_pub_keys.append(keydata.fingerprint) + except KeyError: + keydata = self.add_from_key(key) + missing_pub_keys.append(keydata.fingerprint) + + self._storage.save_contact(self.db_values()) + return missing_pub_keys + + def set_public_key(self, fingerprint): + try: + keydata = self._key_store[fingerprint] + except KeyError: + log.warning('Set public key on unknown fingerprint: %s %s', + self.jid, fingerprint) + else: + keydata.has_pubkey = True + log.info('Set public key: %s %s', self.jid, fingerprint) + + def get_keys(self, only_trusted=True): + keys = list(self._key_store.values()) + if not only_trusted: + return keys + return [k for k in keys if k.active and k.trust in (Trust.VERIFIED, + Trust.BLIND)] + + def get_key(self, fingerprint): + return self._key_store.get(fingerprint, None) + + def set_trust(self, fingerprint, trust): + self._storage.set_trust(self.jid, fingerprint, trust) + + def delete_key(self, fingerprint): + self._storage.delete_key(self.jid, fingerprint) + self._pgp.delete_key(fingerprint) + del self._key_store[fingerprint] + + +class PGPContacts: + ''' + Holds all contacts available for PGP encryption + ''' + def __init__(self, pgp, storage): + self._contacts = {} + self._storage = storage + self._pgp = pgp + self._load_from_storage() + self._load_from_keyring() + + def _load_from_keyring(self): + log.info('Load keys from keyring') + keyring = self._pgp.get_keys() + for key in keyring: + log.info('Found: %s %s', key.jid, key.fingerprint) + self.set_public_key(key.jid, key.fingerprint) + + def _load_from_storage(self): + log.info('Load contacts from storage') + rows = self._storage.load_contacts() + if rows is None: + return + + for row in rows: + log.info('Found: %s %s', row.jid, row.fingerprint) + try: + contact_data = self._contacts[row.jid] + except KeyError: + contact_data = ContactData(row.jid, self._storage, self._pgp) + contact_data.add_from_db(row) + self._contacts[row.jid] = contact_data + else: + contact_data.add_from_db(row) + + def process_keylist(self, jid, keylist): + try: + contact_data = self._contacts[jid] + except KeyError: + contact_data = ContactData(jid, self._storage, self._pgp) + missing_pub_keys = contact_data.process_keylist(keylist) + self._contacts[jid] = contact_data + else: + missing_pub_keys = contact_data.process_keylist(keylist) + + return missing_pub_keys + + def set_public_key(self, jid, fingerprint): + try: + contact_data = self._contacts[jid] + except KeyError: + log.warning('ContactData not found: %s %s', jid, fingerprint) + else: + contact_data.set_public_key(fingerprint) + + def get_keys(self, jid, only_trusted=True): + try: + contact_data = self._contacts[jid] + return contact_data.get_keys(only_trusted=only_trusted) + except KeyError: + return [] + + def get_trust(self, jid, fingerprint): + contact_data = self._contacts.get(jid, None) + if contact_data is None: + return Trust.UNKNOWN + + key = contact_data.get_key(fingerprint) + if key is None: + return Trust.UNKNOWN + return key.trust diff --git a/openpgp/modules/openpgp.py b/openpgp/modules/openpgp.py index 61f996e..a91da26 100644 --- a/openpgp/modules/openpgp.py +++ b/openpgp/modules/openpgp.py @@ -1,40 +1,47 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 +# 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 . - -# XEP-0373: OpenPGP for XMPP +# along with OpenPGP Gajim Plugin. If not, see . import time import logging from pathlib import Path -from base64 import b64decode, b64encode -from nbxmpp import Node, isResultNode +import nbxmpp +from nbxmpp import Node +from nbxmpp import StanzaMalformed +from nbxmpp.util import is_error_result +from nbxmpp.structs import StanzaHandler +from nbxmpp.modules.openpgp import PGPKeyMetadata +from nbxmpp.modules.openpgp import parse_signcrypt +from nbxmpp.modules.openpgp import create_signcrypt_node +from nbxmpp.modules.openpgp import create_message_stanza from gajim.common import app from gajim.common import configpaths from gajim.common.nec import NetworkEvent +from gajim.common.const import EncryptionData +from gajim.common.modules.base import BaseModule +from gajim.common.modules.util import event_node -from openpgp.modules import util from openpgp.modules.util import ENCRYPTION_NAME -from openpgp.modules.util import add_additional_data -from openpgp.modules.util import NS_OPENPGP_PUBLIC_KEYS -from openpgp.modules.util import NS_OPENPGP +from openpgp.modules.util import NOT_ENCRYPTED_TAGS from openpgp.modules.util import Key -from openpgp.modules.util import Trust +from openpgp.modules.util import add_additional_data from openpgp.modules.util import DecryptionFailed +from openpgp.modules.util import prepare_stanza +from openpgp.modules.key_store import PGPContacts from openpgp.backend.sql import Storage from openpgp.backend.pygpg import PGPContext @@ -47,252 +54,34 @@ name = ENCRYPTION_NAME zeroconf = False -class KeyData: - ''' - Holds all data related to a certain key - ''' - def __init__(self, contact_data): - self._contact_data = contact_data - self.fingerprint = None - self.active = False - self._trust = Trust.UNKNOWN - self.timestamp = None - self.comment = None - self.has_pubkey = False +class OpenPGP(BaseModule): - @property - def trust(self): - return self._trust + _nbxmpp_extends = 'OpenPGP' + _nbxmpp_methods = [ + 'set_keylist', + 'request_keylist', + 'set_public_key', + 'request_public_key', + 'set_secret_key', + 'request_secret_key', + ] - @trust.setter - def trust(self, value): - if value not in (Trust.NOT_TRUSTED, - Trust.UNKNOWN, - Trust.BLIND, - Trust.VERIFIED): - raise ValueError('Trust value not allowed: %s' % value) - self._trust = value - self._contact_data.set_trust(self.fingerprint, self._trust) - - @classmethod - def from_key(cls, contact_data, key, trust): - keydata = cls(contact_data) - keydata.fingerprint = key.fingerprint - keydata.timestamp = key.date - keydata.active = True - keydata._trust = trust - return keydata - - @classmethod - def from_row(cls, contact_data, row): - keydata = cls(contact_data) - keydata.fingerprint = row.fingerprint - keydata.timestamp = row.timestamp - keydata.comment = row.comment - keydata._trust = row.trust - keydata.active = row.active - return keydata - - def delete(self): - self._contact_data.delete_key(self.fingerprint) - - -class ContactData: - ''' - Holds all data related to a contact - ''' - def __init__(self, jid, storage, pgp): - self.jid = jid - self._key_store = {} - self._storage = storage - self._pgp = pgp - - @property - def userid(self): - if self.jid is None: - raise ValueError('JID not set') - return 'xmpp:%s' % self.jid - - @property - def default_trust(self): - for key in self._key_store.values(): - if key.trust in (Trust.NOT_TRUSTED, Trust.BLIND): - return Trust.UNKNOWN - return Trust.BLIND - - def db_values(self): - for key in self._key_store.values(): - yield (self.jid, - key.fingerprint, - key.active, - key.trust, - key.timestamp, - key.comment) - - def add_from_key(self, key): - try: - keydata = self._key_store[key.fingerprint] - except KeyError: - keydata = KeyData.from_key(self, key, self.default_trust) - self._key_store[key.fingerprint] = keydata - log.info('Add from key: %s %s', self.jid, keydata.fingerprint) - return keydata - - def add_from_db(self, row): - try: - keydata = self._key_store[row.fingerprint] - except KeyError: - keydata = KeyData.from_row(self, row) - self._key_store[row.fingerprint] = keydata - log.info('Add from row: %s %s', self.jid, row.fingerprint) - return keydata - - def process_keylist(self, keylist): - log.info('Process keylist: %s %s', self.jid, keylist) - - if keylist is None: - for keydata in self._key_store.values(): - keydata.active = False - self._storage.save_contact(self.db_values()) - return [] - - missing_pub_keys = [] - fingerprints = set([key.fingerprint for key in keylist]) - if fingerprints == self._key_store.keys(): - log.info('No updates found') - for key in self._key_store.values(): - if not key.has_pubkey: - missing_pub_keys.append(key.fingerprint) - return missing_pub_keys - - for keydata in self._key_store.values(): - keydata.active = False - - for key in keylist: - try: - keydata = self._key_store[key.fingerprint] - keydata.active = True - if not keydata.has_pubkey: - missing_pub_keys.append(keydata.fingerprint) - except KeyError: - keydata = self.add_from_key(key) - missing_pub_keys.append(keydata.fingerprint) - - self._storage.save_contact(self.db_values()) - return missing_pub_keys - - def set_public_key(self, fingerprint): - try: - keydata = self._key_store[fingerprint] - except KeyError: - log.warning('Set public key on unknown fingerprint: %s %s', - self.jid, fingerprint) - else: - keydata.has_pubkey = True - log.info('Set public key: %s %s', self.jid, fingerprint) - - def get_keys(self, only_trusted=True): - keys = list(self._key_store.values()) - if not only_trusted: - return keys - return [k for k in keys if k.active and k.trust in (Trust.VERIFIED, - Trust.BLIND)] - - def get_key(self, fingerprint): - return self._key_store.get(fingerprint, None) - - def set_trust(self, fingerprint, trust): - self._storage.set_trust(self.jid, fingerprint, trust) - - def delete_key(self, fingerprint): - self._storage.delete_key(self.jid, fingerprint) - self._pgp.delete_key(fingerprint) - del self._key_store[fingerprint] - - -class PGPContacts: - ''' - Holds all contacts available for PGP encryption - ''' - def __init__(self, pgp, storage): - self._contacts = {} - self._storage = storage - self._pgp = pgp - self._load_from_storage() - self._load_from_keyring() - - def _load_from_keyring(self): - log.info('Load keys from keyring') - keyring = self._pgp.get_keys() - for key in keyring: - log.info('Found: %s %s', key.jid, key.fingerprint) - self.set_public_key(key.jid, key.fingerprint) - - def _load_from_storage(self): - log.info('Load contacts from storage') - rows = self._storage.load_contacts() - if rows is None: - return - - for row in rows: - log.info('Found: %s %s', row.jid, row.fingerprint) - try: - contact_data = self._contacts[row.jid] - except KeyError: - contact_data = ContactData(row.jid, self._storage, self._pgp) - contact_data.add_from_db(row) - self._contacts[row.jid] = contact_data - else: - contact_data.add_from_db(row) - - def process_keylist(self, jid, keylist): - try: - contact_data = self._contacts[jid] - except KeyError: - contact_data = ContactData(jid, self._storage, self._pgp) - missing_pub_keys = contact_data.process_keylist(keylist) - self._contacts[jid] = contact_data - else: - missing_pub_keys = contact_data.process_keylist(keylist) - - return missing_pub_keys - - def set_public_key(self, jid, fingerprint): - try: - contact_data = self._contacts[jid] - except KeyError: - log.warning('ContactData not found: %s %s', jid, fingerprint) - else: - contact_data.set_public_key(fingerprint) - - def get_keys(self, jid, only_trusted=True): - try: - contact_data = self._contacts[jid] - return contact_data.get_keys(only_trusted=only_trusted) - except KeyError: - return [] - - def get_trust(self, jid, fingerprint): - contact_data = self._contacts.get(jid, None) - if contact_data is None: - return Trust.UNKNOWN - - key = contact_data.get_key(fingerprint) - if key is None: - return Trust.UNKNOWN - return key.trust - - -class OpenPGP: def __init__(self, con): - self._con = con - self._account = con.name + BaseModule.__init__(self, con) - self.handlers = [] + self.handlers = [ + StanzaHandler(name='message', + callback=self.decrypt_message, + ns=nbxmpp.NS_OPENPGP, + priority=9), + ] - self.own_jid = self.get_own_jid(stripped=True) + self._register_pubsub_handler(self._keylist_notification_received) - path = Path(configpaths.get('MY_DATA')) / 'openpgp' / self.own_jid + self.own_jid = self._con.get_own_jid() + + own_bare_jid = self.own_jid.getBare() + path = Path(configpaths.get('MY_DATA')) / 'openpgp' / own_bare_jid if not path.exists(): path.mkdir(parents=True) @@ -306,11 +95,6 @@ class OpenPGP: def secret_key_available(self): return self._fingerprint is not None - def get_own_jid(self, stripped=False): - if stripped: - return self._con.get_own_jid().getStripped() - return self._con.get_own_jid() - def get_own_key_details(self): self._fingerprint, self._date = self._pgp.get_own_key_details() return self._fingerprint, self._date @@ -318,105 +102,77 @@ class OpenPGP: def generate_key(self): self._pgp.generate_key() - def publish_key(self): - log.info('%s => Publish key', self._account) + def set_public_key(self): + log.info('%s => Publish public key', self._account) key = self._pgp.export_key(self._fingerprint) + self._nbxmpp('OpenPGP').set_public_key( + key, self._fingerprint, self._date) - date = time.strftime( - '%Y-%m-%dT%H:%M:%SZ', time.gmtime(self._date)) - pubkey_node = Node('pubkey', attrs={'xmlns': NS_OPENPGP, - 'date': date}) - data = pubkey_node.addChild('data') - data.addData(b64encode(key).decode('utf8')) - node = '%s:%s' % (NS_OPENPGP_PUBLIC_KEYS, self._fingerprint) - - self._con.get_module('PubSub').send_pb_publish( - self.own_jid, node, pubkey_node, - id_='current', cb=self._public_result) - - def _publish_key_list(self, keylist=None): - if keylist is None: - keylist = [Key(self._fingerprint, self._date)] - log.info('%s => Publish keys list', self._account) - self._con.get_module('PGPKeylist').send(keylist) - - def _public_result(self, con, stanza): - if not isResultNode(stanza): - log.error('%s => Publishing failed: %s', - self._account, stanza.getError()) - - def _query_public_key(self, jid, fingerprint): - log.info('%s => Fetch public key %s - %s', + def request_public_key(self, jid, fingerprint): + log.info('%s => Request public key %s - %s', self._account, fingerprint, jid) - node = '%s:%s' % (NS_OPENPGP_PUBLIC_KEYS, fingerprint) - self._con.get_module('PubSub').send_pb_retrieve( - jid, node, cb=self._public_key_received, fingerprint=fingerprint) + self._nbxmpp('OpenPGP').request_public_key( + jid, + fingerprint, + callback=self._public_key_received, + user_data=fingerprint) - def _public_key_received(self, con, stanza, fingerprint): - if not isResultNode(stanza): + def _public_key_received(self, result, fingerprint): + if is_error_result(result): log.error('%s => Public Key not found: %s', - self._account, stanza.getError()) - return - pubkey = util.unpack_public_key(stanza, fingerprint) - if pubkey is None: - log.warning('Invalid public key received:\n%s', stanza) + self._account, result) return - jid = stanza.getFrom().getStripped() - result = self._pgp.import_key(pubkey, jid) - if result is not None: - self._contacts.set_public_key(jid, fingerprint) + imported_key = self._pgp.import_key(result.key, result.jid) + if imported_key is not None: + self._contacts.set_public_key(result.jid, fingerprint) - def query_key_list(self, jid=None): + def set_keylist(self, keylist=None): + if keylist is None: + keylist = [PGPKeyMetadata(None, self._fingerprint, self._date)] + log.info('%s => Publish keylist', self._account) + self._nbxmpp('OpenPGP').set_keylist(keylist) + + @event_node(nbxmpp.NS_OPENPGP_PK) + def _keylist_notification_received(self, _con, _stanza, properties): + keylist = [] + if not properties.pubsub_event.empty: + keylist = properties.pubsub_event.data + + self._process_keylist(keylist, properties.jid) + + def request_keylist(self, jid=None): if jid is None: jid = self.own_jid - log.info('%s => Fetch keys list %s', self._account, jid) - self._con.get_module('PubSub').send_pb_retrieve( - jid, NS_OPENPGP_PUBLIC_KEYS, - cb=self._query_key_list_result) + log.info('%s => Fetch keylist %s', self._account, jid) - def _query_key_list_result(self, con, stanza): - from_jid = stanza.getFrom() - if from_jid is None: - from_jid = self.own_jid - else: - from_jid = from_jid.getStripped() + self._nbxmpp('OpenPGP').request_keylist( + jid, + callback=self._keylist_received, + user_data=jid) - if not isResultNode(stanza): - log.error('%s => Keys list query failed: %s', - self._account, stanza.getError()) - if from_jid == self.own_jid and self._fingerprint is not None: - self._publish_key_list() + def _keylist_received(self, result, jid): + if is_error_result(result): + log.error('%s => Keylist query failed: %s', + self._account, result) + if self.own_jid.bareMatch(jid) and self._fingerprint is not None: + self.set_keylist() return - from_jid = stanza.getFrom() - if from_jid is None: - from_jid = self.own_jid - else: - from_jid = from_jid.getStripped() - - log.info('Key list query received from %s', from_jid) - - keylist = util.unpack_public_key_list(stanza, from_jid) - self.key_list_received(keylist, from_jid) - - def key_list_received(self, keylist, from_jid): - if keylist is None: - log.warning('Invalid keys list received') - if from_jid == self.own_jid and self._fingerprint is not None: - self._publish_key_list() - return + log.info('Keylist received from %s', jid) + self._process_keylist(result, jid) + def _process_keylist(self, keylist, from_jid): if not keylist: - log.warning('%s => Empty keys list received from %s', + log.warning('%s => Empty keylist received from %s', self._account, from_jid) self._contacts.process_keylist(self.own_jid, keylist) - if from_jid == self.own_jid and self._fingerprint is not None: - self._publish_key_list() + if self.own_jid.bareMatch(from_jid) and self._fingerprint is not None: + self.set_keylist() return - if from_jid == self.own_jid: - log.info('Received own keys list') + if self.own_jid.bareMatch(from_jid): + log.info('Received own keylist') for key in keylist: log.info(key.fingerprint) for key in keylist: @@ -427,7 +183,7 @@ class OpenPGP: log.info('Own key not published') if self._fingerprint is not None: keylist.append(Key(self._fingerprint, self._date)) - self._publish_key_list(keylist) + self.set_keylist(keylist) return missing_pub_keys = self._contacts.process_keylist(from_jid, keylist) @@ -436,83 +192,48 @@ class OpenPGP: log.info(key.fingerprint) for fingerprint in missing_pub_keys: - self._query_public_key(from_jid, fingerprint) + self.request_public_key(from_jid, fingerprint) - def decrypt_message(self, obj, callback): - if obj.encrypted: - # Another Plugin already decrypted the message + def decrypt_message(self, _con, stanza, properties): + if not properties.is_openpgp: return - if obj.name == 'message-received': - enc_tag = obj.stanza.getTag('openpgp', namespace=NS_OPENPGP) - jid = obj.jid - else: - enc_tag = obj.message.getTag('openpgp', namespace=NS_OPENPGP) - jid = obj.with_ - - if enc_tag is None: - return - - log.info('Received OpenPGP message from: %s', jid) - b64encode_payload = enc_tag.getData() - encrypted_payload = b64decode(b64encode_payload) - try: - decrypted_payload, fingerprint = self._pgp.decrypt( - encrypted_payload) + payload, fingerprint = self._pgp.decrypt(properties.openpgp) except DecryptionFailed as error: log.warning(error) return - signcrypt = Node(node=decrypted_payload) + signcrypt = Node(node=payload) - signcrypt_jid = signcrypt.getTagAttr('to', 'jid') - if self.own_jid != signcrypt_jid: - log.warning('signcrypt "to" attr %s != %s', - self.own_jid, signcrypt_jid) - log.debug(signcrypt) + try: + payload, to, timestamp = parse_signcrypt(signcrypt) + except StanzaMalformed as error: + log.warning('Decryption failed: %s', error) + log.warning(payload) return - payload = signcrypt.getTag('payload') + if not self.own_jid.bareMatch(to): + log.warning('to attr not valid') + log.warning(payload) + return - body = None - if obj.name == 'message-received': - obj.stanza.delChild(enc_tag) - for node in payload.getChildren(): - if node.name == 'body': - body = node.getData() - obj.stanza.setTagData('body', body) - else: - obj.stanza.addChild(node=node) - else: - obj.msg_.delChild(enc_tag) - for node in payload.getChildren(): - if node.name == 'body': - body = node.getData() - obj.msg_.setTagData('body', node.getData()) - else: - obj.msg_.addChild(node=node) + log.info('Received OpenPGP message from: %s', properties.jid) + prepare_stanza(stanza, payload) - if body: - obj.msgtxt = body - - add_additional_data(obj.additional_data, - fingerprint) - - obj.encrypted = ENCRYPTION_NAME - callback(obj) + properties.encrypted = EncryptionData({'name': ENCRYPTION_NAME, + 'fingerprint': fingerprint}) def encrypt_message(self, obj, callback): keys = self._contacts.get_keys(obj.jid) if not keys: - # TODO: this should never happen in theory log.error('Droping stanza to %s, because we have no key', obj.jid) return keys += self._contacts.get_keys(self.own_jid) keys += [Key(self._fingerprint, None)] - payload = util.create_signcrypt_node(obj) + payload = create_signcrypt_node(obj.msg_iq, NOT_ENCRYPTED_TAGS) encrypted_payload, error = self._pgp.encrypt(payload, keys) if error: @@ -523,16 +244,15 @@ class OpenPGP: jid=obj.jid, message=obj.message, error=error, - time_=time.time())) + time_=time.time(), + session=None)) return - util.create_openpgp_message(obj, encrypted_payload) - + create_message_stanza(obj.msg_iq, encrypted_payload, bool(obj.message)) add_additional_data(obj.additional_data, self._fingerprint) obj.encrypted = ENCRYPTION_NAME - self.print_msg_to_log(obj.msg_iq) callback(obj) @staticmethod @@ -550,7 +270,7 @@ class OpenPGP: return self._contacts.get_keys(jid, only_trusted=only_trusted) def clear_fingerprints(self): - self._publish_key_list() + self.set_keylist() def cleanup(self): self._storage.cleanup() diff --git a/openpgp/modules/pgp_keylist.py b/openpgp/modules/pgp_keylist.py deleted file mode 100644 index cbd9619..0000000 --- a/openpgp/modules/pgp_keylist.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) 2018 Philipp Hörist -# -# 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 . - -# XEP-0373: OpenPGP for XMPP - -import logging -import time - -import nbxmpp - -from gajim.common import app -from gajim.common.exceptions import StanzaMalformed -from gajim.common.modules.pep import AbstractPEPModule, AbstractPEPData -from gajim.common.modules.date_and_time import parse_datetime - -from openpgp.modules import util -from openpgp.modules.util import Key - -log = logging.getLogger('gajim.plugin_system.openpgp.pep') - -# Module name -name = 'PGPKeylist' -zeroconf = False - - -class PGPKeylistData(AbstractPEPData): - - type_ = 'openpgp-keylist' - - -class PGPKeylist(AbstractPEPModule): - ''' - - - - - - - ''' - - name = 'openpgp-keylist' - namespace = util.NS_OPENPGP_PUBLIC_KEYS - pep_class = PGPKeylistData - store_publish = True - _log = log - - def _extract_info(self, item): - keylist_tag = item.getTag('public-keys-list', - namespace=util.NS_OPENPGP) - if keylist_tag is None: - raise StanzaMalformed('No public-keys-list node') - - metadata = keylist_tag.getTags('pubkey-metadata') - if not metadata: - raise StanzaMalformed('No metadata found') - - keylist = [] - for data in metadata: - attrs = data.getAttrs() - - if not attrs or 'v4-fingerprint' not in attrs: - raise StanzaMalformed('No fingerprint in metadata') - - date = attrs.get('date', None) - if date is None: - raise StanzaMalformed('No date in metadata') - else: - timestamp = parse_datetime(date, epoch=True) - if timestamp is None: - raise StanzaMalformed('Invalid date timestamp: %s' % date) - - keylist.append(Key(attrs['v4-fingerprint'], int(timestamp))) - - return keylist - - def _notification_received(self, jid, keylist): - con = app.connections[self._account] - con.get_module('OpenPGP').key_list_received(keylist.data, - jid.getStripped()) - - def _build_node(self, keylist): - keylist_node = nbxmpp.Node('public-keys-list', - {'xmlns': util.NS_OPENPGP}) - if keylist is None: - return keylist_node - for key in keylist: - attrs = {'v4-fingerprint': key.fingerprint} - if key.date is not None: - date = time.strftime( - '%Y-%m-%dT%H:%M:%SZ', time.gmtime(key.date)) - attrs['date'] = date - keylist_node.addChild('pubkey-metadata', attrs=attrs) - return keylist_node - - -def get_instance(*args, **kwargs): - return PGPKeylist(*args, **kwargs), 'PGPKeylist' diff --git a/openpgp/modules/util.py b/openpgp/modules/util.py index 7934819..6c759d9 100644 --- a/openpgp/modules/util.py +++ b/openpgp/modules/util.py @@ -1,49 +1,38 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 . +# along with OpenPGP Gajim Plugin. If not, see . -# XEP-0373: OpenPGP for XMPP - -import logging -import random -import time -import string from enum import IntEnum from collections import namedtuple -from base64 import b64decode, b64encode import nbxmpp -from nbxmpp import Node -from gajim.common.modules.date_and_time import parse_datetime ENCRYPTION_NAME = 'OpenPGP' -NS_OPENPGP = 'urn:xmpp:openpgp:0' -NS_OPENPGP_PUBLIC_KEYS = 'urn:xmpp:openpgp:0:public-keys' -NS_NOTIFY = NS_OPENPGP_PUBLIC_KEYS + '+notify' -NOT_ENCRYPTED_TAGS = [('no-store', nbxmpp.NS_MSG_HINTS), - ('store', nbxmpp.NS_MSG_HINTS), - ('no-copy', nbxmpp.NS_MSG_HINTS), - ('no-permanent-store', nbxmpp.NS_MSG_HINTS), - ('thread', None)] +NOT_ENCRYPTED_TAGS = [ + ('no-store', nbxmpp.NS_MSG_HINTS), + ('store', nbxmpp.NS_MSG_HINTS), + ('no-copy', nbxmpp.NS_MSG_HINTS), + ('no-permanent-store', nbxmpp.NS_MSG_HINTS), + ('origin-id', nbxmpp.NS_SID), + ('thread', None) +] Key = namedtuple('Key', 'fingerprint date') -log = logging.getLogger('gajim.plugin_system.openpgp.util') - class Trust(IntEnum): NOT_TRUSTED = 0 @@ -52,159 +41,25 @@ class Trust(IntEnum): VERIFIED = 3 -def unpack_public_key_list(stanza, from_jid): - fingerprints = [] +def prepare_stanza(stanza, payload): + delete_nodes(stanza, 'openpgp', nbxmpp.NS_OPENPGP) + delete_nodes(stanza, 'body') - parent = stanza.getTag('pubsub', namespace=nbxmpp.NS_PUBSUB) - if parent is None: - parent = stanza.getTag('event', namespace=nbxmpp.NS_PUBSUB_EVENT) - if parent is None: - log.warning('PGP keys list has no pubsub/event node') - return + nodes = [(node.getName(), node.getNamespace()) for node in payload] + for name, namespace in nodes: + delete_nodes(stanza, name, namespace) - items = parent.getTag('items', attrs={'node': NS_OPENPGP_PUBLIC_KEYS}) - if items is None: - log.warning('PGP keys list has no items node') - return - - item = items.getTags('item') - if not item: - log.warning('PGP keys list has no item node') - return - - if len(item) > 1: - log.warning('PGP keys list has more than one item') - return - - key_list = item[0].getTag('public-keys-list', namespace=NS_OPENPGP) - if key_list is None: - log.warning('PGP keys list has no public-keys-list node') - return - - metadata = key_list.getTags('pubkey-metadata') - if not metadata: - return [] - - for node in metadata: - attrs = node.getAttrs() - if 'v4-fingerprint' not in attrs: - log.warning('No fingerprint in metadata node') - return - - date = attrs.get('date', None) - if date is None: - log.warning('No date in metadata') - return - - timestamp = parse_datetime(date, epoch=True) - if timestamp is None: - log.warning('Invalid date timestamp: %s', date) - return - - fingerprints.append( - Key(attrs['v4-fingerprint'], int(timestamp))) - - return fingerprints + for node in payload: + stanza.addChild(node=node) -def unpack_public_key(stanza, fingerprint): - pubsub = stanza.getTag('pubsub', namespace=nbxmpp.NS_PUBSUB) - if pubsub is None: - log.warning('PGP public key has no pubsub node') - return - node = '%s:%s' % (NS_OPENPGP_PUBLIC_KEYS, fingerprint) - items = pubsub.getTag('items', attrs={'node': node}) - if items is None: - log.warning('PGP public key has no items node') - return - - item = items.getTags('item') - if not item: - log.warning('PGP public key has no item node') - return - - if len(item) > 1: - log.warning('PGP public key has more than one item') - return - - pub_key = item[0].getTag('pubkey', namespace=NS_OPENPGP) - if pub_key is None: - log.warning('PGP public key has no pubkey node') - return - - data = pub_key.getTag('data') - if data is None: - log.warning('PGP public key has no data node') - return - - return b64decode(data.getData().encode('utf8')) - - -def create_signcrypt_node(obj): - ''' - - - - ''' - - encrypted_nodes = [] - child_nodes = obj.msg_iq.getChildren() - for node in child_nodes: - if (node.name, node.namespace) not in NOT_ENCRYPTED_TAGS: - if not node.namespace: - node.setNamespace(nbxmpp.NS_CLIENT) - encrypted_nodes.append(node) - obj.msg_iq.delChild(node) - - signcrypt = Node('signcrypt', attrs={'xmlns': NS_OPENPGP}) - signcrypt.addChild('to', attrs={'jid': obj.jid}) - - timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) - signcrypt.addChild('time', attrs={'stamp': timestamp}) - - signcrypt.addChild('rpad').addData(get_rpad()) - - payload = signcrypt.addChild('payload') - - for node in encrypted_nodes: - payload.addChild(node=node) - - return signcrypt - - -def get_rpad(): - rpad_range = random.randint(30, 50) - return ''.join( - random.choice(string.ascii_letters) for _ in range(rpad_range)) - - -def create_openpgp_message(obj, encrypted_payload): - b64encoded_payload = b64encode(encrypted_payload).decode('utf8') - - openpgp_node = nbxmpp.Node('openpgp', attrs={'xmlns': NS_OPENPGP}) - openpgp_node.addData(b64encoded_payload) - obj.msg_iq.addChild(node=openpgp_node) - - eme_node = nbxmpp.Node('encryption', - attrs={'xmlns': nbxmpp.NS_EME, - 'namespace': NS_OPENPGP}) - obj.msg_iq.addChild(node=eme_node) - - if obj.message: - obj.msg_iq.setBody(get_info_message()) - - -def get_info_message(): - return '[This message is *encrypted* with OpenPGP (See :XEP:`0373`]' +def delete_nodes(stanza, name, namespace=None): + attrs = None + if namespace is not None: + attrs = {'xmlns': nbxmpp.NS_OPENPGP} + nodes = stanza.getTags(name, attrs) + for node in nodes: + stanza.delChild(node) def add_additional_data(data, fingerprint): diff --git a/openpgp/pgpplugin.py b/openpgp/pgpplugin.py index 17207eb..c2e21d5 100644 --- a/openpgp/pgpplugin.py +++ b/openpgp/pgpplugin.py @@ -1,20 +1,18 @@ -# Copyright (C) 2018 Philipp Hörist +# Copyright (C) 2019 Philipp Hörist # -# This file is part of Gajim. +# This file is part of the OpenPGP Gajim Plugin. # -# Gajim is free software; you can redistribute it and/or modify +# OpenPGP Gajim Plugin 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, +# OpenPGP Gajim Plugin 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 . - -# XEP-0373: OpenPGP for XMPP +# along with OpenPGP Gajim Plugin. If not, see . import logging import os @@ -22,19 +20,21 @@ from pathlib import Path from gi.repository import Gtk from gi.repository import Gdk +import nbxmpp +from nbxmpp import JID -from gajim.plugins import GajimPlugin from gajim.common import app from gajim.common import ged from gajim.common import configpaths from gajim.common import helpers from gajim.common.const import CSSPriority + from gajim.gtk.dialogs import ErrorDialog + +from gajim.plugins import GajimPlugin from gajim.plugins.plugins_i18n import _ -from openpgp.modules.util import NS_NOTIFY from openpgp.modules.util import ENCRYPTION_NAME -from openpgp.modules import pgp_keylist try: from openpgp.modules import openpgp except ImportError as e: @@ -45,8 +45,6 @@ else: log = logging.getLogger('gajim.plugin_system.openpgp') -#TODO: we cant encrypt "thread" right now, because its needed for Gajim to find ChatControls. - class OpenPGPPlugin(GajimPlugin): def init(self): if ERROR_MSG: @@ -59,14 +57,12 @@ class OpenPGPPlugin(GajimPlugin): 'signed-in': (ged.PRECORE, self.signed_in), } - self.modules = [pgp_keylist, - openpgp] + self.modules = [openpgp] self.encryption_name = ENCRYPTION_NAME self.config_dialog = None self.gui_extension_points = { 'encrypt' + self.encryption_name: (self._encrypt_message, None), - 'decrypt': (self._decrypt_message, None), 'send_message' + self.encryption_name: ( self._before_sendmessage, None), 'encryption_dialog' + self.encryption_name: ( @@ -114,8 +110,8 @@ class OpenPGPPlugin(GajimPlugin): if con.get_module('OpenPGP').secret_key_available: log.info('%s => Publish keylist and public key after sign in', account) - con.get_module('OpenPGP').query_key_list() - con.get_module('OpenPGP').publish_key() + con.get_module('OpenPGP').request_keylist() + con.get_module('OpenPGP').set_public_key() def activate(self): for account in app.connections: @@ -128,16 +124,17 @@ class OpenPGPPlugin(GajimPlugin): if con.get_module('OpenPGP').secret_key_available: log.info('%s => Publish keylist and public key ' 'after plugin activation', account) - con.get_module('OpenPGP').query_key_list() - con.get_module('OpenPGP').publish_key() + con.get_module('OpenPGP').request_keylist() + con.get_module('OpenPGP').set_public_key() def deactivate(self): pass @staticmethod def _update_caps(account): - if NS_NOTIFY not in app.gajim_optional_features[account]: - app.gajim_optional_features[account].append(NS_NOTIFY) + namespace = nbxmpp.NS_OPENPGP_PK + '+notify' + if namespace not in app.gajim_optional_features[account]: + app.gajim_optional_features[account].append(namespace) def activate_encryption(self, chat_control): account = chat_control.account @@ -147,21 +144,24 @@ class OpenPGPPlugin(GajimPlugin): keys = app.connections[account].get_module('OpenPGP').get_keys( jid, only_trusted=False) if not keys: - con.get_module('OpenPGP').query_key_list(jid) + con.get_module('OpenPGP').request_keylist(JID(jid)) ErrorDialog( _('No OpenPGP key'), _('We didnt receive a OpenPGP key from this contact.')) return return True - else: - from openpgp.gtk.wizard import KeyWizard - KeyWizard(self, account, chat_control) - def encryption_state(self, chat_control, state): + from openpgp.gtk.wizard import KeyWizard + KeyWizard(self, account, chat_control) + return False + + @staticmethod + def encryption_state(_chat_control, state): state['authenticated'] = True state['visible'] = True - def on_encryption_button_clicked(self, chat_control): + @staticmethod + def on_encryption_button_clicked(chat_control): account = chat_control.account jid = chat_control.contact.jid transient = chat_control.parent_win.window @@ -186,12 +186,8 @@ class OpenPGPPlugin(GajimPlugin): _('There was no trusted and active key found')) chat_control.sendmessage = False - def _encrypt_message(self, con, obj, callback): + @staticmethod + def _encrypt_message(con, obj, callback): if not con.get_module('OpenPGP').secret_key_available: return con.get_module('OpenPGP').encrypt_message(obj, callback) - - def _decrypt_message(self, con, obj, callback): - if not con.get_module('OpenPGP').secret_key_available: - return - con.get_module('OpenPGP').decrypt_message(obj, callback)