Commit 162b0c68 authored by Link Mauve's avatar Link Mauve
Browse files

Make /upload work in Flatpak

When /upload isn’t given an argument, it will instead open a file
chooser and block poezio until the user selected a file.  This will make
poezio timeout from all rooms until the user is done choosing a file,
but I didn’t find a good way to integrate GLib’s main loop with asyncio
for now, and this can be fixed in a latter commit.
parent 30c1dc78
Pipeline #4483 passed with stages
in 11 minutes and 56 seconds
......@@ -9,11 +9,14 @@ This plugin adds a command to the chat tabs.
.. glossary::
/upload
**Usage:** ``/upload <filename>``
**Usage:** ``/upload [filename]``
Uploads the <filename> file to the preferred HTTP File Upload
service (see XEP-0363) and fill the input with its URL.
If <filename> isn’t specified, use the FileChooser from
xdg-desktop-portal to ask the user which file to upload.
"""
......@@ -23,6 +26,7 @@ import asyncio
import traceback
from os.path import expanduser
from glob import glob
from concurrent.futures import ThreadPoolExecutor
from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound
......@@ -31,6 +35,12 @@ from poezio.core.structs import Completion
from poezio.decorators import command_args_parser
from poezio import tabs
try:
from gi.repository import Gio, GLib
from urllib.parse import urlparse, unquote
HAVE_GLIB = True
except ImportError:
HAVE_GLIB = False
class Plugin(BasePlugin):
dependencies = {'embed'}
......@@ -56,7 +66,13 @@ class Plugin(BasePlugin):
short='Upload a file',
completion=self.completion_filename)
async def upload(self, filename, encrypted=False) -> Optional[str]:
async def upload(self, filename: Optional[str], encrypted=False) -> Optional[str]:
if filename is None:
with ThreadPoolExecutor() as pool:
loop = asyncio.get_running_loop()
filename = await loop.run_in_executor(pool, self.open_file_xdg_desktop_portal)
if filename is None:
return None
try:
upload_file = self.core.xmpp['xep_0363'].upload_file
if encrypted:
......@@ -75,18 +91,17 @@ class Plugin(BasePlugin):
return None
return url
async def send_upload(self, filename, tab, encrypted=False):
async def send_upload(self, filename: Optional[str], tab, encrypted=False):
url = await self.upload(filename, encrypted)
if url is not None:
self.embed.embed_image_url(url, tab)
@command_args_parser.quoted(1)
@command_args_parser.quoted(0, 1)
def command_upload(self, args):
if args is None:
self.core.command.help('upload')
return
filename, = args
filename = expanduser(filename)
if args:
filename = expanduser(args[0])
else:
filename = None
tab = self.api.current_tab()
encrypted = self.core.xmpp['xep_0454'] and tab.e2e_encryption is not None
asyncio.create_task(self.send_upload(filename, tab, encrypted))
......@@ -96,3 +111,75 @@ class Plugin(BasePlugin):
txt = expanduser(the_input.get_text()[8:])
files = glob(txt + '*')
return Completion(the_input.auto_completion, files, quotify=False)
def open_file_xdg_desktop_portal(self):
'''
Use org.freedesktop.portal.FileChooser from xdg-desktop-portal to open a
file chooser dialog.
This method uses GDBus from GLib, and specifically runs its mainloop which
will block the entirety of poezio until it is done, which might cause us to
drop from rooms and such if the user isn’t quick enough at choosing the
file…
See https://flatpak.github.io/xdg-desktop-portal/portal-docs.html
'''
if not HAVE_GLIB:
self.api.information('GLib or Gio not available.', 'Error')
return None
def get_file(connection,
sender,
path,
interface,
signal,
params):
nonlocal return_path
# TODO: figure out how to raise an exception to the outside of the GLib
# loop.
if not isinstance(params, GLib.Variant):
loop.quit()
return
response_code, results = params.unpack()
if response_code != 0:
loop.quit()
return
uris = results['uris']
if len(uris) != 1:
loop.quit()
return
parsed_uri = urlparse(uris[0])
if parsed_uri.scheme != "file":
loop.quit()
return
return_path = unquote(parsed_uri.path)
loop.quit()
return_path = None
proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION,
Gio.DBusProxyFlags.NONE,
None,
'org.freedesktop.portal.Desktop',
'/org/freedesktop/portal/desktop',
'org.freedesktop.portal.FileChooser',
None)
try:
handle = proxy.OpenFile('(ssa{sv})', '', 'poezio', {
'accept_label': GLib.Variant('s', '_Upload'),
})
except GLib.Error as err:
self.api.information('Failed to query file selection portal: %s' % err, 'Error')
return None
conn = proxy.get_connection()
conn.signal_subscribe('org.freedesktop.portal.Desktop',
'org.freedesktop.portal.Request',
'Response',
handle,
None,
Gio.DBusSignalFlags.NO_MATCH_RULE,
get_file)
loop = GLib.MainLoop()
loop.run()
return return_path
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