changeset e1cdecc6c2be in /home/hg/repos/gajim-plugins
author: lovetox <forenjunkie(a)chelo.at>
branches:
details:gajim-plugins?cmd=changeset;node=e1cdecc6c2be
description: Add file storing to harddisk and file cache
diffstat:
url_image_preview/url_image_preview.py | 339 +++++++++++++++++++-------------
1 files changed, 200 insertions(+), 139 deletions(-)
diffs (truncated from 574 to 300 lines):
diff -r 1938ce0462db -r e1cdecc6c2be url_image_preview/url_image_preview.py
--- a/url_image_preview/url_image_preview.py Fri Sep 02 22:33:45 2016 +0200
+++ b/url_image_preview/url_image_preview.py Sun Sep 04 23:30:39 2016 +0200
@@ -5,6 +5,7 @@
import re
import os
import urllib2
+import hashlib
from urlparse import urlparse
import logging
@@ -12,6 +13,7 @@
from common import gajim
from common import ged
from common import helpers
+from common import configpaths
from plugins import GajimPlugin
from plugins.helpers import log_calls, log
from plugins.gui import GajimPluginConfigDialog
@@ -53,13 +55,11 @@
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), }
+ 'print_special_text': (self.print_special_text, None), }
self.config_default_values = {
'PREVIEW_SIZE': (150, 'Preview size(10-512)'),
'MAX_FILE_SIZE': (524288, 'Max file size for image preview')}
- self.chat_control = None
- self.controls = []
+ self.controls = {}
# remove oob tag if oob url == message text
def handle_message_received(self, event):
@@ -69,37 +69,35 @@
if oob_node:
oob_url = oob_node.getTagData('url')
oob_desc = oob_node.getTagData('desc')
- 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...")
+ 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):
-
- self.chat_control = chat_control
- control = Base(self, self.chat_control)
- self.controls.append(control)
+ account = chat_control.contact.account.name
+ jid = chat_control.contact.jid
+ if account not in self.controls:
+ self.controls[account] = {}
+ self.controls[account][jid] = Base(self, chat_control)
@log_calls('UrlImagePreviewPlugin')
def disconnect_from_chat_control(self, chat_control):
- for control in self.controls:
- control.disconnect_from_chat_control()
- self.controls = []
+ account = chat_control.contact.account.name
+ jid = chat_control.contact.jid
+ del self.controls[account][jid]
def print_special_text(self, tv, special_text, other_tags, graphics=True,
iter_=None):
- for control in self.controls:
- if control.chat_control.conv_textview != tv:
+ account = tv.account
+ for jid in self.controls[account]:
+ if self.controls[account][jid].chat_control.conv_textview != tv:
continue
- control.print_special_text(special_text, other_tags, graphics=True,
- iter_=iter_)
-
- def print_special_text1(self, chat_control, special_text, other_tags=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)
+ self.controls[account][jid].print_special_text(
+ special_text, other_tags, graphics=True, iter_=iter_)
+ return
class Base(object):
@@ -122,9 +120,9 @@
if special_text.startswith('ftp.'):
special_text = 'ftp://' + special_text
- parts = urlparse(special_text)
- if parts.scheme not in ["https", "http", "ftp", "ftps"] or \
- not parts.netloc:
+ urlparts = urlparse(special_text)
+ if urlparts.scheme not in ["https", "http", "ftp", "ftps"] or \
+ not urlparts.netloc:
log.info("Not accepting URL for image preview: %s" % special_text)
return
@@ -142,86 +140,104 @@
# Show URL, until image is loaded (if ever)
repl_start = buffer_.create_mark(None, iter_, True)
- buffer_.insert_with_tags(iter_, special_text, \
+ buffer_.insert_with_tags(iter_, special_text,
*[(ttt.lookup(t) if isinstance(t, str) else t) for t in ["url"]])
repl_end = buffer_.create_mark(None, iter_, True)
- # First get the http head request with does not fetch data, just headers
- gajim.thread_interface(self._get_http_head, [self.textview.account,
- special_text], self._check_mime_size, [special_text, repl_start, repl_end])
+ directory = os.path.join(configpaths.gajimpaths['MY_CACHE'],
+ 'Downloads')
+
+ filename = os.path.basename(urlparts.path)
+ ext = os.path.splitext(filename)[1]
+ name = os.path.splitext(filename)[0]
+ namehash = hashlib.sha1(special_text).hexdigest()
+ newfilename = name + '_' + namehash + ext
+
+ fullpath = os.path.join(directory, newfilename)
+
+ if os.path.exists(fullpath):
+ def open_file():
+ with open(fullpath, 'rb') as f:
+ mem = f.read()
+ f.closed
+ log.debug('File from disk')
+ return (mem, '')
+
+ gajim.thread_interface(
+ open_file, [],
+ self._update_img, [special_text, repl_start,
+ repl_end, fullpath])
+
+ else:
+ key = ''
+ iv = ''
+ encrypted = False
+ 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 = ''.join(fragment)
+ key = fragment[16:]
+ iv = fragment[:16]
+ if len(key) == 32 and len(iv) == 16:
+ encrypted = True
+
+ if encrypted:
+ if decryption_available:
+ # First get the http head request
+ # which does not fetch data, just headers
+ gajim.thread_interface(
+ self._get_http_head,
+ [self.textview.account, special_text],
+ self._check_mime_size,
+ [special_text, repl_start, repl_end,
+ fullpath, directory, key, iv, encrypted])
+ else:
+ log.debug('Please install Crytography to decrypt pictures')
+ else:
+ # First get the http head request
+ # which does not fetch data, just headers
+ gajim.thread_interface(
+ self._get_http_head, [self.textview.account, special_text],
+ self._check_mime_size, [special_text, repl_start, repl_end,
+ fullpath, directory, key, iv,
+ encrypted])
# Don't print the URL in the message window (in the calling function)
self.textview.plugin_modified = True
- def _check_mime_size(self, (file_mime, file_size), url, repl_start, repl_end):
+ def _check_mime_size(self, (file_mime, file_size),
+ url, repl_start, repl_end, fullpath,
+ directory, key, iv, encrypted):
# Check if mime type is acceptable
if file_mime == '' and file_size == 0:
- log.info("Failed to load HEAD Request for URL: '%s' (see debug log for more info)" % url)
+ log.info("Failed to load HEAD Request for URL: '%s'"
+ "(see debug log for more info)" % url)
# URL is already displayed
return
if file_mime.lower() not in ACCEPTED_MIME_TYPES:
- log.info("Not accepted mime type '%s' for URL: '%s'" % (file_mime.lower(), url))
+ log.info("Not accepted mime type '%s' for URL: '%s'"
+ % (file_mime.lower(), url))
# URL is already displayed
return
# Check if file size is acceptable
if file_size > self.plugin.config['MAX_FILE_SIZE'] or file_size == 0:
- log.info("File size (%s) too big or unknown (zero) for URL: '%s'" % (str(file_size), url))
+ log.info("File size (%s) too big or unknown (zero) for URL: '%s'"
+ % (str(file_size), url))
# URL is already displayed
return
- # check for encryption (conversations mode)
- urlparts = urlparse(url)
- key = ''
- iv = ''
- 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 = ''.join(fragment)
- key = fragment[16:]
- iv = fragment[:16]
+ attributes = {'src': url,
+ 'max_size': self.plugin.config['MAX_FILE_SIZE'],
+ 'fullpath': fullpath,
+ 'directory': directory,
+ 'key': key,
+ 'iv': iv}
- # decrypt if the encryption parameters are correct
- 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, 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'] } ],
- _decryptor, [url, file_mime, repl_start, repl_end, key, iv])
- else:
- # Start downloading image (self._update_img() 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'] } ],
- self._update_img, [url, file_mime, repl_start, repl_end])
-
- def _decrypt_url(self, (mem, alt), key, iv, url):
- try:
- log.info("Before decrypt image")
- 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)
- raise
- if not mem or not len(mem):
- log.error('Could not decrypt image for URL: %s' % url)
- return (None, alt)
- return (mem, alt)
+ gajim.thread_interface(
+ self._download_image, [self.textview.account,
+ attributes, encrypted],
+ self._update_img, [url, repl_start, repl_end, fullpath])
def aes_decrypt_fast(self, key, iv, payload):
# Use AES128 GCM with the given key and iv to decrypt the payload.
@@ -233,21 +249,24 @@
backend=self.backend).decryptor()
return decryptor.update(data) + decryptor.finalize()
- def _update_img(self, (mem, alt), url, file_mime, repl_start, repl_end, decrypted=False):
+ def _update_img(self, (mem, alt), url, repl_start, repl_end, fullpath):
if mem:
try:
loader = gtk.gdk.PixbufLoader()
loader.write(mem)
loader.close()
pixbuf = loader.get_pixbuf()
- pixbuf, w, h = self.get_pixbuf_of_size(pixbuf,
- self.plugin.config['PREVIEW_SIZE'])
+ pixbuf, w, h = self.get_pixbuf_of_size(
+ pixbuf, self.plugin.config['PREVIEW_SIZE'])
eb = gtk.EventBox()
eb.connect('button-press-event', self.on_button_press_event,
- url)
+ fullpath)
eb.connect('enter-notify-event', self.on_enter_event)
eb.connect('leave-notify-event', self.on_leave_event)
- # this is threadsafe (gtk textview is NOT threadsafe by itself!!)
+
+ # this is threadsafe
+ # (gtk textview is NOT threadsafe by itself!!)
+
def add_to_textview():
try: # textview closed in the meantime etc.
buffer_ = repl_start.get_buffer()
@@ -255,24 +274,26 @@