From 1d6faf3cc8970839cd952b703cd7d013cda94f0e Mon Sep 17 00:00:00 2001 From: Harish Krishnaswamy Date: Mon, 10 Jan 2005 10:43:42 +0000 Subject: [PATCH] Attachment support for Groupwise calendar and task items. * 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 | 19 ++ .../groupwise/e-cal-backend-groupwise-utils.c | 226 ++++++++++++++++++++- .../backends/groupwise/e-cal-backend-groupwise.c | 39 +++- .../backends/groupwise/e-cal-backend-groupwise.h | 1 + calendar/libecal/e-cal.c | 70 +++++++ calendar/libecal/e-cal.h | 1 + 6 files changed, 350 insertions(+), 6 deletions(-) diff --git a/calendar/ChangeLog b/calendar/ChangeLog index a4e1f06..32f88fc 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,22 @@ +2005-01-10 Harish Krishnaswamy + + * 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 * backends/weather/e-cal-backend-weather.c (create_weather): make diff --git a/calendar/backends/groupwise/e-cal-backend-groupwise-utils.c b/calendar/backends/groupwise/e-cal-backend-groupwise-utils.c index c839a12..0d0d400 100644 --- a/calendar/backends/groupwise/e-cal-backend-groupwise-utils.c +++ b/calendar/backends/groupwise/e-cal-backend-groupwise-utils.c @@ -3,8 +3,8 @@ * Authors : * JP Rosevear * Rodrigo Moya - * - * Copyright 2003, Novell, Inc. + * Harish Krishnaswamy + * 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 @@ -21,11 +21,21 @@ * USA */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include +#include +#include +#include +#include +#include #include #include #include #include +#include #include "e-cal-backend-groupwise-utils.h" #include @@ -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:///compuid- + */ + 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); } + + + diff --git a/calendar/backends/groupwise/e-cal-backend-groupwise.c b/calendar/backends/groupwise/e-cal-backend-groupwise.c index 7e60ad1..656a40e 100644 --- a/calendar/backends/groupwise/e-cal-backend-groupwise.c +++ b/calendar/backends/groupwise/e-cal-backend-groupwise.c @@ -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; +} diff --git a/calendar/backends/groupwise/e-cal-backend-groupwise.h b/calendar/backends/groupwise/e-cal-backend-groupwise.h index 623597c..ecf9ffc 100644 --- a/calendar/backends/groupwise/e-cal-backend-groupwise.h +++ b/calendar/backends/groupwise/e-cal-backend-groupwise.h @@ -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 diff --git a/calendar/libecal/e-cal.c b/calendar/libecal/e-cal.c index a2c7f46..4a38a06 100644 --- a/calendar/libecal/e-cal.c +++ b/calendar/libecal/e-cal.c @@ -106,6 +106,8 @@ struct _ECalPrivate { /* The component listener to keep track of the lifetime of backends */ EComponentListener *comp_listener; + + char *local_attachment_store; }; @@ -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: diff --git a/calendar/libecal/e-cal.h b/calendar/libecal/e-cal.h index a78e21f..ea045ec 100644 --- a/calendar/libecal/e-cal.h +++ b/calendar/libecal/e-cal.h @@ -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 -- 2.7.4