1 /*
2  * gnc-gwen-gui.c --
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, contact:
16  *
17  * Free Software Foundation           Voice:  +1-617-542-5942
18  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
19  * Boston, MA  02110-1301,  USA       gnu@gnu.org
20  */
21 
22 /**
23  * @internal
24  * @file gnc-gwen-gui.c
25  * @brief GUI callbacks for AqBanking
26  * @author Copyright (C) 2002 Christian Stimming <stimming@tuhh.de>
27  * @author Copyright (C) 2006 David Hampton <hampton@employees.org>
28  * @author Copyright (C) 2008 Andreas Koehler <andi5.py@gmx.net>
29  */
30 
31 #include <config.h>
32 
33 #include <ctype.h>
34 #include <glib/gi18n.h>
35 #include <gwenhywfar/gui_be.h>
36 #include <gwenhywfar/inherit.h>
37 #include <gwenhywfar/version.h>
38 
39 #include "dialog-utils.h"
40 #include "gnc-ab-utils.h"
41 #include "gnc-component-manager.h"
42 #include "gnc-gwen-gui.h"
43 #include "gnc-session.h"
44 #include "gnc-prefs.h"
45 #include "gnc-ui.h"
46 #include "gnc-plugin-aqbanking.h"
47 #include "qof.h"
48 
49 #include "gnc-flicker-gui.h"
50 
51 # define GNC_GWENHYWFAR_CB GWENHYWFAR_CB
52 
53 #define GWEN_GUI_CM_CLASS "dialog-hbcilog"
54 #define GNC_PREFS_GROUP_CONNECTION GNC_PREFS_GROUP_AQBANKING ".connection-dialog"
55 #define GNC_PREF_CLOSE_ON_FINISH   "close-on-finish"
56 #define GNC_PREF_REMEMBER_PIN      "remember-pin"
57 
58 # include <gwen-gui-gtk3/gtk3_gui.h>
59 
60 /* This static indicates the debugging module that this .o belongs to.  */
61 static QofLogModule log_module = G_LOG_DOMAIN;
62 
63 /* A unique full-blown GUI, featuring  */
64 static GncGWENGui *full_gui = NULL;
65 
66 /* A unique Gwenhywfar GUI for hooking our logging into the gwenhywfar logging
67  * framework */
68 static GWEN_GUI *log_gwen_gui = NULL;
69 
70 /* A mapping from gwenhywfar log levels to glib ones */
71 static GLogLevelFlags log_levels[] =
72 {
73     G_LOG_LEVEL_ERROR,     /* GWEN_LoggerLevel_Emergency */
74     G_LOG_LEVEL_ERROR,     /* GWEN_LoggerLevel_Alert */
75     G_LOG_LEVEL_CRITICAL,  /* GWEN_LoggerLevel_Critical */
76     G_LOG_LEVEL_CRITICAL,  /* GWEN_LoggerLevel_Error */
77     G_LOG_LEVEL_WARNING,   /* GWEN_LoggerLevel_Warning */
78     G_LOG_LEVEL_MESSAGE,   /* GWEN_LoggerLevel_Notice */
79     G_LOG_LEVEL_INFO,      /* GWEN_LoggerLevel_Info */
80     G_LOG_LEVEL_DEBUG,     /* GWEN_LoggerLevel_Debug */
81     G_LOG_LEVEL_DEBUG      /* GWEN_LoggerLevel_Verbous */
82 };
83 static guint8 n_log_levels = G_N_ELEMENTS(log_levels);
84 
85 /* Macros to determine the GncGWENGui* from a GWEN_GUI* */
86 GWEN_INHERIT(GWEN_GUI, GncGWENGui)
87 #define SETDATA_GUI(gwen_gui, gui) GWEN_INHERIT_SETDATA(GWEN_GUI, GncGWENGui, \
88                                                         (gwen_gui), (gui), NULL)
89 #define GETDATA_GUI(gwen_gui) GWEN_INHERIT_GETDATA(GWEN_GUI, GncGWENGui, (gwen_gui))
90 
91 #define OTHER_ENTRIES_ROW_OFFSET 3
92 
93 typedef struct _Progress Progress;
94 typedef enum _GuiState GuiState;
95 
96 static void register_callbacks(GncGWENGui *gui);
97 static void unregister_callbacks(GncGWENGui *gui);
98 static void setup_dialog(GncGWENGui *gui);
99 static void enable_password_cache(GncGWENGui *gui, gboolean enabled);
100 static void reset_dialog(GncGWENGui *gui);
101 static void set_finished(GncGWENGui *gui);
102 static void set_aborted(GncGWENGui *gui);
103 static void show_dialog(GncGWENGui *gui, gboolean clear_log);
104 static void hide_dialog(GncGWENGui *gui);
105 static gboolean show_progress_cb(gpointer user_data);
106 static void show_progress(GncGWENGui *gui, Progress *progress);
107 static void hide_progress(GncGWENGui *gui, Progress *progress);
108 static void free_progress(Progress *progress, gpointer unused);
109 static gboolean keep_alive(GncGWENGui *gui);
110 static void cm_close_handler(gpointer user_data);
111 static void erase_password(gchar *password);
112 static gchar *strip_html(gchar *text);
113 #ifndef AQBANKING6
114 static void get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
115                       const gchar *text, gchar **input, gint min_len,
116                       gint max_len);
117 #else
118 static void get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
119                       const gchar *text, const char *mimeType,
120                       const char *pChallenge, uint32_t lChallenge,
121                       gchar **input, gint min_len, gint max_len);
122 #endif
123 static gint GNC_GWENHYWFAR_CB messagebox_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *title,
124                           const gchar *text, const gchar *b1, const gchar *b2,
125                           const gchar *b3, guint32 guiid);
126 static gint GNC_GWENHYWFAR_CB inputbox_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *title,
127                         const gchar *text, gchar *buffer, gint min_len,
128                         gint max_len, guint32 guiid);
129 static guint32 GNC_GWENHYWFAR_CB showbox_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *title,
130                           const gchar *text, guint32 guiid);
131 static void GWENHYWFAR_CB hidebox_cb(GWEN_GUI *gwen_gui, guint32 id);
132 static guint32 GNC_GWENHYWFAR_CB progress_start_cb(GWEN_GUI *gwen_gui, uint32_t progressFlags,
133                                  const char *title, const char *text,
134                                  uint64_t total, uint32_t guiid);
135 static gint GNC_GWENHYWFAR_CB progress_advance_cb(GWEN_GUI *gwen_gui, uint32_t id,
136                                 uint64_t new_progress);
137 static gint GNC_GWENHYWFAR_CB progress_log_cb(GWEN_GUI *gwen_gui, guint32 id,
138                             GWEN_LOGGER_LEVEL level, const gchar *text);
139 static gint GNC_GWENHYWFAR_CB progress_end_cb(GWEN_GUI *gwen_gui, guint32 id);
140 #ifndef AQBANKING6
141 static gint GNC_GWENHYWFAR_CB getpassword_cb(GWEN_GUI *gwen_gui, guint32 flags,
142                                              const gchar *token,
143                                              const gchar *title,
144                                              const gchar *text, gchar *buffer,
145                                              gint min_len, gint max_len,
146                                              guint32 guiid);
147 #else
148 static gint GNC_GWENHYWFAR_CB getpassword_cb(GWEN_GUI *gwen_gui, guint32 flags,
149                                              const gchar *token,
150                                              const gchar *title,
151                                              const gchar *text, gchar *buffer,
152                                              gint min_len, gint max_len,
153                                              GWEN_GUI_PASSWORD_METHOD methodId,
154                                              GWEN_DB_NODE *methodParams,
155                                              guint32 guiid);
156 #endif
157 static gint GNC_GWENHYWFAR_CB setpasswordstatus_cb(GWEN_GUI *gwen_gui, const gchar *token,
158         const gchar *pin,
159         GWEN_GUI_PASSWORD_STATUS status, guint32 guiid);
160 static gint GNC_GWENHYWFAR_CB loghook_cb(GWEN_GUI *gwen_gui, const gchar *log_domain,
161         GWEN_LOGGER_LEVEL priority, const gchar *text);
162 typedef GWEN_SYNCIO GWEN_IO_LAYER;
163 static gint GNC_GWENHYWFAR_CB checkcert_cb(GWEN_GUI *gwen_gui, const GWEN_SSLCERTDESCR *cert,
164         GWEN_IO_LAYER *io, guint32 guiid);
165 
166 gboolean ggg_delete_event_cb(GtkWidget *widget, GdkEvent *event,
167                              gpointer user_data);
168 void ggg_abort_clicked_cb(GtkButton *button, gpointer user_data);
169 void ggg_close_clicked_cb(GtkButton *button, gpointer user_data);
170 void ggg_close_toggled_cb(GtkToggleButton *button, gpointer user_data);
171 
172 enum _GuiState
173 {
174     INIT,
175     RUNNING,
176     FINISHED,
177     ABORTED,
178     HIDDEN
179 };
180 
181 struct _GncGWENGui
182 {
183     GWEN_GUI *gwen_gui;
184     GtkWidget *parent;
185     GtkWidget *dialog;
186 
187     /* Progress bars */
188     GtkWidget *entries_grid;
189     GtkWidget *top_entry;
190     GtkWidget *top_progress;
191     GtkWidget *second_entry;
192     GtkWidget *other_entries_box;
193 
194     /* Stack of nested Progresses */
195     GList *progresses;
196 
197     /* Number of steps in top-level progress or -1 */
198     guint64 max_actions;
199     guint64 current_action;
200 
201     /* Log window */
202     GtkWidget *log_text;
203 
204     /* Buttons */
205     GtkWidget *abort_button;
206     GtkWidget *close_button;
207     GtkWidget *close_checkbutton;
208 
209     /* Flags to keep track on whether an HBCI action is running or not */
210     gboolean keep_alive;
211     GuiState state;
212 
213     /* Password caching */
214     gboolean cache_passwords;
215     GHashTable *passwords;
216 
217     /* Certificates handling */
218     GHashTable *accepted_certs;
219     GWEN_DB_NODE *permanently_accepted_certs;
220     GWEN_GUI_CHECKCERT_FN builtin_checkcert;
221 
222     /* Dialogs */
223     guint32 showbox_id;
224     GHashTable *showbox_hash;
225     GtkWidget *showbox_last;
226 
227     /* Cache the lowest loglevel, corresponding to the most serious warning */
228     GWEN_LOGGER_LEVEL min_loglevel;
229 };
230 
231 struct _Progress
232 {
233     GncGWENGui *gui;
234 
235     /* Title of the process */
236     gchar *title;
237 
238     /* Event source id for showing delayed */
239     guint source;
240 };
241 
242 void
gnc_GWEN_Gui_log_init(void)243 gnc_GWEN_Gui_log_init(void)
244 {
245     if (!log_gwen_gui)
246     {
247         log_gwen_gui = Gtk3_Gui_new();
248 
249         /* Always use our own logging */
250         GWEN_Gui_SetLogHookFn(log_gwen_gui, loghook_cb);
251 
252         /* Keep a reference so that the GWEN_GUI survives a GUI switch */
253         GWEN_Gui_Attach(log_gwen_gui);
254     }
255     GWEN_Gui_SetGui(log_gwen_gui);
256 }
257 
258 GncGWENGui *
gnc_GWEN_Gui_get(GtkWidget * parent)259 gnc_GWEN_Gui_get(GtkWidget *parent)
260 {
261     GncGWENGui *gui;
262 
263     ENTER("parent=%p", parent);
264 
265     if (full_gui)
266     {
267         if (full_gui->state == INIT || full_gui->state == RUNNING)
268         {
269             LEAVE("full_gui in use, state=%d", full_gui->state);
270             return NULL;
271         }
272 
273         gui = full_gui;
274         gui->parent = parent;
275         reset_dialog(gui);
276         register_callbacks(gui);
277 
278         LEAVE("gui=%p", gui);
279         return gui;
280     }
281 
282     gui = g_new0(GncGWENGui, 1);
283     gui->parent = parent;
284     setup_dialog(gui);
285     register_callbacks(gui);
286 
287     full_gui = gui;
288 
289     LEAVE("new gui=%p", gui);
290     return gui;
291 }
292 
293 void
gnc_GWEN_Gui_release(GncGWENGui * gui)294 gnc_GWEN_Gui_release(GncGWENGui *gui)
295 {
296     g_return_if_fail(gui && gui == full_gui);
297 
298     /* Currently a no-op */
299     ENTER("gui=%p", gui);
300     LEAVE(" ");
301 }
302 
303 void
gnc_GWEN_Gui_shutdown(void)304 gnc_GWEN_Gui_shutdown(void)
305 {
306     GncGWENGui *gui = full_gui;
307 
308     ENTER(" ");
309 
310     if (log_gwen_gui)
311     {
312         GWEN_Gui_free(log_gwen_gui);
313         log_gwen_gui = NULL;
314     }
315     GWEN_Gui_SetGui(NULL);
316 
317     if (!gui)
318         return;
319 
320     gui->parent = NULL;
321     reset_dialog(gui);
322     if (gui->passwords)
323         g_hash_table_destroy(gui->passwords);
324     if (gui->showbox_hash)
325         g_hash_table_destroy(gui->showbox_hash);
326     if (gui->permanently_accepted_certs)
327         GWEN_DB_Group_free(gui->permanently_accepted_certs);
328     if (gui->accepted_certs)
329         g_hash_table_destroy(gui->accepted_certs);
330     gtk_widget_destroy(gui->dialog);
331     g_free(gui);
332 
333     full_gui = NULL;
334 
335     LEAVE(" ");
336 }
337 
338 void
gnc_GWEN_Gui_set_close_flag(gboolean close_when_finished)339 gnc_GWEN_Gui_set_close_flag(gboolean close_when_finished)
340 {
341     gnc_prefs_set_bool(
342         GNC_PREFS_GROUP_AQBANKING, GNC_PREF_CLOSE_ON_FINISH,
343         close_when_finished);
344 
345     if (full_gui)
346     {
347         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(full_gui->close_checkbutton))
348                 != close_when_finished)
349         {
350             gtk_toggle_button_set_active(
351                 GTK_TOGGLE_BUTTON(full_gui->close_checkbutton),
352                 close_when_finished);
353         }
354     }
355 }
356 
357 gboolean
gnc_GWEN_Gui_get_close_flag()358 gnc_GWEN_Gui_get_close_flag()
359 {
360     return gnc_prefs_get_bool (GNC_PREFS_GROUP_AQBANKING, GNC_PREF_CLOSE_ON_FINISH);
361 }
362 
363 gboolean
gnc_GWEN_Gui_show_dialog()364 gnc_GWEN_Gui_show_dialog()
365 {
366     GncGWENGui *gui = full_gui;
367 
368     if (!gui)
369     {
370         gnc_GWEN_Gui_get(NULL);
371     }
372 
373     if (gui)
374     {
375         if (gui->state == HIDDEN)
376         {
377             gui->state = FINISHED;
378         }
379         gtk_toggle_button_set_active(
380             GTK_TOGGLE_BUTTON(gui->close_checkbutton),
381             gnc_prefs_get_bool (GNC_PREFS_GROUP_AQBANKING, GNC_PREF_CLOSE_ON_FINISH));
382 
383         show_dialog(gui, FALSE);
384 
385         return TRUE;
386     }
387 
388     return FALSE;
389 }
390 
391 void
gnc_GWEN_Gui_hide_dialog()392 gnc_GWEN_Gui_hide_dialog()
393 {
394     GncGWENGui *gui = full_gui;
395 
396     if (gui)
397     {
398         hide_dialog(gui);
399     }
400 }
401 
402 static void
register_callbacks(GncGWENGui * gui)403 register_callbacks(GncGWENGui *gui)
404 {
405     GWEN_GUI *gwen_gui;
406 
407     g_return_if_fail(gui && !gui->gwen_gui);
408 
409     ENTER("gui=%p", gui);
410 
411     gwen_gui = Gtk3_Gui_new();
412     gui->gwen_gui = gwen_gui;
413 
414     GWEN_Gui_SetMessageBoxFn(gwen_gui, messagebox_cb);
415     GWEN_Gui_SetInputBoxFn(gwen_gui, inputbox_cb);
416     GWEN_Gui_SetShowBoxFn(gwen_gui, showbox_cb);
417     GWEN_Gui_SetHideBoxFn(gwen_gui, hidebox_cb);
418     GWEN_Gui_SetProgressStartFn(gwen_gui, progress_start_cb);
419     GWEN_Gui_SetProgressAdvanceFn(gwen_gui, progress_advance_cb);
420     GWEN_Gui_SetProgressLogFn(gwen_gui, progress_log_cb);
421     GWEN_Gui_SetProgressEndFn(gwen_gui, progress_end_cb);
422     GWEN_Gui_SetGetPasswordFn(gwen_gui, getpassword_cb);
423     GWEN_Gui_SetSetPasswordStatusFn(gwen_gui, setpasswordstatus_cb);
424     GWEN_Gui_SetLogHookFn(gwen_gui, loghook_cb);
425     gui->builtin_checkcert = GWEN_Gui_SetCheckCertFn(gwen_gui, checkcert_cb);
426 
427     GWEN_Gui_SetGui(gwen_gui);
428     SETDATA_GUI(gwen_gui, gui);
429 
430     LEAVE(" ");
431 }
432 
433 static void
unregister_callbacks(GncGWENGui * gui)434 unregister_callbacks(GncGWENGui *gui)
435 {
436     g_return_if_fail(gui);
437 
438     ENTER("gui=%p", gui);
439 
440     if (!gui->gwen_gui)
441     {
442         LEAVE("already unregistered");
443         return;
444     }
445 
446     /* Switch to log_gwen_gui and free gui->gwen_gui */
447     gnc_GWEN_Gui_log_init();
448 
449     gui->gwen_gui = NULL;
450 
451     LEAVE(" ");
452 }
453 
454 static void
setup_dialog(GncGWENGui * gui)455 setup_dialog(GncGWENGui *gui)
456 {
457     GtkBuilder *builder;
458     gint component_id;
459 
460     g_return_if_fail(gui);
461 
462     ENTER("gui=%p", gui);
463 
464     builder = gtk_builder_new();
465     gnc_builder_add_from_file (builder, "dialog-ab.glade", "aqbanking_connection_dialog");
466 
467     gui->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "aqbanking_connection_dialog"));
468 
469     gui->entries_grid = GTK_WIDGET(gtk_builder_get_object (builder, "entries_grid"));
470     gui->top_entry = GTK_WIDGET(gtk_builder_get_object (builder, "top_entry"));
471     gui->top_progress = GTK_WIDGET(gtk_builder_get_object (builder, "top_progress"));
472     gui->second_entry = GTK_WIDGET(gtk_builder_get_object (builder, "second_entry"));
473     gui->other_entries_box = NULL;
474     gui->progresses = NULL;
475     gui->log_text = GTK_WIDGET(gtk_builder_get_object (builder, "log_text"));
476     gui->abort_button = GTK_WIDGET(gtk_builder_get_object (builder, "abort_button"));
477     gui->close_button = GTK_WIDGET(gtk_builder_get_object (builder, "close_button"));
478     gui->close_checkbutton = GTK_WIDGET(gtk_builder_get_object (builder, "close_checkbutton"));
479     gui->accepted_certs = NULL;
480     gui->permanently_accepted_certs = NULL;
481     gui->showbox_hash = NULL;
482     gui->showbox_id = 1;
483 
484     /* Connect the Signals */
485     gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, gui);
486 
487     gtk_toggle_button_set_active(
488         GTK_TOGGLE_BUTTON(gui->close_checkbutton),
489         gnc_prefs_get_bool (GNC_PREFS_GROUP_AQBANKING, GNC_PREF_CLOSE_ON_FINISH));
490 
491     component_id = gnc_register_gui_component(GWEN_GUI_CM_CLASS, NULL,
492                    cm_close_handler, gui);
493     gnc_gui_component_set_session(component_id, gnc_get_current_session());
494 
495 
496 
497     g_object_unref(G_OBJECT(builder));
498 
499     reset_dialog(gui);
500 
501     LEAVE(" ");
502 }
503 
504 static void
enable_password_cache(GncGWENGui * gui,gboolean enabled)505 enable_password_cache(GncGWENGui *gui, gboolean enabled)
506 {
507     g_return_if_fail(gui);
508 
509     if (enabled && !gui->passwords)
510     {
511         /* Remember passwords in memory, mapping tokens to passwords */
512         gui->passwords = g_hash_table_new_full(
513                              g_str_hash, g_str_equal, (GDestroyNotify) g_free,
514                              (GDestroyNotify) erase_password);
515     }
516     else if (!enabled && gui->passwords)
517     {
518         /* Erase and free remembered passwords from memory */
519         g_hash_table_destroy(gui->passwords);
520         gui->passwords = NULL;
521     }
522     gui->cache_passwords = enabled;
523 }
524 
525 static void
reset_dialog(GncGWENGui * gui)526 reset_dialog(GncGWENGui *gui)
527 {
528     gboolean cache_passwords;
529 
530     g_return_if_fail(gui);
531 
532     ENTER("gui=%p", gui);
533 
534     gtk_entry_set_text(GTK_ENTRY(gui->top_entry), "");
535     gtk_entry_set_text(GTK_ENTRY(gui->second_entry), "");
536     g_list_foreach(gui->progresses, (GFunc) free_progress, NULL);
537     g_list_free(gui->progresses);
538     gui->progresses = NULL;
539 
540     if (gui->other_entries_box)
541     {
542         gtk_grid_remove_row (GTK_GRID(gui->entries_grid),
543                              OTHER_ENTRIES_ROW_OFFSET);
544         gtk_widget_destroy(gui->other_entries_box);
545         gui->other_entries_box = NULL;
546     }
547     if (gui->showbox_hash)
548         g_hash_table_destroy(gui->showbox_hash);
549     gui->showbox_last = NULL;
550     gui->showbox_hash = g_hash_table_new_full(
551                             NULL, NULL, NULL, (GDestroyNotify) gtk_widget_destroy);
552 
553     if (gui->parent)
554         gtk_window_set_transient_for(GTK_WINDOW(gui->dialog),
555                                      GTK_WINDOW(gui->parent));
556     gnc_restore_window_size(GNC_PREFS_GROUP_CONNECTION,
557                             GTK_WINDOW(gui->dialog), GTK_WINDOW(gui->parent));
558 
559     gui->keep_alive = TRUE;
560     gui->state = INIT;
561     gui->min_loglevel = GWEN_LoggerLevel_Verbous;
562 
563     cache_passwords = gnc_prefs_get_bool(GNC_PREFS_GROUP_AQBANKING,
564                                          GNC_PREF_REMEMBER_PIN);
565     enable_password_cache(gui, cache_passwords);
566 
567     if (!gui->accepted_certs)
568         gui->accepted_certs = g_hash_table_new_full(
569                                   g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
570     if (!gui->permanently_accepted_certs)
571         gui->permanently_accepted_certs = gnc_ab_get_permanent_certs();
572 
573     LEAVE(" ");
574 }
575 
576 static void
set_running(GncGWENGui * gui)577 set_running(GncGWENGui *gui)
578 {
579     g_return_if_fail(gui);
580 
581     ENTER("gui=%p", gui);
582 
583     gui->state = RUNNING;
584     gtk_widget_set_sensitive(gui->abort_button, TRUE);
585     gtk_widget_set_sensitive(gui->close_button, FALSE);
586     gui->keep_alive = TRUE;
587 
588     LEAVE(" ");
589 }
590 
591 static void
set_finished(GncGWENGui * gui)592 set_finished(GncGWENGui *gui)
593 {
594     g_return_if_fail(gui);
595 
596     ENTER("gui=%p", gui);
597 
598     /* Do not serve as GUI anymore */
599     gui->state = FINISHED;
600     unregister_callbacks(gui);
601 
602     gtk_widget_set_sensitive(gui->abort_button, FALSE);
603     gtk_widget_set_sensitive(gui->close_button, TRUE);
604     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gui->close_checkbutton)))
605         hide_dialog(gui);
606 
607     LEAVE(" ");
608 }
609 
610 static void
set_aborted(GncGWENGui * gui)611 set_aborted(GncGWENGui *gui)
612 {
613     g_return_if_fail(gui);
614 
615     ENTER("gui=%p", gui);
616 
617     /* Do not serve as GUI anymore */
618     gui->state = ABORTED;
619     unregister_callbacks(gui);
620 
621     gtk_widget_set_sensitive(gui->abort_button, FALSE);
622     gtk_widget_set_sensitive(gui->close_button, TRUE);
623     gui->keep_alive = FALSE;
624 
625     LEAVE(" ");
626 }
627 
628 static void
show_dialog(GncGWENGui * gui,gboolean clear_log)629 show_dialog(GncGWENGui *gui, gboolean clear_log)
630 {
631     g_return_if_fail(gui);
632 
633     ENTER("gui=%p, clear_log=%d", gui, clear_log);
634 
635     gtk_widget_show(gui->dialog);
636 
637     gnc_plugin_aqbanking_set_logwindow_visible(TRUE);
638 
639     /* Clear the log window */
640     if (clear_log)
641     {
642         gtk_text_buffer_set_text(
643             gtk_text_view_get_buffer(GTK_TEXT_VIEW(gui->log_text)), "", 0);
644     }
645 
646     LEAVE(" ");
647 }
648 
649 static void
hide_dialog(GncGWENGui * gui)650 hide_dialog(GncGWENGui *gui)
651 {
652     g_return_if_fail(gui);
653 
654     ENTER("gui=%p", gui);
655 
656     /* Hide the dialog */
657     gtk_widget_hide(gui->dialog);
658 
659     gnc_plugin_aqbanking_set_logwindow_visible(FALSE);
660 
661     /* Remember whether the dialog is to be closed when finished */
662     gnc_prefs_set_bool(
663         GNC_PREFS_GROUP_AQBANKING, GNC_PREF_CLOSE_ON_FINISH,
664         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gui->close_checkbutton)));
665 
666     /* Remember size and position of the dialog */
667     gnc_save_window_size(GNC_PREFS_GROUP_CONNECTION, GTK_WINDOW(gui->dialog));
668 
669     /* Do not serve as GUI anymore */
670     gui->state = HIDDEN;
671     unregister_callbacks(gui);
672 
673     LEAVE(" ");
674 }
675 
676 static gboolean
show_progress_cb(gpointer user_data)677 show_progress_cb(gpointer user_data)
678 {
679     Progress *progress = user_data;
680 
681     g_return_val_if_fail(progress, FALSE);
682 
683     ENTER("progress=%p", progress);
684 
685     show_progress(progress->gui, progress);
686 
687     LEAVE(" ");
688     return FALSE;
689 }
690 
691 /**
692  * Show all processes down to and including @a progress.
693  */
694 static void
show_progress(GncGWENGui * gui,Progress * progress)695 show_progress(GncGWENGui *gui, Progress *progress)
696 {
697     GList *item;
698     Progress *current;
699 
700     g_return_if_fail(gui);
701 
702     ENTER("gui=%p, progress=%p", gui, progress);
703 
704     for (item = g_list_last(gui->progresses); item; item = item->prev)
705     {
706         current = (Progress*) item->data;
707 
708         if (!current->source
709                 && current != progress)
710             /* Already showed */
711             continue;
712 
713         /* Show it */
714         if (!item->next)
715         {
716             /* Top-level progress */
717             show_dialog(gui, TRUE);
718             gtk_entry_set_text(GTK_ENTRY(gui->top_entry), current->title);
719         }
720         else if (!item->next->next)
721         {
722             /* Second-level progress */
723             gtk_entry_set_text(GTK_ENTRY(gui->second_entry), current->title);
724         }
725         else
726         {
727             /* Other progress */
728             GtkWidget *entry = gtk_entry_new();
729             GtkWidget *box = gui->other_entries_box;
730             gboolean new_box = box == NULL;
731 
732             gtk_entry_set_text(GTK_ENTRY(entry), current->title);
733             if (new_box)
734             {
735                 gui->other_entries_box = box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
736                 gtk_box_set_homogeneous (GTK_BOX (gui->other_entries_box), TRUE);
737                 gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
738             }
739 
740             gtk_box_pack_start(GTK_BOX(box), entry, TRUE, TRUE, 0);
741             gtk_widget_show(entry);
742             if (new_box)
743             {
744                 gtk_grid_attach (GTK_GRID(gui->entries_grid), box,
745                                  1, OTHER_ENTRIES_ROW_OFFSET, 1, 1);
746                 gtk_widget_show(box);
747             }
748         }
749 
750         if (current->source)
751         {
752             /* Stop delayed call */
753             g_source_remove(current->source);
754             current->source = 0;
755         }
756 
757         if (current == progress)
758             break;
759     }
760 
761     LEAVE(" ");
762 }
763 
764 /**
765  * Hide all processes up to and including @a progress.
766  */
767 static void
hide_progress(GncGWENGui * gui,Progress * progress)768 hide_progress(GncGWENGui *gui, Progress *progress)
769 {
770     GList *item;
771     Progress *current;
772 
773     g_return_if_fail(gui);
774 
775     ENTER("gui=%p, progress=%p", gui, progress);
776 
777     for (item = gui->progresses; item; item = item->next)
778     {
779         current = (Progress*) item->data;
780 
781         if (current->source)
782         {
783             /* Not yet showed */
784             g_source_remove(current->source);
785             current->source = 0;
786             if (current == progress)
787                 break;
788             else
789                 continue;
790         }
791 
792         /* Hide it */
793         if (!item->next)
794         {
795             /* Top-level progress */
796             gtk_entry_set_text(GTK_ENTRY(gui->second_entry), "");
797         }
798         else if (!item->next->next)
799         {
800             /* Second-level progress */
801             gtk_entry_set_text(GTK_ENTRY(gui->second_entry), "");
802         }
803         else
804         {
805             /* Other progress */
806             GtkWidget *box = gui->other_entries_box;
807             GList *entries;
808 
809             g_return_if_fail(box);
810             entries = gtk_container_get_children(GTK_CONTAINER(box));
811             g_return_if_fail(entries);
812             if (entries->next)
813             {
814                 /* Another progress is still to be showed */
815                 gtk_widget_destroy(GTK_WIDGET(g_list_last(entries)->data));
816             }
817             else
818             {
819                 /* Last other progress to be hidden */
820                 gtk_grid_remove_row (GTK_GRID(gui->entries_grid),
821                                      OTHER_ENTRIES_ROW_OFFSET);
822                 /* Box destroyed, Null the reference. */
823                 gui->other_entries_box = NULL;
824             }
825             g_list_free(entries);
826         }
827 
828         if (current == progress)
829             break;
830     }
831 
832     LEAVE(" ");
833 }
834 
835 static void
free_progress(Progress * progress,gpointer unused)836 free_progress(Progress *progress, gpointer unused)
837 {
838     if (progress->source)
839         g_source_remove(progress->source);
840     g_free(progress->title);
841     g_free(progress);
842 }
843 
844 static gboolean
keep_alive(GncGWENGui * gui)845 keep_alive(GncGWENGui *gui)
846 {
847     g_return_val_if_fail(gui, FALSE);
848 
849     ENTER("gui=%p", gui);
850 
851     /* Let the widgets be redrawn */
852     while (g_main_context_iteration(NULL, FALSE));
853 
854     LEAVE("alive=%d", gui->keep_alive);
855     return gui->keep_alive;
856 }
857 
858 static void
cm_close_handler(gpointer user_data)859 cm_close_handler(gpointer user_data)
860 {
861     GncGWENGui *gui = user_data;
862 
863     g_return_if_fail(gui);
864 
865     ENTER("gui=%p", gui);
866 
867     /* FIXME */
868     set_aborted(gui);
869 
870     LEAVE(" ");
871 }
872 
873 static void
erase_password(gchar * password)874 erase_password(gchar *password)
875 {
876     g_return_if_fail(password);
877 
878     ENTER(" ");
879 
880     memset(password, 0, strlen(password));
881     g_free(password);
882 
883     LEAVE(" ");
884 }
885 
886 /**
887  * Find first <[Hh][Tt][Mm][Ll]> and cut off the string there.
888  */
889 static gchar *
strip_html(gchar * text)890 strip_html(gchar *text)
891 {
892     gchar *p, *q;
893 
894     if (!text)
895         return NULL;
896 
897     p = text;
898     while (strchr(p, '<'))
899     {
900         q = p + 1;
901         if (*q && toupper(*q++) == 'H'
902                 && *q && toupper(*q++) == 'T'
903                 && *q && toupper(*q++) == 'M'
904                 && *q && toupper(*q) == 'L')
905         {
906             *p = '\0';
907             return text;
908         }
909         p++;
910     }
911     return text;
912 }
913 
914 static void
915 #ifndef AQBANKING6
get_input(GncGWENGui * gui,guint32 flags,const gchar * title,const gchar * text,gchar ** input,gint min_len,gint max_len)916 get_input(GncGWENGui *gui, guint32 flags, const gchar *title, const gchar *text,
917           gchar **input, gint min_len, gint max_len)
918 #else
919 get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
920                       const gchar *text, const char *mimeType,
921                       const char *pChallenge, uint32_t lChallenge,
922                       gchar **input, gint min_len, gint max_len)
923 #endif
924 {
925     GtkBuilder *builder;
926     GtkWidget *dialog;
927     GtkWidget *heading_label;
928     GtkWidget *input_entry;
929     GtkWidget *confirm_entry;
930     GtkWidget *confirm_label;
931     GtkWidget *remember_pin_checkbutton;
932     GtkImage *optical_challenge;
933 
934     static GncFlickerGui *flickergui = NULL;
935 
936     const gchar *internal_input, *internal_confirmed;
937     gboolean confirm = (flags & GWEN_GUI_INPUT_FLAGS_CONFIRM) != 0;
938     gboolean is_tan = (flags & GWEN_GUI_INPUT_FLAGS_TAN) != 0;
939 
940     g_return_if_fail(input);
941     g_return_if_fail(max_len >= min_len && max_len > 0);
942 
943     ENTER(" ");
944 
945     /* Set up dialog */
946     builder = gtk_builder_new();
947     gnc_builder_add_from_file (builder, "dialog-ab.glade", "aqbanking_password_dialog");
948     dialog = GTK_WIDGET(gtk_builder_get_object (builder, "aqbanking_password_dialog"));
949 
950     heading_label = GTK_WIDGET(gtk_builder_get_object (builder, "heading_pw_label"));
951     input_entry = GTK_WIDGET(gtk_builder_get_object (builder, "input_entry"));
952     confirm_entry = GTK_WIDGET(gtk_builder_get_object (builder, "confirm_entry"));
953     confirm_label = GTK_WIDGET(gtk_builder_get_object (builder, "confirm_label"));
954     remember_pin_checkbutton = GTK_WIDGET(gtk_builder_get_object (builder, "remember_pin"));
955     optical_challenge = GTK_IMAGE(gtk_builder_get_object (builder, "optical_challenge"));
956     gtk_widget_set_visible(GTK_WIDGET(optical_challenge), FALSE);
957 
958     flickergui = g_slice_new(GncFlickerGui);
959     flickergui->flicker_challenge = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_challenge"));
960     flickergui->flicker_marker = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_marker"));
961     flickergui->flicker_hbox = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_hbox"));
962     flickergui->spin_barwidth = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spin_barwidth"));
963     flickergui->spin_delay = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spin_delay"));
964 
965     gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_challenge), FALSE);
966     gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_marker), FALSE);
967     gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_hbox), FALSE);
968     gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_barwidth), FALSE);
969     gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_delay), FALSE);
970 
971     #ifdef AQBANKING6
972     if (g_strcmp0(mimeType,"text/x-flickercode") == 0 && pChallenge != NULL)
973     {
974         /* Chiptan Optic (aka Flicker) */
975         gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_challenge), TRUE);
976         gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_marker), TRUE);
977         gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_hbox), TRUE);
978         gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_barwidth), TRUE);
979         gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_delay), TRUE);
980     }
981     else if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
982     {
983         /* Phototan or Chiptan QR */
984         gtk_widget_set_visible(GTK_WIDGET(optical_challenge), TRUE);
985     }
986     #endif
987     if (is_tan)
988     {
989         gtk_widget_hide(remember_pin_checkbutton);
990     }
991     else
992     {
993         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_pin_checkbutton),
994                                      gui->cache_passwords);
995     }
996 
997     /* Enable the normal input visibility for TAN and for the set SHOW flag */
998     if ((flags & (GWEN_GUI_INPUT_FLAGS_TAN | GWEN_GUI_INPUT_FLAGS_SHOW)) != 0)
999     {
1000         gtk_widget_set_visible(input_entry, TRUE);
1001         gtk_entry_set_visibility(GTK_ENTRY(input_entry), TRUE);
1002     }
1003 
1004     if (gui->dialog)
1005     {
1006         gtk_window_set_transient_for(GTK_WINDOW(dialog),
1007                                      GTK_WINDOW(gui->dialog));
1008     }
1009     else
1010     {
1011         if (gui->parent)
1012             gtk_window_set_transient_for(GTK_WINDOW(dialog),
1013                                          GTK_WINDOW(gui->parent));
1014     }
1015     if (title)
1016         gtk_window_set_title(GTK_WINDOW(dialog), title);
1017 
1018     if (text)
1019     {
1020         gchar *raw_text = strip_html(g_strdup(text));
1021         gtk_label_set_text(GTK_LABEL(heading_label), raw_text);
1022         g_free(raw_text);
1023     }
1024 
1025     #ifdef AQBANKING6
1026     /* Optical challenge. Flickercode sets the mimetype to
1027      * x-flickercode and doesn't set the challenge length */
1028     if (g_strcmp0(mimeType,"text/x-flickercode") == 0 && pChallenge != NULL)
1029     {
1030          /* Chiptan Optic (aka Flicker) */
1031          flickergui->dialog = dialog;
1032          flickergui->input_entry = input_entry;
1033 
1034          ini_flicker_gui(pChallenge, flickergui);
1035          g_slice_free(GncFlickerGui, flickergui);
1036     }
1037     /* While phototan has multiple mimetypes and does set the
1038      * challenge length. */
1039     else if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
1040     {
1041         /* Phototan or Chiptan QR */
1042         // convert PNG and load into widget
1043         // TBD: check mimeType?
1044         guchar *gudata = (guchar*)pChallenge;
1045 
1046         GError *error = NULL;
1047         GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type(mimeType, &error);
1048         GdkPixbuf *pixbuf;
1049 
1050         if(error != NULL)
1051         {
1052             PERR("Pixbuf loader not loaded: %s, perhaps MIME type %s isn't supported.", error->message, mimeType);
1053         }
1054 
1055         gdk_pixbuf_loader_write(loader, gudata, lChallenge, NULL);
1056         gdk_pixbuf_loader_close(loader, NULL);
1057 
1058         pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1059 
1060         g_object_ref(pixbuf);
1061         g_object_unref(loader);
1062 
1063         gtk_image_set_from_pixbuf(optical_challenge, pixbuf);
1064     }
1065 #endif
1066 
1067     if (*input)
1068     {
1069         gtk_entry_set_text(GTK_ENTRY(input_entry), *input);
1070         erase_password(*input);
1071         *input = NULL;
1072     }
1073 
1074     if (confirm)
1075     {
1076         gtk_entry_set_activates_default(GTK_ENTRY(input_entry), FALSE);
1077         gtk_entry_set_activates_default(GTK_ENTRY(confirm_entry), TRUE);
1078         gtk_entry_set_max_length(GTK_ENTRY(input_entry), max_len);
1079         gtk_entry_set_max_length(GTK_ENTRY(confirm_entry), max_len);
1080     }
1081     else
1082     {
1083         gtk_entry_set_activates_default(GTK_ENTRY(input_entry), TRUE);
1084         gtk_entry_set_max_length(GTK_ENTRY(input_entry), max_len);
1085         gtk_widget_hide(confirm_entry);
1086         gtk_widget_hide(confirm_label);
1087     }
1088     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
1089 
1090     /* Ask the user until he enters a valid input or cancels */
1091     while (TRUE)
1092     {
1093         gboolean remember_pin;
1094 
1095         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK)
1096             break;
1097 
1098         if (!is_tan)
1099         {
1100             /* Enable or disable the password cache */
1101             remember_pin = gtk_toggle_button_get_active(
1102                                GTK_TOGGLE_BUTTON(remember_pin_checkbutton));
1103             enable_password_cache(gui, remember_pin);
1104             gnc_prefs_set_bool(GNC_PREFS_GROUP_AQBANKING, GNC_PREF_REMEMBER_PIN,
1105                                remember_pin);
1106         }
1107 
1108         internal_input = gtk_entry_get_text(GTK_ENTRY(input_entry));
1109         if (strlen(internal_input) < min_len)
1110         {
1111             gboolean retval;
1112             gchar *msg = g_strdup_printf(
1113                              _("The PIN needs to be at least %d characters\n"
1114                                "long. Do you want to try again?"), min_len);
1115             retval = gnc_verify_dialog (GTK_WINDOW (gui->parent), TRUE, "%s", msg);
1116             g_free(msg);
1117             if (!retval)
1118                 break;
1119             continue;
1120         }
1121 
1122         if (!confirm)
1123         {
1124             *input = g_strdup(internal_input);
1125             break;
1126         }
1127 
1128         internal_confirmed = gtk_entry_get_text(GTK_ENTRY(confirm_entry));
1129         if (strcmp(internal_input, internal_confirmed) == 0)
1130         {
1131             *input = g_strdup(internal_input);
1132             break;
1133         }
1134     }
1135 
1136     g_object_unref(G_OBJECT(builder));
1137 
1138     /* This trashes passwords in the entries' memory as well */
1139     gtk_widget_destroy(dialog);
1140 
1141     LEAVE("input %s", *input ? "non-NULL" : "NULL");
1142 }
1143 
1144 static gint GNC_GWENHYWFAR_CB
messagebox_cb(GWEN_GUI * gwen_gui,guint32 flags,const gchar * title,const gchar * text,const gchar * b1,const gchar * b2,const gchar * b3,guint32 guiid)1145 messagebox_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *title,
1146               const gchar *text, const gchar *b1, const gchar *b2,
1147               const gchar *b3, guint32 guiid)
1148 {
1149     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1150     GtkWidget *dialog;
1151     GtkWidget *vbox;
1152     GtkWidget *label;
1153     gchar *raw_text;
1154     gint result;
1155 
1156     ENTER("gui=%p, flags=%d, title=%s, b1=%s, b2=%s, b3=%s", gui, flags,
1157           title ? title : "(null)", b1 ? b1 : "(null)", b2 ? b2 : "(null)",
1158           b3 ? b3 : "(null)");
1159 
1160     dialog = gtk_dialog_new_with_buttons(
1161                  title, gui->parent ? GTK_WINDOW(gui->parent) : NULL,
1162                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1163                  b1, 1, b2, 2, b3, 3, (gchar*) NULL);
1164 
1165     raw_text = strip_html(g_strdup(text));
1166     label = gtk_label_new(raw_text);
1167     g_free(raw_text);
1168     gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1169     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1170     gtk_box_set_homogeneous (GTK_BOX (vbox), TRUE);
1171     gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1172     gtk_container_add(GTK_CONTAINER(vbox), label);
1173     gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
1174     gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area (GTK_DIALOG(dialog))), vbox);
1175     gtk_widget_show_all(dialog);
1176 
1177     result = gtk_dialog_run(GTK_DIALOG(dialog));
1178     gtk_widget_destroy(dialog);
1179 
1180     if (result < 1 || result > 3)
1181     {
1182         g_warning("messagebox_cb: Bad result %d", result);
1183         result = 0;
1184     }
1185 
1186     LEAVE("result=%d", result);
1187     return result;
1188 }
1189 
1190 static gint GNC_GWENHYWFAR_CB
inputbox_cb(GWEN_GUI * gwen_gui,guint32 flags,const gchar * title,const gchar * text,gchar * buffer,gint min_len,gint max_len,guint32 guiid)1191 inputbox_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *title,
1192             const gchar *text, gchar *buffer, gint min_len, gint max_len,
1193             guint32 guiid)
1194 {
1195     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1196     gchar *input = NULL;
1197 
1198     g_return_val_if_fail(gui, -1);
1199 
1200     ENTER("gui=%p, flags=%d", gui, flags);
1201 
1202     #ifndef AQBANKING6
1203     get_input(gui, flags, title, text, &input, min_len, max_len);
1204     #else
1205     get_input(gui, flags, title, text, NULL, NULL, 0, &input, min_len, max_len);
1206     #endif
1207 
1208     if (input)
1209     {
1210         /* Copy the input to the result buffer */
1211         strncpy(buffer, input, max_len);
1212         buffer[max_len-1] = '\0';
1213     }
1214 
1215     LEAVE(" ");
1216     return input ? 0 : -1;
1217 }
1218 
1219 static guint32 GNC_GWENHYWFAR_CB
showbox_cb(GWEN_GUI * gwen_gui,guint32 flags,const gchar * title,const gchar * text,guint32 guiid)1220 showbox_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *title,
1221            const gchar *text, guint32 guiid)
1222 {
1223     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1224     GtkWidget *dialog;
1225     guint32 showbox_id;
1226 
1227     g_return_val_if_fail(gui, -1);
1228 
1229     ENTER("gui=%p, flags=%d, title=%s", gui, flags, title ? title : "(null)");
1230 
1231     dialog = gtk_message_dialog_new(
1232                  gui->parent ? GTK_WINDOW(gui->parent) : NULL, 0, GTK_MESSAGE_INFO,
1233                  GTK_BUTTONS_OK, "%s", text);
1234 
1235     if (title)
1236         gtk_window_set_title(GTK_WINDOW(dialog), title);
1237 
1238     g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_hide), NULL);
1239     gtk_widget_show_all(dialog);
1240 
1241     showbox_id = gui->showbox_id++;
1242     g_hash_table_insert(gui->showbox_hash, GUINT_TO_POINTER(showbox_id),
1243                         dialog);
1244     gui->showbox_last = dialog;
1245 
1246     /* Give it a change to be showed */
1247     if (!keep_alive(gui))
1248         showbox_id = 0;
1249 
1250     LEAVE("id=%" G_GUINT32_FORMAT, showbox_id);
1251     return showbox_id;
1252 }
1253 
1254 static void GNC_GWENHYWFAR_CB
hidebox_cb(GWEN_GUI * gwen_gui,guint32 id)1255 hidebox_cb(GWEN_GUI *gwen_gui, guint32 id)
1256 {
1257     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1258 
1259     g_return_if_fail(gui && gui->showbox_hash);
1260 
1261     ENTER("gui=%p, id=%d", gui, id);
1262 
1263     if (id == 0)
1264     {
1265         if (gui->showbox_last)
1266         {
1267             g_hash_table_remove(gui->showbox_hash,
1268                                 GUINT_TO_POINTER(gui->showbox_id));
1269             gui->showbox_last = NULL;
1270         }
1271         else
1272         {
1273             g_warning("hidebox_cb: Last showed message box already destroyed");
1274         }
1275     }
1276     else
1277     {
1278         gpointer p_var;
1279         p_var = g_hash_table_lookup(gui->showbox_hash, GUINT_TO_POINTER(id));
1280         if (p_var)
1281         {
1282             g_hash_table_remove(gui->showbox_hash, GUINT_TO_POINTER(id));
1283             if (p_var == gui->showbox_last)
1284                 gui->showbox_last = NULL;
1285         }
1286         else
1287         {
1288             g_warning("hidebox_cb: Message box %d could not been found", id);
1289         }
1290     }
1291 
1292     LEAVE(" ");
1293 }
1294 
1295 static guint32 GNC_GWENHYWFAR_CB
progress_start_cb(GWEN_GUI * gwen_gui,uint32_t progressFlags,const char * title,const char * text,uint64_t total,uint32_t guiid)1296 progress_start_cb(GWEN_GUI *gwen_gui, uint32_t progressFlags, const char *title,
1297                   const char *text, uint64_t total, uint32_t guiid)
1298 {
1299     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1300     Progress *progress;
1301 
1302     g_return_val_if_fail(gui, -1);
1303 
1304     ENTER("gui=%p, flags=%d, title=%s, total=%" G_GUINT64_FORMAT, gui,
1305           progressFlags, title ? title : "(null)", (guint64)total);
1306 
1307     if (!gui->progresses)
1308     {
1309         /* Top-level progress */
1310         if (progressFlags & GWEN_GUI_PROGRESS_SHOW_PROGRESS)
1311         {
1312             gtk_widget_set_sensitive(gui->top_progress, TRUE);
1313             gtk_progress_bar_set_fraction(
1314                 GTK_PROGRESS_BAR(gui->top_progress), 0.0);
1315             gui->max_actions = total;
1316         }
1317         else
1318         {
1319             gtk_widget_set_sensitive(gui->top_progress, FALSE);
1320             gui->max_actions = -1;
1321         }
1322         set_running(gui);
1323     }
1324 
1325     /* Put progress onto the stack */
1326     progress = g_new0(Progress, 1);
1327     progress->gui = gui;
1328     progress->title = title ? g_strdup(title) : "";
1329     gui->progresses = g_list_prepend(gui->progresses, progress);
1330 
1331     if (progressFlags & GWEN_GUI_PROGRESS_DELAY)
1332     {
1333         /* Show progress later */
1334         progress->source = g_timeout_add(GWEN_GUI_DELAY_SECS * 1000,
1335                                          (GSourceFunc) show_progress_cb,
1336                                          progress);
1337     }
1338     else
1339     {
1340         /* Show it now */
1341         progress->source = 0;
1342         show_progress(gui, progress);
1343     }
1344 
1345     LEAVE(" ");
1346     return g_list_length(gui->progresses);
1347 }
1348 
1349 static gint GNC_GWENHYWFAR_CB
progress_advance_cb(GWEN_GUI * gwen_gui,uint32_t id,uint64_t progress)1350 progress_advance_cb(GWEN_GUI *gwen_gui, uint32_t id, uint64_t progress)
1351 {
1352     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1353 
1354     g_return_val_if_fail(gui, -1);
1355 
1356     ENTER("gui=%p, progress=%" G_GUINT64_FORMAT, gui, (guint64)progress);
1357 
1358     if (id == 1                                  /* top-level progress */
1359             && gui->max_actions > 0                  /* progressbar active */
1360             && progress != GWEN_GUI_PROGRESS_NONE)   /* progressbar update needed */
1361     {
1362         if (progress == GWEN_GUI_PROGRESS_ONE)
1363             gui->current_action++;
1364         else
1365             gui->current_action = progress;
1366 
1367         gtk_progress_bar_set_fraction(
1368             GTK_PROGRESS_BAR(gui->top_progress),
1369             ((gdouble) gui->current_action) / ((gdouble) gui->max_actions));
1370     }
1371 
1372     LEAVE(" ");
1373     return !keep_alive(gui);
1374 }
1375 
1376 static gint GNC_GWENHYWFAR_CB
progress_log_cb(GWEN_GUI * gwen_gui,guint32 id,GWEN_LOGGER_LEVEL level,const gchar * text)1377 progress_log_cb(GWEN_GUI *gwen_gui, guint32 id, GWEN_LOGGER_LEVEL level,
1378                 const gchar *text)
1379 {
1380     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1381     GtkTextBuffer *tb;
1382     GtkTextView *tv;
1383 
1384     g_return_val_if_fail(gui, -1);
1385 
1386     ENTER("gui=%p, text=%s", gui, text ? text : "(null)");
1387 
1388     tv = GTK_TEXT_VIEW(gui->log_text);
1389     tb = gtk_text_view_get_buffer(tv);
1390     gtk_text_buffer_insert_at_cursor(tb, text, -1);
1391     gtk_text_buffer_insert_at_cursor(tb, "\n", -1);
1392 
1393     /* Scroll to the end of the buffer */
1394     gtk_text_view_scroll_to_mark(tv, gtk_text_buffer_get_insert(tb),
1395                                  0.0, FALSE, 0.0, 0.0);
1396 
1397     /* Cache loglevel */
1398     if (level < gui->min_loglevel)
1399         gui->min_loglevel = level;
1400 
1401     LEAVE(" ");
1402     return !keep_alive(gui);
1403 }
1404 
1405 static gint GNC_GWENHYWFAR_CB
progress_end_cb(GWEN_GUI * gwen_gui,guint32 id)1406 progress_end_cb(GWEN_GUI *gwen_gui, guint32 id)
1407 {
1408     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1409     Progress *progress;
1410 
1411     g_return_val_if_fail(gui, -1);
1412     g_return_val_if_fail(id == g_list_length(gui->progresses), -1);
1413 
1414     ENTER("gui=%p, id=%d", gui, id);
1415 
1416     if (gui->state != RUNNING)
1417     {
1418         /* Ignore finishes of progresses we do not track */
1419         LEAVE("not running anymore");
1420         return 0;
1421     }
1422 
1423     /* Hide progress */
1424     progress = (Progress*) gui->progresses->data;
1425     hide_progress(gui, progress);
1426 
1427     /* Remove progress from stack and free memory */
1428     gui->progresses = g_list_delete_link(gui->progresses, gui->progresses);
1429     free_progress(progress, NULL);
1430 
1431     if (!gui->progresses)
1432     {
1433         /* top-level progress finished */
1434         set_finished(gui);
1435     }
1436 
1437     LEAVE(" ");
1438     return 0;
1439 }
1440 
1441 static gint GNC_GWENHYWFAR_CB
1442 #ifndef AQBANKING6
getpassword_cb(GWEN_GUI * gwen_gui,guint32 flags,const gchar * token,const gchar * title,const gchar * text,gchar * buffer,gint min_len,gint max_len,guint32 guiid)1443 getpassword_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *token,
1444                const gchar *title, const gchar *text, gchar *buffer,
1445                gint min_len, gint max_len, guint32 guiid)
1446 #else
1447 getpassword_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *token,
1448                const gchar *title, const gchar *text, gchar *buffer,
1449                gint min_len, gint max_len, GWEN_GUI_PASSWORD_METHOD methodId,
1450                GWEN_DB_NODE *methodParams, guint32 guiid)
1451 #endif
1452 {
1453     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1454     gchar *password = NULL;
1455     gboolean is_tan = (flags & GWEN_GUI_INPUT_FLAGS_TAN) != 0;
1456 
1457     #ifdef AQBANKING6
1458     int opticalMethodId;
1459     const char *mimeType = NULL;
1460     const char *pChallenge = NULL;
1461     uint32_t lChallenge = 0;
1462     #endif
1463 
1464     g_return_val_if_fail(gui, -1);
1465 
1466     #ifdef AQBANKING6
1467     // cf. https://www.aquamaniac.de/rdm/projects/aqbanking/wiki/ImplementTanMethods
1468     if(is_tan && methodId == GWEN_Gui_PasswordMethod_OpticalHHD)
1469     {
1470         /**
1471         * use GWEN_Gui_PasswordMethod_Mask to get the basic method id
1472         *  cf. gui/gui.h of gwenhywfar
1473         */
1474         opticalMethodId=GWEN_DB_GetIntValue(methodParams, "tanMethodId", 0, AB_BANKING_TANMETHOD_TEXT);
1475         switch(opticalMethodId)
1476         {
1477             case AB_BANKING_TANMETHOD_CHIPTAN:
1478                 break;
1479             case AB_BANKING_TANMETHOD_CHIPTAN_OPTIC:
1480                 mimeType = "text/x-flickercode";
1481                 pChallenge = GWEN_DB_GetCharValue(methodParams, "challenge", 0, NULL);
1482                 if ((pChallenge == NULL) || (pChallenge[0] == '\0'))
1483                 {
1484                     /* empty flicker-data */
1485                     return GWEN_ERROR_NO_DATA;
1486                 }
1487                 break;
1488             case AB_BANKING_TANMETHOD_CHIPTAN_USB:
1489                 /**
1490                  * ToDo: is this the same as CHIPTAN_OPTIC ?
1491                  */
1492                  break;
1493             case AB_BANKING_TANMETHOD_PHOTOTAN:
1494             case AB_BANKING_TANMETHOD_CHIPTAN_QR:
1495                 /**
1496                  * image data is in methodParams
1497                  */
1498                 mimeType=GWEN_DB_GetCharValue(methodParams, "mimeType", 0, NULL);
1499                 pChallenge=(const char*) GWEN_DB_GetBinValue(methodParams, "imageData", 0, NULL, 0, &lChallenge);
1500                 if (!(pChallenge && lChallenge))
1501                 {
1502                     /* empty optical data */
1503                     return GWEN_ERROR_NO_DATA;
1504                 }
1505                 break;
1506             default:
1507                 break;
1508         }
1509     }
1510     #endif
1511 
1512     ENTER("gui=%p, flags=%d, token=%s", gui, flags, token ? token : "(null");
1513 
1514     /* Check remembered passwords, excluding TANs */
1515     if (!is_tan && gui->cache_passwords && gui->passwords && token)
1516     {
1517         if (flags & GWEN_GUI_INPUT_FLAGS_RETRY)
1518         {
1519             /* If remembered, remove password from memory */
1520             g_hash_table_remove(gui->passwords, token);
1521         }
1522         else
1523         {
1524             gpointer p_var;
1525             if (g_hash_table_lookup_extended(gui->passwords, token, NULL,
1526                                              &p_var))
1527             {
1528                 /* Copy the password to the result buffer */
1529                 password = p_var;
1530                 strncpy(buffer, password, max_len);
1531                 buffer[max_len-1] = '\0';
1532 
1533                 LEAVE("chose remembered password");
1534                 return 0;
1535             }
1536         }
1537     }
1538 
1539     #ifndef AQBANKING6
1540     get_input(gui, flags, title, text, &password, min_len, max_len);
1541     #else
1542     get_input(gui, flags, title, text, mimeType, pChallenge, lChallenge, &password, min_len, max_len);
1543     #endif
1544 
1545     if (password)
1546     {
1547         /* Copy the password to the result buffer */
1548         strncpy(buffer, password, max_len);
1549         buffer[max_len-1] = '\0';
1550 
1551         if (!is_tan && token)
1552         {
1553             if (gui->cache_passwords && gui->passwords)
1554             {
1555                 /* Remember password */
1556                 DEBUG("Remember password, token=%s", token);
1557                 g_hash_table_insert(gui->passwords, g_strdup(token), password);
1558             }
1559             else
1560             {
1561                 /* Remove the password from memory */
1562                 DEBUG("Forget password, token=%s", token);
1563                 erase_password(password);
1564             }
1565         }
1566     }
1567 
1568     LEAVE(" ");
1569     return password ? 0 : -1;
1570 }
1571 
1572 static gint GNC_GWENHYWFAR_CB
setpasswordstatus_cb(GWEN_GUI * gwen_gui,const gchar * token,const gchar * pin,GWEN_GUI_PASSWORD_STATUS status,guint32 guiid)1573 setpasswordstatus_cb(GWEN_GUI *gwen_gui, const gchar *token, const gchar *pin,
1574                      GWEN_GUI_PASSWORD_STATUS status, guint32 guiid)
1575 {
1576     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1577 
1578     g_return_val_if_fail(gui, -1);
1579 
1580     ENTER("gui=%p, token=%s, status=%d", gui, token ? token : "(null)", status);
1581 
1582     if (gui->passwords && status != GWEN_Gui_PasswordStatus_Ok)
1583     {
1584         /* If remembered, remove password from memory */
1585         g_hash_table_remove(gui->passwords, token);
1586     }
1587 
1588     LEAVE(" ");
1589     return 0;
1590 }
1591 
1592 static gint GNC_GWENHYWFAR_CB
loghook_cb(GWEN_GUI * gwen_gui,const gchar * log_domain,GWEN_LOGGER_LEVEL priority,const gchar * text)1593 loghook_cb(GWEN_GUI *gwen_gui, const gchar *log_domain,
1594            GWEN_LOGGER_LEVEL priority, const gchar *text)
1595 {
1596     if (G_LIKELY(priority < n_log_levels))
1597         g_log(log_domain, log_levels[priority], "%s", text);
1598 
1599     return 1;
1600 }
1601 
1602 static gint GNC_GWENHYWFAR_CB
checkcert_cb(GWEN_GUI * gwen_gui,const GWEN_SSLCERTDESCR * cert,GWEN_IO_LAYER * io,guint32 guiid)1603 checkcert_cb(GWEN_GUI *gwen_gui, const GWEN_SSLCERTDESCR *cert,
1604              GWEN_IO_LAYER *io, guint32 guiid)
1605 {
1606     GncGWENGui *gui = GETDATA_GUI(gwen_gui);
1607     const gchar *hash, *status;
1608     GChecksum *gcheck = g_checksum_new (G_CHECKSUM_MD5);
1609     gchar cert_hash[16];
1610     gint retval, i;
1611     gsize hashlen = 0;
1612 
1613     g_return_val_if_fail(gui && gui->accepted_certs, -1);
1614 
1615     ENTER("gui=%p, cert=%p", gui, cert);
1616 
1617     hash = GWEN_SslCertDescr_GetFingerPrint(cert);
1618     status = GWEN_SslCertDescr_GetStatusText(cert);
1619 
1620     g_checksum_update (gcheck, hash, strlen (hash));
1621     g_checksum_update (gcheck, status, strlen (status));
1622 
1623     /* Did we get the permanently accepted certs from AqBanking? */
1624     if (gui->permanently_accepted_certs)
1625     {
1626         /* Generate a hex string of the cert_hash for usage by AqBanking cert store */
1627         retval = GWEN_DB_GetIntValue(gui->permanently_accepted_certs,
1628 				     g_checksum_get_string (gcheck), 0, -1);
1629         if (retval == 0)
1630         {
1631             /* Certificate is marked as accepted in AqBanking's cert store */
1632 	    g_checksum_free (gcheck);
1633             LEAVE("Certificate accepted by AqBanking's permanent cert store");
1634             return 0;
1635         }
1636     }
1637     else
1638     {
1639         g_warning("Can't check permanently accepted certs from invalid AqBanking cert store.");
1640     }
1641 
1642     g_checksum_get_digest (gcheck, cert_hash, &hashlen);
1643     g_checksum_free (gcheck);
1644     g_assert (hashlen <= sizeof (cert_hash));
1645 
1646     if (g_hash_table_lookup(gui->accepted_certs, cert_hash))
1647     {
1648         /* Certificate has been accepted by Gnucash before */
1649         LEAVE("Automatically accepting certificate");
1650         return 0;
1651     }
1652 
1653     retval = gui->builtin_checkcert(gwen_gui, cert, io, guiid);
1654     if (retval == 0)
1655     {
1656         /* Certificate has been accepted */
1657         g_hash_table_insert(gui->accepted_certs, g_strdup(cert_hash), cert_hash);
1658     }
1659 
1660     LEAVE("retval=%d", retval);
1661     return retval;
1662 }
1663 
1664 gboolean
ggg_delete_event_cb(GtkWidget * widget,GdkEvent * event,gpointer user_data)1665 ggg_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1666 {
1667     GncGWENGui *gui = user_data;
1668 
1669     g_return_val_if_fail(gui, FALSE);
1670 
1671     ENTER("gui=%p, state=%d", gui, gui->state);
1672 
1673     if (gui->state == RUNNING)
1674     {
1675         const char *still_running_msg =
1676             _("The Online Banking job is still running; are you "
1677               "sure you want to cancel?");
1678         if (!gnc_verify_dialog (GTK_WINDOW (gui->dialog), FALSE, "%s", still_running_msg))
1679             return FALSE;
1680 
1681         set_aborted(gui);
1682     }
1683 
1684     hide_dialog(gui);
1685 
1686     LEAVE(" ");
1687     return TRUE;
1688 }
1689 
1690 void
ggg_abort_clicked_cb(GtkButton * button,gpointer user_data)1691 ggg_abort_clicked_cb(GtkButton *button, gpointer user_data)
1692 {
1693     GncGWENGui *gui = user_data;
1694 
1695     g_return_if_fail(gui && gui->state == RUNNING);
1696 
1697     ENTER("gui=%p", gui);
1698 
1699     set_aborted(gui);
1700 
1701     LEAVE(" ");
1702 }
1703 
1704 void
ggg_close_clicked_cb(GtkButton * button,gpointer user_data)1705 ggg_close_clicked_cb(GtkButton *button, gpointer user_data)
1706 {
1707     GncGWENGui *gui = user_data;
1708 
1709     g_return_if_fail(gui);
1710     g_return_if_fail(gui->state == FINISHED || gui->state == ABORTED);
1711 
1712     ENTER("gui=%p", gui);
1713 
1714     hide_dialog(gui);
1715 
1716     LEAVE(" ");
1717 }
1718 
1719 void
ggg_close_toggled_cb(GtkToggleButton * button,gpointer user_data)1720 ggg_close_toggled_cb(GtkToggleButton *button, gpointer user_data)
1721 {
1722     GncGWENGui *gui = user_data;
1723 
1724     g_return_if_fail(gui);
1725     g_return_if_fail(gui->parent);
1726 
1727     ENTER("gui=%p", gui);
1728 
1729     gnc_prefs_set_bool(
1730         GNC_PREFS_GROUP_AQBANKING, GNC_PREF_CLOSE_ON_FINISH,
1731         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)));
1732 
1733     LEAVE(" ");
1734 }
1735