[epiphany] Add Chrome/Chromium password import



commit b8e2dff86d84dfff6d97fea3975c3b26ca1b05f9
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Thu Apr 30 11:17:33 2020 +0200

    Add Chrome/Chromium password import
    
    Fixes: https://gitlab.gnome.org/GNOME/epiphany/-/issues/424

 lib/sync/ephy-password-import.c        | 280 +++++++++++++++++++++++++++++++++
 lib/sync/ephy-password-import.h        |  33 ++++
 lib/sync/ephy-password-manager.c       |  30 ++++
 lib/sync/ephy-password-manager.h       |   6 +
 lib/sync/meson.build                   |   1 +
 src/ephy-header-bar.c                  |   2 +
 src/ephy-shell.c                       |  13 ++
 src/resources/gtk/page-menu-popover.ui |  17 ++
 src/window-commands.c                  | 175 +++++++++++++++++++++
 src/window-commands.h                  |   3 +
 10 files changed, 560 insertions(+)
---
diff --git a/lib/sync/ephy-password-import.c b/lib/sync/ephy-password-import.c
new file mode 100644
index 000000000..591a7e289
--- /dev/null
+++ b/lib/sync/ephy-password-import.c
@@ -0,0 +1,280 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2020 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-password-import.h"
+#include "ephy-password-manager.h"
+#include "ephy-sqlite-connection.h"
+#include "ephy-uri-helpers.h"
+
+#include <glib/gi18n.h>
+
+#include <nettle/pbkdf2.h>
+#include <nettle/aes.h>
+#include <nettle/cbc.h>
+
+#define PASSWORDS_IMPORT_ERROR passwords_import_error_quark ()
+#define SECRET_SCHEMA  libsecret_get_schema ()
+
+GQuark passwords_import_error_quark (void);
+G_DEFINE_QUARK (ephy - passwords - import - error - quark, passwords_import_error)
+
+typedef enum {
+  PASSWORDS_IMPORT_ERROR_PASSWORDS = 1001
+} PasswordsImportErrorCode;
+
+const SecretSchema *libsecret_get_schema (void)
+{
+  static const SecretSchema the_schema = {
+    "chrome_libsecret_os_crypt_password_v2",
+    SECRET_SCHEMA_DONT_MATCH_NAME,
+    {
+      {"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
+      {NULL, SECRET_SCHEMA_ATTRIBUTE_STRING},
+    }
+  };
+
+  return &the_schema;
+}
+
+static char *
+get_libsecret_phrase (ChromeType type)
+{
+  g_autoptr (GError) error = NULL;
+  char *phrase;
+
+  if (type == CHROME)
+    phrase = secret_password_lookup_sync (SECRET_SCHEMA, NULL, &error, "application", "chrome", NULL);
+  else if (type == CHROMIUM)
+    phrase = secret_password_lookup_sync (SECRET_SCHEMA, NULL, &error, "application", "chromium", NULL);
+  else
+    return NULL;
+
+  if (error) {
+    g_warning ("Could not read secret phrase: %s\n", error->message);
+
+    return NULL;
+  }
+
+  return phrase;
+}
+
+static char *
+decrypt (unsigned char *password,
+         int            password_length,
+         char          *phrase)
+{
+  struct CBC_CTX (struct aes128_ctx, AES_BLOCK_SIZE) aes;
+  unsigned char iv[16] = {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
0x20, 0x20, 0x20};
+  unsigned char salt[9] = {'s', 'a', 'l', 't', 'y', 's', 'a', 'l', 't'};
+  unsigned char key[16];
+  char *out;
+
+  pbkdf2_hmac_sha1 (strlen (phrase), (unsigned char *)phrase, 1, sizeof (salt), salt, sizeof (key), key);
+
+  out = g_malloc0 (password_length + 1);
+  aes128_set_decrypt_key (&aes.ctx, key);
+  CBC_SET_IV (&aes, iv);
+  CBC_DECRYPT (&aes, aes128_decrypt, password_length, (unsigned char *)out, password);
+
+  /* Remove padding */
+  for (int i = 0; i < password_length; i++) {
+    if (!g_ascii_isprint (out[i]))
+      out[i] = '\0';
+  }
+
+  return out;
+}
+
+gboolean
+ephy_password_import_from_chrome (EphyPasswordManager  *manager,
+                                  ChromeType            type,
+                                  GError              **error)
+{
+  g_autoptr (EphySQLiteConnection) connection = NULL;
+  g_autoptr (EphySQLiteStatement) statement = NULL;
+  g_autoptr (GError) my_error = NULL;
+  g_autofree char *secret_phrase = NULL;
+  g_autofree char *filename = NULL;
+  const char *statement_str = "SELECT origin_url, action_url, username_element, username_value, 
password_element, password_value FROM logins WHERE blacklisted_by_user = 0";
+
+  if (type == CHROME)
+    filename = g_build_filename (g_get_user_config_dir (), "google-chrome", "Default", "Login Data", NULL);
+  else if (type == CHROMIUM)
+    filename = g_build_filename (g_get_user_config_dir (), "chromium", "Default", "Login Data", NULL);
+  else
+    return FALSE;
+
+
+  connection = ephy_sqlite_connection_new (EPHY_SQLITE_CONNECTION_MODE_MEMORY, filename);
+  if (!connection) {
+    g_set_error (error,
+                 PASSWORDS_IMPORT_ERROR,
+                 PASSWORDS_IMPORT_ERROR_PASSWORDS,
+                 _("Cannot create SQLite connection. Close browser and try again."));
+    return FALSE;
+  }
+
+  if (!ephy_sqlite_connection_open (connection, &my_error)) {
+    g_warning ("Error during opening connection: %s", my_error->message);
+    g_set_error (error,
+                 PASSWORDS_IMPORT_ERROR,
+                 PASSWORDS_IMPORT_ERROR_PASSWORDS,
+                 _("Browser password database could not be opened. Close browser and try again."));
+    return FALSE;
+  }
+
+  statement = ephy_sqlite_connection_create_statement (connection, statement_str, &my_error);
+  if (my_error) {
+    g_warning ("Could not build password query statement: %s", my_error->message);
+    g_set_error (error,
+                 PASSWORDS_IMPORT_ERROR,
+                 PASSWORDS_IMPORT_ERROR_PASSWORDS,
+                 _("Browser password database could not be opened. Close browser and try again."));
+
+    ephy_sqlite_connection_close (connection);
+    return FALSE;
+  }
+
+  while (ephy_sqlite_statement_step (statement, &my_error)) {
+    const char *origin = ephy_sqlite_statement_get_column_as_string (statement, 0);
+    const char *target_origin = ephy_sqlite_statement_get_column_as_string (statement, 1);
+    const char *username_field = ephy_sqlite_statement_get_column_as_string (statement, 2);
+    const char *username = ephy_sqlite_statement_get_column_as_string (statement, 3);
+    const char *password_field = ephy_sqlite_statement_get_column_as_string (statement, 4);
+    const void *password = ephy_sqlite_statement_get_column_as_blob (statement, 5);
+    int password_size = ephy_sqlite_statement_get_column_size (statement, 5);
+    g_autofree char *decrypted_password = NULL;
+    g_autofree char *secure_origin = NULL;
+    g_autofree char *secure_target_origin = NULL;
+    gboolean exists;
+
+    /* Skip unsupported protocols */
+    if (!g_str_has_prefix (origin, "http") && !g_str_has_prefix (origin, "https"))
+      continue;
+
+    if (!password)
+      continue;
+
+    if (!secret_phrase) {
+      if (memcmp (password, "v11", 3) == 0) {
+        /* V11: System based password manager, we only support libsecret */
+        secret_phrase = get_libsecret_phrase (type);
+      } else if (strncmp (password, "v10", 3) == 0) {
+        /* V10: Browser based master key: peanuts */
+        secret_phrase = g_strdup ("peanuts");
+      }
+
+      if (!secret_phrase)
+        continue;
+    }
+
+    decrypted_password = decrypt ((unsigned char *)(password) + 3, password_size - 3, secret_phrase);
+    secure_origin = ephy_uri_to_security_origin (origin);
+
+    secure_target_origin = ephy_uri_to_security_origin (target_origin);
+
+    if (!secure_target_origin)
+      secure_target_origin = g_strdup (secure_origin);
+
+    exists = ephy_password_manager_find (manager,
+                                         secure_origin,
+                                         secure_target_origin,
+                                         username,
+                                         username_field,
+                                         password_field);
+
+    ephy_password_manager_save (manager,
+                                secure_origin,
+                                secure_target_origin,
+                                username,
+                                decrypted_password,
+                                username_field,
+                                password_field,
+                                !exists);
+  }
+
+  ephy_sqlite_connection_close (connection);
+
+  return TRUE;
+}
+
+typedef struct {
+  ChromeType type;
+  EphyPasswordManager *manager;
+} PasswordImportChromeData;
+
+static void
+ephy_password_import_from_chrome_data_free (PasswordImportChromeData *data)
+{
+  g_object_unref (data->manager);
+
+  g_free (data);
+}
+
+static void
+ephy_password_import_from_chrome_thread_cb (GTask        *task,
+                                            gpointer      source_object,
+                                            gpointer      task_data,
+                                            GCancellable *cancellable)
+{
+  PasswordImportChromeData *data = task_data;
+  g_autoptr (GError) error = NULL;
+  gboolean retval;
+
+  retval = ephy_password_import_from_chrome (data->manager, data->type, &error);
+
+  g_task_return_boolean (task, retval);
+}
+
+void
+ephy_password_import_from_chrome_async (EphyPasswordManager *manager,
+                                        ChromeType           type,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  g_autoptr (GTask) task = NULL;
+  PasswordImportChromeData *data = NULL;
+
+  g_assert (manager);
+
+  task = g_task_new (NULL, NULL, callback, user_data);
+  g_task_set_source_tag (task, ephy_password_import_from_chrome_async);
+
+  data = g_new0 (PasswordImportChromeData, 1);
+  data->type = type;
+  data->manager = g_object_ref (manager);
+
+  g_task_set_task_data (task, data, (void *)ephy_password_import_from_chrome_data_free);
+
+  g_task_run_in_thread (task, ephy_password_import_from_chrome_thread_cb);
+}
+
+gboolean
+ephy_password_import_from_chrome_finish (GObject       *object,
+                                         GAsyncResult  *result,
+                                         GError       **error)
+{
+  g_assert (g_task_is_valid (result, object));
+  g_assert (error && !*error);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/lib/sync/ephy-password-import.h b/lib/sync/ephy-password-import.h
new file mode 100644
index 000000000..e85e1b48a
--- /dev/null
+++ b/lib/sync/ephy-password-import.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 20120 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib.h>
+
+#include "ephy-password-manager.h"
+
+typedef enum {
+  CHROME,
+  CHROMIUM
+} ChromeType;
+
+void ephy_password_import_from_chrome_async (EphyPasswordManager *manager, ChromeType type, 
GAsyncReadyCallback callback, gpointer user_data);
+gboolean ephy_password_import_from_chrome_finish (GObject *source_object, GAsyncResult *result, GError 
**error);
diff --git a/lib/sync/ephy-password-manager.c b/lib/sync/ephy-password-manager.c
index 681dd61f1..381bc5267 100644
--- a/lib/sync/ephy-password-manager.c
+++ b/lib/sync/ephy-password-manager.c
@@ -683,6 +683,36 @@ ephy_password_manager_query (EphyPasswordManager              *self,
   g_hash_table_unref (attributes);
 }
 
+gboolean
+ephy_password_manager_find (EphyPasswordManager *self,
+                            const char          *origin,
+                            const char          *target_origin,
+                            const char          *username,
+                            const char          *username_field,
+                            const char          *password_field)
+{
+  GHashTable *attributes;
+  g_autoptr (GList) list = NULL;
+
+  g_assert (EPHY_IS_PASSWORD_MANAGER (self));
+
+  LOG ("Querying password records for (%s, %s, %s, %s)",
+       origin, username, username_field, password_field);
+
+  attributes = get_attributes_table (NULL, origin, target_origin, username,
+                                     username_field, password_field, -1);
+
+  list = secret_password_searchv_sync (EPHY_FORM_PASSWORD_SCHEMA,
+                                       attributes,
+                                       SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
+                                       NULL,
+                                       NULL);
+
+  g_hash_table_unref (attributes);
+
+  return list != NULL;
+}
+
 static void
 secret_password_clear_cb (GObject      *source_object,
                           GAsyncResult *result,
diff --git a/lib/sync/ephy-password-manager.h b/lib/sync/ephy-password-manager.h
index 9ec1da65c..22e03affb 100644
--- a/lib/sync/ephy-password-manager.h
+++ b/lib/sync/ephy-password-manager.h
@@ -66,6 +66,12 @@ void                 ephy_password_manager_query                    (EphyPasswor
                                                                      const char                       
*password_field,
                                                                      EphyPasswordManagerQueryCallback  
callback,
                                                                      gpointer                          
user_data);
+gboolean             ephy_password_manager_find                     (EphyPasswordManager              *self,
+                                                                     const char                       
*origin,
+                                                                     const char                       
*target_origin,
+                                                                     const char                       
*username,
+                                                                     const char                       
*username_field,
+                                                                     const char                       
*password_field);
 void                 ephy_password_manager_forget                    (EphyPasswordManager *self,
                                                                       const char          *id);
 void                 ephy_password_manager_forget_all                (EphyPasswordManager *self);
diff --git a/lib/sync/meson.build b/lib/sync/meson.build
index 73a0bfa1d..c34cba340 100644
--- a/lib/sync/meson.build
+++ b/lib/sync/meson.build
@@ -4,6 +4,7 @@ libephysync_sources = [
   'ephy-history-record.c',
   'ephy-open-tabs-manager.c',
   'ephy-open-tabs-record.c',
+  'ephy-password-import.c',
   'ephy-password-manager.c',
   'ephy-password-record.c',
   'ephy-sync-crypto.c',
diff --git a/src/ephy-header-bar.c b/src/ephy-header-bar.c
index 758c9a8bb..1012310d4 100644
--- a/src/ephy-header-bar.c
+++ b/src/ephy-header-bar.c
@@ -275,6 +275,8 @@ ephy_header_bar_constructed (GObject *object)
     gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "override-text-encoding-button")));
     gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "keyboard-shortcuts-button")));
     gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "help-button")));
+    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "passwords-separator")));
+    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "import-passwords-button")));
   } else if (ephy_is_running_inside_flatpak ()) {
     gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "save-as-application-separator")));
     gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "save-as-application-button")));
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index 281be67c8..7e5831734 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -207,6 +207,18 @@ export_bookmarks (GSimpleAction *action,
   window_cmd_export_bookmarks (NULL, NULL, EPHY_WINDOW (window));
 }
 
+static void
+import_passwords (GSimpleAction *action,
+                  GVariant      *parameter,
+                  gpointer       user_data)
+{
+  GtkWindow *window;
+
+  window = gtk_application_get_active_window (GTK_APPLICATION (ephy_shell));
+
+  window_cmd_import_passwords (NULL, NULL, EPHY_WINDOW (window));
+}
+
 static void
 show_history (GSimpleAction *action,
               GVariant      *parameter,
@@ -325,6 +337,7 @@ static GActionEntry app_entries[] = {
   { "new-incognito", new_incognito_window, NULL, NULL, NULL },
   { "import-bookmarks", import_bookmarks, NULL, NULL, NULL },
   { "export-bookmarks", export_bookmarks, NULL, NULL, NULL },
+  { "import-passwords", import_passwords, NULL, NULL, NULL },
   { "history", show_history, NULL, NULL, NULL },
   { "preferences", show_preferences, NULL, NULL, NULL },
   { "shortcuts", show_shortcuts, NULL, NULL, NULL },
diff --git a/src/resources/gtk/page-menu-popover.ui b/src/resources/gtk/page-menu-popover.ui
index 4100654e3..e6293f1bc 100644
--- a/src/resources/gtk/page-menu-popover.ui
+++ b/src/resources/gtk/page-menu-popover.ui
@@ -233,6 +233,23 @@
               </object>
             </child>
             <!-- FRAGILE: These buttons are manually removed for app mode in ephy-header-bar.c. -->
+            <child>
+              <object class="GtkSeparator" id="passwords-separator">
+                <property name="orientation">horizontal</property>
+                <property name="margin-top">6</property>
+                <property name="margin-bottom">6</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkModelButton" id="import-passwords-button">
+                <property name="can_focus">True</property>
+                <property name="text" translatable="yes">I_mport Passwords…</property>
+                <property name="action-name">app.import-passwords</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <!-- FRAGILE: These buttons are manually removed for app mode in ephy-header-bar.c. -->
             <child>
               <object class="GtkSeparator" id="save-as-application-separator">
                 <property name="orientation">horizontal</property>
diff --git a/src/window-commands.c b/src/window-commands.c
index c134367f7..8221a66c6 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -47,6 +47,7 @@
 #include "ephy-link.h"
 #include "ephy-location-entry.h"
 #include "ephy-notebook.h"
+#include "ephy-password-import.h"
 #include "ephy-prefs.h"
 #include "ephy-session.h"
 #include "ephy-settings.h"
@@ -666,6 +667,180 @@ window_cmd_export_bookmarks (GSimpleAction *action,
   g_object_unref (dialog);
 }
 
+static gboolean
+chrome_passwords_exists (void)
+{
+  g_autofree char *filename = NULL;
+
+  filename = g_build_filename (g_get_user_config_dir (), "google-chrome", "Default", "Login Data", NULL);
+
+  return g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR);
+}
+
+static gboolean
+chromium_passwords_exists (void)
+{
+  g_autofree char *filename = NULL;
+
+  filename = g_build_filename (g_get_user_config_dir (), "chromium", "Default", "Login Data", NULL);
+
+  return g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR);
+}
+
+static struct import_option import_passwords_options[] = {
+  { N_("Chrome"), IMPORT_TYPE_IMPORT, chrome_passwords_exists },
+  { N_("Chromium"), IMPORT_TYPE_IMPORT, chromium_passwords_exists }
+};
+
+static GtkTreeModel *
+create_import_passwords_tree_model (void)
+{
+  enum {
+    TEXT_COL
+  };
+  GtkListStore *list_store;
+  GtkTreeIter iter;
+  int i;
+
+  list_store = gtk_list_store_new (1, G_TYPE_STRING);
+  for (i = G_N_ELEMENTS (import_passwords_options) - 1; i >= 0; i--) {
+    if (import_passwords_options[i].exists && !import_passwords_options[i].exists ())
+      continue;
+
+    gtk_list_store_prepend (list_store, &iter);
+    gtk_list_store_set (list_store, &iter,
+                        TEXT_COL, _(import_passwords_options[i].name),
+                        -1);
+  }
+
+  return GTK_TREE_MODEL (list_store);
+}
+
+static void
+dialog_password_import_cb (GObject      *source_object,
+                           GAsyncResult *res,
+                           gpointer      user_data)
+{
+  g_autoptr (GError) error = NULL;
+  gboolean imported = ephy_password_import_from_chrome_finish (source_object, res, &error);
+  GtkWidget *import_info_dialog;
+
+  import_info_dialog = gtk_message_dialog_new (NULL,
+                                               GTK_DIALOG_MODAL,
+                                               imported ? GTK_MESSAGE_INFO : GTK_MESSAGE_WARNING,
+                                               GTK_BUTTONS_OK,
+                                               "%s",
+                                               imported ? _("Passwords successfully imported!")
+                                                        : error->message);
+  gtk_dialog_run (GTK_DIALOG (import_info_dialog));
+  gtk_widget_destroy (import_info_dialog);
+}
+
+static void
+dialog_passwords_import_cb (GtkDialog   *dialog,
+                            int          response,
+                            GtkComboBox *combo_box)
+{
+  if (response == GTK_RESPONSE_OK) {
+    EphyPasswordManager *manager;
+    int active;
+
+    manager = ephy_embed_shell_get_password_manager (EPHY_EMBED_SHELL (ephy_shell_get_default ()));
+    active = gtk_combo_box_get_active (combo_box);
+
+    switch (active) {
+      case 0:
+        ephy_password_import_from_chrome_async (manager, CHROME, dialog_password_import_cb, NULL);
+        break;
+      case 1:
+        ephy_password_import_from_chrome_async (manager, CHROMIUM, dialog_password_import_cb, NULL);
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+  }
+
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+passwords_combo_box_changed_cb (GtkComboBox *combo_box,
+                                GtkButton   *button)
+{
+  int active;
+
+  g_assert (GTK_IS_COMBO_BOX (combo_box));
+  g_assert (GTK_IS_BUTTON (button));
+
+  active = gtk_combo_box_get_active (combo_box);
+  if (import_passwords_options[active].type == IMPORT_TYPE_CHOOSE)
+    gtk_button_set_label (button, _("Ch_oose File"));
+  else if (import_passwords_options[active].type == IMPORT_TYPE_IMPORT)
+    gtk_button_set_label (button, _("I_mport"));
+}
+
+void
+window_cmd_import_passwords (GSimpleAction *action,
+                             GVariant      *parameter,
+                             gpointer       user_data)
+{
+  EphyWindow *window = EPHY_WINDOW (user_data);
+  GtkWidget *dialog;
+  GtkWidget *content_area;
+  GtkWidget *hbox;
+  GtkWidget *label;
+  GtkWidget *combo_box;
+  GtkTreeModel *tree_model;
+  GtkCellRenderer *cell_renderer;
+
+  dialog = hdy_dialog_new (GTK_WINDOW (window));
+  gtk_window_set_title (GTK_WINDOW (dialog), _("Import Passwords"));
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                          _("_Cancel"),
+                          GTK_RESPONSE_CANCEL,
+                          _("Ch_oose File"),
+                          GTK_RESPONSE_OK,
+                          NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+  gtk_widget_set_valign (content_area, GTK_ALIGN_CENTER);
+  gtk_widget_set_margin_start (content_area, 25);
+  gtk_widget_set_margin_end (content_area, 25);
+  gtk_container_set_border_width (GTK_CONTAINER (content_area), 5);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+
+  label = gtk_label_new (_("From:"));
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+  tree_model = create_import_passwords_tree_model ();
+  combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (tree_model));
+  g_object_unref (tree_model);
+
+  g_signal_connect (GTK_COMBO_BOX (combo_box), "changed",
+                    G_CALLBACK (passwords_combo_box_changed_cb),
+                    gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK));
+
+  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
+
+  cell_renderer = gtk_cell_renderer_text_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell_renderer, TRUE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell_renderer,
+                                  "text", 0, NULL);
+  gtk_box_pack_start (GTK_BOX (hbox), combo_box, TRUE, TRUE, 0);
+
+  gtk_container_add (GTK_CONTAINER (content_area), hbox);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (dialog_passwords_import_cb),
+                    GTK_COMBO_BOX (combo_box));
+
+  gtk_widget_show_all (dialog);
+}
+
+
 void
 window_cmd_show_history (GSimpleAction *action,
                          GVariant      *parameter,
diff --git a/src/window-commands.h b/src/window-commands.h
index 59d6af87f..c7f58cd35 100644
--- a/src/window-commands.h
+++ b/src/window-commands.h
@@ -236,5 +236,8 @@ void window_cmd_tabs_pin                        (GSimpleAction *action,
 void window_cmd_tabs_unpin                      (GSimpleAction *action,
                                                  GVariant      *parameter,
                                                  gpointer       user_data);
+void window_cmd_import_passwords                (GSimpleAction *action,
+                                                 GVariant      *parameter,
+                                                 gpointer       user_data);
 
 G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]