aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/manage_accounts/add_account_dialog.vala
blob: d7bbe66b1cce456b487f4cb64105049183f78411 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
using Gee;
using Gtk;
using Pango;

using Dino.Entities;
using Xmpp;

namespace Dino.Ui.ManageAccounts {

[GtkTemplate (ui = "/im/dino/Dino/manage_accounts/add_account_dialog.ui")]
public class AddAccountDialog : Gtk.Dialog {

    public signal void added(Account account);

    [GtkChild] private unowned Stack stack;

    [GtkChild] private unowned Revealer notification_revealer;
    [GtkChild] private unowned Label notification_label;

    // Sign in - JID
    [GtkChild] private unowned Box sign_in_jid_box;
    [GtkChild] private unowned Label sign_in_jid_error_label;
    [GtkChild] private unowned Entry jid_entry;
    [GtkChild] private unowned Stack sign_in_jid_continue_stack;
    [GtkChild] private unowned Button sign_in_jid_continue_button;
    [GtkChild] private unowned Button sign_in_jid_serverlist_button;

    // Sign in - TLS error
    [GtkChild] private unowned Box sign_in_tls_box;
    [GtkChild] private unowned Label sign_in_tls_label;
    [GtkChild] private unowned Stack sign_in_password_continue_stack;
    [GtkChild] private unowned Button sign_in_tls_back_button;

    // Sign in - Password
    [GtkChild] private unowned Box sign_in_password_box;
    [GtkChild] private unowned Label sign_in_password_title;
    [GtkChild] private unowned Label sign_in_password_error_label;

    [GtkChild] private unowned Entry password_entry;
    [GtkChild] private unowned Button sign_in_password_continue_button;
    [GtkChild] private unowned Button sign_in_password_back_button;

    // Select Server
    [GtkChild] private unowned Box create_account_box;
    [GtkChild] private unowned Button login_button;
    [GtkChild] private unowned Stack select_server_continue_stack;
    [GtkChild] private unowned Button select_server_continue;
    [GtkChild] private unowned Label register_form_continue_label;
    [GtkChild] private unowned ListBox server_list_box;
    [GtkChild] private unowned Entry server_entry;

    // Register Form
    [GtkChild] private unowned Box register_box;
    [GtkChild] private unowned Label register_title;
    [GtkChild] private unowned Box form_box;
    [GtkChild] private unowned Button register_form_back;
    [GtkChild] private unowned Stack register_form_continue_stack;
    [GtkChild] private unowned Button register_form_continue;

    // Success
    [GtkChild] private unowned Box success_box;
    [GtkChild] private unowned Label success_description;
    [GtkChild] private unowned Button success_continue_button;

    private static string[] server_list = new string[]{
        "5222.de",
        "jabber.fr",
        "movim.eu",
        "yax.im"
    };

    private StreamInteractor stream_interactor;
    private Database db;
    private HashMap<ListBoxRow, string> list_box_jids = new HashMap<ListBoxRow, string>();
    private Jid? server_jid = null;
    private Jid? login_jid = null;
    private Xep.InBandRegistration.Form? form = null;

    public AddAccountDialog(StreamInteractor stream_interactor, Database db) {
        this.stream_interactor = stream_interactor;
        this.db = db;
        this.title = _("Add Account");

        // Sign in - Jid
        Util.force_error_color(sign_in_jid_error_label);
        jid_entry.changed.connect(on_jid_entry_changed);
        sign_in_jid_continue_button.clicked.connect(on_sign_in_jid_continue_button_clicked);
        sign_in_jid_serverlist_button.clicked.connect(show_select_server);

        // Sign in - TLS error
        sign_in_tls_back_button.clicked.connect(show_sign_in_jid);

        // Sign in - Password
        Util.force_error_color(sign_in_password_error_label);
        password_entry.changed.connect(() => { sign_in_password_continue_button.set_sensitive(password_entry.text.length > 0); });
        sign_in_password_continue_button.clicked.connect(on_sign_in_password_continue_button_clicked);
        sign_in_password_back_button.clicked.connect(show_sign_in_jid);

        // Select Server
        server_entry.changed.connect(() => {
            try {
                Jid jid = new Jid(server_entry.text);
                select_server_continue.sensitive = jid != null && jid.localpart == null && jid.resourcepart == null;
            } catch (InvalidJidError e) {
                select_server_continue.sensitive = false;
            }
        });
        select_server_continue.clicked.connect(on_select_server_continue);
        login_button.clicked.connect(show_sign_in_jid);

        foreach (string server in server_list) {
            ListBoxRow list_box_row = new ListBoxRow();
            list_box_row.set_child(new Label(server) { xalign=0, margin_start=7, margin_end=7 });
            list_box_jids[list_box_row] = server;
            server_list_box.append(list_box_row);
        }

        // Register Form
        register_form_continue.clicked.connect(on_register_form_continue_clicked);
        register_form_back.clicked.connect(show_select_server);

        // Success
        success_continue_button.clicked.connect(() => close());

        show_sign_in_jid();
    }

    private void show_sign_in_jid() {
        sign_in_jid_box.visible = true;
        jid_entry.grab_focus();
        stack.visible_child_name = "login_jid";
        sign_in_tls_box.visible = false;
        sign_in_password_box.visible = false;
        create_account_box.visible = false;
        register_box.visible = false;
        success_box.visible = false;
        set_default_widget(sign_in_jid_continue_button);

        sign_in_jid_error_label.label = "";
        jid_entry.sensitive = true;
        animate_window_resize(sign_in_jid_box);
    }

    private void show_tls_error(string domain, TlsCertificateFlags error_flags) {
        sign_in_tls_box.visible = true;
        stack.visible_child_name = "tls_error";
        sign_in_jid_box.visible = false;
        sign_in_password_box.visible = false;
        create_account_box.visible = false;
        register_box.visible = false;
        success_box.visible = false;

        string error_desc = _("The server could not prove that it is %s.").printf("<b>" + domain + "</b>");
        if (TlsCertificateFlags.UNKNOWN_CA in error_flags) {
            error_desc += " " + _("Its security certificate is not trusted by your operating system.");
        } else if (TlsCertificateFlags.BAD_IDENTITY in error_flags) {
            error_desc += " " + _("Its security certificate is issued to another domain.");
        } else if (TlsCertificateFlags.NOT_ACTIVATED in error_flags) {
            error_desc += " " + _("Its security certificate will only become valid in the future.");
        } else if (TlsCertificateFlags.EXPIRED in error_flags) {
            error_desc += " " + _("Its security certificate is expired.");
        }
        sign_in_tls_label.label = error_desc;

        animate_window_resize(sign_in_tls_box);
    }

    private void show_sign_in_password() {
        sign_in_password_box.visible = true;
        password_entry.grab_focus();
        stack.visible_child_name = "login_password";
        sign_in_jid_box.visible = false;
        sign_in_tls_box.visible = false;
        create_account_box.visible = false;
        register_box.visible = false;
        success_box.visible = false;
        set_default_widget(sign_in_password_continue_button);

        sign_in_password_error_label.label = "";
        sign_in_password_title.label = _("Sign in to %s").printf(login_jid.to_string());
        animate_window_resize(sign_in_password_box);
    }

    private void show_select_server() {
        server_entry.text = "";
        server_entry.grab_focus();
        set_default_widget(select_server_continue);

        server_list_box.row_activated.disconnect(on_server_list_row_activated);
        server_list_box.unselect_all();
        server_list_box.row_activated.connect(on_server_list_row_activated);

        create_account_box.visible = true;
        stack.visible_child_name = "server";
        sign_in_jid_box.visible = false;
        sign_in_tls_box.visible = false;
        register_box.visible = false;
        success_box.visible = false;

        animate_window_resize(create_account_box);
    }

    private void show_register_form() {
        register_box.visible = true;
        stack.visible_child_name = "form";
        sign_in_jid_box.visible = false;
        sign_in_tls_box.visible = false;
        sign_in_password_box.visible = false;
        create_account_box.visible = false;
        success_box.visible = false;

        set_default_widget(register_form_continue);
        animate_window_resize(register_box);
    }

    private void show_success(Account account) {
        success_box.visible = true;
        stack.visible_child_name = "success";
        sign_in_jid_box.visible = false;
        sign_in_tls_box.visible = false;
        sign_in_password_box.visible = false;
        create_account_box.visible = false;
        register_box.visible = false;
        success_description.label = _("You can now use the account %s.").printf("<b>" + Markup.escape_text(account.bare_jid.to_string()) + "</b>");

        set_default_widget(success_continue_button);
    }

    private void on_jid_entry_changed() {
        try {
            login_jid = new Jid(jid_entry.text);
            if (login_jid.localpart != null && login_jid.resourcepart == null) {
                sign_in_jid_continue_button.sensitive = true;
                jid_entry.secondary_icon_name = null;
            } else {
                sign_in_jid_continue_button.sensitive = false;
            }
        } catch (InvalidJidError e) {
            sign_in_jid_continue_button.sensitive = false;
        }
    }

    private async void on_sign_in_jid_continue_button_clicked() {
        try {
            login_jid = new Jid(jid_entry.text);
            jid_entry.sensitive = false;
            sign_in_tls_label.label = "";
            sign_in_jid_error_label.label = "";
            sign_in_jid_continue_button.sensitive = false;
            sign_in_jid_continue_stack.visible_child_name = "spinner";
            Register.ServerAvailabilityReturn server_status = yield Register.check_server_availability(login_jid);
            sign_in_jid_continue_stack.visible_child_name = "label";
            sign_in_jid_continue_button.sensitive = true;
            if (server_status.available) {
                show_sign_in_password();
            } else {
                jid_entry.sensitive = true;
                if (server_status.error_flags != null) {
                    show_tls_error(login_jid.domainpart, server_status.error_flags);
                } else {
                    sign_in_jid_error_label.label = _("Could not connect to %s").printf(login_jid.domainpart);
                }
            }
        } catch (InvalidJidError e) {
            warning("Invalid address from interface allowed login: %s", e.message);
            sign_in_jid_error_label.label = _("Invalid address");
        }
    }

    private async void on_sign_in_password_continue_button_clicked() {
        string password = password_entry.text;
        Account account = new Account(login_jid, null, password, null);

        sign_in_password_continue_stack.visible_child_name = "spinner";
        ConnectionManager.ConnectionError.Source? error = yield stream_interactor.get_module(Register.IDENTITY).add_check_account(account);
        sign_in_password_continue_stack.visible_child_name = "label";

        if (error != null) {
            switch (error) {
                case ConnectionManager.ConnectionError.Source.SASL:
                    sign_in_password_error_label.label = _("Wrong username or password");
                    break;
                default:
                    sign_in_password_error_label.label = _("Something went wrong");
                    break;
            }
        } else {
            add_activate_account(account);
            show_success(account);
        }
    }

    private void on_select_server_continue() {
        try {
            server_jid = new Jid(server_entry.text);
            request_show_register_form.begin(server_jid);
        } catch (InvalidJidError e) {
            warning("Invalid address from interface allowed server: %s", e.message);
            display_notification(_("Invalid address"));
        }
    }

    private void on_server_list_row_activated(ListBox box, ListBoxRow row) {
        try {
            server_jid = new Jid(list_box_jids[row]);
            request_show_register_form.begin(server_jid);
        } catch (InvalidJidError e) {
            warning("Invalid address from selected server: %s", e.message);
            display_notification(_("Invalid address"));
        }
    }

    private async void request_show_register_form(Jid server_jid) {
        select_server_continue_stack.visible_child_name = "spinner";
        Register.RegistrationFormReturn form_return = yield Register.get_registration_form(server_jid);
        if (select_server_continue_stack == null) {
            return;
        }
        select_server_continue_stack.visible_child_name = "label";
        if (form_return.form != null) {
            form = form_return.form;
            set_register_form(server_jid, form);
            show_register_form();
        } else if (form_return.error_flags != null) {
            show_tls_error(server_jid.domainpart, form_return.error_flags);
        } else {
            display_notification(_("No response from server"));
        }
    }

    private void set_register_form(Jid server, Xep.InBandRegistration.Form form) {
        Widget widget = form_box.get_first_child();
        while (widget != null) {
            form_box.remove(widget);
            widget = form_box.get_first_child();
        }

        register_title.label = _("Register on %s").printf(server.to_string());

        if (form.oob != null) {
            form_box.append(new Label(_("The server requires to sign up through a website")));
            form_box.append(new Label(@"<a href=\"$(form.oob)\">$(form.oob)</a>") { use_markup=true });
            register_form_continue_label.label = _("Open website");
            register_form_continue.visible = true;
            register_form_continue.grab_focus();
        } else if (form.fields.size > 0) {
            if (form.instructions != null && form.instructions != "") {
                string markup_instructions = Util.parse_add_markup(form.instructions, null, true, false);
                form_box.append(new Label(markup_instructions) { use_markup=true, halign=Align.CENTER, xalign=0, margin_top=7,
                    wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR });
            }
            foreach (Xep.DataForms.DataForm.Field field in form.fields) {
                Widget? field_widget = Util.get_data_form_field_widget(field);
                if (field.label != null && field.label != "" && field_widget != null) {
                    form_box.append(new Label(field.label) { xalign=0, margin_top=7 });
                    form_box.append(field_widget);
                } else if (field.type_ == Xep.DataForms.DataForm.Type.FIXED && field.get_value_string() != "") {
                    string markup_fixed_field = Util.parse_add_markup(field.get_value_string(), null, true, false);
                    form_box.append(new Label(markup_fixed_field) { use_markup=true, xalign=0, margin_top=7,
                        wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR });
                }
            }
            register_form_continue.visible = true;
            register_form_continue_label.label = _("Register");
        } else {
            form_box.append(new Label(_("Check %s for information on how to sign up").printf(@"<a href=\"http://$(server)\">$(server)</a>")) { use_markup=true });
            register_form_continue.visible = false;
        }
    }

    private async void on_register_form_continue_clicked() {
        notification_revealer.set_reveal_child(false);
        // Button is opening a registration website
        if (form.oob != null) {
            try {
                AppInfo.launch_default_for_uri(form.oob, null);
            } catch (Error e) { }
            show_sign_in_jid();
            return;
        }

        register_form_continue_stack.visible_child_name = "spinner";
        string? error = yield Register.submit_form(server_jid, form);
        if (register_form_continue_stack == null) {
            return;
        }
        register_form_continue_stack.visible_child_name = "label";
        if (error == null) {
            string? username = null, password = null;
            foreach (Xep.DataForms.DataForm.Field field in form.fields) {
                switch (field.var) {
                    case "username": username = field.get_value_string(); break;
                    case "password": password = field.get_value_string(); break;
                }
            }
            try {
                Account account = new Account(new Jid.components(username, server_jid.domainpart, null), null, password, null);
                add_activate_account(account);
                show_success(account);
            } catch (InvalidJidError e) {
                warning("Invalid address from components of registration: %s", e.message);
                display_notification(_("Invalid address"));
            }
        } else {
            display_notification(error);
        }
    }

    private void display_notification(string text) {
        notification_label.label = text;
        notification_revealer.set_reveal_child(true);
        Timeout.add_seconds(5, () => {
            notification_revealer.set_reveal_child(false);
            return false;
        });
    }

    private void add_activate_account(Account account) {
        account.enabled = true;
        account.persist(db);
        stream_interactor.connect_account(account);
        added(account);
    }

    private void animate_window_resize(Widget widget) { // TODO code duplication
        int curr_height = widget.get_size(Orientation.VERTICAL);
        var natural_size = Requisition();
        stack.get_preferred_size(null, out natural_size);
        int difference = natural_size.height + 5 - curr_height;
        Timer timer = new Timer();
        Timeout.add((int) (stack.transition_duration / 30), () => {
            ulong microsec;
            timer.elapsed(out microsec);
            ulong millisec = microsec / 1000;
            double partial = double.min(1, (double) millisec / stack.transition_duration);
            default_height = (int) (curr_height + difference * partial);
            return millisec < stack.transition_duration;
        });
    }
}

}