changeset 7e92f69b2f36 in /home/hg/repos/gajim-plugins
author: Philipp Hoerist branches: details:gajim-plugins?cmd=changeset;node=7e92f69b2f36 description: fast image decryption on linux
diffstat:
url_image_preview/manifest.ini | 3 +- url_image_preview/url_image_preview.py | 105 ++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 35 deletions(-)
diffs (247 lines):
diff -r b374cf0c5089 -r 7e92f69b2f36 url_image_preview/manifest.ini --- a/url_image_preview/manifest.ini Wed Jul 20 19:12:55 2016 +0200 +++ b/url_image_preview/manifest.ini Sun Jul 17 18:33:55 2016 +0200 @@ -1,13 +1,14 @@ [info] name: Url image preview short_name: url_image_preview -version: 0.7.3 +version: 0.7.4 description: Url image preview in chatbox.<br/> Based on patch in <a href="http://trac.gajim.org/attachment/ticket/5300">ticket #5300</a>. authors = Denis Fomin fominde@gmail.com Yann Leboulanger asterix@lagaule.org Anders Sandblad runeson@gmail.com Thilo Molitor thilo@eightysoft.de + Philipp Hoerist philipp@hoerist.com homepage = http://trac-plugins.gajim.org/wiki/UrlImagePreviewPlugin min_gajim_version: 0.16.4 max_gajim_version: 0.16.9 diff -r b374cf0c5089 -r 7e92f69b2f36 url_image_preview/url_image_preview.py --- a/url_image_preview/url_image_preview.py Wed Jul 20 19:12:55 2016 +0200 +++ b/url_image_preview/url_image_preview.py Sun Jul 17 18:33:55 2016 +0200 @@ -18,10 +18,29 @@ from conversation_textview import TextViewImage from .aes_gcm import aes_decrypt
+from common import demandimport +demandimport.enable() +demandimport.ignore += ['_imp'] + log = logging.getLogger('gajim.plugin_system.url_image_preview')
-ACCEPTED_MIME_TYPES = ('image/png','image/jpeg','image/gif','image/raw', - 'image/svg+xml') +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 + decryption_available = True + except Exception as e: + log.debug(e) + decryption_available = False +else: + decryption_available = False + log.info('Cryptography not available on Windows for now') + +ACCEPTED_MIME_TYPES = ('image/png', 'image/jpeg', 'image/gif', 'image/raw', + 'image/svg+xml') +
class UrlImagePreviewPlugin(GajimPlugin): @log_calls('UrlImagePreviewPlugin') @@ -31,13 +50,13 @@ self.events_handlers['message-received'] = (ged.PRECORE, self.handle_message_received) self.gui_extension_points = { - 'chat_control_base': (self.connect_with_chat_control, - self.disconnect_from_chat_control), - 'print_special_text': (self.print_special_text, - self.print_special_text1),} + 'chat_control_base': (self.connect_with_chat_control, + self.disconnect_from_chat_control), + 'print_special_text': (self.print_special_text, + self.print_special_text1), } self.config_default_values = { - 'PREVIEW_SIZE': (150, 'Preview size(10-512)'), - 'MAX_FILE_SIZE': (524288, 'Max file size for image preview')} + 'PREVIEW_SIZE': (150, 'Preview size(10-512)'), + 'MAX_FILE_SIZE': (524288, 'Max file size for image preview')} self.chat_control = None self.controls = []
@@ -52,7 +71,7 @@ if oob_url and oob_url == event.msgtxt and (not oob_desc or oob_desc == ""): log.debug("Detected oob tag containing same url as the message text, deleting oob tag...") event.stanza.delChild(oob_node) - + @log_calls('UrlImagePreviewPlugin') def connect_with_chat_control(self, chat_control):
@@ -67,20 +86,21 @@ self.controls = []
def print_special_text(self, tv, special_text, other_tags, graphics=True, - iter_=None): + iter_=None): for control in self.controls: if control.chat_control.conv_textview != tv: continue control.print_special_text(special_text, other_tags, graphics=True, - iter_=iter_) + iter_=iter_)
def print_special_text1(self, chat_control, special_text, other_tags=None, - graphics=True, iter_=None): + graphics=True, iter_=None): for control in self.controls: if control.chat_control == chat_control: control.disconnect_from_chat_control() self.controls.remove(control)
+ class Base(object): def __init__(self, plugin, chat_control): self.plugin = plugin @@ -88,7 +108,7 @@ self.textview = self.chat_control.conv_textview
def print_special_text(self, special_text, other_tags, graphics=True, - iter_=None): + iter_=None): # remove qip bbcode special_text = special_text.rsplit('[/img]')[0]
@@ -96,13 +116,13 @@ special_text = 'http://' + special_text if special_text.startswith('ftp.'): special_text = 'ftp://' + special_text - + parts = urlparse(special_text) - if not parts.scheme in ["https", "http", "ftp", "ftps"] or \ - not parts.netloc: + if parts.scheme not in ["https", "http", "ftp", "ftps"] or \ + not parts.netloc: log.info("Not accepting URL for image preview: %s" % special_text) return - + buffer_ = self.textview.tv.get_buffer() if not iter_: iter_ = buffer_.get_end_iter() @@ -151,23 +171,24 @@ if len(urlparts.fragment): fragment = [] for i in range(0, len(urlparts.fragment), 2): - fragment.append( chr( int (urlparts.fragment[i:i+2], 16 ) ) ) + fragment.append(chr(int(urlparts.fragment[i:i + 2], 16))) fragment = ''.join(fragment) key = fragment[16:] iv = fragment[:16] - + # decrypt if the encryption parameters are correct - if len(urlparts.fragment) and len(key)==32 and len(iv)==16: + if len(urlparts.fragment) and len(key) == 32 and len(iv) == 16: def _decryptor((mem, alt), url, file_mime, repl_start, repl_end, key, iv): if not mem: log.error('Could not download image for URL: %s -- %s' % (url, alt)) return # start self._decrypt_url() in own thread and self._update_img() afterwards - gajim.thread_interface(self._decrypt_url, - [(mem, alt), key, iv], - self._update_img, - [url, file_mime, repl_start, repl_end, True]) - + gajim.thread_interface( + self._decrypt_url, + [(mem, alt), key, iv, url], + self._update_img, + [url, file_mime, repl_start, repl_end, True]) + # Start downloading image (_decryptor is callback when download has finished) gajim.thread_interface(self._download_image, [ self.textview.account, { 'src': url, 'max_size': self.plugin.config['MAX_FILE_SIZE'] } ], @@ -178,10 +199,15 @@ 'src': url, 'max_size': self.plugin.config['MAX_FILE_SIZE'] } ], self._update_img, [url, file_mime, repl_start, repl_end])
- def _decrypt_url(self, (mem, alt), key, iv): + def _decrypt_url(self, (mem, alt), key, iv, url): try: log.info("Before decrypt image") - mem = aes_decrypt(key, iv, mem) + if decryption_available: + log.info("Fast decrypt") + mem = self.aes_decrypt_fast(key, iv, mem) + else: + log.info("Slow decrypt") + mem = aes_decrypt(key, iv, mem) log.info("After decrypt image") except Exception: log.error('Could not decrypt image for URL (exception raised): %s' % url) @@ -190,7 +216,18 @@ log.error('Could not decrypt image for URL: %s' % url) return (None, alt) return (mem, alt) - + + def aes_decrypt_fast(self, key, iv, payload): + # Use AES128 GCM with the given key and iv to decrypt the payload. + data = payload[:-16] + tag = payload[-16:] + backend = default_backend() + decryptor = Cipher( + algorithms.AES(key), + GCM(iv, tag=tag), + backend=backend).decryptor() + return decryptor.update(data) + decryptor.finalize() + def _update_img(self, (mem, alt), url, file_mime, repl_start, repl_end, decrypted=False): if mem: try: @@ -232,7 +269,7 @@ # If image could not be downloaded, URL is already displayed log.error('Could not download image for URL: %s -- %s' % (url, alt))
- def _get_http_head (self, account, url): + def _get_http_head(self, account, url): # Check if proxy is used proxy = helpers.get_proxy_info(account) if proxy and proxy['type'] in ('http', 'socks5'): @@ -244,8 +281,8 @@ if proxy and proxy['type'] in ('http', 'socks5'): return self._get_img_proxy(attrs, proxy) return self._get_img_direct(attrs) - - def _get_http_head_direct (self, url): + + def _get_http_head_direct(self, url): log.debug('Get head request direct for URL: %s' % url) try: req = urllib2.Request(url) @@ -263,7 +300,7 @@ ctype = ctype_list[0] clen = 0 clen_list = url_headers.getheaders('Content-Length') - if clen_list: + if clen_list: try: clen = int(clen_list[0]) except ValueError: @@ -322,9 +359,9 @@ try: clen = int(searchObj.group(1).strip()) except ValueError: - pass + pass return (ctype, clen) - + def _get_img_direct(self, attrs): """ Download an image. This function should be launched in a separated thread.