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