aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camel/ChangeLog33
-rw-r--r--camel/camel-folder-summary.c93
-rw-r--r--camel/camel-folder-summary.h2
-rw-r--r--camel/camel-folder.c50
-rw-r--r--camel/camel-folder.h10
-rw-r--r--camel/camel-vee-folder.c424
-rw-r--r--camel/providers/imap/camel-imap-command.c2
-rw-r--r--camel/providers/imap/camel-imap-folder.c46
-rw-r--r--camel/providers/imap/camel-imap-search.c45
-rw-r--r--camel/providers/local/camel-local-folder.c44
-rw-r--r--camel/providers/local/camel-spool-folder.c43
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)
{