diff options
-rw-r--r-- | camel/ChangeLog | 33 | ||||
-rw-r--r-- | camel/camel-folder-summary.c | 93 | ||||
-rw-r--r-- | camel/camel-folder-summary.h | 2 | ||||
-rw-r--r-- | camel/camel-folder.c | 50 | ||||
-rw-r--r-- | camel/camel-folder.h | 10 | ||||
-rw-r--r-- | camel/camel-vee-folder.c | 424 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-command.c | 2 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-folder.c | 46 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-search.c | 45 | ||||
-rw-r--r-- | camel/providers/local/camel-local-folder.c | 44 | ||||
-rw-r--r-- | camel/providers/local/camel-spool-folder.c | 43 |
11 files changed, 632 insertions, 160 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index 0f0bf75ebe..ded2ff8b35 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,36 @@ +2001-10-09 <NotZed@Ximian.com> + + * providers/local/camel-spool-folder.c (spool_search_by_uids): + Implement. + + * providers/imap/camel-imap-search.c (imap_body_contains): If + searching a sub-set of the total message count, then use a UID + range to search only specific messages. + + * camel-vee-folder.c (vee_folder_change_match): Removed. + (folder_changed_add_uid): Helper func for changed code. + (folder_changed_remove_uid): " + (folder_changed_change_uid): " + (folder_changed): Rewritten. Supports proper auto-updating of + changes, but not removals till a sync occurs. + (vee_search_by_uids): Implement. + (folder_changed): Changed to call an async threaded function to do + the actual folder updating. + + * camel-folder-summary.c (camel_flag_list_copy): New func to copy + a whole list of flags. + (camel_tag_list_copy): New func to copy a whole list of flags. + + * providers/imap/camel-imap-folder.c (imap_search_by_uids): + Implement. + + * providers/local/camel-local-folder.c (local_search_by_uids): + Implement. + + * camel-folder.c (camel_folder_search_by_uids): New function, + search a subset of uid's. + (search_by_uids): Default impl, return error. + 2001-10-08 Dan Winship <danw@ximian.com> * camel-folder.h (struct _CamelFolder): replace the ever-growing diff --git a/camel/camel-folder-summary.c b/camel/camel-folder-summary.c index 45c2fadae4..a83517ebcd 100644 --- a/camel/camel-folder-summary.c +++ b/camel/camel-folder-summary.c @@ -2150,8 +2150,49 @@ camel_flag_list_free(CamelFlag **list) *list = NULL; } -const char -*camel_tag_get(CamelTag **list, const char *name) +/** + * camel_flag_list_copy: + * @to: + * @from: + * + * Copy a flag list, return true if the destination list @to changed. + * + * Return value: + **/ +gboolean +camel_flag_list_copy(CamelFlag **to, CamelFlag **from) +{ + CamelFlag *flag, *tmp; + int changed = FALSE; + + if (*to == NULL && from == NULL) + return FALSE; + + /* Remove any now-missing flags */ + flag = (CamelFlag *)to; + while (flag->next) { + tmp = flag->next; + if (!camel_flag_get(from, tmp->name)) { + flag->next = tmp->next; + g_free(tmp); + changed = TRUE; + } else { + flag = tmp; + } + } + + /* Add any new flags */ + flag = *from; + while (flag) { + changed |= camel_flag_set(to, flag->name, TRUE); + flag = flag->next; + } + + return changed; +} + +const char * +camel_tag_get(CamelTag **list, const char *name) { CamelTag *tag; @@ -2231,6 +2272,54 @@ int camel_tag_list_size(CamelTag **list) return count; } +static void +rem_tag(char *key, char *value, CamelTag **to) +{ + camel_tag_set(to, key, NULL); +} + +/** + * camel_tag_list_copy: + * @to: + * @from: + * + * Copy a list of tags. + * + * Return value: + **/ +gboolean +camel_tag_list_copy(CamelTag **to, CamelTag **from) +{ + int changed = FALSE; + CamelTag *tag; + GHashTable *left; + + if (*to == NULL && from == NULL) + return FALSE; + + left = g_hash_table_new(g_str_hash, g_str_equal); + tag = *to; + while (tag) { + g_hash_table_insert(left, tag->name, tag); + tag = tag->next; + } + + tag = *from; + while (tag) { + changed |= camel_tag_set(to, tag->name, tag->value); + g_hash_table_remove(left, tag->name); + tag = tag->next; + } + + if (g_hash_table_size(left)>0) { + g_hash_table_foreach(left, (GHFunc)rem_tag, to); + changed = TRUE; + } + g_hash_table_destroy(left); + + return changed; +} + /** * camel_tag_list_free: * @list: diff --git a/camel/camel-folder-summary.h b/camel/camel-folder-summary.h index fddba046b2..5b84135409 100644 --- a/camel/camel-folder-summary.h +++ b/camel/camel-folder-summary.h @@ -270,6 +270,7 @@ int camel_folder_summary_decode_token(FILE *, char **); /* message flag operations */ gboolean camel_flag_get(CamelFlag **list, const char *name); gboolean camel_flag_set(CamelFlag **list, const char *name, gboolean state); +gboolean camel_flag_list_copy(CamelFlag **to, CamelFlag **from); int camel_flag_list_size(CamelFlag **list); void camel_flag_list_free(CamelFlag **list); @@ -279,6 +280,7 @@ gboolean camel_system_flag_get (guint32 flags, const char *name); /* message tag operations */ const char *camel_tag_get(CamelTag **list, const char *name); gboolean camel_tag_set(CamelTag **list, const char *name, const char *value); +gboolean camel_tag_list_copy(CamelTag **to, CamelTag **from); int camel_tag_list_size(CamelTag **list); void camel_tag_list_free(CamelTag **list); diff --git a/camel/camel-folder.c b/camel/camel-folder.c index b8868197b1..e5ae0ba2e5 100644 --- a/camel/camel-folder.c +++ b/camel/camel-folder.c @@ -46,7 +46,7 @@ static CamelObjectClass *parent_class = NULL; /* Returns the class for a CamelFolder */ -#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) +#define CF_CLASS(so) ((CamelFolderClass *)((CamelObject *)(so))->classfuncs) static void camel_folder_finalize (CamelObject *object); @@ -95,11 +95,9 @@ static CamelMessageInfo *get_message_info (CamelFolder *folder, const char *uid) static void free_message_info (CamelFolder *folder, CamelMessageInfo *info); static void ref_message_info (CamelFolder *folder, CamelMessageInfo *info); -static GPtrArray *search_by_expression (CamelFolder *folder, - const char *exp, - CamelException *ex); -static void search_free (CamelFolder * folder, - GPtrArray * result); +static GPtrArray *search_by_expression (CamelFolder *folder, const char *exp, CamelException *ex); +static GPtrArray *search_by_uids (CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex); +static void search_free (CamelFolder * folder, GPtrArray *result); static void copy_messages_to (CamelFolder *source, GPtrArray *uids, @@ -153,6 +151,7 @@ camel_folder_class_init (CamelFolderClass *camel_folder_class) camel_folder_class->get_summary = get_summary; camel_folder_class->free_summary = free_summary; camel_folder_class->search_by_expression = search_by_expression; + camel_folder_class->search_by_uids = search_by_uids; camel_folder_class->search_free = search_free; camel_folder_class->get_message_info = get_message_info; camel_folder_class->ref_message_info = ref_message_info; @@ -1081,6 +1080,45 @@ camel_folder_search_by_expression (CamelFolder *folder, const char *expression, return ret; } +static GPtrArray * +search_by_uids(CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex) +{ + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, + _("Unsupported operation: search by uids: for %s"), + camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); + + w(g_warning ("CamelFolder::search_by_expression not implemented for " + "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)))); + + return NULL; +} + +/** + * camel_folder_search_by_uids: + * @folder: + * @expr: + * @uids: array of uid's to match against. + * @ex: + * + * Search a subset of uid's for an expression match. + * + * Return value: + **/ +GPtrArray * +camel_folder_search_by_uids(CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex) +{ + GPtrArray *ret; + + g_return_val_if_fail(CAMEL_IS_FOLDER (folder), NULL); + g_return_val_if_fail(folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY, NULL); + + /* NOTE: that it is upto the callee to lock */ + + ret = CF_CLASS(folder)->search_by_uids(folder, expr, uids, ex); + + return ret; +} + static void search_free (CamelFolder *folder, GPtrArray *result) { diff --git a/camel/camel-folder.h b/camel/camel-folder.h index e9b15b1306..3d59c2bd7f 100644 --- a/camel/camel-folder.h +++ b/camel/camel-folder.h @@ -136,9 +136,8 @@ typedef struct { gboolean (*has_search_capability) (CamelFolder *folder); - GPtrArray * (*search_by_expression) (CamelFolder *folder, - const char *expression, - CamelException *ex); + GPtrArray * (*search_by_expression) (CamelFolder *, const char *, CamelException *); + GPtrArray * (*search_by_uids) (CamelFolder *, const char *, GPtrArray *uids, CamelException *); void (*search_free) (CamelFolder *folder, GPtrArray *result); @@ -254,9 +253,8 @@ void camel_folder_free_uids (CamelFolder *folder, /* search api */ gboolean camel_folder_has_search_capability (CamelFolder *folder); -GPtrArray * camel_folder_search_by_expression (CamelFolder *folder, - const char *expression, - CamelException *ex); +GPtrArray * camel_folder_search_by_expression (CamelFolder *folder, const char *expr, CamelException *ex); +GPtrArray * camel_folder_search_by_uids (CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex); void camel_folder_search_free (CamelFolder *folder, GPtrArray *); /* summary info */ diff --git a/camel/camel-vee-folder.c b/camel/camel-vee-folder.c index fbee88af2f..bf3a35b278 100644 --- a/camel/camel-vee-folder.c +++ b/camel/camel-vee-folder.c @@ -33,6 +33,7 @@ #include "camel-mime-message.h" #include "camel-folder-search.h" +#include "camel-session.h" #include "camel-vee-store.h" /* for open flags */ #include "camel-private.h" @@ -43,6 +44,8 @@ #endif #define d(x) +extern int camel_verbose_debug; +#define dd(x) (camel_verbose_debug?(x):0) #define _PRIVATE(o) (((CamelVeeFolder *)(o))->priv) @@ -55,6 +58,7 @@ static CamelMimeMessage *vee_get_message (CamelFolder *folder, const gchar *uid, static void vee_move_messages_to(CamelFolder *source, GPtrArray *uids, CamelFolder *dest, CamelException *ex); static GPtrArray *vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex); +static GPtrArray *vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex); static void vee_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set); static void vee_set_message_user_flag (CamelFolder *folder, const char *uid, const char *name, gboolean value); @@ -69,6 +73,9 @@ static void vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source); static void message_changed(CamelFolder *f, const char *uid, CamelVeeFolder *vf); static void folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf); + +static void folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf); + static CamelFolderClass *camel_vee_folder_parent; /* a vfolder for unmatched messages */ @@ -119,6 +126,7 @@ camel_vee_folder_class_init (CamelVeeFolderClass *klass) folder_class->move_messages_to = vee_move_messages_to; folder_class->search_by_expression = vee_search_by_expression; + folder_class->search_by_uids = vee_search_by_uids; folder_class->set_message_flags = vee_set_message_flags; folder_class->set_message_user_flag = vee_set_message_user_flag; @@ -599,6 +607,51 @@ vee_search_by_expression(CamelFolder *folder, const char *expression, CamelExcep return result; } +static GPtrArray * +vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) +{ + GList *node; + GPtrArray *matches, *result = g_ptr_array_new (); + char *expr; + CamelVeeFolder *vf = (CamelVeeFolder *)folder; + struct _CamelVeeFolderPrivate *p = _PRIVATE(vf); + GHashTable *searched = g_hash_table_new(NULL, NULL); + + CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock); + + expr = g_strdup_printf("(and %s %s)", vf->expression, expression); + node = p->folders; + while (node) { + CamelFolder *f = node->data; + int i; + char hash[8]; + + /* make sure we only search each folder once - for unmatched folder to work right */ + if (g_hash_table_lookup(searched, f) == NULL) { + camel_vee_folder_hash_folder(f, hash); + matches = camel_folder_search_by_uids(f, expression, uids, ex); + if (matches) { + for (i = 0; i < matches->len; i++) { + char *uid = matches->pdata[i]; + g_ptr_array_add(result, g_strdup_printf("%.8s%s", hash, uid)); + } + camel_folder_search_free(f, matches); + } else { + g_warning("Search failed: %s", camel_exception_get_description(ex)); + } + g_hash_table_insert(searched, f, f); + } + node = g_list_next(node); + } + + g_free(expr); + CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock); + + g_hash_table_destroy(searched); + + return result; +} + static void vee_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) { @@ -981,187 +1034,276 @@ vee_folder_build_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException */ -/* must be called with summary_lock held */ +/* Hold all these with summary lock and unmatched summary lock held */ static void -vee_folder_change_match(CamelVeeFolder *vf, CamelVeeMessageInfo *vinfo, const CamelMessageInfo *info) +folder_changed_add_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf) { - CamelFlag *flag; - CamelTag *tag; + CamelVeeMessageInfo *vinfo; + const char *vuid; + char *oldkey; + int n; + + vinfo = vee_folder_add_uid(vf, sub, uid, hash); + if (vinfo == NULL) + return; + + vuid = camel_message_info_uid(vinfo); + camel_folder_change_info_add_uid(vf->changes, vuid); - d(printf("changing match %s\n", camel_message_info_uid(vinfo))); + if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) { + if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, (void **)&n)) { + g_hash_table_insert(unmatched_uids, oldkey, (void *)(n+1)); + } else { + g_hash_table_insert(unmatched_uids, g_strdup(vuid), (void *)1); + } + vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid); + if (vinfo) { + camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid); + camel_folder_summary_remove(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)vinfo); + camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo); + } + } +} + +static void +folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf) +{ + CamelFolder *folder = (CamelFolder *)vf; + char *vuid, *oldkey; + int n; + CamelVeeMessageInfo *vinfo; - vinfo->info.flags = info->flags; - camel_flag_list_free(&vinfo->info.user_flags); - flag = info->user_flags; - while (flag) { - camel_flag_set(&vinfo->info.user_flags, flag->name, TRUE); - flag = flag->next; + vuid = g_strdup_printf("%.8s%s", hash, uid); + vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); + if (vinfo) { + camel_folder_change_info_remove_uid(vf->changes, vuid); + camel_folder_summary_remove(folder->summary, (CamelMessageInfo *)vinfo); + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); } - camel_tag_list_free(&vinfo->info.user_tags); - tag = info->user_tags; - while (tag) { - camel_tag_set(&vinfo->info.user_tags, tag->name, tag->value); - tag = tag->next; + + if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) { + if (keep) { + if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, (void **)&n)) { + if (n == 1) { + g_hash_table_remove(unmatched_uids, oldkey); + if (vee_folder_add_uid(folder_unmatched, sub, vuid, hash)) + camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey); + g_free(oldkey); + } else { + g_hash_table_insert(unmatched_uids, oldkey, (void *)(n-1)); + } + } + } else { + if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, (void **)&n)) { + g_hash_table_remove(unmatched_uids, oldkey); + g_free(oldkey); + } + + vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid); + if (vinfo) { + camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid); + camel_folder_summary_remove_uid(((CamelFolder *)folder_unmatched)->summary, vuid); + camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo); + } + } } - camel_folder_change_info_change_uid(vf->changes, camel_message_info_uid(vinfo)); + g_free(vuid); } static void -folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf) +folder_changed_change_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf) { CamelFolder *folder = (CamelFolder *)vf; + char *vuid; + CamelVeeMessageInfo *vinfo; + CamelMessageInfo *info; + + vuid = g_strdup_printf("%.8s%s", hash, uid); + vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); + if (vinfo) { + info = camel_folder_get_message_info(sub, uid); + if (info) { + int changed = FALSE; + + if (vinfo->info.flags != info->flags){ + vinfo->info.flags = info->flags; + changed = TRUE; + } + + changed |= camel_flag_list_copy(&vinfo->info.user_flags, &info->user_flags); + changed |= camel_tag_list_copy(&vinfo->info.user_tags, &info->user_tags); + if (changed) + camel_folder_change_info_change_uid(vf->changes, camel_message_info_uid(vinfo)); + camel_folder_free_message_info(sub, info); + } else { + folder_changed_remove_uid(sub, uid, hash, FALSE, vf); + } + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); + } + g_free(vuid); +} + +struct _folder_changed_msg { +#ifdef ENABLE_THREADS + CamelSessionThreadMsg msg; +#endif + CamelFolderChangeInfo *changes; + CamelFolder *sub; + CamelVeeFolder *vf; +}; + +static void +folder_changed_change(CamelSession *session, CamelSessionThreadMsg *msg) +{ + struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg; + CamelFolder *sub = m->sub; + CamelFolder *folder = (CamelFolder *)m->vf; + CamelVeeFolder *vf = m->vf; + CamelFolderChangeInfo *changes = m->changes; char *vuid, hash[8]; CamelVeeMessageInfo *vinfo; int i; - CamelMessageInfo *info; - char *oldkey; - int n; CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL; + GPtrArray *matches; + GHashTable *matches_hash; camel_vee_folder_hash_folder(sub, hash); - /* if not auto-updating, only propagate changed/removed events, not added items */ - if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) { - - CAMEL_VEE_FOLDER_LOCK(vf, changed_lock); - /* add this folder to our changed folders list if we have stuff we can't catch easily */ - /* Unfortuantely if its a change that doesn't affect the match, we're still going to - rerun it :( */ - if (changes->uid_changed->len > 0 || changes->uid_added->len > 0) - if (g_list_find(vf->priv->folders_changed, sub) != NULL) - vf->priv->folders_changed = g_list_prepend(vf->priv->folders_changed, sub); + CAMEL_VEE_FOLDER_LOCK(vf, summary_lock); + CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock); - CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock); + dd(printf("Vfolder '%s' subfolder changed '%s'\n", folder->full_name, sub->full_name)); + dd(printf(" changed %d added %d removed %d\n", changes->uid_changed->len, changes->uid_added->len, changes->uid_removed->len)); - CAMEL_VEE_FOLDER_LOCK(vf, summary_lock); - CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock); + /* Always remove removed uid's, in any case */ + for (i=0;i<changes->uid_removed->len;i++) { + dd(printf(" removing uid '%s'\n", (char *)changes->uid_removed->pdata[i])); + folder_changed_remove_uid(sub, changes->uid_removed->pdata[i], hash, FALSE, vf); + } + + /* Always add any new uid's, if they match */ + dd(printf(" Searching for added matches '%s'\n", vf->expression)); + if (changes->uid_added->len > 0 + && (matches = camel_folder_search_by_uids(sub, vf->expression, changes->uid_added, NULL))) { + for (i=0;i<matches->len;i++) { + dd(printf(" adding uid '%s' [newly matched]\n", (char *)matches->pdata[i])); + folder_changed_add_uid(sub, matches->pdata[i], hash, vf); + } + camel_folder_search_free(sub, matches); + } + if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) { + /* If we are not auto-updating, just change changed uids */ + dd(printf(" Not auto-update\n")); for (i=0;i<changes->uid_changed->len;i++) { - info = camel_folder_get_message_info(sub, changes->uid_changed->pdata[i]); - vuid = g_strdup_printf("%.8s%s", hash, (char *)changes->uid_changed->pdata[i]); - vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); - if (vinfo && info) - vee_folder_change_match(vf, vinfo, info); - g_free(vuid); - if (info) - camel_folder_free_message_info(sub, info); - if (vinfo) - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); + printf(" changed uid '%s'\n", (char *)changes->uid_changed->pdata[i]); + folder_changed_change_uid(sub, changes->uid_changed->pdata[i], hash, vf); } - - for (i=0;i<changes->uid_removed->len;i++) { - vuid = g_strdup_printf("%.8s%s", hash, (char *)changes->uid_removed->pdata[i]); + } else if ((matches = camel_folder_search_by_uids(sub, vf->expression, changes->uid_changed, NULL))) { + /* If we are auto-updating, then re-check changed uids still match */ + dd(printf(" Vfolder auto-update\n")); + matches_hash = g_hash_table_new(g_str_hash, g_str_equal); + for (i=0;i<matches->len;i++) + g_hash_table_insert(matches_hash, matches->pdata[i], matches->pdata[i]); + for (i=0;i<changes->uid_changed->len;i++) { + vuid = g_strdup_printf("%.8s%s", hash, (char *)changes->uid_changed->pdata[i]); vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); - if (vinfo) { - camel_folder_change_info_remove_uid(vf->changes, vuid); - camel_folder_summary_remove(folder->summary, (CamelMessageInfo *)vinfo); - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); - - if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) { - if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, (void **)&n)) { - g_hash_table_remove(unmatched_uids, oldkey); - g_free(oldkey); - } - camel_folder_summary_remove_uid(((CamelFolder *)folder_unmatched)->summary, vuid); + if (vinfo == NULL) { + /* A uid we dont have, but now it matches, add it */ + if (g_hash_table_lookup(matches_hash, changes->uid_changed->pdata[i])) { + dd(printf(" adding uid '%s' [newly matched]\n", (char *)changes->uid_changed->pdata[i])); + folder_changed_add_uid(sub, changes->uid_changed->pdata[i], hash, vf); } - + } else { + if (g_hash_table_lookup(matches_hash, changes->uid_changed->pdata[i])) { + /* still match, change event, (if it changed) */ + dd(printf(" changing uid '%s' [still matches]\n", (char *)changes->uid_changed->pdata[i])); + folder_changed_change_uid(sub, changes->uid_changed->pdata[i], hash, vf); + } else { + /* No longer matches, remove it, but keep it in unmatched (potentially) */ + dd(printf(" removing uid '%s' [did match]\n", (char *)changes->uid_changed->pdata[i])); + folder_changed_remove_uid(sub, changes->uid_changed->pdata[i], hash, TRUE, vf); + } + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); } g_free(vuid); } + g_hash_table_destroy(matches_hash); + camel_folder_search_free(sub, matches); + } - if (camel_folder_change_info_changed(folder_unmatched->changes)) { - unmatched_changes = folder_unmatched->changes; - folder_unmatched->changes = camel_folder_change_info_new(); - } + if (camel_folder_change_info_changed(folder_unmatched->changes)) { + unmatched_changes = folder_unmatched->changes; + folder_unmatched->changes = camel_folder_change_info_new(); + } - if (camel_folder_change_info_changed(vf->changes)) { - vf_changes = vf->changes; - vf->changes = camel_folder_change_info_new(); - } + if (camel_folder_change_info_changed(vf->changes)) { + vf_changes = vf->changes; + vf->changes = camel_folder_change_info_new(); + } - CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock); - CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock); + CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock); + CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock); - if (unmatched_changes) { - camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes); - camel_folder_change_info_free(unmatched_changes); - } - - if (vf_changes) { - camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes); - camel_folder_change_info_free(vf_changes); + if (unmatched_changes) { + camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes); + camel_folder_change_info_free(unmatched_changes); + } + + if (vf_changes) { + /* If not auto-updating, keep track of changed folders for later re-sync */ + if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) { + CAMEL_VEE_FOLDER_LOCK(vf, changed_lock); + if (g_list_find(vf->priv->folders_changed, sub) != NULL) + vf->priv->folders_changed = g_list_prepend(vf->priv->folders_changed, sub); + CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock); } - return; + camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes); + camel_folder_change_info_free(vf_changes); } +} - /* if we are autoupdating, then do the magic */ - /* FIXME: This should be optimised to be incremental, but its just too much work right now to validate it */ - vee_folder_build_folder(vf, sub, NULL); +static void +folder_changed_free(CamelSession *session, CamelSessionThreadMsg *msg) +{ + struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg; -#if 0 - /* assume its faster to search a long list in whole, than by part */ - if (changes && (changes->uid_added->len + changes->uid_changed->len) < 500) { - gboolean match; - - /* FIXME: We dont search body contents with this search, so, it isn't as - useful as it might be. - We shold probably just perform a whole search if we need to, i.e. there - are added items. Changed items we are unlikely to want to remove immediately - anyway, although I guess it might be useful. - Removed items can always just be removed. - */ - - /* see if added ones now match us */ - for (i=0;i<changes->uid_added->len;i++) { - info = camel_folder_get_message_info(sub, changes->uid_added->pdata[i]); - if (info) { - camel_folder_search_set_folder(vf->search, sub); - match = camel_folder_search_match_expression(vf->search, vf->expression, info, NULL); - if (match) - vinfo = vee_folder_add_change(vf, sub, info); - camel_folder_free_message_info(sub, info); - } - } + camel_folder_change_info_free(m->changes); + camel_object_unref((CamelObject *)m->vf); + camel_object_unref((CamelObject *)m->sub); +} - /* check if changed ones still match */ - for (i=0;i<changes->uid_changed->len;i++) { - info = camel_folder_get_message_info(sub, changes->uid_changed->pdata[i]); - vuid = g_strdup_printf("%p:%s", sub, (char *)changes->uid_changed->pdata[i]); - vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); - if (info) { - camel_folder_search_set_folder(vf->search, sub); - match = camel_folder_search_match_expression(vf->search, vf->expression, info, NULL); - if (vinfo) { - if (!match) - vfolder_remove_match(vf, vinfo); - else - vfolder_change_match(vf, vinfo, info); - } else if (match) - vee_folder_add_change(vf, sub, info); - camel_folder_free_message_info(sub, info); - } else if (vinfo) - vfolder_remove_match(vf, vinfo); - - if (vinfo) - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); +#ifdef ENABLE_THREADS +static CamelSessionThreadOps folder_changed_ops = { + folder_changed_change, + folder_changed_free, +}; +#endif - g_free(vuid); - } +static void +folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf) +{ + struct _folder_changed_msg *m; + CamelSession *session = ((CamelService *)((CamelFolder *)vf)->parent_store)->session; - /* mirror removes directly, if they used to match */ - for (i=0;i<changes->uid_removed->len;i++) { - vuid = g_strdup_printf("%p:%s", sub, (char *)changes->uid_removed->pdata[i]); - vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); - if (vinfo) { - vfolder_remove_match(vf, vinfo); - camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); - } - g_free(vuid); - } - } else { - vee_folder_build_folder(vf, sub, NULL); - } +#ifdef ENABLE_THREADS + m = camel_session_thread_msg_new(session, &folder_changed_ops, sizeof(*m)); + m->changes = camel_folder_change_info_new(); + camel_folder_change_info_cat(m->changes, changes); + m->sub = sub; + camel_object_ref((CamelObject *)sub); + m->vf = vf; + camel_object_ref((CamelObject *)vf); + camel_session_thread_queue(session, &m->msg, 0); +#else + m = g_malloc(sizeof(*m)); + m->changes = changes; + m->sub = sub; + m->vf = vf; + folder_changed_change(session, &m->msg); + folder_changed_free(&m->msg); + g_free(m); #endif } diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c index 64095b320d..141ae19ae5 100644 --- a/camel/providers/imap/camel-imap-command.c +++ b/camel/providers/imap/camel-imap-command.c @@ -678,7 +678,7 @@ imap_command_strdup_vprintf (CamelImapStore *store, const char *fmt, len += arglen * 2; start = p + 1; break; - + case '%': start = p; break; diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index b17ffb55cd..5fe517f4ef 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -97,6 +97,7 @@ static void imap_move_messages_to (CamelFolder *source, GPtrArray *uids, /* searching */ static GPtrArray *imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex); +static GPtrArray *imap_search_by_uids (CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex); static void imap_search_free (CamelFolder *folder, GPtrArray *uids); static void imap_thaw (CamelFolder *folder); @@ -115,6 +116,7 @@ camel_imap_folder_class_init (CamelImapFolderClass *camel_imap_folder_class) camel_folder_class->get_message = imap_get_message; camel_folder_class->move_messages_to = imap_move_messages_to; camel_folder_class->search_by_expression = imap_search_by_expression; + camel_folder_class->search_by_uids = imap_search_by_uids; camel_folder_class->search_free = imap_search_free; camel_folder_class->thaw = imap_thaw; @@ -1301,6 +1303,50 @@ imap_search_by_expression (CamelFolder *folder, const char *expression, CamelExc return matches; } +static GPtrArray * +imap_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) +{ + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER(folder); + GPtrArray *summary, *matches; + int i; + + if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (folder->parent_store), ex)) + return NULL; + + /* 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_IMAP_FOLDER_LOCK(folder, search_lock); + + if (imap_folder->search == NULL) + imap_folder->search = camel_imap_search_new(); + + camel_folder_search_set_folder(imap_folder->search, folder); + camel_folder_search_set_summary(imap_folder->search, summary); + + matches = camel_folder_search_execute_expression(imap_folder->search, expression, ex); + + CAMEL_IMAP_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 imap_search_free (CamelFolder *folder, GPtrArray *uids) { diff --git a/camel/providers/imap/camel-imap-search.c b/camel/providers/imap/camel-imap-search.c index 3c96449963..0418a34617 100644 --- a/camel/providers/imap/camel-imap-search.c +++ b/camel/providers/imap/camel-imap-search.c @@ -34,6 +34,7 @@ #include "camel-imap-store.h" #include "camel-imap-search.h" #include "camel-imap-private.h" +#include "camel-imap-utils.h" static ESExpResult * imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, @@ -67,6 +68,21 @@ camel_imap_search_get_type (void) return camel_imap_search_type; } +static int +cmp_uid(const void *ap, const void *bp) +{ + unsigned int a, b; + + a = strtoul(((char **)ap)[0], NULL, 10); + b = strtoul(((char **)bp)[0], NULL, 10); + if (a<b) + return -1; + else if (a>b) + return 1; + + return 0; +} + static ESExpResult * imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolderSearch *s) @@ -79,6 +95,9 @@ imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, ESExpResult *r; CamelMessageInfo *info; GHashTable *uid_hash = NULL; + char *set; + GPtrArray *sorted; + int i; if (s->current) { uid = camel_message_info_uid (s->current); @@ -100,11 +119,29 @@ imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, g_ptr_array_add (r->value.ptrarray, (char *)camel_message_info_uid (info)); } } else { - /* FIXME: danw: what if we have multiple string args? */ + /* If searching a (reasonably small) subset of + the real folder size, then use a + message-set to optimise it */ + /* TODO: This peeks a bunch of 'private'ish data */ + if (s->summary->len < camel_folder_get_message_count(s->folder)/2) { + sorted = g_ptr_array_new(); + g_ptr_array_set_size(sorted, s->summary->len); + for (i=0;i<s->summary->len;i++) + sorted->pdata[i] = (void *)camel_message_info_uid((CamelMessageInfo *)s->summary->pdata[i]); + qsort(sorted->pdata, sorted->len, sizeof(sorted->pdata[0]), cmp_uid); + set = imap_uid_array_to_set(s->folder->summary, sorted); + response = camel_imap_command (store, s->folder, NULL, + "UID SEARCH UID %s BODY \"%s\"", + set, value); + g_free(set); + g_ptr_array_free(sorted, TRUE); + } else { + response = camel_imap_command (store, s->folder, NULL, + "UID SEARCH BODY \"%s\"", + value); + } + r->value.ptrarray = g_ptr_array_new (); - response = camel_imap_command (store, s->folder, NULL, - "UID SEARCH BODY \"%s\"", - value); } } diff --git a/camel/providers/local/camel-local-folder.c b/camel/providers/local/camel-local-folder.c index 43622eb23a..27b49a5c70 100644 --- a/camel/providers/local/camel-local-folder.c +++ b/camel/providers/local/camel-local-folder.c @@ -62,6 +62,7 @@ static void local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex static void local_expunge(CamelFolder *folder, CamelException *ex); static GPtrArray *local_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex); +static GPtrArray *local_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex); static void local_search_free(CamelFolder *folder, GPtrArray * result); static void local_finalize(CamelObject * object); @@ -80,6 +81,7 @@ camel_local_folder_class_init(CamelLocalFolderClass * camel_local_folder_class) camel_folder_class->expunge = local_expunge; camel_folder_class->search_by_expression = local_search_by_expression; + camel_folder_class->search_by_uids = local_search_by_uids; camel_folder_class->search_free = local_search_free; camel_local_folder_class->lock = local_lock; @@ -334,6 +336,48 @@ local_search_by_expression(CamelFolder *folder, const char *expression, CamelExc return matches; } +static GPtrArray * +local_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) +{ + CamelLocalFolder *local_folder = CAMEL_LOCAL_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_LOCAL_FOLDER_LOCK(folder, search_lock); + + if (local_folder->search == NULL) + local_folder->search = camel_folder_search_new(); + + camel_folder_search_set_folder(local_folder->search, folder); + camel_folder_search_set_body_index(local_folder->search, local_folder->index); + camel_folder_search_set_summary(local_folder->search, summary); + + matches = camel_folder_search_execute_expression(local_folder->search, expression, ex); + + CAMEL_LOCAL_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 local_search_free(CamelFolder *folder, GPtrArray * result) { diff --git a/camel/providers/local/camel-spool-folder.c b/camel/providers/local/camel-spool-folder.c index bc715e46bc..9e487dfa89 100644 --- a/camel/providers/local/camel-spool-folder.c +++ b/camel/providers/local/camel-spool-folder.c @@ -64,6 +64,7 @@ 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, CamelException *ex); @@ -87,6 +88,7 @@ camel_spool_folder_class_init(CamelSpoolFolderClass * camel_spool_folder_class) camel_folder_class->expunge = spool_expunge; 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; /* virtual method overload */ @@ -363,6 +365,47 @@ spool_search_by_expression(CamelFolder *folder, const char *expression, CamelExc 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) { |