Skip to content
Snippets Groups Projects
Select Git revision
  • 5613a0b6afd4be9635f1370647d09abe7d2f0200
  • master default protected
  • release/202106
  • release/202104
  • release/202101
  • release/202012
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • release/201811
  • release/201808
  • 1.0.0
  • 0.3.0
  • 0.2.1
  • 0.2.0
  • 0.1.0
26 results

accountcreationwizard.cpp

  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    accountcreationwizard.cpp 40.79 KiB
    /*
     *  Copyright (C) 2016-2019 Savoir-faire Linux Inc.
     *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
     *  Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.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 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.
     */
    
    // GTK+ related
    #include <glib/gi18n.h>
    #include <gtk/gtk.h>
    
    // LRC
    #include "api/newaccountmodel.h"
    
    // Jami Client
    #include "utils/models.h"
    #include "avatarmanipulation.h"
    #include "accountcreationwizard.h"
    #include "usernameregistrationbox.h"
    #include "utils/files.h"
    
    struct _AccountCreationWizard
    {
        GtkScrolledWindow parent;
    };
    
    struct _AccountCreationWizardClass
    {
        GtkScrolledWindowClass parent_class;
    };
    
    typedef struct _AccountCreationWizardPrivate AccountCreationWizardPrivate;
    
    enum class Mode {
        ADD_LOCAL,
        IMPORT_FROM_DEVICE,
        IMPORT_FROM_BACKUP,
        CONNECT_TO_MANAGER
    };
    
    struct _AccountCreationWizardPrivate
    {
        AccountInfoPointer const *accountInfo_ = nullptr;
        gchar* accountId;
        gchar* username;
        gchar* password;
        gchar* avatar;
    
        GtkWidget *stack_account_creation;
        gboolean username_available;
    
        /* choose_account_type_vbox */
        GtkWidget *choose_account_type_vbox;
        GtkWidget *choose_account_type_ring_logo;
        GtkWidget *button_new_account;
        GtkWidget *button_import_from_device;
        GtkWidget *button_import_from_backup;
        GtkWidget *button_show_advanced;
        GtkWidget *button_connect_account_manager;
        GtkWidget *button_new_sip_account;
    
        /* existing account */
        GtkWidget *existing_account;
        GtkWidget *existing_account_label;
        GtkWidget *row_archive;
        GtkWidget *row_pin;
        GtkWidget *button_pin_info;
        GtkWidget *row_pin_label;
        GtkWidget *button_import_from_next;
        GtkWidget *button_import_from_previous;
        GtkWidget *entry_existing_account_pin;
        GtkWidget *entry_existing_account_archive;
        GtkWidget *entry_existing_account_password;
        GtkWidget *retrieving_account;
    
        /* account creation */
        GtkWidget *account_creation;
        GtkWidget *entry_username;
        GtkWidget *entry_password;
        GtkWidget *entry_password_confirm;
        GtkWidget *label_password_error;
        GtkWidget *button_account_creation_next;
        GtkWidget *button_account_creation_previous;
        GtkWidget *box_avatarselection;
        GtkWidget *avatar_manipulation;
        GtkWidget *box_username_entry;
        GtkWidget *switch_register;
        GtkWidget *username_registration_box;
        GtkWidget *entry_display_name;
    
        /* generating_account_spinner */
        GtkWidget *vbox_generating_account_spinner;
    
        /* registering_username_spinner */
        GtkWidget *vbox_registering_username_spinner;
    
        /* backup */
        GtkWidget *info_backup;
        GtkWidget *button_never_show_again;
        GtkWidget *button_export_account;
        GtkWidget *button_skip_backup;
        GtkWidget *button_backup_info;
        GtkWidget *row_backup_label;
    
        /* error_view */
        GtkWidget *error_view;
        GtkWidget *label_error_view;
        GtkWidget *button_error_view_ok;
    
        /* connect to account_manager */
        GtkWidget *connect_to_account_manager;
        GtkWidget *entry_account_manager_username;
        GtkWidget *entry_account_manager_password;
        GtkWidget *entry_account_manager_uri;
        GtkWidget *button_account_manager_connect_previous;
        GtkWidget *button_account_manager_connect_next;
    
        lrc::api::AVModel* avModel_;
        lrc::api::NewAccountModel* accountModel_;
    
        Mode mode;
    
        GSettings *settings;
    };
    
    G_DEFINE_TYPE_WITH_PRIVATE(AccountCreationWizard, account_creation_wizard, GTK_TYPE_SCROLLED_WINDOW);
    
    #define ACCOUNT_CREATION_WIZARD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizardPrivate))
    
    /* signals */
    enum {
        ACCOUNT_CREATION_LOCK,
        ACCOUNT_CREATION_UNLOCK,
        ACCOUNT_CREATION_COMPLETED,
        LAST_SIGNAL
    };
    
    static guint account_creation_wizard_signals[LAST_SIGNAL] = { 0 };
    
    static void
    account_creation_wizard_dispose(GObject *object)
    {
        // make sure preview is stopped and destroyed
        account_creation_wizard_show_preview(ACCOUNT_CREATION_WIZARD(object), FALSE);
    
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(object);
        g_clear_object(&priv->settings);
    
        G_OBJECT_CLASS(account_creation_wizard_parent_class)->dispose(object);
    }
    
    static void
    account_creation_wizard_init(AccountCreationWizard *view)
    {
        gtk_widget_init_template(GTK_WIDGET(view));
    
        auto *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        priv->settings = g_settings_new_full(get_settings_schema(), NULL, NULL);
    
    
        g_settings_bind(priv->settings, "never-show-backup-again",
                        priv->button_never_show_again, "active",
                        G_SETTINGS_BIND_DEFAULT);
    }
    
    static void
    account_creation_wizard_class_init(AccountCreationWizardClass *klass)
    {
        G_OBJECT_CLASS(klass)->dispose = account_creation_wizard_dispose;
    
        gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
                                                    "/net/jami/JamiGnome/accountcreationwizard.ui");
    
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, stack_account_creation);
    
        /* choose_account_type_vbox */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_vbox);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_ring_logo);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_new_account);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_device);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_backup);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_show_advanced);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_connect_account_manager);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_new_sip_account);
    
        /* existing account */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, existing_account);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, existing_account_label);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_archive);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_pin);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_pin_info);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_pin_label);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_next);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_previous);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_pin);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_archive);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_password);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, retrieving_account);
    
        /* account creation */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, account_creation);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password_confirm);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_next);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_previous);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, box_avatarselection);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_password_error);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, box_username_entry);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, switch_register);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_display_name);
    
        /* generating_account_spinner */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_generating_account_spinner);
    
        /* registering_username_spinner */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_registering_username_spinner);
    
        /* info backup */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, info_backup);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_never_show_again);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_export_account);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_backup_label);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_backup_info);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_skip_backup);
    
        /* error view */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, error_view);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_error_view);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_error_view_ok);
    
        /* connect to account_manager */
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, connect_to_account_manager);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_account_manager_username);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_account_manager_password);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_account_manager_uri);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_manager_connect_previous);
        gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_manager_connect_next);
    
        /* add signals */
        account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED] = g_signal_new("account-creation-completed",
                     G_TYPE_FROM_CLASS(klass),
                     (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
                     0,
                     nullptr,
                     nullptr,
                     g_cclosure_marshal_VOID__VOID,
                     G_TYPE_NONE, 0);
    
        account_creation_wizard_signals[ACCOUNT_CREATION_LOCK] = g_signal_new("account-creation-lock",
                     G_TYPE_FROM_CLASS(klass),
                     (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
                     0,
                     nullptr,
                     nullptr,
                     g_cclosure_marshal_VOID__VOID,
                     G_TYPE_NONE, 0);
    
        account_creation_wizard_signals[ACCOUNT_CREATION_UNLOCK] = g_signal_new("account-creation-unlock",
                     G_TYPE_FROM_CLASS(klass),
                     (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
                     0,
                     nullptr,
                     nullptr,
                     g_cclosure_marshal_VOID__VOID,
                     G_TYPE_NONE, 0);
    }
    
    void
    account_creation_show_error_view(AccountCreationWizard *view, const std::string& id)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        if (priv->accountId && id == priv->accountId) {
            switch (priv->mode) {
                case Mode::ADD_LOCAL:
                    gtk_label_set_text(GTK_LABEL(priv->label_error_view),
                        _("An error occured during the account creation."));
                    break;
                case Mode::IMPORT_FROM_DEVICE:
                    gtk_label_set_text(GTK_LABEL(priv->label_error_view),
                        _("Cannot retrieve any account, please verify your PIN and your password."));
                    break;
                case Mode::IMPORT_FROM_BACKUP:
                    gtk_label_set_text(GTK_LABEL(priv->label_error_view),
                        _("Cannot retrieve any account, please verify your archive and your password."));
                    break;
                case Mode::CONNECT_TO_MANAGER:
                    gtk_label_set_text(GTK_LABEL(priv->label_error_view),
                        _("Cannot connect to provided account manager. Please check your credentials."));
                    break;
            }
            gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->error_view);
        }
    }
    
    static gboolean
    connect_to_manager(AccountCreationWizard *view, gchar* username, gchar* password, gchar* managerUri)
    {
        g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(view), G_SOURCE_REMOVE);
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_LOCK], 0);
    
        std::string accountId = lrc::api::NewAccountModel::connectToAccountManager(
                                       username? username : "",
                                       password? password : "",
                                       managerUri? managerUri : "");
        priv->accountId = g_strdup(accountId.c_str());
        // NOTE: NewAccountModel::accountAdded will be triggered here and will call account_creation_wizard_account_added
    
        g_object_ref(view);  // ref so its not destroyed too early
    
        return G_SOURCE_REMOVE;
    }
    
    static gboolean
    create_account(AccountCreationWizard *view,
                        gchar *display_name,
                        gchar *username,
                        gchar *password,
                        gchar *pin,
                        gchar *archivePath)
    {
        g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(view), G_SOURCE_REMOVE);
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_LOCK], 0);
    
        priv->username = g_strdup(username);
        priv->password = g_strdup(password);
        std::string accountId = lrc::api::NewAccountModel::createNewAccount(
                                       lrc::api::profile::Type::RING,
                                       display_name? display_name : "",
                                       archivePath? archivePath : "",
                                       password? password : "",
                                       pin? pin : "");
        priv->accountId = g_strdup(accountId.c_str());
        priv->avatar = g_strdup(avatar_manipulation_get_temporary(AVATAR_MANIPULATION(priv->avatar_manipulation)));
        // NOTE: NewAccountModel::accountAdded will be triggered here and will call account_creation_wizard_account_added
    
        g_object_ref(view);  // ref so its not destroyed too early
    
        return G_SOURCE_REMOVE;
    }
    
    void
    account_creation_wizard_account_added(AccountCreationWizard *view, const std::string& id)
    {
        g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        if (id != priv->accountId) {
            // Not for this account. Abort
            return;
        }
        // Register username
        if (priv->username) {
            priv->accountModel_->registerName(priv->accountId, priv->password, priv->username);
        }
        // Set avatar if any.
        if (priv->avatar) {
            try {
                priv->accountModel_->setAvatar(priv->accountId, priv->avatar);
            } catch (std::out_of_range&) {
                g_warning("Can't set avatar for unknown account");
            }
        }
    
        if (!g_settings_get_boolean(priv->settings, "never-show-backup-again") && priv->mode == Mode::ADD_LOCAL) {
            gtk_button_set_label(GTK_BUTTON(priv->button_export_account), _("Export account"));
            gtk_widget_show_all(priv->info_backup);
            gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->info_backup);
        } else {
            g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
        }
    
        g_object_unref(view);
    }
    
    static gboolean
    create_new_account(AccountCreationWizard *win)
    {
        g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
    
        /* Tuleap: #1441
         * if the user did not validate the avatar area selection, we still take that as the image
         * for their avatar; otherwise many users end up with no avatar by default
         * TODO: improve avatar creation process to not need this fix
         */
        avatar_manipulation_wizard_completed(AVATAR_MANIPULATION(priv->avatar_manipulation));
    
        gchar *display_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_display_name)));
        gchar *username = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_username)));
        gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_password)));
        gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
        gtk_entry_set_text(GTK_ENTRY(priv->entry_password_confirm), "");
    
        auto status = create_account(win, display_name, username, password, nullptr, nullptr);
    
        // Now that we've use the preview to generate the avatar, we can safely close it. Don't
        // assume owner will do it for us, this might not always be the case
        account_creation_wizard_show_preview(win, FALSE);
    
        g_free(display_name);
        g_free(password);
        g_free(username);
    
        return status;
    }
    
    static gboolean
    connect_to_account_manager(AccountCreationWizard *win)
    {
        g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
    
        gchar *username = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_username)));
        gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_password)));
        gchar *managerUri = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_uri)));
    
        auto status = connect_to_manager(win, username, password, managerUri);
    
        g_free(username);
        g_free(password);
        g_free(managerUri);
    
        return status;
    }
    
    static gboolean
    create_existing_ring_account(AccountCreationWizard *win)
    {
        g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
    
        gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_password)));
        gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
    
        gchar *pin = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin)));
        if (pin and strlen(pin) > 0) {
            gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
        }
    
        gchar *archive = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive)));
        if (archive) {
            gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive), nullptr);
        }
    
        auto status = create_account(win, NULL, NULL, password, pin, archive);
    
        g_free(archive);
        g_free(password);
        g_free(pin);
    
        return status;
    }
    
    static void
    show_generating_account_spinner(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->vbox_generating_account_spinner);
    }
    
    static void
    account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
    
        /* Check for correct password */
        const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
        const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
    
        if (g_strcmp0(password, password_confirm) != 0)
        {
            gtk_label_set_text(GTK_LABEL(priv->label_password_error), _("Passwords don't match"));
            return;
        }
    
        show_generating_account_spinner(win);
    
        /* now create account after a short timeout so that the the save doesn't
         * happen freeze the client before the widget changes happen;
         * the timeout function should then display the next step in account creation
         */
        g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_new_account, win, NULL);
    }
    
    static void
    connect_account_manager_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
    {
        show_generating_account_spinner(win);
    
        /* now create account after a short timeout so that the the save doesn't
         * happen freeze the client before the widget changes happen;
         * the timeout function should then display the next step in account creation
         */
        g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)connect_to_account_manager, win, NULL);
    }
    
    static void
    show_retrieving_account(AccountCreationWizard *win)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->retrieving_account);
    }
    
    static void
    existing_account_next_clicked(AccountCreationWizard *win)
    {
        show_retrieving_account(win);
    
        /* now create account after a short timeout so that the the save doesn't
         * happen freeze the client before the widget changes happen;
         * the timeout function should then display the next step in account creation
         */
        g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_existing_ring_account, win, NULL);
    }
    
    static void
    show_choose_account_type(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        /* Make sure other stack elements are hidden, otherwise their width will prevent
           the window to be correctly resized at this stage. */
        gtk_widget_hide(priv->account_creation);
        gtk_widget_hide(priv->existing_account);
        gtk_widget_hide(priv->connect_to_account_manager);
    
        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->choose_account_type_vbox);
    }
    
    static void
    show_pin_label(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        gtk_widget_set_visible(priv->row_pin_label, !gtk_widget_is_visible(priv->row_pin_label));
    }
    
    static void
    show_backup_label(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        gtk_widget_set_visible(priv->row_backup_label, !gtk_widget_is_visible(priv->row_backup_label));
    }
    
    static void
    error_ok_clicked(AccountCreationWizard *view)
    {
        g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_UNLOCK], 0);
        show_choose_account_type(view);
    }
    
    static void
    skip_backup_clicked(AccountCreationWizard *view)
    {
        g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
    }
    
    static void
    account_creation_previous_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
    {
        // make sure to stop preview before going back to choose account type
        account_creation_wizard_show_preview(view, FALSE);
        show_choose_account_type(view);
    }
    
    static void
    connect_account_manager_previous_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
    {
        show_choose_account_type(view);
    }
    
    static void
    show_import_from_device(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        gtk_label_set_text(GTK_LABEL(priv->existing_account_label), _("Import from device"));
        gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
        gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive), nullptr);
    
        gtk_widget_show_all(priv->existing_account);
        gtk_widget_hide(priv->row_archive);
        gtk_widget_hide(priv->row_pin_label);
        gtk_widget_hide(priv->row_backup_label);
    
        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->existing_account);
        priv->mode = Mode::IMPORT_FROM_DEVICE;
    }
    
    static void
    show_import_from_backup(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        gtk_label_set_text(GTK_LABEL(priv->existing_account_label), _("Import from backup"));
        gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
        gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive), nullptr);
    
        gtk_widget_show_all(priv->existing_account);
        gtk_widget_hide(priv->row_pin);
        gtk_widget_hide(priv->row_pin_label);
        gtk_widget_hide(priv->row_backup_label);
    
        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->existing_account);
        priv->mode = Mode::IMPORT_FROM_BACKUP;
    }
    
    static void
    show_connect_account_manager(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        gtk_widget_show(priv->connect_to_account_manager);
        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->connect_to_account_manager);
        priv->mode = Mode::CONNECT_TO_MANAGER;
    }
    
    static void
    show_advanced(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        gtk_widget_set_visible(GTK_WIDGET(priv->button_connect_account_manager), !gtk_widget_is_visible(GTK_WIDGET(priv->button_connect_account_manager)));
        gtk_widget_set_visible(GTK_WIDGET(priv->button_new_sip_account), !gtk_widget_is_visible(GTK_WIDGET(priv->button_new_sip_account)));
    }
    
    static void
    create_new_sip_account(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
    {
        lrc::api::NewAccountModel::createNewAccount(lrc::api::profile::Type::SIP, "SIP");
        g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
        g_object_unref(view);
    }
    
    static void
    entries_existing_account_changed(G_GNUC_UNUSED GtkEntry *entry, AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        const gchar *pin = gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin));
        gchar *archive_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive));
    
        bool hasPin = pin and strlen(pin) > 0;
        bool hasArchive = archive_path and strlen(archive_path) > 0;
    
        gtk_widget_set_sensitive(
            priv->entry_existing_account_pin,
            (not hasArchive)
        );
        gtk_widget_set_sensitive(
            priv->entry_existing_account_archive,
            (not hasPin)
        );
        gtk_widget_set_sensitive(
            priv->button_import_from_next,
            (hasArchive || hasPin)
        );
    
        g_free(archive_path);
    }
    
    static void
    entries_new_account_changed(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        const gchar *username = gtk_entry_get_text(GTK_ENTRY(priv->entry_username));
        const gboolean sign_up_blockchain = gtk_switch_get_active(GTK_SWITCH(priv->switch_register));
    
        if (
                (sign_up_blockchain && strlen(username) > 0 && priv->username_available) || // we are signing up, username is set and avaialble
                !sign_up_blockchain // We are not signing up
            )
        {
            gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
        }
        else
        {
            gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
        }
    }
    
    static void
    entries_connect_account_manager_changed(AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        const std::string username = gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_username));
        const std::string managerUri = gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_uri));
        const std::string password = gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_password));
    
        gtk_widget_set_sensitive(priv->button_account_manager_connect_next, (!username.empty() && !managerUri.empty() && !password.empty()));
    }
    
    static void
    sign_up_blockchain_switched(GtkSwitch* switch_btn, GParamSpec*, AccountCreationWizard *view)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        gboolean sign_up_blockchain = gtk_switch_get_active(switch_btn);
    
        username_registration_box_set_use_blockchain(
            USERNAME_REGISTRATION_BOX(priv->username_registration_box),
            sign_up_blockchain
        );
    
        gtk_widget_set_sensitive(priv->entry_username, sign_up_blockchain);
    
        if (!sign_up_blockchain)
        {
            gtk_entry_set_text(GTK_ENTRY(priv->entry_username), "");
        }
    
        /* Unchecking blockchain signup when there is an empty username should
         * result in activating the next button.
         */
        entries_new_account_changed(view);
    }
    
    static void
    username_availability_changed(AccountCreationWizard *view, gboolean username_available)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        priv->username_available = username_available;
        entries_new_account_changed(view);
    }
    
    // Export file
    static void
    choose_export_file(AccountCreationWizard *view)
    {
        g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        // Get preferred path
        GtkWidget* dialog;
        GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
        gint res;
        gchar* filename = nullptr;
    
        dialog = gtk_file_chooser_dialog_new(_("Save File"),
                                             GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))),
                                             action,
                                             _("_Cancel"),
                                             GTK_RESPONSE_CANCEL,
                                             _("_Save"),
                                             GTK_RESPONSE_ACCEPT,
                                             nullptr);
    
        const auto& accountInfo = priv->accountModel_->getAccountInfo(priv->accountId);
        std::string alias = accountInfo.profileInfo.alias;
        std::string uri = alias.empty()? "export.gz" : alias + ".gz";
        gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), uri.c_str());
        res = gtk_dialog_run(GTK_DIALOG(dialog));
    
        if (res == GTK_RESPONSE_ACCEPT) {
            auto chooser = GTK_FILE_CHOOSER(dialog);
            filename = gtk_file_chooser_get_filename(chooser);
        }
        gtk_widget_destroy(dialog);
    
        if (!filename) return;
    
        std::string password = {};
        auto prop = priv->accountModel_->getAccountConfig(priv->accountId);
        if (prop.archiveHasPassword) {
            auto* top_window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
            auto* password_dialog = gtk_message_dialog_new(top_window,
                GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
                _("You need to enter your password to export the full archive."));
            gtk_window_set_title(GTK_WINDOW(password_dialog), _("Enter password to export archive"));
            gtk_dialog_set_default_response(GTK_DIALOG(password_dialog), GTK_RESPONSE_OK);
    
            auto* message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(password_dialog));
            auto* password_entry = gtk_entry_new();
            gtk_entry_set_visibility(GTK_ENTRY(password_entry), false);
            gtk_entry_set_invisible_char(GTK_ENTRY(password_entry), '*');
            gtk_box_pack_start(GTK_BOX(message_area), password_entry, true, true, 0);
            gtk_widget_show_all(password_dialog);
    
            auto res = gtk_dialog_run(GTK_DIALOG(password_dialog));
            if (res == GTK_RESPONSE_OK) {
                password = gtk_entry_get_text(GTK_ENTRY(password_entry));
            } else {
                gtk_widget_destroy(password_dialog);
                return;
            }
    
            gtk_widget_destroy(password_dialog);
        }
    
        // export account
        auto success = priv->accountModel_->exportToFile(priv->accountId, filename, password);
        std::string label = success? _("Account exported!") : _("Export account failure.");
        gtk_button_set_label(GTK_BUTTON(priv->button_export_account), label.c_str());
        g_free(filename);
    
        if (success) {
            g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
        }
    }
    
    static void
    build_creation_wizard_view(AccountCreationWizard *view)
    {
        g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
    
        /* set ring logo */
        GError *error = NULL;
        GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/net/jami/JamiGnome/jami-logo-blue",
                                                                      -1, 50, TRUE, &error);
        if (logo_ring == NULL) {
            g_debug("Could not load logo: %s", error->message);
            g_clear_error(&error);
        } else
            gtk_image_set_from_pixbuf(GTK_IMAGE(priv->choose_account_type_ring_logo), logo_ring);
    
        /* create the username_registration_box */
        priv->username_registration_box = username_registration_box_new_empty(false);
        gtk_box_pack_end(GTK_BOX(priv->box_username_entry), GTK_WIDGET(priv->username_registration_box), false, false, 0);
        gtk_widget_show(priv->username_registration_box);
        priv->entry_username = GTK_WIDGET(
            username_registration_box_get_entry(
                USERNAME_REGISTRATION_BOX(priv->username_registration_box)
            )
        );
    
        /* use the real name / username of the logged in user as the default */
        const char* real_name = g_get_real_name();
        const char* user_name = g_get_user_name();
        g_debug("real_name = %s",real_name);
        g_debug("user_name = %s",user_name);
    
        /* check first if the real name was determined */
        if (g_strcmp0 (real_name,"Unknown") != 0)
            gtk_entry_set_text(GTK_ENTRY(priv->entry_display_name), real_name);
        else
            gtk_entry_set_text(GTK_ENTRY(priv->entry_display_name), user_name);
    
        /* choose_account_type signals */
        g_signal_connect_swapped(priv->button_new_account, "clicked", G_CALLBACK(account_creation_wizard_show_preview), view);
        g_signal_connect_swapped(priv->button_import_from_device, "clicked", G_CALLBACK(show_import_from_device), view);
        g_signal_connect_swapped(priv->button_import_from_backup, "clicked", G_CALLBACK(show_import_from_backup), view);
        g_signal_connect(priv->button_show_advanced, "clicked", G_CALLBACK(show_advanced), view);
        g_signal_connect_swapped(priv->button_connect_account_manager, "clicked", G_CALLBACK(show_connect_account_manager), view);
        g_signal_connect(priv->button_new_sip_account, "clicked", G_CALLBACK(create_new_sip_account), view);
    
        /* account_creation signals */
        g_signal_connect_swapped(priv->entry_username, "changed", G_CALLBACK(entries_new_account_changed), view);
        g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), view);
        g_signal_connect(priv->button_account_creation_previous, "clicked", G_CALLBACK(account_creation_previous_clicked), view);
        g_signal_connect_swapped(priv->entry_password, "changed", G_CALLBACK(entries_new_account_changed), view);
        g_signal_connect_swapped(priv->entry_password_confirm, "changed", G_CALLBACK(entries_new_account_changed), view);
        g_signal_connect_swapped(priv->entry_display_name, "changed", G_CALLBACK(entries_new_account_changed), view);
        g_signal_connect(priv->switch_register, "notify::active", G_CALLBACK(sign_up_blockchain_switched), view);
        g_signal_connect_swapped(priv->username_registration_box, "username-availability-changed", G_CALLBACK(username_availability_changed), view);
    
        /* existing_account signals */
        g_signal_connect_swapped(priv->button_import_from_previous, "clicked", G_CALLBACK(show_choose_account_type), view);
        g_signal_connect_swapped(priv->button_pin_info, "clicked", G_CALLBACK(show_pin_label), view);
        g_signal_connect_swapped(priv->button_import_from_next, "clicked", G_CALLBACK(existing_account_next_clicked), view);
        g_signal_connect(priv->entry_existing_account_pin, "changed", G_CALLBACK(entries_existing_account_changed), view);
        g_signal_connect(priv->entry_existing_account_archive, "file-set", G_CALLBACK(entries_existing_account_changed), view);
        g_signal_connect(priv->entry_existing_account_password, "changed", G_CALLBACK(entries_existing_account_changed), view);
    
        /* Backup */
        g_signal_connect_swapped(priv->button_export_account, "clicked", G_CALLBACK(choose_export_file), view);
        g_signal_connect_swapped(priv->button_skip_backup, "clicked", G_CALLBACK(skip_backup_clicked), view);
        g_signal_connect_swapped(priv->button_backup_info, "clicked", G_CALLBACK(show_backup_label), view);
    
        /* error_view signals */
        g_signal_connect_swapped(priv->button_error_view_ok, "clicked", G_CALLBACK(error_ok_clicked), view);
    
    
        /* Connect to account_manager */
        g_signal_connect_swapped(priv->entry_account_manager_username, "changed", G_CALLBACK(entries_connect_account_manager_changed), view);
        g_signal_connect_swapped(priv->entry_account_manager_password, "changed", G_CALLBACK(entries_connect_account_manager_changed), view);
        g_signal_connect_swapped(priv->entry_account_manager_uri, "changed", G_CALLBACK(entries_connect_account_manager_changed), view);
        g_signal_connect(priv->button_account_manager_connect_previous, "clicked", G_CALLBACK(connect_account_manager_previous_clicked), view);
        g_signal_connect(priv->button_account_manager_connect_next, "clicked", G_CALLBACK(connect_account_manager_next_clicked), view);
    
        show_choose_account_type(view);
    
        gtk_button_set_relief(GTK_BUTTON(priv->button_show_advanced), GTK_RELIEF_NONE);
    
        auto provider = gtk_css_provider_new();
        gtk_css_provider_load_from_data(provider,
            ".black { color: grey; font-size: 0.8em; }\
            .transparent-button { margin-left: 10px; border: 0; background-color: rgba(0,0,0,0); margin-right: 0; padding-right: 0;}\
            .infos-button { margin: 0; border: 0; background-color: rgba(0,0,0,0); padding: 0; box-shadow: 0;}",
            -1, nullptr
        );
        gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
                                                  GTK_STYLE_PROVIDER(provider),
                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }
    
    GtkWidget *
    account_creation_wizard_new(lrc::api::AVModel& avModel, lrc::api::NewAccountModel& accountModel)
    {
        gpointer view = g_object_new(ACCOUNT_CREATION_WIZARD_TYPE, NULL);
    
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
        priv->avModel_ = &avModel;
        priv->accountModel_ = &accountModel;
    
        build_creation_wizard_view(ACCOUNT_CREATION_WIZARD(view));
        return (GtkWidget *)view;
    }
    
    void
    account_creation_wizard_show_preview(AccountCreationWizard *win, gboolean show_preview)
    {
        auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
    
        if (priv->account_creation) {
            if (show_preview)
                gtk_widget_show(priv->account_creation);
            else
                gtk_widget_hide(priv->account_creation);
        }
        priv->mode = Mode::ADD_LOCAL;
    
        /* Similarily to general settings view, we construct and destroy the avatar manipulation widget
           each time the profile is made visible / hidden. While not the most elegant solution, this
           allows us to run the preview if and only if it is displayed, and always stop it when hidden. */
        if (show_preview && !priv->avatar_manipulation) {
            priv->avatar_manipulation = avatar_manipulation_new_from_wizard(priv->avModel_);
            gtk_box_pack_start(GTK_BOX(priv->box_avatarselection), priv->avatar_manipulation, true, true, 0);
            gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->account_creation);
        } else if (priv->avatar_manipulation) {
            /* make sure the AvatarManipulation widget is destroyed so the VideoWidget inside of it is too;
             * NOTE: destorying its parent (box_avatarselection) first will cause a mystery 'BadDrawable'
             * crash due to X error */
            gtk_container_remove(GTK_CONTAINER(priv->box_avatarselection), priv->avatar_manipulation);
            priv->avatar_manipulation = nullptr;
        }
    }