1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* camel-groupwise-utils.c
4 * Copyright (C) 2001 Ximian, Inc.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
28 #include <sys/types.h>
31 #include <glib/gstdio.h>
33 #include <libsoup/soup-misc.h>
35 #include "camel/camel-address.h"
36 #include "camel/camel-mime-filter-charset.h"
37 #include "camel/camel-mime-message.h"
38 #include "camel/camel-multipart.h"
39 #include "camel/camel-service.h"
40 #include "camel/camel-stream-filter.h"
41 #include "camel/camel-stream-mem.h"
43 #include "camel-groupwise-utils.h"
45 #define SUBFOLDER_DIR_NAME "subfolders"
46 #define SUBFOLDER_DIR_NAME_LEN 10
47 #define RFC_822 "message/rfc822"
49 static void do_multipart (EGwConnection *cnc, EGwItem *item, CamelMultipart *mp, GSList **attach_list);
52 * @prefix: a prefix to prepend to the path, or %NULL
53 * @path: the virtual path to convert to a filesystem path.
55 * This converts the "virtual" path @path into an expanded form that
56 * allows a given name to refer to both a file and a directory. The
57 * expanded path will have a "subfolders" directory inserted between
58 * each path component. If the path ends with "/", the returned
59 * physical path will end with "/subfolders"
61 * If @prefix is non-%NULL, it will be prepended to the returned path.
63 * Return value: the expanded path
66 e_path_to_physical (const char *prefix, const char *vpath)
79 /* Calculate the length of the real path. */
80 ppath_len = strlen (vpath);
81 ppath_len++; /* For the ending zero. */
83 prefix_len = strlen (prefix);
84 ppath_len += prefix_len;
85 ppath_len++; /* For the separating slash. */
87 /* Take account of the fact that we need to translate every
88 * separator into `subfolders/'.
92 newp = strchr (p, '/');
96 ppath_len += SUBFOLDER_DIR_NAME_LEN;
97 ppath_len++; /* For the separating slash. */
99 /* Skip consecutive slashes. */
106 ppath = g_malloc (ppath_len);
109 memcpy (dp, prefix, prefix_len);
113 /* Copy the mangled path. */
116 newp = strchr (p, '/');
122 memcpy (dp, p, newp - p + 1); /* `+ 1' to copy the slash too. */
125 memcpy (dp, SUBFOLDER_DIR_NAME, SUBFOLDER_DIR_NAME_LEN);
126 dp += SUBFOLDER_DIR_NAME_LEN;
130 /* Skip consecutive slashes. */
142 find_folders_recursive (const char *physical_path, const char *path,
143 EPathFindFoldersCallback callback, gpointer data)
146 char *subfolder_directory_path;
150 if (!callback (physical_path, path, data))
153 subfolder_directory_path = g_strdup_printf ("%s/%s", physical_path, SUBFOLDER_DIR_NAME);
155 /* On the top level, we have no folders and,
156 * consequently, no subfolder directory.
159 subfolder_directory_path = g_strdup (physical_path);
162 /* Now scan the subfolders and load them. */
163 dir = g_dir_open (subfolder_directory_path, 0, NULL);
165 g_free (subfolder_directory_path);
171 struct stat file_stat;
176 dirent = g_dir_read_name (dir);
180 file_path = g_strdup_printf ("%s/%s", subfolder_directory_path, dirent);
182 if (g_stat (file_path, &file_stat) < 0 ||
183 ! S_ISDIR (file_stat.st_mode)) {
188 new_path = g_strdup_printf ("%s/%s", path, dirent);
190 ok = find_folders_recursive (file_path, new_path, callback, data);
197 g_free (subfolder_directory_path);
203 * e_path_find_folders:
204 * @prefix: directory to start from
205 * @callback: Callback to invoke on each folder
206 * @data: Data for @callback
208 * Walks the folder tree starting at @prefix and calls @callback
211 * Return value: %TRUE on success, %FALSE if an error occurs at any point
214 e_path_find_folders (const char *prefix,
215 EPathFindFoldersCallback callback,
218 return find_folders_recursive (prefix, "", callback, data);
224 * @prefix: a prefix to prepend to the path, or %NULL
225 * @path: the virtual path to convert to a filesystem path.
227 * This removes the directory pointed to by @prefix and @path
228 * and attempts to remove its parent "subfolders" directory too
231 * Return value: -1 (with errno set) if it failed to rmdir the
232 * specified directory. 0 otherwise, whether or not it removed
233 * the parent directory.
236 e_path_rmdir (const char *prefix, const char *vpath)
238 char *physical_path, *p;
240 /* Remove the directory itself */
241 physical_path = e_path_to_physical (prefix, vpath);
242 if (g_rmdir (physical_path) == -1) {
243 g_free (physical_path);
247 /* Attempt to remove its parent "subfolders" directory,
248 * ignoring errors since it might not be empty.
251 p = strrchr (physical_path, '/');
253 g_free (physical_path);
257 p = strrchr (physical_path, '/');
258 if (!p || strcmp (p + 1, SUBFOLDER_DIR_NAME) != 0) {
259 g_free (physical_path);
263 g_rmdir (physical_path);
264 g_free (physical_path);
269 add_recipients(GSList *recipient_list, CamelAddress *recipients, int recipient_type)
272 EGwItemRecipient *recipient;
274 total_add = camel_address_length (recipients);
275 for (i=0 ; i<total_add ; i++) {
276 const char *name = NULL, *addr = NULL;
277 if(camel_internet_address_get ((CamelInternetAddress *)recipients, i , &name, &addr )) {
279 recipient = g_new0 (EGwItemRecipient, 1);
281 recipient->email = g_strdup (addr);
282 recipient->display_name = g_strdup (name);
283 recipient->type = recipient_type;
284 recipient->status = E_GW_ITEM_STAT_NONE;
285 recipient_list = g_slist_prepend (recipient_list, recipient);
288 return recipient_list;
292 send_as_attachment (EGwConnection *cnc, EGwItem *item, CamelStreamMem *content, CamelContentType *type, CamelDataWrapper *dw, const char *filename, const char *cid, GSList **attach_list)
294 EGwItemLinkInfo *info = NULL;
295 EGwConnectionStatus status;
296 EGwItemAttachment *attachment;
299 attachment = g_new0 (EGwItemAttachment, 1);
300 attachment->contentType = camel_content_type_simple (type);
303 attachment->contentid = camel_header_contentid_decode (cid);
306 if (camel_content_type_is (type, "application", "pgp-signature")) {
309 temp_str = soup_base64_encode (content->buffer->data, content->buffer->len);
310 temp_len = strlen (temp_str);
311 attachment->data = g_strdup (temp_str);
312 attachment->size = temp_len;
317 attachment->data = soup_base64_encode(content->buffer->data, content->buffer->len);
318 attachment->size = strlen (attachment->data);
323 if (!strcmp (attachment->contentType, "multipart/digest")) {
326 temp_str = soup_base64_encode (content->buffer->data, content->buffer->len);
327 temp_len = strlen (temp_str);
328 attachment->data = g_strdup (temp_str);
329 attachment->size = temp_len;
336 if (camel_content_type_is (type, "text", "html") || camel_content_type_is (type, "multipart", "alternative")) {
338 filename = "text.htm";
339 if (camel_content_type_is (type, "multipart", "alternative")) {
340 /* FIXME: this just feels so wrong... */
341 g_free (attachment->contentType);
342 attachment->contentType = g_strdup ("text/html");
346 attachment->name = g_strdup (filename ? filename : "");
347 if (camel_content_type_is (type, "message", "rfc822")) {
348 const char *message_id;
352 message_id = camel_medium_get_header (CAMEL_MEDIUM (dw), "Message-Id");
354 * XXX: The following code piece is a screwed up way of doing stuff.
355 * But we dont have much choice. Do not use 'camel_header_msgid_decode'
356 * since it removes the container id portion from the id and which the
357 * groupwise server needs.
360 len = strlen (message_id);
361 msgid = (char *)g_malloc0 (len-1);
362 msgid = memcpy(msgid, message_id+2, len-3);
363 g_print ("||| msgid:%s\n", msgid);
365 status = e_gw_connection_forward_item (cnc, msgid, NULL, TRUE, &temp_item);
368 if (status != E_GW_CONNECTION_STATUS_OK) {
369 g_warning ("Could not send a forwardRequest...continuing without!!\n");
371 GSList *attach_list = e_gw_item_get_attach_id_list (temp_item);
372 EGwItemAttachment *temp_attach = (EGwItemAttachment *)attach_list->data;
373 attachment->id = g_strdup (temp_attach->id);
374 attachment->item_reference = g_strdup (temp_attach->item_reference);
375 g_free (attachment->name);
376 attachment->name = g_strdup (temp_attach->name);
377 g_free (attachment->contentType);
378 attachment->contentType = g_strdup ("Mail");
379 g_free (attachment->data);
380 attachment->data = NULL;
381 attachment->size = 0;
382 info = e_gw_item_get_link_info (temp_item);
383 e_gw_item_set_link_info (item, info);
387 *attach_list = g_slist_append (*attach_list, attachment);
391 camel_groupwise_util_item_from_message (EGwConnection *cnc, CamelMimeMessage *message, CamelAddress *from)
394 EGwItemOrganizer *org = g_new0 (EGwItemOrganizer, 1);
395 const char *display_name = NULL, *email = NULL;
396 char *send_options = NULL;
398 GSList *recipient_list = NULL, *attach_list = NULL;
399 CamelAddress *recipients;
402 item = e_gw_item_new_empty ();
404 /*populate recipient list*/
405 recipients = CAMEL_ADDRESS (camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO));
406 recipient_list=add_recipients(recipient_list,recipients,E_GW_ITEM_RECIPIENT_TO);
408 recipients = CAMEL_ADDRESS (camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC));
409 recipient_list=add_recipients(recipient_list,recipients,E_GW_ITEM_RECIPIENT_CC);
411 recipients = CAMEL_ADDRESS (camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC));
412 recipient_list=add_recipients(recipient_list,recipients,E_GW_ITEM_RECIPIENT_BC);
413 recipient_list = g_slist_reverse (recipient_list);
415 /** Get the mime parts from CamelMimemessge **/
416 mp = (CamelMultipart *)camel_medium_get_content_object (CAMEL_MEDIUM (message));
418 g_warning ("ERROR: Could not get content object");
419 camel_operation_end (NULL);
423 if (CAMEL_IS_MULTIPART (mp)) {
424 /*contains multiple parts*/
425 do_multipart (cnc, item, mp, &attach_list);
428 CamelStreamMem *content = (CamelStreamMem *)camel_stream_mem_new ();
429 CamelDataWrapper *dw = NULL;
430 CamelContentType *type;
432 dw = camel_medium_get_content_object (CAMEL_MEDIUM (message));
433 type = camel_mime_part_get_content_type((CamelMimePart *)message);
435 if (camel_content_type_is (type, "text", "plain")) {
436 CamelStream *filtered_stream;
437 CamelMimeFilter *filter;
441 content_type = camel_content_type_simple (type);
442 e_gw_item_set_content_type (item, content_type);
443 g_free (content_type);
445 charset = camel_content_type_param (type, "charset");
446 if (charset && g_ascii_strcasecmp (charset, "US-ASCII") && g_ascii_strcasecmp (charset, "UTF-8")) {
447 filter = (CamelMimeFilter *) camel_mime_filter_charset_new_convert (charset, "UTF-8");
448 filtered_stream = (CamelStream *) camel_stream_filter_new_with_stream ((CamelStream *) content);
449 camel_stream_filter_add ((CamelStreamFilter *) filtered_stream, filter);
450 camel_object_unref (filter);
452 /* US-ASCII or UTF-8 */
453 filtered_stream = (CamelStream *) content;
454 camel_object_ref (content);
457 camel_data_wrapper_decode_to_stream (dw, filtered_stream);
458 camel_stream_flush (filtered_stream);
459 camel_object_unref (filtered_stream);
461 camel_stream_write ((CamelStream *) content, "", 1);
462 e_gw_item_set_message (item, (const char *)content->buffer->data);
464 camel_data_wrapper_decode_to_stream (dw, (CamelStream *) content);
465 send_as_attachment (cnc, item, content, type, dw, NULL, NULL, &attach_list);
468 camel_object_unref (content);
472 camel_internet_address_get ((CamelInternetAddress *)from, 0 , &display_name, &email);
473 org->display_name = g_strdup (display_name);
474 org->email = g_strdup (email);
475 e_gw_item_set_organizer (item, org);
477 e_gw_item_set_recipient_list (item, recipient_list);
478 /*Item type is mail*/
479 e_gw_item_set_item_type (item, E_GW_ITEM_TYPE_MAIL);
481 e_gw_item_set_subject (item, camel_mime_message_get_subject(message));
483 e_gw_item_set_attach_id_list (item, attach_list);
486 e_gw_item_set_sendoptions (item, TRUE);
488 if ((char *)camel_medium_get_header (CAMEL_MEDIUM(message), X_REPLY_CONVENIENT))
489 e_gw_item_set_reply_request (item, TRUE);
491 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM(message), X_REPLY_WITHIN);
493 e_gw_item_set_reply_request (item, TRUE);
494 e_gw_item_set_reply_within (item, send_options);
496 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM(message),X_EXPIRE_AFTER);
498 e_gw_item_set_expires (item, send_options);
500 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM(message), X_DELAY_UNTIL);
502 e_gw_item_set_delay_until (item, send_options);
504 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM(message), X_TRACK_WHEN);
506 /*we check if user has modified the status tracking options, if no then we anyway
507 * set status tracking all*/
509 switch (atoi(send_options)) {
510 case 1: e_gw_item_set_track_info (item, E_GW_ITEM_DELIVERED);
512 case 2: e_gw_item_set_track_info (item, E_GW_ITEM_DELIVERED_OPENED);
514 case 3: e_gw_item_set_track_info (item, E_GW_ITEM_ALL);
516 default: e_gw_item_set_track_info (item, E_GW_ITEM_NONE);
520 e_gw_item_set_track_info (item, E_GW_ITEM_ALL);
522 if ((char *)camel_medium_get_header (CAMEL_MEDIUM(message), X_AUTODELETE))
523 e_gw_item_set_autodelete (item, TRUE);
525 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM (message), X_RETURN_NOTIFY_OPEN);
527 switch (atoi(send_options)) {
528 case 0: e_gw_item_set_notify_opened (item, E_GW_ITEM_NOTIFY_NONE);
530 case 1: e_gw_item_set_notify_opened (item, E_GW_ITEM_NOTIFY_MAIL);
533 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM (message), X_RETURN_NOTIFY_DELETE);
535 switch (atoi(send_options)) {
536 case 0: e_gw_item_set_notify_deleted (item, E_GW_ITEM_NOTIFY_NONE);
538 case 1: e_gw_item_set_notify_deleted (item, E_GW_ITEM_NOTIFY_MAIL);
542 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM (message), X_SEND_OPT_PRIORITY);
544 switch (atoi(send_options)) {
545 case E_GW_PRIORITY_HIGH: e_gw_item_set_priority(item, "High");
547 case E_GW_PRIORITY_LOW: e_gw_item_set_priority(item, "Low");
549 case E_GW_PRIORITY_STANDARD: e_gw_item_set_priority(item, "Standard");
554 send_options = (char *)camel_medium_get_header (CAMEL_MEDIUM (message), X_SEND_OPT_SECURITY);
556 switch (atoi(send_options)) {
557 case E_GW_SECURITY_NORMAL : e_gw_item_set_security(item, "Normal");
559 case E_GW_SECURITY_PROPRIETARY : e_gw_item_set_security(item, "Proprietary");
561 case E_GW_SECURITY_CONFIDENTIAL : e_gw_item_set_security(item, "Confidential");
563 case E_GW_SECURITY_SECRET : e_gw_item_set_security(item, "Secret");
565 case E_GW_SECURITY_TOP_SECRET : e_gw_item_set_security(item, "TopSecret");
567 case E_GW_SECURITY_FOR_YOUR_EYES_ONLY : e_gw_item_set_security(item, "ForYourEyesOnly");
575 do_flags_diff (flags_diff_t *diff, guint32 old, guint32 _new)
577 diff->changed = old ^ _new;
578 diff->bits = _new & diff->changed;
582 gw_concat ( const char *prefix, const char *suffix)
586 len = strlen (prefix);
587 if (len == 0 || prefix[len - 1] == '/')
588 return g_strdup_printf ("%s%s", prefix, suffix);
590 return g_strdup_printf ("%s%c%s", prefix, '/', suffix);
594 strip_lt_gt (char **string, int s_offset, int e_offset)
599 temp = g_strdup (*string);
600 len = strlen (*string);
602 *string = (char *)g_malloc0 (len-1);
603 *string = memcpy(*string, temp+s_offset, len-e_offset);
608 do_multipart (EGwConnection *cnc, EGwItem *item, CamelMultipart *mp, GSList **attach_list)
610 /*contains multiple parts*/
614 part_count = camel_multipart_get_number (mp);
615 for ( i=0 ; i<part_count ; i++) {
616 CamelContentType *type;
618 CamelStreamMem *content = (CamelStreamMem *)camel_stream_mem_new ();
619 CamelDataWrapper *dw = NULL;
620 const char *disposition, *filename;
621 const char *content_id = NULL;
622 gboolean is_alternative = FALSE;
625 * Assuming the first part always is the actual message
626 * and an attachment otherwise.....
628 part = camel_multipart_get_part (mp, i);
633 type = camel_mime_part_get_content_type(part);
634 dw = camel_medium_get_content_object (CAMEL_MEDIUM (part));
636 if (CAMEL_IS_MULTIPART (dw)) {
637 do_multipart (cnc, item, (CamelMultipart *) camel_medium_get_content_object ((CamelMedium *) part), attach_list);
641 if (type->subtype && !strcmp (type->subtype, "alternative")) {
642 /* eh... I don't think this code will ever get hit? */
643 CamelMimePart *temp_part;
644 const char *cid = NULL;
645 CamelStreamMem *temp_content = (CamelStreamMem *)camel_stream_mem_new ();
646 CamelDataWrapper *temp_dw = NULL;
648 temp_part = camel_multipart_get_part ((CamelMultipart *)dw, 1);
650 is_alternative = TRUE;
651 temp_dw = camel_medium_get_content_object (CAMEL_MEDIUM (temp_part));
652 camel_data_wrapper_write_to_stream(temp_dw, (CamelStream *)temp_content);
653 filename = camel_mime_part_get_filename (temp_part);
654 disposition = camel_mime_part_get_disposition (temp_part);
655 cid = camel_mime_part_get_content_id (temp_part);
656 send_as_attachment (cnc, item, temp_content, type, temp_dw, filename, cid, attach_list);
658 camel_object_unref (temp_content);
662 if (i == 0 && camel_content_type_is (type, "text", "plain")) {
663 CamelStream *filtered_stream;
664 CamelMimeFilter *filter;
668 content_type = camel_content_type_simple (type);
669 e_gw_item_set_content_type (item, content_type);
670 g_free (content_type);
672 charset = camel_content_type_param (type, "charset");
673 if (charset && g_ascii_strcasecmp (charset, "US-ASCII") && g_ascii_strcasecmp (charset, "UTF-8")) {
674 filter = (CamelMimeFilter *) camel_mime_filter_charset_new_convert (charset, "UTF-8");
675 filtered_stream = (CamelStream *) camel_stream_filter_new_with_stream ((CamelStream *) content);
676 camel_stream_filter_add ((CamelStreamFilter *) filtered_stream, filter);
677 camel_object_unref (filter);
679 /* US-ASCII or UTF-8 */
680 filtered_stream = (CamelStream *) content;
681 camel_object_ref (content);
684 camel_data_wrapper_decode_to_stream (dw, filtered_stream);
685 camel_stream_flush (filtered_stream);
686 camel_object_unref (filtered_stream);
688 camel_stream_write ((CamelStream *) content, "", 1);
689 e_gw_item_set_message (item, (const char *)content->buffer->data);
691 filename = camel_mime_part_get_filename (part);
692 disposition = camel_mime_part_get_disposition (part);
693 content_id = camel_mime_part_get_content_id (part);
695 camel_data_wrapper_decode_to_stream (dw, (CamelStream *) content);
696 send_as_attachment (cnc, item, content, type, dw, filename, content_id, attach_list);
699 camel_object_unref (content);