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