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