summaryrefslogtreecommitdiff
path: root/src/contacts-add-address-book-dialog.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/contacts-add-address-book-dialog.vala')
-rw-r--r--src/contacts-add-address-book-dialog.vala270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/contacts-add-address-book-dialog.vala b/src/contacts-add-address-book-dialog.vala
new file mode 100644
index 0000000..323fffb
--- /dev/null
+++ b/src/contacts-add-address-book-dialog.vala
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2022 Niels De Graef <nielsdegraef@gmail.com>
+ *
+ * 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 2 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/>.
+ */
+
+using Folks;
+
+[GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-add-address-book-dialog.ui")]
+public class Contacts.AddAddressBookDialog : Gtk.Dialog {
+
+ // The "scratch source" that we will end up saving
+ private E.Source e_source;
+
+ public AddressBookType address_book_type { get; construct set; }
+
+ [GtkChild]
+ private unowned Adw.EntryRow address_book_name_row;
+
+ [GtkChild]
+ private unowned Adw.EntryRow server_row;
+ [GtkChild]
+ private unowned Adw.EntryRow username_row;
+ [GtkChild]
+ private unowned Adw.PasswordEntryRow password_row;
+ [GtkChild]
+ private unowned Gtk.Button connect_button;
+
+ [GtkChild]
+ private unowned Gtk.Stack bottom_stack;
+ [GtkChild]
+ private unowned Gtk.Label error_label;
+
+ [GtkChild]
+ private unowned Adw.PreferencesGroup address_books_group;
+ private DiscoveredSourceRow? selected_source_row = null;
+
+ [GtkChild]
+ private unowned Gtk.Button add_button;
+ //XXX only make sensitive once selected-source-row is set
+
+ static construct {
+ install_action ("address-book.connect", null, (Gtk.WidgetActionActivateFunc) connect_action);
+ install_action ("address-book.save", null, (Gtk.WidgetActionActivateFunc) save_action);
+ }
+
+ construct {
+ // Initial widget state
+ this.use_header_bar = 1;
+
+ // Create a so-called scratch source
+ this.e_source = new E.Source (null, null);
+
+ // Set the backend name depending on the address book type
+ var ab_extension = (E.SourceAddressBook)
+ this.e_source.get_extension (E.SOURCE_EXTENSION_ADDRESS_BOOK);
+ ab_extension.set_backend_name (this.address_book_type.to_e_backend_name ());
+ }
+
+ public AddAddressBookDialog (Gtk.Window? parent_window) {
+ // NOTE: we only support CardDAV for now
+ Object (address_book_type: AddressBookType.CARDDAV,
+ transient_for: parent_window);
+ }
+
+ private void connect_action (string action_name, Variant? parameter) {
+ this.connect_button.sensitive = false;
+
+ connect_to_server.begin (null, (obj, res) => {
+ try {
+ connect_to_server.end (res);
+ } catch (Error e) {
+ warning ("Couldn't connect: %s[%d] %s", e.domain.to_string(), e.code, e.message);
+
+ // We can use ESoupSessionError to find out errors that match a
+ // specific HTTP code
+ if (e.domain == E.SoupSession.error_quark ()) {
+ if (e.code == 401) {
+ show_error (_("Error: Invalid username or password"));
+ } else {
+ show_error (_("Error: server returned HTTP %u").printf (e.code));
+ }
+ } else if (e.domain == UriError.quark ()) {
+ show_error (_("Error: Invalid URL"));
+ } else {
+ show_error (_("Error connecting to the server"));
+ }
+ } finally {
+ this.connect_button.sensitive = true;
+ }
+ });
+ }
+
+ private void save_action (string action_name, Variant? parameter) {
+ save.begin (null, (obj, res) => {
+ try {
+ save.end (res);
+ } catch (Error e) {
+ //XXX show something in the UI
+ warning ("Couldn't save: %s", e.message);
+ }
+ });
+ close ();
+ }
+
+ [GtkCallback]
+ private void on_address_book_name_row_notify_text (Object object, ParamSpec pspec) {
+ check_connect_button ();
+ }
+
+ [GtkCallback]
+ private void on_server_row_notify_text (Object object, ParamSpec pspec) {
+ check_connect_button ();
+ }
+
+ [GtkCallback]
+ private void on_username_row_notify_text (Object object, ParamSpec pspec) {
+ check_connect_button ();
+ }
+
+ [GtkCallback]
+ private void on_password_row_notify_text (Object object, ParamSpec pspec) {
+ check_connect_button ();
+ }
+
+ private void check_connect_button () {
+ // XXX check also if uri is valid
+ // XXX maybe set warning label?
+ this.connect_button.sensitive =
+ (this.username_row.text.strip () != "" &&
+ this.password_row.text != "" &&
+ this.server_row.text.strip () != "");
+ }
+
+ private void show_error (string label) {
+ this.error_label.label = label;
+ this.bottom_stack.visible_child_name = "error";
+ }
+
+ private async void connect_to_server (Cancellable? cancellable) throws Error {
+ // First of all try to parse the URI (if it fails, it throws an error)
+ var uri = Uri.parse (this.server_row.text, UriFlags.NONE);
+
+ // Do a basic check that we have a connection to E-D-S
+ //XXX maybe we can just create a registry here?
+ if (!ensure_eds_accounts (true)) {
+ warning ("Can't connect to evolution-data-server");
+ return;// XXX throw error
+ }
+
+ // The basic details
+ var ab_name = this.address_book_name_row.text;
+ debug ("Connecting to source '%s' (type: %s)", ab_name, this.address_book_type.to_string ());
+
+ if (ab_name.strip () != "")
+ this.e_source.display_name = ab_name;
+
+ // Get the webdav and auth extensions to set those
+ var webdav_ext = (E.SourceWebdav)
+ this.e_source.get_extension (E.SOURCE_EXTENSION_WEBDAV_BACKEND);
+ webdav_ext.uri = uri;
+
+ var auth_ext = (E.SourceAuthentication)
+ this.e_source.get_extension (E.SOURCE_EXTENSION_AUTHENTICATION);
+ auth_ext.user = this.username_row.text;
+
+ // Credentials
+ var credentials = new E.NamedParameters ();
+ credentials.set (E.SOURCE_CREDENTIAL_USERNAME, this.username_row.text);
+ credentials.set (E.SOURCE_CREDENTIAL_PASSWORD, this.password_row.text);
+
+ debug ("Discovering address books for '%s'", ab_name);
+ this.bottom_stack.visible_child_name = "loading";
+
+ // Discover which address books are available at the server
+ string cert_pem;
+ TlsCertificateFlags cert_errors;
+ SList<E.WebDAVDiscoveredSource> discovered_sources;
+ SList<string> discovered_email_addrs;
+ yield this.e_source.webdav_discover_sources (null,
+ E.WebDAVDiscoverSupports.CONTACTS,
+ credentials,
+ cancellable,
+ out cert_pem,
+ out cert_errors,
+ out discovered_sources,
+ out discovered_email_addrs);
+ debug ("Discovered %u address books", discovered_sources.length ());
+
+ if (discovered_sources.length () == 0) {
+ show_error (_("No address books found"));
+ return;
+ }
+
+ // Give the user the option to select which one to import
+ this.bottom_stack.visible_child_name = "address_books";
+ this.address_books_group.description = ngettext (
+ "Found %u address book to import",
+ "Found %u address books. Please select the address book you want to import",
+ discovered_sources.length ()).printf (discovered_sources.length ());
+ foreach (unowned var s in discovered_sources) {
+ debug ("SOURCE '%s' (%s): '%s'", s.display_name, s.href, s.description);
+
+ var source_row = new DiscoveredSourceRow (s);
+ this.address_books_group.add (source_row);
+ source_row.activated.connect ((row) => {
+ unowned var src_row = (DiscoveredSourceRow) row;
+ if (src_row == this.selected_source_row)
+ return;
+
+ this.selected_source_row.selected = false;
+ src_row.selected = true;
+ this.selected_source_row = src_row;
+ });
+ }
+ }
+
+ public async void save (Cancellable? cancellable) throws Error
+ requires (this.selected_source_row != null) {
+ // Note that eds_source_registry is guaranteed to be non-null due to
+ // ensure_eds_accounts()
+ //XXX maybe do create our own registry?
+ if (eds_source_registry == null)
+ error ("eds_source_registry is null");
+
+ var selected_source = this.selected_source_row.source;
+ if (selected_source.href != null && selected_source.href != "") {
+ var webdav_ext = (E.SourceWebdav)
+ this.e_source.get_extension (E.SOURCE_EXTENSION_WEBDAV_BACKEND);
+ var uri = Uri.parse (this.server_row.text, UriFlags.NONE);
+ webdav_ext.uri = uri;
+ }
+
+ debug ("Saving source '%s'", this.e_source.display_name);
+ yield eds_source_registry.commit_source (this.e_source, cancellable);
+ debug ("Saved source '%s'", this.e_source.display_name);
+ }
+
+ // Helper class to show a remote address book that can be imported
+ private class DiscoveredSourceRow : Adw.ActionRow {
+
+ public E.WebDAVDiscoveredSource source { get; construct set; }
+
+ public bool selected { get; set; default = false; }
+
+ public DiscoveredSourceRow (E.WebDAVDiscoveredSource source) {
+ Object (source: source);
+
+ this.title = source.display_name;
+ if (source.description != null && source.description != "")
+ this.subtitle = source.description;
+
+ var checkmark = new Gtk.Image.from_icon_name ("object-select-symbolic");
+ bind_property ("selected", checkmark, "visible", BindingFlags.SYNC_CREATE);
+ add_suffix (checkmark);
+ this.activatable_widget = checkmark;
+ }
+ }
+}