Attachment support for Groupwise calendar and task items.
authorHarish Krishnaswamy <kharish@src.gnome.org>
Mon, 10 Jan 2005 10:43:42 +0000 (10:43 +0000)
committerHarish Krishnaswamy <kharish@src.gnome.org>
Mon, 10 Jan 2005 10:43:42 +0000 (10:43 +0000)
* backends/groupwise/e-cal-backend-groupwise-utils.c:
(e_cal_backend_groupwise_set_attachments_from_comp),
(set_properties_from_cal_component), (get_attach_data_from_server),
(set_attachments_to_cal_component), (e_gw_item_to_cal_component):
Attachment support for Groupwise calendar and task items.
(e_cal_backend_groupwise_store_settings): Remove unused variable.
* backends/groupwise/e-cal-backend-groupwise.[ch]:
* (populate_cache), (e_cal_backend_groupwise_finalize), (e_cal_backend_groupwise_open),
(e_cal_backend_groupwise_notify_error_code),
(e_cal_backend_groupwise_get_local_attachments_store):
set the path to the attachment store in the filesystem.
* libecal/e-cal.[ch]
* (e_cal_init), * (e_cal_finalize), (set_local_attachment_store), (e_cal_new),
(e_cal_get_local_attachment_store): Add a new member to ECal that
stores the local attachment store for various backends.

calendar/ChangeLog
calendar/backends/groupwise/e-cal-backend-groupwise-utils.c
calendar/backends/groupwise/e-cal-backend-groupwise.c
calendar/backends/groupwise/e-cal-backend-groupwise.h
calendar/libecal/e-cal.c
calendar/libecal/e-cal.h

index a4e1f06..32f88fc 100644 (file)
@@ -1,3 +1,22 @@
+2005-01-10  Harish Krishnaswamy  <kharish@novell.com>
+
+       * backends/groupwise/e-cal-backend-groupwise-utils.c:
+       (e_cal_backend_groupwise_set_attachments_from_comp),
+       (set_properties_from_cal_component), (get_attach_data_from_server),
+       (set_attachments_to_cal_component), (e_gw_item_to_cal_component):
+       Attachment support for Groupwise calendar and task items.
+       (e_cal_backend_groupwise_store_settings): Remove unused variable.
+       * backends/groupwise/e-cal-backend-groupwise.[ch]: (populate_cache),
+       (e_cal_backend_groupwise_finalize), (e_cal_backend_groupwise_open),
+       (e_cal_backend_groupwise_notify_error_code),
+       (e_cal_backend_groupwise_get_local_attachments_store): 
+       set the path to the attachment store in the filesystem.
+       * libecal/e-cal.[ch] (e_cal_init), (e_cal_finalize),
+       (set_local_attachment_store), (e_cal_new), 
+       (e_cal_get_local_attachment_store): Add a new member to ECal that
+       stores the local attachment store for various backends.
+       
+
 2005-01-08  JP Rosevear  <jpr@novell.com>
 
        * backends/weather/e-cal-backend-weather.c (create_weather): make
index c839a12..0d0d400 100644 (file)
@@ -3,8 +3,8 @@
  * Authors : 
  *  JP Rosevear <jpr@ximian.com>
  *  Rodrigo Moya <rodrigo@ximian.com>
- *
- * Copyright 2003, Novell, Inc.
+ *  Harish Krishnaswamy <kharish@novell.com>
+ *  Copyright 2003, Novell, Inc.
  *
  * This program is free software; you can redistribute it and/or 
  * modify it under the terms of version 2 of the GNU General Public 
  * USA
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
 #include <e-gw-connection.h>
 #include <e-gw-message.h>
 #include <libecal/e-cal-recur.h>
 #include <libecal/e-cal-time-util.h>
+#include <libsoup/soup-misc.h>
 #include "e-cal-backend-groupwise-utils.h"
 #include <libedataserver/e-source-list.h>
 
@@ -234,6 +244,94 @@ add_send_options_data_to_item (EGwItem *item, ECalComponent *comp, icaltimezone
 
 }
 
+static void
+e_cal_backend_groupwise_set_attachments_from_comp (ECalComponent *comp,
+               EGwItem *item)
+{
+       GSList *attach_list = NULL, *attach_file_list = NULL;
+       GSList *l;
+
+       e_cal_component_get_attachment_list (comp, &attach_file_list);
+       
+       for (l = attach_file_list; l ; l = l->next) {
+               
+               EGwItemAttachment *attach_item;
+               char *file_contents, *encoded_data;
+               int fd, len;
+               int len_read = 0;
+               char buf[1024];
+               struct stat sb;
+               char *attach_filename_full, *filename;
+               const char *uid;
+               
+
+               attach_filename_full = (char *)l->data + 7;
+               attach_item = g_new0 (EGwItemAttachment, 1);
+               /* FIXME the member does not follow the naming convention.
+                * Should be fixed in e-gw-item*/
+               attach_item->contentType = g_strdup (gnome_vfs_get_mime_type (attach_filename_full));
+
+               /*
+                * Would gnome_vfs_async be better suited for this ?
+                */
+               fd = open (attach_filename_full, O_RDONLY); 
+               if (fd == -1) {
+                       /* TODO handle error conditions */
+                       g_free (attach_item);
+                       g_message ("DEBUG: could not open the file descriptor\n");
+               }
+               if (fstat (fd, &sb) == -1) {
+                       /* TODO handle error conditions */
+                       g_free (attach_item);
+                       g_message ("DEBUG: could not fstat the attachment file\n");
+                       continue;
+               }
+               len = sb.st_size;
+
+               file_contents = g_malloc (len + 1);
+       
+               while (len_read < len) {
+                       int c = read (fd, buf, sizeof (buf));
+
+                       if (c == -1)
+                               break;
+
+                       memcpy (&file_contents[len_read], buf, c);
+                       len_read += c;
+               }
+               file_contents [len_read] = 0;
+
+               /* Extract the simple file name from the
+                * attach_filename_full which is of the form
+                * file://<path>/compuid-<simple filename> 
+                */
+               e_cal_component_get_uid (comp, &uid);
+               filename = g_strrstr (attach_filename_full, uid);               
+
+               if (filename == NULL) {
+                       /* TODO handle error conditions */
+                       g_free (attach_item);
+                       g_message ("DEBUG:\n This is an invalid attachment file\n");
+                       continue;
+               }       
+
+
+               attach_item->name = g_strdup (filename + strlen(uid) + 1);
+               /* do a base64 encoding so it can be embedded in a soap
+                * message */
+               /* TODO handle error conditions */
+               encoded_data = soup_base64_encode (file_contents, len_read);
+               attach_item->data = encoded_data;
+               attach_item->size = strlen (encoded_data); 
+
+               g_free (file_contents);
+               close (fd);
+               attach_list = g_slist_append (attach_list, attach_item);
+       }
+
+       e_gw_item_set_attach_id_list (item, attach_list);
+}
+
 static EGwItem *
 set_properties_from_cal_component (EGwItem *item, ECalComponent *comp, ECalBackendGroupwise *cbgw)
 {
@@ -482,6 +580,11 @@ set_properties_from_cal_component (EGwItem *item, ECalComponent *comp, ECalBacke
                
                e_gw_item_set_recurrence_dates (item, recur_dates);
            }
+       
+       /* attachments */
+       if (e_cal_component_has_attachments (comp)) {
+               e_cal_backend_groupwise_set_attachments_from_comp (comp, item); 
+       }
 
        return item;
 }
@@ -499,6 +602,90 @@ e_gw_item_new_from_cal_component (const char *container, ECalBackendGroupwise *c
        return set_properties_from_cal_component (item, comp, cbgw);
 }
 
+/* Fetch data from the server and unencode it to the actual data 
+ * and populate the attach_data
+ */
+static gboolean
+get_attach_data_from_server (GSList *attach_item_list, ECalBackendGroupwise *cbgw)
+{
+       GSList *l;
+       EGwConnection *cnc;
+       EGwConnectionStatus status;
+
+       cnc = e_cal_backend_groupwise_get_connection (cbgw);
+       g_return_val_if_fail (E_IS_GW_CONNECTION (cnc), E_GW_CONNECTION_STATUS_INVALID_CONNECTION);
+
+       for (l = attach_item_list; l; l = l->next) {
+               EGwItemAttachment *attach_item = (EGwItemAttachment *) l->data;
+       
+               status = e_gw_connection_get_attachment (cnc, attach_item->id, 0, -1, 
+                       (const char *)&(attach_item->data), &(attach_item->size)); 
+
+               if (status != E_GW_CONNECTION_STATUS_OK ) {
+                       g_warning ("Failed to read the attachment from the server\n");
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
+static void
+set_attachments_to_cal_component (EGwItem *item, ECalComponent *comp, ECalBackendGroupwise *cbgw)
+{
+       /*TODO if attach_list just has attach_ids - fetch the
+        * attachments. For each, serialize them in the attach
+        * store location and set the url in the cal component*/
+       GSList *attach_data = NULL, *attach_id_list, *l;
+       GSList *comp_attachment_list = NULL;
+
+       
+       attach_id_list = e_gw_item_get_attach_id_list (item);
+       if (attach_id_list == NULL)
+               return;
+       
+       /* Download the data and populate the structure */
+       if (!get_attach_data_from_server (attach_id_list, cbgw))
+               return;
+
+       
+       /* FIXME the code below assumes the attach_data now contains
+        * EGwItemAttachment structures and proceeds to serialize 
+        * them. Check base64 encoding assumptions by mailer code*/
+
+       for (l = attach_data; l ; l = l->next) {
+               int fd, length;
+               char *attach_file_url, *attach_data;
+               const char *uid;
+               EGwItemAttachment *attach_item;
+
+               attach_item = (EGwItemAttachment *) l->data;
+               /*TODO decode the encoded data on attachments */
+               if (attach_item->size > 0) {
+                       attach_data = soup_base64_decode (attach_item->data, &length);
+               }
+               e_cal_component_get_uid (comp, &uid);
+               attach_file_url = g_strconcat
+                       (e_cal_backend_groupwise_get_local_attachments_store (cbgw), 
+                        "/", uid, "-", attach_item->name, NULL);
+               fd = open (attach_file_url+7, O_RDWR|O_CREAT|O_TRUNC, 0600);
+               if (fd == -1) {
+                       /* TODO handle error conditions */
+                       g_message ("DEBUG: could not serialize attachments\n");
+               }
+
+               if (write (fd, attach_data, length) == -1) {
+                       /* TODO handle error condition */
+                       g_message ("DEBUG: attachment write failed.\n");
+               }
+               
+               g_free (attach_data);
+               close (fd);
+
+               comp_attachment_list = g_slist_append
+                       (comp_attachment_list, attach_file_url);
+       }
+       
+}
 ECalComponent *
 e_gw_item_to_cal_component (EGwItem *item, ECalBackendGroupwise *cbgw)
 {
@@ -519,6 +706,7 @@ e_gw_item_to_cal_component (EGwItem *item, ECalBackendGroupwise *cbgw)
        GSList *recipient_list, *rl, *attendee_list = NULL;
        EGwItemOrganizer *organizer;
        EGwItemType item_type;
+       GSList *attach_list;
 
        default_zone = e_cal_backend_groupwise_get_default_zone (cbgw);
        categories_by_id = e_cal_backend_groupwise_get_categories_by_id (cbgw);
@@ -694,6 +882,33 @@ e_gw_item_to_cal_component (EGwItem *item, ECalBackendGroupwise *cbgw)
                e_cal_component_set_organizer (comp, cal_organizer);
        }
 
+       /* set attachments */
+       attach_list = e_gw_item_get_attach_id_list (item);      
+       if (attach_list != NULL) {
+               /* Iterate thro the list and stuff it into ical*/
+               EGwItemAttachment *attach_data;
+               int attach_length;
+               unsigned char *attachment;
+               EGwConnectionStatus status;
+
+               /* First check if the item has attachments . if yes check if it 
+                * exists on the filesystem or pull it from the server,
+                * serialize it.
+                */
+               /*  nice if we could just get it from the cache/item not
+                * from a n/w call here */
+               attach_data = (EGwItemAttachment *) attach_list->data;
+               status  = e_gw_connection_get_attachment (e_cal_backend_groupwise_get_connection (cbgw), attach_data->id, 0, 0, 
+                                       (const char *)&attachment, &attach_length); 
+               if (status == E_GW_CONNECTION_STATUS_OK) 
+                       g_message ("DEBUG : Spewing out the attachment\n%s\n", attachment);             
+               else 
+                       g_message ("DEBUG:attachment call was a disaster\n");
+
+               set_attachments_to_cal_component (item, comp, cbgw);
+       
+       }
+
        /* set specific properties */
        switch (item_type) {
        case E_GW_ITEM_TYPE_APPOINTMENT :
@@ -813,6 +1028,9 @@ e_gw_item_to_cal_component (EGwItem *item, ECalBackendGroupwise *cbgw)
                return NULL;
        }
 
+       e_cal_component_commit_sequence (comp);
+       g_message ("DEBUG:\n %s\n", e_cal_component_get_as_string (comp));
+
        return comp;
 }
 
@@ -1363,7 +1581,6 @@ add_return_value (EGwSendOptionsReturnNotify track, ESource *source, char *notif
 void
 e_cal_backend_groupwise_store_settings (EGwSendOptions *opts, ECalBackendGroupwise *cbgw)
 {
-       ECalBackendGroupwisePrivate *priv = cbgw->priv;
        EGwSendOptionsGeneral *gopts;
        EGwSendOptionsStatusTracking *sopts;
        icaltimetype tt;
@@ -1463,3 +1680,6 @@ e_cal_backend_groupwise_store_settings (EGwSendOptions *opts, ECalBackendGroupwi
 
        g_object_unref (gconf);
 }
+
+
+       
index 7e60ad1..656a40e 100644 (file)
@@ -58,6 +58,7 @@ struct _ECalBackendGroupwisePrivate {
 
        /* fields for storing info while offline */
        char *user_email;
+       char *local_attachments_store;
 };
 
 static void e_cal_backend_groupwise_dispose (GObject *object);
@@ -132,7 +133,9 @@ populate_cache (ECalBackendGroupwise *cbgw)
                g_mutex_unlock (mutex);
                 return status;
         }
-       status = e_gw_connection_create_cursor (priv->cnc, priv->container_id, "recipients message recipientStatus default", NULL, &cursor);
+       status = e_gw_connection_create_cursor (priv->cnc,
+                       priv->container_id, 
+                       "recipients message recipientStatus attachments default", NULL, &cursor);
        if (status != E_GW_CONNECTION_STATUS_OK) {
                e_cal_backend_groupwise_notify_error_code (cbgw, status);
                g_mutex_unlock (mutex);
@@ -628,6 +631,11 @@ e_cal_backend_groupwise_finalize (GObject *object)
                priv->user_email = NULL;
        }
 
+       if (priv->local_attachments_store) {
+               g_free (priv->local_attachments_store);
+               priv->local_attachments_store = NULL;
+       }
+
        if (priv->timeout_id) {
                g_source_remove (priv->timeout_id);
                priv->timeout_id = 0;
@@ -742,6 +750,8 @@ e_cal_backend_groupwise_open (ECalBackendSync *backend, EDataCal *cal, gboolean
        ECalBackendGroupwise *cbgw;
        ECalBackendGroupwisePrivate *priv;
        ECalBackendSyncStatus status;
+       char *mangled_uri;
+       int i;
        
        cbgw = E_CAL_BACKEND_GROUPWISE (backend);
        priv = cbgw->priv;
@@ -779,7 +789,23 @@ e_cal_backend_groupwise_open (ECalBackendSync *backend, EDataCal *cal, gboolean
 
        priv->username = g_strdup (username);
        priv->password = g_strdup (password);
-       
+
+       /* Set the local attachment store*/
+       mangled_uri = g_strdup (e_cal_backend_get_uri (E_CAL_BACKEND (cbgw)));
+       /* mangle the URI to not contain invalid characters */
+       for (i = 0; i < strlen (mangled_uri); i++) {
+               switch (mangled_uri[i]) {
+               case ':' :
+               case '/' :
+                       mangled_uri[i] = '_';
+               }
+       }
+
+       priv->local_attachments_store = 
+               g_strconcat ("file://", g_get_home_dir (), "/", ".evolution/cache/calendar",
+                            "/", mangled_uri, NULL);
+       g_free (mangled_uri);
+
        /* FIXME: no need to set it online here when we implement the online/offline stuff correctly */
        status = connect_to_server (cbgw);
 
@@ -1812,7 +1838,7 @@ e_cal_backend_groupwise_init (ECalBackendGroupwise *cbgw, ECalBackendGroupwiseCl
        cbgw->priv = priv;
 }
 
-/* Class initialization function for the file backend */
+/* Class initialization function for the gw backend */
 static void
 e_cal_backend_groupwise_class_init (ECalBackendGroupwiseClass *class)
 {
@@ -1903,3 +1929,10 @@ e_cal_backend_groupwise_notify_error_code (ECalBackendGroupwise *cbgw, EGwConnec
        if (msg)
                e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), msg);
 }
+
+const char *
+e_cal_backend_groupwise_get_local_attachments_store (ECalBackendGroupwise *cbgw)
+{
+       g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
+       return cbgw->priv->local_attachments_store;
+}
index 623597c..ecf9ffc 100644 (file)
@@ -57,6 +57,7 @@ GHashTable* e_cal_backend_groupwise_get_categories_by_id (ECalBackendGroupwise *
 GHashTable* e_cal_backend_groupwise_get_categories_by_name (ECalBackendGroupwise *cbgw);
 icaltimezone* e_cal_backend_groupwise_get_default_zone (ECalBackendGroupwise *cbgw);
 void    e_cal_backend_groupwise_notify_error_code (ECalBackendGroupwise *cbgw, EGwConnectionStatus status);
+const char * e_cal_backend_groupwise_get_local_attachments_store (ECalBackendGroupwise *cbgw);
 
 G_END_DECLS
 
index a2c7f46..4a38a06 100644 (file)
@@ -106,6 +106,8 @@ struct _ECalPrivate {
 
        /* The component listener to keep track of the lifetime of backends */
        EComponentListener *comp_listener;
+
+       char *local_attachment_store;
 };
 
 \f
@@ -1086,6 +1088,7 @@ e_cal_init (ECal *ecal, ECalClass *klass)
 
        priv->load_state = E_CAL_LOAD_NOT_LOADED;
        priv->uri = NULL;
+       priv->local_attachment_store = NULL;
        priv->mutex = g_mutex_new ();
        priv->listener = e_cal_listener_new (cal_set_mode_cb, ecal);
 
@@ -1168,6 +1171,11 @@ e_cal_finalize (GObject *object)
                priv->uri = NULL;
        }
 
+       if (priv->local_attachment_store) {
+               g_free (priv->local_attachment_store);
+               priv->local_attachment_store = NULL;
+       }
+
        if (priv->mutex) {
                g_mutex_free (priv->mutex);
                priv->mutex = NULL;
@@ -1364,6 +1372,41 @@ e_cal_activate ()
        g_static_mutex_unlock (&e_cal_lock);
 }
 
+
+/* TODO - For now, the policy of where each backend serializes its
+ * attachment data is hardcoded below. Should this end up as a
+ * gconf key set during the account creation  and fetched
+ * from eds???
+ */
+static void
+set_local_attachment_store (ECal *ecal)
+{
+       ECalPrivate *priv;
+       char *mangled_uri;
+       int i;
+
+       priv = ecal->priv;
+       mangled_uri = g_strdup (priv->uri);
+       /* mangle the URI to not contain invalid characters */
+       for (i = 0; i < strlen (mangled_uri); i++) {
+               switch (mangled_uri[i]) {
+               case ':' :
+               case '/' :
+                       mangled_uri[i] = '_';
+               }
+       }
+
+       /* the file backend uses its uri as the attachment store*/
+       if (g_str_has_prefix (priv->uri, "file://"))
+               priv->local_attachment_store = g_strdup (priv->uri);
+       if (g_str_has_prefix (priv->uri, "groupwise://")) {
+               /* points to the location of the cache*/
+               priv->local_attachment_store = 
+                       g_strconcat ("file://", g_get_home_dir (), "/", ".evolution/cache/calendar",
+                                    "/", mangled_uri, NULL);
+       }
+}
+
 /**
  * e_cal_new:
  * @source: 
@@ -1390,6 +1433,9 @@ e_cal_new (ESource *source, ECalSourceType type)
                return NULL;
        }
 
+       /* Set the local attachment store path for the calendar */
+       set_local_attachment_store (ecal);
+
        /* initialize component listener */
        ecal->priv->comp_listener = e_component_listener_new ((Bonobo_Unknown) ecal->priv->cal);
        g_signal_connect (G_OBJECT (ecal->priv->comp_listener), "component_died",
@@ -1896,6 +1942,30 @@ e_cal_get_uri (ECal *ecal)
 }
 
 /**
+ * e_cal_get_local_attachment_store
+ * @ecal: A calendar ecal.
+ * 
+ * Queries the URL where the calendar attachments are
+ * serialized in the local filesystem. This enable clients
+ * to operate with the reference to attachments rather than the data itself
+ * unless it specifically uses the attachments for open/sending
+ * operations.
+ * Return value: The URL where the attachments are serialized in the
+ * local filesystem.
+ **/
+const char *
+e_cal_get_local_attachment_store (ECal *ecal)
+{
+       ECalPrivate *priv;
+
+       g_return_val_if_fail (ecal != NULL, NULL);
+       g_return_val_if_fail (E_IS_CAL (ecal), NULL);
+
+       priv = ecal->priv;
+       return (const char *)priv->local_attachment_store;
+}
+
+/**
  * e_cal_is_read_only:
  * @ecal: A calendar ecal.
  * @read_only: 
index a78e21f..ea045ec 100644 (file)
@@ -204,6 +204,7 @@ gboolean    e_cal_open_default (ECal **ecal, ECalSourceType type, ECalAuthFunc f
 gboolean    e_cal_set_default (ECal  *ecal, GError **error);
 gboolean    e_cal_set_default_source (ESource *source, ECalSourceType type, GError **error);
 gboolean    e_cal_get_sources (ESourceList **sources, ECalSourceType type, GError **error);
+const char * e_cal_get_local_attachment_store (ECal *ecal);
 
 G_END_DECLS