PageRenderTime 50ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/src/html/gnc-html.c

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