aboutsummaryrefslogblamecommitdiffstats
path: root/executive-summary/test-service/rdf-summary.c
blob: 9e4c699c2daf8822a62fb38a1c2aa39b5422b39d (plain) (tree)



















                                                                           
                                

                                                           
                                                           








                                                                             
                                                                                   
 







                                

                                                







                           


                     


                                      













































































                                                                                    
                               

                         
                       





                                  
                                   
















































                                                                                 
 

                                                 
                                                    




                                                                               



                                                                               
      

                                                




                                                                              
                                                            



                                                                              





















































                                                                                                                     





                                                  
                        
                                                       




                                 





















































































































                                                                                      



                                            
 
                      
                      
                  
 

                            
 





                                                 
                           
                               
 







                                                                       
                                                                             
                                                           
                                                                                                         
                              

                             
         


                                 
 



                                                                                                      
 










                                                                      
 













                                                                                                         
 









                                                                          
 






                                           
 
















                                                                                                         

         

                                             
 

                                                                                     
 







                                                                              
 



                     
























































                                                                          
                                                                          






























































                                                                                             
                                                             












                                                        

                           
                            

                                        
                                    

                                        

                                                                   











                                                                              




                                                                              

                               
                                                                   




                                                                                 
                                 

                                                                   
                           
                                     


                                                                            
                                     


                                                                            
                                                                    
                                 
                                                                         
                                                                  
                                                              




                                                                        
                                                                         
 




                                                                             
                        
                                                                

                         





                                           
                                        
 
                                                                                        
                                 































                                                                                
 

                                                              
 
                 

        
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* RDF viewer Evolution Executive Summary Component.
 * Bonoboised by Iain Holmes  <iain@helixcode.com>
 * Copyright (C) 2000 Helix Code, Inc.
 *
 * Based on code from Portaloo
 * Channel retrieval tool
 *
 * (C) 1998 Alan Cox.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
 
#include <glib.h>
#include <gnome.h>
#include <bonobo.h>
#include <gnome-xml/parser.h>
#include <gnome-xml/xmlmemory.h>

#include <evolution-services/executive-summary-component.h>
#include <evolution-services/executive-summary-html-view.h>
#include <liboaf/liboaf.h>
#include <libgnomevfs/gnome-vfs.h>

int xmlSubstituteEntitiesDefaultValue = 1;  /* DV thinks of everything */

static int wipe_trackers = FALSE;
static int running_views = 0;

static BonoboGenericFactory *factory = NULL;
#define RDF_SUMMARY_ID "OAFIID:GNOME_Evolution_Summary_rdf_SummaryComponentFactory"

enum {
    PROPERTY_TITLE,
    PROPERTY_ICON
};

struct _RdfSummary {
    BonoboObject *component;
    BonoboObject *view;
    BonoboPropertyBag *bag;
    BonoboPropertyControl *property_control;

    GtkWidget *rdf;
    GtkWidget *g_limit;

    char *title;
    char *icon;
    char *location;
    int limit;

    GString *str;
    char *buffer;
};
typedef struct _RdfSummary RdfSummary;

/************ 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) {
        free (wb);
    }
    
    wb = w = malloc (3 * strlen (p));
    
    if (w == NULL) {
        fprintf(stderr, "Out of memory\n");
        exit(1);
    }
    
    if (*r == ' ') r++; /* Fix UF bug */

    while (*r) {
        if (memcmp (r, "&amp;", 5) == 0) {
            *w++ = '&';
            r += 5;
            continue;
        }
        if (memcmp (r, "&lt;", 4) == 0) {
            *w++ = '<';
            r += 4;
            continue;
        }
        if (memcmp (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,
       RdfSummary *summary,
       GString *html)
{
    BonoboArg *arg;
    xmlNodePtr walk;
    xmlNodePtr rewalk = root;
    xmlNodePtr channel = NULL;
    xmlNodePtr image = NULL;
    xmlNodePtr item[16];
    int items = 0;
    int limit = summary->limit;
    int i;
    char *t;
    char n[512];
    char *tmp;
    
    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");
        exit(1);
    }

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

    arg = bonobo_arg_new (BONOBO_ARG_STRING);
    BONOBO_ARG_SET_STRING (arg, t);
    bonobo_property_bag_set_value (summary->bag,
                       "window_title", (const BonoboArg *) arg,
                       NULL);
    bonobo_arg_release (arg);

#if 0
    tmp = g_strdup_printf ("%s",
                   layer_find(channel->childs, "description", ""));
    g_string_append (html, tmp);
    g_free (tmp);
#endif

    if (image && !wipe_trackers) {      
        char *icon;

        icon = layer_find_url (image->childs, "url", "apple-red.png");
        arg = bonobo_arg_new (BONOBO_ARG_STRING);
        BONOBO_ARG_SET_STRING (arg, icon);
        bonobo_property_bag_set_value (summary->bag,
                           "window_icon", 
                           (const BonoboArg *) arg, NULL);
        bonobo_arg_release (arg);
            
    }

    g_string_append (html, "<br clear=all><FONT size=\"-1\" face=\"helvetica\"><P><UL>\n");

    for (i = 0; i < items; i++) {
        char *p = layer_find (item[i]->childs, "title", "No information");
        
        if(i == limit)
            g_string_append (html, "--\n");
        if (wipe_trackers) {
            char *p = layer_find_url (item[i]->childs, "link", "");
            char *x = strchr (p, '?');
            unsigned char *r, *w;
            int n;
            if (x == NULL)
                continue;
            x++;
            r = x;
            w = x;
            while (*r) {
                if (*r == '+') {
                    *w++ = ' ';
                } else if (*r == '%') {
                    sscanf (r+1, "%02x", &n);
                    *w++ = n;
                    r += 2;
                } else {
                    *w++ = *r;
                }
                r++;
            }
            *w = 0;
            tmp = g_strdup_printf ("<LI><A href=\"%s\">\n", x+4);
            g_string_append (html, tmp);
            g_free (tmp);
        }
        else {
            tmp = g_strdup_printf ("<LI><A href=\"%s\">\n", layer_find_url(item[i]->childs, "link", ""));
            g_string_append (html, tmp);
            g_free (tmp);
        }

        tmp = g_strdup_printf ("%s\n</A>\n", p);
        g_string_append (html, tmp);
        g_free (tmp);
    }
    g_string_append (html, "</UL></FONT>\n");
}

/********* ExecutiveSummaryComponent section **************/
static void 
view_destroyed (GtkObject *object,
        gpointer data)
{
    RdfSummary *summary = (RdfSummary *) data;

    g_free (summary->title);
    g_free (summary->icon);
    g_free (summary);

    running_views--;
    g_print ("Running_views: %d\n", running_views);
    if (running_views <= 0) {
        gtk_main_quit ();
    }
}

/* PersistStream callbacks */
static void
load_from_stream (BonoboPersistStream *ps,
          Bonobo_Stream stream,
          Bonobo_Persist_ContentType type,
          gpointer data,
          CORBA_Environment *ev)
{
    RdfSummary *summary = (RdfSummary *) data;
    char *str;
    xmlChar *xml_str;
    xmlDocPtr doc;
    xmlNodePtr root, children;

    if (*type && g_strcasecmp (type, "application/x-rdf-summary") != 0) {
        CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
                     ex_Bonobo_Persist_WrongDataType, NULL);
        return;
    }

    bonobo_stream_client_read_string (stream, &str, ev);
    if (ev->_major != CORBA_NO_EXCEPTION || str == NULL) {
        CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
                     ex_Bonobo_Persist_WrongDataType, NULL);
        return;
    }

    g_print ("Hydrating with %s\n", str);
    doc = xmlParseDoc ((xmlChar *) str);
    g_free (str);

    root = doc->root;
    children = root->childs;
    while (children) {
        if (strcasecmp (children->name, "location") == 0) {
            xml_str = xmlNodeListGetString (doc, children->childs, 1);
            summary->location = g_strdup (xml_str);
            g_print ("Location = %s\n", summary->location);
            xmlFree (xml_str);

            children = children->next;
            continue;
        } 

        if (strcasecmp (children->name, "limit") == 0) {
            xml_str = xmlNodeListGetString (doc, children->childs, 1);
            summary->limit = atoi (xml_str);
            g_print ("Limit = %d\n", summary->limit);
            xmlFree (xml_str);

            children = children->next;
            continue;
        }

        g_print ("Unknown name: %s\n", children->name);
        children = children->next;
    }
    xmlFreeDoc (doc);
}

static char *
summary_to_string (RdfSummary *summary)
{
    xmlChar *out_str;
    int out_len = 0;
    xmlDocPtr doc;
    xmlNodePtr root;
    xmlNsPtr ns;
    char *tmp_str;
    
    doc = xmlNewDoc ("1.0");
    ns = xmlNewGlobalNs (doc, "http://www.helixcode.com", "rdf");
    
    doc->root = xmlNewDocNode (doc, ns, "rdf-summary", NULL);

    xmlNewChild (doc->root, ns, "location", summary->location);
    tmp_str = g_strdup_printf ("%d", summary->limit);
    xmlNewChild (doc->root, ns, "limit", tmp_str);
    g_free (tmp_str);

    xmlDocDumpMemory (doc, &out_str, &out_len);
    g_print ("%s\n", out_str);

    return out_str;
}

static void
save_to_stream (BonoboPersistStream *ps,
        const Bonobo_Stream stream,
        Bonobo_Persist_ContentType type,
        gpointer data,
        CORBA_Environment *ev)
{
    RdfSummary *summary = (RdfSummary *) data;
    char *str;

    if (*type && g_strcasecmp (type, "application/x-rdf-summary") != 0) {
        CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
                     ex_Bonobo_Persist_WrongDataType, NULL);
        return;
    }

    str = summary_to_string (summary);
    if (str)
        bonobo_stream_client_printf (stream, TRUE, ev, str);
    xmlFree (str);

    return;
}

static Bonobo_Persist_ContentTypeList *
content_types (BonoboPersistStream *ps,
           void *closure,
           CORBA_Environment *ev)
{
    return bonobo_persist_generate_content_types (1, "application/x-rdf-summary");
}

static void
close_callback (GnomeVFSAsyncHandle *handle,
        GnomeVFSResult result,
        RdfSummary *summary)
{
    GString *html;
    xmlDocPtr doc;
    char *xml;

    if (summary == NULL)
        return;

    g_free (summary->buffer);
    xml = summary->str->str;
    g_string_free (summary->str, FALSE);

    doc = xmlParseMemory (xml, strlen (xml));
    if (doc == NULL) {
        char *emsg;
        BonoboArg *arg;

        arg = bonobo_arg_new (BONOBO_ARG_STRING);
        BONOBO_ARG_SET_STRING (arg, _("Error"));
        bonobo_property_bag_set_value (summary->bag,
                           "window_title", 
                           (const BonoboArg *) arg,
                           NULL);
        bonobo_arg_release (arg);
        
        emsg = g_strdup_printf ("<b>Cannot open location:<br>%s</b>",
                    summary->location);
        executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), emsg);
        g_free (emsg);
        g_free (xml);
        return;
    }
    
    g_free (xml);
    html = g_string_new ("");

    tree_walk (doc->root, summary, html);
    executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), html->str);
    g_string_free (html, TRUE);
}

static void
read_callback (GnomeVFSAsyncHandle *handle,
           GnomeVFSResult result,
           gpointer buffer,
           GnomeVFSFileSize bytes_requested,
           GnomeVFSFileSize bytes_read,
           RdfSummary *summary)
{
    if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
        char *emsg;
        BonoboArg *arg;

        arg = bonobo_arg_new (BONOBO_ARG_STRING);
        BONOBO_ARG_SET_STRING (arg, _("Error"));
        bonobo_property_bag_set_value (summary->bag,
                           "window_title", 
                           (const BonoboArg *) arg,
                           NULL);
        bonobo_arg_release (arg);
        
        emsg = g_strdup_printf ("<b>Cannot open location:<br>%s</b>",
                    summary->location);
        executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), emsg);
        g_free (emsg);
        gnome_vfs_async_close (handle, close_callback, NULL);
    }

    if (bytes_read == 0) {
        /* EOF */
        gnome_vfs_async_close (handle, close_callback, summary);
    } else {
        *((char *) buffer + bytes_read) = 0;
        g_string_append (summary->str, (const char *) buffer);
        gnome_vfs_async_read (handle, buffer, 4095, read_callback,
                      summary);
    }
}

static void
open_callback (GnomeVFSAsyncHandle *handle,
           GnomeVFSResult result,
           RdfSummary *summary)
{
    GList *uri;
    char *buffer;

    if (result != GNOME_VFS_OK) {
        char *emsg;
        BonoboArg *arg;

        arg = bonobo_arg_new (BONOBO_ARG_STRING);
        BONOBO_ARG_SET_STRING (arg, _("Error"));
        bonobo_property_bag_set_value (summary->bag,
                           "window_title", 
                           (const BonoboArg *) arg,
                           NULL);
        bonobo_arg_release (arg);
        
        emsg = g_strdup_printf ("<b>Cannot open location:<br>%s</b>",
                    summary->location);
        executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), emsg);
        g_free (emsg);
        return;
    }

    summary->str = g_string_new ("");
    summary->buffer = g_new (char, 4096);

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

static int
download (RdfSummary *summary)
{
    GnomeVFSAsyncHandle *handle;

    gnome_vfs_async_open (&handle, summary->location, GNOME_VFS_OPEN_READ,
                  (GnomeVFSAsyncOpenCallback) open_callback, 
                  summary);

    return FALSE;
}

static void
get_prop (BonoboPropertyBag *bag,
      BonoboArg *arg,
      guint arg_id,
      gpointer user_data)
{
    RdfSummary *summary = (RdfSummary *) user_data;

    switch (arg_id) {
    case PROPERTY_TITLE:
        BONOBO_ARG_SET_STRING (arg, summary->title);
        break;

    case PROPERTY_ICON:
        BONOBO_ARG_SET_STRING (arg, summary->icon);
        break;
        
    default:
        break;
    }
}

static void
set_prop (BonoboPropertyBag *bag,
      const BonoboArg *arg,
      guint arg_id,
      gpointer user_data)
{
    RdfSummary *summary = (RdfSummary *) user_data;

    switch (arg_id) {
    case PROPERTY_TITLE:
        if (summary->title)
            g_free (summary->title);

        summary->title = g_strdup (BONOBO_ARG_GET_STRING (arg));
        bonobo_property_bag_notify_listeners (bag, "window_title",
                              arg, NULL);
        break;

    case PROPERTY_ICON:
        if (summary->icon)
            g_free (summary->icon);

        summary->icon = g_strdup (BONOBO_ARG_GET_STRING (arg));
        bonobo_property_bag_notify_listeners (bag, "window_icon",
                              arg, NULL);
        break;

    default:
        break;
    }
}

static void
entry_changed (GtkEntry *entry,
          RdfSummary *summary)
{
    bonobo_property_control_changed (summary->property_control, NULL);
}

static BonoboControl *
property_control (BonoboPropertyControl *property_control,
          int page_num,
          gpointer user_data)
{
    BonoboControl *control;
    RdfSummary *summary = (RdfSummary *) user_data;
    GtkWidget *container, *label, *hbox;
    char *climit;

    container = gtk_vbox_new (FALSE, 2);
    gtk_container_set_border_width (GTK_CONTAINER (container), 2);
    hbox = gtk_hbox_new (FALSE, 2);

    label = gtk_label_new ("Location:");
    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

    summary->rdf = gtk_entry_new ();
    if (summary->location)
        gtk_entry_set_text (GTK_ENTRY (summary->rdf), summary->location);

    gtk_signal_connect (GTK_OBJECT (summary->rdf), "changed",
                GTK_SIGNAL_FUNC (entry_changed), summary);

    gtk_box_pack_start (GTK_BOX (hbox), summary->rdf, TRUE, TRUE, 0);

    gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0);

    hbox = gtk_hbox_new (FALSE, 2);

    label = gtk_label_new ("Maximum number of entries:");
    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

    summary->g_limit = gtk_entry_new ();
    climit = g_strdup_printf ("%d", summary->limit);
    gtk_entry_set_text (GTK_ENTRY (summary->g_limit), climit);
    g_free (climit);

    gtk_signal_connect (GTK_OBJECT (summary->g_limit), "changed",
                GTK_SIGNAL_FUNC (entry_changed), summary);

    gtk_box_pack_start (GTK_BOX (hbox), summary->g_limit, TRUE, TRUE, 0);

    gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0);
    gtk_widget_show_all (container);

    control = bonobo_control_new (container);
    return control;
}

static void
property_action (GtkObject *property_control,
         int page_num,
         Bonobo_PropertyControl_Action action,
         RdfSummary *summary)
{
    switch (action) {
    case Bonobo_PropertyControl_APPLY:
        g_free (summary->location);
        summary->location = g_strdup (gtk_entry_get_text (GTK_ENTRY (summary->rdf)));
        summary->limit = atoi (gtk_entry_get_text (GTK_ENTRY (summary->g_limit)));
        g_idle_add ((GSourceFunc) download, summary);
        break;
 
    case Bonobo_PropertyControl_HELP:
        g_print ("HELP: Page %d!\n", page_num);
        break;

    default:
        break;
    }
}

static BonoboObject *
create_view (ExecutiveSummaryComponentFactory *_factory,
         void *closure)
{
    RdfSummary *summary;
    BonoboObject *component, *view;
    BonoboEventSource *event_source;
    BonoboPersistStream *stream;
    BonoboPropertyBag *bag;
    BonoboPropertyControl *property;
    char *html = "<b>Loading RDF file. . .<br>Please wait</b>";
    
    summary = g_new (RdfSummary, 1);
    summary->icon = g_strdup ("apple-green.png");
    summary->title = g_strdup ("Downloading...");
    summary->location = g_strdup ("http://news.gnome.org/gnome-news/rdf");
    summary->limit = 10;

    component = executive_summary_component_new ();
    gtk_signal_connect (GTK_OBJECT (component), "destroy",
                GTK_SIGNAL_FUNC (view_destroyed), summary);

    summary->component = component;

    /* Share the event source between the ExecutiveSummaryHtmlView and the
       BonoboPropertyControl as we can only have one Bonobo::EventSource
       interface aggregated */
    event_source = bonobo_event_source_new ();


    /* Summary::HtmlView */
    view = executive_summary_html_view_new_full (event_source);
    summary->view = view;
    executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (view),
                          html);
    bonobo_object_add_interface (component, view);

    /* Bonobo::PropertyBag */
    bag = bonobo_property_bag_new_full (get_prop, set_prop, 
                        event_source, summary);
    summary->bag = bag;
    bonobo_property_bag_add (bag,
                 "window_title", PROPERTY_TITLE,
                 BONOBO_ARG_STRING, NULL,
                 "The title of this component's window", 0);
    bonobo_property_bag_add (bag,
                 "window_icon", PROPERTY_ICON,
                 BONOBO_ARG_STRING, NULL,
                 "The icon for this component's window", 0);
    bonobo_object_add_interface (component, BONOBO_OBJECT(bag));
                 
    property = bonobo_property_control_new_full (property_control, 1,
                             event_source,
                             summary);
    summary->property_control = property;

    gtk_signal_connect (GTK_OBJECT (property), "action",
                GTK_SIGNAL_FUNC (property_action), summary);

    bonobo_object_add_interface (component, BONOBO_OBJECT(property));

    /* Bonobo::PersistStream */
    stream = bonobo_persist_stream_new (load_from_stream, save_to_stream,
                        NULL, content_types, summary);
    bonobo_object_add_interface (component, BONOBO_OBJECT (stream));

    running_views++;
    gtk_timeout_add (5000, (GSourceFunc) download, summary);

    return component;
}

static BonoboObject *
factory_fn (BonoboGenericFactory *_factory,
        void *closure)
{
    BonoboObject *component_factory;

    component_factory = executive_summary_component_factory_new (create_view, NULL);
    return component_factory;
}

static void
factory_init (void)
{
    if (factory != NULL) {
        return;
    }

    factory = bonobo_generic_factory_new (RDF_SUMMARY_ID, factory_fn, NULL);
    if (factory == NULL) {
        g_error ("Cannot initialize factory");
    }
}

int 
main (int argc, 
      char *argv[])
{
    CORBA_ORB orb;

    gnome_init_with_popt_table ("RDF-Summary", VERSION,
                    argc, argv, oaf_popt_options, 0, NULL);
    orb = oaf_init (argc, argv);
    gnome_vfs_init ();

    if (bonobo_init (orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL) == FALSE) {
        g_error ("Could not initialize Bonobo");
    }

    factory_init ();
    bonobo_main ();

    if (factory != NULL)
        bonobo_object_unref (BONOBO_OBJECT (factory));

    return 0;
}