diff options
Diffstat (limited to 'camel')
22 files changed, 1045 insertions, 2418 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index 04f3d9b440..be4b562afc 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,132 @@ +2002-06-26 Not Zed <NotZed@Ximian.com> + + * camel-gpg-context.c: Include <sys/time.h> for struct timeval. + + * providers/local/camel-local-provider.c + (camel_provider_module_init): Removed spoold provider. The spool + provider does it now. + +2002-06-25 Not Zed <NotZed@Ximian.com> + + * providers/local/camel-spool-folder.c (camel_spool_folder_new): + Support a new xstatus option - folders update/honour the + Status/X-Status headers in addition to X-Evolution. + + * providers/local/camel-local-summary.c + (camel_local_summary_write_headers): If supplied with an + additional status or xstatus arg, write a Status header and/or + X-Status. Also fix the case of properly terminating the headers + if an xev line isn't supplied. + + * providers/local/Makefile.am (libcamellocalinclude_HEADERS,SOURCES): + Removed spoold-store.[ch]. + + * providers/local/camel-local-provider.c + (camel_provider_module_init): For the spoold type, just use the + spool store instead. + + * providers/local/camel-spool-store.h: Added a type field, so the + 1 store can implement different types without having to subclass. + + * providers/local/camel-spool-store.c + (camel_spool_store_get_toplevel_dir): Removed, inherits from local + store now. + (construct): If we're pointing to a file, treat it as mbox mode, + otherwise treat it as 'elm' mode. + (get_folder): Only test for INBOX in mbox mode. + (get_folder_info_elm): + (get_folder_info_mbox): Two alternatives for getting folder info, + depending on the type of folder we're looking at. + (get_folder_info_mbox): Make the url include the protocol. + (scan_dir): " + + * providers/local/camel-spoold-store.c + (camel_spoold_store_get_toplevel_dir): Removed, inherits from + local store now. + + * camel-folder.c (get_message_user_tag): Dont use a + g_return_if_fail for info==NULL. This is not an error. + (set_message_user_tag): And same here. + (set_message_user_flag): Sigh, and here. + (get_message_user_flag): And here. + (set_message_flags): and here ... + (get_message_flags): Dum de dum, de done at last. + + * providers/local/camel-mbox-folder.c (mbox_get_message): Check + for new messages whenever we retrieve one. In the common + no-update case, this is a single stat. + (mbox_get_message): If we need to rescan, then force a full rescan + to make sure it does the right thing. + (mbox_get_message): Cleanup the exception handling a bit, if we do + get an error, propagate any folder changes anyway as well. + (mbox_set_message_user_flag): Argh more of these stupid g_returns + taht shouldn't be. + (mbox_set_message_user_tag): Here too. + (mbox_set_message_flags): If the read flag is being changed, mark + it as an xevchange (i.e. Status line change). + + * providers/local/camel-mbox-summary.c (summary_rebuild): Merged + into summary_update. + (summary_update): Changed to allow it to update existing lists of + messages without clearing out the summary. + (mbox_summary_check): Dont clear the summary, just re-scan. + (message_info_new): Attempt to support the 'Status: RO' elm/pine + thing. + (camel_mbox_summary_encode_status): + (camel_mbox_summary_decode_status): Util functions for + creating/parsing the Status line. + (camel_mbox_summary_sync_mbox): Write out the status line if we're + going to try support it. + (camel_mbox_summary_xstatus): Implement option to control + read/write of (x-)status. + (message_info_new): Do x-status stuff based on run-time option. + (camel_mbox_summary_sync_mbox): " + (mbox_summary_add): If x-status enabled, then always add + status/x-status headers to message. + + * camel-folder-summary.c (summary_assign_uid): If the messageinfo + is already in the summary, AND is the same messageinfo, dont do + anything, return a value to indicate this. + (camel_folder_summary_add): Do nothing if this info already in the + summary, so we can perform updates. + +2002-06-24 Not Zed <NotZed@Ximian.com> + + * providers/local/camel-local-summary.c + (camel_local_summary_check_force): New method to force the next + summary check to be a full check, set if a mismatch occurs. + + * camel-folder-summary.c (camel_folder_summary_load): If we have + no summary path set, dont do any i/o, rather than abort. + (camel_folder_summary_save): " + (camel_folder_summary_header_load): " + + * providers/local/camel-spool-store.h: Inherit from camel mbox + store, even if we override almost everything. + + * providers/local/camel-local-folder.c + (camel_local_folder_construct): If the base path points to a file, + use that as the folder path as well. + + * providers/local/camel-spool-folder.h: Inherit from + camel-mbox-folder. + + * providers/local/camel-spool-summary.c (spool_summary_sync_full): + Use camel_mbox_summary_sync_mbox to do most of the work. + + * providers/local/camel-spool-summary.[ch]: Make spool-summary + inherit from mbox summary rather than foldersummary. + + * providers/local/camel-mbox-summary.c (mbox_summary_sync): Make + sync_full/quick virtual methods. + (camel_mbox_summary_sync_mbox): The full sync method put into a + simple function that sync's from fd to fd. + (mbox_summary_sync_full): Use summary_sync_mbox to do the real + work. + (mbox_summary_check): Create removed events if the folder gets + cleared. Also, dont clear the summary before a rebuild, try to + merge. + 2002-06-25 Jeffrey Stedfast <fejj@ximian.com> * camel-gpg-context.c: #include <sys/time.h> diff --git a/camel/camel-folder-summary.c b/camel/camel-folder-summary.c index aa5156b9be..d0d24b8820 100644 --- a/camel/camel-folder-summary.c +++ b/camel/camel-folder-summary.c @@ -549,7 +549,8 @@ camel_folder_summary_load(CamelFolderSummary *s) int i; CamelMessageInfo *mi; - g_assert(s->summary_path); + if (s->summary_path == NULL) + return 0; in = fopen(s->summary_path, "r"); if (in == NULL) @@ -629,9 +630,8 @@ camel_folder_summary_save(CamelFolderSummary *s) guint32 count; CamelMessageInfo *mi; - g_assert(s->summary_path); - - if ((s->flags & CAMEL_SUMMARY_DIRTY) == 0) + if (s->summary_path == NULL + || (s->flags & CAMEL_SUMMARY_DIRTY) == 0) return 0; fd = open(s->summary_path, O_RDWR|O_CREAT, 0600); @@ -690,7 +690,8 @@ int camel_folder_summary_header_load(CamelFolderSummary *s) FILE *in; int ret; - g_assert(s->summary_path); + if (s->summary_path == NULL) + return 0; in = fopen(s->summary_path, "r"); if (in == NULL) @@ -705,10 +706,11 @@ int camel_folder_summary_header_load(CamelFolderSummary *s) return ret; } -static void +static int summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info) { const char *uid; + CamelMessageInfo *mi; uid = camel_message_info_uid(info); if (uid == NULL || uid[0] == 0) { @@ -718,9 +720,11 @@ summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info) CAMEL_SUMMARY_LOCK(s, summary_lock); - while (g_hash_table_lookup(s->messages_uid, uid)) { - g_warning("Trying to insert message with clashing uid (%s). new uid re-assigned", camel_message_info_uid(info)); + while ((mi = g_hash_table_lookup(s->messages_uid, uid))) { CAMEL_SUMMARY_UNLOCK(s, summary_lock); + if (mi == info) + return 0; + g_warning("Trying to insert message with clashing uid (%s). new uid re-assigned", camel_message_info_uid(info)); camel_message_info_set_uid(info, camel_folder_summary_next_uid_string(s)); uid = camel_message_info_uid(info); info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; @@ -728,6 +732,7 @@ summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info) } CAMEL_SUMMARY_UNLOCK(s, summary_lock); + return 1; } /** @@ -747,7 +752,8 @@ void camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info) if (info == NULL) return; - summary_assign_uid(s, info); + if (summary_assign_uid(s, info) == 0) + return; CAMEL_SUMMARY_LOCK(s, summary_lock); diff --git a/camel/camel-folder.c b/camel/camel-folder.c index f0162646c0..89c5bc2e14 100644 --- a/camel/camel-folder.c +++ b/camel/camel-folder.c @@ -649,7 +649,8 @@ get_message_flags(CamelFolder *folder, const char *uid) g_return_val_if_fail(folder->summary != NULL, 0); info = camel_folder_summary_uid(folder->summary, uid); - g_return_val_if_fail(info != NULL, 0); + if (info == NULL) + return 0; flags = info->flags; camel_folder_summary_info_free(folder->summary, info); @@ -686,7 +687,8 @@ set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 s g_return_if_fail(folder->summary != NULL); info = camel_folder_summary_uid(folder->summary, uid); - g_return_if_fail(info != NULL); + if (info == NULL) + return; new = (info->flags & ~flags) | (set & flags); if (new == info->flags) { @@ -731,7 +733,8 @@ get_message_user_flag(CamelFolder *folder, const char *uid, const char *name) g_return_val_if_fail(folder->summary != NULL, FALSE); info = camel_folder_summary_uid(folder->summary, uid); - g_return_val_if_fail(info != NULL, FALSE); + if (info == NULL) + return FALSE; ret = camel_flag_get(&info->user_flags, name); camel_folder_summary_info_free(folder->summary, info); @@ -768,7 +771,8 @@ set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gb g_return_if_fail(folder->summary != NULL); info = camel_folder_summary_uid(folder->summary, uid); - g_return_if_fail(info != NULL); + if (info == NULL) + return; if (camel_flag_set(&info->user_flags, name, value)) { info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; @@ -807,7 +811,8 @@ get_message_user_tag(CamelFolder *folder, const char *uid, const char *name) g_return_val_if_fail(folder->summary != NULL, NULL); info = camel_folder_summary_uid(folder->summary, uid); - g_return_val_if_fail(info != NULL, FALSE); + if (info == NULL) + return NULL; #warning "Need to duplicate tag string" @@ -846,7 +851,8 @@ set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, con g_return_if_fail(folder->summary != NULL); info = camel_folder_summary_uid(folder->summary, uid); - g_return_if_fail(info != NULL); + if (info == NULL) + return; if (camel_tag_set(&info->user_tags, name, value)) { info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; diff --git a/camel/camel-gpg-context.c b/camel/camel-gpg-context.c index 2150de1357..124182b955 100644 --- a/camel/camel-gpg-context.c +++ b/camel/camel-gpg-context.c @@ -33,6 +33,7 @@ #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/wait.h> +#include <sys/time.h> #include <termios.h> #include <signal.h> #include <unistd.h> diff --git a/camel/providers/local/Makefile.am b/camel/providers/local/Makefile.am index 1261dad798..05e2d171fc 100644 --- a/camel/providers/local/Makefile.am +++ b/camel/providers/local/Makefile.am @@ -33,8 +33,7 @@ libcamellocal_la_SOURCES = \ camel-maildir-summary.c \ camel-spool-folder.c \ camel-spool-store.c \ - camel-spool-summary.c \ - camel-spoold-store.c + camel-spool-summary.c libcamellocalinclude_HEADERS = \ camel-local-folder.h \ @@ -51,8 +50,7 @@ libcamellocalinclude_HEADERS = \ camel-maildir-summary.h \ camel-spool-folder.h \ camel-spool-store.h \ - camel-spool-summary.h \ - camel-spoold-store.h + camel-spool-summary.h noinst_HEADERS = \ camel-local-private.h diff --git a/camel/providers/local/camel-local-folder.c b/camel/providers/local/camel-local-folder.c index e79e6b8f5d..1f82bd4c2b 100644 --- a/camel/providers/local/camel-local-folder.c +++ b/camel/providers/local/camel-local-folder.c @@ -190,9 +190,10 @@ camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store, con CamelFolderInfo *fi; CamelFolder *folder; const char *root_dir_path, *name; + char *tmp; char folder_path[PATH_MAX]; struct stat st; - int forceindex; + int forceindex, len; folder = (CamelFolder *)lf; @@ -205,11 +206,26 @@ camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store, con camel_folder_construct(folder, parent_store, full_name, name); root_dir_path = camel_local_store_get_toplevel_dir(CAMEL_LOCAL_STORE(folder->parent_store)); + /* strip the trailing '/' which is always present */ + len = strlen(root_dir_path); + tmp = alloca(len+1); + strcpy(tmp, root_dir_path); + if (len>1 && tmp[len-1] == '/') + tmp[len-1] = 0; lf->base_path = g_strdup(root_dir_path); - lf->folder_path = g_strdup_printf("%s/%s", root_dir_path, full_name); - lf->summary_path = g_strdup_printf("%s/%s.ev-summary", root_dir_path, full_name); - lf->index_path = g_strdup_printf("%s/%s.ibex", root_dir_path, full_name); + + /* if the base store points to a file, then use that */ + if (stat(tmp, &st) != -1 && S_ISREG(st.st_mode)) { + lf->folder_path = g_strdup(tmp); + /* not really sure to do with these for now? */ + lf->summary_path = g_strdup_printf("%s.ev-summary", tmp); + lf->index_path = g_strdup_printf("%s.ibex", tmp); + } else { + lf->folder_path = g_strdup_printf("%s/%s", root_dir_path, full_name); + lf->summary_path = g_strdup_printf("%s/%s.ev-summary", root_dir_path, full_name); + lf->index_path = g_strdup_printf("%s/%s.ibex", root_dir_path, full_name); + } /* follow any symlinks to the mailbox */ if (lstat (lf->folder_path, &st) != -1 && S_ISLNK (st.st_mode) && diff --git a/camel/providers/local/camel-local-private.h b/camel/providers/local/camel-local-private.h index 4dfe9f635c..568cec201c 100644 --- a/camel/providers/local/camel-local-private.h +++ b/camel/providers/local/camel-local-private.h @@ -51,20 +51,6 @@ struct _CamelLocalFolderPrivate { #define CAMEL_LOCAL_FOLDER_UNLOCK(f, l) #endif -struct _CamelSpoolFolderPrivate { -#ifdef ENABLE_THREADS - GMutex *search_lock; /* for locking the search object */ -#endif -}; - -#ifdef ENABLE_THREADS -#define CAMEL_SPOOL_FOLDER_LOCK(f, l) (g_mutex_lock(((CamelSpoolFolder *)f)->priv->l)) -#define CAMEL_SPOOL_FOLDER_UNLOCK(f, l) (g_mutex_unlock(((CamelSpoolFolder *)f)->priv->l)) -#else -#define CAMEL_SPOOL_FOLDER_LOCK(f, l) -#define CAMEL_SPOOL_FOLDER_UNLOCK(f, l) -#endif - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/camel/providers/local/camel-local-provider.c b/camel/providers/local/camel-local-provider.c index 7bf9decfc7..36ec6ff8f0 100644 --- a/camel/providers/local/camel-local-provider.c +++ b/camel/providers/local/camel-local-provider.c @@ -92,15 +92,15 @@ static CamelProvider maildir_provider = { static CamelProviderConfEntry spool_conf_entries[] = { CAMEL_PROVIDER_CONF_DEFAULT_PATH, - { CAMEL_PROVIDER_CONF_CHECKBOX, "filter", NULL, - N_("Apply filters to new messages in INBOX"), "0" }, + { CAMEL_PROVIDER_CONF_CHECKBOX, "filter", NULL, N_("Apply filters to new messages in INBOX"), "0" }, + { CAMEL_PROVIDER_CONF_CHECKBOX, "xstatus", NULL, N_("Store status headers in Elm/Pine/Mutt format"), "0" }, { CAMEL_PROVIDER_CONF_END } }; static CamelProvider spool_provider = { "spool", - N_("Standard Unix mbox spools"), - N_("For reading and storing local mail in standard mbox spool files."), + N_("Standard Unix mbox spool or directory"), + N_("For reading and storing local mail in standard mbox spool files.\nMay also be used to read a tree of Elm, Pine, or Mutt style folders."), "mail", CAMEL_PROVIDER_IS_SOURCE | CAMEL_PROVIDER_IS_STORAGE, CAMEL_URL_NEED_PATH | CAMEL_URL_PATH_IS_ABSOLUTE, @@ -108,22 +108,6 @@ static CamelProvider spool_provider = { /* ... */ }; -static CamelProviderConfEntry spoold_conf_entries[] = { - CAMEL_PROVIDER_CONF_DEFAULT_PATH, - { CAMEL_PROVIDER_CONF_END } -}; - -static CamelProvider spoold_provider = { - "spoold", - N_("Directory tree of mbox files"), - N_("For accessing mail storedin an external tree of mbox files.\nThis will allow you to directly access pine and elm folders.\nNOTE: This provider is still experimental so ensure you backup any mail folders first."), - "mail", - CAMEL_PROVIDER_IS_SOURCE | CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_IS_LOCAL, - CAMEL_URL_NEED_PATH | CAMEL_URL_PATH_IS_ABSOLUTE, - NULL, - /* ... */ -}; - /* build a canonical 'path' */ static char * make_can_path(char *p, char *o) @@ -236,11 +220,4 @@ void camel_provider_module_init(CamelSession * session) maildir_provider.url_hash = local_url_hash; maildir_provider.url_equal = local_url_equal; camel_session_register_provider(session, &maildir_provider); - - path = g_strdup_printf ("%s/mail", g_get_home_dir ()); - spoold_conf_entries[0].value = path; /* default path */ - spoold_provider.object_types[CAMEL_PROVIDER_STORE] = camel_spoold_store_get_type (); - spoold_provider.url_hash = local_url_hash; - spoold_provider.url_equal = local_url_equal; - camel_session_register_provider(session, &spoold_provider); } diff --git a/camel/providers/local/camel-local-summary.c b/camel/providers/local/camel-local-summary.c index eae265d5a6..e1007223a0 100644 --- a/camel/providers/local/camel-local-summary.c +++ b/camel/providers/local/camel-local-summary.c @@ -160,6 +160,11 @@ camel_local_summary_load(CamelLocalSummary *cls, int forceindex, CamelException return 0; } +void camel_local_summary_check_force(CamelLocalSummary *cls) +{ + cls->check_force = 1; +} + char * camel_local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *info) { @@ -300,15 +305,18 @@ camel_local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const Cam * @fd: * @header: * @xevline: + * @status: + * @xstatus: * * Write a bunch of headers to the file @fd. IF xevline is non NULL, then * an X-Evolution header line is created at the end of all of the headers. + * If @status is non NULL, then a Status header line is also written. * The headers written are termianted with a blank line. * * Return value: -1 on error, otherwise the number of bytes written. **/ int -camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevline) +camel_local_summary_write_headers(int fd, struct _header_raw *header, const char *xevline, const char *status, const char *xstatus) { int outlen = 0, len; int newfd; @@ -327,7 +335,9 @@ camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevl } while (header) { - if (strcmp(header->name, "X-Evolution")) { + if (strcmp(header->name, "X-Evolution") != 0 + && (status == NULL || strcmp(header->name, "Status") != 0) + && (xstatus == NULL || strcmp(header->name, "X-Status") != 0)) { len = fprintf(out, "%s:%s\n", header->name, header->value); if (len == -1) { fclose(out); @@ -338,8 +348,26 @@ camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevl header = header->next; } + if (status) { + len = fprintf(out, "Status: %s\n", status); + if (len == -1) { + fclose(out); + return -1; + } + outlen += len; + } + + if (xstatus) { + len = fprintf(out, "X-Status: %s\n", xstatus); + if (len == -1) { + fclose(out); + return -1; + } + outlen += len; + } + if (xevline) { - len = fprintf(out, "X-Evolution: %s\n\n", xevline); + len = fprintf(out, "X-Evolution: %s\n", xevline); if (len == -1) { fclose(out); return -1; @@ -347,6 +375,13 @@ camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevl outlen += len; } + len = fprintf(out, "\n"); + if (len == -1) { + fclose(out); + return -1; + } + outlen += len; + if (fclose(out) == -1) return -1; diff --git a/camel/providers/local/camel-local-summary.h b/camel/providers/local/camel-local-summary.h index d1153fa8c6..fafa992163 100644 --- a/camel/providers/local/camel-local-summary.h +++ b/camel/providers/local/camel-local-summary.h @@ -37,6 +37,7 @@ typedef struct _CamelLocalSummaryClass CamelLocalSummaryClass; enum { CAMEL_MESSAGE_FOLDER_NOXEV = 1<<17, CAMEL_MESSAGE_FOLDER_XEVCHANGE = 1<<18, + CAMEL_MESSAGE_FOLDER_NOTSEEN = 1<<19, /* have we seen this in processing this loop? */ }; struct _CamelLocalSummary { @@ -47,7 +48,8 @@ struct _CamelLocalSummary { char *folder_path; /* name of matching folder */ CamelIndex *index; - int index_force; /* do we force index during creation? */ + unsigned int index_force:1; /* do we force index during creation? */ + unsigned int check_force:1; /* does a check force a full check? */ }; struct _CamelLocalSummaryClass { @@ -74,12 +76,15 @@ int camel_local_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFold /* add a new message to the summary */ CamelMessageInfo *camel_local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex); +/* force the next check to be a full check/rebuild */ +void camel_local_summary_check_force(CamelLocalSummary *cls); + /* generate an X-Evolution header line */ char *camel_local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *info); int camel_local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *info); -/* utility functions - write headers to a file with optional X-Evolution header */ -int camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevline); +/* utility functions - write headers to a file with optional X-Evolution header and/or status header */ +int camel_local_summary_write_headers(int fd, struct _header_raw *header, const char *xevline, const char *status, const char *xstatus); #endif /* ! _CAMEL_LOCAL_SUMMARY_H */ diff --git a/camel/providers/local/camel-mbox-folder.c b/camel/providers/local/camel-mbox-folder.c index c8065b5f33..9eba280f24 100644 --- a/camel/providers/local/camel-mbox-folder.c +++ b/camel/providers/local/camel-mbox-folder.c @@ -55,6 +55,10 @@ static CamelLocalFolderClass *parent_class = NULL; static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); static void mbox_unlock(CamelLocalFolder *lf); +#ifdef STATUS_PINE +static void mbox_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set); +#endif + static void mbox_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value); static void mbox_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value); @@ -78,6 +82,9 @@ camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class) camel_folder_class->append_message = mbox_append_message; camel_folder_class->get_message = mbox_get_message; +#ifdef STATUS_PINE + camel_folder_class->set_message_flags = mbox_set_message_flags; +#endif camel_folder_class->set_message_user_flag = mbox_set_message_user_flag; camel_folder_class->set_message_user_tag = mbox_set_message_user_tag; @@ -313,17 +320,24 @@ static CamelMimeMessage * mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex) { CamelLocalFolder *lf = (CamelLocalFolder *)folder; - CamelMimeMessage *message; + CamelMimeMessage *message = NULL; CamelMboxMessageInfo *info; - CamelMimeParser *parser; + CamelMimeParser *parser = NULL; int fd, retval; int retried = FALSE; - + off_t frompos; + d(printf("Getting message %s\n", uid)); - /* lock the folder first, burn if we can't */ - if (camel_local_folder_lock(lf, CAMEL_LOCK_READ, ex) == -1) + /* lock the folder first, burn if we can't, need write lock for summary check */ + if (camel_local_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) return NULL; + + /* check for new messages always */ + if (camel_local_summary_check((CamelLocalSummary *)folder->summary, lf->changes, ex) == -1) { + camel_local_folder_unlock(lf); + return NULL; + } retry: /* get the message summary info */ @@ -332,12 +346,14 @@ retry: if (info == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), uid, _("No such message")); - camel_local_folder_unlock(lf); - return NULL; + goto fail; } /* no frompos, its an error in the library (and we can't do anything with it) */ g_assert(info->frompos != -1); + + frompos = info->frompos; + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); /* we use an fd instead of a normal stream here - the reason is subtle, camel_mime_part will cache the whole message in memory if the stream is non-seekable (which it is when built from a parser @@ -349,9 +365,7 @@ retry: camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, strerror(errno)); - camel_local_folder_unlock(lf); - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); - return NULL; + goto fail; } /* we use a parser to verify the message is correct, and in the correct position */ @@ -359,21 +373,22 @@ retry: camel_mime_parser_init_with_fd(parser, fd); camel_mime_parser_scan_from(parser, TRUE); - camel_mime_parser_seek(parser, info->frompos, SEEK_SET); + camel_mime_parser_seek(parser, frompos, SEEK_SET); if (camel_mime_parser_step(parser, NULL, NULL) != HSCAN_FROM - || camel_mime_parser_tell_start_from(parser) != info->frompos) { + || camel_mime_parser_tell_start_from(parser) != frompos) { g_warning("Summary doesn't match the folder contents! eek!\n" - " expecting offset %ld got %ld, state = %d", (long int)info->frompos, + " expecting offset %ld got %ld, state = %d", (long int)frompos, (long int)camel_mime_parser_tell_start_from(parser), camel_mime_parser_state(parser)); camel_object_unref((CamelObject *)parser); - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); + parser = NULL; if (!retried) { retried = TRUE; - retval = camel_local_summary_check ((CamelLocalSummary *)folder->summary, lf->changes, ex); + camel_local_summary_check_force((CamelLocalSummary *)folder->summary); + retval = camel_local_summary_check((CamelLocalSummary *)folder->summary, lf->changes, ex); if (retval != -1) goto retry; } @@ -381,30 +396,26 @@ retry: camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, _("The folder appears to be irrecoverably corrupted.")); - - camel_local_folder_unlock(lf); - return NULL; + goto fail; } - - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); message = camel_mime_message_new(); if (camel_mime_part_construct_from_parser((CamelMimePart *)message, parser) == -1) { camel_exception_setv(ex, errno==EINTR?CAMEL_EXCEPTION_USER_CANCEL:CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, _("Message construction failed: Corrupt mailbox?")); - camel_object_unref((CamelObject *)parser); camel_object_unref((CamelObject *)message); - camel_local_folder_unlock(lf); - return NULL; + message = NULL; + goto fail; } camel_medium_remove_header((CamelMedium *)message, "X-Evolution"); - +fail: /* and unlock now we're finished with it */ camel_local_folder_unlock(lf); - camel_object_unref((CamelObject *)parser); + if (parser) + camel_object_unref((CamelObject *)parser); /* use the opportunity to notify of changes (particularly if we had a rebuild) */ if (camel_folder_change_info_changed(lf->changes)) { @@ -415,6 +426,20 @@ retry: return message; } +#ifdef STATUS_PINE +static void +mbox_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) +{ + /* Basically, if anything could change the Status line, presume it does */ + if (flags & (CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_FLAGGED|CAMEL_MESSAGE_ANSWERED|CAMEL_MESSAGE_DELETED)) { + flags |= CAMEL_MESSAGE_FOLDER_XEVCHANGE|CAMEL_MESSAGE_FOLDER_FLAGGED; + set |= CAMEL_MESSAGE_FOLDER_XEVCHANGE|CAMEL_MESSAGE_FOLDER_FLAGGED; + } + + ((CamelFolderClass *)parent_class)->set_message_flags(folder, uid, flags, set); +} +#endif + static void mbox_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value) { @@ -423,7 +448,8 @@ mbox_set_message_user_flag(CamelFolder *folder, const char *uid, const char *nam g_return_if_fail(folder->summary != NULL); info = camel_folder_summary_uid(folder->summary, uid); - g_return_if_fail(info != NULL); + if (info == NULL) + return; if (camel_flag_set(&info->user_flags, name, value)) { info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; @@ -441,7 +467,8 @@ mbox_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name g_return_if_fail(folder->summary != NULL); info = camel_folder_summary_uid(folder->summary, uid); - g_return_if_fail(info != NULL); + if (info == NULL) + return; if (camel_tag_set(&info->user_tags, name, value)) { info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; diff --git a/camel/providers/local/camel-mbox-summary.c b/camel/providers/local/camel-mbox-summary.c index 2d06d29a38..3c5222406e 100644 --- a/camel/providers/local/camel-mbox-summary.c +++ b/camel/providers/local/camel-mbox-summary.c @@ -62,11 +62,26 @@ static int message_info_save (CamelFolderSummary *, FILE *, CamelMessageInfo static int mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex); static int mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); +#ifdef STATUS_PINE +static CamelMessageInfo *mbox_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex); +#endif + +static int mbox_summary_sync_quick(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); +static int mbox_summary_sync_full(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); static void camel_mbox_summary_class_init (CamelMboxSummaryClass *klass); static void camel_mbox_summary_init (CamelMboxSummary *obj); static void camel_mbox_summary_finalise (CamelObject *obj); +#ifdef STATUS_PINE +/* Which status flags are stored in each separate header */ +#define STATUS_XSTATUS (CAMEL_MESSAGE_FLAGGED|CAMEL_MESSAGE_ANSWERED|CAMEL_MESSAGE_DELETED) +#define STATUS_STATUS (CAMEL_MESSAGE_SEEN) + +static void encode_status(guint32 flags, char status[8]); +static guint32 decode_status(const char *status); +#endif + static CamelLocalSummaryClass *camel_mbox_summary_parent; CamelType @@ -106,6 +121,12 @@ camel_mbox_summary_class_init(CamelMboxSummaryClass *klass) lklass->check = mbox_summary_check; lklass->sync = mbox_summary_sync; +#ifdef STATUS_PINE + lklass->add = mbox_summary_add; +#endif + + klass->sync_quick = mbox_summary_sync_quick; + klass->sync_full = mbox_summary_sync_full; } static void @@ -146,6 +167,11 @@ camel_mbox_summary_new(const char *filename, const char *mbox_name, CamelIndex * return new; } +void camel_mbox_summary_xstatus(CamelMboxSummary *mbs, int state) +{ + mbs->xstatus = state; +} + static int summary_header_load(CamelFolderSummary *s, FILE *in) { @@ -172,10 +198,75 @@ static CamelMessageInfo * message_info_new(CamelFolderSummary *s, struct _header_raw *h) { CamelMessageInfo *mi; + CamelMboxSummary *mbs = (CamelMboxSummary *)s; mi = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_new(s, h); if (mi) { CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi; + const char *xev, *uid; + CamelMessageInfo *info = NULL; + int add = 0; /* bitmask of things to add, 1 assign uid, 2, just add as new, 4 = recent */ +#ifdef STATUS_PINE + const char *status = NULL, *xstatus = NULL; + guint32 flags = 0; + + if (mbs->xstatus) { + /* check for existance of status & x-status headers */ + status = header_raw_find(&h, "Status", NULL); + if (status) + flags = decode_status(status); + xstatus = header_raw_find(&h, "X-Status", NULL); + if (xstatus) + flags |= decode_status(xstatus); + } +#endif + /* if we have an xev header, use it, else assign a new one */ + xev = header_raw_find(&h, "X-Evolution", NULL); + if (xev != NULL + && camel_local_summary_decode_x_evolution((CamelLocalSummary *)s, xev, mi) == 0) { + uid = camel_message_info_uid(mi); + d(printf("found valid x-evolution: %s\n", uid)); + info = camel_folder_summary_uid(s, uid); + if (info) { + if ((info->flags & CAMEL_MESSAGE_FOLDER_NOTSEEN)) { + info->flags &= ~CAMEL_MESSAGE_FOLDER_NOTSEEN; + camel_folder_summary_info_free(s, mi); + mbi = (CamelMboxMessageInfo *)mi = info; + } else { + add = 7; + d(printf("seen '%s' before, adding anew\n", uid)); + camel_folder_summary_info_free(s, info); + } + } else { + add = 2; + d(printf("but isn't present in summary\n")); + } + } else { + d(printf("didn't find x-evolution\n")); + add = 7; + } + + if (add&1) { + mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV; + camel_message_info_set_uid(mi, camel_folder_summary_next_uid_string(s)); + } else { + camel_folder_summary_set_uid(s, strtoul(camel_message_info_uid(mi), NULL, 10)); + } +#ifdef STATUS_PINE + if (mbs->xstatus && add&2) { + /* use the status as the flags when we read it the first time */ + if (status) + mi->flags = (mi->flags & ~(STATUS_STATUS)) | (flags & STATUS_STATUS); + if (xstatus) + mi->flags = (mi->flags & ~(STATUS_XSTATUS)) | (flags & STATUS_XSTATUS); + } +#endif + if (mbs->changes) { + if (add&2) + camel_folder_change_info_add_uid(mbs->changes, camel_message_info_uid(mi)); + if ((add&4) && status == NULL) + camel_folder_change_info_recent_uid(mbs->changes, camel_message_info_uid(mi)); + } mbi->frompos = -1; } @@ -233,19 +324,23 @@ message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi) return 0; } +/* like summary_rebuild, but also do changeinfo stuff (if supplied) */ static int -summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) +summary_update(CamelLocalSummary *cls, off_t offset, CamelFolderChangeInfo *changeinfo, CamelException *ex) { - CamelLocalSummary *cls = (CamelLocalSummary *)mbs; - CamelFolderSummary *s = (CamelFolderSummary *)mbs; + int i, count; + CamelFolderSummary *s = (CamelFolderSummary *)cls; + CamelMboxSummary *mbs = (CamelMboxSummary *)cls; CamelMimeParser *mp; + CamelMessageInfo *mi; int fd; int ok = 0; struct stat st; off_t size = 0; - /* FIXME: If there is a failure, it shouldn't clear the summary and restart, - it should try and merge the summary info's. This is a bit tricky. */ + d(printf("Calling summary update, from pos %d\n", (int)offset)); + + cls->index_force = FALSE; camel_operation_start(NULL, _("Storing folder")); @@ -267,25 +362,31 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) camel_mime_parser_seek(mp, offset, SEEK_SET); if (offset > 0) { - if (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM) { - if (camel_mime_parser_tell_start_from(mp) != offset) { - g_warning("The next message didn't start where I expected, building summary from start"); - camel_mime_parser_drop_step(mp); - offset = 0; - camel_mime_parser_seek(mp, offset, SEEK_SET); - camel_folder_summary_clear(s); - } else { - camel_mime_parser_unstep(mp); - } + if (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM + && camel_mime_parser_tell_start_from(mp) == offset) { + camel_mime_parser_unstep(mp); } else { - d(printf("mime parser state ran out? state is %d\n", camel_mime_parser_state(mp))); - camel_object_unref(CAMEL_OBJECT(mp)); - /* end of file - no content? no error either */ - camel_operation_end(NULL); - return 0; + g_warning("The next message didn't start where I expected, building summary from start"); + camel_mime_parser_drop_step(mp); + offset = 0; + camel_mime_parser_seek(mp, offset, SEEK_SET); } } + /* we mark messages as to whether we've seen them or not. + If we're not starting from the start, we must be starting + from the old end, so everything must be treated as new */ + count = camel_folder_summary_count(s); + for (i=0;i<count;i++) { + mi = camel_folder_summary_index(s, i); + if (offset == 0) + mi->flags |= CAMEL_MESSAGE_FOLDER_NOTSEEN; + else + mi->flags &= ~CAMEL_MESSAGE_FOLDER_NOTSEEN; + camel_folder_summary_info_free(s, mi); + } + mbs->changes = changeinfo; + while (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM) { CamelMessageInfo *info; off_t pc = camel_mime_parser_tell_start_from (mp) + 1; @@ -304,6 +405,21 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) } camel_object_unref(CAMEL_OBJECT (mp)); + + count = camel_folder_summary_count(s); + for (i=0;i<count;i++) { + mi = camel_folder_summary_index(s, i); + /* must've dissapeared from the file? */ + if (mi->flags & CAMEL_MESSAGE_FOLDER_NOTSEEN) { + d(printf("uid '%s' vanished, removing", camel_message_info_uid(mi))); + if (changeinfo) + camel_folder_change_info_remove_uid(changeinfo, camel_message_info_uid(mi)); + camel_folder_summary_remove(s, mi); + count--; + } + camel_folder_summary_info_free(s, mi); + } + mbs->changes = NULL; /* update the file size/mtime in the summary */ if (ok != -1) { @@ -319,43 +435,6 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) return ok; } -/* like summary_rebuild, but also do changeinfo stuff (if supplied) */ -static int -summary_update(CamelLocalSummary *cls, off_t offset, CamelFolderChangeInfo *changeinfo, CamelException *ex) -{ - int ret, i, count; - CamelFolderSummary *s = (CamelFolderSummary *)cls; - CamelMboxSummary *mbs = (CamelMboxSummary *)cls; - - d(printf("Calling summary update, from pos %d\n", (int)offset)); - - if (changeinfo) { - /* we use the diff function of the change_info to build the update list. */ - for (i = 0; i < camel_folder_summary_count(s); i++) { - CamelMessageInfo *mi = camel_folder_summary_index(s, i); - - camel_folder_change_info_add_source(changeinfo, camel_message_info_uid(mi)); - camel_folder_summary_info_free(s, mi); - } - } - - /* do the actual work */ - cls->index_force = FALSE; - ret = summary_rebuild(mbs, offset, ex); - - if (changeinfo) { - count = camel_folder_summary_count(s); - for (i = 0; i < count; i++) { - CamelMessageInfo *mi = camel_folder_summary_index(s, i); - camel_folder_change_info_add_update(changeinfo, camel_message_info_uid(mi)); - camel_folder_summary_info_free(s, mi); - } - camel_folder_change_info_build_diff(changeinfo); - } - - return ret; -} - static int mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, CamelException *ex) { @@ -363,6 +442,7 @@ mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Camel CamelFolderSummary *s = (CamelFolderSummary *)cls; struct stat st; int ret = 0; + int i, count; d(printf("Checking summary\n")); @@ -373,15 +453,24 @@ mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Camel return -1; } + if (cls->check_force) + mbs->folder_size = 0; + cls->check_force = 0; + if (st.st_size == 0) { /* empty? No need to scan at all */ d(printf("Empty mbox, clearing summary\n")); camel_folder_summary_clear(s); + count= camel_folder_summary_count(s); + for (i=0;i<count;i++) { + CamelMessageInfo *info = camel_folder_summary_index(s, i); + + if (info) { + camel_folder_change_info_remove_uid(changes, camel_message_info_uid(info)); + camel_folder_summary_info_free(s, info); + } + } ret = 0; - } else if (s->messages->len == 0) { - /* if we are empty, then we rebuilt from scratch */ - d(printf("Empty summary, rebuilding from start\n")); - ret = summary_update(cls, 0, changes, ex); } else { /* is the summary uptodate? */ if (st.st_size != mbs->folder_size || st.st_mtime != s->time) { @@ -391,9 +480,10 @@ mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Camel ret = summary_update(cls, mbs->folder_size, changes, ex); } else { d(printf("folder shrank! rebuilding from start\n")); - camel_folder_summary_clear(s); ret = summary_update(cls, 0, changes, ex); } + } else { + d(printf("Folder unchanged, do nothing\n")); } } @@ -480,19 +570,12 @@ camel_mbox_summary_build_from(struct _header_raw *header) /* perform a full sync */ static int -mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) +mbox_summary_sync_full(CamelMboxSummary *mbs, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) { - CamelMboxSummary *mbs = (CamelMboxSummary *)cls; - CamelFolderSummary *s = (CamelFolderSummary *)mbs; - CamelMimeParser *mp = NULL; - int i, count; - CamelMboxMessageInfo *info = NULL; + CamelLocalSummary *cls = (CamelLocalSummary *)mbs; int fd = -1, fdout = -1; char *tmpname = NULL; - char *buffer, *xevnew = NULL; - int len; - const char *fromline; - int lastdel = FALSE; + guint32 flags = (expunge?1:0); d(printf("performing full summary/sync\n")); @@ -507,11 +590,6 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan return -1; } - mp = camel_mime_parser_new(); - camel_mime_parser_scan_from(mp, TRUE); - camel_mime_parser_scan_pre_from(mp, TRUE); - camel_mime_parser_init_with_fd(mp, fd); - tmpname = alloca(strlen (cls->folder_path) + 5); sprintf(tmpname, "%s.tmp", cls->folder_path); d(printf("Writing tmp file to %s\n", tmpname)); @@ -522,107 +600,8 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan goto error; } - count = camel_folder_summary_count(s); - for (i = 0; i < count; i++) { - int pc = (i + 1) * 100 / count; - - camel_operation_progress(NULL, pc); - - info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i); - - g_assert(info); - - d(printf("Looking at message %s\n", camel_message_info_uid(info))); - - /* only need to seek past deleted messages, otherwise we should be at the right spot/state already */ - if (lastdel) { - d(printf("seeking to %d\n", (int)info->frompos)); - camel_mime_parser_seek(mp, info->frompos, SEEK_SET); - } - - if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_FROM) { - g_warning("Expected a From line here, didn't get it"); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary and folder mismatch, even after a sync")); - goto error; - } - - if (camel_mime_parser_tell_start_from(mp) != info->frompos) { - g_warning("Didn't get the next message where I expected (%d) got %d instead", - (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp)); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary and folder mismatch, even after a sync")); - goto error; - } - - lastdel = FALSE; - if (expunge && info->info.flags & CAMEL_MESSAGE_DELETED) { - const char *uid = camel_message_info_uid(info); - - d(printf("Deleting %s\n", uid)); - - if (cls->index) - camel_index_delete_name(cls->index, uid); - - /* remove it from the change list */ - camel_folder_change_info_remove_uid(changeinfo, uid); - camel_folder_summary_remove(s, (CamelMessageInfo *)info); - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); - count--; - i--; - info = NULL; - lastdel = TRUE; - } else { - /* otherwise, the message is staying, copy its From_ line across */ - if (i>0) { - write(fdout, "\n", 1); - } - info->frompos = lseek(fdout, 0, SEEK_CUR); - fromline = camel_mime_parser_from_line(mp); - write(fdout, fromline, strlen(fromline)); - } - - if (info && info->info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) { - d(printf("Updating header for %s flags = %08x\n", camel_message_info_uid(info), info->info.flags)); - - if (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_FROM_END) { - g_warning("camel_mime_parser_step failed (2)"); - goto error; - } - - xevnew = camel_local_summary_encode_x_evolution(cls, (CamelMessageInfo *)info); - if (camel_local_summary_write_headers(fdout, camel_mime_parser_headers_raw(mp), xevnew) == -1) { - d(printf("Error writing to tmp mailbox\n")); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Error writing to temp mailbox: %s"), - strerror(errno)); - goto error; - } - info->info.flags &= 0xffff; - g_free(xevnew); - xevnew = NULL; - camel_mime_parser_drop_step(mp); - } - - camel_mime_parser_drop_step(mp); - if (info) { - d(printf("looking for message content to copy across from %d\n", (int)camel_mime_parser_tell(mp))); - while (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_PRE_FROM) { - /*d(printf("copying mbox contents to tmp: '%.*s'\n", len, buffer));*/ - if (write(fdout, buffer, len) != len) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Writing to tmp mailbox failed: %s: %s"), - cls->folder_path, strerror(errno)); - goto error; - } - } - d(printf("we are now at %d, from = %d\n", (int)camel_mime_parser_tell(mp), - (int)camel_mime_parser_tell_start_from(mp))); - camel_mime_parser_unstep(mp); - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); - info = NULL; - } - } + if (camel_mbox_summary_sync_mbox((CamelMboxSummary *)cls, flags, changeinfo, fd, fdout, ex) == -1) + goto error; d(printf("Closing folders\n")); @@ -655,7 +634,6 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan } tmpname = NULL; - camel_object_unref((CamelObject *)mp); camel_operation_end(NULL); return 0; @@ -666,14 +644,8 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan if (fdout != -1) close(fdout); - g_free(xevnew); - if (tmpname) unlink(tmpname); - if (mp) - camel_object_unref((CamelObject *)mp); - if (info) - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); camel_operation_end(NULL); @@ -682,9 +654,9 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan /* perform a quick sync - only system flags have changed */ static int -mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) +mbox_summary_sync_quick(CamelMboxSummary *mbs, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) { - CamelMboxSummary *mbs = (CamelMboxSummary *)cls; + CamelLocalSummary *cls = (CamelLocalSummary *)mbs; CamelFolderSummary *s = (CamelFolderSummary *)mbs; CamelMimeParser *mp = NULL; int i, count; @@ -856,7 +828,7 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf ret = -1; if (quick) { if (work) { - ret = mbox_summary_sync_quick(cls, expunge, changeinfo, ex); + ret = ((CamelMboxSummaryClass *)((CamelObject *)cls)->klass)->sync_quick(mbs, expunge, changeinfo, ex); if (ret == -1) { g_warning("failed a quick-sync, trying a full sync"); camel_exception_clear(ex); @@ -867,7 +839,7 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf } if (ret == -1) - ret = mbox_summary_sync_full(cls, expunge, changeinfo, ex); + ret = ((CamelMboxSummaryClass *)((CamelObject *)cls)->klass)->sync_full(mbs, expunge, changeinfo, ex); if (ret == -1) return -1; @@ -884,3 +856,228 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf return ((CamelLocalSummaryClass *)camel_mbox_summary_parent)->sync(cls, expunge, changeinfo, ex); } + +int +camel_mbox_summary_sync_mbox(CamelMboxSummary *cls, guint32 flags, CamelFolderChangeInfo *changeinfo, int fd, int fdout, CamelException *ex) +{ + CamelMboxSummary *mbs = (CamelMboxSummary *)cls; + CamelFolderSummary *s = (CamelFolderSummary *)mbs; + CamelMimeParser *mp = NULL; + int i, count; + CamelMboxMessageInfo *info = NULL; + char *buffer, *xevnew = NULL; + int len; + const char *fromline; + int lastdel = FALSE; +#ifdef STATUS_PINE + char statnew[8], xstatnew[8]; +#endif + + d(printf("performing full summary/sync\n")); + + /* need to dup this because the mime-parser owns the fd after we give it to it */ + fd = dup(fd); + if (fd == -1) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not store folder: %s"), strerror(errno)); + return -1; + } + + mp = camel_mime_parser_new(); + camel_mime_parser_scan_from(mp, TRUE); + camel_mime_parser_scan_pre_from(mp, TRUE); + camel_mime_parser_init_with_fd(mp, fd); + + count = camel_folder_summary_count(s); + for (i = 0; i < count; i++) { + int pc = (i + 1) * 100 / count; + + camel_operation_progress(NULL, pc); + + info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i); + + g_assert(info); + + d(printf("Looking at message %s\n", camel_message_info_uid(info))); + + /* only need to seek past deleted messages, otherwise we should be at the right spot/state already */ + if (lastdel) { + d(printf("seeking to %d\n", (int)info->frompos)); + camel_mime_parser_seek(mp, info->frompos, SEEK_SET); + } + + if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_FROM) { + g_warning("Expected a From line here, didn't get it"); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Summary and folder mismatch, even after a sync")); + goto error; + } + + if (camel_mime_parser_tell_start_from(mp) != info->frompos) { + g_warning("Didn't get the next message where I expected (%d) got %d instead", + (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp)); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Summary and folder mismatch, even after a sync")); + goto error; + } + + lastdel = FALSE; + if ((flags&1) && info->info.flags & CAMEL_MESSAGE_DELETED) { + const char *uid = camel_message_info_uid(info); + + d(printf("Deleting %s\n", uid)); + + if (((CamelLocalSummary *)cls)->index) + camel_index_delete_name(((CamelLocalSummary *)cls)->index, uid); + + /* remove it from the change list */ + camel_folder_change_info_remove_uid(changeinfo, uid); + camel_folder_summary_remove(s, (CamelMessageInfo *)info); + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); + count--; + i--; + info = NULL; + lastdel = TRUE; + } else { + /* otherwise, the message is staying, copy its From_ line across */ + if (i>0) + write(fdout, "\n", 1); + info->frompos = lseek(fdout, 0, SEEK_CUR); + fromline = camel_mime_parser_from_line(mp); + write(fdout, fromline, strlen(fromline)); + } + + if (info && info->info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) { + d(printf("Updating header for %s flags = %08x\n", camel_message_info_uid(info), info->info.flags)); + + if (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_FROM_END) { + g_warning("camel_mime_parser_step failed (2)"); + goto error; + } + + xevnew = camel_local_summary_encode_x_evolution((CamelLocalSummary *)cls, (CamelMessageInfo *)info); +#ifdef STATUS_PINE + if (mbs->xstatus) { + encode_status(((CamelMessageInfo *)info)->flags & STATUS_STATUS, statnew); + encode_status(((CamelMessageInfo *)info)->flags & STATUS_XSTATUS, xstatnew); + len = camel_local_summary_write_headers(fdout, camel_mime_parser_headers_raw(mp), xevnew, statnew, xstatnew); + } else { +#endif + len = camel_local_summary_write_headers(fdout, camel_mime_parser_headers_raw(mp), xevnew, NULL, NULL); +#ifdef STATUS_PINE + } +#endif + if (len == -1) { + d(printf("Error writing to tmp mailbox\n")); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Error writing to temp mailbox: %s"), + strerror(errno)); + goto error; + } + info->info.flags &= 0xffff; + g_free(xevnew); + xevnew = NULL; + camel_mime_parser_drop_step(mp); + } + + camel_mime_parser_drop_step(mp); + if (info) { + d(printf("looking for message content to copy across from %d\n", (int)camel_mime_parser_tell(mp))); + while (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_PRE_FROM) { + /*d(printf("copying mbox contents to tmp: '%.*s'\n", len, buffer));*/ + if (write(fdout, buffer, len) != len) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Writing to tmp mailbox failed: %s: %s"), + ((CamelLocalSummary *)cls)->folder_path, strerror(errno)); + goto error; + } + } + d(printf("we are now at %d, from = %d\n", (int)camel_mime_parser_tell(mp), + (int)camel_mime_parser_tell_start_from(mp))); + camel_mime_parser_unstep(mp); + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); + info = NULL; + } + } + + /* if last was deleted, append the \n we removed */ + if (lastdel && count > 0) + write(fdout, "\n", 1); + + camel_object_unref((CamelObject *)mp); + + return 0; + error: + g_free(xevnew); + + if (mp) + camel_object_unref((CamelObject *)mp); + if (info) + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); + + return -1; +} + +#ifdef STATUS_PINE +static CamelMessageInfo * +mbox_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex) +{ + CamelMessageInfo *mi; + + mi = ((CamelLocalSummaryClass *)camel_mbox_summary_parent)->add(cls, msg, info, ci, ex); + if (mi && ((CamelMboxSummary *)cls)->xstatus) { + char status[8]; + + /* we snoop and add status/x-status headers to suit */ + encode_status(mi->flags & STATUS_STATUS, status); + camel_medium_set_header((CamelMedium *)msg, "Status", status); + encode_status(mi->flags & STATUS_XSTATUS, status); + camel_medium_set_header((CamelMedium *)msg, "X-Status", status); + } + + return mi; +} + +static struct { + char tag; + guint32 flag; +} status_flags[] = { + { 'F', CAMEL_MESSAGE_FLAGGED }, + { 'A', CAMEL_MESSAGE_ANSWERED }, + { 'D', CAMEL_MESSAGE_DELETED }, + { 'R', CAMEL_MESSAGE_SEEN }, +}; + +static void +encode_status(guint32 flags, char status[8]) +{ + char *p; + int i; + + p = status; + for (i=0;i<sizeof(status_flags)/sizeof(status_flags[0]);i++) + if (status_flags[i].flag & flags) + *p++ = status_flags[i].tag; + *p++ = 'O'; + *p=0; +} + +static guint32 +decode_status(const char *status) +{ + const char *p; + char c; + guint32 flags = 0; + int i; + + p = status; + while ((c = *p++)) { + for (i=0;i<sizeof(status_flags)/sizeof(status_flags[0]);i++) + if (status_flags[i].tag == *p) + flags |= status_flags[i].flag; + } + + return flags; +} + +#endif /* STATUS_PINE */ diff --git a/camel/providers/local/camel-mbox-summary.h b/camel/providers/local/camel-mbox-summary.h index 6c61da21e5..13c5f1c027 100644 --- a/camel/providers/local/camel-mbox-summary.h +++ b/camel/providers/local/camel-mbox-summary.h @@ -23,6 +23,9 @@ #include "camel-local-summary.h" +/* Enable the use of elm/pine style "Status" & "X-Status" headers */ +#define STATUS_PINE + #define CAMEL_MBOX_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_mbox_summary_get_type (), CamelMboxSummary) #define CAMEL_MBOX_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_mbox_summary_get_type (), CamelMboxSummaryClass) #define CAMEL_IS_MBOX_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_mbox_summary_get_type ()) @@ -45,18 +48,33 @@ struct _CamelMboxSummary { struct _CamelMboxSummaryPrivate *priv; + CamelFolderChangeInfo *changes; /* used to build change sets */ + size_t folder_size; /* size of the mbox file, last sync */ + + unsigned int xstatus:1; /* do we store/honour xstatus/status headers */ }; struct _CamelMboxSummaryClass { CamelLocalSummaryClass parent_class; + + /* sync in-place */ + int (*sync_quick)(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); + /* sync requires copy */ + int (*sync_full)(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); }; CamelType camel_mbox_summary_get_type (void); CamelMboxSummary *camel_mbox_summary_new (const char *filename, const char *mbox_name, CamelIndex *index); +/* do we honour/use xstatus headers, etc */ +void camel_mbox_summary_xstatus(CamelMboxSummary *mbs, int state); + /* generate a From line from headers */ char *camel_mbox_summary_build_from(struct _header_raw *header); +/* build a new mbox from an existing mbox storing summary information */ +int camel_mbox_summary_sync_mbox(CamelMboxSummary *cls, guint32 flags, CamelFolderChangeInfo *changeinfo, int fd, int fdout, CamelException *ex); + #endif /* ! _CAMEL_MBOX_SUMMARY_H */ diff --git a/camel/providers/local/camel-mh-summary.c b/camel/providers/local/camel-mh-summary.c index 9eeeb25718..f809fa2601 100644 --- a/camel/providers/local/camel-mh-summary.c +++ b/camel/providers/local/camel-mh-summary.c @@ -329,7 +329,7 @@ mh_summary_sync_message(CamelLocalSummary *cls, CamelMessageInfo *info, CamelExc outfd = open(tmpname, O_CREAT|O_WRONLY|O_TRUNC, 0600); if (outfd != -1) { outlen = 0; - len = camel_local_summary_write_headers(outfd, camel_mime_parser_headers_raw(mp), xevnew); + len = camel_local_summary_write_headers(outfd, camel_mime_parser_headers_raw(mp), xevnew, NULL, NULL); if (len != -1) { while (outlen != -1 && (len = camel_mime_parser_read(mp, &buffer, 10240)) > 0) { d(printf("camel mime parser read, read %d bytes: %.*s\n", len, len, buffer)); diff --git a/camel/providers/local/camel-spool-folder.c b/camel/providers/local/camel-spool-folder.c index a519c58683..55df340c26 100644 --- a/camel/providers/local/camel-spool-folder.c +++ b/camel/providers/local/camel-spool-folder.c @@ -56,101 +56,38 @@ static CamelFolderClass *parent_class = NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CSPOOLS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so)) -static int spool_lock(CamelSpoolFolder *lf, CamelLockType type, CamelException *ex); -static void spool_unlock(CamelSpoolFolder *lf); +static CamelLocalSummary *spool_create_summary(const char *path, const char *folder, CamelIndex *index); -static void spool_sync(CamelFolder *folder, gboolean expunge, CamelException *ex); -static void spool_expunge(CamelFolder *folder, CamelException *ex); - -static GPtrArray *spool_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex); -static GPtrArray *spool_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex); -static void spool_search_free(CamelFolder *folder, GPtrArray * result); - -static void spool_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, char **appended_uid, CamelException *ex); -static CamelMimeMessage *spool_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex); -static void spool_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value); -static void spool_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value); +static int spool_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); +static void spool_unlock(CamelLocalFolder *lf); static void spool_finalize(CamelObject * object); static void -camel_spool_folder_class_init(CamelSpoolFolderClass * camel_spool_folder_class) +camel_spool_folder_class_init(CamelSpoolFolderClass *klass) { - CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_spool_folder_class); - - parent_class = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs(camel_folder_get_type())); - - /* virtual method definition */ - - /* virtual method overload */ - camel_folder_class->sync = spool_sync; - camel_folder_class->expunge = spool_expunge; + CamelLocalFolderClass *lklass = (CamelLocalFolderClass *)klass; - camel_folder_class->search_by_expression = spool_search_by_expression; - camel_folder_class->search_by_uids = spool_search_by_uids; - camel_folder_class->search_free = spool_search_free; + parent_class = (CamelFolderClass *)camel_mbox_folder_get_type(); /* virtual method overload */ - camel_folder_class->append_message = spool_append_message; - camel_folder_class->get_message = spool_get_message; - - camel_folder_class->set_message_user_flag = spool_set_message_user_flag; - camel_folder_class->set_message_user_tag = spool_set_message_user_tag; - - camel_spool_folder_class->lock = spool_lock; - camel_spool_folder_class->unlock = spool_unlock; + lklass->create_summary = spool_create_summary; + lklass->lock = spool_lock; + lklass->unlock = spool_unlock; } static void spool_init(gpointer object, gpointer klass) { - CamelFolder *folder = object; CamelSpoolFolder *spool_folder = object; - folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY | - CAMEL_FOLDER_HAS_SEARCH_CAPABILITY); - - folder->permanent_flags = CAMEL_MESSAGE_ANSWERED | - CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_DRAFT | - CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_USER; - - folder->summary = NULL; - spool_folder->search = NULL; - - spool_folder->priv = g_malloc0(sizeof(*spool_folder->priv)); -#ifdef ENABLE_THREADS - spool_folder->priv->search_lock = g_mutex_new(); -#endif + spool_folder->lockid = -1; } static void spool_finalize(CamelObject * object) { - CamelSpoolFolder *spool_folder = CAMEL_SPOOL_FOLDER(object); - CamelFolder *folder = (CamelFolder *)object; - - if (folder->summary) { - camel_spool_summary_sync((CamelSpoolSummary *)folder->summary, FALSE, spool_folder->changes, NULL); - camel_object_unref((CamelObject *)folder->summary); - folder->summary = NULL; - } - - if (spool_folder->search) { - camel_object_unref((CamelObject *)spool_folder->search); - } - - while (spool_folder->locked> 0) - camel_spool_folder_unlock(spool_folder); - - g_free(spool_folder->base_path); - g_free(spool_folder->folder_path); - - camel_folder_change_info_free(spool_folder->changes); - -#ifdef ENABLE_THREADS - g_mutex_free(spool_folder->priv->search_lock); -#endif - g_free(spool_folder->priv); + /*CamelSpoolFolder *spool_folder = CAMEL_SPOOL_FOLDER(object);*/ } CamelType camel_spool_folder_get_type(void) @@ -158,7 +95,7 @@ CamelType camel_spool_folder_get_type(void) static CamelType camel_spool_folder_type = CAMEL_INVALID_TYPE; if (camel_spool_folder_type == CAMEL_INVALID_TYPE) { - camel_spool_folder_type = camel_type_register(CAMEL_FOLDER_TYPE, "CamelSpoolFolder", + camel_spool_folder_type = camel_type_register(camel_mbox_folder_get_type(), "CamelSpoolFolder", sizeof(CamelSpoolFolder), sizeof(CamelSpoolFolderClass), (CamelObjectClassInitFunc) camel_spool_folder_class_init, @@ -170,59 +107,8 @@ CamelType camel_spool_folder_get_type(void) return camel_spool_folder_type; } -CamelSpoolFolder * -camel_spool_folder_construct(CamelSpoolFolder *lf, CamelStore *parent_store, const char *full_name, const char *path, guint32 flags, CamelException *ex) -{ - CamelFolderInfo *fi; - CamelFolder *folder; - const char *root_dir_path, *name; - - folder = (CamelFolder *)lf; - - name = strrchr(full_name, '/'); - if (name) - name++; - else - name = full_name; - - camel_folder_construct(folder, parent_store, full_name, name); - - root_dir_path = camel_spool_store_get_toplevel_dir(CAMEL_SPOOL_STORE(folder->parent_store)); - -#if 0 - lf->base_path = g_strdup(root_dir_path); - lf->folder_path = g_strdup(root_dir_path); -#else - lf->base_path = g_strdup(path); - lf->folder_path = g_strdup(path); -#endif - lf->changes = camel_folder_change_info_new(); - lf->flags = flags; - - folder->summary = (CamelFolderSummary *)camel_spool_summary_new(lf->folder_path); - if (camel_spool_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) { - camel_object_unref((CamelObject *)lf); - return NULL; - } - - camel_spool_summary_check((CamelSpoolSummary *)folder->summary, lf->changes, ex); - camel_spool_folder_unlock(lf); - - fi = g_malloc0(sizeof(*fi)); - fi->full_name = g_strdup(full_name); - fi->name = g_strdup(name); - fi->url = g_strdup_printf("spool:%s#%s", ((CamelService *)parent_store)->url->path, full_name); - fi->unread_message_count = camel_folder_get_unread_message_count(folder); - camel_folder_info_build_path(fi, '/'); - - camel_object_trigger_event(CAMEL_OBJECT(parent_store), "folder_created", fi); - camel_folder_info_free (fi); - - return lf; -} - CamelFolder * -camel_spool_folder_new(CamelStore *parent_store, const char *full_name, const char *path, guint32 flags, CamelException *ex) +camel_spool_folder_new(CamelStore *parent_store, const char *full_name, guint32 flags, CamelException *ex) { CamelFolder *folder; @@ -233,47 +119,31 @@ camel_spool_folder_new(CamelStore *parent_store, const char *full_name, const ch if (parent_store->flags & CAMEL_STORE_FILTER_INBOX && strcmp(full_name, "INBOX") == 0) folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT; - folder = (CamelFolder *)camel_spool_folder_construct((CamelSpoolFolder *)folder, parent_store, full_name, path, flags, ex); + flags &= CAMEL_STORE_FOLDER_BODY_INDEX; - return folder; -} + folder = (CamelFolder *)camel_local_folder_construct((CamelLocalFolder *)folder, parent_store, full_name, flags, ex); -/* lock the folder, may be called repeatedly (with matching unlock calls), - with type the same or less than the first call */ -int camel_spool_folder_lock(CamelSpoolFolder *lf, CamelLockType type, CamelException *ex) -{ - if (lf->locked > 0) { - /* lets be anal here - its important the code knows what its doing */ - g_assert(lf->locktype == type || lf->locktype == CAMEL_LOCK_WRITE); - } else { - if (CSPOOLF_CLASS(lf)->lock(lf, type, ex) == -1) - return -1; - lf->locktype = type; - } - - lf->locked++; + if (camel_url_get_param(((CamelService *)parent_store)->url, "xstatus")) + camel_mbox_summary_xstatus((CamelMboxSummary *)folder->summary, TRUE); - return 0; + return folder; } -/* unlock folder */ -int camel_spool_folder_unlock(CamelSpoolFolder *lf) +static CamelLocalSummary * +spool_create_summary(const char *path, const char *folder, CamelIndex *index) { - g_assert(lf->locked>0); - lf->locked--; - if (lf->locked == 0) - CSPOOLF_CLASS(lf)->unlock(lf); - - return 0; + return (CamelLocalSummary *)camel_spool_summary_new(folder); } static int -spool_lock(CamelSpoolFolder *lf, CamelLockType type, CamelException *ex) +spool_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex) { int retry = 0; + CamelMboxFolder *mf = (CamelMboxFolder *)lf; + CamelSpoolFolder *sf = (CamelSpoolFolder *)lf; - lf->lockfd = open(lf->folder_path, O_RDWR, 0); - if (lf->lockfd == -1) { + mf->lockfd = open(lf->folder_path, O_RDWR, 0); + if (mf->lockfd == -1) { camel_exception_setv(ex, 1, _("Cannot create folder lock on %s: %s"), lf->folder_path, strerror(errno)); return -1; } @@ -284,13 +154,13 @@ spool_lock(CamelSpoolFolder *lf, CamelLockType type, CamelException *ex) camel_exception_clear(ex); - if (camel_lock_fcntl(lf->lockfd, type, ex) == 0) { - if (camel_lock_flock(lf->lockfd, type, ex) == 0) { - if ((lf->lockid = camel_lock_helper_lock(lf->folder_path, ex)) != -1) + if (camel_lock_fcntl(mf->lockfd, type, ex) == 0) { + if (camel_lock_flock(mf->lockfd, type, ex) == 0) { + if ((sf->lockid = camel_lock_helper_lock(lf->folder_path, ex)) != -1) return 0; - camel_unlock_flock(lf->lockfd); + camel_unlock_flock(mf->lockfd); } - camel_unlock_fcntl(lf->lockfd); + camel_unlock_fcntl(mf->lockfd); } retry++; } @@ -299,403 +169,16 @@ spool_lock(CamelSpoolFolder *lf, CamelLockType type, CamelException *ex) } static void -spool_unlock(CamelSpoolFolder *lf) -{ - camel_lock_helper_unlock(lf->lockid); - lf->lockid = -1; - camel_unlock_flock(lf->lockid); - camel_unlock_fcntl(lf->lockid); - - close(lf->lockfd); - lf->lockfd = -1; -} - -static void -spool_sync(CamelFolder *folder, gboolean expunge, CamelException *ex) -{ - CamelSpoolFolder *lf = CAMEL_SPOOL_FOLDER(folder); - - d(printf("spool sync, expunge=%s\n", expunge?"true":"false")); - - if (camel_spool_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) - return; - - /* if sync fails, we'll pass it up on exit through ex */ - camel_spool_summary_sync((CamelSpoolSummary *)folder->summary, expunge, lf->changes, ex); - camel_spool_folder_unlock(lf); - - if (camel_folder_change_info_changed(lf->changes)) { - camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", lf->changes); - camel_folder_change_info_clear(lf->changes); - } -} - -static void -spool_expunge(CamelFolder *folder, CamelException *ex) -{ - d(printf("expunge\n")); - - /* Just do a sync with expunge, serves the same purpose */ - /* call the callback directly, to avoid locking problems */ - CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(folder))->sync(folder, TRUE, ex); -} - -static GPtrArray * -spool_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex) -{ - CamelSpoolFolder *spool_folder = CAMEL_SPOOL_FOLDER(folder); - GPtrArray *summary, *matches; - - /* NOTE: could get away without the search lock by creating a new - search object each time */ - - CAMEL_SPOOL_FOLDER_LOCK(folder, search_lock); - - if (spool_folder->search == NULL) - spool_folder->search = camel_folder_search_new(); - - camel_folder_search_set_folder(spool_folder->search, folder); - summary = camel_folder_get_summary(folder); - camel_folder_search_set_summary(spool_folder->search, summary); - - matches = camel_folder_search_execute_expression(spool_folder->search, expression, ex); - - CAMEL_SPOOL_FOLDER_UNLOCK(folder, search_lock); - - camel_folder_free_summary(folder, summary); - - return matches; -} - -static GPtrArray * -spool_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) -{ - CamelSpoolFolder *spool_folder = CAMEL_SPOOL_FOLDER(folder); - GPtrArray *summary, *matches; - int i; - - /* NOTE: could get away without the search lock by creating a new - search object each time */ - - summary = g_ptr_array_new(); - for (i=0;i<uids->len;i++) { - CamelMessageInfo *info; - - info = camel_folder_get_message_info(folder, uids->pdata[i]); - if (info) - g_ptr_array_add(summary, info); - } - - if (summary->len == 0) - return summary; - - CAMEL_SPOOL_FOLDER_LOCK(folder, search_lock); - - if (spool_folder->search == NULL) - spool_folder->search = camel_folder_search_new(); - - camel_folder_search_set_folder(spool_folder->search, folder); - camel_folder_search_set_summary(spool_folder->search, summary); - - matches = camel_folder_search_execute_expression(spool_folder->search, expression, ex); - - CAMEL_SPOOL_FOLDER_UNLOCK(folder, search_lock); - - for (i=0;i<summary->len;i++) - camel_folder_free_message_info(folder, summary->pdata[i]); - g_ptr_array_free(summary, TRUE); - - return matches; -} - -static void -spool_search_free(CamelFolder *folder, GPtrArray * result) -{ - CamelSpoolFolder *spool_folder = CAMEL_SPOOL_FOLDER(folder); - - /* we need to lock this free because of the way search_free_result works */ - /* FIXME: put the lock inside search_free_result */ - CAMEL_SPOOL_FOLDER_LOCK(folder, search_lock); - - camel_folder_search_free_result(spool_folder->search, result); - - CAMEL_SPOOL_FOLDER_UNLOCK(folder, search_lock); -} - -static void -spool_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, char **appended_uid, CamelException *ex) +spool_unlock(CamelLocalFolder *lf) { - CamelSpoolFolder *lf = (CamelSpoolFolder *)folder; - CamelStream *output_stream = NULL, *filter_stream = NULL; - CamelMimeFilter *filter_from = NULL; - CamelSpoolSummary *mbs = (CamelSpoolSummary *)folder->summary; - CamelMessageInfo *mi; - char *fromline = NULL; - int fd; - struct stat st; -#if 0 - char *xev; -#endif - /* If we can't lock, dont do anything */ - if (camel_spool_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) - return; - - d(printf("Appending message\n")); + CamelMboxFolder *mf = (CamelMboxFolder *)lf; + CamelSpoolFolder *sf = (CamelSpoolFolder *)lf; - /* first, check the summary is correct (updates folder_size too) */ - camel_spool_summary_check((CamelSpoolSummary *)folder->summary, lf->changes, ex); - if (camel_exception_is_set(ex)) - goto fail; + camel_lock_helper_unlock(sf->lockid); + sf->lockid = -1; + camel_unlock_flock(mf->lockfd); + camel_unlock_fcntl(mf->lockfd); - /* add it to the summary/assign the uid, etc */ - mi = camel_spool_summary_add((CamelSpoolSummary *)folder->summary, message, info, lf->changes, ex); - if (camel_exception_is_set(ex)) - goto fail; - - d(printf("Appending message: uid is %s\n", camel_message_info_uid(mi))); - - output_stream = camel_stream_fs_new_with_name(lf->folder_path, O_WRONLY|O_APPEND, 0600); - if (output_stream == NULL) { - camel_exception_setv(ex, 1, _("Cannot open mailbox: %s: %s\n"), lf->folder_path, strerror(errno)); - goto fail; - } - - /* and we need to set the frompos/XEV explicitly */ - ((CamelSpoolMessageInfo *)mi)->frompos = mbs->folder_size?mbs->folder_size+1:0; -#if 0 - xev = camel_spool_summary_encode_x_evolution((CamelLocalSummary *)folder->summary, mi); - if (xev) { - /* the x-ev header should match the 'current' flags, no problem, so store as much */ - camel_medium_set_header((CamelMedium *)message, "X-Evolution", xev); - mi->flags &= ~ CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_FLAGGED; - g_free(xev); - } -#endif - - /* we must write this to the non-filtered stream ... prepend a \n if not at the start of the file */ - fromline = camel_spool_summary_build_from(((CamelMimePart *)message)->headers); - if (camel_stream_printf(output_stream, mbs->folder_size==0?"%s":"\n%s", fromline) == -1) - goto fail_write; - - /* and write the content to the filtering stream, that translated '\nFrom' into '\n>From' */ - filter_stream = (CamelStream *) camel_stream_filter_new_with_stream(output_stream); - filter_from = (CamelMimeFilter *) camel_mime_filter_from_new(); - camel_stream_filter_add((CamelStreamFilter *) filter_stream, filter_from); - if (camel_data_wrapper_write_to_stream((CamelDataWrapper *)message, filter_stream) == -1) - goto fail_write; - - if (camel_stream_close(filter_stream) == -1) - goto fail_write; - - /* unlock as soon as we can */ - camel_spool_folder_unlock(lf); - - /* filter stream ref's the output stream itself, so we need to unref it too */ - camel_object_unref((CamelObject *)filter_from); - camel_object_unref((CamelObject *)filter_stream); - camel_object_unref((CamelObject *)output_stream); - g_free(fromline); - - /* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */ - /* the stat really shouldn't fail, we just wrote to it */ - if (stat(lf->folder_path, &st) == 0) { - mbs->folder_size = st.st_size; - ((CamelFolderSummary *)mbs)->time = st.st_mtime; - } - - if (camel_folder_change_info_changed(lf->changes)) { - camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes); - camel_folder_change_info_clear(lf->changes); - } - - if (appended_uid) - *appended_uid = g_strdup(camel_message_info_uid(mi)); - - return; - -fail_write: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot append message to spool file: %s: %s"), - lf->folder_path, g_strerror (errno)); - - if (filter_stream) - camel_object_unref(CAMEL_OBJECT(filter_stream)); - - if (output_stream) - camel_object_unref(CAMEL_OBJECT(output_stream)); - - if (filter_from) - camel_object_unref(CAMEL_OBJECT(filter_from)); - - g_free(fromline); - - /* reset the file to original size */ - fd = open(lf->folder_path, O_WRONLY, 0600); - if (fd != -1) { - ftruncate(fd, mbs->folder_size); - close(fd); - } - - /* remove the summary info so we are not out-of-sync with the spool */ - camel_folder_summary_remove_uid (CAMEL_FOLDER_SUMMARY (mbs), camel_message_info_uid (mi)); - - /* and tell the summary its uptodate */ - if (stat(lf->folder_path, &st) == 0) { - mbs->folder_size = st.st_size; - ((CamelFolderSummary *)mbs)->time = st.st_mtime; - } - -fail: - /* make sure we unlock the folder - before we start triggering events into appland */ - camel_spool_folder_unlock(lf); - - /* cascade the changes through, anyway, if there are any outstanding */ - if (camel_folder_change_info_changed(lf->changes)) { - camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes); - camel_folder_change_info_clear(lf->changes); - } -} - -static CamelMimeMessage * -spool_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex) -{ - CamelSpoolFolder *lf = (CamelSpoolFolder *)folder; - CamelMimeMessage *message; - CamelSpoolMessageInfo *info; - CamelMimeParser *parser; - int fd; - int retried = FALSE; - - d(printf("Getting message %s\n", uid)); - - /* lock the folder first, burn if we can't, need write lock for summary check */ - if (camel_spool_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) - return NULL; - - /* check for new messages, this may renumber uid's though */ - if (camel_spool_summary_check((CamelSpoolSummary *)folder->summary, lf->changes, ex) == -1) - return NULL; - -retry: - /* get the message summary info */ - info = (CamelSpoolMessageInfo *) camel_folder_summary_uid(folder->summary, uid); - - if (info == NULL) { - camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, - _("Cannot get message: %s\n %s"), uid, _("No such message")); - camel_spool_folder_unlock(lf); - return NULL; - } - - /* no frompos, its an error in the library (and we can't do anything with it */ - g_assert(info->frompos != -1); - - /* we use an fd instead of a normal stream here - the reason is subtle, camel_mime_part will cache - the whole message in memory if the stream is non-seekable (which it is when built from a parser - with no stream). This means we dont have to lock the spool for the life of the message, but only - while it is being created. */ - - fd = open(lf->folder_path, O_RDONLY); - if (fd == -1) { - camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, - _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, - strerror(errno)); - camel_spool_folder_unlock(lf); - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); - return NULL; - } - - /* we use a parser to verify the message is correct, and in the correct position */ - parser = camel_mime_parser_new(); - camel_mime_parser_init_with_fd(parser, fd); - camel_mime_parser_scan_from(parser, TRUE); - - camel_mime_parser_seek(parser, info->frompos, SEEK_SET); - if (camel_mime_parser_step(parser, NULL, NULL) != HSCAN_FROM - || camel_mime_parser_tell_start_from(parser) != info->frompos) { - - g_warning("Summary doesn't match the folder contents! eek!\n" - " expecting offset %ld got %ld, state = %d", (long int)info->frompos, - (long int)camel_mime_parser_tell_start_from(parser), - camel_mime_parser_state(parser)); - - camel_object_unref((CamelObject *)parser); - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); - - if (!retried) { - retried = TRUE; - camel_spool_summary_check((CamelSpoolSummary *)folder->summary, lf->changes, ex); - if (!camel_exception_is_set(ex)) - goto retry; - } - - camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, - _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, - _("The folder appears to be irrecoverably corrupted.")); - - camel_spool_folder_unlock(lf); - return NULL; - } - - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); - - message = camel_mime_message_new(); - if (camel_mime_part_construct_from_parser((CamelMimePart *)message, parser) == -1) { - camel_exception_setv(ex, (errno==EINTR)?CAMEL_EXCEPTION_USER_CANCEL:CAMEL_EXCEPTION_FOLDER_INVALID_UID, - _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, - _("Message construction failed: Corrupt mailbox?")); - camel_object_unref((CamelObject *)parser); - camel_object_unref((CamelObject *)message); - camel_spool_folder_unlock(lf); - return NULL; - } - - /* and unlock now we're finished with it */ - camel_spool_folder_unlock(lf); - - camel_object_unref((CamelObject *)parser); - - /* use the opportunity to notify of changes (particularly if we had a rebuild) */ - if (camel_folder_change_info_changed(lf->changes)) { - camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes); - camel_folder_change_info_clear(lf->changes); - } - - return message; -} - -static void -spool_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value) -{ - CamelMessageInfo *info; - - g_return_if_fail(folder->summary != NULL); - - info = camel_folder_summary_uid(folder->summary, uid); - g_return_if_fail(info != NULL); - - if (camel_flag_set(&info->user_flags, name, value)) { - info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; - camel_folder_summary_touch(folder->summary); - camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); - } - camel_folder_summary_info_free(folder->summary, info); -} - -static void -spool_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value) -{ - CamelMessageInfo *info; - - g_return_if_fail(folder->summary != NULL); - - info = camel_folder_summary_uid(folder->summary, uid); - g_return_if_fail(info != NULL); - - if (camel_tag_set(&info->user_tags, name, value)) { - info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; - camel_folder_summary_touch(folder->summary); - camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); - } - camel_folder_summary_info_free(folder->summary, info); + close(mf->lockfd); + mf->lockfd = -1; } diff --git a/camel/providers/local/camel-spool-folder.h b/camel/providers/local/camel-spool-folder.h index af43e0dca1..e778cdecf7 100644 --- a/camel/providers/local/camel-spool-folder.h +++ b/camel/providers/local/camel-spool-folder.h @@ -27,7 +27,7 @@ extern "C" { #pragma } #endif /* __cplusplus }*/ -#include <camel/camel-folder.h> +#include "camel-mbox-folder.h" #include <camel/camel-folder-search.h> #include <camel/camel-index.h> #include "camel-spool-summary.h" @@ -41,59 +41,21 @@ extern "C" { #define CAMEL_IS_SPOOL_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_SPOOL_FOLDER_TYPE)) typedef struct { - CamelFolder parent_object; - struct _CamelSpoolFolderPrivate *priv; + CamelMboxFolder parent; - guint32 flags; /* open mode flags */ + struct _CamelSpoolFolderPrivate *priv; - int locked; /* lock counter */ - CamelLockType locktype; /* what type of lock we have */ - int lockfd; /* lock fd used for fcntl/etc locking */ int lockid; /* lock id for dot locking */ - - char *base_path; /* base path of the spool folder */ - char *folder_path; /* the path to the folder itself */ -#if 0 - char *summary_path; /* where the summary lives */ - char *index_path; /* where the index file lives */ - - ibex *index; /* index for this folder */ -#endif - CamelFolderSearch *search; /* used to run searches, we just use the real thing (tm) */ - CamelFolderChangeInfo *changes; /* used to store changes to the folder during processing */ } CamelSpoolFolder; typedef struct { - CamelFolderClass parent_class; - - /* Virtual methods */ - - /* summary factory, only used at init */ - CamelSpoolSummary *(*create_summary)(const char *path, const char *folder, CamelIndex *index); - - /* Lock the folder for my operations */ - int (*lock)(CamelSpoolFolder *, CamelLockType type, CamelException *ex); - - /* Unlock the folder for my operations */ - void (*unlock)(CamelSpoolFolder *); + CamelMboxFolderClass parent_class; } CamelSpoolFolderClass; - -/* public methods */ -/* flags are taken from CAMEL_STORE_FOLDER_* flags */ -CamelSpoolFolder *camel_spool_folder_construct(CamelSpoolFolder *lf, CamelStore *parent_store, - const char *full_name, const char *path, guint32 flags, CamelException *ex); - /* Standard Camel function */ CamelType camel_spool_folder_get_type(void); -CamelFolder *camel_spool_folder_new(CamelStore *parent_store, const char *full_name, const char *path, - guint32 flags, CamelException *ex); - -/* Lock the folder for internal use. May be called repeatedly */ -/* UNIMPLEMENTED */ -int camel_spool_folder_lock(CamelSpoolFolder *lf, CamelLockType type, CamelException *ex); -int camel_spool_folder_unlock(CamelSpoolFolder *lf); +CamelFolder *camel_spool_folder_new(CamelStore *parent_store, const char *full_name, guint32 flags, CamelException *ex); #ifdef __cplusplus } diff --git a/camel/providers/local/camel-spool-store.c b/camel/providers/local/camel-spool-store.c index b0a1bda602..6c26f780a0 100644 --- a/camel/providers/local/camel-spool-store.c +++ b/camel/providers/local/camel-spool-store.c @@ -28,6 +28,7 @@ #include <string.h> #include <unistd.h> #include <stdio.h> +#include <dirent.h> #include "camel-spool-store.h" #include "camel-spool-folder.h" @@ -60,7 +61,7 @@ camel_spool_store_class_init (CamelSpoolStoreClass *camel_spool_store_class) CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_spool_store_class); CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS (camel_spool_store_class); - parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ())); + parent_class = CAMEL_STORE_CLASS(camel_mbox_store_get_type()); /* virtual method overload */ camel_service_class->construct = construct; @@ -80,7 +81,7 @@ camel_spool_store_get_type (void) static CamelType camel_spool_store_type = CAMEL_INVALID_TYPE; if (camel_spool_store_type == CAMEL_INVALID_TYPE) { - camel_spool_store_type = camel_type_register (CAMEL_STORE_TYPE, "CamelSpoolStore", + camel_spool_store_type = camel_type_register (camel_mbox_store_get_type(), "CamelSpoolStore", sizeof (CamelSpoolStore), sizeof (CamelSpoolStoreClass), (CamelObjectClassInitFunc) camel_spool_store_class_init, @@ -110,40 +111,43 @@ construct (CamelService *service, CamelSession *session, CamelProvider *provider return; } - if (stat(service->url->path, &st) == -1 || !S_ISREG(st.st_mode)) { + if (stat(service->url->path, &st) == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, - _("Spool `%s' does not exist or is not a regular file"), - service->url->path); + _("Spool `%s' cannot be opened: %s"), + service->url->path, strerror(errno)); return; } -} - -const char * -camel_spool_store_get_toplevel_dir (CamelSpoolStore *store) -{ - CamelURL *url = CAMEL_SERVICE (store)->url; - g_assert (url != NULL); - return url->path; + if (S_ISREG(st.st_mode)) + ((CamelSpoolStore *)service)->type = CAMEL_SPOOL_STORE_MBOX; + else if (S_ISDIR(st.st_mode)) + /* we could check here for slight variations */ + ((CamelSpoolStore *)service)->type = CAMEL_SPOOL_STORE_ELM; + else { + camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, + _("Spool `%s' is not a regular file or directory"), + service->url->path); + return; + } } static CamelFolder * get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex) { - char *path = ((CamelService *)store)->url->path; CamelFolder *folder; d(printf("opening folder %s on path %s\n", folder_name, path)); - /* we only support an 'INBOX' */ - if (strcmp(folder_name, "INBOX") != 0) { + /* we only support an 'INBOX' in mbox mode */ + if (((CamelSpoolStore *)store)->type == CAMEL_SPOOL_STORE_MBOX + && strcmp(folder_name, "INBOX") != 0) { camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, _("Folder `%s/%s' does not exist."), - path, folder_name); + ((CamelService *)store)->url->path, folder_name); return NULL; } - folder = camel_spool_folder_new(store, folder_name, path, flags, ex); + folder = camel_spool_folder_new(store, folder_name, flags, ex); return folder; } @@ -151,21 +155,236 @@ get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelExce static CamelFolder * get_inbox(CamelStore *store, CamelException *ex) { - return get_folder (store, "INBOX", CAMEL_STORE_FOLDER_CREATE, ex); + if (((CamelSpoolStore *)store)->type == CAMEL_SPOOL_STORE_MBOX) + return get_folder (store, "INBOX", CAMEL_STORE_FOLDER_CREATE, ex); + else { + camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, + _("Store does not support an INBOX")); + return NULL; + } } static char * get_name (CamelService *service, gboolean brief) { if (brief) - return g_strdup (service->url->path); + return g_strdup(service->url->path); else - return g_strdup_printf (_("Spool mail file %s"), service->url->path); + return g_strdup_printf(((CamelSpoolStore *)service)->type == CAMEL_SPOOL_STORE_MBOX? + _("Spool mail file %s"):_("Spool folder tree %s"), service->url->path); +} + +/* default implementation, rename all */ +static void +rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex) +{ + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Spool folders cannot be renamed")); +} + +/* default implementation, only delete metadata */ +static void +delete_folder(CamelStore *store, const char *folder_name, CamelException *ex) +{ + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Spool folders cannot be deleted")); +} + +static void free_folder_info (CamelStore *store, CamelFolderInfo *fi) +{ + if (fi) { + g_free(fi->url); + g_free(fi->name); + g_free(fi->full_name); + g_free(fi->path); + g_free(fi); + } +} + +static CamelFolderInfo * +camel_folder_info_new(const char *url, const char *full, const char *name, int unread) +{ + CamelFolderInfo *fi; + + fi = g_malloc0(sizeof(*fi)); + fi->url = g_strdup(url); + fi->full_name = g_strdup(full); + fi->name = g_strdup(name); + fi->unread_message_count = unread; + camel_folder_info_build_path(fi, '/'); + + d(printf("Adding spoold info: '%s' '%s' '%s' '%s'\n", fi->path, fi->name, fi->full_name, fi->url)); + + return fi; +} + +/* used to find out where we've visited already */ +struct _inode { + dev_t dnode; + ino_t inode; +}; + +/* returns number of records found at or below this level */ +static int scan_dir(CamelStore *store, GHashTable *visited, char *root, const char *path, guint32 flags, CamelFolderInfo *parent, CamelFolderInfo **fip, CamelException *ex) +{ + DIR *dir; + struct dirent *d; + char *name, *uri, *tmp, *fname; + CamelFolderInfo *fi = NULL; + struct stat st; + CamelFolder *folder; + int unread; + char from[80]; + FILE *fp; + + d(printf("checking dir '%s' part '%s' for mbox content\n", root, path)); + + /* look for folders matching the right structure, recursively */ + if (path) { + name = alloca(strlen(root)+strlen(path)+2); + sprintf(name, "%s/%s", root, path); + } else + name = root; + + dir = opendir(name); + if (dir == NULL) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not scan folder `%s': %s"), + root, strerror(errno)); + g_free(name); + return -1; + } + + if (path != NULL) { + uri = g_strdup_printf("%s:%s;noselect=yes#%s", ((CamelService *)store)->url->protocol, root, path); + tmp = strrchr(path, '/'); + if (tmp == NULL) + tmp = (char *)path; + else + tmp++; + fi = camel_folder_info_new(uri, path, tmp, -1); + fi->parent = parent; + fi->sibling = *fip; + *fip = fi; + g_free(uri); + + fip = &fi->child; + parent = fi; + } + + while ( (d = readdir(dir)) ) { + if (strcmp(d->d_name, ".") == 0 + || strcmp(d->d_name, "..") == 0) + continue; + + tmp = g_strdup_printf("%s/%s", name, d->d_name); + if (stat(tmp, &st) == 0) { + if (path) + fname = g_strdup_printf("%s/%s", path, d->d_name); + else + fname = g_strdup(d->d_name); + + if (S_ISREG(st.st_mode)) { + /* first, see if we already have it open */ + CAMEL_STORE_LOCK(store, cache_lock); + folder = g_hash_table_lookup(store->folders, fname); + if (folder) + unread = camel_folder_get_unread_message_count(folder); + else + unread = -1; + CAMEL_STORE_UNLOCK(store, cache_lock); + + /* no? check its content to see if its a folder or not */ + if (folder == NULL) { + fp = fopen(tmp, "r"); + if (fp != NULL) { + if (fgets(from, sizeof(from), fp) != NULL + && strncmp(from, "From ", 5) == 0) { + folder = (CamelFolder *)1; + /* TODO: if slow mode selected, we could look up unread counts here - + but its pretty expensive */ + } + fclose(fp); + } + } + + if (folder != NULL) { + uri = g_strdup_printf("%s:%s#%s", ((CamelService *)store)->url->protocol, root, fname); + fi = camel_folder_info_new(uri, fname, d->d_name, unread); + fi->parent = parent; + fi->sibling = *fip; + *fip = fi; + g_free(uri); + } + + } else if (S_ISDIR(st.st_mode)) { + struct _inode in = { st.st_dev, st.st_ino }; + + /* see if we've visited already */ + if (g_hash_table_lookup(visited, &in) == NULL) { + struct _inode *inew = g_malloc(sizeof(*inew)); + + *inew = in; + g_hash_table_insert(visited, inew, inew); + + if (scan_dir(store, visited, root, fname, flags, parent, fip, ex) == -1) { + g_free(tmp); + g_free(fname); + closedir(dir); + return -1; + } + } + } + g_free(fname); + + } + g_free(tmp); + } + closedir(dir); + + return 0; +} + +static guint inode_hash(const void *d) +{ + const struct _inode *v = d; + + return v->inode ^ v->dnode; +} + +static gboolean inode_equal(const void *a, const void *b) +{ + const struct _inode *v1 = a, *v2 = b; + + return v1->inode == v2->inode && v1->dnode == v2->dnode; +} + +static void inode_free(void *k, void *v, void *d) +{ + g_free(k); +} + +static CamelFolderInfo * +get_folder_info_elm(CamelStore *store, const char *top, guint32 flags, CamelException *ex) +{ + CamelFolderInfo *fi = NULL; + GHashTable *visited; + + visited = g_hash_table_new(inode_hash, inode_equal); + + if (scan_dir(store, visited, ((CamelService *)store)->url->path, top, flags, NULL, &fi, ex) == -1 && fi != NULL) { + camel_store_free_folder_info_full(store, fi); + fi = NULL; + } + + g_hash_table_foreach(visited, inode_free, NULL); + g_hash_table_destroy(visited); + + return fi; } static CamelFolderInfo * -get_folder_info (CamelStore *store, const char *top, - guint32 flags, CamelException *ex) +get_folder_info_mbox(CamelStore *store, const char *top, guint32 flags, CamelException *ex) { CamelFolderInfo *fi = NULL; CamelService *service = (CamelService *)store; @@ -173,9 +392,9 @@ get_folder_info (CamelStore *store, const char *top, if (top == NULL || strcmp(top, "INBOX") == 0) { fi = g_malloc0(sizeof(*fi)); - fi->full_name = "INBOX"; - fi->name = "INBOX"; - fi->url = g_strdup_printf("spool:%s#%s", service->url->path, fi->name); + fi->full_name = g_strdup("INBOX"); + fi->name = g_strdup("INBOX"); + fi->url = g_strdup_printf("%s:%s#%s", service->url->protocol, service->url->path, fi->name); CAMEL_STORE_LOCK(store, cache_lock); folder = g_hash_table_lookup(store->folders, fi->full_name); @@ -191,27 +410,11 @@ get_folder_info (CamelStore *store, const char *top, return fi; } -static void free_folder_info (CamelStore *store, CamelFolderInfo *fi) -{ - if (fi) { - g_free(fi->url); - g_free(fi); - } -} - - -/* default implementation, rename all */ -static void -rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex) -{ - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Spool folders cannot be renamed")); -} - -/* default implementation, only delete metadata */ -static void -delete_folder(CamelStore *store, const char *folder_name, CamelException *ex) +static CamelFolderInfo * +get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Spool folders cannot be deleted")); + if (((CamelSpoolStore *)store)->type == CAMEL_SPOOL_STORE_MBOX) + return get_folder_info_mbox(store, top, flags, ex); + else + return get_folder_info_elm(store, top, flags, ex); } diff --git a/camel/providers/local/camel-spool-store.h b/camel/providers/local/camel-spool-store.h index 677f6aa0ed..1e1753481b 100644 --- a/camel/providers/local/camel-spool-store.h +++ b/camel/providers/local/camel-spool-store.h @@ -29,23 +29,28 @@ extern "C" { #pragma } #endif /* __cplusplus }*/ -#include "camel-store.h" +#include "camel-mbox-store.h" #define CAMEL_SPOOL_STORE_TYPE (camel_spool_store_get_type ()) #define CAMEL_SPOOL_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_SPOOL_STORE_TYPE, CamelSpoolStore)) #define CAMEL_SPOOL_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_SPOOL_STORE_TYPE, CamelSpoolStoreClass)) #define CAMEL_IS_SPOOL_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_SPOOL_STORE_TYPE)) +typedef enum _camel_spool_store_t { + CAMEL_SPOOL_STORE_MBOX, /* a single mbox */ + CAMEL_SPOOL_STORE_ELM, /* elm/pine/etc tree of mbox files in folders */ +} camel_spool_store_t; typedef struct { - CamelStore parent_object; + CamelMboxStore parent_object; + camel_spool_store_t type; } CamelSpoolStore; typedef struct { - CamelStoreClass parent_class; + CamelMboxStoreClass parent_class; } CamelSpoolStoreClass; @@ -55,8 +60,6 @@ typedef struct { /* Standard Camel function */ CamelType camel_spool_store_get_type (void); -const gchar *camel_spool_store_get_toplevel_dir (CamelSpoolStore *store); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/camel/providers/local/camel-spool-summary.c b/camel/providers/local/camel-spool-summary.c index ff8f6a85a9..3a1a15cbc8 100644 --- a/camel/providers/local/camel-spool-summary.c +++ b/camel/providers/local/camel-spool-summary.c @@ -49,27 +49,15 @@ struct _CamelSpoolSummaryPrivate { #define _PRIVATE(o) (((CamelSpoolSummary *)(o))->priv) -static int summary_header_load (CamelFolderSummary *, FILE *); -static int summary_header_save (CamelFolderSummary *, FILE *); +static int spool_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex); +static int spool_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex); -static CamelMessageInfo * message_info_new (CamelFolderSummary *, struct _header_raw *); -static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp); -static CamelMessageInfo * message_info_load (CamelFolderSummary *, FILE *); -static int message_info_save (CamelFolderSummary *, FILE *, CamelMessageInfo *); - -static int spool_summary_decode_x_evolution(CamelSpoolSummary *cls, const char *xev, CamelMessageInfo *mi); -static char *spool_summary_encode_x_evolution(CamelSpoolSummary *cls, const CamelMessageInfo *mi); - -static int spool_summary_load(CamelSpoolSummary *cls, int forceindex, CamelException *ex); -static int spool_summary_check(CamelSpoolSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex); -static int spool_summary_sync(CamelSpoolSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); -static CamelMessageInfo *spool_summary_add(CamelSpoolSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex); - -static int spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); +static int spool_summary_sync_full(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); static void camel_spool_summary_class_init (CamelSpoolSummaryClass *klass); static void camel_spool_summary_init (CamelSpoolSummary *obj); static void camel_spool_summary_finalise (CamelObject *obj); + static CamelFolderSummaryClass *camel_spool_summary_parent; CamelType @@ -78,7 +66,7 @@ camel_spool_summary_get_type(void) static CamelType type = CAMEL_INVALID_TYPE; if (type == CAMEL_INVALID_TYPE) { - type = camel_type_register(camel_folder_summary_get_type(), "CamelSpoolSummary", + type = camel_type_register(camel_mbox_summary_get_type(), "CamelSpoolSummary", sizeof (CamelSpoolSummary), sizeof (CamelSpoolSummaryClass), (CamelObjectClassInitFunc) camel_spool_summary_class_init, @@ -93,25 +81,15 @@ camel_spool_summary_get_type(void) static void camel_spool_summary_class_init(CamelSpoolSummaryClass *klass) { - CamelFolderSummaryClass *sklass = (CamelFolderSummaryClass *) klass; - - camel_spool_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS(camel_type_get_global_classfuncs(camel_folder_summary_get_type())); - - sklass->summary_header_load = summary_header_load; - sklass->summary_header_save = summary_header_save; + CamelLocalSummaryClass *lklass = (CamelLocalSummaryClass *)klass; + CamelMboxSummaryClass *mklass = (CamelMboxSummaryClass *)klass; - sklass->message_info_new = message_info_new; - sklass->message_info_new_from_parser = message_info_new_from_parser; - sklass->message_info_load = message_info_load; - sklass->message_info_save = message_info_save; + camel_spool_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS(camel_mbox_summary_get_type()); - klass->load = spool_summary_load; - klass->check = spool_summary_check; - klass->sync = spool_summary_sync; - klass->add = spool_summary_add; + lklass->load = spool_summary_load; + lklass->check = spool_summary_check; - klass->encode_x_evolution = spool_summary_encode_x_evolution; - klass->decode_x_evolution = spool_summary_decode_x_evolution; + mklass->sync_full = spool_summary_sync_full; } static void @@ -122,9 +100,7 @@ camel_spool_summary_init(CamelSpoolSummary *obj) p = _PRIVATE(obj) = g_malloc0(sizeof(*p)); - /* subclasses need to set the right instance data sizes */ - s->message_info_size = sizeof(CamelSpoolMessageInfo); - s->content_info_size = sizeof(CamelMessageContentInfo); + /* message info size is from mbox parent */ /* and a unique file version */ s->version += CAMEL_SPOOL_SUMMARY_VERSION; @@ -133,532 +109,50 @@ camel_spool_summary_init(CamelSpoolSummary *obj) static void camel_spool_summary_finalise(CamelObject *obj) { - CamelSpoolSummary *mbs = CAMEL_SPOOL_SUMMARY(obj); - - g_free(mbs->folder_path); + /*CamelSpoolSummary *mbs = CAMEL_SPOOL_SUMMARY(obj);*/ } CamelSpoolSummary * -camel_spool_summary_new(const char *filename) +camel_spool_summary_new(const char *mbox_name) { CamelSpoolSummary *new = (CamelSpoolSummary *)camel_object_new(camel_spool_summary_get_type()); - camel_folder_summary_set_build_content(CAMEL_FOLDER_SUMMARY(new), FALSE); - new->folder_path = g_strdup(filename); - + camel_local_summary_construct((CamelLocalSummary *)new, NULL, mbox_name, NULL); return new; } static int -spool_summary_load(CamelSpoolSummary *cls, int forceindex, CamelException *ex) -{ - g_warning("spool_summary_load() should nto b e called\n"); - - return camel_folder_summary_load((CamelFolderSummary *)cls); -} - -/* load/check the summary */ -int -camel_spool_summary_load(CamelSpoolSummary *cls, int forceindex, CamelException *ex) -{ - struct stat st; - CamelFolderSummary *s = (CamelFolderSummary *)cls; - - g_warning("spool_summary_load() should nto b e called\n"); - - d(printf("Loading summary ...\n")); - - if (forceindex - || stat(s->summary_path, &st) == -1 - || ((CamelSpoolSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->load(cls, forceindex, ex) == -1) { - camel_folder_summary_clear((CamelFolderSummary *)cls); - } - - return camel_spool_summary_check(cls, NULL, ex); -} - -char * -camel_spool_summary_encode_x_evolution(CamelSpoolSummary *cls, const CamelMessageInfo *info) -{ - return ((CamelSpoolSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->encode_x_evolution(cls, info); -} - -int -camel_spool_summary_decode_x_evolution(CamelSpoolSummary *cls, const char *xev, CamelMessageInfo *info) -{ - return ((CamelSpoolSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->decode_x_evolution(cls, xev, info); -} - -int -camel_spool_summary_check(CamelSpoolSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex) -{ - int ret; - - ret = ((CamelSpoolSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->check(cls, changeinfo, ex); - - return ret; -} - -int -camel_spool_summary_sync(CamelSpoolSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) -{ - return ((CamelSpoolSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->sync(cls, expunge, changeinfo, ex); -} - -CamelMessageInfo * -camel_spool_summary_add(CamelSpoolSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex) -{ - return ((CamelSpoolSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->add(cls, msg, info, ci, ex); -} - -/** - * camel_spool_summary_write_headers: - * @fd: - * @header: - * @xevline: - * - * Write a bunch of headers to the file @fd. IF xevline is non NULL, then - * an X-Evolution header line is created at the end of all of the headers. - * The headers written are termianted with a blank line. - * - * Return value: -1 on error, otherwise the number of bytes written. - **/ -int -camel_spool_summary_write_headers(int fd, struct _header_raw *header, char *xevline) -{ - int outlen = 0, len; - int newfd; - FILE *out; - - /* dum de dum, maybe the whole sync function should just use stdio for output */ - newfd = dup(fd); - if (newfd == -1) - return -1; - - out = fdopen(newfd, "w"); - if (out == NULL) { - close(newfd); - errno = EINVAL; - return -1; - } - - while (header) { - if (strcmp(header->name, "X-Evolution")) { - len = fprintf(out, "%s:%s\n", header->name, header->value); - if (len == -1) { - fclose(out); - return -1; - } - outlen += len; - } - header = header->next; - } - - if (xevline) { - len = fprintf(out, "X-Evolution: %s\n\n", xevline); - if (len == -1) { - fclose(out); - return -1; - } - outlen += len; - } - - if (fclose(out) == -1) - return -1; - - return outlen; -} - -static int -summary_header_load(CamelFolderSummary *s, FILE *in) -{ - CamelSpoolSummary *mbs = CAMEL_SPOOL_SUMMARY(s); - - if (((CamelFolderSummaryClass *)camel_spool_summary_parent)->summary_header_load(s, in) == -1) - return -1; - - return camel_file_util_decode_uint32(in, &mbs->folder_size); -} - -static int -summary_header_save(CamelFolderSummary *s, FILE *out) +spool_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex) { - CamelSpoolSummary *mbs = CAMEL_SPOOL_SUMMARY(s); - - if (((CamelFolderSummaryClass *)camel_spool_summary_parent)->summary_header_save(s, out) == -1) - return -1; - - return camel_file_util_encode_uint32(out, mbs->folder_size); -} - -static CamelMessageInfo * -message_info_new(CamelFolderSummary *s, struct _header_raw *h) -{ - CamelMessageInfo *mi; - CamelSpoolSummary *cls = (CamelSpoolSummary *)s; - - mi = ((CamelFolderSummaryClass *)camel_spool_summary_parent)->message_info_new(s, h); - if (mi) { - CamelSpoolMessageInfo *mbi = (CamelSpoolMessageInfo *)mi; - const char *xev; - - xev = header_raw_find(&h, "X-Evolution", NULL); - if (xev==NULL || camel_spool_summary_decode_x_evolution(cls, xev, mi) == -1) { - /* to indicate it has no xev header */ - mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV; - camel_message_info_set_uid(mi, camel_folder_summary_next_uid_string(s)); - } - - mbi->frompos = -1; - } - - return mi; -} - -static CamelMessageInfo * -message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp) -{ - CamelMessageInfo *mi; - - mi = ((CamelFolderSummaryClass *)camel_spool_summary_parent)->message_info_new_from_parser(s, mp); - if (mi) { - CamelSpoolMessageInfo *mbi = (CamelSpoolMessageInfo *)mi; - - mbi->frompos = camel_mime_parser_tell_start_from(mp); - } - - return mi; -} - -static CamelMessageInfo * -message_info_load(CamelFolderSummary *s, FILE *in) -{ - CamelMessageInfo *mi; - - io(printf("loading spool message info\n")); - - mi = ((CamelFolderSummaryClass *)camel_spool_summary_parent)->message_info_load(s, in); - if (mi) { - CamelSpoolMessageInfo *mbi = (CamelSpoolMessageInfo *)mi; - - if (camel_file_util_decode_off_t(in, &mbi->frompos) == -1) - goto error; - } - - return mi; -error: - camel_folder_summary_info_free(s, mi); - return NULL; -} - -static int -message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi) -{ - CamelSpoolMessageInfo *mbi = (CamelSpoolMessageInfo *)mi; - - io(printf("saving spool message info\n")); - - if (((CamelFolderSummaryClass *)camel_spool_summary_parent)->message_info_save(s, out, mi) == -1 - || camel_file_util_encode_off_t(out, mbi->frompos) == -1) - return -1; - + g_warning("spool summary - not loading anything\n"); return 0; } -static int -summary_rebuild(CamelSpoolSummary *cls, off_t offset, CamelException *ex) -{ - CamelFolderSummary *s = (CamelFolderSummary *)cls; - CamelMimeParser *mp; - int fd; - int ok = 0; - struct stat st; - off_t size = 0; - - /* FIXME: If there is a failure, it shouldn't clear the summary and restart, - it should try and merge the summary info's. This is a bit tricky. */ - - camel_operation_start(NULL, _("Storing folder")); - - fd = open(cls->folder_path, O_RDONLY); - if (fd == -1) { - d(printf("%s failed to open: %s\n", cls->folder_path, strerror(errno))); - camel_exception_setv(ex, 1, _("Could not open folder: %s: %s"), - cls->folder_path, offset, strerror(errno)); - camel_operation_end(NULL); - return -1; - } - - if (fstat(fd, &st) == 0) - size = st.st_size; - - mp = camel_mime_parser_new(); - camel_mime_parser_init_with_fd(mp, fd); - camel_mime_parser_scan_from(mp, TRUE); - camel_mime_parser_seek(mp, offset, SEEK_SET); - - if (offset > 0) { - if (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM) { - if (camel_mime_parser_tell_start_from(mp) != offset) { - g_warning("The next message didn't start where I expected, building summary from start"); - camel_mime_parser_drop_step(mp); - offset = 0; - camel_mime_parser_seek(mp, offset, SEEK_SET); - camel_folder_summary_clear(s); - } else { - camel_mime_parser_unstep(mp); - } - } else { - d(printf("mime parser state ran out? state is %d\n", camel_mime_parser_state(mp))); - camel_object_unref(CAMEL_OBJECT(mp)); - /* end of file - no content? no error either */ - camel_operation_end(NULL); - return 0; - } - } - - while (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM) { - CamelMessageInfo *info; - off_t pc = camel_mime_parser_tell_start_from (mp) + 1; - - camel_operation_progress (NULL, (int) (((float) pc / size) * 100)); - - info = camel_folder_summary_add_from_parser(s, mp); - if (info == NULL) { - camel_exception_setv(ex, 1, _("Fatal mail parser error near position %ld in folder %s"), - camel_mime_parser_tell(mp), cls->folder_path); - ok = -1; - break; - } - - g_assert(camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM_END); - } - - camel_object_unref(CAMEL_OBJECT (mp)); - - /* update the file size/mtime in the summary */ - if (ok != -1) { - if (stat(cls->folder_path, &st) == 0) { - camel_folder_summary_touch(s); - cls->folder_size = st.st_size; - s->time = st.st_mtime; - } - } - - camel_operation_end(NULL); - - return ok; -} - -/* like summary_rebuild, but also do changeinfo stuff (if supplied) */ -static int -summary_update(CamelSpoolSummary *cls, off_t offset, CamelFolderChangeInfo *changeinfo, CamelException *ex) -{ - int ret, i, count; - CamelFolderSummary *s = (CamelFolderSummary *)cls; - - d(printf("Calling summary update, from pos %d\n", (int)offset)); - - if (changeinfo) { - /* we use the diff function of the change_info to build the update list. */ - for (i = 0; i < camel_folder_summary_count(s); i++) { - CamelMessageInfo *mi = camel_folder_summary_index(s, i); - - camel_folder_change_info_add_source(changeinfo, camel_message_info_uid(mi)); - camel_folder_summary_info_free(s, mi); - } - } - - /* do the actual work */ - ret = summary_rebuild(cls, offset, ex); - - if (changeinfo) { - count = camel_folder_summary_count(s); - for (i = 0; i < count; i++) { - CamelMessageInfo *mi = camel_folder_summary_index(s, i); - camel_folder_change_info_add_update(changeinfo, camel_message_info_uid(mi)); - camel_folder_summary_info_free(s, mi); - } - camel_folder_change_info_build_diff(changeinfo); - } - - return ret; -} - -static int -spool_summary_check(CamelSpoolSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex) -{ - CamelFolderSummary *s = (CamelFolderSummary *)cls; - struct stat st; - int ret = 0; - - d(printf("Checking summary\n")); - - /* check if the summary is up-to-date */ - if (stat(cls->folder_path, &st) == -1) { - camel_folder_summary_clear(s); - camel_exception_setv(ex, 1, _("Cannot check folder: %s: %s"), cls->folder_path, strerror(errno)); - return -1; - } - - if (st.st_size == 0) { - /* empty? No need to scan at all */ - d(printf("Empty spool, clearing summary\n")); - camel_folder_summary_clear(s); - ret = 0; - } else if (s->messages->len == 0) { - /* if we are empty, then we rebuilt from scratch */ - d(printf("Empty summary, rebuilding from start\n")); - ret = summary_update(cls, 0, changeinfo, ex); - } else { - /* is the summary uptodate? */ - if (st.st_size != cls->folder_size || st.st_mtime != s->time) { - if (cls->folder_size < st.st_size) { - /* this will automatically rescan from 0 if there is a problem */ - d(printf("folder grew, attempting to rebuild from %d\n", cls->folder_size)); - ret = summary_update(cls, cls->folder_size, changeinfo, ex); - } else { - d(printf("folder shrank! rebuilding from start\n")); - camel_folder_summary_clear(s); - ret = summary_update(cls, 0, changeinfo, ex); - } - } - } - - if (ret != -1) { - int i, work, count; - - /* check to see if we need to copy/update the file; missing xev headers prompt this */ - work = FALSE; - count = camel_folder_summary_count(s); - for (i=0;!work && i<count; i++) { - CamelMessageInfo *info = camel_folder_summary_index(s, i); - g_assert(info); - work = (info->flags & (CAMEL_MESSAGE_FOLDER_NOXEV)) != 0; - camel_folder_summary_info_free(s, info); - } - - /* if we do, then write out the headers using sync_full, etc */ - if (work) { - d(printf("Have to add new headers, re-syncing from the start to accomplish this\n")); - ret = spool_summary_sync_full(cls, FALSE, changeinfo, ex); - - if (stat(cls->folder_path, &st) == -1) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Unknown error: %s"), strerror(errno)); - return -1; - } - } - cls->folder_size = st.st_size; - s->time = st.st_mtime; - } - - return ret; -} - -static char *tz_months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static char *tz_days[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; - -/* tries to build a From line, based on message headers */ -char * -camel_spool_summary_build_from(struct _header_raw *header) -{ - GString *out = g_string_new("From "); - char *ret; - const char *tmp; - time_t thetime; - int offset; - struct tm tm; - - tmp = header_raw_find(&header, "Sender", NULL); - if (tmp == NULL) - tmp = header_raw_find(&header, "From", NULL); - if (tmp != NULL) { - struct _header_address *addr = header_address_decode(tmp); - - tmp = NULL; - if (addr) { - if (addr->type == HEADER_ADDRESS_NAME) { - g_string_append(out, addr->v.addr); - tmp = ""; - } - header_address_unref(addr); - } - } - if (tmp == NULL) { - g_string_append(out, "unknown@nodomain.now.au"); - } - - /* try use the received header to get the date */ - tmp = header_raw_find(&header, "Received", NULL); - if (tmp) { - tmp = strrchr(tmp, ';'); - if (tmp) - tmp++; - } - - /* if there isn't one, try the Date field */ - if (tmp == NULL) - tmp = header_raw_find(&header, "Date", NULL); - - thetime = header_decode_date(tmp, &offset); - - thetime += ((offset / 100) * (60 * 60)) + (offset % 100) * 60; - - /* a pseudo, but still bogus attempt at thread safing the function */ - /*memcpy(&tm, gmtime(&thetime), sizeof(tm));*/ - gmtime_r(&thetime, &tm); - - g_string_sprintfa(out, " %s %s %2d %02d:%02d:%02d %4d\n", - tz_days[tm.tm_wday], - tz_months[tm.tm_mon], tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900); - - ret = out->str; - g_string_free(out, FALSE); - return ret; -} - /* perform a full sync */ static int -spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) +spool_summary_sync_full(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) { - CamelFolderSummary *s = (CamelFolderSummary *)cls; - CamelMimeParser *mp = NULL; - int i, count; - CamelSpoolMessageInfo *info = NULL; int fd = -1, fdout = -1; char *tmpname = NULL; - char *buffer, *xevnew = NULL, *p; - int len; - const char *fromline; - int lastdel = FALSE; + char *buffer, *p; off_t spoollen, outlen; int size, sizeout; struct stat st; + guint32 flags = (expunge?1:0); d(printf("performing full summary/sync\n")); camel_operation_start(NULL, _("Storing folder")); - fd = open(cls->folder_path, O_RDWR); + fd = open(((CamelLocalSummary *)cls)->folder_path, O_RDWR); if (fd == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not open file: %s: %s"), - cls->folder_path, strerror(errno)); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno)); camel_operation_end(NULL); return -1; } - mp = camel_mime_parser_new(); - camel_mime_parser_scan_from(mp, TRUE); - camel_mime_parser_scan_pre_from(mp, TRUE); - camel_mime_parser_init_with_fd(mp, fd); - #ifdef HAVE_MKSTEMP tmpname = alloca(64); sprintf(tmpname, "/tmp/spool.camel.XXXXXX"); @@ -676,127 +170,16 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha goto error; } - count = camel_folder_summary_count(s); - for (i = 0; i < count; i++) { - int pc = (i + 1) * 100 / count; - - camel_operation_progress(NULL, pc); - - info = (CamelSpoolMessageInfo *)camel_folder_summary_index(s, i); - - g_assert(info); - - d(printf("Looking at message %s\n", camel_message_info_uid(info))); - - /* only need to seek past deleted messages, otherwise we should be at the right spot/state already */ - if (lastdel) { - d(printf("seeking to %d\n", (int)info->frompos)); - camel_mime_parser_seek(mp, info->frompos, SEEK_SET); - } - - if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_FROM) { - g_warning("Expected a From line here, didn't get it"); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary and folder mismatch, even after a sync")); - goto error; - } - - if (camel_mime_parser_tell_start_from(mp) != info->frompos) { - g_warning("Didn't get the next message where I expected (%d) got %d instead", - (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp)); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary and folder mismatch, even after a sync")); - goto error; - } - - lastdel = FALSE; - if (expunge && info->info.flags & CAMEL_MESSAGE_DELETED) { - const char *uid = camel_message_info_uid(info); - - d(printf("Deleting %s\n", uid)); - -#if 0 - if (cls->index) - ibex_unindex(cls->index, (char *)uid); -#endif - /* remove it from the change list */ - camel_folder_change_info_remove_uid(changeinfo, uid); - camel_folder_summary_remove(s, (CamelMessageInfo *)info); - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); - count--; - i--; - info = NULL; - lastdel = TRUE; - } else { - /* otherwise, the message is staying, copy its From_ line across */ - if (i>0) { - write(fdout, "\n", 1); - } - info->frompos = lseek(fdout, 0, SEEK_CUR); - fromline = camel_mime_parser_from_line(mp); - write(fdout, fromline, strlen(fromline)); - } - - if (info && info->info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) { - d(printf("Updating header for %s flags = %08x\n", camel_message_info_uid(info), info->info.flags)); - - if (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_FROM_END) { - g_warning("camel_mime_parser_step failed (2)"); - goto error; - } - - xevnew = camel_spool_summary_encode_x_evolution(cls, (CamelMessageInfo *)info); - if (camel_spool_summary_write_headers(fdout, camel_mime_parser_headers_raw(mp), xevnew) == -1) { - d(printf("Error writing to tmp mailbox\n")); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Error writing to temp mailbox: %s"), - strerror(errno)); - goto error; - } - - /* mark this message as recent */ - if (info->info.flags & CAMEL_MESSAGE_FOLDER_NOXEV) - camel_folder_change_info_recent_uid(changeinfo, camel_message_info_uid(info)); - - info->info.flags &= 0xffff; - g_free(xevnew); - xevnew = NULL; - camel_mime_parser_drop_step(mp); - } - - camel_mime_parser_drop_step(mp); - if (info) { - d(printf("looking for message content to copy across from %d\n", (int)camel_mime_parser_tell(mp))); - while (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_PRE_FROM) { - d(printf("copying spool contents to tmp: '%.*s'\n", len, buffer)); - if (write(fdout, buffer, len) != len) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Writing to tmp mailbox failed: %s: %s"), - cls->folder_path, strerror(errno)); - goto error; - } - } - d(printf("we are now at %d, from = %d\n", (int)camel_mime_parser_tell(mp), - (int)camel_mime_parser_tell_start_from(mp))); - camel_mime_parser_unstep(mp); - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); - info = NULL; - } - } + if (camel_mbox_summary_sync_mbox((CamelMboxSummary *)cls, flags, changeinfo, fd, fdout, ex) == -1) + goto error; - /* if the last message was deleted, and we had any messages left, - make sure we close out with a closing \n - since we removed the - one part of the From line following it */ - if (lastdel && count > 0) { - write(fdout, "\n", 1); - } /* sync out content */ if (fsync(fdout) == -1) { g_warning("Cannot sync temporary folder: %s", strerror(errno)); camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not sync temporary folder %s: %s"), - cls->folder_path, strerror(errno)); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno)); goto error; } @@ -805,7 +188,7 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha g_warning("Cannot sync temporary folder: %s", strerror(errno)); camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not sync temporary folder %s: %s"), - cls->folder_path, strerror(errno)); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno)); goto error; } spoollen = st.st_size; @@ -814,12 +197,12 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha g_warning("Cannot sync temporary folder: %s", strerror(errno)); camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not sync temporary folder %s: %s"), - cls->folder_path, strerror(errno)); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno)); goto error; } outlen = st.st_size; - /* I think this is the right way to do this */ + /* I think this is the right way to do this - checking that the file will fit the new data */ if (outlen>0 && (lseek(fd, outlen-1, SEEK_SET) == -1 || write(fd, "", 1) != 1 @@ -829,7 +212,7 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha g_warning("Cannot sync spool folder: %s", strerror(errno)); camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not sync spool folder %s: %s"), - cls->folder_path, strerror(errno)); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno)); /* incase we ran out of room, remove any trailing space first */ ftruncate(fd, spoollen); goto error; @@ -860,7 +243,7 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not sync spool folder %s: %s\n" "Folder may be corrupt, copy saved in `%s'"), - cls->folder_path, strerror(errno), tmpnam); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno), tmpnam); /* so we dont delete it */ close(fdout); tmpname = NULL; @@ -878,7 +261,7 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not sync spool folder %s: %s\n" "Folder may be corrupt, copy saved in `%s'"), - cls->folder_path, strerror(errno), tmpnam); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno), tmpnam); close(fdout); tmpname = NULL; fdout = -1; @@ -890,7 +273,7 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not sync spool folder %s: %s\n" "Folder may be corrupt, copy saved in `%s'"), - cls->folder_path, strerror(errno), tmpnam); + ((CamelLocalSummary *)cls)->folder_path, strerror(errno), tmpnam); close(fdout); tmpname = NULL; fdout = -1; @@ -901,7 +284,6 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha close(fdout); unlink(tmpname); - camel_object_unref((CamelObject *)mp); camel_operation_end(NULL); return 0; @@ -912,377 +294,48 @@ spool_summary_sync_full(CamelSpoolSummary *cls, gboolean expunge, CamelFolderCha if (fdout != -1) close(fdout); - g_free(xevnew); - if (tmpname) unlink(tmpname); - if (mp) - camel_object_unref((CamelObject *)mp); - if (info) - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); camel_operation_end(NULL); return -1; } -/* perform a quick sync - only system flags have changed */ static int -spool_summary_sync_quick(CamelSpoolSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) -{ - CamelFolderSummary *s = (CamelFolderSummary *)cls; - CamelMimeParser *mp = NULL; - int i, count; - CamelSpoolMessageInfo *info = NULL; - int fd = -1; - char *xevnew, *xevtmp; - const char *xev; - int len; - off_t lastpos; - - d(printf("Performing quick summary sync\n")); - - camel_operation_start(NULL, _("Storing folder")); - - fd = open(cls->folder_path, O_RDWR); - if (fd == -1) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not file: %s: %s"), - cls->folder_path, strerror(errno)); - - camel_operation_end(NULL); - return -1; - } - - mp = camel_mime_parser_new(); - camel_mime_parser_scan_from(mp, TRUE); - camel_mime_parser_scan_pre_from(mp, TRUE); - camel_mime_parser_init_with_fd(mp, fd); - - count = camel_folder_summary_count(s); - for (i = 0; i < count; i++) { - int xevoffset; - int pc = (i+1)*100/count; - - camel_operation_progress(NULL, pc); - - info = (CamelSpoolMessageInfo *)camel_folder_summary_index(s, i); - - g_assert(info); - - d(printf("Checking message %s %08x\n", camel_message_info_uid(info), info->info.flags)); - - if ((info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) { - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); - info = NULL; - continue; - } - - d(printf("Updating message %s\n", camel_message_info_uid(info))); - - camel_mime_parser_seek(mp, info->frompos, SEEK_SET); - - if (camel_mime_parser_step(mp, 0, 0) != HSCAN_FROM) { - g_warning("Expected a From line here, didn't get it"); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary and folder mismatch, even after a sync")); - goto error; - } - - if (camel_mime_parser_tell_start_from(mp) != info->frompos) { - g_warning("Didn't get the next message where I expected (%d) got %d instead", - (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp)); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary and folder mismatch, even after a sync")); - goto error; - } - - if (camel_mime_parser_step(mp, 0, 0) == HSCAN_FROM_END) { - g_warning("camel_mime_parser_step failed (2)"); - goto error; - } - - xev = camel_mime_parser_header(mp, "X-Evolution", &xevoffset); - if (xev == NULL || camel_spool_summary_decode_x_evolution(cls, xev, NULL) == -1) { - g_warning("We're supposed to have a valid x-ev header, but we dont"); - goto error; - } - xevnew = camel_spool_summary_encode_x_evolution(cls, (CamelMessageInfo *)info); - /* SIGH: encode_param_list is about the only function which folds headers by itself. - This should be fixed somehow differently (either parser doesn't fold headers, - or param_list doesn't, or something */ - xevtmp = header_unfold(xevnew); - /* the raw header contains a leading ' ', so (dis)count that too */ - if (strlen(xev)-1 != strlen(xevtmp)) { - g_free(xevnew); - g_free(xevtmp); - g_warning("Hmm, the xev headers shouldn't have changed size, but they did"); - goto error; - } - g_free(xevtmp); - - /* we write out the xevnew string, assuming its been folded identically to the original too! */ - - lastpos = lseek(fd, 0, SEEK_CUR); - lseek(fd, xevoffset+strlen("X-Evolution: "), SEEK_SET); - do { - len = write(fd, xevnew, strlen(xevnew)); - } while (len == -1 && errno == EINTR); - lseek(fd, lastpos, SEEK_SET); - g_free(xevnew); - - camel_mime_parser_drop_step(mp); - camel_mime_parser_drop_step(mp); - - info->info.flags &= 0xffff; - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); - } - - d(printf("Closing folders\n")); - - if (close(fd) == -1) { - g_warning("Cannot close source folder: %s", strerror(errno)); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not close source folder %s: %s"), - cls->folder_path, strerror(errno)); - fd = -1; - goto error; - } - - camel_object_unref((CamelObject *)mp); - - camel_operation_end(NULL); - - return 0; - error: - if (fd != -1) - close(fd); - if (mp) - camel_object_unref((CamelObject *)mp); - if (info) - camel_folder_summary_info_free(s, (CamelMessageInfo *)info); - - camel_operation_end(NULL); - - return -1; -} - -static int -spool_summary_sync(CamelSpoolSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) +spool_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex) { + int i, work, count; struct stat st; CamelFolderSummary *s = (CamelFolderSummary *)cls; - int i, count; - int quick = TRUE, work=FALSE; - int ret; - /* first, sync ourselves up, just to make sure */ - summary_update(cls, cls->folder_size, changeinfo, ex); - if (camel_exception_is_set(ex)) + if (((CamelLocalSummaryClass *)camel_spool_summary_parent)->check(cls, changeinfo, ex) == -1) return -1; + /* check to see if we need to copy/update the file; missing xev headers prompt this */ + work = FALSE; count = camel_folder_summary_count(s); - if (count == 0) - return 0; - - /* check what work we have to do, if any */ - for (i=0;quick && i<count; i++) { + for (i=0;!work && i<count; i++) { CamelMessageInfo *info = camel_folder_summary_index(s, i); g_assert(info); - if ((expunge && (info->flags & CAMEL_MESSAGE_DELETED)) || - (info->flags & (CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_XEVCHANGE))) - quick = FALSE; - else - work |= (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0; + work = (info->flags & (CAMEL_MESSAGE_FOLDER_NOXEV)) != 0; camel_folder_summary_info_free(s, info); } - /* yuck i hate this logic, but its to simplify the 'all ok, update summary' and failover cases */ - ret = -1; - if (quick) { - if (work) { - ret = spool_summary_sync_quick(cls, expunge, changeinfo, ex); - if (ret == -1) { - g_warning("failed a quick-sync, trying a full sync"); - camel_exception_clear(ex); - } - } else { - ret = 0; - } - } - - if (ret == -1) - ret = spool_summary_sync_full(cls, expunge, changeinfo, ex); - if (ret == -1) - return -1; - - if (stat(cls->folder_path, &st) == -1) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Unknown error: %s"), strerror(errno)); - return -1; - } - - camel_folder_summary_touch(s); - s->time = st.st_mtime; - cls->folder_size = st.st_size; - /*camel_folder_summary_save(s);*/ - - return 0; -} - - -static CamelMessageInfo * -spool_summary_add(CamelSpoolSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex) -{ - CamelMessageInfo *mi; - char *xev; - - d(printf("Adding message to summary\n")); - - mi = camel_folder_summary_add_from_message((CamelFolderSummary *)cls, msg); - if (mi) { - d(printf("Added, uid = %s\n", camel_message_info_uid(mi))); - if (info) { - CamelTag *tag = info->user_tags; - CamelFlag *flag = info->user_flags; - - while (flag) { - camel_flag_set(&mi->user_flags, flag->name, TRUE); - flag = flag->next; - } - - while (tag) { - camel_tag_set(&mi->user_tags, tag->name, tag->value); - tag = tag->next; - } - - mi->flags = mi->flags | (info->flags & 0xffff); - } - mi->flags &= ~(CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_FLAGGED); - xev = camel_spool_summary_encode_x_evolution(cls, mi); - camel_medium_set_header((CamelMedium *)msg, "X-Evolution", xev); - g_free(xev); - camel_folder_change_info_add_uid(ci, camel_message_info_uid(mi)); - } else { - d(printf("Failed!\n")); - camel_exception_set(ex, 1, _("Unable to add message to summary: unknown reason")); - } - return mi; -} - -static char * -spool_summary_encode_x_evolution(CamelSpoolSummary *cls, const CamelMessageInfo *mi) -{ - GString *out = g_string_new(""); - struct _header_param *params = NULL; - GString *val = g_string_new(""); - CamelFlag *flag = mi->user_flags; - CamelTag *tag = mi->user_tags; - char *ret; - const char *p, *uidstr; - guint32 uid; - - /* FIXME: work out what to do with uid's that aren't stored here? */ - /* FIXME: perhaps make that a mbox folder only issue?? */ - p = uidstr = camel_message_info_uid(mi); - while (*p && isdigit(*p)) - p++; - if (*p == 0 && sscanf(uidstr, "%u", &uid) == 1) { - g_string_sprintf(out, "%08x-%04x", uid, mi->flags & 0xffff); - } else { - g_string_sprintf(out, "%s-%04x", uidstr, mi->flags & 0xffff); - } - - if (flag || tag) { - val = g_string_new(""); - - if (flag) { - while (flag) { - g_string_append(val, flag->name); - if (flag->next) - g_string_append_c(val, ','); - flag = flag->next; - } - header_set_param(¶ms, "flags", val->str); - g_string_truncate(val, 0); - } - if (tag) { - while (tag) { - g_string_append(val, tag->name); - g_string_append_c(val, '='); - g_string_append(val, tag->value); - if (tag->next) - g_string_append_c(val, ','); - tag = tag->next; - } - header_set_param(¶ms, "tags", val->str); + /* if we do, then write out the headers using sync_full, etc */ + if (work) { + d(printf("Have to add new headers, re-syncing from the start to accomplish this\n")); + if (((CamelMboxSummaryClass *)((CamelObject *)cls)->klass)->sync_full((CamelMboxSummary *)cls, FALSE, changeinfo, ex) == -1) + return -1; + + if (stat(cls->folder_path, &st) == -1) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Unknown error: %s"), strerror(errno)); + return -1; } - g_string_free(val, TRUE); - header_param_list_format_append(out, params); - header_param_list_free(params); - } - ret = out->str; - g_string_free(out, FALSE); - return ret; -} - -static int -spool_summary_decode_x_evolution(CamelSpoolSummary *cls, const char *xev, CamelMessageInfo *mi) -{ - struct _header_param *params, *scan; - guint32 uid, flags; - char *header; - int i; - /* check for uid/flags */ - header = header_token_decode(xev); - if (header && strlen(header) == strlen("00000000-0000") - && sscanf(header, "%08x-%04x", &uid, &flags) == 2) { - char uidstr[20]; - if (mi) { - sprintf(uidstr, "%u", uid); - camel_message_info_set_uid(mi, g_strdup(uidstr)); - mi->flags = flags; - } - } else { - g_free(header); - return -1; + ((CamelMboxSummary *)cls)->folder_size = st.st_size; + ((CamelFolderSummary *)cls)->time = st.st_mtime; } - g_free(header); - - if (mi == NULL) - return 0; - - /* check for additional data */ - header = strchr(xev, ';'); - if (header) { - params = header_param_list_decode(header+1); - scan = params; - while (scan) { - if (!strcasecmp(scan->name, "flags")) { - char **flagv = g_strsplit(scan->value, ",", 1000); - - for (i=0;flagv[i];i++) { - camel_flag_set(&mi->user_flags, flagv[i], TRUE); - } - g_strfreev(flagv); - } else if (!strcasecmp(scan->name, "tags")) { - char **tagv = g_strsplit(scan->value, ",", 10000); - char *val; - for (i=0;tagv[i];i++) { - val = strchr(tagv[i], '='); - if (val) { - *val++ = 0; - camel_tag_set(&mi->user_tags, tagv[i], val); - val[-1]='='; - } - } - g_strfreev(tagv); - } - scan = scan->next; - } - header_param_list_free(params); - } return 0; } - diff --git a/camel/providers/local/camel-spool-summary.h b/camel/providers/local/camel-spool-summary.h index 77fa6bdb3e..ac846220f4 100644 --- a/camel/providers/local/camel-spool-summary.h +++ b/camel/providers/local/camel-spool-summary.h @@ -25,6 +25,7 @@ #include <camel/camel-folder.h> #include <camel/camel-exception.h> #include <camel/camel-index.h> +#include "camel-mbox-summary.h" #define CAMEL_SPOOL_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_spool_summary_get_type (), CamelSpoolSummary) #define CAMEL_SPOOL_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_spool_summary_get_type (), CamelSpoolSummaryClass) @@ -33,38 +34,14 @@ typedef struct _CamelSpoolSummary CamelSpoolSummary; typedef struct _CamelSpoolSummaryClass CamelSpoolSummaryClass; -/* extra summary flags */ -enum { - CAMEL_MESSAGE_FOLDER_NOXEV = 1<<17, - CAMEL_MESSAGE_FOLDER_XEVCHANGE = 1<<18, -}; - -typedef struct _CamelSpoolMessageInfo { - CamelMessageInfo info; - - off_t frompos; -} CamelSpoolMessageInfo; - struct _CamelSpoolSummary { - CamelFolderSummary parent; + CamelMboxSummary parent; struct _CamelSpoolSummaryPrivate *priv; - - char *folder_path; /* name of matching folder */ - - size_t folder_size; }; struct _CamelSpoolSummaryClass { - CamelFolderSummaryClass parent_class; - - int (*load)(CamelSpoolSummary *cls, int forceindex, CamelException *ex); - int (*check)(CamelSpoolSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex); - int (*sync)(CamelSpoolSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); - CamelMessageInfo *(*add)(CamelSpoolSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex); - - char *(*encode_x_evolution)(CamelSpoolSummary *cls, const CamelMessageInfo *info); - int (*decode_x_evolution)(CamelSpoolSummary *cls, const char *xev, CamelMessageInfo *info); + CamelMboxSummaryClass parent_class; }; CamelType camel_spool_summary_get_type (void); diff --git a/camel/providers/local/camel-spoold-store.c b/camel/providers/local/camel-spoold-store.c deleted file mode 100644 index e6b375ce44..0000000000 --- a/camel/providers/local/camel-spoold-store.c +++ /dev/null @@ -1,389 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Authors: Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 2001 Ximian Inc (www.ximian.com/) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * 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 Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <sys/types.h> -#include <sys/stat.h> -#include <errno.h> -#include <string.h> -#include <unistd.h> -#include <stdio.h> -#include <dirent.h> - -#include "camel-spoold-store.h" -#include "camel-spool-folder.h" -#include "camel-exception.h" -#include "camel-url.h" -#include "camel-private.h" - -#define d(x) - -/* Returns the class for a CamelSpoolDStore */ -#define CSPOOLS_CLASS(so) CAMEL_SPOOLD_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so)) -#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) - -static void construct (CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex); -static CamelFolder *get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex); -static char *get_name(CamelService *service, gboolean brief); -static CamelFolder *get_inbox (CamelStore *store, CamelException *ex); -static void rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex); -static CamelFolderInfo *get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex); -static void free_folder_info (CamelStore *store, CamelFolderInfo *fi); - -static void delete_folder(CamelStore *store, const char *folder_name, CamelException *ex); -static void rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex); - -static CamelStoreClass *parent_class = NULL; - -static void -camel_spoold_store_class_init (CamelSpoolDStoreClass *camel_spoold_store_class) -{ - CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_spoold_store_class); - CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS (camel_spoold_store_class); - - parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ())); - - /* virtual method overload */ - camel_service_class->construct = construct; - camel_service_class->get_name = get_name; - camel_store_class->get_folder = get_folder; - camel_store_class->get_inbox = get_inbox; - camel_store_class->get_folder_info = get_folder_info; - camel_store_class->free_folder_info = free_folder_info; - - camel_store_class->delete_folder = delete_folder; - camel_store_class->rename_folder = rename_folder; -} - -CamelType -camel_spoold_store_get_type (void) -{ - static CamelType camel_spoold_store_type = CAMEL_INVALID_TYPE; - - if (camel_spoold_store_type == CAMEL_INVALID_TYPE) { - camel_spoold_store_type = camel_type_register (CAMEL_SPOOL_STORE_TYPE, "CamelSpoolDStore", - sizeof (CamelSpoolDStore), - sizeof (CamelSpoolDStoreClass), - (CamelObjectClassInitFunc) camel_spoold_store_class_init, - NULL, - NULL, - NULL); - } - - return camel_spoold_store_type; -} - -static void -construct (CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex) -{ - struct stat st; - - d(printf("constructing store of type %s '%s:%s'\n", - camel_type_to_name(((CamelObject *)service)->s.type), url->protocol, url->path)); - - CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex); - if (camel_exception_is_set (ex)) - return; - - if (service->url->path[0] != '/') { - camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, - _("Store root %s is not an absolute path"), service->url->path); - return; - } - - if (stat(service->url->path, &st) == -1 || !S_ISDIR(st.st_mode)) { - camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, - _("Store `%s' does not exist or is not a directory"), - service->url->path); - return; - } -} - -const char * -camel_spoold_store_get_toplevel_dir (CamelSpoolDStore *store) -{ - CamelURL *url = CAMEL_SERVICE (store)->url; - - g_assert (url != NULL); - return url->path; -} - -static CamelFolder * -get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex) -{ - char *path = ((CamelService *)store)->url->path; - CamelFolder *folder; - - - path = alloca(strlen(((CamelService *)store)->url->path) + strlen(folder_name)+2); - sprintf(path, "%s/%s", ((CamelService *)store)->url->path, folder_name); - - d(printf("opening folder %s on path %s\n", folder_name, path)); - -#if 0 - /* need to check path? */ - if (strcmp(folder_name, "INBOX") != 0) { - camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, - _("Folder `%s/%s' does not exist."), - path, folder_name); - return NULL; - } -#endif - folder = camel_spool_folder_new(store, folder_name, path, flags, ex); - - return folder; -} - -static CamelFolder * -get_inbox(CamelStore *store, CamelException *ex) -{ - camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, - _("Store does not support an INBOX")); - - return NULL; -} - -static char * -get_name (CamelService *service, gboolean brief) -{ - if (brief) - return g_strdup (service->url->path); - else - return g_strdup_printf (_("Mail tree %s"), service->url->path); -} - -static void free_folder_info (CamelStore *store, CamelFolderInfo *fi) -{ - if (fi) { - g_free(fi->url); - g_free(fi->name); - g_free(fi->full_name); - g_free(fi->path); - g_free(fi); - } -} - - -/* default implementation, rename all */ -static void -rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex) -{ - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Spool folders cannot be renamed")); -} - -/* default implementation, only delete metadata */ -static void -delete_folder(CamelStore *store, const char *folder_name, CamelException *ex) -{ - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Spool folders cannot be deleted")); -} - - - -static CamelFolderInfo *camel_folder_info_new(const char *url, const char *full, const char *name, int unread) -{ - CamelFolderInfo *fi; - - fi = g_malloc0(sizeof(*fi)); - fi->url = g_strdup(url); - fi->full_name = g_strdup(full); - fi->name = g_strdup(name); - fi->unread_message_count = unread; - camel_folder_info_build_path(fi, '/'); - - d(printf("Adding spoold info: '%s' '%s' '%s' '%s'\n", fi->path, fi->name, fi->full_name, fi->url)); - - return fi; -} - -/* used to find out where we've visited already */ -struct _inode { - dev_t dnode; - ino_t inode; -}; - -/* returns number of records found at or below this level */ -static int scan_dir(CamelStore *store, GHashTable *visited, char *root, const char *path, guint32 flags, CamelFolderInfo *parent, CamelFolderInfo **fip, CamelException *ex) -{ - DIR *dir; - struct dirent *d; - char *name, *uri, *tmp, *fname; - CamelFolderInfo *fi = NULL; - struct stat st; - CamelFolder *folder; - int unread; - char from[80]; - FILE *fp; - - d(printf("checking dir '%s' part '%s' for mbox content\n", root, path)); - - /* look for folders matching the right structure, recursively */ - if (path) { - name = alloca(strlen(root)+strlen(path)+2); - sprintf(name, "%s/%s", root, path); - } else - name = root; - - dir = opendir(name); - if (dir == NULL) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not scan folder `%s': %s"), - root, strerror(errno)); - g_free(name); - return -1; - } - - if (path != NULL) { - uri = g_strdup_printf("spoold:%s;noselect=yes#%s", root, path); - tmp = strrchr(path, '/'); - if (tmp == NULL) - tmp = (char *)path; - else - tmp++; - fi = camel_folder_info_new(uri, path, tmp, -1); - fi->parent = parent; - fi->sibling = *fip; - *fip = fi; - g_free(uri); - - fip = &fi->child; - parent = fi; - } - - while ( (d = readdir(dir)) ) { - if (strcmp(d->d_name, ".") == 0 - || strcmp(d->d_name, "..") == 0) - continue; - - tmp = g_strdup_printf("%s/%s", name, d->d_name); - if (stat(tmp, &st) == 0) { - if (path) - fname = g_strdup_printf("%s/%s", path, d->d_name); - else - fname = g_strdup(d->d_name); - - if (S_ISREG(st.st_mode)) { - /* first, see if we already have it open */ - CAMEL_STORE_LOCK(store, cache_lock); - folder = g_hash_table_lookup(store->folders, fname); - if (folder) - unread = camel_folder_get_unread_message_count(folder); - else - unread = -1; - CAMEL_STORE_UNLOCK(store, cache_lock); - - /* no? check its content to see if its a folder or not */ - if (folder == NULL) { - fp = fopen(tmp, "r"); - if (fp != NULL) { - if (fgets(from, sizeof(from), fp) != NULL - && strncmp(from, "From ", 5) == 0) { - folder = (CamelFolder *)1; - /* TODO: if slow mode selected, we could look up unread counts here - - but its pretty expensive */ - } - fclose(fp); - } - } - - if (folder != NULL) { - uri = g_strdup_printf("spoold:%s#%s", root, fname); - fi = camel_folder_info_new(uri, fname, d->d_name, unread); - fi->parent = parent; - fi->sibling = *fip; - *fip = fi; - g_free(uri); - } - - } else if (S_ISDIR(st.st_mode)) { - struct _inode in = { st.st_dev, st.st_ino }; - - /* see if we've visited already */ - if (g_hash_table_lookup(visited, &in) == NULL) { - struct _inode *inew = g_malloc(sizeof(*inew)); - - *inew = in; - g_hash_table_insert(visited, inew, inew); - - if (scan_dir(store, visited, root, fname, flags, parent, fip, ex) == -1) { - g_free(tmp); - g_free(fname); - closedir(dir); - return -1; - } - } - } - g_free(fname); - - } - g_free(tmp); - } - closedir(dir); - - return 0; -} - -static guint inode_hash(const void *d) -{ - const struct _inode *v = d; - - return v->inode ^ v->dnode; -} - -static gboolean inode_equal(const void *a, const void *b) -{ - const struct _inode *v1 = a, *v2 = b; - - return v1->inode == v2->inode && v1->dnode == v2->dnode; -} - -static void inode_free(void *k, void *v, void *d) -{ - g_free(k); -} - -static CamelFolderInfo * -get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex) -{ - CamelFolderInfo *fi = NULL; - GHashTable *visited; - - visited = g_hash_table_new(inode_hash, inode_equal); - - if (scan_dir(store, visited, ((CamelService *)store)->url->path, top, flags, NULL, &fi, ex) == -1 && fi != NULL) { - camel_store_free_folder_info_full(store, fi); - fi = NULL; - } - - g_hash_table_foreach(visited, inode_free, NULL); - g_hash_table_destroy(visited); - - return fi; -} - - - - diff --git a/camel/providers/local/camel-spoold-store.h b/camel/providers/local/camel-spoold-store.h deleted file mode 100644 index 0f80e1bdfb..0000000000 --- a/camel/providers/local/camel-spoold-store.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Authors: Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 2001 Ximian Inc (www.ximian.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * 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 Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - - -#ifndef CAMEL_SPOOLD_STORE_H -#define CAMEL_SPOOLD_STORE_H 1 - - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus }*/ - -#include "camel-spool-store.h" - -#define CAMEL_SPOOLD_STORE_TYPE (camel_spoold_store_get_type ()) -#define CAMEL_SPOOLD_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_SPOOLD_STORE_TYPE, CamelSpoolDStore)) -#define CAMEL_SPOOLD_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_SPOOLD_STORE_TYPE, CamelSpoolDStoreClass)) -#define CAMEL_IS_SPOOLD_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_SPOOLD_STORE_TYPE)) - - -typedef struct { - CamelSpoolStore parent_object; - -} CamelSpoolDStore; - - - -typedef struct { - CamelSpoolStoreClass parent_class; - -} CamelSpoolDStoreClass; - - -/* public methods */ - -/* Standard Camel function */ -CamelType camel_spoold_store_get_type (void); - -const gchar *camel_spoold_store_get_toplevel_dir (CamelSpoolDStore *store); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CAMEL_SPOOLD_STORE_H */ - - |