Add dedicated method for parsing and opening URIs

This commit is contained in:
Philipp Hörist 2019-04-27 12:17:16 +02:00
parent 6c6c4bdcfe
commit dd7496d7a7
15 changed files with 169 additions and 112 deletions

View File

@ -285,13 +285,11 @@ def on_delete_motd(action, param):
def on_contents(action, param):
helpers.launch_browser_mailer(
'url', 'https://dev.gajim.org/gajim/gajim/wikis')
helpers.open_uri('https://dev.gajim.org/gajim/gajim/wikis')
def on_faq(action, param):
helpers.launch_browser_mailer(
'url', 'https://dev.gajim.org/gajim/gajim/wikis/help/gajimfaq')
helpers.open_uri('https://dev.gajim.org/gajim/gajim/wikis/help/gajimfaq')
def on_keyboard_shortcuts(action, param):
@ -351,8 +349,8 @@ def show_next_pending_event(action, param):
def open_link(_action, param):
kind, link = param.get_strv()
helpers.launch_browser_mailer(kind, link)
uri = param.get_string()
helpers.open_uri(uri)
def copy_text(_action, param):

View File

@ -420,7 +420,7 @@ class GajimApplication(Gtk.Application):
act.connect("activate", app_actions.copy_text)
self.add_action(act)
act = Gio.SimpleAction.new('open-link', GLib.VariantType.new('as'))
act = Gio.SimpleAction.new('open-link', GLib.VariantType.new('s'))
act.connect("activate", app_actions.open_link)
self.add_action(act)

View File

@ -43,6 +43,7 @@ from gajim.common import ged
from gajim.common import i18n
from gajim.common.i18n import _
from gajim.common.helpers import AdditionalDataDict
from gajim.common.helpers import open_uri
from gajim.common.contacts import GC_Contact
from gajim.common.const import AvatarSize
from gajim.common.const import KindConstant
@ -651,7 +652,7 @@ class ChatControl(ChatControlBase):
uri = 'https://www.openstreetmap.org/?' + \
'mlat=%(lat)s&mlon=%(lon)s&zoom=16' % {'lat': location['lat'],
'lon': location['lon']}
helpers.launch_browser_mailer('url', uri)
open_uri(uri)
def on_location_eventbox_leave_notify_event(self, widget, event):
"""

View File

@ -167,9 +167,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
if self.parent_win:
self.parent_win.redraw_tab(self)
def status_url_clicked(self, widget, url):
helpers.launch_browser_mailer('url', url)
def setup_seclabel(self):
self.seclabel_combo.hide()
self.seclabel_combo.set_no_show_all(True)

View File

@ -184,6 +184,22 @@ class Display(Enum):
QUARTZ = 'GdkQuartzDisplay'
class URIType(Enum):
UNKNOWN = 'unknown'
XMPP = 'xmpp'
MAIL = 'mail'
GEO = 'geo'
WEB = 'web'
FILE = 'file'
AT = 'at'
class URIAction(Enum):
MESSAGE = 'message'
JOIN = 'join'
SUBSCRIBE = 'subscribe'
EME_MESSAGES = {
'urn:xmpp:otr:0':
_('This message was encrypted with OTR '

View File

@ -65,6 +65,9 @@ from gajim.common.i18n import _
from gajim.common.i18n import ngettext
from gajim.common.const import ShowConstant
from gajim.common.const import Display
from gajim.common.const import URIType
from gajim.common.const import URIAction
from gajim.common.structs import URI
if app.is_installed('PYCURL'):
import pycurl
@ -629,35 +632,6 @@ def get_contact_dict_for_account(account):
contacts_dict[name] = contact
return contacts_dict
def launch_browser_mailer(kind, uri):
# kind = 'url' or 'mail'
if kind == 'url' and uri.startswith('file://'):
launch_file_manager(uri)
return
if kind in ('mail', 'sth_at_sth') and not uri.startswith('mailto:'):
uri = 'mailto:' + uri
if kind == 'url' and uri.startswith('www.'):
uri = 'http://' + uri
if not app.config.get('autodetect_browser_mailer'):
if kind == 'url':
command = app.config.get('custombrowser')
elif kind in ('mail', 'sth_at_sth'):
command = app.config.get('custommailapp')
if command == '': # if no app is configured
return
command = build_command(command, uri)
try:
exec_command(command)
except Exception:
pass
else:
webbrowser.open(uri)
def launch_file_manager(path_to_open):
if os.name == 'nt':
try:
@ -1502,3 +1476,92 @@ def delay_execution(milliseconds):
milliseconds, timeout_wrapper)
return func_wrapper
return delay_execution_decorator
def parse_uri(uri):
if uri.startswith('xmpp:'):
uri = uri[5:]
if '?' in uri:
jid, action = uri.split('?')
try:
return URI(type=URIType.XMPP,
action=URIAction(action),
data=jid)
except ValueError:
# Unknown action
pass
return URI(type=URIType.XMPP, action=URIAction.MESSAGE, data=uri)
if uri.startswith('mailto:'):
uri = uri[7:]
return URI(type=URIType.MAIL, data=uri)
if app.interface.sth_at_sth_dot_sth_re.match(uri):
return URI(type=URIType.AT, data=uri)
if uri.startswith('geo:'):
location = uri[4:]
lat, _, lon = location.partition(',')
if not lon:
return URI(type=URIType.UNKNOWN, data=uri)
uri = ('https://www.openstreetmap.org/?'
'mlat=%s&mlon=%s&zoom=16') % (lat, lon)
return URI(type=URIType.GEO, data=uri)
if uri.startswith('file://'):
return URI(type=URIType.FILE, data=uri)
return URI(type=URIType.WEB, data=uri)
def open_uri(uri, account=None):
if not isinstance(uri, URI):
uri = parse_uri(uri)
if uri.type == URIType.FILE:
launch_file_manager(uri.data)
elif uri.type == URIType.MAIL:
uri = 'mailto:%s' % uri.data
if not app.config.get('autodetect_browser_mailer'):
open_uri_with_custom('custommailapp', 'mailto:%s' % uri)
else:
webbrowser.open(uri)
elif uri.type in (URIType.WEB, URIType.GEO):
if not app.config.get('autodetect_browser_mailer'):
open_uri_with_custom('custombrowser', uri.data)
else:
webbrowser.open(uri.data)
elif uri.type == URIType.AT:
app.interface.new_chat_from_jid(account, uri.data)
elif uri.type == URIType.XMPP:
if account is None:
log.warning('Account must be specified to open XMPP uri')
return
if uri.action == URIAction.JOIN:
app.interface.join_gc_minimal(account, uri.data)
elif uri.action == URIAction.MESSAGE:
app.interface.new_chat_from_jid(account, uri.data)
else:
log.warning('Cant open URI: %s', uri)
else:
log.warning('Cant open URI: %s', uri)
def open_uri_with_custom(config_app, uri):
command = app.config.get(config_app)
if not command:
log.warning('No custom application set')
return
command = build_command(command, uri)
try:
exec_command(command)
except Exception:
pass

18
gajim/common/structs.py Normal file
View File

@ -0,0 +1,18 @@
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from collections import namedtuple
URI = namedtuple('URI', 'type action data')
URI.__new__.__defaults__ = (None, None) # type: ignore

View File

@ -518,7 +518,7 @@ class ConversationTextview(GObject.GObject):
"""
Basically it filters out the widget instance
"""
helpers.launch_browser_mailer('url', link)
helpers.open_uri(link)
def on_textview_populate_popup(self, textview, menu):
"""

View File

@ -55,7 +55,7 @@ from gajim.common.caps_cache import muc_caps_cache
from gajim.common import events
from gajim.common import app
from gajim.common import helpers
from gajim.common.helpers import launch_browser_mailer
from gajim.common.helpers import open_uri
from gajim.common.helpers import AdditionalDataDict
from gajim.common import ged
from gajim.common.i18n import _
@ -3106,5 +3106,5 @@ class SubjectPopover(Gtk.Popover):
def _on_activate_link(_label, uri):
# We have to use this, because the default GTK handler
# is not cross-platform compatible
launch_browser_mailer(None, uri)
open_uri(uri)
return Gdk.EVENT_STOP

View File

@ -19,7 +19,7 @@ from gi.repository import Gtk
from gi.repository import GObject
from gajim.common import app
from gajim.common.helpers import launch_browser_mailer
from gajim.common.helpers import open_uri
from gajim.common.i18n import _
from gajim.common.const import DEVS_CURRENT
from gajim.common.const import DEVS_PAST
@ -75,7 +75,7 @@ class AboutDialog(Gtk.AboutDialog):
def _on_activate_link(_label, uri):
# We have to use this, because the default GTK handler
# is not cross-platform compatible
launch_browser_mailer(None, uri)
open_uri(uri)
return Gdk.EVENT_STOP
def _connect_link_handler(self, parent):

View File

@ -21,7 +21,7 @@ from gajim.gtkgui_helpers import scale_pixbuf_from_data
from gajim.common import app
from gajim.common.i18n import _
from gajim.common.helpers import launch_browser_mailer
from gajim.common.helpers import open_uri
from gajim.common.modules.dataforms import extend_form
from gajim.gtk.util import MultiLineLabel
@ -588,9 +588,7 @@ class FakeDataFormWidget(Gtk.ScrolledWindow):
button = Gtk.Button(label='Register')
button.set_halign(Gtk.Align.CENTER)
button.get_style_context().add_class('suggested-action')
button.connect('clicked',
lambda *args: launch_browser_mailer('url',
redirect_url))
button.connect('clicked', lambda *args: open_uri(redirect_url))
self._grid.attach(button, 0, self._row_count, 2, 1)
else:
self._add_fields()

View File

@ -49,6 +49,8 @@ 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
@ -877,8 +879,8 @@ class HtmlTextView(Gtk.TextView):
self._cursor_changed = False
return False
def show_context_menu(self, _event, kind, text):
menu = get_conv_context_menu(self.account, kind, text)
def show_context_menu(self, uri):
menu = get_conv_context_menu(self.account, uri)
if menu is None:
return
@ -904,53 +906,21 @@ class HtmlTextView(Gtk.TextView):
# Detect XHTML-IM link
word = getattr(texttag, 'href', None)
if word:
if word.startswith('xmpp'):
kind = 'xmpp'
elif word.startswith('mailto:'):
kind = 'mail'
elif app.interface.sth_at_sth_dot_sth_re.match(word):
# it's a JID or mail
kind = 'sth_at_sth'
else:
if not word:
word = self.get_buffer().get_text(begin_iter, end_iter, True)
uri = parse_uri(word)
if event.button.button == 3: # right click
self.show_context_menu(event, kind, word)
self.show_context_menu(uri)
return True
self.plugin_modified = False
app.plugin_manager.extension_point(
'hyperlink_handler', word, kind, self,
self.get_toplevel())
'hyperlink_handler', uri, self, self.get_toplevel())
if self.plugin_modified:
return
# we launch the correct application
if kind == 'xmpp':
word = word[5:]
if '?' in word:
(jid, action) = word.split('?')
if action == 'join':
app.interface.join_gc_minimal(self.account, jid)
else:
app.interface.new_chat_from_jid(self.account, jid)
else:
app.interface.new_chat_from_jid(self.account, word)
# handle geo:-URIs
elif word[:4] == 'geo:':
location = word[4:]
lat, _, lon = location.partition(',')
if lon == '':
return
uri = 'https://www.openstreetmap.org/?' \
'mlat=%(lat)s&mlon=%(lon)s&zoom=16' % \
{'lat': lat, 'lon': lon}
helpers.launch_browser_mailer(kind, uri)
else:
# other URIs
helpers.launch_browser_mailer(kind, word)
open_uri(uri, account=self.account)
def display_html(self, html, textview, conv_textview, iter_=None):
buffer_ = self.get_buffer()

View File

@ -1957,10 +1957,6 @@ class Interface:
self.systray_enabled = False
self.systray.hide_icon()
@staticmethod
def on_launch_browser_mailer(widget, url, kind):
helpers.launch_browser_mailer(kind, url)
def process_connections(self):
"""
Called each foo (200) milliseconds. Check for idlequeue timeouts
@ -2381,11 +2377,6 @@ class Interface:
app.config.get_per('accounts', account, 'active'):
app.connections[account] = Connection(account)
# gtk hooks
# Gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
# Gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
# Gtk.link_button_set_uri_hook(self.on_launch_browser_mailer, 'url')
self.instances = {}
for a in app.connections:

View File

@ -25,6 +25,8 @@ from gajim.common import app
from gajim.common import helpers
from gajim.common.i18n import ngettext
from gajim.common.i18n import _
from gajim.common.const import URIType
from gajim.common.const import URIAction
from gajim.gtk.util import get_builder
@ -918,9 +920,9 @@ def get_encryption_menu(control_id, type_id, zeroconf=False):
return menu
def get_conv_context_menu(account, kind, text):
if kind == 'xmpp':
if '?join' in text:
def get_conv_context_menu(account, uri):
if uri.type == URIType.XMPP:
if uri.action == URIAction.JOIN:
context_menu = [
('copy-text', _('Copy JID')),
('-join-groupchat', _('Join Groupchat')),
@ -932,19 +934,25 @@ def get_conv_context_menu(account, kind, text):
('-add-contact', _('Add to Roster…')),
]
elif kind == 'url':
elif uri.type == URIType.WEB:
context_menu = [
('copy-text', _('Copy Link Location')),
('open-link', _('Open Link in Browser')),
]
elif kind == 'mail':
elif uri.type == URIType.MAIL:
context_menu = [
('copy-text', _('Copy Email Address')),
('open-link', _('Open Email Composer')),
]
elif kind == 'sth_at_sth':
elif uri.type == URIType.GEO:
context_menu = [
('copy-text', _('Copy Location')),
('open-link', _('Show Location')),
]
elif uri.type == URIType.AT:
context_menu = [
('copy-text', _('Copy JID/Email')),
('open-link', _('Open Email Composer')),
@ -953,6 +961,7 @@ def get_conv_context_menu(account, kind, text):
('-add-contact', _('Add to Roster…')),
]
else:
log.warning('No handler for URI type: %s', uri)
return
menu = Gtk.Menu()
@ -962,19 +971,15 @@ def get_conv_context_menu(account, kind, text):
menuitem.set_label(label)
if action.startswith('-'):
text = text.replace('xmpp:', '')
text = text.split('?')[0]
action = 'app.%s%s' % (account, action)
else:
action = 'app.%s' % action
menuitem.set_action_name(action)
if action == 'app.open-link':
value = GLib.Variant.new_strv([kind, text])
elif action == 'app.copy-text':
value = GLib.Variant.new_string(text)
if action in ('app.open-link', 'app.copy-text'):
value = GLib.Variant.new_string(uri.data)
else:
value = GLib.Variant.new_strv([account, text])
value = GLib.Variant.new_strv([account, uri.data])
menuitem.set_action_target_value(value)
menuitem.show()
menu.append(menuitem)

View File

@ -32,7 +32,7 @@ from gi.repository import Gdk
from gajim.common import app
from gajim.common import configpaths
from gajim.common.exceptions import PluginsystemError
from gajim.common.helpers import launch_browser_mailer
from gajim.common.helpers import open_uri
from gajim.plugins.helpers import log_calls
from gajim.plugins.helpers import GajimPluginActivateException
@ -250,7 +250,7 @@ class PluginsWindow:
@log_calls('PluginsWindow')
def on_install_plugin_button_clicked(self, widget):
if app.is_flatpak():
launch_browser_mailer('url', 'https://dev.gajim.org/gajim/gajim/wikis/help/flathub')
open_uri('https://dev.gajim.org/gajim/gajim/wikis/help/flathub')
return
def show_warn_dialog():