aboutsummaryrefslogblamecommitdiffstats
path: root/src/bookmarks/ephy-bookmarks-export.c
blob: 1c40dcaf9ed655285ffe85b6f85fa46272178f83 (plain) (tree)
1
2
3
  

                                          












                                                                        
                                                                                  
  

   
                   
 

                                  
                              
                        

                       
                           

                             


                              
 









                                                          
                                                                            















                                                                                 



                                           

                            
                                   
              
                    














                                                                                             
                                                                  

                 
 
                                                  
         
                                         
                                 
                                  


                                                            
                                                                    

                                                 



                                               


                                   
                                   

         


                                 

 

                                    
                       
                                   
 
                                             
                            
                       
                   
                            


                        
 
                                      
 

                                                                     
 
                                         



                                                                            
                              
 



                                                         


                                           




                                                                 


                                           




                                                              

                              
                                                                        

                              
                                       
                                         


                                                                  




                                           

                             
                              
 



                                                   
                              
 
                                       


                                                                        
                              
 
                                                                      
                              
 




                                         
                              


                                                         
                                                                    


                                                     

                                                 





                                         
                                  


                                                      




                                                               


                                                                  
                                     
                 


                                        
                                                          







                                                                    

                 









                                                                                                   
                                                   



                                                

                                   




                                                                    












                                                             


                                        

                                   
                                              


                                                      




                                                               
                                                                  




                                                           
                                     
                 


                                        
                                                          







                                                                    






                                              
                                                                             
                                   
 


                                                                    
                                                   



                                             





                                           
 
                                                                      


                                               

                                    
                                   
 


                                               

                                   
                                   


                              


                                                                                




                                                         

                                                
                                           
                 
 



                                                                  
         







                                                             






                                                    
                                                 

                                

                               

                
                                                  
 

                                           


                                                              

                                                            
                                                             

                           

                                          









                                                                      
                                                  



                                   

                     
                                                                         




                                 


                                  
 
                                          
 
                                                                        
 


                                                        
                                                    



                                     

                                       

                     
                                                     


                                                                      
                                                                 
                          
                                          



                                               
                                       


                       


                                                       
                                                      

                                  


























                                                                                                    
                               


                                              
                                                                            
 
/*
 *  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 <libxml/globals.h>
#include <libxml/tree.h>
#include <libxml/xmlwriter.h>
#include <libxslt/xslt.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>

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;
    GFile *file, *tmp_file;
    char *tmp_file_path;
    int ret;

    LOG ("Exporting as RDF to %s", file_path);

    START_PROFILER ("Exporting as RDF")

    tmp_file_path = g_strconcat (file_path, ".tmp", NULL);
    file = g_file_new_for_path (file_path);
    tmp_file = g_file_new_for_path (tmp_file_path);

    /* FIXME: do we want to turn on compression here? */
    writer = xmlNewTextWriterFilename (tmp_file_path, 0);
    if (writer == NULL)
    {
        g_object_unref (file);
        g_object_unref (tmp_file);
        g_free (tmp_file);
        return;
    }

    ret = xmlTextWriterSetIndent (writer, 1);
    if (ret < 0) goto out;

    ret = xmlTextWriterSetIndentString (writer, (xmlChar *) "  ");
    if (ret < 0) goto out;
    
    ret = write_rdf (bookmarks, file, writer);
    if (ret < 0) goto out;

    xmlFreeTextWriter (writer);
out:
    if (ret >= 0)
    {
        if (ephy_file_switch_temp_file (file, tmp_file) == FALSE)
        {
            ret = -1;
        }
    }

    g_object_unref (file);
    g_object_unref (tmp_file);
    g_free (tmp_file_path);

    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");
}