[httpupload] Remove encryption logic from plugin

Call the `encrypt_file` method of the encryption plugin instead
This commit is contained in:
Philipp Hörist
2017-06-09 22:27:48 +02:00
parent 4d03491261
commit 378964fe20

View File

@@ -18,10 +18,10 @@ import os
import threading import threading
import ssl import ssl
import urllib import urllib
import io
from urllib.request import Request, urlopen from urllib.request import Request, urlopen
import mimetypes import mimetypes
import logging import logging
from binascii import hexlify
if os.name == 'nt': if os.name == 'nt':
import certifi import certifi
@@ -35,28 +35,12 @@ from dialogs import FileChooserDialog, ErrorDialog
log = logging.getLogger('gajim.plugin_system.httpupload') log = logging.getLogger('gajim.plugin_system.httpupload')
try:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.hazmat.primitives.ciphers.modes import GCM
ENCRYPTION_AVAILABLE = True
except Exception as exc:
DEP_MSG = 'For encryption of files, ' \
'please install python-cryptography!'
log.error('Cryptography Import Error: %s', exc)
log.info('Decryption/Encryption disabled due to errors')
ENCRYPTION_AVAILABLE = False
IQ_CALLBACK = {} IQ_CALLBACK = {}
NS_HTTPUPLOAD = 'urn:xmpp:http:upload' NS_HTTPUPLOAD = 'urn:xmpp:http:upload'
TAGSIZE = 16
class HttpuploadPlugin(GajimPlugin): class HttpuploadPlugin(GajimPlugin):
def init(self): def init(self):
if not ENCRYPTION_AVAILABLE:
self.available_text = DEP_MSG
self.config_dialog = None self.config_dialog = None
self.events_handlers = { self.events_handlers = {
'agent-info-received': ( 'agent-info-received': (
@@ -174,10 +158,10 @@ class Base(object):
for jid in self.controls: for jid in self.controls:
self.set_button_state(state, self.controls[jid]) self.set_button_state(state, self.controls[jid])
def encryption_activated(self, chat_control): @staticmethod
encrypted = chat_control.encryption == 'OMEMO' def encryption_activated(chat_control):
log.info('Encryption is: %s', encrypted) log.info('Encryption is: %s', chat_control.encryption or 'disabled')
return encrypted return chat_control.encryption is not None
def on_file_dialog_ok(self, widget, chat_control): def on_file_dialog_ok(self, widget, chat_control):
path = widget.get_filename() path = widget.get_filename()
@@ -200,20 +184,6 @@ class Base(object):
transient_for=chat_control.parent_win.window) transient_for=chat_control.parent_win.window)
return return
encrypted = self.encryption_activated(chat_control)
if encrypted and not ENCRYPTION_AVAILABLE:
ErrorDialog(
_('Error'),
'Please install python-cryptography for encrypted uploads',
transient_for=chat_control.parent_win.window)
return
size = os.path.getsize(path)
key, iv = None, None
if encrypted:
key = os.urandom(32)
iv = os.urandom(16)
size += TAGSIZE
mime = mimetypes.MimeTypes().guess_type(path)[0] mime = mimetypes.MimeTypes().guess_type(path)[0]
if not mime: if not mime:
mime = 'application/octet-stream' # fallback mime type mime = 'application/octet-stream' # fallback mime type
@@ -223,10 +193,20 @@ class Base(object):
progress = ProgressWindow( progress = ProgressWindow(
self.plugin, chat_control.parent_win.window, event) self.plugin, chat_control.parent_win.window, event)
file = File(path=path, size=size, mime=mime, encrypted=encrypted, try:
key=key, iv=iv, control=chat_control, file = File(path, mime=mime, encrypted=False,
progress=progress, event=event) control=chat_control, progress=progress, event=event)
self.request_slot(file) except Exception as error:
log.exception('Error while loading file')
file.progress.close_dialog()
ErrorDialog(_('Error'), str(error),
transient_for=file.control.parent_win.window)
return
if self.encryption_activated(chat_control):
self.encrypt_file(file)
else:
self.request_slot(file)
def on_file_button_clicked(self, widget, chat_control): def on_file_button_clicked(self, widget, chat_control):
FileChooserDialog( FileChooserDialog(
@@ -239,7 +219,22 @@ class Base(object):
default_response=Gtk.ResponseType.OK, default_response=Gtk.ResponseType.OK,
transient_for=chat_control.parent_win.window) transient_for=chat_control.parent_win.window)
def encrypt_file(self, file):
GLib.idle_add(file.progress.label.set_text, _('Encrypting file...'))
encryption = file.control.encryption
plugin = gajim.plugin_manager.encryption_plugins[encryption]
if hasattr(plugin, 'encrypt_file'):
plugin.encrypt_file(file, self.account, self.request_slot)
else:
file.progress.close_dialog()
ErrorDialog(
_('Error'),
'For the choosen encryption is no encryption method available',
transient_for=file.control.parent_win.window)
def request_slot(self, file): def request_slot(self, file):
GLib.idle_add(file.progress.label.set_text,
_('Requesting HTTP Upload Slot...'))
iq = nbxmpp.Iq(typ='get', to=self.component) iq = nbxmpp.Iq(typ='get', to=self.component)
id_ = gajim.get_an_id() id_ = gajim.get_an_id()
iq.setID(id_) iq.setID(id_)
@@ -275,7 +270,7 @@ class Base(object):
return return
try: try:
file.stream = StreamFileWithProgress(file, "rb") file.stream = StreamFileWithProgress(file)
except Exception as exc: except Exception as exc:
file.progress.close_dialog() file.progress.close_dialog()
log.exception("Could not open file") log.exception("Could not open file")
@@ -331,8 +326,8 @@ class Base(object):
if 200 <= response_code < 300: if 200 <= response_code < 300:
log.info("Upload completed successfully") log.info("Upload completed successfully")
message = file.get message = file.get
if file.encrypted: if file.user_data:
message += '#' + hexlify(file.iv + file.key).decode('utf-8') message += '#' + file.user_data
else: else:
self.plugin.messages.append(message) self.plugin.messages.append(message)
file.control.send_message(message=message) file.control.send_message(message=message)
@@ -353,28 +348,34 @@ class Base(object):
class File: class File:
def __init__(self, **kwargs): def __init__(self, path, **kwargs):
for k, v in kwargs.items(): for k, v in kwargs.items():
setattr(self, k, v) setattr(self, k, v)
self.stream = None self.stream = None
self.path = path
self.put = None self.put = None
self.get = None self.get = None
self.data = None
self.user_data = None
self.size = None
self.load_data()
def load_data(self):
with open(self.path, 'rb') as content:
self.data = content.read()
self.size = len(self.data)
def get_data(self, full=False):
if full:
return io.BytesIO(self.data).getvalue()
return io.BytesIO(self.data)
class StreamFileWithProgress: class StreamFileWithProgress:
def __init__(self, file, mode, *args): def __init__(self, file, *args):
self.event = file.event self.event = file.event
self.backing = open(file.path, mode) self.backing = file.get_data()
self.encrypted = file.encrypted
self.backing.seek(0, os.SEEK_END) self.backing.seek(0, os.SEEK_END)
if self.encrypted: self._total = self.backing.tell()
self.encryptor = Cipher(
algorithms.AES(file.key),
GCM(file.iv),
backend=default_backend()).encryptor()
self._total = self.backing.tell() + TAGSIZE
else:
self._total = self.backing.tell()
self.backing.seek(0) self.backing.seek(0)
self._callback = file.progress.update_progress self._callback = file.progress.update_progress
self._args = args self._args = args
@@ -386,26 +387,13 @@ class StreamFileWithProgress:
def read(self, size): def read(self, size):
if self.event.isSet(): if self.event.isSet():
raise UploadAbortedException raise UploadAbortedException
if self.encrypted:
data = self.backing.read(size) data = self.backing.read(size)
if len(data) > 0: self._seen += len(data)
data = self.encryptor.update(data) if self._callback:
self._seen += len(data) GLib.idle_add(
if (self._seen + TAGSIZE) == self._total: self._callback, self._seen, self._total, *self._args)
self.encryptor.finalize() return data
data += self.encryptor.tag
self._seen += TAGSIZE
if self._callback:
GLib.idle_add(
self._callback, self._seen, self._total, *self._args)
return data
else:
data = self.backing.read(size)
self._seen += len(data)
if self._callback:
GLib.idle_add(
self._callback, self._seen, self._total, *self._args)
return data
def close(self): def close(self):
return self.backing.close() return self.backing.close()
@@ -422,7 +410,6 @@ class ProgressWindow:
self.dialog.set_transient_for(parent) self.dialog.set_transient_for(parent)
self.dialog.set_title('HTTP Upload') self.dialog.set_title('HTTP Upload')
self.label = self.xml.get_object('label') self.label = self.xml.get_object('label')
self.label.set_text(_('Requesting HTTP Upload Slot...'))
self.progressbar = self.xml.get_object('progressbar') self.progressbar = self.xml.get_object('progressbar')
self.dialog.show_all() self.dialog.show_all()
self.xml.connect_signals(self) self.xml.connect_signals(self)