aboutsummaryrefslogblamecommitdiffstats
path: root/my-evolution/e-summary-rdf.c
blob: 97e95a873a0c55a974f70d7d4a28c934ccea63a6 (plain) (tree)
1
2
3
4
5

                                                                           


                                  















                                                                     
   
 



                    
                   
                 

                        


                                

                                




                                  
                                       


                    
                        
                        















                                          




































































                                                                                    
                                                   



                                   
                                                  



                                   
                                                  



























                                                         
                  



                    

                                              

                                                       

         




                                             
                                                                                                                   





                                             
                                                                                                                   

















































                                                                                 
                       




                                                                       

                                  





                                               
                                                








                                                                                  


                                                                                                                               
                



                                                                               

                                                                  


                                            
                                        







                                                                                 
                                                            
 
                               





                                                                                      
 
                                                
                                                
                                 


                                                    
 
                         
                                        










                                            
                          


                      





                                                                             

                                   
                                 
                                                
                                 




                           
                         

                                         
                         


                                      
                                


                                                 
     
                          
                                 





                                                                
      















                                                                      
                          
 
                                 


                                                                                      
 
                             
























                                                                                       

                          
                                 



                                                                                      
 
                                









                                                                            
        



                                        




                                                                      


                                                      














                                                             







                                                                                      









                                         
                                                                   





















                                          


























                                              
                                            


























                                                                      





                                                   




                           


                                                
 


                                      
 



                   
                                            
                                                                           






















                                                                                              


                                      
                             
                         
                                       
                    



                                                  
                                     


                                      








                                                          
                           

                                                              
                                                                                      
                            
                                                                              













                                                                                     

               














                                                  


                                       

                       
                            
                             
         
                          







                                                                                                                                    




























                                                                      
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* e-summary-rdf.c
 *
 * Copyright (C) 2001 Ximian, Inc.
 *
 * 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 of the
 * License, 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Author: Iain Holmes
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <glib.h>
#include <gtk/gtkmain.h>

#include <gnome-xml/parser.h>
#include <gnome-xml/xmlmemory.h>

#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-i18n.h>
#include <gal/widgets/e-unicode.h>
#include <libgnomevfs/gnome-vfs.h>
#include "e-summary.h"

struct _ESummaryRDF {
    ESummaryConnection *connection;
    GList *rdfs;

    char *html;
    guint32 timeout;
    gboolean online;
};

typedef struct _RDF {
    char *uri;
    char *html;
    GnomeVFSAsyncHandle *handle;
    GString *string;
    char *buffer;

    xmlDocPtr cache;
    ESummary *summary;

    gboolean shown;
} RDF;

int xmlSubstituteEntitiesDefaultValue = 1;

char *
e_summary_rdf_get_html (ESummary *summary)
{
    GList *rdfs;
    char *html;
    GString *string;

    if (summary->rdf == NULL) {
        return NULL;
    }

    string = g_string_new ("");
    for (rdfs = summary->rdf->rdfs; rdfs; rdfs = rdfs->next) {
        if (((RDF *)rdfs->data)->html == NULL) {
            continue;
        }

        g_string_append (string, ((RDF *)rdfs->data)->html);
    }

    html = string->str;
    g_string_free (string, FALSE);
    return html;
}

/************ RDF Parser *******************/

static char *
layer_find (xmlNodePtr node, 
        char *match, 
        char *fail)
{
    while (node!=NULL) {
#ifdef RDF_DEBUG
        xmlDebugDumpNode (stdout, node, 32);
        printf("%s.\n", node->name);
#endif
        if (strcasecmp (node->name, match)==0) {
            if (node->childs != NULL && node->childs->content != NULL) {
                return node->childs->content;
            } else {
                return fail;
            }
        }
        node = node->next;
    }
    return fail;
}

static char *
layer_find_url (xmlNodePtr node, 
        char *match, 
        char *fail)
{
    char *p = layer_find (node, match, fail);
    char *r = p;
    static char *wb = NULL;
    char *w;
    
    if (wb) {
        g_free (wb);
    }
    
    wb = w = g_malloc (3 * strlen (p));
    
    if (*r == ' ') r++; /* Fix UF bug */

    while (*r) {
        if (strncmp (r, "&amp;", 5) == 0) {
            *w++ = '&';
            r += 5;
            continue;
        }
        if (strncmp (r, "&lt;", 4) == 0) {
            *w++ = '<';
            r += 4;
            continue;
        }
        if (strncmp (r, "&gt;", 4) == 0) {
            *w++ = '>';
            r += 4;
            continue;
        }
        if (*r == '"' || *r == ' '){
            *w++ = '%';
            *w++ = "0123456789ABCDEF"[*r/16];
            *w++ = "0123456789ABCDEF"[*r&15];
            r++;
            continue;
        }
        *w++ = *r++;
    }
    *w = 0;
    return wb;
}

static void 
tree_walk (xmlNodePtr root,
       RDF *r,
       GString *html)
{
    xmlNodePtr walk;
    xmlNodePtr rewalk = root;
    xmlNodePtr channel = NULL;
    xmlNodePtr image = NULL;
    xmlNodePtr item[16];
    int items = 0;
    int limit;
    int i;
    char *t, *u;
    char *tmp;

    if (r->summary->preferences == NULL) {
        limit = 10;
    } else {
        limit = r->summary->preferences->limit;
    }

    /* FIXME: Need arrows */
    if (r->shown == FALSE) {
        char *p;

        /* FIXME: Hash table & UID */
        p = g_strdup_printf ("<font size=\"-2\"><a href=\"rdf://%d\">(+)</a></font>", GPOINTER_TO_INT (r));
        g_string_append (html, p);
        g_free (p);
    } else {
        char *p;

        /* FIXME: Hash table & UID */
        p = g_strdup_printf ("<font size=\"-2\"><a href=\"rdf://%d\">(-)</a></font>", GPOINTER_TO_INT (r));
        g_string_append (html, p);
        g_free (p);
    }
    
    do {
        walk = rewalk;
        rewalk = NULL;
        
        while (walk!=NULL){
#ifdef RDF_DEBUG
            printf ("%p, %s\n", walk, walk->name);
#endif
            if (strcasecmp (walk->name, "rdf") == 0) {
                rewalk = walk->childs;
                walk = walk->next;
                continue;
            }
            if (strcasecmp (walk->name, "rss") == 0){
                rewalk = walk->childs;
                walk = walk->next;
                continue;
            }
            /* This is the channel top level */
#ifdef RDF_DEBUG
            printf ("Top level '%s'.\n", walk->name);
#endif
            if (strcasecmp (walk->name, "channel") == 0) {
                channel = walk;
                rewalk = channel->childs;
            }
            if (strcasecmp (walk->name, "image") == 0) {
                image = walk;
                g_print ("Image\n");
            }
            if (strcasecmp (walk->name, "item") == 0 && items < 16) {
                item[items++] = walk;
            }
            walk = walk->next;
        }
    }
    while (rewalk);
    
    if (channel == NULL) {
        fprintf(stderr, "No channel definition.\n");
        return;
    }

    t = layer_find(channel->childs, "title", "");
    u = layer_find(channel->childs, "link", "");

    if (*u != '\0')
        g_string_sprintfa (html, "<a href=\"%s\">", u);
    if (r->cache->encoding)
        t = e_utf8_from_charset_string (r->cache->encoding, t);
    else
        t = e_utf8_from_locale_string (t);
    g_string_append (html, t);
    g_free (t);
    if (*u != '\0') {
        g_string_append (html, "</a>");
    }
    g_string_append (html, "</b></dt>");

    if (r->shown == FALSE) {
        g_string_append (html, "</dl>");
        return;
    }

    g_string_append (html, "<ul>");

    items = MIN (limit, items);
    for (i = 0; i < items; i++) {
        char *p = layer_find (item[i]->childs, "title", "No information");
        
        tmp = g_strdup_printf ("<LI><font size=\"-1\"><A href=\"%s\">\n", layer_find_url(item[i]->childs, "link", ""));
        g_string_append (html, tmp);
        g_free (tmp);
        
        if (r->cache->encoding)
            p = e_utf8_from_charset_string (r->cache->encoding, p);
        else
            p = e_utf8_from_locale_string (p);
        tmp = g_strdup_printf ("%s\n</A></font></li>", p);
        g_free (p);
        g_string_append (html, tmp);
        g_free (tmp);
    }
    g_string_append (html, "</UL>");
}

static void
display_doc (RDF *r)
{
    GString *html;

    html = g_string_new ("<dl><dt><img src=\"ico-rdf.png\" align=\"middle\" "
                 "width=\"48\" height=\"48\">");

    if (r->cache == NULL) {
        char *tmp_utf, *str;

        str = g_strdup_printf ("<b>%s:</b><br>%s", _("Error downloading RDF"),
                       r->uri);
        tmp_utf = e_utf8_from_locale_string (str);
        g_free (str);

        g_string_append (html, tmp_utf);
        g_string_append (html, "</dt>");
        g_free (tmp_utf);
    } else {
        tree_walk (r->cache->root, r, html);
    }

    g_free (r->html);
    g_string_append (html, "</dl>");
    r->html = html->str;
    g_string_free (html, FALSE);

    e_summary_draw (r->summary);
}

static void
close_callback (GnomeVFSAsyncHandle *handle,
        GnomeVFSResult result,
        RDF *r)
{
    ESummary *summary;
    char *xml;
    xmlDocPtr doc;

    summary = r->summary;
    if (summary->rdf->connection->callback) {
        ESummaryConnection *connection = summary->rdf->connection;
        connection->callback (summary, connection->callback_closure);
    }

    if (r->handle == NULL) {
        g_free (r->buffer);
        r->buffer = NULL;
        g_string_free (r->string, TRUE);
        r->string = NULL;
        return;
    }

    r->handle = NULL;
    g_free (r->buffer);
    r->buffer = NULL;
    xml = r->string->str;
    g_string_free (r->string, FALSE);
    r->string = NULL;

    if (r->cache != NULL) {
        xmlFreeDoc (r->cache);
        r->cache = NULL;
    }

    doc = xmlParseMemory (xml, strlen (xml));
#if 0
    if (doc == NULL) {
        g_free (r->html);
        r->html = g_strdup ("<b>Error parsing XML</b>");

        e_summary_draw (r->summary);
        g_free (xml);
        return;
    }
#endif
    g_free (xml);
    r->cache = doc;

    /* Draw it */
    display_doc (r);
}

static void
read_callback (GnomeVFSAsyncHandle *handle,
           GnomeVFSResult result,
           gpointer buffer,
           GnomeVFSFileSize bytes_requested,
           GnomeVFSFileSize bytes_read,
           RDF *r)
{
    if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
        char *str;

        g_free (r->html);
        str = g_strdup_printf ("<b>%s:</b><br>%s", _("Error downloading RDF"),
                       r->uri);
        r->html = e_utf8_from_locale_string (str);

        g_free (str);

        e_summary_draw (r->summary);
        r->handle = NULL;
        gnome_vfs_async_close (handle, 
                       (GnomeVFSAsyncCloseCallback) close_callback, r);
        return;
    }

    if (bytes_read == 0) {
        gnome_vfs_async_close (handle,
                       (GnomeVFSAsyncCloseCallback) close_callback, r);
    } else {
        *((char *) buffer + bytes_read) = 0;
        g_string_append (r->string, (const char *) buffer);
        gnome_vfs_async_read (handle, buffer, 4095,
                      (GnomeVFSAsyncReadCallback) read_callback, r);
    }
}

static void
open_callback (GnomeVFSAsyncHandle *handle,
           GnomeVFSResult result,
           RDF *r)
{
    if (result != GNOME_VFS_OK) {
        char *str;

        g_free (r->html);
        str = g_strdup_printf ("<b>%s:</b><br>%s", _("Error downloading RDF"),
                       r->uri);
        r->html = e_utf8_from_locale_string (str);
        g_free (str);

        display_doc (r);
        return;
    }

    r->string = g_string_new ("");
    r->buffer = g_new (char, 4096);

    gnome_vfs_async_read (handle, r->buffer, 4095,
                  (GnomeVFSAsyncReadCallback) read_callback, r);
}

gboolean
e_summary_rdf_update (ESummary *summary)
{
    GList *r;

    if (summary->rdf->online == FALSE) {
        g_warning ("%s: Repolling but offline", __FUNCTION__);
        return TRUE;
    }

    for (r = summary->rdf->rdfs; r; r = r->next) {
        RDF *rdf = r->data;

        if (rdf->handle) {
            gnome_vfs_async_cancel (rdf->handle);
            rdf->handle = NULL;
        }

        if (rdf->buffer) {
            g_free (rdf->buffer);
            rdf->buffer = NULL;
        }
        
        if (rdf->string) {
            g_string_free (rdf->string, TRUE);
            rdf->string = NULL;
        }

        gnome_vfs_async_open (&rdf->handle, rdf->uri, 
                      GNOME_VFS_OPEN_READ,
                      (GnomeVFSAsyncOpenCallback) open_callback, rdf);
    }

    return TRUE;
}
        
static void
e_summary_rdf_add_uri (ESummary *summary,
               const char *uri)
{
    RDF *r;

    r = g_new0 (RDF, 1);
    r->summary = summary;
    r->uri = g_strdup (uri);
    r->shown = TRUE;
    summary->rdf->rdfs = g_list_append (summary->rdf->rdfs, r);
}

static void
e_summary_rdf_protocol (ESummary *summary,
            const char *uri,
            void *closure)
{
    RDF *r;
    int a;

    a = atoi (uri + 6);
    if (a == 0) {
        g_warning ("A == 0");
        return;
    }

    r = (RDF *) GINT_TO_POINTER (a);
    r->shown = !r->shown;

    display_doc (r);
}

static int
e_summary_rdf_count (ESummary *summary,
             void *data)
{
    ESummaryRDF *rdf;
    GList *p;
    int count = 0;

    rdf = summary->rdf;
    for (p = rdf->rdfs; p; p = p->next) {
        RDF *r = p->data;

        if (r->handle != NULL) {
            count++;
        }
    }

    return count;
}

static ESummaryConnectionData *
make_connection (RDF *r)
{
    ESummaryConnectionData *d;

    d = g_new (ESummaryConnectionData, 1);
    d->hostname = g_strdup (r->uri);
    d->type = g_strdup (_("News Feed"));

    return d;
}

static GList *
e_summary_rdf_add (ESummary *summary,
           void *data)
{
    ESummaryRDF *rdf;
    GList *p, *connections = NULL;

    rdf = summary->rdf;
    for (p = rdf->rdfs; p; p = p->next) {
        RDF *r = p->data;

        if (r->handle != NULL) {
            ESummaryConnectionData *d;

            d = make_connection (r);
            connections = g_list_prepend (connections, d);
        }
    }

    return connections;
}

static void
rdf_free (RDF *r)
{
    /* Stop the download */
    if (r->handle) {
        gnome_vfs_async_cancel (r->handle);
    }

    g_free (r->uri);
    g_free (r->html);
    g_free (r->buffer);

    if (r->string) {
        g_string_free (r->string, TRUE);
    }

    if (r->cache) {
        xmlFreeDoc (r->cache);
    }

    g_free (r);
}

static void
e_summary_rdf_set_online (ESummary *summary,
              GNOME_Evolution_OfflineProgressListener progress,
              gboolean online,
              void *data)
{
    ESummaryRDF *rdf;

    rdf = summary->rdf;
    if (rdf->online == online) {
        return;
    }

    if (online == TRUE) {
        e_summary_rdf_update (summary);
        rdf->timeout = gtk_timeout_add (summary->preferences->rdf_refresh_time * 1000,
                        (GtkFunction) e_summary_rdf_update,
                        summary);
    } else {
        gtk_timeout_remove (rdf->timeout);
        rdf->timeout = 0;
    }

    rdf->online = online;
}

void
e_summary_rdf_init (ESummary *summary)
{
    ESummaryPrefs *prefs;
    ESummaryRDF *rdf;
    ESummaryConnection *connection;
    int timeout;

    g_return_if_fail (summary != NULL);
    g_return_if_fail (IS_E_SUMMARY (summary));

    prefs = summary->preferences;
    rdf = g_new0 (ESummaryRDF, 1);
    summary->rdf = rdf;

    connection = g_new (ESummaryConnection, 1);
    connection->count = e_summary_rdf_count;
    connection->add = e_summary_rdf_add;
    connection->set_online = e_summary_rdf_set_online;
    connection->closure = NULL;
    connection->callback = NULL;
    connection->callback_closure = NULL;

    rdf->connection = connection;
    rdf->online = TRUE;
    e_summary_add_online_connection (summary, connection);

    e_summary_add_protocol_listener (summary, "rdf", e_summary_rdf_protocol, rdf);
    if (prefs == NULL) {
        e_summary_rdf_add_uri (summary, "http://www.cnn.com/cnn.rss");
        timeout = 600;
    } else {
        GList *p;

        for (p = prefs->rdf_urls; p; p = p->next) {
            e_summary_rdf_add_uri (summary, p->data);
        }
        timeout = prefs->rdf_refresh_time;
    }

    e_summary_rdf_update (summary);
    rdf->timeout = gtk_timeout_add (timeout * 1000,
                    (GtkFunction) e_summary_rdf_update, summary);

    return;
}

void
e_summary_rdf_reconfigure (ESummary *summary)
{
    ESummaryRDF *rdf;
    GList *old, *p;

    g_return_if_fail (summary != NULL);
    g_return_if_fail (IS_E_SUMMARY (summary));

    rdf = summary->rdf;

    /* Stop timeout */
    gtk_timeout_remove (rdf->timeout);

    old = rdf->rdfs;
    rdf->rdfs = NULL;
    for (p = old; p; p = p->next) {
        RDF *r;

        r = p->data;
        rdf_free (r);
    }
    g_list_free (old);

    for (p = summary->preferences->rdf_urls; p; p = p->next) {
        e_summary_rdf_add_uri (summary, p->data);
    }

    rdf->timeout = gtk_timeout_add (summary->preferences->rdf_refresh_time * 1000, (GtkFunction) e_summary_rdf_update, summary);
    e_summary_rdf_update (summary);
}

void
e_summary_rdf_free (ESummary *summary)
{
    ESummaryRDF *rdf;
    GList *p;

    g_return_if_fail (summary != NULL);
    g_return_if_fail (IS_E_SUMMARY (summary));

    rdf = summary->rdf;

    if (rdf->timeout != 0) {
        gtk_timeout_remove (rdf->timeout);
    }
    for (p = rdf->rdfs; p; p = p->next) {
        RDF *r = p->data;

        rdf_free (r);
    }
    g_list_free (rdf->rdfs);
    g_free (rdf->html);

    e_summary_remove_online_connection (summary, rdf->connection);
    g_free (rdf->connection);

    g_free (rdf);
    summary->rdf = NULL;
}