/* * Copyright © 2003 Marco Pesenti Gritti * Copyright © 2003 Christian Persch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "config.h" #include "ephy-bookmarks-export.h" #include "ephy-node-common.h" #include "ephy-file-helpers.h" #include "ephy-string.h" #include "ephy-debug.h" #include #include #include #include #include #include static inline xmlChar * sanitise_string (const xmlChar *string) { xmlChar *copy, *p; if (!string) return xmlStrdup ((const xmlChar *) ""); /* http://www.w3.org/TR/REC-xml/#sec-well-formed : Character Range [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */ copy = xmlStrdup (string); for (p = copy; *p; p++) { xmlChar c = *p; if (G_UNLIKELY (c < 0x20 && c != 0xd && c != 0xa && c != 0x9)) { *p = 0x20; } } return copy; } static int write_topics_list (EphyNode *topics, EphyNode *bmk, xmlTextWriterPtr writer) { GPtrArray *children; GList *keywords = NULL, *l; int i; int ret = 0; children = ephy_node_get_children (topics); for (i = 0; i < children->len; i++) { EphyNode *kid; EphyNodePriority priority; kid = g_ptr_array_index (children, i); priority = ephy_node_get_property_int (kid, EPHY_NODE_KEYWORD_PROP_PRIORITY); if (priority == -1) priority = EPHY_NODE_NORMAL_PRIORITY; if (priority == EPHY_NODE_NORMAL_PRIORITY && ephy_node_has_child (kid, bmk)) { keywords = g_list_prepend (keywords, kid); } } for (l = keywords; l != NULL; l = l->next) { EphyNode *node = l->data; const char *name; xmlChar *safeName; name = ephy_node_get_property_string (node, EPHY_NODE_KEYWORD_PROP_NAME); safeName = sanitise_string ((const xmlChar *) name); ret = xmlTextWriterWriteElementNS (writer, (xmlChar *) "dc", (xmlChar *) "subject", NULL, safeName); xmlFree (safeName); if (ret < 0) break; } g_list_free (keywords); return ret >= 0 ? 0 : -1; } static int write_rdf (EphyBookmarks *bookmarks, GFile *file, xmlTextWriterPtr writer) { EphyNode *bmks, *topics, *smart_bmks; GPtrArray *children; char *file_uri; int i, ret; xmlChar *safeString; #ifdef ENABLE_ZEROCONF EphyNode *local; #endif START_PROFILER ("Writing RDF") ret = xmlTextWriterStartDocument (writer, "1.0", NULL, NULL); if (ret < 0) goto out; ret = xmlTextWriterStartElementNS (writer, (xmlChar *) "rdf", (xmlChar *) "RDF", (xmlChar *) "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); if (ret < 0) goto out; ret = xmlTextWriterWriteAttribute (writer, (xmlChar *) "xmlns", (xmlChar *) "http://purl.org/rss/1.0/"); if (ret < 0) goto out; ret = xmlTextWriterWriteAttributeNS (writer, (xmlChar *) "xmlns", (xmlChar *) "dc", NULL, (xmlChar *) "http://purl.org/dc/elements/1.1/"); if (ret < 0) goto out; ret = xmlTextWriterWriteAttributeNS (writer, (xmlChar *) "xmlns", (xmlChar *) "ephy", NULL, (xmlChar *) "http://gnome.org/ns/epiphany#"); if (ret < 0) goto out; ret = xmlTextWriterStartElement (writer, (xmlChar *) "channel"); if (ret < 0) goto out; /* FIXME: sanitise file_uri? */ file_uri = g_file_get_uri (file); safeString = sanitise_string ((const xmlChar *) file_uri); g_free (file_uri); ret = xmlTextWriterWriteAttributeNS (writer, (xmlChar *) "rdf", (xmlChar *) "about", NULL, safeString); xmlFree (safeString); if (ret < 0) goto out; ret = xmlTextWriterWriteElement (writer, (xmlChar *) "title", (xmlChar *) "Epiphany bookmarks"); if (ret < 0) goto out; ret = xmlTextWriterWriteElement (writer, (xmlChar *) "link", (xmlChar *) "http://www.gnome.org/projects/epiphany/"); if (ret < 0) goto out; ret = xmlTextWriterStartElement (writer, (xmlChar *) "items"); if (ret < 0) goto out; ret = xmlTextWriterStartElementNS (writer, (xmlChar *) "rdf", (xmlChar *) "Seq", NULL); if (ret < 0) goto out; bmks = ephy_bookmarks_get_bookmarks (bookmarks); topics = ephy_bookmarks_get_keywords (bookmarks); smart_bmks = ephy_bookmarks_get_smart_bookmarks (bookmarks); #ifdef ENABLE_ZEROCONF local = ephy_bookmarks_get_local (bookmarks); #endif children = ephy_node_get_children (bmks); for (i=0; i < children->len; i++) { EphyNode *kid; const char *url; char *link = NULL; gboolean smart_url; xmlChar *safeLink; kid = g_ptr_array_index (children, i); #ifdef ENABLE_ZEROCONF /* Don't export the local bookmarks */ if (ephy_node_has_child (local, kid)) continue; #endif smart_url = ephy_node_has_child (smart_bmks, kid); url = ephy_node_get_property_string (kid, EPHY_NODE_BMK_PROP_LOCATION); if (smart_url && url) { char *scheme; char *host_name; scheme = g_uri_parse_scheme (url); host_name = ephy_string_get_host_name (url); link = g_strconcat (scheme, "://", host_name, NULL); g_free (scheme); g_free (host_name); } safeLink = sanitise_string (link ? (const xmlChar *) link : (const xmlChar *) url); g_free (link); ret = xmlTextWriterStartElementNS (writer, (xmlChar *) "rdf", (xmlChar *) "li", NULL); if (ret < 0) break; ret = xmlTextWriterWriteAttributeNS (writer, (xmlChar *) "rdf", (xmlChar *) "resource", NULL, safeLink); xmlFree (safeLink); if (ret < 0) break; ret = xmlTextWriterEndElement (writer); /* rdf:li */ if (ret < 0) break; } if (ret < 0) goto out; ret = xmlTextWriterEndElement (writer); /* rdf:Seq */ if (ret < 0) goto out; ret = xmlTextWriterEndElement (writer); /* items */ if (ret < 0) goto out; ret = xmlTextWriterEndElement (writer); /* channel */ if (ret < 0) goto out; children = ephy_node_get_children (bmks); for (i=0; i < children->len; i++) { EphyNode *kid; const char *url, *title; char *link = NULL; gboolean smart_url; xmlChar *safeLink, *safeTitle; kid = g_ptr_array_index (children, i); #ifdef ENABLE_ZEROCONF /* Don't export the local bookmarks */ if (ephy_node_has_child (local, kid)) continue; #endif smart_url = ephy_node_has_child (smart_bmks, kid); url = ephy_node_get_property_string (kid, EPHY_NODE_BMK_PROP_LOCATION); title = ephy_node_get_property_string (kid, EPHY_NODE_BMK_PROP_TITLE); if (smart_url && url) { char *scheme; char *host_name; scheme = g_uri_parse_scheme (url); host_name = ephy_string_get_host_name (url); link = g_strconcat (scheme, "://", host_name, NULL); g_free (scheme); g_free (host_name); } if (link == NULL) { link = g_strdup (url); } ret = xmlTextWriterStartElement (writer, (xmlChar *) "item"); if (ret < 0) break; safeLink = sanitise_string ((const xmlChar *) link); g_free (link); ret = xmlTextWriterWriteAttributeNS (writer, (xmlChar *) "rdf", (xmlChar *) "about", NULL, safeLink); if (ret < 0) { xmlFree (safeLink); break; } safeTitle = sanitise_string ((const xmlChar *) title); ret = xmlTextWriterWriteElement (writer, (xmlChar *) "title", safeTitle); xmlFree (safeTitle); if (ret < 0) break; ret = xmlTextWriterWriteElement (writer, (xmlChar *) "link", safeLink); xmlFree (safeLink); if (ret < 0) break; if (smart_url) { xmlChar *safeSmartLink; safeSmartLink = sanitise_string ((const xmlChar *) url); ret = xmlTextWriterWriteElementNS (writer, (xmlChar *) "ephy", (xmlChar *) "smartlink", NULL, safeSmartLink); xmlFree (safeSmartLink); if (ret < 0) break; } ret = write_topics_list (topics, kid, writer); if (ret < 0) break; ret = xmlTextWriterEndElement (writer); /* item */ } if (ret < 0) goto out; ret = xmlTextWriterEndElement (writer); /* rdf:RDF */ if (ret < 0) goto out; ret = xmlTextWriterEndDocument (writer); out: STOP_PROFILER ("Writing RDF") return ret; } void ephy_bookmarks_export_rdf (EphyBookmarks *bookmarks, const char *file_path) { xmlTextWriterPtr writer; xmlBufferPtr buf; GFile *file; int ret; LOG ("Exporting as RDF to %s", file_path); START_PROFILER ("Exporting as RDF") buf = xmlBufferCreate (); if (buf == NULL) { return; } /* FIXME: do we want to turn on compression here? */ writer = xmlNewTextWriterMemory (buf, 0); if (writer == NULL) { xmlBufferFree (buf); return; } ret = xmlTextWriterSetIndent (writer, 1); if (ret < 0) goto out; ret = xmlTextWriterSetIndentString (writer, (xmlChar *) " "); if (ret < 0) goto out; file = g_file_new_for_path (file_path); ret = write_rdf (bookmarks, file, writer); g_object_unref (file); if (ret < 0) goto out; xmlFreeTextWriter (writer); out: if (ret >= 0) { if (g_file_set_contents (file_path, (const char *)buf->content, buf->use, NULL) == FALSE) { ret = -1; } } xmlBufferFree (buf); STOP_PROFILER ("Exporting as RDF") LOG ("Exporting as RDF %s.", ret >= 0 ? "succeeded" : "FAILED"); } void ephy_bookmarks_export_mozilla (EphyBookmarks *bookmarks, const char *filename) { xsltStylesheetPtr cur = NULL; xmlTextWriterPtr writer; xmlDocPtr doc = NULL, res; char *tmp_file_path, *template; GFile *tmp_file; int ret = -1; LOG ("Exporting as Mozilla to %s", filename); template = g_build_filename (g_get_tmp_dir (), "export-bookmarks-XXXXXX", NULL); tmp_file_path = ephy_file_tmp_filename (template, "rdf"); g_free (template); if (tmp_file_path == NULL) return; writer = xmlNewTextWriterDoc (&doc, 0); if (writer == NULL || doc == NULL) { g_free (tmp_file_path); return; } START_PROFILER ("Exporting as Mozilla"); tmp_file = g_file_new_for_path (tmp_file_path); ret = write_rdf (bookmarks, tmp_file, writer); g_object_unref (tmp_file); if (ret < 0) goto out; /* Set up libxml stuff */ xmlLoadExtDtdDefaultValue = 1; xmlSubstituteEntitiesDefault (1); cur = xsltParseStylesheetFile ((const xmlChar *) ephy_file ("epiphany-bookmarks-html.xsl")); if (cur == NULL) goto out; res = xsltApplyStylesheet (cur, doc, NULL); if (res == NULL) { xsltFreeStylesheet (cur); goto out; } ret = xsltSaveResultToFilename (filename, res, cur, FALSE); xsltFreeStylesheet (cur); xmlFreeDoc (res); /* Clean up libxslt stuff */ xsltCleanupGlobals (); out: xmlFreeTextWriter (writer); xmlFreeDoc (doc); g_free (tmp_file_path); STOP_PROFILER ("Exporting as Mozilla") LOG ("Exporting as Mozilla %s.", ret >= 0 ? "succeeded" : "FAILED"); }