Skip to content

Commit

Permalink
Re-Adding "Send by Email" (#1822)
Browse files Browse the repository at this point in the history
* Adding ComposeEmail Portal

* Added fix to avoid portal to crash

* Send by Mail: Support file descriptors

Thanks @Marukesu!

* Added link for clarification of bug in portal

* Fixed code style issues

Co-authored-by: Jeremy Wootten <jeremy@elementaryos.org>
  • Loading branch information
marbetschar and Jeremy Wootten authored Nov 2, 2021
1 parent b4b2f27 commit a3779bd
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 0 deletions.
1 change: 1 addition & 0 deletions plugins/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ subdir('pantheon-files-trash')
subdir('pantheon-files-ctags')
subdir('pantheon-files-cloud')
subdir('git')
subdir('send-by-email')
62 changes: 62 additions & 0 deletions plugins/send-by-email/Portal.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*-
* Copyright (c) 2021 elementary, Inc. (https://elementary.io)
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/

namespace Portal {
const string DBUS_DESKTOP_PATH = "/org/freedesktop/portal/desktop";
const string DBUS_DESKTOP_NAME = "org.freedesktop.portal.Desktop";
Email? email = null;

public static string generate_token () {
return "%s_%i".printf (
GLib.Application.get_default ().application_id.replace (".", "_"),
Random.int_range (0, int32.MAX)
);
}

[DBus (name = "org.freedesktop.portal.Email")]
interface Email : DBusProxy {
[DBus (name = "version")]
public abstract uint version { get; }

public static Email @get () throws IOError, DBusError {
if (email == null) {
var connection = GLib.Application.get_default ().get_dbus_connection ();
email = connection.get_proxy_sync<Email> (DBUS_DESKTOP_NAME, DBUS_DESKTOP_PATH);
}

return email;
}

[DBus (visible = false)]
public ObjectPath compose_email (string window_handle, HashTable<string, Variant> options, UnixFDList? attachments) throws Error {
var options_builder = new VariantBuilder (VariantType.VARDICT);
options.foreach ((key, val) => {
options_builder.add ("{sv}", key, val);
});

var response = call_with_unix_fd_list_sync (
"ComposeEmail",
new Variant ("(sa{sv})", window_handle, options_builder),
DBusCallFlags.NONE,
-1,
attachments
);

return (ObjectPath) response.get_child_value (0).get_string ();
}
}
}
17 changes: 17 additions & 0 deletions plugins/send-by-email/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
send_by_email_plugin_dir = join_paths(plugin_dir, 'core')

gdk_dep = [ dependency('gdk-x11-3.0'), dependency('gdk-wayland-3.0') ]

shared_module(
'pantheon-files-send-by-email',
'plugin.vala',
'Portal.vala',
dependencies: [pantheon_files_core_dep, gdk_dep],
install: true,
install_dir: send_by_email_plugin_dir
)

install_data(
'pantheon-files-send-by-email.plug',
install_dir: send_by_email_plugin_dir
)
3 changes: 3 additions & 0 deletions plugins/send-by-email/pantheon-files-send-by-email.plug
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[Plugin]
Name=Send by Email
File=libpantheon-files-send-by-email.so
146 changes: 146 additions & 0 deletions plugins/send-by-email/plugin.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright 2021 elementary, Inc. (https://elementary.io)
*
* This program 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; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/

public class Files.Plugins.SendByEmailMenuItem : Gtk.MenuItem {
private GLib.File[] files;

public SendByEmailMenuItem (GLib.File[] files) {
this.files = files;

label = _("Send by Email");
}

public override void activate () {
try {
var portal = Portal.Email.get ();

window_export.begin ((obj, res) => {
var options = new HashTable<string, Variant> (str_hash, str_equal);
options["handle_token"] = Portal.generate_token ();

var files_builder = new VariantBuilder (new VariantType ("ah"));
var file_descriptors = new UnixFDList ();
foreach (var file in files) {
var fd = Posix.open (file.get_path (), Posix.O_RDONLY | Posix.O_CLOEXEC);
if (fd == -1) {
warning ("send-by-mail: cannot open file: '%s'", file.get_path ());
continue;
}

try {
files_builder.add ("h", file_descriptors.append (fd));
} catch (Error e) {
warning ("send-by-mail: cannot append file descriptor: %s", e.message);
}
}

options["attachment_fds"] = files_builder.end ();

/** Even though the org.freedesktop.portal.Email portal specs
* claims that "all the keys in the options are are optional",
* the portal does not work if no "addresses" key is passed.
* This is a bug in the Gtk backend of the portal:
* https://github.com/flatpak/xdg-desktop-portal-gtk/issues/343
*/
if (portal.version > 2) {
options["addresses"] = new Variant ("as", null);
}

try {
var handle = window_export.end (res);
portal.compose_email (handle, options, file_descriptors);

} catch (Error e) {
warning (e.message);
}
});
} catch (Error e) {
warning (e.message);
}
}

private async string window_export () {
var window = get_toplevel ().get_window ();

if (window is Gdk.X11.Window) {
var xid = ((Gdk.X11.Window) window).get_xid ();
return "x11:%x".printf ((uint) xid);
} else if (window is Gdk.Wayland.Window) {
var handle = "wayland:";
((Gdk.Wayland.Window) window).export_handle ((w, h) => {
handle += h;
window_export.callback ();
});
yield;

if (handle != "wayland:") {
return handle;
}

return "";

} else {
warning ("Unknown windowing system, not exporting window");
return "";
}
}
}

public class Files.Plugins.SendByEmail : Files.Plugins.Base {

public override void context_menu (Gtk.Widget widget, List<Files.File> gof_files) {
var menu = widget as Gtk.Menu;

if (gof_files == null || gof_files.length () == 0) {
return;
}

var files = get_file_array (gof_files);
if (files != null && files.length > 0) {
add_menuitem (menu, new Gtk.SeparatorMenuItem ());
add_menuitem (menu, new SendByEmailMenuItem (files));
}
}

private void add_menuitem (Gtk.Menu menu, Gtk.MenuItem menu_item) {
menu.append (menu_item);
menu_item.show ();
plugins.menuitem_references.add (menu_item);
}

private static GLib.File[] get_file_array (List<Files.File> files) {
GLib.File[] file_array = new GLib.File[0];

foreach (unowned Files.File file in files) {
if (file.location != null && !file.is_directory && file.is_readable ()) {
if (file.location.get_uri_scheme () == "recent") {
file_array += GLib.File.new_for_uri (file.get_display_target_uri ());
} else {
file_array += file.location;
}
}
}

return file_array;
}
}

public Files.Plugins.Base module_init () {
return new Files.Plugins.SendByEmail ();
}

0 comments on commit a3779bd

Please sign in to comment.