GnuCash  5.6-150-g038405b370+
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"
47 #include "gnc-plugin-aqbanking.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. */
62 static QofLogModule log_module = G_LOG_DOMAIN;
63 
64 /* A unique full-blown GUI, featuring */
65 static GncGWENGui *full_gui = NULL;
66 
67 /* A unique Gwenhywfar GUI for hooking our logging into the gwenhywfar logging
68  * framework */
69 static GWEN_GUI *log_gwen_gui = NULL;
70 
71 /* A mapping from gwenhywfar log levels to glib ones */
72 static 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 };
84 static guint8 n_log_levels = G_N_ELEMENTS(log_levels);
85 
86 /* Macros to determine the GncGWENGui* from a GWEN_GUI* */
87 GWEN_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 
94 typedef struct _Progress Progress;
95 typedef enum _GuiState GuiState;
96 
97 static void register_callbacks(GncGWENGui *gui);
98 static void unregister_callbacks(GncGWENGui *gui);
99 static void setup_dialog(GncGWENGui *gui);
100 static void enable_password_cache(GncGWENGui *gui, gboolean enabled);
101 static void reset_dialog(GncGWENGui *gui);
102 static void set_finished(GncGWENGui *gui);
103 static void set_aborted(GncGWENGui *gui);
104 static void show_dialog(GncGWENGui *gui, gboolean clear_log);
105 static void hide_dialog(GncGWENGui *gui);
106 static gboolean show_progress_cb(gpointer user_data);
107 static void show_progress(GncGWENGui *gui, Progress *progress);
108 static void hide_progress(GncGWENGui *gui, Progress *progress);
109 static void free_progress(Progress *progress, gpointer unused);
110 static gboolean keep_alive(GncGWENGui *gui);
111 static void cm_close_handler(gpointer user_data);
112 static void erase_password(gchar *password);
113 static gchar *strip_html(gchar *text);
114 static 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);
118 static 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);
121 static 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);
124 static guint32 GNC_GWENHYWFAR_CB showbox_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *title,
125  const gchar *text, guint32 guiid);
126 static void GWENHYWFAR_CB hidebox_cb(GWEN_GUI *gwen_gui, guint32 id);
127 static 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);
130 static gint GNC_GWENHYWFAR_CB progress_advance_cb(GWEN_GUI *gwen_gui, uint32_t id,
131  uint64_t new_progress);
132 static gint GNC_GWENHYWFAR_CB progress_log_cb(GWEN_GUI *gwen_gui, guint32 id,
133  GWEN_LOGGER_LEVEL level, const gchar *text);
134 static gint GNC_GWENHYWFAR_CB progress_end_cb(GWEN_GUI *gwen_gui, guint32 id);
135 static 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);
143 static 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);
146 static gint GNC_GWENHYWFAR_CB loghook_cb(GWEN_GUI *gwen_gui, const gchar *log_domain,
147  GWEN_LOGGER_LEVEL priority, const gchar *text);
148 typedef GWEN_SYNCIO GWEN_IO_LAYER;
149 static gint GNC_GWENHYWFAR_CB checkcert_cb(GWEN_GUI *gwen_gui, const GWEN_SSLCERTDESCR *cert,
150  GWEN_IO_LAYER *io, guint32 guiid);
151 
152 gboolean ggg_delete_event_cb(GtkWidget *widget, GdkEvent *event,
153  gpointer user_data);
154 void ggg_abort_clicked_cb(GtkButton *button, gpointer user_data);
155 void ggg_close_clicked_cb(GtkButton *button, gpointer user_data);
156 void ggg_close_toggled_cb(GtkToggleButton *button, gpointer user_data);
157 
158 enum _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 
217 struct _Progress
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 
228 void
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 
244 GncGWENGui *
245 gnc_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 
279 void
280 gnc_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 
289 void
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 
324 void
325 gnc_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 
343 gboolean
345 {
346  return gnc_prefs_get_bool (GNC_PREFS_GROUP_AQBANKING, GNC_PREF_CLOSE_ON_FINISH);
347 }
348 
349 gboolean
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 
380 void
382 {
383  GncGWENGui *gui = full_gui;
384 
385  if (gui)
386  {
387  hide_dialog(gui);
388  }
389 }
390 
391 static void
392 register_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 
422 static void
423 unregister_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 
443 static void
444 setup_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 
493 static void
494 enable_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 
514 static void
515 reset_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 
565 static void
566 set_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 
580 static void
581 set_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 
599 static void
600 set_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 
617 static void
618 show_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 
638 static void
639 hide_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 
665 static gboolean
666 show_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 
683 static void
684 show_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 
756 static void
757 hide_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 
824 static void
825 free_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 
833 static gboolean
834 keep_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 
847 static void
848 cm_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 
862 static void
863 erase_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 
878 static gchar *
879 strip_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 
903 static void
904 get_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 
1124 static gint GNC_GWENHYWFAR_CB
1125 messagebox_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 
1170 static gint GNC_GWENHYWFAR_CB
1171 inputbox_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 
1195 static guint32 GNC_GWENHYWFAR_CB
1196 showbox_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 
1230 static void GNC_GWENHYWFAR_CB
1231 hidebox_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 
1271 static guint32 GNC_GWENHYWFAR_CB
1272 progress_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 
1325 static gint GNC_GWENHYWFAR_CB
1326 progress_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 
1352 static gint GNC_GWENHYWFAR_CB
1353 progress_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 
1381 static gint GNC_GWENHYWFAR_CB
1382 progress_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 
1417 static gint GNC_GWENHYWFAR_CB
1418 getpassword_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 
1534 static gint GNC_GWENHYWFAR_CB
1535 setpasswordstatus_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 
1554 static gint GNC_GWENHYWFAR_CB
1555 loghook_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 
1564 static gint GNC_GWENHYWFAR_CB
1565 checkcert_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 
1626 gboolean
1627 ggg_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 
1652 void
1653 ggg_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 
1666 void
1667 ggg_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 
1681 void
1682 ggg_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 }
gboolean gnc_GWEN_Gui_show_dialog()
Unhides Online Banking Connection Window (Make log visible)
Definition: gnc-gwen-gui.c:350
void gnc_GWEN_Gui_shutdown(void)
Free all memory related to both the full-blown and minimalistic GUI objects.
Definition: gnc-gwen-gui.c:290
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
gboolean gnc_GWEN_Gui_get_close_flag()
Get "Close when finished" flag.
Definition: gnc-gwen-gui.c:344
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
#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
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...
Definition: gnc-gwen-gui.c:245
GWEN_DB_NODE * gnc_ab_get_permanent_certs(void)
get the GWEN_DB_NODE from AqBanking configuration files
void gnc_GWEN_Gui_release(GncGWENGui *gui)
Currently a no-op.
Definition: gnc-gwen-gui.c:280
GUI callbacks for Flicker and ChipTAN(optisch)
void gnc_plugin_aqbanking_set_logwindow_visible(gboolean logwindow_visible)
Set MENU_TOGGLE_ACTION_AB_VIEW_LOGWINDOW.
gboolean gnc_prefs_set_bool(const gchar *group, const gchar *pref_name, gboolean value)
Store a boolean value into the preferences backend.
Definition: gnc-prefs.c:277
Generic api to store and retrieve preferences.
Plugin registration of the AqBanking module.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
void ini_flicker_gui(const char *pChallenge, GncFlickerGui *gui)
Initialize the dialog and drawing area.
GUI callbacks for AqBanking.
void gnc_GWEN_Gui_log_init(void)
Hook our logging into the gwenhywfar logging framework by creating a minimalistic GWEN_GUI with only ...
Definition: gnc-gwen-gui.c:229
AqBanking utility functions.
void gnc_GWEN_Gui_set_close_flag(gboolean close_when_finished)
Set "Close when finished" flag.
Definition: gnc-gwen-gui.c:325
void gnc_GWEN_Gui_hide_dialog()
Hides Online Banking Connection Window (Close log window)
Definition: gnc-gwen-gui.c:381