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