GnuCash  5.6-150-g038405b370+
gnc-html.c
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 <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <regex.h>
43 
44 #include "Account.h"
45 #include "print-session.h"
46 #include "gnc-engine.h"
47 #include "gnc-html.h"
48 #include "gnc-html-history.h"
49 
50 /* indicates the debugging module that this .o belongs to. */
51 static QofLogModule log_module = GNC_MOD_HTML;
52 
53 /* hashes for URLType -> protocol and protocol -> URLType */
54 static GHashTable * gnc_html_type_to_proto_hash = NULL;
55 GHashTable * gnc_html_proto_to_type_hash = NULL;
56 
57 /* hashes an HTML <object classid="ID"> classid to a handler function */
58 GHashTable* gnc_html_object_handlers = NULL;
59 
60 /* hashes handlers for loading different URLType data */
61 GHashTable* gnc_html_stream_handlers = NULL;
62 
63 /* hashes handlers for handling different URLType data */
64 GHashTable* gnc_html_url_handlers = NULL;
65 
66 /* hashes an HTML <object classid="ID"> classid to a handler function */
67 extern GHashTable* gnc_html_object_handlers;
68 
69 G_DEFINE_ABSTRACT_TYPE(GncHtml, gnc_html, GTK_TYPE_BIN)
70 
71 static void gnc_html_dispose( GObject* obj );
72 static void gnc_html_finalize( GObject* obj );
73 /*
74 #define GNC_HTML_GET_PRIVATE(o) \
75  ((GncHtmlPrivate*)gnc_html_get_instance_private((GncHtml*)o))
76 */
77 #define GNC_HTML_GET_PRIVATE(o) (GNC_HTML(o)->priv)
78 
79 #include "gnc-html-p.h"
80 
81 static void
82 gnc_html_class_init( GncHtmlClass* klass )
83 {
84  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
85 
86  gobject_class->dispose = gnc_html_dispose;
87  gobject_class->finalize = gnc_html_finalize;
88 
89  klass->show_url = NULL;
90  klass->show_data = NULL;
91  klass->reload = NULL;
92  klass->copy_to_clipboard = NULL;
93  klass->export_to_file = NULL;
94  klass->print = NULL;
95  klass->cancel = NULL;
96  klass->parse_url = NULL;
97  klass->set_parent = NULL;
98 }
99 
100 static void
101 gnc_html_init( GncHtml* self )
102 {
103  GncHtmlPrivate* priv;
104  priv = self->priv = g_new0( GncHtmlPrivate, 1 );
105 
106  priv->container = gtk_scrolled_window_new( NULL, NULL );
107  gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(priv->container),
108  GTK_POLICY_AUTOMATIC,
109  GTK_POLICY_AUTOMATIC );
110 
111  priv->request_info = g_hash_table_new( g_str_hash, g_str_equal );
112  priv->history = gnc_html_history_new();
113 }
114 
115 static void
116 gnc_html_dispose( GObject* obj )
117 {
118  GncHtml* self = GNC_HTML(obj);
119  GncHtmlPrivate* priv = GNC_HTML_GET_PRIVATE(self);
120 
121  if ( priv->container != NULL )
122  {
123  gtk_widget_destroy( GTK_WIDGET(priv->container) );
124  g_object_unref( G_OBJECT(priv->container) );
125  priv->container = NULL;
126  }
127  if ( priv->request_info != NULL )
128  {
129  g_hash_table_destroy( priv->request_info );
130  priv->request_info = NULL;
131  }
132  if ( priv->history != NULL )
133  {
134  gnc_html_history_destroy( priv->history );
135  priv->history = NULL;
136  }
137 
138  G_OBJECT_CLASS(gnc_html_parent_class)->dispose( obj );
139 }
140 
141 static void
142 gnc_html_finalize( GObject* obj )
143 {
144  GncHtml* self = GNC_HTML(obj);
145 
146  if ( self->priv != NULL )
147  {
148  g_free( self->priv );
149  self->priv = NULL;
150  }
151 
152  G_OBJECT_CLASS(gnc_html_parent_class)->finalize( obj );
153 }
154 
155 /***********************************************************************************/
156 
157 static char*
158 extract_machine_name( const gchar* path )
159 {
160  gchar machine_rexp[] = "^(//[^/]*)/*(.*)?$";
161  regex_t compiled_m;
162  regmatch_t match[4];
163  gchar* machine = NULL;
164 
165  if ( path == NULL ) return NULL;
166 
167  regcomp( &compiled_m, machine_rexp, REG_EXTENDED );
168 
169  /* step 1: split the machine name away from the path
170  * components */
171  if ( !regexec( &compiled_m, path, 4, match, 0 ) )
172  {
173  /* $1 is the machine name */
174  if ( match[1].rm_so != -1 )
175  {
176  machine = g_strndup( path + match[1].rm_so, match[1].rm_eo - match[1].rm_so );
177  }
178  }
179  regfree(&compiled_m);
180  return machine;
181 }
182 
183 /********************************************************************
184  * gnc_html_parse_url
185  * this takes a URL and determines the protocol type, location, and
186  * possible anchor name from the URL.
187  ********************************************************************/
188 
189 URLType
190 gnc_html_parse_url( GncHtml* self, const gchar* url,
191  gchar** url_location, gchar** url_label )
192 {
193  gchar uri_rexp[] = "^(([^:][^:]+):)?([^#]+)?(#(.*))?$";
194  regex_t compiled;
195  regmatch_t match[6];
196  gchar* protocol = NULL;
197  gchar* path = NULL;
198  gchar* label = NULL;
199  gboolean found_protocol = FALSE;
200  gboolean found_path = FALSE;
201  gboolean found_label = FALSE;
202  URLType retval;
203  GncHtmlPrivate* priv = GNC_HTML_GET_PRIVATE(self);
204 
205  g_return_val_if_fail( self != NULL, NULL );
206  g_return_val_if_fail( GNC_IS_HTML(self), NULL );
207 
208  DEBUG( "parsing %s, base_location %s",
209  url ? url : "(null)",
210  self ? (priv->base_location ? priv->base_location
211  : "(null base_location)")
212  : "(null html)");
213 
214  regcomp( &compiled, uri_rexp, REG_EXTENDED );
215 
216  if ( !regexec( &compiled, url, 6, match, 0 ) )
217  {
218  if ( match[2].rm_so != -1 )
219  {
220  protocol = g_new0( gchar, match[2].rm_eo - match[2].rm_so + 1 );
221  strncpy( protocol, url + match[2].rm_so, match[2].rm_eo - match[2].rm_so );
222  protocol[match[2].rm_eo - match[2].rm_so] = 0;
223  found_protocol = TRUE;
224  }
225  if ( match[3].rm_so != -1 )
226  {
227  path = g_new0( gchar, match[3].rm_eo - match[3].rm_so + 1 );
228  strncpy( path, url + match[3].rm_so, match[3].rm_eo - match[3].rm_so );
229  path[match[3].rm_eo - match[3].rm_so] = 0;
230  found_path = TRUE;
231  }
232  if ( match[5].rm_so != -1 )
233  {
234  label = g_new0( gchar, match[5].rm_eo - match[5].rm_so + 1 );
235  strncpy( label, url + match[5].rm_so, match[5].rm_eo - match[5].rm_so );
236  label[match[5].rm_eo - match[5].rm_so] = 0;
237  found_label = TRUE;
238  }
239  }
240 
241  regfree( &compiled );
242 
243  if ( found_protocol )
244  {
245  retval = g_hash_table_lookup( gnc_html_proto_to_type_hash, protocol );
246  if ( retval == NULL )
247  {
248  PWARN( "unhandled URL type for '%s'", url ? url : "(null)" );
249  retval = URL_TYPE_OTHER;
250  }
251  }
252  else if ( found_label && !found_path )
253  {
254  retval = URL_TYPE_JUMP;
255  }
256  else
257  {
258  if ( self )
259  {
260  retval = priv->base_type;
261  }
262  else
263  {
264  retval = URL_TYPE_FILE;
265  }
266  }
267 
268  g_free( protocol );
269 
270  if ( !g_strcmp0( retval, URL_TYPE_FILE ) )
271  {
272  if ( !found_protocol && path && self && priv->base_location )
273  {
274  if ( g_path_is_absolute( path ) )
275  {
276  *url_location = g_strdup( path );
277  }
278  else
279  {
280  *url_location = g_build_filename( priv->base_location, path, (gchar*)NULL );
281  }
282  g_free( path );
283  }
284  else
285  {
286  *url_location = g_strdup( path );
287  g_free( path );
288  }
289 
290  }
291  else if ( !g_strcmp0( retval, URL_TYPE_JUMP ) )
292  {
293  *url_location = NULL;
294  g_free( path );
295 
296  }
297  else
298  {
299  /* case URL_TYPE_OTHER: */
300 
301  if ( !found_protocol && path && self && priv->base_location )
302  {
303  if ( g_path_is_absolute( path ) )
304  {
305  *url_location = g_build_filename( extract_machine_name( priv->base_location ),
306  path, (gchar*)NULL );
307  }
308  else
309  {
310  *url_location = g_build_filename( priv->base_location, path, (gchar*)NULL );
311  }
312  g_free( path );
313  }
314  else
315  {
316  *url_location = g_strdup( path );
317  g_free( path );
318  }
319  }
320 
321  *url_label = label;
322  return retval;
323 }
324 
325 /********************************************************************
326  * gnc_html_show_data
327  * display some HTML that the creator of the gnc-html got from
328  * somewhere.
329  ********************************************************************/
330 
331 void
332 gnc_html_show_data( GncHtml* self, const gchar* data, int datalen )
333 {
334  g_return_if_fail( self != NULL );
335  g_return_if_fail( GNC_IS_HTML(self) );
336 
337  if ( GNC_HTML_GET_CLASS(self)->show_data != NULL )
338  {
339  GNC_HTML_GET_CLASS(self)->show_data( self, data, datalen );
340  }
341  else
342  {
343  DEBUG( "'show_data' not implemented" );
344  }
345 }
346 
347 
348 /********************************************************************
349  * gnc_html_show_url
350  *
351  * open a URL. This is called when the user clicks a link or
352  * for the creator of the gnc_html window to explicitly request
353  * a URL.
354  ********************************************************************/
355 
356 void
357 gnc_html_show_url( GncHtml* self, URLType type,
358  const gchar* location, const gchar* label,
359  gboolean new_window_hint )
360 {
361  char* lc_type = NULL;
362 
363  g_return_if_fail( self != NULL );
364  g_return_if_fail( GNC_IS_HTML(self) );
365 
366  lc_type = g_ascii_strdown (type, -1);
367  if ( GNC_HTML_GET_CLASS(self)->show_url != NULL )
368  {
369  GNC_HTML_GET_CLASS(self)->show_url( self, lc_type, location, label, new_window_hint );
370  }
371  else
372  {
373  DEBUG( "'show_url' not implemented" );
374  }
375 
376  g_free (lc_type);
377 }
378 
379 
380 /********************************************************************
381  * gnc_html_reload
382  * reload the current page
383  * if force_rebuild is TRUE, the report is recreated, if FALSE, report
384  * is reloaded ib the view
385  ********************************************************************/
386 
387 void
388 gnc_html_reload( GncHtml* self, gboolean force_rebuild )
389 {
390  g_return_if_fail( self != NULL );
391  g_return_if_fail( GNC_IS_HTML(self) );
392 
393  if ( GNC_HTML_GET_CLASS(self)->reload != NULL )
394  {
395  GNC_HTML_GET_CLASS(self)->reload( self, force_rebuild );
396  }
397  else
398  {
399  DEBUG( "'reload' not implemented" );
400  }
401 }
402 
403 /********************************************************************
404  * gnc_html_cancel
405  * cancel any outstanding HTML fetch requests.
406  ********************************************************************/
407 
408 void
409 gnc_html_cancel( GncHtml* self )
410 {
411  g_return_if_fail( self != NULL );
412  g_return_if_fail( GNC_IS_HTML(self) );
413 
414  if ( GNC_HTML_GET_CLASS(self)->cancel != NULL )
415  {
416  GNC_HTML_GET_CLASS(self)->cancel( self );
417  }
418  else
419  {
420  DEBUG( "'cancel' not implemented" );
421  }
422 }
423 
424 
425 /********************************************************************
426  * gnc_html_destroy
427  * destroy the struct
428  ********************************************************************/
429 
430 void
431 gnc_html_destroy( GncHtml* self )
432 {
433  g_return_if_fail( self != NULL );
434  g_return_if_fail( GNC_IS_HTML(self) );
435 
436  if ( g_object_is_floating( G_OBJECT(self) ) )
437  {
438  (void)g_object_ref_sink( G_OBJECT(self) );
439  }
440 
441  g_object_unref( G_OBJECT(self) );
442 }
443 
444 void
445 gnc_html_set_urltype_cb( GncHtml* self, GncHTMLUrltypeCB urltype_cb )
446 {
447  GncHtmlPrivate* priv;
448 
449  g_return_if_fail( self != NULL );
450  g_return_if_fail( GNC_IS_HTML(self) );
451 
452  priv = GNC_HTML_GET_PRIVATE(self);
453  priv->urltype_cb = urltype_cb;
454 }
455 
456 void
457 gnc_html_set_load_cb( GncHtml* self, GncHTMLLoadCB load_cb, gpointer data )
458 {
459  GncHtmlPrivate* priv;
460 
461  g_return_if_fail( self != NULL );
462  g_return_if_fail( GNC_IS_HTML(self) );
463 
464  priv = GNC_HTML_GET_PRIVATE(self);
465  priv->load_cb = load_cb;
466  priv->load_cb_data = data;
467 }
468 
469 void
470 gnc_html_set_flyover_cb( GncHtml* self, GncHTMLFlyoverCB flyover_cb, gpointer data )
471 {
472  GncHtmlPrivate* priv;
473 
474  g_return_if_fail( self != NULL );
475  g_return_if_fail( GNC_IS_HTML(self) );
476 
477  priv = GNC_HTML_GET_PRIVATE(self);
478  priv->flyover_cb = flyover_cb;
479  priv->flyover_cb_data = data;
480 }
481 
482 void
483 gnc_html_set_button_cb( GncHtml* self, GncHTMLButtonCB button_cb, gpointer data )
484 {
485  GncHtmlPrivate* priv;
486 
487  g_return_if_fail( self != NULL );
488  g_return_if_fail( GNC_IS_HTML(self) );
489 
490  priv = GNC_HTML_GET_PRIVATE(self);
491  priv->button_cb = button_cb;
492  priv->button_cb_data = data;
493 }
494 
495 void
496 gnc_html_copy_to_clipboard( GncHtml* self )
497 {
498  g_return_if_fail( self != NULL );
499  g_return_if_fail( GNC_IS_HTML(self) );
500 
501  if ( GNC_HTML_GET_CLASS(self)->copy_to_clipboard != NULL )
502  {
503  GNC_HTML_GET_CLASS(self)->copy_to_clipboard( self );
504  }
505  else
506  {
507  DEBUG( "'copy_to_clipboard' not implemented" );
508  }
509 }
510 
511 /**************************************************************
512  * gnc_html_export_to_file : wrapper around the builtin function in gtkhtml
513  **************************************************************/
514 
515 gboolean
516 gnc_html_export_to_file( GncHtml* self, const gchar* filepath )
517 {
518  g_return_val_if_fail( self != NULL, FALSE );
519  g_return_val_if_fail( GNC_IS_HTML(self), FALSE );
520 
521  if ( GNC_HTML_GET_CLASS(self)->export_to_file != NULL )
522  {
523  return GNC_HTML_GET_CLASS(self)->export_to_file( self, filepath );
524  }
525  else
526  {
527  DEBUG( "'export_to_file' not implemented" );
528  return FALSE;
529  }
530 }
531 #ifdef WEBKIT1
532 void
533 gnc_html_print (GncHtml* self, const char *jobname, gboolean export_pdf)
534 #else
535 void
536 gnc_html_print (GncHtml* self, const char *jobname)
537 #endif
538 {
539  g_return_if_fail( self != NULL );
540  g_return_if_fail( jobname != NULL );
541  g_return_if_fail( GNC_IS_HTML(self) );
542 
543  if ( GNC_HTML_GET_CLASS(self)->print != NULL )
544  {
545 #ifdef WEBKIT1
546  GNC_HTML_GET_CLASS(self)->print (self, jobname, export_pdf);
547 #else
548  GNC_HTML_GET_CLASS(self)->print (self, jobname);
549 #endif
550  }
551  else
552  {
553  DEBUG( "'print' not implemented" );
554  }
555 }
556 
557 gnc_html_history *
558 gnc_html_get_history( GncHtml* self )
559 {
560  g_return_val_if_fail( self != NULL, NULL );
561  g_return_val_if_fail( GNC_IS_HTML(self), NULL );
562 
563  return GNC_HTML_GET_PRIVATE(self)->history;
564 }
565 
566 
567 GtkWidget *
568 gnc_html_get_widget( GncHtml* self )
569 {
570  g_return_val_if_fail( self != NULL, NULL );
571  g_return_val_if_fail( GNC_IS_HTML(self), NULL );
572 
573  return GNC_HTML_GET_PRIVATE(self)->container;
574 }
575 
576 
577 GtkWidget *
578 gnc_html_get_webview( GncHtml* self )
579 {
580  GncHtmlPrivate* priv;
581  GList *sw_list = NULL;
582  GtkWidget *webview = NULL;
583 
584  g_return_val_if_fail (self != NULL, NULL);
585  g_return_val_if_fail (GNC_IS_HTML(self), NULL);
586 
587  priv = GNC_HTML_GET_PRIVATE(self);
588  sw_list = gtk_container_get_children (GTK_CONTAINER(priv->container));
589 
590  if (sw_list) // the scroll window has only one child
591  {
592 #ifdef WEBKIT1
593  webview = sw_list->data;
594 #else
595  GList *vp_list = gtk_container_get_children (GTK_CONTAINER(sw_list->data));
596 
597  if (vp_list) // the viewport has only one child
598  {
599  webview = vp_list->data;
600  g_list_free (vp_list);
601  }
602 #endif
603  }
604  g_list_free (sw_list);
605  return webview;
606 }
607 
608 
609 void
610 gnc_html_set_parent( GncHtml* self, GtkWindow* parent )
611 {
612  g_return_if_fail( self != NULL );
613  g_return_if_fail( GNC_IS_HTML(self) );
614 
615  if ( GNC_HTML_GET_CLASS(self)->set_parent != NULL )
616  {
617  GNC_HTML_GET_CLASS(self)->set_parent( self, parent );
618  }
619  else
620  {
621  DEBUG( "'set_parent' not implemented" );
622  }
623 }
624 
625 /* Register the URLType if it doesn't already exist.
626  * Returns TRUE if successful, FALSE if the type already exists.
627  */
628 gboolean
629 gnc_html_register_urltype( URLType type, const char *protocol )
630 {
631  char* lc_type = NULL;
632  char *lc_proto = NULL;
633 
634  if (!gnc_html_type_to_proto_hash)
635  {
636  gnc_html_type_to_proto_hash = g_hash_table_new (g_str_hash, g_str_equal);
637  gnc_html_proto_to_type_hash = g_hash_table_new (g_str_hash, g_str_equal);
638  }
639  if (!protocol) return FALSE;
640 
641  lc_type = g_ascii_strdown (type, -1);
642  if (g_hash_table_lookup (gnc_html_type_to_proto_hash, lc_type))
643  {
644  g_free (lc_type);
645  return FALSE;
646  }
647 
648  lc_proto = g_ascii_strdown (protocol, -1);
649  g_hash_table_insert (gnc_html_type_to_proto_hash, lc_type, (gpointer)lc_proto);
650  if (*lc_proto)
651  g_hash_table_insert (gnc_html_proto_to_type_hash, (gpointer)lc_proto, lc_type);
652 
653  return TRUE;
654 }
655 
656 void
657 gnc_html_initialize( void )
658 {
659  int i;
660  static struct
661  {
662  URLType type;
663  char * protocol;
664  } types[] =
665  {
666  { URL_TYPE_FILE, "file" },
667  { URL_TYPE_JUMP, "" },
668  { URL_TYPE_HTTP, "http" },
669  { URL_TYPE_FTP, "ftp" },
670  { URL_TYPE_SECURE, "https" },
671  { URL_TYPE_REGISTER, "gnc-register" },
672  { URL_TYPE_ACCTTREE, "gnc-acct-tree" },
673  { URL_TYPE_REPORT, "gnc-report" },
674  { URL_TYPE_OPTIONS, "gnc-options" },
675  { URL_TYPE_SCHEME, "gnc-scm" },
676  { URL_TYPE_HELP, "gnc-help" },
677  { URL_TYPE_XMLDATA, "gnc-xml" },
678  { URL_TYPE_PRICE, "gnc-price" },
679  { URL_TYPE_BUDGET, "gnc-budget" },
680  { URL_TYPE_OTHER, "" },
681  { NULL, NULL }
682  };
683 
684  for (i = 0; types[i].type; i++)
685  gnc_html_register_urltype (types[i].type, types[i].protocol);
686 }
687 
696 gchar*
697 gnc_build_url( URLType type, const gchar* location, const gchar* label )
698 {
699  char* lc_type = NULL;
700  char * type_name;
701 
702  DEBUG(" ");
703  lc_type = g_ascii_strdown (type, -1);
704  type_name = g_hash_table_lookup (gnc_html_type_to_proto_hash, lc_type);
705  g_free (lc_type);
706  if (!type_name)
707  type_name = "";
708 
709  if (label)
710  {
711  return g_strdup_printf("%s%s%s#%s", type_name, (*type_name ? ":" : ""),
712  (location ? location : ""),
713  label ? label : "");
714  }
715  else
716  {
717  return g_strdup_printf("%s%s%s", type_name, (*type_name ? ":" : ""),
718  (location ? location : ""));
719  }
720 }
721 
722 /********************************************************************
723  * gnc_html_encode_string
724  * RFC 1738 encoding of string for submission with an HTML form.
725  * GPL code lifted from gtkhtml. copyright notice:
726  *
727  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
728  * Copyright (C) 1997 Torben Weis (weis@kde.org)
729  * Copyright (C) 1999 Helix Code, Inc.
730  ********************************************************************/
731 
732 char *
733 gnc_html_encode_string(const char * str)
734 {
735  static gchar *safe = "$-._!*(),"; /* RFC 1738 */
736  unsigned pos = 0;
737  GString *encoded = g_string_new ("");
738  static const size_t buf_size = 5;
739  gchar buffer[buf_size], *ptr;
740  guchar c;
741 
742  if (!str) return NULL;
743 
744  while (pos < strlen(str))
745  {
746  c = (unsigned char) str[pos];
747 
748  if ((( c >= 'A') && ( c <= 'Z')) ||
749  (( c >= 'a') && ( c <= 'z')) ||
750  (( c >= '0') && ( c <= '9')) ||
751  (strchr(safe, c)))
752  {
753  encoded = g_string_append_c (encoded, c);
754  }
755  else if ( c == ' ' )
756  {
757  encoded = g_string_append_c (encoded, '+');
758  }
759  else if ( c == '\n' )
760  {
761  encoded = g_string_append (encoded, "%0D%0A");
762  }
763  else if ( c != '\r' )
764  {
765  snprintf( buffer, buf_size, "%%%02X", (int)c );
766  encoded = g_string_append (encoded, buffer);
767  }
768  pos++;
769  }
770 
771  ptr = g_string_free (encoded, FALSE);
772 
773  return (char *)ptr;
774 }
775 
776 
777 char *
778 gnc_html_decode_string(const char * str)
779 {
780  static gchar * safe = "$-._!*(),"; /* RFC 1738 */
781  GString * decoded = g_string_new ("");
782  const gchar * ptr;
783  guchar c;
784  guint hexval;
785  ptr = str;
786 
787  if (!str) return NULL;
788 
789  while (*ptr)
790  {
791  c = (unsigned char) * ptr;
792  if ((( c >= 'A') && ( c <= 'Z')) ||
793  (( c >= 'a') && ( c <= 'z')) ||
794  (( c >= '0') && ( c <= '9')) ||
795  (strchr(safe, c)))
796  {
797  decoded = g_string_append_c (decoded, c);
798  }
799  else if ( c == '+' )
800  {
801  decoded = g_string_append_c (decoded, ' ');
802  }
803  else if (!strncmp(ptr, "%0D0A", 5))
804  {
805  decoded = g_string_append (decoded, "\n");
806  ptr += 4;
807  }
808  else if (c == '%')
809  {
810  ptr++;
811  if (1 == sscanf(ptr, "%02X", &hexval))
812  decoded = g_string_append_c(decoded, (char)hexval);
813  else
814  decoded = g_string_append_c(decoded, ' ');
815  ptr++;
816  }
817  ptr++;
818  }
819  ptr = g_string_free (decoded, FALSE);
820 
821  return (char *)ptr;
822 }
823 
824 /********************************************************************
825  * escape/unescape_newlines : very simple string encoding for GPG
826  * ASCII-armored text.
827  ********************************************************************/
828 
829 char *
830 gnc_html_unescape_newlines(const gchar * in)
831 {
832  const char * ip = in;
833  char * cstr = NULL;
834  GString * rv = g_string_new("");
835 
836  for (ip = in; *ip; ip++)
837  {
838  if ((*ip == '\\') && (*(ip + 1) == 'n'))
839  {
840  g_string_append(rv, "\n");
841  ip++;
842  }
843  else
844  {
845  g_string_append_c(rv, *ip);
846  }
847  }
848 
849  g_string_append_c(rv, 0);
850  cstr = g_string_free (rv, FALSE);
851  return cstr;
852 }
853 
854 char *
855 gnc_html_escape_newlines(const gchar * in)
856 {
857  char *out;
858  const char * ip = in;
859  GString * escaped = g_string_new("");
860 
861  for (ip = in; *ip; ip++)
862  {
863  if (*ip == '\012')
864  {
865  g_string_append(escaped, "\\n");
866  }
867  else
868  {
869  g_string_append_c(escaped, *ip);
870  }
871  }
872  g_string_append_c(escaped, 0);
873  out = g_string_free (escaped, FALSE);
874  return out;
875 }
876 
877 void
878 gnc_html_register_object_handler( const char * classid,
879  GncHTMLObjectCB hand )
880 {
881  g_return_if_fail( classid != NULL );
882 
883  if ( gnc_html_object_handlers == NULL )
884  {
885  gnc_html_object_handlers = g_hash_table_new( g_str_hash, g_str_equal );
886  }
887 
888  gnc_html_unregister_object_handler( classid );
889  if ( hand != NULL )
890  {
891  gchar *lc_id = g_ascii_strdown (classid, -1);
892  g_hash_table_insert( gnc_html_object_handlers, lc_id, hand );
893  }
894 }
895 
896 void
897 gnc_html_unregister_object_handler( const gchar* classid )
898 {
899  gchar* keyptr = NULL;
900  gchar* valptr = NULL;
901  gchar** p_keyptr = &keyptr;
902  gchar** p_valptr = &valptr;
903  gchar* lc_id = g_ascii_strdown (classid, -1);
904 
905  if ( g_hash_table_lookup_extended( gnc_html_object_handlers,
906  lc_id,
907  (gpointer *)p_keyptr,
908  (gpointer *)p_valptr) )
909  {
910  g_hash_table_remove( gnc_html_object_handlers, lc_id );
911  g_free( keyptr );
912  }
913  g_free( lc_id );
914 }
915 
916 void
917 gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand )
918 {
919  g_return_if_fail( url_type != NULL && *url_type != '\0' );
920 
921  if ( gnc_html_stream_handlers == NULL )
922  {
923  gnc_html_stream_handlers = g_hash_table_new( g_str_hash, g_str_equal );
924  }
925 
926  gnc_html_unregister_stream_handler( url_type );
927  if ( hand != NULL )
928  {
929  char* lc_type = g_ascii_strdown (url_type, -1);
930  g_hash_table_insert( gnc_html_stream_handlers, lc_type, hand );
931  }
932 }
933 
934 void
935 gnc_html_unregister_stream_handler( URLType url_type )
936 {
937  char* lc_type = g_ascii_strdown (url_type, -1);
938  g_hash_table_remove( gnc_html_stream_handlers, lc_type );
939  g_free(lc_type);
940 }
941 
942 void
943 gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand )
944 {
945  g_return_if_fail( url_type != NULL && *url_type != '\0' );
946 
947  if ( gnc_html_url_handlers == NULL )
948  {
949  gnc_html_url_handlers = g_hash_table_new( g_str_hash, g_str_equal );
950  }
951 
952  gnc_html_unregister_url_handler( url_type );
953  if ( hand != NULL )
954  {
955  char* lc_type = g_ascii_strdown (url_type, -1);
956  g_hash_table_insert( gnc_html_url_handlers, lc_type, hand );
957  }
958 }
959 
960 void
961 gnc_html_unregister_url_handler( URLType url_type )
962 {
963  char* lc_type = g_ascii_strdown (url_type, -1);
964  g_hash_table_remove( gnc_html_url_handlers, lc_type );
965  g_free(lc_type);
966 }
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
Account handling public routines.
All type declarations for the whole Gnucash engine.