GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-html.cpp
1/********************************************************************
2 * gnc-html.c -- display HTML with some special gnucash tags. *
3 * *
4 * Copyright (C) 2000 Bill Gribble <grib@billgribble.com> *
5 * Copyright (C) 2001 Linas Vepstas <linas@linas.org> *
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; either version 2 of *
10 * the License, or (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License*
18 * along with this program; if not, contact: *
19 * *
20 * Free Software Foundation Voice: +1-617-542-5942 *
21 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22 * Boston, MA 02110-1301, USA gnu@gnu.org *
23 ********************************************************************/
24
25#include <config.h>
26
27#include <platform.h>
28#if PLATFORM(WINDOWS)
29#include <windows.h>
30#endif
31
32#include <gtk/gtk.h>
33#include <glib/gi18n.h>
34#include <glib/gstdio.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <cstdlib>
38#include <cstring>
39#include <string>
40#include <string_view>
41#include <cerrno>
42#include <fcntl.h>
43#include <unistd.h>
44#include <regex.h>
45
46#include "Account.h"
47#include "print-session.h"
48#include "gnc-engine.h"
49#include "gnc-html.h"
50#include "gnc-html-history.h"
51
52/* indicates the debugging module that this .o belongs to. */
53static QofLogModule log_module = GNC_MOD_HTML;
54
55/* hashes for URLType -> protocol and protocol -> URLType */
56static GHashTable * gnc_html_type_to_proto_hash = nullptr;
57GHashTable * gnc_html_proto_to_type_hash = nullptr;
58
59/* hashes an HTML <object classid="ID"> classid to a handler function */
60GHashTable* gnc_html_object_handlers = nullptr;
61
62/* hashes handlers for loading different URLType data */
63GHashTable* gnc_html_stream_handlers = nullptr;
64
65/* hashes handlers for handling different URLType data */
66GHashTable* gnc_html_url_handlers = nullptr;
67
68/* hashes an HTML <object classid="ID"> classid to a handler function */
69extern GHashTable* gnc_html_object_handlers;
70
71G_DEFINE_ABSTRACT_TYPE(GncHtml, gnc_html, GTK_TYPE_BIN)
72
73static void gnc_html_dispose( GObject* obj );
74static void gnc_html_finalize( GObject* obj );
75/*
76#define GNC_HTML_GET_PRIVATE(o) \
77 ((GncHtmlPrivate*)gnc_html_get_instance_private((GncHtml*)o))
78*/
79#define GNC_HTML_GET_PRIVATE(o) (GNC_HTML(o)->priv)
80
81#include "gnc-html-p.h"
82
83static void
84gnc_html_class_init( GncHtmlClass* klass )
85{
86 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
87
88 gobject_class->dispose = gnc_html_dispose;
89 gobject_class->finalize = gnc_html_finalize;
90
91 klass->show_url = nullptr;
92 klass->show_data = nullptr;
93 klass->reload = nullptr;
94 klass->copy_to_clipboard = nullptr;
95 klass->export_to_file = nullptr;
96 klass->print = nullptr;
97 klass->cancel = nullptr;
98 klass->parse_url = nullptr;
99 klass->set_parent = nullptr;
100}
101
102static void
103gnc_html_init( GncHtml* self )
104{
105 GncHtmlPrivate *priv = self->priv = g_new0( GncHtmlPrivate, 1 );
106
107 priv->container = gtk_scrolled_window_new( nullptr, nullptr );
108 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(priv->container),
109 GTK_POLICY_AUTOMATIC,
110 GTK_POLICY_AUTOMATIC );
111
112 priv->request_info = g_hash_table_new( g_str_hash, g_str_equal );
113 priv->history = gnc_html_history_new();
114}
115
116static void
117gnc_html_dispose( GObject* obj )
118{
119 GncHtml* self = GNC_HTML(obj);
120 GncHtmlPrivate* priv = GNC_HTML_GET_PRIVATE(self);
121
122 if ( priv->container != nullptr )
123 {
124 gtk_widget_destroy( GTK_WIDGET(priv->container) );
125 g_object_unref( G_OBJECT(priv->container) );
126 priv->container = nullptr;
127 }
128 if ( priv->request_info != nullptr )
129 {
130 g_hash_table_destroy( priv->request_info );
131 priv->request_info = nullptr;
132 }
133 if ( priv->history != nullptr )
134 {
135 gnc_html_history_destroy( priv->history );
136 priv->history = nullptr;
137 }
138
139 G_OBJECT_CLASS(gnc_html_parent_class)->dispose( obj );
140}
141
142static void
143gnc_html_finalize( GObject* obj )
144{
145 GncHtml* self = GNC_HTML(obj);
146
147 if ( self->priv != nullptr )
148 {
149 g_free( self->priv );
150 self->priv = nullptr;
151 }
152
153 G_OBJECT_CLASS(gnc_html_parent_class)->finalize( obj );
154}
155
156/***********************************************************************************/
157
158static char*
159extract_machine_name( const gchar* path )
160{
161 constexpr gchar machine_rexp[] = "^(//[^/]*)/*(.*)?$";
162 regex_t compiled_m;
163 constexpr size_t MATCH_LEN = 4;
164 regmatch_t match[MATCH_LEN];
165 gchar* machine = nullptr;
166
167 if ( path == nullptr ) return nullptr;
168
169 regcomp( &compiled_m, machine_rexp, REG_EXTENDED );
170
171 /* step 1: split the machine name away from the path
172 * components */
173 if ( !regexec( &compiled_m, path, MATCH_LEN, match, 0 ) )
174 {
175 /* $1 is the machine name */
176 if ( match[1].rm_so != -1 )
177 {
178 machine = g_strndup( path + match[1].rm_so, match[1].rm_eo - match[1].rm_so );
179 }
180 }
181 regfree(&compiled_m);
182 return machine;
183}
184
185/********************************************************************
186 * gnc_html_parse_url
187 * this takes a URL and determines the protocol type, location, and
188 * possible anchor name from the URL.
189 ********************************************************************/
190
191URLType
192gnc_html_parse_url( GncHtml* self, const gchar* url,
193 gchar** url_location, gchar** url_label ) noexcept
194{
195 constexpr gchar uri_rexp[] = "^(([^:][^:]+):)?([^#]+)?(#(.*))?$";
196 regex_t compiled;
197 constexpr size_t MATCH_LEN = 6;
198 regmatch_t match[MATCH_LEN];
199 gchar* protocol = nullptr;
200 gchar* path = nullptr;
201 gchar* label = nullptr;
202 bool found_protocol = false;
203 bool found_path = false;
204 bool found_label = false;
205 URLType retval;
206 GncHtmlPrivate* priv = GNC_HTML_GET_PRIVATE(self);
207
208 g_return_val_if_fail( self != nullptr, nullptr );
209 g_return_val_if_fail( GNC_IS_HTML(self), nullptr );
210
211 DEBUG( "parsing %s, base_location %s",
212 url ? url : "(null)",
213 self ? (priv->base_location ? priv->base_location
214 : "(null base_location)")
215 : "(null html)");
216
217 regcomp( &compiled, uri_rexp, REG_EXTENDED );
218
219 if ( !regexec( &compiled, url, MATCH_LEN, match, 0 ) )
220 {
221 if ( match[2].rm_so != -1 )
222 {
223 protocol = g_new0( gchar, match[2].rm_eo - match[2].rm_so + 1 );
224 strncpy( protocol, url + match[2].rm_so, match[2].rm_eo - match[2].rm_so );
225 protocol[match[2].rm_eo - match[2].rm_so] = 0;
226 found_protocol = true;
227 }
228 if ( match[3].rm_so != -1 )
229 {
230 path = g_new0( gchar, match[3].rm_eo - match[3].rm_so + 1 );
231 strncpy( path, url + match[3].rm_so, match[3].rm_eo - match[3].rm_so );
232 path[match[3].rm_eo - match[3].rm_so] = 0;
233 found_path = true;
234 }
235 if ( match[5].rm_so != -1 )
236 {
237 label = g_new0( gchar, match[5].rm_eo - match[5].rm_so + 1 );
238 strncpy( label, url + match[5].rm_so, match[5].rm_eo - match[5].rm_so );
239 label[match[5].rm_eo - match[5].rm_so] = 0;
240 found_label = true;
241 }
242 }
243
244 regfree( &compiled );
245
246 if ( found_protocol )
247 {
248 const gpointer p = g_hash_table_lookup( gnc_html_proto_to_type_hash, protocol );
249 retval = static_cast<const char *>(p);
250 if ( retval == nullptr )
251 {
252 PWARN( "unhandled URL type for '%s'", url ? url : "(null)" );
253 retval = URL_TYPE_OTHER;
254 }
255 }
256 else if ( found_label && !found_path )
257 {
258 retval = URL_TYPE_JUMP;
259 }
260 else
261 {
262 if ( self )
263 {
264 retval = priv->base_type;
265 }
266 else
267 {
268 retval = URL_TYPE_FILE;
269 }
270 }
271
272 g_free( protocol );
273
274 if ( !g_strcmp0( retval, URL_TYPE_FILE ) )
275 {
276 if ( !found_protocol && path && self && priv->base_location )
277 {
278 if ( g_path_is_absolute( path ) )
279 {
280 *url_location = g_strdup( path );
281 }
282 else
283 {
284 *url_location = g_build_filename( priv->base_location, path, nullptr );
285 }
286 g_free( path );
287 }
288 else
289 {
290 *url_location = g_strdup( path );
291 g_free( path );
292 }
293
294 }
295 else if ( !g_strcmp0( retval, URL_TYPE_JUMP ) )
296 {
297 *url_location = nullptr;
298 g_free( path );
299
300 }
301 else
302 {
303 /* case URL_TYPE_OTHER: */
304
305 if ( !found_protocol && path && self && priv->base_location )
306 {
307 if ( g_path_is_absolute( path ) )
308 {
309 *url_location = g_build_filename( extract_machine_name( priv->base_location ),
310 path, nullptr );
311 }
312 else
313 {
314 *url_location = g_build_filename( priv->base_location, path, nullptr );
315 }
316 g_free( path );
317 }
318 else
319 {
320 *url_location = g_strdup( path );
321 g_free( path );
322 }
323 }
324
325 *url_label = label;
326 return retval;
327}
328
329/********************************************************************
330 * gnc_html_show_data
331 * display some HTML that the creator of the gnc-html got from
332 * somewhere.
333 ********************************************************************/
334
335void
336gnc_html_show_data( GncHtml* self, const gchar* data, int datalen ) noexcept
337{
338 g_return_if_fail( self != nullptr );
339 g_return_if_fail( GNC_IS_HTML(self) );
340
341 if ( GNC_HTML_GET_CLASS(self)->show_data != nullptr )
342 {
343 GNC_HTML_GET_CLASS(self)->show_data( self, data, datalen );
344 }
345 else
346 {
347 DEBUG( "'show_data' not implemented" );
348 }
349}
350
351
352/********************************************************************
353 * gnc_html_show_url
354 *
355 * open a URL. This is called when the user clicks a link or
356 * for the creator of the gnc_html window to explicitly request
357 * a URL.
358 ********************************************************************/
359
360void
361gnc_html_show_url( GncHtml* self, URLType type,
362 const gchar* location, const gchar* label,
363 gboolean new_window_hint ) noexcept
364{
365 g_return_if_fail( self != nullptr );
366 g_return_if_fail( GNC_IS_HTML(self) );
367
368 char* lc_type = g_ascii_strdown (type, -1);
369
370 if ( GNC_HTML_GET_CLASS(self)->show_url != nullptr )
371 {
372 GNC_HTML_GET_CLASS(self)->show_url( self, lc_type, location, label, new_window_hint );
373 }
374 else
375 {
376 DEBUG( "'show_url' not implemented" );
377 }
378
379 g_free (lc_type);
380}
381
382
383/********************************************************************
384 * gnc_html_reload
385 * reload the current page
386 * if force_rebuild is TRUE, the report is recreated, if FALSE, report
387 * is reloaded ib the view
388 ********************************************************************/
389
390void
391gnc_html_reload( GncHtml* self, gboolean force_rebuild ) noexcept
392{
393 g_return_if_fail( self != nullptr );
394 g_return_if_fail( GNC_IS_HTML(self) );
395
396 if ( GNC_HTML_GET_CLASS(self)->reload != nullptr )
397 {
398 GNC_HTML_GET_CLASS(self)->reload( self, force_rebuild );
399 }
400 else
401 {
402 DEBUG( "'reload' not implemented" );
403 }
404}
405
406/********************************************************************
407 * gnc_html_cancel
408 * cancel any outstanding HTML fetch requests.
409 ********************************************************************/
410
411void
412gnc_html_cancel( GncHtml* self ) noexcept
413{
414 g_return_if_fail( self != nullptr );
415 g_return_if_fail( GNC_IS_HTML(self) );
416
417 if ( GNC_HTML_GET_CLASS(self)->cancel != nullptr )
418 {
419 GNC_HTML_GET_CLASS(self)->cancel( self );
420 }
421 else
422 {
423 DEBUG( "'cancel' not implemented" );
424 }
425}
426
427
428/********************************************************************
429 * gnc_html_destroy
430 * destroy the struct
431 ********************************************************************/
432
433void
434gnc_html_destroy( GncHtml* self ) noexcept
435{
436 g_return_if_fail( self != nullptr );
437 g_return_if_fail( GNC_IS_HTML(self) );
438
439 if ( g_object_is_floating( G_OBJECT(self) ) )
440 {
441 (void)g_object_ref_sink( G_OBJECT(self) );
442 }
443
444 g_object_unref( G_OBJECT(self) );
445}
446
447void
448gnc_html_set_urltype_cb( GncHtml* self, GncHTMLUrltypeCB urltype_cb ) noexcept
449{
450 g_return_if_fail( self != nullptr );
451 g_return_if_fail( GNC_IS_HTML(self) );
452
453 auto priv = GNC_HTML_GET_PRIVATE(self);
454 priv->urltype_cb = urltype_cb;
455}
456
457void
458gnc_html_set_load_cb( GncHtml* self, GncHTMLLoadCB load_cb, gpointer data ) noexcept
459{
460 g_return_if_fail( self != nullptr );
461 g_return_if_fail( GNC_IS_HTML(self) );
462
463 auto priv = GNC_HTML_GET_PRIVATE(self);
464 priv->load_cb = load_cb;
465 priv->load_cb_data = data;
466}
467
468void
469gnc_html_set_flyover_cb( GncHtml* self, GncHTMLFlyoverCB flyover_cb, gpointer data ) noexcept
470{
471 g_return_if_fail( self != nullptr );
472 g_return_if_fail( GNC_IS_HTML(self) );
473
474 auto priv = GNC_HTML_GET_PRIVATE(self);
475 priv->flyover_cb = flyover_cb;
476 priv->flyover_cb_data = data;
477}
478
479void
480gnc_html_set_button_cb( GncHtml* self, GncHTMLButtonCB button_cb, gpointer data ) noexcept
481{
482 g_return_if_fail( self != nullptr );
483 g_return_if_fail( GNC_IS_HTML(self) );
484
485 auto priv = GNC_HTML_GET_PRIVATE(self);
486 priv->button_cb = button_cb;
487 priv->button_cb_data = data;
488}
489
490void
491gnc_html_copy_to_clipboard( GncHtml* self ) noexcept
492{
493 g_return_if_fail( self != nullptr );
494 g_return_if_fail( GNC_IS_HTML(self) );
495
496 if ( GNC_HTML_GET_CLASS(self)->copy_to_clipboard != nullptr )
497 {
498 GNC_HTML_GET_CLASS(self)->copy_to_clipboard( self );
499 }
500 else
501 {
502 DEBUG( "'copy_to_clipboard' not implemented" );
503 }
504}
505
506/**************************************************************
507 * gnc_html_export_to_file : wrapper around the builtin function in gtkhtml
508 **************************************************************/
509
510gboolean
511gnc_html_export_to_file( GncHtml* self, const gchar* filepath ) noexcept
512{
513 g_return_val_if_fail( self != nullptr, FALSE );
514 g_return_val_if_fail( GNC_IS_HTML(self), FALSE );
515
516 if ( GNC_HTML_GET_CLASS(self)->export_to_file != nullptr )
517 {
518 return GNC_HTML_GET_CLASS(self)->export_to_file( self, filepath );
519 }
520 else
521 {
522 DEBUG( "'export_to_file' not implemented" );
523 return FALSE;
524 }
525}
526#ifdef WEBKIT1
527void
528gnc_html_print (GncHtml* self, const char *jobname, gboolean export_pdf) noexcept
529#else
530void
531gnc_html_print (GncHtml* self, const char *jobname) noexcept
532#endif
533{
534 g_return_if_fail( self != nullptr );
535 g_return_if_fail( jobname != nullptr );
536 g_return_if_fail( GNC_IS_HTML(self) );
537
538 if ( GNC_HTML_GET_CLASS(self)->print != nullptr )
539 {
540#ifdef WEBKIT1
541 GNC_HTML_GET_CLASS(self)->print (self, jobname, export_pdf);
542#else
543 GNC_HTML_GET_CLASS(self)->print (self, jobname);
544#endif
545 }
546 else
547 {
548 DEBUG( "'print' not implemented" );
549 }
550}
551
552gnc_html_history *
553gnc_html_get_history( GncHtml* self ) noexcept
554{
555 g_return_val_if_fail( self != nullptr, nullptr );
556 g_return_val_if_fail( GNC_IS_HTML(self), nullptr );
557
558 return GNC_HTML_GET_PRIVATE(self)->history;
559}
560
561
562GtkWidget *
563gnc_html_get_widget( GncHtml* self ) noexcept
564{
565 g_return_val_if_fail( self != nullptr, nullptr );
566 g_return_val_if_fail( GNC_IS_HTML(self), nullptr );
567
568 return GNC_HTML_GET_PRIVATE(self)->container;
569}
570
571
572GtkWidget *
573gnc_html_get_webview( GncHtml* self ) noexcept
574{
575 g_return_val_if_fail (self != nullptr, nullptr);
576 g_return_val_if_fail (GNC_IS_HTML(self), nullptr);
577
578 auto priv = GNC_HTML_GET_PRIVATE(self);
579 GList *sw_list = gtk_container_get_children (GTK_CONTAINER(priv->container));
580 GtkWidget *webview = nullptr;
581
582 if (sw_list) // the scroll window has only one child
583 {
584#ifdef WEBKIT1
585 webview = static_cast<GtkWidget *>(sw_list->data);
586#else
587 GList *vp_list = gtk_container_get_children (GTK_CONTAINER(sw_list->data));
588
589 if (vp_list) // the viewport has only one child
590 {
591 webview = static_cast<GtkWidget *>(vp_list->data);
592 g_list_free (vp_list);
593 }
594#endif
595 }
596 g_list_free (sw_list);
597 return webview;
598}
599
600
601void
602gnc_html_set_parent( GncHtml* self, GtkWindow* parent ) noexcept
603{
604 g_return_if_fail( self != nullptr );
605 g_return_if_fail( GNC_IS_HTML(self) );
606
607 if ( GNC_HTML_GET_CLASS(self)->set_parent != nullptr )
608 {
609 GNC_HTML_GET_CLASS(self)->set_parent( self, parent );
610 }
611 else
612 {
613 DEBUG( "'set_parent' not implemented" );
614 }
615}
616
617/* Register the URLType if it doesn't already exist.
618 * Returns TRUE if successful, FALSE if the type already exists.
619 */
620gboolean
621gnc_html_register_urltype( URLType type, const char *protocol ) noexcept
622{
623 if (!protocol) return FALSE;
624
625 if (!gnc_html_type_to_proto_hash)
626 {
627 gnc_html_type_to_proto_hash = g_hash_table_new (g_str_hash, g_str_equal);
628 gnc_html_proto_to_type_hash = g_hash_table_new (g_str_hash, g_str_equal);
629 }
630
631 char *lc_type = g_ascii_strdown (type, -1);
632 if (g_hash_table_lookup (gnc_html_type_to_proto_hash, lc_type))
633 {
634 g_free (lc_type);
635 return FALSE;
636 }
637
638 char *lc_proto = g_ascii_strdown (protocol, -1);
639 g_hash_table_insert (gnc_html_type_to_proto_hash, lc_type, static_cast<gpointer>(lc_proto));
640 if (*lc_proto)
641 g_hash_table_insert (gnc_html_proto_to_type_hash, static_cast<gpointer>(lc_proto), lc_type);
642
643 return TRUE;
644}
645
646void
647gnc_html_initialize( void ) noexcept
648{
649 static struct
650 {
651 URLType type;
652 const char *protocol;
653 } types[] =
654 {
655 { URL_TYPE_FILE, "file" },
656 { URL_TYPE_JUMP, "" },
657 { URL_TYPE_HTTP, "http" },
658 { URL_TYPE_FTP, "ftp" },
659 { URL_TYPE_SECURE, "https" },
660 { URL_TYPE_REGISTER, "gnc-register" },
661 { URL_TYPE_ACCTTREE, "gnc-acct-tree" },
662 { URL_TYPE_REPORT, "gnc-report" },
663 { URL_TYPE_OPTIONS, "gnc-options" },
664 { URL_TYPE_SCHEME, "gnc-scm" },
665 { URL_TYPE_HELP, "gnc-help" },
666 { URL_TYPE_XMLDATA, "gnc-xml" },
667 { URL_TYPE_PRICE, "gnc-price" },
668 { URL_TYPE_BUDGET, "gnc-budget" },
669 { URL_TYPE_OTHER, "" }
670 };
671
672 for (const auto& elem : types)
673 {
674 (void) gnc_html_register_urltype (elem.type, elem.protocol);
675 }
676}
677
686gchar*
687gnc_build_url( URLType type, const gchar* location, const gchar* label ) noexcept
688{
689 DEBUG(" ");
690 char *lc_type = g_ascii_strdown (type, -1);
691 const gpointer p = g_hash_table_lookup (gnc_html_type_to_proto_hash, lc_type);
692 const char *type_name = static_cast<const char *>(p);
693 g_free (static_cast<gpointer>(lc_type));
694 if (!type_name)
695 type_name = "";
696
697 if (label)
698 {
699 return g_strdup_printf("%s%s%s#%s", type_name, (*type_name ? ":" : ""),
700 (location ? location : ""),
701 label ? label : "");
702 }
703 else
704 {
705 return g_strdup_printf("%s%s%s", type_name, (*type_name ? ":" : ""),
706 (location ? location : ""));
707 }
708}
709
710void
711gnc_html_register_object_handler( const char * classid,
712 GncHTMLObjectCB hand ) noexcept
713{
714 g_return_if_fail( classid != nullptr );
715
716 if ( gnc_html_object_handlers == nullptr )
717 {
718 gnc_html_object_handlers = g_hash_table_new( g_str_hash, g_str_equal );
719 }
720
721 gnc_html_unregister_object_handler( classid );
722 if ( hand != nullptr )
723 {
724 gchar *lc_id = g_ascii_strdown (classid, -1);
725 g_hash_table_insert( gnc_html_object_handlers, lc_id,
726 reinterpret_cast<gpointer>(hand) );
727 }
728}
729
730void
731gnc_html_unregister_object_handler( const gchar* classid ) noexcept
732{
733 gchar* keyptr = nullptr;
734 gchar* valptr = nullptr;
735 gchar* lc_id = g_ascii_strdown (classid, -1);
736
737 if ( g_hash_table_lookup_extended( gnc_html_object_handlers,
738 lc_id,
739 reinterpret_cast<gpointer *>(&keyptr),
740 reinterpret_cast<gpointer *>(&valptr) ) )
741 {
742 g_hash_table_remove( gnc_html_object_handlers, lc_id );
743 g_free( keyptr );
744 }
745 g_free( lc_id );
746}
747
748void
749gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand ) noexcept
750{
751 g_return_if_fail( url_type != nullptr && *url_type != '\0' );
752
753 if ( gnc_html_stream_handlers == nullptr )
754 {
755 gnc_html_stream_handlers = g_hash_table_new( g_str_hash, g_str_equal );
756 }
757
758 gnc_html_unregister_stream_handler( url_type );
759 if ( hand != nullptr )
760 {
761 char* lc_type = g_ascii_strdown (url_type, -1);
762 g_hash_table_insert( gnc_html_stream_handlers, lc_type,
763 reinterpret_cast<gpointer>(hand) );
764 }
765}
766
767void
768gnc_html_unregister_stream_handler( URLType url_type ) noexcept
769{
770 char* lc_type = g_ascii_strdown (url_type, -1);
771 g_hash_table_remove( gnc_html_stream_handlers, lc_type );
772 g_free(lc_type);
773}
774
775void
776gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand ) noexcept
777{
778 g_return_if_fail( url_type != nullptr && *url_type != '\0' );
779
780 if ( gnc_html_url_handlers == nullptr )
781 {
782 gnc_html_url_handlers = g_hash_table_new( g_str_hash, g_str_equal );
783 }
784
785 gnc_html_unregister_url_handler( url_type );
786 if ( hand != nullptr )
787 {
788 char* lc_type = g_ascii_strdown (url_type, -1);
789 g_hash_table_insert( gnc_html_url_handlers, lc_type,
790 reinterpret_cast<gpointer>(hand) );
791 }
792}
793
794void
795gnc_html_unregister_url_handler( URLType url_type ) noexcept
796{
797 char* lc_type = g_ascii_strdown (url_type, -1);
798 g_hash_table_remove( gnc_html_url_handlers, lc_type );
799 g_free(lc_type);
800}
Account handling public routines.
All type declarations for the whole Gnucash engine.
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
Functions for printing.