changeset 90e748ab1c84 in /home/hg/repos/gajim-plugins
author: Linus Heckemann git@sphalerite.org branches: gtk3 details:gajim-plugins?cmd=changeset;node=90e748ab1c84 description: Add back HTTP upload
diffstat:
httpupload/__init__.py | 2 + httpupload/httpupload.py | 660 +++++++++++++++++++++++++++++++++++ httpupload/manifest.ini | 16 + httpupload/upload_progress_dialog.ui | 105 +++++ 4 files changed, 783 insertions(+), 0 deletions(-)
diffs (truncated from 799 to 300 lines):
diff -r aa7dd59ea9b5 -r 90e748ab1c84 httpupload/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/httpupload/__init__.py Thu Aug 18 19:52:31 2016 +0200 @@ -0,0 +1,2 @@ +# simple redirect +from httpupload import HttpuploadPlugin diff -r aa7dd59ea9b5 -r 90e748ab1c84 httpupload/httpupload.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/httpupload/httpupload.py Thu Aug 18 19:52:31 2016 +0200 @@ -0,0 +1,660 @@ +# -*- coding: utf-8 -*- +## + +from common import demandimport +demandimport.enable() +demandimport.ignore += ['builtins', '__builtin__', 'PIL','_imp'] + +import gtk +import gobject +import os +import time +import base64 +import tempfile +import urllib2 +import mimetypes # better use the magic packet, but that's not a standard lib +import gtkgui_helpers +from Queue import Queue +try: + from PIL import Image + pil_available = True +except: + pil_available = False +from io import BytesIO +import base64 +import binascii + +from common import gajim +from common import ged +import chat_control +from plugins import GajimPlugin +from plugins.helpers import log_calls +import logging +from dialogs import FileChooserDialog, ImageChooserDialog, ErrorDialog +import nbxmpp + +log = logging.getLogger('gajim.plugin_system.httpupload') + +if os.name != 'nt': + 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 e: + log.debug(e) + encryption_available = False +else: + encryption_available = False + log.info('Cryptography not available on Windows for now') + +# XEP-0363 (http://xmpp.org/extensions/xep-0363.html) +NS_HTTPUPLOAD = 'urn:xmpp:http:upload' +TAGSIZE = 16 + +jid_to_servers = {} +iq_ids_to_callbacks = {} +last_info_query = {} +max_thumbnail_size = 2048 +max_thumbnail_dimension = 160 + + +class HttpuploadPlugin(GajimPlugin): + + @log_calls('HttpuploadPlugin') + def init(self): + self.config_dialog = None # HttpuploadPluginConfigDialog(self) + self.controls = [] + self.events_handlers = {} + self.events_handlers['agent-info-received'] = (ged.PRECORE, + self.handle_agent_info_received) + self.events_handlers['raw-iq-received'] = (ged.PRECORE, + self.handle_iq_received) + self.gui_extension_points = { + 'chat_control_base': (self.connect_with_chat_control, + self.disconnect_from_chat_control), + 'chat_control_base_update_toolbar': (self.update_button_state, + None)} + self.first_run = True + + def handle_iq_received(self, event): + global iq_ids_to_callbacks + id_ = event.stanza.getAttr("id") + if str(id_) in iq_ids_to_callbacks: + try: + iq_ids_to_callbacks[str(id_)](event.stanza) + except: + raise + finally: + del iq_ids_to_callbacks[str(id_)] + + def handle_agent_info_received(self, event): + global jid_to_servers + if NS_HTTPUPLOAD in event.features and gajim.jid_is_transport(event.jid): + own_jid = gajim.get_jid_without_resource(str(event.stanza.getTo())) + jid_to_servers[own_jid] = event.jid # map own jid to upload component's jid + log.info(own_jid + " can do http uploads via component " + event.jid) + # update all buttons + for base in self.controls: + self.update_button_state(base.chat_control) + + @log_calls('HttpuploadPlugin') + def connect_with_chat_control(self, control): + self.chat_control = control + base = Base(self, self.chat_control) + self.controls.append(base) + if self.first_run: + # ALT + U + gtk.binding_entry_add_signal(control.msg_textview, + gtk.keysyms.u, gtk.gdk.MOD1_MASK, 'mykeypress', + int, gtk.keysyms.u, gtk.gdk.ModifierType, gtk.gdk.MOD1_MASK) + self.first_run = False + self.update_button_state(self.chat_control) + + @log_calls('HttpuploadPlugin') + def disconnect_from_chat_control(self, chat_control): + for control in self.controls: + control.disconnect_from_chat_control() + self.controls = [] + + @log_calls('HttpuploadPlugin') + def update_button_state(self, chat_control): + global jid_to_servers + global iq_ids_to_callbacks + global last_info_query + + if gajim.connections[chat_control.account].connection == None and \ + gajim.get_jid_from_account(chat_control.account) in jid_to_servers: + # maybe don't delete this and detect vanished upload components when actually trying to upload something + log.info("Deleting %s from jid_to_servers (disconnected)" % gajim.get_jid_from_account(chat_control.account)) + del jid_to_servers[gajim.get_jid_from_account(chat_control.account)] + #pass + + # query info at most every 60 seconds in case something goes wrong + if (not chat_control.account in last_info_query or \ + last_info_query[chat_control.account] + 60 < time.time()) and \ + not gajim.get_jid_from_account(chat_control.account) in jid_to_servers and \ + gajim.account_is_connected(chat_control.account): + log.info("Account %s: Using dicovery to find jid of httpupload component" % chat_control.account) + id_ = gajim.get_an_id() + iq = nbxmpp.Iq( + typ='get', + to=gajim.get_server_from_jid(gajim.get_jid_from_account(chat_control.account)), + queryNS="http://jabber.org/protocol/disco#items" + ) + iq.setID(id_) + def query_info(stanza): + global last_info_query + for item in stanza.getTag("query").getTags("item"): + id_ = gajim.get_an_id() + iq = nbxmpp.Iq( + typ='get', + to=item.getAttr("jid"), + queryNS="http://jabber.org/protocol/disco#info" + ) + iq.setID(id_) + last_info_query[chat_control.account] = time.time() + gajim.connections[chat_control.account].connection.send(iq) + iq_ids_to_callbacks[str(id_)] = query_info + gajim.connections[chat_control.account].connection.send(iq) + #send disco query to main server jid + id_ = gajim.get_an_id() + iq = nbxmpp.Iq( + typ='get', + to=gajim.get_server_from_jid(gajim.get_jid_from_account(chat_control.account)), + queryNS="http://jabber.org/protocol/disco#info" + ) + iq.setID(id_) + last_info_query[chat_control.account] = time.time() + gajim.connections[chat_control.account].connection.send(iq) + + for base in self.controls: + if base.chat_control == chat_control: + is_supported = gajim.get_jid_from_account(chat_control.account) in jid_to_servers and \ + gajim.connections[chat_control.account].connection != None + log.info("Account %s: httpupload is_supported: %s" % (str(chat_control.account), str(is_supported))) + if not is_supported: + text = _('Your server does not support http uploads') + image_text = text + else: + text = _('Send file via http upload') + image_text = _('Send image via http upload') + base.button.set_sensitive(is_supported) + base.button.set_tooltip_text(text) + base.image_button.set_sensitive(is_supported) + base.image_button.set_tooltip_text(image_text) + + +class Base(object): + def __init__(self, plugin, chat_control): + self.dlg = None + self.dialog_type = 'file' + self.keypress_id = chat_control.msg_textview.connect('mykeypress', + self.on_key_press) + self.plugin = plugin + self.encrypted_upload = False + self.chat_control = chat_control + actions_hbox = chat_control.xml.get_object('actions_hbox') + self.button = gtk.Button(label=None, stock=None, use_underline=True) + self.button.set_property('relief', gtk.RELIEF_NONE) + self.button.set_property('can-focus', False) + self.button.set_sensitive(False) + img = gtk.Image() + img.set_from_file(self.plugin.local_file_path('httpupload.png')) + self.button.set_image(img) + self.button.set_tooltip_text(_('Your server does not support http uploads')) + self.image_button = gtk.Button(label=None, stock=None, use_underline=True) + self.image_button.set_property('relief', gtk.RELIEF_NONE) + self.image_button.set_property('can-focus', False) + self.image_button.set_sensitive(False) + img = gtk.Image() + img.set_from_file(self.plugin.local_file_path('image.png')) + self.image_button.set_image(img) + self.image_button.set_tooltip_text(_('Your server does not support http uploads')) + send_button = chat_control.xml.get_object('send_button') + send_button_pos = actions_hbox.child_get_property(send_button, + 'position') + actions_hbox.add_with_properties(self.button, 'position', + send_button_pos - 2, 'expand', False) + + actions_hbox.add_with_properties(self.image_button, 'position', + send_button_pos - 1, 'expand', False) + + file_id = self.button.connect('clicked', self.on_file_button_clicked) + image_id = self.image_button.connect('clicked', self.on_image_button_clicked) + chat_control.handlers[file_id] = self.button + chat_control.handlers[image_id] = self.image_button + chat_control.handlers[self.keypress_id] = chat_control.msg_textview + self.button.show() + self.image_button.show() + + def on_key_press(self, widget, event_keyval, event_keymod): + # construct event instance from binding + event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) # it's always a key-press here + event.keyval = event_keyval + event.state = event_keymod + event.time = 0 # assign current time + + if event.keyval != gtk.keysyms.u: + return + if event.state != gtk.gdk.MOD1_MASK: # ALT+u + return + is_supported = gajim.get_jid_from_account(self.chat_control.account) in jid_to_servers and \ + gajim.connections[self.chat_control.account].connection != None + if not is_supported: + from dialogs import WarningDialog + WarningDialog('Warning', _('Your server does not support http uploads'), + transient_for=self.chat_control.parent_win.window) + return + self.on_file_button_clicked(widget) + + def disconnect_from_chat_control(self): + actions_hbox = self.chat_control.xml.get_object('actions_hbox') + actions_hbox.remove(self.button) + actions_hbox.remove(self.image_button) + if self.keypress_id in self.chat_control.handlers and \ + self.chat_control.handlers[self.keypress_id].handler_is_connected(self.keypress_id): + self.chat_control.handlers[self.keypress_id].disconnect(self.keypress_id) + del self.chat_control.handlers[self.keypress_id] + + def encryption_activated(self): + if not encryption_available: + return False + jid = self.chat_control.contact.jid + account = self.chat_control.account + for plugin in gajim.plugin_manager.active_plugins: + if type(plugin).__name__ == 'OmemoPlugin': + omemo = plugin + break + if omemo: + state = omemo.get_omemo_state(account) + log.info('Encryption is: ' + + str(state.encryption.is_active(jid))) + return state.encryption.is_active(jid) + log.info('Encryption is: False / OMEMO not found') + return False + + def on_file_dialog_ok(self, widget, path_to_file=None): + global jid_to_servers + + try: + self.encrypted_upload = self.encryption_activated() + except Exception as e: + log.debug(e) + self.encrypted_upload = False + + if not path_to_file: + path_to_file = self.dlg.get_filename() + if not path_to_file: + self.dlg.destroy()