From 7f4c97c9043af8108c41278814cd4d8745ec85dc Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Tue, 29 Jun 2004 21:22:09 +0000 Subject: Aded a new %formatter 'V' which takes a string vector (needed for SEARCH). 2004-06-29 Jeffrey Stedfast * providers/imap4/camel-imap4-command.c (camel_imap4_command_newv): Aded a new %formatter 'V' which takes a string vector (needed for SEARCH). * providers/imap4/camel-imap4-search.[c,h]: New source files implementing search functionality. * providers/imap4/camel-imap4-folder.c (imap4_sync_flag): Use the new public version of imap4_get_uid_set(). (imap4_transfer_messages_to): Same. (camel_imap4_folder_new): Create a search context. (camel_imap4_folder_finalize): Unref the search context. (camel_imap4_folder_class_init): Override the search methods. (imap4_search_by_expression): New. (imap4_search_by_uids): New. (imap4_search_free): New. * providers/imap4/camel-imap4-utils.c (camel_imap4_get_uid_set): Moved here from camel-imap4-folder.c svn path=/trunk/; revision=26551 --- camel/ChangeLog | 22 ++ camel/providers/imap4/Makefile.am | 2 + camel/providers/imap4/camel-imap4-command.c | 19 ++ camel/providers/imap4/camel-imap4-folder.c | 231 ++++++--------------- camel/providers/imap4/camel-imap4-folder.h | 2 + camel/providers/imap4/camel-imap4-search.c | 309 ++++++++++++++++++++++++++++ camel/providers/imap4/camel-imap4-search.h | 64 ++++++ camel/providers/imap4/camel-imap4-utils.c | 164 +++++++++++++++ camel/providers/imap4/camel-imap4-utils.h | 3 +- 9 files changed, 650 insertions(+), 166 deletions(-) create mode 100644 camel/providers/imap4/camel-imap4-search.c create mode 100644 camel/providers/imap4/camel-imap4-search.h diff --git a/camel/ChangeLog b/camel/ChangeLog index e35319056b..763f858ef1 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,25 @@ +2004-06-29 Jeffrey Stedfast + + * providers/imap4/camel-imap4-command.c + (camel_imap4_command_newv): Aded a new %formatter 'V' which takes + a string vector (needed for SEARCH). + + * providers/imap4/camel-imap4-search.[c,h]: New source files + implementing search functionality. + + * providers/imap4/camel-imap4-folder.c (imap4_sync_flag): Use the + new public version of imap4_get_uid_set(). + (imap4_transfer_messages_to): Same. + (camel_imap4_folder_new): Create a search context. + (camel_imap4_folder_finalize): Unref the search context. + (camel_imap4_folder_class_init): Override the search methods. + (imap4_search_by_expression): New. + (imap4_search_by_uids): New. + (imap4_search_free): New. + + * providers/imap4/camel-imap4-utils.c (camel_imap4_get_uid_set): + Moved here from camel-imap4-folder.c + 2004-06-29 Not Zed * camel-vee-store.c (vee_rename_folder): add any parents of the diff --git a/camel/providers/imap4/Makefile.am b/camel/providers/imap4/Makefile.am index d1bb57e150..aca5e7856b 100644 --- a/camel/providers/imap4/Makefile.am +++ b/camel/providers/imap4/Makefile.am @@ -24,6 +24,8 @@ libcamelimap4_la_SOURCES = \ camel-imap4-folder.c \ camel-imap4-folder.h \ camel-imap4-provider.c \ + camel-imap4-search.c \ + camel-imap4-search.h \ camel-imap4-specials.c \ camel-imap4-specials.h \ camel-imap4-store.c \ diff --git a/camel/providers/imap4/camel-imap4-command.c b/camel/providers/imap4/camel-imap4-command.c index dc3d43c5d4..9b6061dbd4 100644 --- a/camel/providers/imap4/camel-imap4-command.c +++ b/camel/providers/imap4/camel-imap4-command.c @@ -197,6 +197,7 @@ camel_imap4_command_newv (CamelIMAP4Engine *engine, CamelIMAP4Folder *imap4_fold if (ch == '%') { CamelIMAP4Literal *literal; CamelIMAP4Folder *folder; + char *function, **strv; unsigned int u; char *string; size_t len; @@ -262,6 +263,24 @@ camel_imap4_command_newv (CamelIMAP4Engine *engine, CamelIMAP4Folder *imap4_fold g_string_truncate (str, 0); + break; + case 'V': + /* a string vector of arguments which may need to be quoted or made into literals */ + function = str->str + str->len - 2; + while (*function != ' ') + function--; + function++; + + function = g_strdup (function); + + strv = va_arg (args, char **); + for (d = 0; strv[d]; d++) { + if (d > 0) + g_string_append (str, function); + imap4_command_append_string (engine, &tail, str, strv[d]); + } + + g_free (function); break; case 'S': /* string which may need to be quoted or made into a literal */ diff --git a/camel/providers/imap4/camel-imap4-folder.c b/camel/providers/imap4/camel-imap4-folder.c index 0d84408cbc..7175a81476 100644 --- a/camel/providers/imap4/camel-imap4-folder.c +++ b/camel/providers/imap4/camel-imap4-folder.c @@ -50,6 +50,7 @@ #include "camel-imap4-stream.h" #include "camel-imap4-command.h" #include "camel-imap4-summary.h" +#include "camel-imap4-search.h" #define d(x) x @@ -65,6 +66,9 @@ static void imap4_append_message (CamelFolder *folder, CamelMimeMessage *message const CamelMessageInfo *info, char **appended_uid, CamelException *ex); static void imap4_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex); +static GPtrArray *imap4_search_by_expression (CamelFolder *folder, const char *expr, CamelException *ex); +static GPtrArray *imap4_search_by_uids (CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex); +static void imap4_search_free (CamelFolder *folder, GPtrArray *uids); static CamelFolderClass *parent_class = NULL; @@ -102,13 +106,19 @@ camel_imap4_folder_class_init (CamelIMAP4FolderClass *klass) folder_class->get_message = imap4_get_message; folder_class->append_message = imap4_append_message; folder_class->transfer_messages_to = imap4_transfer_messages_to; + folder_class->search_by_expression = imap4_search_by_expression; + folder_class->search_by_uids = imap4_search_by_uids; + folder_class->search_free = imap4_search_free; } static void camel_imap4_folder_init (CamelIMAP4Folder *folder, CamelIMAP4FolderClass *klass) { + ((CamelFolder *) folder)->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY | CAMEL_FOLDER_HAS_SEARCH_CAPABILITY; + folder->utf7_name = NULL; folder->cachedir = NULL; + folder->search = NULL; } static void @@ -116,6 +126,8 @@ camel_imap4_folder_finalize (CamelObject *object) { CamelIMAP4Folder *folder = (CamelIMAP4Folder *) object; + camel_object_unref (folder->search); + g_free (folder->utf7_name); g_free (folder->cachedir); } @@ -280,12 +292,14 @@ camel_imap4_folder_new (CamelStore *store, const char *full_name, CamelException camel_folder_summary_load (folder->summary); + imap_folder->search = camel_imap4_search_new (((CamelIMAP4Store *) store)->engine, imap_folder->cachedir); + if (camel_imap4_engine_select_folder (((CamelIMAP4Store *) store)->engine, folder, ex) == -1) { camel_object_unref (folder); folder = NULL; } - if (camel_imap4_summary_flush_updates (folder->summary, ex) == -1) { + if (folder && camel_imap4_summary_flush_updates (folder->summary, ex) == -1) { camel_object_unref (folder); folder = NULL; } @@ -313,168 +327,6 @@ static struct { { "\\Seen", CAMEL_MESSAGE_SEEN }, }; -struct _uidset_range { - struct _uidset_range *next; - guint32 first, last; - uint8_t buflen; - char buf[24]; -}; - -struct _uidset { - CamelFolderSummary *summary; - struct _uidset_range *ranges; - struct _uidset_range *tail; - size_t maxlen, setlen; -}; - -static void -uidset_range_free (struct _uidset_range *range) -{ - struct _uidset_range *next; - - while (range != NULL) { - next = range->next; - g_free (range); - range = next; - } -} - -static void -uidset_init (struct _uidset *uidset, CamelFolderSummary *summary, size_t maxlen) -{ - uidset->ranges = g_new (struct _uidset_range, 1); - uidset->ranges->first = (guint32) -1; - uidset->ranges->last = (guint32) -1; - uidset->ranges->next = NULL; - uidset->ranges->buflen = 0; - - uidset->tail = uidset->ranges; - uidset->summary = summary; - uidset->maxlen = maxlen; - uidset->setlen = 0; -} - -/* returns: -1 on full-and-not-added, 0 on added-and-not-full or 1 on added-and-full */ -static int -uidset_add (struct _uidset *uidset, CamelMessageInfo *info) -{ - GPtrArray *messages = uidset->summary->messages; - struct _uidset_range *node, *tail = uidset->tail; - const char *iuid = camel_message_info_uid (info); - size_t uidlen, len; - const char *colon; - guint32 index; - - /* Note: depends on integer overflow for initial 'add' */ - for (index = tail->last + 1; index < messages->len; index++) { - if (info == messages->pdata[index]) - break; - } - - g_assert (index < messages->len); - - uidlen = strlen (iuid); - - if (tail->buflen == 0) { - /* first add */ - tail->first = tail->last = index; - strcpy (tail->buf, iuid); - uidset->setlen = uidlen; - tail->buflen = uidlen; - } else if (index == (tail->last + 1)) { - /* add to last range */ - if (tail->last == tail->first) { - /* make sure we've got enough room to add this one... */ - if ((uidset->setlen + uidlen + 1) > uidset->maxlen) - return -1; - - tail->buf[tail->buflen++] = ':'; - uidset->setlen++; - } else { - colon = strchr (tail->buf, ':') + 1; - - len = strlen (colon); - uidset->setlen -= len; - tail->buflen -= len; - } - - strcpy (tail->buf + tail->buflen, iuid); - uidset->setlen += uidlen; - tail->buflen += uidlen; - - tail->last = index; - } else if ((uidset->setlen + uidlen + 1) < uidset->maxlen) { - /* the beginning of a new range */ - tail->next = node = g_new (struct _uidset_range, 1); - node->first = node->last = index; - strcpy (node->buf, iuid); - uidset->setlen += uidlen + 1; - node->buflen = uidlen; - uidset->tail = node; - node->next = NULL; - } else { - /* can't add this one... */ - return -1; - } - - fprintf (stderr, "added uid %s to uidset (summary index = %u)\n", iuid, index); - - if (uidset->setlen < uidset->maxlen) - return 0; - - return 1; -} - -static char * -uidset_to_string (struct _uidset *uidset) -{ - struct _uidset_range *range; - GString *string; - char *str; - - string = g_string_new (""); - - range = uidset->ranges; - while (range != NULL) { - g_string_append (string, range->buf); - range = range->next; - if (range) - g_string_append_c (string, ','); - } - - str = string->str; - g_string_free (string, FALSE); - - return str; -} - -static int -imap4_get_uid_set (CamelIMAP4Engine *engine, CamelFolderSummary *summary, GPtrArray *infos, int cur, size_t linelen, char **set) -{ - struct _uidset uidset; - size_t maxlen; - int rv = 0; - int i; - - if (engine->maxlentype == CAMEL_IMAP4_ENGINE_MAXLEN_LINE) - maxlen = engine->maxlen - linelen; - else - maxlen = engine->maxlen; - - uidset_init (&uidset, summary, maxlen); - - for (i = cur; i < infos->len && rv != 1; i++) { - if ((rv = uidset_add (&uidset, infos->pdata[i])) == -1) - break; - } - - if (i > cur) - *set = uidset_to_string (&uidset); - - uidset_range_free (uidset.ranges); - - return (i - cur); -} static int imap4_sync_flag (CamelFolder *folder, GPtrArray *infos, char onoff, const char *flag, CamelException *ex) @@ -485,7 +337,7 @@ imap4_sync_flag (CamelFolder *folder, GPtrArray *infos, char onoff, const char * char *set = NULL; for (i = 0; i < infos->len; ) { - i += imap4_get_uid_set (engine, folder->summary, infos, i, 30 + strlen (flag), &set); + i += camel_imap4_get_uid_set (engine, folder->summary, infos, i, 30 + strlen (flag), &set); ic = camel_imap4_engine_queue (engine, folder, "UID STORE %s %cFLAGS.SILENT (%s)\r\n", set, onoff, flag); while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) @@ -1060,7 +912,7 @@ imap4_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest dest_namelen = strlen (camel_imap4_folder_utf7_name ((CamelIMAP4Folder *) dest)); for (i = 0; i < infos->len; i += n) { - n = imap4_get_uid_set (engine, src->summary, infos, i, 10 + dest_namelen, &set); + n = camel_imap4_get_uid_set (engine, src->summary, infos, i, 10 + dest_namelen, &set); ic = camel_imap4_engine_queue (engine, src, "UID COPY %s %F\r\n", set, dest); while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) @@ -1124,3 +976,52 @@ imap4_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest CAMEL_SERVICE_LOCK (src->parent_store, connect_lock); } + +static GPtrArray * +imap4_search_by_expression (CamelFolder *folder, const char *expr, CamelException *ex) +{ + CamelIMAP4Folder *imap4_folder = (CamelIMAP4Folder *) folder; + GPtrArray *matches; + + CAMEL_SERVICE_LOCK(folder->parent_store, connect_lock); + + camel_folder_search_set_folder (imap4_folder->search, folder); + matches = camel_folder_search_search (imap4_folder->search, expr, NULL, ex); + + CAMEL_SERVICE_UNLOCK(folder->parent_store, connect_lock); + + return matches; +} + +static GPtrArray * +imap4_search_by_uids (CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex) +{ + CamelIMAP4Folder *imap4_folder = (CamelIMAP4Folder *) folder; + GPtrArray *matches; + + if (uids->len == 0) + return g_ptr_array_new (); + + CAMEL_SERVICE_LOCK(folder->parent_store, connect_lock); + + camel_folder_search_set_folder (imap4_folder->search, folder); + matches = camel_folder_search_search (imap4_folder->search, expr, uids, ex); + + CAMEL_SERVICE_UNLOCK(folder->parent_store, connect_lock); + + return matches; +} + +static void +imap4_search_free (CamelFolder *folder, GPtrArray *uids) +{ + CamelIMAP4Folder *imap4_folder = (CamelIMAP4Folder *) folder; + + g_return_if_fail (imap4_folder->search); + + CAMEL_SERVICE_LOCK(folder->parent_store, connect_lock); + + camel_folder_search_free_result (imap4_folder->search, uids); + + CAMEL_SERVICE_UNLOCK(folder->parent_store, connect_lock); +} diff --git a/camel/providers/imap4/camel-imap4-folder.h b/camel/providers/imap4/camel-imap4-folder.h index 203c80074a..a5d3ce3cc0 100644 --- a/camel/providers/imap4/camel-imap4-folder.h +++ b/camel/providers/imap4/camel-imap4-folder.h @@ -42,6 +42,8 @@ typedef struct _CamelIMAP4FolderClass CamelIMAP4FolderClass; struct _CamelIMAP4Folder { CamelFolder parent_object; + CamelFolderSearch *search; + char *cachedir; char *utf7_name; }; diff --git a/camel/providers/imap4/camel-imap4-search.c b/camel/providers/imap4/camel-imap4-search.c new file mode 100644 index 0000000000..4a23dee83e --- /dev/null +++ b/camel/providers/imap4/camel-imap4-search.c @@ -0,0 +1,309 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * Copyright 2004 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Street #330, Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "camel-imap4-command.h" +#include "camel-imap4-engine.h" +#include "camel-imap4-stream.h" +#include "camel-imap4-utils.h" + +#include "camel-imap4-search.h" + + +static void camel_imap4_search_class_init (CamelIMAP4SearchClass *klass); +static void camel_imap4_search_init (CamelIMAP4Search *search, CamelIMAP4SearchClass *klass); +static void camel_imap4_search_finalize (CamelObject *object); + +static ESExpResult *imap4_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolderSearch *search); + + +static CamelFolderSearchClass *parent_class = NULL; + + +CamelType +camel_imap4_search_get_type (void) +{ + static CamelType type = 0; + + if (!type) { + type = camel_type_register (camel_folder_search_get_type (), + "CamelIMAP4Search", + sizeof (CamelIMAP4Search), + sizeof (CamelIMAP4SearchClass), + (CamelObjectClassInitFunc) camel_imap4_search_class_init, + NULL, + (CamelObjectInitFunc) camel_imap4_search_init, + (CamelObjectFinalizeFunc) camel_imap4_search_finalize); + } + + return type; +} + +static void +camel_imap4_search_class_init (CamelIMAP4SearchClass *klass) +{ + CamelFolderSearchClass *search_class = (CamelFolderSearchClass *) klass; + + parent_class = (CamelFolderSearchClass *) camel_type_get_global_classfuncs (CAMEL_FOLDER_SEARCH_TYPE); + + search_class->body_contains = imap4_body_contains; +} + +static void +camel_imap4_search_init (CamelIMAP4Search *search, CamelIMAP4SearchClass *klass) +{ + search->engine = NULL; +} + +static void +camel_imap4_search_finalize (CamelObject *object) +{ + ; +} + + +CamelFolderSearch * +camel_imap4_search_new (CamelIMAP4Engine *engine, const char *cachedir) +{ + CamelIMAP4Search *search; + + search = (CamelIMAP4Search *) camel_object_new (camel_imap4_search_get_type ()); + camel_folder_search_construct ((CamelFolderSearch *) search); + search->engine = engine; + + return (CamelFolderSearch *) search; +} + + +static int +untagged_search (CamelIMAP4Engine *engine, CamelIMAP4Command *ic, guint32 index, camel_imap4_token_t *token, CamelException *ex) +{ + CamelFolderSummary *summary = ((CamelFolder *) engine->folder)->summary; + GPtrArray *matches = ic->user_data; + CamelMessageInfo *info; + char uid[12]; + + while (1) { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token == '\n') + break; + + if (token->token != CAMEL_IMAP4_TOKEN_NUMBER || token->v.number == 0) + goto unexpected; + + sprintf (uid, "%u", token->v.number); + if ((info = camel_folder_summary_uid (summary, uid))) { + g_ptr_array_add (matches, (char *) camel_message_info_uid (info)); + camel_folder_summary_info_free (summary, info); + } + } + + return 0; + + unexpected: + + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + + return -1; +} + +static ESExpResult * +imap4_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolderSearch *search) +{ + CamelIMAP4Search *imap4_search = (CamelIMAP4Search *) search; + CamelIMAP4Engine *engine = imap4_search->engine; + GPtrArray *strings, *matches, *infos; + register const unsigned char *inptr; + gboolean utf8_search = FALSE; + GPtrArray *summary_set; + CamelMessageInfo *info; + CamelIMAP4Command *ic; + const char *expr; + ESExpResult *r; + int id, i, n; + size_t used; + char *set; + + summary_set = search->summary_set ? search->summary_set : search->summary; + + /* check the simple cases */ + if (argc == 0 || summary_set->len == 0) { + /* match nothing */ + if (search->current) { + r = e_sexp_result_new (f, ESEXP_RES_BOOL); + r->value.bool = FALSE; + } else { + r = e_sexp_result_new (f, ESEXP_RES_ARRAY_PTR); + r->value.ptrarray = g_ptr_array_new (); + } + + return r; + } else if (argc == 1 && argv[0]->type == ESEXP_RES_STRING && argv[0]->value.string[0] == '\0') { + /* match everything */ + if (search->current) { + r = e_sexp_result_new (f, ESEXP_RES_BOOL); + r->value.bool = TRUE; + } else { + r = e_sexp_result_new (f, ESEXP_RES_ARRAY_PTR); + r->value.ptrarray = g_ptr_array_new (); + for (i = 0; i < summary_set->len; i++) { + info = g_ptr_array_index (summary_set, i); + g_ptr_array_add (r->value.ptrarray, (char *) camel_message_info_uid (info)); + } + } + + return r; + } + + strings = g_ptr_array_new (); + for (i = 0; i < argc; i++) { + if (argv[i]->type == ESEXP_RES_STRING && argv[i]->value.string[0] != '\0') { + g_ptr_array_add (strings, argv[i]->value.string); + if (!utf8_search) { + inptr = (unsigned char *) argv[i]->value.string; + while (*inptr != '\0') { + if (!isascii ((int) *inptr)) { + utf8_search = TRUE; + break; + } + + inptr++; + } + } + } + } + + if (strings->len == 0) { + /* match everything */ + g_ptr_array_free (strings, TRUE); + + if (search->current) { + r = e_sexp_result_new (f, ESEXP_RES_BOOL); + r->value.bool = TRUE; + } else { + r = e_sexp_result_new (f, ESEXP_RES_ARRAY_PTR); + r->value.ptrarray = g_ptr_array_new (); + for (i = 0; i < summary_set->len; i++) { + info = g_ptr_array_index (summary_set, i); + g_ptr_array_add (r->value.ptrarray, (char *) camel_message_info_uid (info)); + } + } + + return r; + } + + g_ptr_array_add (strings, NULL); + matches = g_ptr_array_new (); + infos = g_ptr_array_new (); + + if (search->current) { + g_ptr_array_add (infos, search->current); + } else { + for (i = 0; i < summary_set->len; i++) { + info = g_ptr_array_index (summary_set, i); + g_ptr_array_add (infos, info); + } + } + + retry: + if (utf8_search && (engine->capa & CAMEL_IMAP4_CAPABILITY_utf8_search)) + expr = "UID SEARCH CHARSET UTF-8 UID %s BODY %V\r\n"; + else + expr = "UID SEARCH UID %s BODY %V\r\n"; + + used = strlen (expr) + (5 * (strings->len - 2)); + + for (i = 0; i < infos->len; i += n) { + n = camel_imap4_get_uid_set (engine, search->folder->summary, infos, i, used, &set); + + ic = camel_imap4_engine_queue (engine, search->folder, expr, set, strings->pdata); + camel_imap4_command_register_untagged (ic, "SEARCH", untagged_search); + ic->user_data = matches; + g_free (set); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_imap4_command_unref (ic); + goto done; + } + + + if (ic->result == CAMEL_IMAP4_RESULT_NO && utf8_search && (engine->capa & CAMEL_IMAP4_CAPABILITY_utf8_search)) { + int j; + + /* might be because the server is lame and doesn't support UTF-8 */ + for (j = 0; j < ic->resp_codes->len; j++) { + CamelIMAP4RespCode *resp = ic->resp_codes->pdata[j]; + + if (resp->code == CAMEL_IMAP4_RESP_CODE_BADCHARSET) { + engine->capa &= ~CAMEL_IMAP4_CAPABILITY_utf8_search; + camel_imap4_command_unref (ic); + goto retry; + } + } + } + + if (ic->result != CAMEL_IMAP4_RESULT_OK) { + camel_imap4_command_unref (ic); + break; + } + + camel_imap4_command_unref (ic); + } + + done: + + g_ptr_array_free (strings, TRUE); + g_ptr_array_free (infos, TRUE); + + if (search->current) { + const char *uid; + + uid = camel_message_info_uid (search->current); + r = e_sexp_result_new (f, ESEXP_RES_BOOL); + r->value.bool = FALSE; + for (i = 0; i < matches->len; i++) { + if (!strcmp (matches->pdata[i], uid)) { + r->value.bool = TRUE; + break; + } + } + + g_ptr_array_free (matches, TRUE); + } else { + r = e_sexp_result_new (f, ESEXP_RES_ARRAY_PTR); + r->value.ptrarray = matches; + } + + return r; +} diff --git a/camel/providers/imap4/camel-imap4-search.h b/camel/providers/imap4/camel-imap4-search.h new file mode 100644 index 0000000000..5165367cfb --- /dev/null +++ b/camel/providers/imap4/camel-imap4-search.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * Copyright 2004 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Street #330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __CAMEL_IMAP4_SEARCH_H__ +#define __CAMEL_IMAP4_SEARCH_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_IMAP4_SEARCH_TYPE (camel_imap4_search_get_type ()) +#define CAMEL_IMAP4_SEARCH(obj) CAMEL_CHECK_CAST (obj, camel_imap4_search_get_type (), CamelIMAP4Search) +#define CAMEL_IMAP4_SEARCH_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imap4_search_get_type (), CamelIMAP4SearchClass) +#define CAMEL_IS_IMAP4_SEARCH(obj) CAMEL_CHECK_TYPE (obj, camel_imap4_search_get_type ()) + +typedef struct _CamelIMAP4Search CamelIMAP4Search; +typedef struct _CamelIMAP4SearchClass CamelIMAP4SearchClass; + +struct _CamelIMAP4Engine; + +struct _CamelIMAP4Search { + CamelFolderSearch parent_object; + + struct _CamelIMAP4Engine *engine; +}; + +struct _CamelIMAP4SearchClass { + CamelFolderSearchClass parent_class; + +}; + + +CamelType camel_imap4_search_get_type (void); + +CamelFolderSearch *camel_imap4_search_new (struct _CamelIMAP4Engine *engine, const char *cachedir); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_SEARCH_H__ */ diff --git a/camel/providers/imap4/camel-imap4-utils.c b/camel/providers/imap4/camel-imap4-utils.c index 6b8f66c4cd..ecad1d28cf 100644 --- a/camel/providers/imap4/camel-imap4-utils.c +++ b/camel/providers/imap4/camel-imap4-utils.c @@ -74,6 +74,170 @@ camel_imap4_merge_flags (guint32 original, guint32 local, guint32 server) } +struct _uidset_range { + struct _uidset_range *next; + guint32 first, last; + uint8_t buflen; + char buf[24]; +}; + +struct _uidset { + CamelFolderSummary *summary; + struct _uidset_range *ranges; + struct _uidset_range *tail; + size_t maxlen, setlen; +}; + +static void +uidset_range_free (struct _uidset_range *range) +{ + struct _uidset_range *next; + + while (range != NULL) { + next = range->next; + g_free (range); + range = next; + } +} + +static void +uidset_init (struct _uidset *uidset, CamelFolderSummary *summary, size_t maxlen) +{ + uidset->ranges = g_new (struct _uidset_range, 1); + uidset->ranges->first = (guint32) -1; + uidset->ranges->last = (guint32) -1; + uidset->ranges->next = NULL; + uidset->ranges->buflen = 0; + + uidset->tail = uidset->ranges; + uidset->summary = summary; + uidset->maxlen = maxlen; + uidset->setlen = 0; +} + +/* returns: -1 on full-and-not-added, 0 on added-and-not-full or 1 on added-and-full */ +static int +uidset_add (struct _uidset *uidset, CamelMessageInfo *info) +{ + GPtrArray *messages = uidset->summary->messages; + struct _uidset_range *node, *tail = uidset->tail; + const char *iuid = camel_message_info_uid (info); + size_t uidlen, len; + const char *colon; + guint32 index; + + /* Note: depends on integer overflow for initial 'add' */ + for (index = tail->last + 1; index < messages->len; index++) { + if (info == messages->pdata[index]) + break; + } + + g_assert (index < messages->len); + + uidlen = strlen (iuid); + + if (tail->buflen == 0) { + /* first add */ + tail->first = tail->last = index; + strcpy (tail->buf, iuid); + uidset->setlen = uidlen; + tail->buflen = uidlen; + } else if (index == (tail->last + 1)) { + /* add to last range */ + if (tail->last == tail->first) { + /* make sure we've got enough room to add this one... */ + if ((uidset->setlen + uidlen + 1) > uidset->maxlen) + return -1; + + tail->buf[tail->buflen++] = ':'; + uidset->setlen++; + } else { + colon = strchr (tail->buf, ':') + 1; + + len = strlen (colon); + uidset->setlen -= len; + tail->buflen -= len; + } + + strcpy (tail->buf + tail->buflen, iuid); + uidset->setlen += uidlen; + tail->buflen += uidlen; + + tail->last = index; + } else if ((uidset->setlen + uidlen + 1) < uidset->maxlen) { + /* the beginning of a new range */ + tail->next = node = g_new (struct _uidset_range, 1); + node->first = node->last = index; + strcpy (node->buf, iuid); + uidset->setlen += uidlen + 1; + node->buflen = uidlen; + uidset->tail = node; + node->next = NULL; + } else { + /* can't add this one... */ + return -1; + } + + fprintf (stderr, "added uid %s to uidset (summary index = %u)\n", iuid, index); + + if (uidset->setlen < uidset->maxlen) + return 0; + + return 1; +} + +static char * +uidset_to_string (struct _uidset *uidset) +{ + struct _uidset_range *range; + GString *string; + char *str; + + string = g_string_new (""); + + range = uidset->ranges; + while (range != NULL) { + g_string_append (string, range->buf); + range = range->next; + if (range) + g_string_append_c (string, ','); + } + + str = string->str; + g_string_free (string, FALSE); + + return str; +} + +int +camel_imap4_get_uid_set (CamelIMAP4Engine *engine, CamelFolderSummary *summary, GPtrArray *infos, int cur, size_t linelen, char **set) +{ + struct _uidset uidset; + size_t maxlen; + int rv = 0; + int i; + + if (engine->maxlentype == CAMEL_IMAP4_ENGINE_MAXLEN_LINE) + maxlen = engine->maxlen - linelen; + else + maxlen = engine->maxlen; + + uidset_init (&uidset, summary, maxlen); + + for (i = cur; i < infos->len && rv != 1; i++) { + if ((rv = uidset_add (&uidset, infos->pdata[i])) == -1) + break; + } + + if (i > cur) + *set = uidset_to_string (&uidset); + + uidset_range_free (uidset.ranges); + + return (i - cur); +} + + void camel_imap4_utils_set_unexpected_token_error (CamelException *ex, CamelIMAP4Engine *engine, camel_imap4_token_t *token) { diff --git a/camel/providers/imap4/camel-imap4-utils.h b/camel/providers/imap4/camel-imap4-utils.h index 31e2b0a67b..6202c1697e 100644 --- a/camel/providers/imap4/camel-imap4-utils.h +++ b/camel/providers/imap4/camel-imap4-utils.h @@ -38,11 +38,12 @@ void camel_imap4_flags_diff (flags_diff_t *diff, guint32 old, guint32 new); guint32 camel_imap4_flags_merge (flags_diff_t *diff, guint32 flags); guint32 camel_imap4_merge_flags (guint32 original, guint32 local, guint32 server); - struct _CamelIMAP4Engine; struct _CamelIMAP4Command; struct _camel_imap4_token_t; +int camel_imap4_get_uid_set (struct _CamelIMAP4Engine *engine, struct _CamelFolderSummary *summary, GPtrArray *infos, int cur, size_t linelen, char **set); + void camel_imap4_utils_set_unexpected_token_error (CamelException *ex, struct _CamelIMAP4Engine *engine, struct _camel_imap4_token_t *token); int camel_imap4_parse_flags_list (struct _CamelIMAP4Engine *engine, guint32 *flags, CamelException *ex); -- cgit v1.2.3