Commit 4493fead authored by Philipp Hörist's avatar Philipp Hörist

HtmlTextview: Remove <img> support

If at all this needs to be reimplemented with libsoup
parent 636effbc
Pipeline #5982 passed with stages
in 3 minutes and 41 seconds
......@@ -140,7 +140,6 @@ _dependencies = {
'AV': False,
'GEOCLUE': False,
'UPNP': False,
'PYCURL': False,
'GSOUND': False,
'GSPELL': False,
'IDLE': False,
......@@ -239,13 +238,6 @@ def detect_dependencies():
except ValueError:
pass
# PYCURL
try:
import pycurl # pylint: disable=unused-import
_dependencies['PYCURL'] = True
except ImportError:
pass
# IDLE
try:
from gajim.common import idle
......
......@@ -38,7 +38,6 @@ import base64
import hashlib
import shlex
import socket
import time
import logging
import json
import copy
......@@ -52,7 +51,6 @@ import string
from string import Template
import urllib
from urllib.parse import unquote
from io import StringIO
from encodings.punycode import punycode_encode
from functools import wraps
from packaging.version import Version as V
......@@ -84,9 +82,6 @@ from gajim.common.const import GIO_TLS_ERRORS
from gajim.common.const import SHOW_LIST
from gajim.common.structs import URI
if app.is_installed('PYCURL'):
import pycurl
log = logging.getLogger('gajim.c.helpers')
......@@ -871,137 +866,6 @@ def get_proxy(proxy_name):
username=username,
password=password)
def get_proxy_info(account):
p = app.config.get_per('accounts', account, 'proxy')
if not p:
p = app.config.get('global_proxy')
if p and p in app.config.get_per('proxies'):
proxy = {}
proxyptr = app.config.get_per('proxies', p)
if not proxyptr:
return proxy
for key in proxyptr.keys():
proxy[key] = proxyptr[key]
return proxy
def _get_img_direct(attrs):
"""
Download an image. This function should be launched in a separated thread.
"""
mem = b''
alt = ''
max_size = 2*1024*1024
if 'max_size' in attrs:
max_size = attrs['max_size']
# Wait maximum 10s for connection
socket.setdefaulttimeout(10)
try:
req = urllib.request.Request(attrs['src'])
req.add_header('User-Agent', 'Gajim ' + app.version)
f = urllib.request.urlopen(req)
except Exception as ex:
log.debug('Error loading image %s ', attrs['src'] + str(ex))
alt = attrs.get('alt', 'Broken image')
else:
# Wait 2s between each byte
try:
f.fp._sock.fp._sock.settimeout(2)
except Exception:
pass
# On a slow internet connection with ~1000kbps
# you need ~10 seconds for 1 MB
deadline = time.time() + (10 * (max_size / 1048576))
while True:
if time.time() > deadline:
log.debug('Timeout loading image %s ', attrs['src'])
mem = ''
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Timeout loading image')
break
try:
temp = f.read(100)
except socket.timeout as ex:
log.debug('Timeout loading image %s ', attrs['src'] + str(ex))
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Timeout loading image')
break
if temp:
mem += temp
else:
break
if len(mem) > max_size:
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Image is too big')
break
f.close()
return (mem, alt)
def _get_img_proxy(attrs, proxy):
"""
Download an image through a proxy. This function should be launched in a
separated thread.
"""
if not app.is_installed('PYCURL'):
return '', _('PyCURL is not installed')
alt, max_size = '', 2*1024*1024
if 'max_size' in attrs:
max_size = attrs['max_size']
try:
b = StringIO()
c = pycurl.Curl()
c.setopt(pycurl.URL, attrs['src'].encode('utf-8'))
c.setopt(pycurl.FOLLOWLOCATION, 1)
# Wait maximum 10s for connection
c.setopt(pycurl.CONNECTTIMEOUT, 10)
# On a slow internet connection with ~1000kbps
# you need ~10 seconds for 1 MB
c.setopt(pycurl.TIMEOUT, 10 * (max_size / 1048576))
c.setopt(pycurl.MAXFILESIZE, max_size)
c.setopt(pycurl.WRITEFUNCTION, b.write)
c.setopt(pycurl.USERAGENT, 'Gajim ' + app.version)
# set proxy
c.setopt(pycurl.PROXY, proxy['host'].encode('utf-8'))
c.setopt(pycurl.PROXYPORT, proxy['port'])
if proxy['useauth']:
c.setopt(pycurl.PROXYUSERPWD, proxy['user'].encode('utf-8')\
+ ':' + proxy['pass'].encode('utf-8'))
c.setopt(pycurl.PROXYAUTH, pycurl.HTTPAUTH_ANY)
if proxy['type'] == 'http':
c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
elif proxy['type'] == 'socks5':
c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5)
c.close()
t = b.getvalue()
return (t, attrs.get('alt', ''))
except pycurl.error as ex:
alt = attrs.get('alt', '')
if alt:
alt += '\n'
if ex.errno == pycurl.E_FILESIZE_EXCEEDED:
alt += _('Image is too big')
elif ex.errno == pycurl.E_OPERATION_TIMEOUTED:
alt += _('Timeout loading image')
else:
alt += _('Error loading image')
except Exception as ex:
log.debug('Error loading image %s ', attrs['src'] + str(ex))
alt = attrs.get('alt', 'Broken image')
return ('', alt)
def download_image(account, attrs):
proxy = get_proxy_info(account)
if proxy and proxy['type'] in ('http', 'socks5'):
return _get_img_proxy(attrs, proxy)
return _get_img_direct(attrs)
def version_condition(current_version, required_version):
if V(current_version) < V(required_version):
return False
......
......@@ -33,7 +33,6 @@ modular.
import re
import logging
import urllib
import xml.sax
import xml.sax.handler
from io import StringIO
......@@ -42,16 +41,12 @@ from gi.repository import GObject
from gi.repository import Pango
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
from gi.repository import GLib
from gajim.common import app
from gajim.common import helpers
from gajim.common.i18n import _
from gajim.common.const import StyleAttr
from gajim.common.helpers import open_uri
from gajim.common.helpers import parse_uri
from gajim.gtk.util import load_icon
from gajim.gtk.util import get_cursor
from gajim.gui_menu_builder import get_conv_context_menu
......@@ -510,123 +505,6 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
tag.title = title
return tag
def _update_img(self, output, attrs, img_mark, tags):
'''Callback function called after the function helpers.download_image.
'''
mem, alt = output
self._process_img(attrs, (mem, alt, img_mark, tags))
def _process_img(self, attrs, loaded=None):
'''Process a img tag.
'''
mem = ''
pixbuf = None
replace_mark = None
replace_tags = None
try:
if attrs['src'].startswith('data:image/'):
# The "data" URL scheme http://tools.ietf.org/html/rfc2397
import base64
img = attrs['src'].split(',')[1]
mem = base64.standard_b64decode(urllib.parse.unquote(
img).encode('utf-8'))
elif loaded is not None:
(mem, alt, replace_mark, replace_tags) = loaded
else:
if self.conv_textview:
img_mark = self.textbuf.create_mark(None, self.iter, True)
app.thread_interface(
helpers.download_image,
[self.conv_textview.account, attrs],
self._update_img,
[attrs, img_mark, self._get_style_tags()])
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Loading')
pixbuf = load_icon('image-missing',
self.textview,
pixbuf=True)
if mem:
# Caveat: GdkPixbuf is known not to be safe to load
# images from network... this program is now potentially
# hackable ;)
loader = GdkPixbuf.PixbufLoader()
dims = [0, 0]
def height_cb(length):
dims[1] = length
def width_cb(length):
dims[0] = length
# process width and height attributes
width = attrs.get('width')
height = attrs.get('height')
# override with width and height styles
for attr, val in style_iter(attrs.get('style', '')):
if attr == 'width':
width = val
elif attr == 'height':
height = val
if width:
self._parse_length(width, False, False, 1, 1000, width_cb)
if height:
self._parse_length(height, False, False, 1, 1000, height_cb)
def set_size(_pixbuf, width_, height_, dims):
"""
FIXME: Floats should be relative to the whole textview, and
resize with it. This needs new pifbufs for every resize,
GdkPixbuf.Pixbuf.scale_simple or similar.
"""
if isinstance(dims[0], float):
dims[0] = int(dims[0] * width_)
elif not dims[0]:
dims[0] = width_
if isinstance(dims[1], float):
dims[1] = int(dims[1] * height_)
if not dims[1]:
dims[1] = height_
loader.set_size(*dims)
if width or height:
loader.connect('size-prepared', set_size, dims)
loader.write(mem)
loader.close()
pixbuf = loader.get_pixbuf()
alt = attrs.get('alt', '')
working_iter = self.iter
if replace_mark is not None:
working_iter = self.textbuf.get_iter_at_mark(replace_mark)
next_iter = working_iter.copy()
next_iter.forward_char()
self.textbuf.delete(working_iter, next_iter)
self.textbuf.delete_mark(replace_mark)
if pixbuf is not None:
if replace_mark:
tags = replace_tags
else:
tags = self._get_style_tags()
if tags:
tmpmark = self.textbuf.create_mark(None, working_iter, True)
self.textbuf.insert_pixbuf(working_iter, pixbuf)
self.starting = False
if tags:
start = self.textbuf.get_iter_at_mark(tmpmark)
for tag in tags:
self.textbuf.apply_tag(tag, start, working_iter)
self.textbuf.delete_mark(tmpmark)
else:
self._insert_text('[IMG: %s]' % alt, working_iter)
except Exception as ex:
log.error('Error loading image %s', str(ex))
pixbuf = None
alt = attrs.get('alt', 'Broken image')
try:
loader.close()
except Exception:
pass
return pixbuf
def _begin_span(self, style, tag=None, id_=None):
if style is None:
self.styles.append(tag)
......@@ -730,8 +608,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
tag.is_anchor = True
elif name in LIST_ELEMS:
style += ';margin-left: 2em'
elif name == 'img':
tag = self._process_img(attrs)
if name in _element_styles:
style += _element_styles[name]
# so that explicit styles override implicit ones,
......
......@@ -40,9 +40,6 @@ ignore_missing_imports = True
[mypy-PIL.*]
ignore_missing_imports = True
[mypy-pycurl.*]
ignore_missing_imports = True
[mypy-netifaces.*]
ignore_missing_imports = True
......
......@@ -3,7 +3,7 @@
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=pycurl
extension-pkg-whitelist=
# Add files or directories to the blacklist. They should be base names, not
# paths.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment