/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /*--------------------------------*-C-*---------------------------------* * * Copyright 2000, Matt Loper . * * 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 Street #330, Boston, MA 02111-1307, USA. * *----------------------------------------------------------------------*/ #include #include #include #include #include #include #include "../../mail/html-stream.h" #include /* gtkhtml stuff */ #include /* corba/bonobo stuff */ #include #include #include #include static void print_usage_and_quit() { g_print ("Usage: message-browser [FILENAME]\n"); g_print ("Where FILENAME is the filename of a mime message "); g_print ("in \"message/rfc822\" format.\n"); exit (0); } /*----------------------------------------------------------------------* * Filling out the tree control view of a mime-message *----------------------------------------------------------------------*/ static void handle_tree_item (CamelDataWrapper* object, GtkWidget* tree_ctrl) { GtkWidget* tree_item; gchar* label = gmime_content_field_get_mime_type (object->mime_type); CamelDataWrapper* containee; GtkWidget* containee_tree_item; gchar* containee_label; GtkWidget* subtree = NULL; tree_item = gtk_tree_item_new_with_label (label); gtk_object_set_data (GTK_OBJECT (tree_item), "camel_data_wrapper", object); gtk_tree_append (GTK_TREE (tree_ctrl), tree_item); gtk_widget_show(tree_item); /* If our object is a leaf, we're done */ if (!CAMEL_IS_MEDIUM (object)) return; containee = camel_medium_get_content_object ( CAMEL_MEDIUM (object)); g_assert (containee); /* If it is a container, insert its contents into the tree */ containee_label = camel_data_wrapper_get_mime_type (containee); subtree = gtk_tree_new(); containee_tree_item = gtk_tree_item_new_with_label (containee_label); gtk_object_set_data (GTK_OBJECT (containee_tree_item), "camel_data_wrapper", containee); gtk_tree_append (GTK_TREE (subtree), containee_tree_item); gtk_tree_item_set_subtree (GTK_TREE_ITEM(tree_item), GTK_WIDGET (subtree)); gtk_widget_show(containee_tree_item); if (CAMEL_IS_MULTIPART (containee)) { CamelMultipart* multipart = CAMEL_MULTIPART (containee); int max_multiparts = camel_multipart_get_number (multipart); int i; if (max_multiparts > 0) { subtree = gtk_tree_new(); gtk_tree_item_set_subtree ( GTK_TREE_ITEM(containee_tree_item), GTK_WIDGET (subtree)); } for (i = 0; i < max_multiparts; i++) { CamelMimeBodyPart* body_part = camel_multipart_get_part ( multipart, i); handle_tree_item ( CAMEL_DATA_WRAPPER (body_part), GTK_WIDGET (subtree)); } } gtk_tree_item_expand ( GTK_TREE_ITEM (containee_tree_item)); gtk_tree_item_expand (GTK_TREE_ITEM (tree_item)); } static GtkWidget* get_gtk_html_contents_window (CamelDataWrapper* data); static void tree_selection_changed( GtkWidget *tree ) { GList *i; i = GTK_TREE_SELECTION(tree); while (i){ gchar* name; GtkLabel* label; GtkWidget* item; CamelDataWrapper* camel_data_wrapper; /* Get a GtkWidget pointer from the list node */ item = GTK_WIDGET (i->data); camel_data_wrapper = gtk_object_get_data (GTK_OBJECT (item), "camel_data_wrapper"); g_assert (camel_data_wrapper); get_gtk_html_contents_window (camel_data_wrapper); label = GTK_LABEL (GTK_BIN (item)->child); gtk_label_get (label, &name); g_print ("\t%s on level %d\n", name, GTK_TREE (item->parent)->level); i = i->next; } } static GtkWidget* get_message_tree_ctrl (CamelMimeMessage* message) { static GtkWidget* scroll_wnd = NULL; static GtkWidget* tree_ctrl = NULL; /* create the tree control, if it doesn't exist already */ if (!tree_ctrl) { tree_ctrl = gtk_tree_new (); gtk_signal_connect (GTK_OBJECT(tree_ctrl), "selection_changed", GTK_SIGNAL_FUNC(tree_selection_changed), tree_ctrl); scroll_wnd = gtk_scrolled_window_new (NULL,NULL); gtk_scrolled_window_add_with_viewport ( GTK_SCROLLED_WINDOW(scroll_wnd), tree_ctrl); gtk_widget_set_usize (scroll_wnd, 225, 200); } else gtk_tree_clear_items (GTK_TREE (tree_ctrl), 0, 1); /* Recursively insert tree items in the tree */ if (message) handle_tree_item (CAMEL_DATA_WRAPPER (message), tree_ctrl); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroll_wnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); return scroll_wnd; } static CamelMimeMessage* filename_to_camel_msg (gchar* filename) { CamelMimeMessage* message; CamelStream* input_stream; camel_init(); input_stream = camel_stream_fs_new_with_name ( filename, CAMEL_STREAM_FS_READ); if (!input_stream) return NULL; message = camel_mime_message_new (); camel_data_wrapper_set_input_stream ( CAMEL_DATA_WRAPPER (message), input_stream); return message; } /*----------------------------------------------------------------------* * Filling out the HTML view of a mime message *----------------------------------------------------------------------*/ static void data_wrapper_to_html (CamelDataWrapper *msg, gchar** body_string) { CamelFormatter* cmf = camel_formatter_new(); CamelStream* body_stream = camel_stream_mem_new (CAMEL_STREAM_FS_WRITE); g_assert (body_string); camel_formatter_wrapper_to_html ( cmf, msg, body_stream); *body_string = g_strndup ( CAMEL_STREAM_MEM (body_stream)->buffer->data, CAMEL_STREAM_MEM (body_stream)->buffer->len); } static void mime_message_header_to_html (CamelMimeMessage *msg, gchar** header_string) { CamelFormatter* cmf = camel_formatter_new(); CamelStream* header_stream = camel_stream_mem_new (CAMEL_STREAM_FS_WRITE); g_assert (header_string); camel_formatter_mime_message_to_html ( cmf, CAMEL_MIME_MESSAGE (msg), header_stream, NULL); *header_string = g_strndup ( CAMEL_STREAM_MEM (header_stream)->buffer->data, CAMEL_STREAM_MEM (header_stream)->buffer->len); } static void on_link_clicked (GtkHTML *html, const gchar *url, gpointer data) { GtkWidget* message_box; CamelStream *stream; CamelStream *save_stream; gchar tmp_buffer[4096]; gint nb_bytes_read; gchar* message = g_strdup_printf ("You have clicked on this link:\n%s", url); if (sscanf (url, "camel://%p", &stream)) { save_stream = camel_stream_fs_new_with_name ("saved-file", CAMEL_STREAM_FS_WRITE); do { /* read next chunk of text */ nb_bytes_read = camel_stream_read (stream, tmp_buffer, 4096); /* If there's any text, write it to the stream */ if (nb_bytes_read > 0) { camel_stream_write (save_stream, tmp_buffer, nb_bytes_read); } } while (!camel_stream_eos (stream)); camel_stream_close (save_stream); } message_box = gnome_message_box_new (message, GNOME_MESSAGE_BOX_INFO, "Okay", NULL); gnome_dialog_set_default (GNOME_DIALOG (message_box), 1); gnome_dialog_run (GNOME_DIALOG (message_box)); g_free (message); } /* * As a page is being loaded, gtkhtml will come across a few types of * tags that it understands (like ). In these cases, it * will simply ask us to stream the data to it. */ static void on_url_data_requested (GtkHTML *html, const gchar *url, GtkHTMLStreamHandle handle, gpointer data) { CamelStream *stream; printf ("url _%s_ (%p) requested\n", url, url); if (sscanf (url, "camel://%p", &stream) == 1) { gchar tmp_buffer[4096]; do { gint nb_bytes_read; /* read next chunk of text */ nb_bytes_read = camel_stream_read (stream, tmp_buffer, 4096); /* If there's any text, write it to the stream */ if (nb_bytes_read > 0) { gtk_html_write (html, handle, tmp_buffer, nb_bytes_read); } } while (!camel_stream_eos (stream)); } } static gboolean hydrate_persist_stream_from_gstring (Bonobo_PersistStream persist_stream, GString* gstr) { CORBA_Environment ev; BonoboStream* mem_stream = bonobo_stream_mem_create (gstr->str, gstr->len, TRUE, FALSE); /* * If the component doesn't support * PersistStream, then we destroy the * stream we created and bail. */ if (persist_stream == CORBA_OBJECT_NIL) { gnome_warning_dialog (_("The component now claims that it " "doesn't support PersistStream!")); bonobo_object_unref (BONOBO_OBJECT (mem_stream)); return FALSE; } CORBA_exception_init (&ev); /* * Load the file into the component using PersistStream. */ Bonobo_PersistStream_load (persist_stream, (Bonobo_Stream) bonobo_object_corba_objref (BONOBO_OBJECT (mem_stream)), &ev); bonobo_object_unref (BONOBO_OBJECT (mem_stream)); if (ev._major != CORBA_NO_EXCEPTION) { gnome_warning_dialog (_("An exception occured while trying " "to load data into the component with " "PersistStream")); CORBA_exception_free (&ev); return FALSE; } CORBA_exception_free (&ev); return TRUE; } static GString* camel_stream_to_gstring (CamelStream* stream) { gchar tmp_buffer[4097]; GString *tmp_gstring = g_string_new (""); do { /* read next chunk of text */ gint nb_bytes_read; nb_bytes_read = camel_stream_read (stream, tmp_buffer, 4096); tmp_buffer [nb_bytes_read] = '\0'; /* If there's any text, append it to the gstring */ if (nb_bytes_read > 0) { tmp_gstring = g_string_append (tmp_gstring, tmp_buffer); } } while (!camel_stream_eos (stream)); return tmp_gstring; } /* * As a page is loaded, when gtkhtml comes across tags, this * callback is invoked. The GtkHTMLEmbedded param is a GtkContainer; * our job in this function is to simply add a child widget to it. */ static void on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, void *unused) { CamelStream *stream; GString *camel_stream_gstr; GtkWidget *bonobo_embeddable; BonoboObjectClient* server; Bonobo_PersistStream persist; CORBA_Environment ev; gchar *uid = gtk_html_embedded_get_parameter (eb, "uid"); /* Both the classid (which specifies which bonobo object to * fire up) and the uid (which tells us where to find data to * persist from) must be available; if one of them isn't, * print an error and bail. */ if (!uid || !eb->classid) { printf ("on_object_requested: couldn't find %s%s%s\n", uid?"a uid":"", (!uid && !eb->classid)?" or ":"", eb->classid?"a classid":""); return; } printf ("object requested : %s\n", eb->classid); printf ("UID = %s\n", uid); /* Try to get a server with goadid specified by eb->classid */ bonobo_embeddable = bonobo_widget_new_subdoc (eb->classid, NULL); server = bonobo_widget_get_server (BONOBO_WIDGET (bonobo_embeddable)); if (!server) { printf ("Couldn't get the server for the bonobo embeddable\n"); return; } /* The UID should be a pointer to a CamelStream */ if (sscanf (uid, "camel://%p", &stream) != 1) { printf ("Couldn't get a pointer from url \"%s\"\n", uid); gtk_object_unref (GTK_OBJECT (bonobo_embeddable)); return; } /* Try to get a PersistStream interface from the server; if it doesn't support that interface, bail. */ persist = (Bonobo_PersistStream) bonobo_object_client_query_interface ( server, "IDL:Bonobo/PersistStream:1.0", NULL); if (persist == CORBA_OBJECT_NIL) { gchar* msg = g_strdup_printf ( _("The %s component doesn't support PersistStream!\n"), uid); gnome_warning_dialog (msg); gtk_object_unref (GTK_OBJECT (bonobo_embeddable)); return; } /* Hydrate the PersistStream from the CamelStream */ camel_stream_gstr = camel_stream_to_gstring (stream); printf ("on_object_requested: The CamelStream has %d elements\n", camel_stream_gstr->len); hydrate_persist_stream_from_gstring (persist, camel_stream_gstr); /* Give our new window to the container */ gtk_widget_show (bonobo_embeddable); gtk_container_add (GTK_CONTAINER(eb), bonobo_embeddable); /* Destroy the PersistStream object.*/ CORBA_exception_init (&ev); Bonobo_Unknown_unref (persist, &ev); CORBA_Object_release (persist, &ev); CORBA_exception_free (&ev); g_string_free (camel_stream_gstr, TRUE); } static GtkWidget* get_gtk_html_contents_window (CamelDataWrapper* data) { static GtkWidget* frame_wnd = NULL; static GtkWidget* scroll_wnd = NULL; static GtkWidget* html_widget = NULL; HTMLStream* html_stream; gchar *body_string; /* create the html widget and scroll window, if they haven't already been created */ if (!html_widget) { html_widget = gtk_html_new(); gtk_signal_connect (GTK_OBJECT (html_widget), "link_clicked", GTK_SIGNAL_FUNC (on_link_clicked), NULL); gtk_signal_connect (GTK_OBJECT (html_widget), "url_requested", GTK_SIGNAL_FUNC (on_url_data_requested), NULL); gtk_signal_connect (GTK_OBJECT (html_widget), "object_requested", GTK_SIGNAL_FUNC (on_object_requested), NULL); scroll_wnd = gtk_scrolled_window_new (NULL, NULL); gtk_container_add (GTK_CONTAINER (scroll_wnd), html_widget); } if (data) { html_stream = HTML_STREAM (html_stream_new (GTK_HTML (html_widget))); /* turn the mime message into html, and write it to the html stream */ data_wrapper_to_html (data, &body_string); camel_stream_write (CAMEL_STREAM (html_stream), body_string, strlen (body_string)); camel_stream_close (CAMEL_STREAM (html_stream)); g_free (body_string); } if (!frame_wnd) { frame_wnd = gtk_frame_new (NULL); gtk_frame_set_shadow_type ( GTK_FRAME (frame_wnd), GTK_SHADOW_IN); gtk_widget_set_usize (scroll_wnd, 500, 400); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroll_wnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (frame_wnd), scroll_wnd); } return frame_wnd; } static GtkWidget* get_gtk_html_header_window (CamelMimeMessage* mime_message) { static GtkWidget* frame_wnd = NULL; static GtkWidget* scroll_wnd = NULL; static GtkWidget* html_widget = NULL; HTMLStream* html_stream; gchar* header_string; /* create the html widget and scroll window, if they haven't already been created */ if (!html_widget) { html_widget = gtk_html_new(); scroll_wnd = gtk_scrolled_window_new (NULL, NULL); gtk_container_add (GTK_CONTAINER (scroll_wnd), html_widget); } if (mime_message) { html_stream = HTML_STREAM (html_stream_new (GTK_HTML (html_widget))); /* turn the mime message into html, and write it to the html stream */ mime_message_header_to_html (mime_message, &header_string); camel_stream_write (CAMEL_STREAM (html_stream), header_string, strlen (header_string)); camel_stream_close (CAMEL_STREAM (html_stream)); g_free (header_string); } if (!frame_wnd) { frame_wnd = gtk_frame_new (NULL); gtk_frame_set_shadow_type ( GTK_FRAME (frame_wnd), GTK_SHADOW_OUT); gtk_widget_set_usize (scroll_wnd, 500, 75); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroll_wnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (frame_wnd), scroll_wnd); } return frame_wnd; } static GtkWidget* get_gtk_html_window (CamelMimeMessage* mime_message) { static GtkWidget* vbox = NULL; GtkWidget* html_header_window = NULL; GtkWidget* html_content_window = NULL; html_content_window = get_gtk_html_contents_window ( CAMEL_DATA_WRAPPER (mime_message)); html_header_window = get_gtk_html_header_window (mime_message); if (!vbox) { vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start ( GTK_BOX (vbox), html_header_window, TRUE, TRUE, 5); gtk_box_pack_start ( GTK_BOX (vbox), html_content_window, TRUE, FALSE, 5); } return vbox; } /*----------------------------------------------------------------------* * Menu callbacks and information *----------------------------------------------------------------------*/ static gchar* fileselection_prev_file = NULL; static void open_ok (GtkWidget *widget, GtkFileSelection *fs) { int ret; GtkWidget *error_dialog; if (fileselection_prev_file) g_free (fileselection_prev_file); if(!g_file_exists (gtk_file_selection_get_filename (fs))) { error_dialog = gnome_message_box_new ( _("File not found"), GNOME_MESSAGE_BOX_ERROR, GNOME_STOCK_BUTTON_OK, NULL); gnome_dialog_set_parent (GNOME_DIALOG (error_dialog), GTK_WINDOW (fs)); ret = gnome_dialog_run (GNOME_DIALOG (error_dialog)); } else { gchar *filename = gtk_file_selection_get_filename (fs); CamelMimeMessage* message = filename_to_camel_msg (filename); if (message) { fileselection_prev_file = g_strdup (filename); get_message_tree_ctrl (message); get_gtk_html_window (message); } else gnome_message_box_new ("Couldn't load message.", GNOME_MESSAGE_BOX_WARNING); gtk_widget_destroy (GTK_WIDGET (fs)); } } static void file_menu_open_cb (GtkWidget *widget, void* data) { GtkFileSelection *fs; fs = GTK_FILE_SELECTION ( gtk_file_selection_new (_("Open Mime Message"))); if (fileselection_prev_file) gtk_file_selection_set_filename (fs, fileselection_prev_file); gtk_signal_connect (GTK_OBJECT (fs->ok_button), "clicked", (GtkSignalFunc) open_ok, fs); gtk_signal_connect_object (GTK_OBJECT (fs->cancel_button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (fs)); gtk_widget_show (GTK_WIDGET (fs)); gtk_grab_add (GTK_WIDGET (fs)); /* Make it modal */ } static void file_menu_exit_cb (GtkWidget *widget, void *data) { gtk_main_quit (); } static GnomeUIInfo file_menu [] = { GNOMEUIINFO_MENU_OPEN_ITEM (file_menu_open_cb, NULL), GNOMEUIINFO_SEPARATOR, GNOMEUIINFO_MENU_EXIT_ITEM (file_menu_exit_cb, NULL), GNOMEUIINFO_END }; static GnomeUIInfo main_menu[] = { GNOMEUIINFO_MENU_FILE_TREE (file_menu), GNOMEUIINFO_END }; /*----------------------------------------------------------------------* * Main() *----------------------------------------------------------------------*/ int main (int argc, char *argv[]) { /* app contains hbox, hbox contains other 2 windows */ GtkWidget* app; GtkWidget* hpane; GtkWidget* tree_ctrl_window; GtkWidget* html_window; CORBA_Environment ev; CamelMimeMessage* message = NULL; /* initialization */ /* Corba and Bonobo stuff */ CORBA_exception_init (&ev); gnome_CORBA_init ("Message Browser", "1.0", &argc, argv, 0, &ev); if (bonobo_init (gnome_CORBA_ORB (), NULL, NULL) == FALSE) g_error ("Cannot bonobo_init"); gdk_rgb_init (); gtk_widget_set_default_colormap (gdk_rgb_get_cmap ()); gtk_widget_set_default_visual (gdk_rgb_get_visual ()); app = gnome_app_new ("Message Browser Test", NULL); gnome_app_create_menus (GNOME_APP (app), main_menu); /* parse command line */ if (argc > 2 || (argc==2 && strstr (argv[1], "--help") != 0)) print_usage_and_quit(); if (argc == 2) { if (strstr (argv[1], "--help") != 0) print_usage_and_quit(); message = filename_to_camel_msg (argv[1]); if (!message) { g_print ("Couldn't load message."); } } hpane = e_hpaned_new(); /* add the tree control view of the message*/ tree_ctrl_window = get_message_tree_ctrl (message); e_paned_add1 (E_PANED (hpane), tree_ctrl_window); /* add the HTML view of the message */ html_window = get_gtk_html_window (message); e_paned_add2 (E_PANED (hpane), html_window); /* rock n roll */ gnome_app_set_contents (GNOME_APP (app), hpane); gtk_widget_show_all (app); gtk_signal_connect (GTK_OBJECT (app), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), &app); if (!message) file_menu_open_cb (NULL, NULL); bonobo_main(); return 1; }