1 /* Copyright (C) 2016-2017 Shengyu Zhang <i@silverrainz.me>
2  *
3  * This file is part of Srain.
4  *
5  * Srain is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * @file sui_connect_panel.c
21  * @brief Panel widget for connecting to server
22  * @author Shengyu Zhang <i@silverrainz.me>
23  * @version 0.06.2
24  * @date 2016-09-16
25  */
26 
27 #include <stdlib.h>
28 #include <gtk/gtk.h>
29 #include <libsecret/secret.h>
30 
31 #include "config/reader.h"
32 #include "config/password.h"
33 #include "sui/sui.h"
34 #include "srain.h"
35 #include "i18n.h"
36 #include "log.h"
37 #include "utils.h"
38 
39 #include "sui_common.h"
40 #include "sui_event_hdr.h"
41 #include "sui_window.h"
42 #include "sui_connect_panel.h"
43 
44 #define SERVER_LIST_STORE_COL_NAME          0
45 #define LOGIN_METHOD_LIST_STORE_COL_ID      0
46 #define LOGIN_METHOD_LIST_STORE_COL_NAME    1
47 
48 #define PAGE_QUICK_MODE     "quick_mode_page"
49 #define PAGE_ADVANCED_MODE  "advanced_mode_page"
50 
51 #define LOGIN_PAGE_NONE     "none"
52 #define LOGIN_PAGE_PASSWORD "password"
53 #define LOGIN_PAGE_CERT     "certificate"
54 
55 struct _SuiConnectPanel {
56     GtkBox parent;
57 
58     GtkStack *stack;
59 
60     /* Quick mode */
61     GtkComboBox *quick_server_combo_box;
62     GtkEntry *quick_nick_entry;
63 
64     /* Advance mode */
65     GtkComboBox *server_combo_box;
66     GtkEntry *host_entry;
67     GtkEntry *port_entry;
68     GtkEntry *password_entry;
69     GtkCheckButton *remember_password_check_button;
70     GtkCheckButton *tls_check_button;
71     GtkCheckButton *tls_noverify_check_button;
72 
73     GtkEntry *nick_entry;
74     GtkComboBox *login_method_combo_box;
75     GtkStack *login_method_stack;
76     GtkEntry *login_password_entry;
77     GtkCheckButton *remember_login_password_check_button;
78     GtkFileChooserButton *login_cert_file_chooser_button;
79 
80 
81     /* Buttons */
82     GtkButton *connect_button;
83     GtkButton *cancel_button;
84 
85     /* Data model */
86     GtkListStore *server_list_store;
87     GtkListStore *login_method_list_store;
88 };
89 
90 struct _SuiConnectPanelClass {
91     GtkBoxClass parent_class;
92 };
93 
94 static void update(SuiConnectPanel *self, const char *srv_name);
95 static void refresh_server_list(SuiConnectPanel *self);
96 static void refresh_login_method_list(SuiConnectPanel *self);
97 static void update_focus(SuiConnectPanel *self);
98 
99 static void server_combo_box_on_changed(GtkComboBox *combo_box,
100         gpointer user_data);
101 static void login_method_combo_box_on_changed(GtkComboBox *combo_box,
102         gpointer user_data);
103 static void connect_button_on_click(gpointer user_data);
104 static void cancel_button_on_click(gpointer user_data);
105 static void nick_entry_on_changed(GtkEditable *editable, gpointer user_data);
106 static void on_password_lookup(GObject *source, GAsyncResult *result,
107         gpointer user_data);
108 static void stack_on_child_changed(GtkWidget *widget, GParamSpec *pspec,
109         gpointer user_data);
110 
111 /*****************************************************************************
112  * GObject functions
113  *****************************************************************************/
114 
115 G_DEFINE_TYPE(SuiConnectPanel, sui_connect_panel, GTK_TYPE_BOX);
116 
sui_connect_panel_init(SuiConnectPanel * self)117 static void sui_connect_panel_init(SuiConnectPanel *self){
118     gtk_widget_init_template(GTK_WIDGET(self));
119 
120 #if GTK_CHECK_VERSION(3, 18, 0)
121     gtk_stack_set_interpolate_size(self->stack, TRUE);
122     gtk_stack_set_interpolate_size(self->login_method_stack, TRUE);
123 #endif
124 
125     /* Set server list model */
126     self->server_list_store = gtk_list_store_new(1, G_TYPE_STRING);
127     gtk_combo_box_set_model(self->quick_server_combo_box,
128             GTK_TREE_MODEL(self->server_list_store));
129     gtk_combo_box_set_id_column(self->quick_server_combo_box, 0);
130     gtk_combo_box_set_model(self->server_combo_box,
131             GTK_TREE_MODEL(self->server_list_store));
132     gtk_combo_box_set_id_column(self->server_combo_box, 0);
133     gtk_combo_box_set_entry_text_column(self->server_combo_box,
134             SERVER_LIST_STORE_COL_NAME);
135     g_object_bind_property(
136             self->quick_server_combo_box, "active",
137             self->server_combo_box, "active",
138             G_BINDING_BIDIRECTIONAL);
139 
140     self->login_method_list_store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
141     gtk_combo_box_set_model(self->login_method_combo_box,
142             GTK_TREE_MODEL(self->login_method_list_store));
143     gtk_combo_box_set_id_column(self->login_method_combo_box, 1);
144 
145     g_object_bind_property(
146             self->quick_nick_entry, "text",
147             self->nick_entry, "text",
148             G_BINDING_BIDIRECTIONAL);
149 
150     g_signal_connect(self->stack, "notify::visible-child",
151             G_CALLBACK(stack_on_child_changed), self);
152 
153     g_signal_connect(self->quick_server_combo_box, "changed",
154             G_CALLBACK(server_combo_box_on_changed), self);
155     g_signal_connect(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(self->server_combo_box))),
156             "changed", G_CALLBACK(server_combo_box_on_changed), self);
157     g_signal_connect(self->nick_entry, "changed",
158             G_CALLBACK(nick_entry_on_changed), self);
159     g_signal_connect(self->login_method_combo_box, "changed",
160             G_CALLBACK(login_method_combo_box_on_changed), self);
161     g_signal_connect_swapped(self->connect_button, "clicked",
162             G_CALLBACK(connect_button_on_click), self);
163     g_signal_connect_swapped(self->cancel_button, "clicked",
164             G_CALLBACK(cancel_button_on_click), self);
165 
166     /* Press enter to connect */
167     g_signal_connect_swapped(self->quick_nick_entry, "activate",
168             G_CALLBACK(connect_button_on_click), self);
169     g_signal_connect_swapped(self->host_entry, "activate",
170             G_CALLBACK(connect_button_on_click), self);
171     g_signal_connect_swapped(self->port_entry, "activate",
172             G_CALLBACK(connect_button_on_click), self);
173     g_signal_connect_swapped(self->password_entry, "activate",
174             G_CALLBACK(connect_button_on_click), self);
175     g_signal_connect_swapped(self->nick_entry, "activate",
176             G_CALLBACK(connect_button_on_click), self);
177     g_signal_connect_swapped(self->login_password_entry, "activate",
178             G_CALLBACK(connect_button_on_click), self);
179 
180     refresh_server_list(self);
181     refresh_login_method_list(self);
182 }
183 
sui_connect_panel_class_init(SuiConnectPanelClass * class)184 static void sui_connect_panel_class_init(SuiConnectPanelClass *class){
185     GtkWidgetClass *widget_class;
186 
187     widget_class = GTK_WIDGET_CLASS(class);
188     gtk_widget_class_set_template_from_resource(widget_class,
189             "/im/srain/Srain/connect_panel.glade");
190 
191     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, stack);
192 
193     /* Quick mode */
194     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, quick_server_combo_box);
195     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, quick_nick_entry);
196 
197     /* Advanced mode */
198     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, server_combo_box);
199     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, host_entry);
200     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, port_entry);
201     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, password_entry);
202     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, remember_password_check_button);
203     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, tls_check_button);
204     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, tls_noverify_check_button);
205 
206     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, nick_entry);
207     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, login_method_combo_box);
208     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, login_method_stack);
209     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, login_password_entry);
210     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, remember_login_password_check_button);
211     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, login_cert_file_chooser_button);
212 
213     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, connect_button);
214     gtk_widget_class_bind_template_child(widget_class, SuiConnectPanel, cancel_button);
215 }
216 
217 /*****************************************************************************
218  * Expored functions
219  *****************************************************************************/
220 
sui_connect_panel_new()221 SuiConnectPanel* sui_connect_panel_new(){
222     return g_object_new(SUI_TYPE_CONNECT_PANEL, NULL);
223 }
224 
225 /*****************************************************************************
226  * Static functions
227  *****************************************************************************/
228 
update(SuiConnectPanel * self,const char * srv_name)229 static void update(SuiConnectPanel *self, const char *srv_name){
230     if (!srv_name || !strlen(srv_name)){
231         gtk_combo_box_set_active_iter(self->server_combo_box, NULL);
232 
233         gtk_entry_set_text(self->host_entry, "");
234         gtk_entry_set_text(self->port_entry, "");
235         gtk_entry_set_text(self->password_entry, "");
236         gtk_toggle_button_set_active(
237                 GTK_TOGGLE_BUTTON(self->remember_password_check_button), FALSE);
238         gtk_toggle_button_set_active(
239                 GTK_TOGGLE_BUTTON(self->tls_check_button), FALSE);
240         gtk_toggle_button_set_active(
241                 GTK_TOGGLE_BUTTON(self->tls_noverify_check_button), FALSE);
242 
243         gtk_entry_set_text(self->nick_entry, "");
244         gtk_combo_box_set_active_iter(self->login_method_combo_box, NULL);
245         gtk_entry_set_text(self->login_password_entry, "");
246         gtk_toggle_button_set_active(
247                 GTK_TOGGLE_BUTTON(self->remember_login_password_check_button), FALSE);
248         gtk_file_chooser_set_filename(
249                 GTK_FILE_CHOOSER(self->login_cert_file_chooser_button), "");
250     } else {
251         SrnRet ret;
252         SrnApplication *app_model;
253         SrnServerConfig *srv_cfg;
254 
255         app_model = sui_application_get_ctx(sui_application_get_instance());
256         srv_cfg = srn_server_config_new();
257 
258         // NOTE: this function blocks UI.
259         // pcf said this is good.
260         ret = srn_config_manager_read_server_config(
261                 app_model->cfg_mgr, srv_cfg, srv_name);
262         if (!RET_IS_OK(ret)) {
263             ERR_FR("Failed to read server config: %1$s", RET_MSG(ret));
264             srn_server_config_free(srv_cfg);
265             return;
266         }
267 
268         if (srv_cfg->addrs){
269             char *port;
270             SrnServerAddr *addr;
271 
272             addr = srv_cfg->addrs->data;
273             port = g_strdup_printf("%d", addr->port);
274             gtk_entry_set_text(self->host_entry, addr->host);
275             gtk_entry_set_text(self->port_entry, port);
276             g_free(port);
277         }
278         if (srv_cfg->password) {
279             gtk_entry_set_text(self->password_entry, srv_cfg->password);
280         }
281         gtk_toggle_button_set_active(
282                 GTK_TOGGLE_BUTTON(self->tls_check_button),
283                 srv_cfg->irc->tls);
284         gtk_toggle_button_set_active(
285                 GTK_TOGGLE_BUTTON(self->tls_noverify_check_button),
286                 srv_cfg->irc->tls_noverify);
287 
288         gtk_entry_set_text(self->nick_entry,
289                 srv_cfg->user->nick? srv_cfg->user->nick: "");
290 
291         gtk_combo_box_set_active_id(self->login_method_combo_box,
292                 srn_login_method_to_string(srv_cfg->user->login->method));
293         gtk_entry_set_text(self->login_password_entry,
294                 srv_cfg->user->login->password ?
295                 srv_cfg->user->login->password : "");
296         gtk_file_chooser_set_filename(
297                 GTK_FILE_CHOOSER(self->login_cert_file_chooser_button),
298                 srv_cfg->user->login->cert_file ?
299                 srv_cfg->user->login->cert_file : "");
300 
301         srn_server_config_free(srv_cfg);
302     }
303 }
304 
refresh_server_list(SuiConnectPanel * self)305 static void refresh_server_list(SuiConnectPanel *self){
306     GList *lst;
307     GList *srv_cfg_lst;
308     GtkTreeIter iter;
309     GtkListStore *store;
310     SrnRet ret;
311     SrnApplication *app_model;
312 
313     app_model = sui_application_get_ctx(sui_application_get_instance());
314     store = self->server_list_store;
315     gtk_list_store_clear(store);
316 
317     srv_cfg_lst = NULL;
318     ret = srn_config_manager_read_server_config_list(
319             app_model->cfg_mgr, &srv_cfg_lst);
320     if (!RET_IS_OK(ret)){
321         ERR_FR("%s", RET_MSG(ret));
322         return;
323     }
324 
325     lst = srv_cfg_lst;
326     while (lst) {
327         gtk_list_store_append(store, &iter);
328         gtk_list_store_set(store, &iter,
329                 SERVER_LIST_STORE_COL_NAME, lst->data,
330                 -1);
331         lst = g_list_next(lst);
332     }
333 
334     g_list_free_full(srv_cfg_lst, g_free);
335 }
336 
refresh_login_method_list(SuiConnectPanel * self)337 static void refresh_login_method_list(SuiConnectPanel *self){
338     GtkTreeIter iter;
339     GtkListStore *store;
340 
341     // Supported login methods
342     static SrnLoginMethod lms[] = {
343         SRN_LOGIN_METHOD_NONE,
344         SRN_LOGIN_METHOD_NICKSERV,
345         SRN_LOGIN_METHOD_MSG_NICKSERV,
346         SRN_LOGIN_METHOD_SASL_PLAIN,
347         SRN_LOGIN_METHOD_SASL_ECDSA_NIST256P_CHALLENGE,
348     };
349 
350     store = self->login_method_list_store;
351     gtk_list_store_clear(store);
352 
353     for (int i = 0; i < sizeof(lms) / sizeof(lms[0]); i++) {
354         gtk_list_store_append(store, &iter);
355         gtk_list_store_set(store, &iter,
356                 LOGIN_METHOD_LIST_STORE_COL_ID, (int)lms[i],
357                 LOGIN_METHOD_LIST_STORE_COL_NAME, srn_login_method_to_string(lms[i]),
358                 -1);
359     }
360 }
361 
server_combo_box_on_changed(GtkComboBox * combo_box,gpointer user_data)362 static void server_combo_box_on_changed(GtkComboBox *combo_box,
363         gpointer user_data){
364     const char *srv_name;
365     GtkEntry *entry;
366     SuiConnectPanel *self;
367 
368     self = SUI_CONNECT_PANEL(user_data);
369     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(self->server_combo_box)));
370     srv_name = gtk_entry_get_text(entry);
371     update(self, srv_name);
372 }
373 
login_method_combo_box_on_changed(GtkComboBox * combo_box,gpointer user_data)374 static void login_method_combo_box_on_changed(GtkComboBox *combo_box,
375         gpointer user_data){
376     const char *page;
377     SrnLoginMethod lm;
378     GtkTreeModel *model;
379     GtkTreeIter iter;
380     SuiConnectPanel *self;
381 
382     self = SUI_CONNECT_PANEL(user_data);
383     model = GTK_TREE_MODEL(self->login_method_list_store);
384 
385     if (!gtk_combo_box_get_active_iter(combo_box, &iter)){
386         lm = SRN_LOGIN_METHOD_NONE;
387     } else {
388         // Set the login_method_stack to the corresponding page
389         gtk_tree_model_get(model, &iter,
390                 LOGIN_METHOD_LIST_STORE_COL_ID, &lm,
391                 -1);
392     }
393 
394     switch (lm) {
395         case SRN_LOGIN_METHOD_NICKSERV:
396         case SRN_LOGIN_METHOD_MSG_NICKSERV:
397         case SRN_LOGIN_METHOD_SASL_PLAIN:
398             page = LOGIN_PAGE_PASSWORD;
399             break;
400         case SRN_LOGIN_METHOD_SASL_ECDSA_NIST256P_CHALLENGE:
401             page = LOGIN_PAGE_CERT;
402             break;
403         case SRN_LOGIN_METHOD_NONE:
404         default:
405             page = LOGIN_PAGE_NONE;
406             break;
407     }
408     gtk_stack_set_visible_child_name(self->login_method_stack, page);
409 }
410 
connect_button_on_click(gpointer user_data)411 static void connect_button_on_click(gpointer user_data){
412     const char *page;
413     const char *srv_name;
414     SrnRet ret;
415     SuiConnectPanel *self;
416     SrnApplication *app_model;
417     SrnServer *srv;
418     SrnServerConfig *srv_cfg;
419 
420     ret = SRN_ERR;
421     self = user_data;
422     page = gtk_stack_get_visible_child_name(self->stack);
423     app_model = sui_application_get_ctx(sui_application_get_instance());
424     srv = NULL;
425     srv_cfg = NULL;
426 
427     if (g_ascii_strcasecmp(page, PAGE_QUICK_MODE) == 0){
428         const char *nick;
429 
430         srv_name = gtk_combo_box_get_active_id(self->quick_server_combo_box);
431         if (!srv_name){
432             ret = RET_ERR(_("No server selected"));
433             goto FIN;
434         }
435 
436         srv_cfg = srn_server_config_new();
437         ret = srn_config_manager_read_server_config(
438                 app_model->cfg_mgr, srv_cfg, srv_name);
439         if (!RET_IS_OK(ret)){
440             goto FIN;
441         }
442 
443         nick = gtk_entry_get_text(self->quick_nick_entry);
444 
445         if (!str_is_empty(nick)) {
446             str_assign(&srv_cfg->user->nick, nick);
447         }
448     } else if (g_ascii_strcasecmp(page, PAGE_ADVANCED_MODE) == 0){
449         const char *host;
450         int port;
451         const char *passwd;
452         bool rmb_passwd;
453         bool tls;
454         bool tls_noverify;
455         const char *nick;
456         const char *method_str;
457         const char *login_passwd;
458         bool rmb_login_passwd;
459         const char *login_cert_file;
460 
461         GtkEntry *entry;
462         SrnLoginMethod method;
463 
464         entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(self->server_combo_box)));
465         srv_name = gtk_entry_get_text(entry);
466 
467         srv_cfg = srn_server_config_new();
468         ret = srn_config_manager_read_server_config(
469                 app_model->cfg_mgr, srv_cfg, srv_name);
470         if (!RET_IS_OK(ret)){
471             goto FIN;
472         }
473 
474         host = gtk_entry_get_text(self->host_entry);
475         port = g_ascii_strtoll(gtk_entry_get_text(self->port_entry), NULL, 10);
476         if (!str_is_empty(host)) {
477             srn_server_config_clear_addr(srv_cfg);
478             srn_server_config_add_addr(srv_cfg, srn_server_addr_new(host, port));
479         }
480 
481         passwd = gtk_entry_get_text(self->password_entry);
482         // Always overwrite password
483         str_assign(&srv_cfg->password, passwd);
484 
485         rmb_passwd = gtk_toggle_button_get_active(
486                 GTK_TOGGLE_BUTTON(self->remember_password_check_button));
487         if (rmb_passwd) {
488             if (strlen(passwd)) {  // Reqeust to store password
489                 ret = srn_config_manager_store_server_password(
490                         app_model->cfg_mgr, passwd, srv_name);
491                 if (!RET_IS_OK(ret)) {
492                     ret = RET_ERR(_("Failed to store server password: %1$s"),
493                             RET_MSG(ret));
494                     sui_message_box(_("Error"), RET_MSG(ret) );
495                     // No need to return
496                 }
497             } else {  // Reqeust to clear password
498                 ret = srn_config_manager_clear_server_password(
499                         app_model->cfg_mgr, srv_name);
500                 if (!RET_IS_OK(ret)) {
501                     ret = RET_ERR(_("Failed to clear server password: %1$s"),
502                             RET_MSG(ret));
503                     sui_message_box(_("Error"), RET_MSG(ret) );
504                     // No need to return
505                 }
506             }
507         }
508 
509         tls_noverify = gtk_toggle_button_get_active(
510                 GTK_TOGGLE_BUTTON(self->tls_noverify_check_button));
511         srv_cfg->irc->tls_noverify = tls_noverify;
512 
513         tls = gtk_toggle_button_get_active(
514                 GTK_TOGGLE_BUTTON(self->tls_check_button));
515         // TODO: Let tls_check_button to be toggled when
516         // tls_noverify_check_button is toggled.
517         srv_cfg->irc->tls = tls || tls_noverify;
518 
519         nick = gtk_entry_get_text(self->nick_entry);
520         if (!str_is_empty(nick)) {
521             str_assign(&srv_cfg->user->nick, nick);
522         }
523 
524         method_str = gtk_combo_box_get_active_id(self->login_method_combo_box);
525         method = srn_login_method_from_string(method_str);
526         srv_cfg->user->login->method = method;
527 
528         login_passwd = gtk_entry_get_text(self->login_password_entry);
529         // Always overwrite password
530         str_assign(&srv_cfg->user->login->password, login_passwd);
531 
532         rmb_login_passwd = gtk_toggle_button_get_active(
533                 GTK_TOGGLE_BUTTON(self->remember_login_password_check_button));
534         if (rmb_login_passwd) {
535             if (strlen(login_passwd)) { // Reqeust to store password
536                 ret = srn_config_manager_store_user_password(
537                         app_model->cfg_mgr, login_passwd, srv_name, nick);
538                 if (!RET_IS_OK(ret)) {
539                     ret = RET_ERR(_("Failed to store user password: %1$s"),
540                             RET_MSG(ret));
541                     sui_message_box(_("Error"), RET_MSG(ret) );
542                     // No need to return
543                 }
544             } else { // Reqeust to clear password
545                 ret = srn_config_manager_clear_user_password(
546                         app_model->cfg_mgr, srv_name, nick);
547                 if (!RET_IS_OK(ret)) {
548                     ret = RET_ERR(_("Failed to clear user password: 1$%s"),
549                             RET_MSG(ret));
550                     sui_message_box(_("Error"), RET_MSG(ret) );
551                     // No need to return
552                 }
553             }
554         }
555 
556         login_cert_file = gtk_file_chooser_get_filename(
557                 GTK_FILE_CHOOSER(self->login_cert_file_chooser_button));
558         if (!str_is_empty(login_cert_file)) {
559             str_assign(&srv_cfg->user->login->cert_file, login_cert_file);
560         }
561     } else {
562         g_warn_if_reached();
563         goto FIN;
564     }
565 
566     ret = srn_application_add_server_with_config(app_model, srv_name, srv_cfg);
567     if (!RET_IS_OK(ret)){
568         goto FIN;
569     }
570     srv_cfg = NULL; // Ownership changed to server
571 
572     srv = srn_application_get_server(app_model, srv_name);
573     if (!srn_server_is_valid(srv)){
574         g_warn_if_reached();
575         goto FIN;
576     }
577 
578     ret = srn_server_connect(srv);
579     LOG_FR("Server connect finished");
580     if (!RET_IS_OK(ret)){
581         goto FIN;
582     }
583 
584     ret = SRN_OK;
585 FIN:
586     if (srv_cfg){
587         srn_server_config_free(srv_cfg);
588     }
589     if (RET_IS_OK(ret)){
590         sui_common_popdown_panel(GTK_WIDGET(self));
591         update(self, NULL);
592     } else {
593         if (srv){
594             srn_application_rm_server(app_model, srv);
595         }
596         sui_message_box(_("Error"), RET_MSG(ret));
597     }
598 }
599 
cancel_button_on_click(gpointer user_data)600 static void cancel_button_on_click(gpointer user_data){
601     SuiConnectPanel *self;
602 
603     self = user_data;
604 
605     sui_common_popdown_panel(GTK_WIDGET(self));
606     update(self, NULL);
607 }
608 
609 
nick_entry_on_changed(GtkEditable * editable,gpointer user_data)610 static void nick_entry_on_changed(GtkEditable *editable, gpointer user_data) {
611     const char *srv_name;
612     const char *user_name;
613     GtkEntry *entry;
614     SuiConnectPanel *self;
615     SrnApplication *app_model;
616     SrnConfigManager *cfg_mgr;
617 
618     entry = GTK_ENTRY(editable);
619     self = SUI_CONNECT_PANEL(user_data);
620     app_model = sui_application_get_ctx(sui_application_get_instance());
621     cfg_mgr = app_model->cfg_mgr;
622 
623     srv_name = gtk_entry_get_text(
624             GTK_ENTRY(gtk_bin_get_child(GTK_BIN(self->server_combo_box))));
625     user_name = gtk_entry_get_text(entry);
626 
627     // Clear login password when user name is not valid
628     if (str_is_empty(user_name)) {
629         gtk_entry_set_text(self->login_password_entry, "");
630         return;
631     }
632 
633     // Lookup login password asynchronously
634     secret_password_lookup(srn_config_manager_get_user_secret_schema(cfg_mgr),
635             NULL, on_password_lookup, self->login_password_entry,
636             SRN_CONFIG_SECRET_SCHEMA_ATTR_SERVER, srv_name,
637             SRN_CONFIG_SECRET_SCHEMA_ATTR_USER, user_name,
638             NULL);
639 }
640 
on_password_lookup(GObject * source,GAsyncResult * result,gpointer user_data)641 static void on_password_lookup(GObject *source, GAsyncResult *result,
642         gpointer user_data) {
643     char *passwd;
644     GtkEntry *entry;
645 
646     entry = GTK_ENTRY(user_data);
647 
648     passwd = secret_password_lookup_finish(result, NULL);
649     if (!passwd) {
650         return;
651     }
652 
653     gtk_entry_set_text(entry, passwd);
654     secret_password_free(passwd);
655 }
656 
stack_on_child_changed(GtkWidget * widget,GParamSpec * pspec,gpointer user_data)657 static void stack_on_child_changed(GtkWidget *widget, GParamSpec *pspec,
658         gpointer user_data) {
659     update_focus(SUI_CONNECT_PANEL(user_data));
660 }
661 
update_focus(SuiConnectPanel * self)662 static void update_focus(SuiConnectPanel *self) {
663     const char *page = gtk_stack_get_visible_child_name(self->stack);
664 
665     if (g_strcmp0(page, PAGE_QUICK_MODE) == 0){
666         gtk_widget_grab_focus(GTK_WIDGET(self->quick_server_combo_box));
667     } else if (g_strcmp0(page, PAGE_ADVANCED_MODE) == 0){
668         gtk_widget_grab_focus(GTK_WIDGET(self->server_combo_box));
669     } else {
670         g_warn_if_reached();
671     }
672 }
673