diff options
author | Jeffrey Stedfast <fejj@ximian.com> | 2004-03-29 02:15:17 +0800 |
---|---|---|
committer | Jeffrey Stedfast <fejj@src.gnome.org> | 2004-03-29 02:15:17 +0800 |
commit | ad297b39d5335488629bec1517174d7d3b77efc0 (patch) | |
tree | 80ae03f524d89db713de476fd713bcc551c94df9 /camel | |
parent | cdb72d1e4b1d907f93d2c8beab579104b6e65f3b (diff) | |
download | gsoc2013-evolution-ad297b39d5335488629bec1517174d7d3b77efc0.tar gsoc2013-evolution-ad297b39d5335488629bec1517174d7d3b77efc0.tar.gz gsoc2013-evolution-ad297b39d5335488629bec1517174d7d3b77efc0.tar.bz2 gsoc2013-evolution-ad297b39d5335488629bec1517174d7d3b77efc0.tar.lz gsoc2013-evolution-ad297b39d5335488629bec1517174d7d3b77efc0.tar.xz gsoc2013-evolution-ad297b39d5335488629bec1517174d7d3b77efc0.tar.zst gsoc2013-evolution-ad297b39d5335488629bec1517174d7d3b77efc0.zip |
New source files implementing the CamelFolderSummary class for the new
2004-03-28 Jeffrey Stedfast <fejj@ximian.com>
* providers/imap4/camel-imap-summary.[c,h]: New source files
implementing the CamelFolderSummary class for the new IMAP4
implementation.
svn path=/trunk/; revision=25209
Diffstat (limited to 'camel')
-rw-r--r-- | camel/ChangeLog | 6 | ||||
-rw-r--r-- | camel/providers/imap4/Makefile.am | 2 | ||||
-rw-r--r-- | camel/providers/imap4/camel-imap-summary.c | 1107 | ||||
-rw-r--r-- | camel/providers/imap4/camel-imap-summary.h | 91 |
4 files changed, 1206 insertions, 0 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index 3b4135b468..adf5c82cde 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,9 @@ +2004-03-28 Jeffrey Stedfast <fejj@ximian.com> + + * providers/imap4/camel-imap-summary.[c,h]: New source files + implementing the CamelFolderSummary class for the new IMAP4 + implementation. + 2004-03-26 Jeffrey Stedfast <fejj@ximian.com> * providers/imap/camel-imap-folder.c (imap_update_summary): diff --git a/camel/providers/imap4/Makefile.am b/camel/providers/imap4/Makefile.am index c07f943e84..5d75e9abce 100644 --- a/camel/providers/imap4/Makefile.am +++ b/camel/providers/imap4/Makefile.am @@ -25,6 +25,8 @@ libcamelimap4_la_SOURCES = \ camel-imap-specials.h \ camel-imap-stream.c \ camel-imap-stream.h \ + camel-imap-summary.c \ + camel-imap-summary.h \ camel-imap-utils.c \ camel-imap-utils.h diff --git a/camel/providers/imap4/camel-imap-summary.c b/camel/providers/imap4/camel-imap-summary.c new file mode 100644 index 0000000000..d97c93b66c --- /dev/null +++ b/camel/providers/imap4/camel-imap-summary.c @@ -0,0 +1,1107 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* Camel + * Copyright (C) 1999-2004 Jeffrey Stedfast + * + * 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 <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <limits.h> +#include <utime.h> +#include <fcntl.h> +#include <ctype.h> + +#include <e-util/md5-utils.h> + +#include <camel/camel-file-utils.h> + +#include "camel-imap-store.h" +#include "camel-imap-engine.h" +#include "camel-imap-folder.h" +#include "camel-imap-stream.h" +#include "camel-imap-command.h" +#include "camel-imap-utils.h" + +#include "camel-imap-summary.h" + +#define IMAP_SUMMARY_VERSION 1 + +static void camel_imap_summary_class_init (CamelIMAPSummaryClass *klass); +static void camel_imap_summary_init (CamelIMAPSummary *summary, CamelIMAPSummaryClass *klass); +static void camel_imap_summary_finalize (CamelObject *object); + +static int imap_header_load (CamelFolderSummary *summary, FILE *fin); +static int imap_header_save (CamelFolderSummary *summary, FILE *fout); +static CamelMessageInfo *imap_message_info_new (CamelFolderSummary *summary, struct _camel_header_raw *header); +static CamelMessageInfo *imap_message_info_load (CamelFolderSummary *summary, FILE *fin); +static int imap_message_info_save (CamelFolderSummary *summary, FILE *fout, CamelMessageInfo *info); + + +static CamelFolderSummaryClass *parent_class = NULL; + + +CamelType +camel_imap_summary_get_type (void) +{ + static CamelType type = 0; + + if (!type) { + type = camel_type_register (CAMEL_TYPE_IMAP_SUMMARY, + "CamelIMAPSummary", + sizeof (CamelIMAPSummary), + sizeof (CamelIMAPSummaryClass), + (CamelObjectClassInitFunc) camel_imap_summary_class_init, + NULL, + (CamelObjectInitFunc) camel_imap_summary_init, + (CamelObjectFinalizeFunc) camel_imap_summary_finalize); + } + + return type; +} + + +static void +camel_imap_summary_class_init (CamelIMAPSummaryClass *klass) +{ + CamelFolderSummaryClass *summary_class = (CamelFolderSummaryClass *) klass; + + parent_class = (CamelFolderSummaryClass *) camel_type_get_global_classfuncs (camel_folder_summary_get_type ()); + + summary_class->summary_header_load = imap_header_load; + summary_class->summary_header_save = imap_header_save; + summary_class->message_info_new = imap_message_info_new; + summary_class->message_info_load = imap_message_info_load; + summary_class->message_info_save = imap_message_info_save; +} + +static void +camel_imap_summary_init (CamelIMAPSummary *summary, CamelIMAPSummaryClass *klass) +{ + CamelFolderSummary *folder_summary = (CamelFolderSummary *) summary; + + folder_summary->version += IMAP_SUMMARY_VERSION; + folder_summary->flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_DELETED | + CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN; + + folder_summary->message_info_size = sizeof (CamelIMAPMessageInfo); +} + +static void +camel_imap_summary_finalize (CamelObject *object) +{ + ; +} + + +CamelFolderSummary * +camel_imap_summary_new (CamelFolder *folder) +{ + CamelFolderSummary *summary; + + summary = (CamelFolderSummary *) camel_object_new (CAMEL_TYPE_IMAP_SUMMARY); + ((CamelIMAPSummary *) summary)->folder = folder; + + return summary; +} + +static int +imap_header_load (CamelFolderSummary *summary, FILE *fin) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + + if (CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->summary_header_load (summary, fin) == -1) + return -1; + + if (camel_file_util_decode_uint32 (fin, &imap_summary->uidvalidity) == -1) + return -1; + + return 0; +} + +static int +imap_header_save (CamelFolderSummary *summary, FILE *fout) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + + if (CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->summary_header_save (summary, fout) == -1) + return -1; + + if (camel_file_util_encode_uint32 (fout, imap_summary->uidvalidity) == -1) + return -1; + + return 0; +} + +static int +envelope_decode_address (CamelIMAPEngine *engine, GString *addrs, CamelException *ex) +{ + camel_imap_token_t token; + gboolean had_name = FALSE; + int part = 0; + + if (camel_imap_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token == CAMEL_IMAP_TOKEN_NIL) { + return 0; + } else if (token.token != '(') { + camel_imap_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + if (addrs->len > 0) + g_string_append (addrs, ", "); + + do { + if (camel_imap_engine_next_token (engine, &token, ex) == -1) + return -1; + + switch (token.token) { + case CAMEL_IMAP_TOKEN_NIL: + break; + case CAMEL_IMAP_TOKEN_ATOM: + case CAMEL_IMAP_TOKEN_QSTRING: + switch (part) { + case 0: + g_string_append_printf (addrs, "\"%s\" <", token.v.qstring); + had_name = TRUE; + break; + case 2: + g_string_append (addrs, token.v.qstring); + break; + case 3: + g_string_append_printf (addrs, "@%s%s", token.v.qstring, had_name ? ">" : ""); + break; + } + break; + default: + camel_imap_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + part++; + } while (part < 4); + + if (camel_imap_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != ')') { + camel_imap_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + return 0; +} + +static int +envelope_decode_addresses (CamelIMAPEngine *engine, char **addrlist, CamelException *ex) +{ + camel_imap_token_t token; + GString *addrs; + + if (camel_imap_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token == CAMEL_IMAP_TOKEN_NIL) { + *addrlist = NULL; + return 0; + } else if (token.token != '(') { + camel_imap_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + addrs = g_string_new (""); + + do { + if (camel_imap_engine_next_token (engine, &token, ex) == -1) { + g_string_free (addrs, TRUE); + return -1; + } + + if (token.token == '(') { + camel_imap_stream_unget_token (engine->istream, &token); + + if (envelope_decode_address (engine, addrs, ex) == -1) { + g_string_free (addrs, TRUE); + return -1; + } + } else if (token.token == ')') { + break; + } else { + camel_imap_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + } while (1); + + *addrlist = addrs->str; + g_string_free (addrs, FALSE); + + return 0; +} + +static int +envelope_decode_date (CamelIMAPEngine *engine, time_t *date, CamelException *ex) +{ + camel_imap_token_t token; + const char *nstring; + + if (camel_imap_engine_next_token (engine, &token, ex) == -1) + return -1; + + switch (token.token) { + case CAMEL_IMAP_TOKEN_NIL: + *date = (time_t) -1; + return 0; + case CAMEL_IMAP_TOKEN_ATOM: + nstring = token.v.atom; + break; + case CAMEL_IMAP_TOKEN_QSTRING: + nstring = token.v.qstring; + break; + default: + camel_imap_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + *date = camel_header_decode_date (nstring, NULL); + + return 0; +} + +static int +envelope_decode_nstring (CamelIMAPEngine *engine, char **nstring, CamelException *ex) +{ + camel_imap_token_t token; + + if (camel_imap_engine_next_token (engine, &token, ex) == -1) + return -1; + + switch (token.token) { + case CAMEL_IMAP_TOKEN_NIL: + *nstring = NULL; + break; + case CAMEL_IMAP_TOKEN_ATOM: + *nstring = g_strdup (token.v.atom); + break; + case CAMEL_IMAP_TOKEN_QSTRING: + *nstring = g_strdup (token.v.qstring); + break; + default: + camel_imap_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + return 0; +} + +static CamelSummaryReferences * +decode_references (const char *string) +{ + struct _camel_header_references *refs, *r; + CamelSummaryReferences *references; + unsigned char md5sum[16]; + guint32 i, n = 0; + MD5Context md5; + + if (!(r = refs = camel_header_references_inreplyto_decode (string))) + return NULL; + + while (r != NULL) { + r = r->next; + n++; + } + + references = g_malloc (sizeof (CamelSummaryReferences) + (sizeof (CamelSummaryMessageID) * (n - 1))); + references->size = n; + + for (i = 0, r = refs; i < n; i++, r = r->next) { + md5_init (&md5); + md5_update (&md5, r->id, strlen (r->id)); + md5_final (&md5, md5sum); + memcpy (references->references[i].id.hash, md5sum, sizeof (references->references[i].id.hash)); + } + + camel_header_references_list_clear (&refs); + + return references; +} + +static int +decode_envelope (CamelIMAPEngine *engine, CamelMessageInfo *info, camel_imap_token_t *token, CamelException *ex) +{ + unsigned char md5sum[16]; + char *nstring; + + if (camel_imap_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token != '(') { + camel_imap_utils_set_unexpected_token_error (ex, engine, token); + return -1; + } + + if (envelope_decode_date (engine, &info->date_sent, ex) == -1) + goto exception; + + /* subject */ + if (envelope_decode_nstring (engine, &nstring, ex) == -1) + goto exception; + camel_message_info_set_subject (info, nstring); + + /* from */ + if (envelope_decode_addresses (engine, &nstring, ex) == -1) + goto exception; + camel_message_info_set_from (info, nstring); + + /* sender */ + if (envelope_decode_addresses (engine, &nstring, ex) == -1) + goto exception; + g_free (nstring); + + /* reply-to */ + if (envelope_decode_addresses (engine, &nstring, ex) == -1) + goto exception; + g_free (nstring); + + /* to */ + if (envelope_decode_addresses (engine, &nstring, ex) == -1) + goto exception; + camel_message_info_set_to (info, nstring); + + /* cc */ + if (envelope_decode_addresses (engine, &nstring, ex) == -1) + goto exception; + camel_message_info_set_cc (info, nstring); + + /* bcc */ + if (envelope_decode_addresses (engine, &nstring, ex) == -1) + goto exception; + g_free (nstring); + + /* in-reply-to */ + if (envelope_decode_nstring (engine, &nstring, ex) == -1) + goto exception; + + if (nstring != NULL) { + info->references = decode_references (nstring); + g_free (nstring); + } + + /* message-id */ + if (envelope_decode_nstring (engine, &nstring, ex) == -1) + goto exception; + + if (nstring != NULL) { + md5_get_digest (nstring, strlen (nstring), md5sum); + memcpy (info->message_id.id.hash, md5sum, sizeof (info->message_id.id.hash)); + g_free (nstring); + } + + if (camel_imap_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token != ')') { + camel_imap_utils_set_unexpected_token_error (ex, engine, token); + goto exception; + } + + return 0; + + exception: + + return -1; +} + +static char *tm_months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static gboolean +decode_time (const char **in, int *hour, int *min, int *sec) +{ + register const unsigned char *inptr = (const unsigned char *) *in; + int *val, colons = 0; + + *hour = *min = *sec = 0; + + val = hour; + for ( ; *inptr && !isspace ((int) *inptr); inptr++) { + if (*inptr == ':') { + colons++; + switch (colons) { + case 1: + val = min; + break; + case 2: + val = sec; + break; + default: + return FALSE; + } + } else if (!isdigit ((int) *inptr)) + return FALSE; + else + *val = (*val * 10) + (*inptr - '0'); + } + + *in = inptr; + + return TRUE; +} + +static time_t +mktime_utc (struct tm *tm) +{ + time_t tt; + + tm->tm_isdst = -1; + tt = mktime (tm); + +#if defined (HAVE_TM_GMTOFF) + tt += tm->tm_gmtoff; +#elif defined (HAVE_TIMEZONE) + if (tm->tm_isdst > 0) { +#if defined (HAVE_ALTZONE) + tt -= altzone; +#else /* !defined (HAVE_ALTZONE) */ + tt -= (timezone - 3600); +#endif + } else + tt -= timezone; +#endif + + return tt; +} + +static time_t +decode_internaldate (const char *in) +{ + const char *inptr = in; + int hour, min, sec, n; + struct tm tm; + time_t date; + char *buf; + + memset ((void *) &tm, 0, sizeof (struct tm)); + + tm.tm_mday = strtoul (inptr, &buf, 10); + if (buf == inptr || *buf != '-') + return (time_t) -1; + + inptr = buf + 1; + if (inptr[3] != '-') + return (time_t) -1; + + for (n = 0; n < 12; n++) { + if (!strncasecmp (inptr, tm_months[n], 3)) + break; + } + + if (n >= 12) + return (time_t) -1; + + tm.tm_mon = n; + + inptr += 4; + + n = strtoul (inptr, &buf, 10); + if (buf == inptr || *buf != ' ') + return (time_t) -1; + + tm.tm_year = n - 1900; + + inptr = buf + 1; + if (!decode_time (&inptr, &hour, &min, &sec)) + return (time_t) -1; + + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + n = strtol (inptr, NULL, 10); + + date = mktime_utc (&tm); + + /* date is now GMT of the time we want, but not offset by the timezone ... */ + + /* this should convert the time to the GMT equiv time */ + date -= ((n / 100) * 60 * 60) + (n % 100) * 60; + + return date; +} + +enum { + IMAP_FETCH_ENVELOPE = (1 << 1), + IMAP_FETCH_FLAGS = (1 << 2), + IMAP_FETCH_INTERNALDATE = (1 << 3), + IMAP_FETCH_RFC822SIZE = (1 << 4), + IMAP_FETCH_UID = (1 << 5), +}; + +#define IMAP_FETCH_ALL (IMAP_FETCH_ENVELOPE | IMAP_FETCH_FLAGS | IMAP_FETCH_INTERNALDATE | IMAP_FETCH_RFC822SIZE | IMAP_FETCH_UID) + +struct imap_envelope_t { + CamelMessageInfo *info; + guint changed; +}; + +struct imap_fetch_all_t { + CamelFolderSummary *summary; + GHashTable *uid_hash; + GPtrArray *added; +}; + +static void +imap_fetch_all_free (struct imap_fetch_all_t *fetch) +{ + struct imap_envelope_t *envelope; + int i; + + for (i = 0; i < fetch->added->len; i++) { + if (!(envelope = fetch->added->pdata[i])) + continue; + + camel_folder_summary_info_free (fetch->summary, envelope->info); + g_free (envelope); + } + + g_ptr_array_free (fetch->added, TRUE); + g_hash_table_destroy (fetch->uid_hash); + + g_free (fetch); +} + +static void +imap_fetch_all_add (struct imap_fetch_all_t *fetch) +{ + struct imap_envelope_t *envelope; + CamelMessageInfo *info; + int i; + + for (i = 0; i < fetch->added->len; i++) { + if (!(envelope = fetch->added->pdata[i])) + continue; + + if (envelope->changed != IMAP_FETCH_ALL) { + fprintf (stderr, "Hmmm, IMAP server didn't give us everything for message %d\n", i + 1); + camel_folder_summary_info_free (fetch->summary, envelope->info); + g_free (envelope); + continue; + } + + if ((info = camel_folder_summary_uid (fetch->summary, camel_message_info_uid (envelope->info)))) { + camel_folder_summary_info_free (fetch->summary, envelope->info); + g_free (envelope); + continue; + } + + camel_folder_summary_add (fetch->summary, envelope->info); + g_free (envelope); + } + + g_ptr_array_free (fetch->added, TRUE); + g_hash_table_destroy (fetch->uid_hash); + + g_free (fetch); +} + +static guint32 +imap_fetch_all_update (struct imap_fetch_all_t *fetch) +{ + CamelIMAPMessageInfo *iinfo, *new_iinfo; + struct imap_envelope_t *envelope; + CamelMessageInfo *info; + guint32 first = 0; + int scount, i; + + scount = camel_folder_summary_count (fetch->summary); + for (i = 0; i < scount; i++) { + info = camel_folder_summary_index (fetch->summary, i); + if (!(envelope = g_hash_table_lookup (fetch->uid_hash, camel_message_info_uid (info)))) { + /* remove it */ + camel_folder_summary_remove (fetch->summary, info); + scount--; + i--; + } else if (envelope->changed & IMAP_FETCH_FLAGS) { + /* update it with the new flags */ + new_iinfo = (CamelIMAPMessageInfo *) envelope->info; + iinfo = (CamelIMAPMessageInfo *) info; + + info->flags = camel_imap_merge_flags (iinfo->server_flags, info->flags, new_iinfo->server_flags); + iinfo->server_flags = new_iinfo->server_flags; + } + + camel_folder_summary_info_free (fetch->summary, info); + } + + for (i = 0; i < fetch->added->len; i++) { + if (!(envelope = fetch->added->pdata[i])) + continue; + + info = envelope->info; + if (!first && camel_message_info_uid (info)) { + if ((info = camel_folder_summary_uid (fetch->summary, camel_message_info_uid (info)))) { + camel_folder_summary_info_free (fetch->summary, info); + } else { + first = i + 1; + } + } + + camel_folder_summary_info_free (fetch->summary, envelope->info); + g_free (envelope); + } + + g_ptr_array_free (fetch->added, TRUE); + g_hash_table_destroy (fetch->uid_hash); + + g_free (fetch); + + return first; +} + +static int +untagged_fetch_all (CamelIMAPEngine *engine, CamelIMAPCommand *ic, guint32 index, camel_imap_token_t *token, CamelException *ex) +{ + struct imap_fetch_all_t *fetch = ic->user_data; + CamelFolderSummary *summary = fetch->summary; + struct imap_envelope_t *envelope; + GPtrArray *added = fetch->added; + CamelIMAPMessageInfo *iinfo; + CamelMessageInfo *info; + char uid[12]; + + if (index > added->len) + g_ptr_array_set_size (added, index); + + if (!(envelope = added->pdata[index - 1])) { + iinfo = (CamelIMAPMessageInfo *) info = camel_folder_summary_info_new (summary); + envelope = g_new (struct imap_envelope_t, 1); + added->pdata[index - 1] = envelope; + envelope->info = info; + envelope->changed = 0; + } else { + iinfo = (CamelIMAPMessageInfo *) info = envelope->info; + } + + if (camel_imap_engine_next_token (engine, token, ex) == -1) + return -1; + + /* parse the FETCH response list */ + if (token->token != '(') { + camel_imap_utils_set_unexpected_token_error (ex, engine, token); + return -1; + } + + do { + if (camel_imap_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token == ')' || token->token == '\n') + break; + + if (token->token != CAMEL_IMAP_TOKEN_ATOM) + goto unexpected; + + if (!strcmp (token->v.atom, "ENVELOPE")) { + if (decode_envelope (engine, info, token, ex) == -1) + goto exception; + + envelope->changed |= IMAP_FETCH_ENVELOPE; + } else if (!strcmp (token->v.atom, "FLAGS")) { + guint32 server_flags = 0; + + if (camel_imap_parse_flags_list (engine, &server_flags, ex) == -1) + return -1; + + info->flags = camel_imap_merge_flags (iinfo->server_flags, info->flags, server_flags); + iinfo->server_flags = server_flags; + + envelope->changed |= IMAP_FETCH_FLAGS; + } else if (!strcmp (token->v.atom, "INTERNALDATE")) { + if (camel_imap_engine_next_token (engine, token, ex) == -1) + goto exception; + + switch (token->token) { + case CAMEL_IMAP_TOKEN_NIL: + info->date_received = (time_t) -1; + break; + case CAMEL_IMAP_TOKEN_ATOM: + case CAMEL_IMAP_TOKEN_QSTRING: + info->date_received = decode_internaldate (token->v.qstring); + break; + default: + goto unexpected; + } + + envelope->changed |= IMAP_FETCH_INTERNALDATE; + } else if (!strcmp (token->v.atom, "RFC822.SIZE")) { + if (camel_imap_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != CAMEL_IMAP_TOKEN_NUMBER) + goto unexpected; + + info->size = token->v.number; + + envelope->changed |= IMAP_FETCH_RFC822SIZE; + } else if (!strcmp (token->v.atom, "UID")) { + if (camel_imap_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != CAMEL_IMAP_TOKEN_NUMBER || token->v.number == 0) + goto unexpected; + + sprintf (uid, "%u", token->v.number); + if (camel_message_info_uid (info) != NULL) { + if (strcmp (camel_message_info_uid (info), uid) != 0) + fprintf (stderr, "Hmmm, UID mismatch for message %u\n", index); + else + fprintf (stderr, "Hmmm, got UID for messages %d again?\n", index); + + g_hash_table_remove (fetch->uid_hash, camel_message_info_uid (info)); + } + + camel_message_info_set_uid (info, g_strdup (uid)); + g_hash_table_insert (fetch->uid_hash, (void *) camel_message_info_uid (info), envelope); + + envelope->changed |= IMAP_FETCH_UID; + } else { + /* wtf? */ + fprintf (stderr, "huh? %s?...\n", token->v.atom); + } + } while (1); + + if (token->token != ')') + goto unexpected; + + return 0; + + unexpected: + + camel_imap_utils_set_unexpected_token_error (ex, engine, token); + + exception: + + return -1; +} + +static CamelIMAPCommand * +imap_summary_fetch_all (CamelFolderSummary *summary, guint32 first, guint32 last) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + CamelFolder *folder = imap_summary->folder; + struct imap_fetch_all_t *fetch; + CamelIMAPEngine *engine; + CamelIMAPCommand *ic; + + engine = ((CamelIMAPStore *) folder->parent_store)->engine; + + fetch = g_new (struct imap_fetch_all_t, 1); + fetch->uid_hash = g_hash_table_new (g_str_hash, g_str_equal); + fetch->added = g_ptr_array_new (); + fetch->summary = summary; + + /* From rfc2060, Section 6.4.5: + * + * The currently defined data items that can be fetched are: + * + * ALL Macro equivalent to: (FLAGS INTERNALDATE + * RFC822.SIZE ENVELOPE) + **/ + + if (last != 0) + ic = camel_imap_engine_queue (engine, folder, "FETCH %u:%u (UID ALL)\r\n", first, last); + else + ic = camel_imap_engine_queue (engine, folder, "FETCH %u:* (UID ALL)\r\n", first); + + camel_imap_command_register_untagged (ic, "FETCH", untagged_fetch_all); + ic->user_data = fetch; + + return ic; +} + +static CamelIMAPCommand * +imap_summary_fetch_flags (CamelFolderSummary *summary, guint32 first, guint32 last) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + CamelFolder *folder = imap_summary->folder; + struct imap_fetch_all_t *fetch; + CamelIMAPEngine *engine; + CamelIMAPCommand *ic; + + engine = ((CamelIMAPStore *) folder->parent_store)->engine; + + fetch = g_new (struct imap_fetch_all_t, 1); + fetch->uid_hash = g_hash_table_new (g_str_hash, g_str_equal); + fetch->added = g_ptr_array_new (); + fetch->summary = summary; + + if (last != 0) + ic = camel_imap_engine_queue (engine, folder, "FETCH %u:%u (UID FLAGS)\r\n", first, last); + else + ic = camel_imap_engine_queue (engine, folder, "FETCH %u:* (UID FLAGS)\r\n", first); + + camel_imap_command_register_untagged (ic, "FETCH", untagged_fetch_all); + ic->user_data = fetch; + + return ic; +} + +#if 0 +static int +imap_build_summary (CamelFolderSummary *summary, guint32 first, guint32 last) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + CamelFolder *folder = imap_summary->folder; + struct imap_fetch_all_t *fetch; + CamelIMAPEngine *engine; + CamelIMAPCommand *ic; + int id; + + engine = ((CamelIMAPStore *) folder->store)->engine; + + ic = imap_summary_fetch_all (summary, first, last); + while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) + ; + + fetch = ic->user_data; + + if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { + camel_imap_command_unref (ic); + imap_fetch_all_free (fetch); + return -1; + } + + imap_fetch_all_add (fetch); + + camel_imap_command_unref (ic); + + return 0; +} +#endif + +static CamelMessageInfo * +imap_message_info_new (CamelFolderSummary *summary, struct _camel_header_raw *header) +{ + CamelMessageInfo *info; + + info = CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->message_info_new (summary, header); + + ((CamelIMAPMessageInfo *) info)->server_flags = 0; + + return info; +} + +static CamelMessageInfo * +imap_message_info_load (CamelFolderSummary *summary, FILE *fin) +{ + CamelIMAPMessageInfo *minfo; + CamelMessageInfo *info; + + if (!(info = CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->message_info_load (summary, fin))) + return NULL; + + minfo = (CamelIMAPMessageInfo *) info; + + if (camel_file_util_decode_uint32 (fin, &minfo->server_flags) == -1) + goto exception; + + return info; + + exception: + + camel_folder_summary_info_free (summary, info); + + return NULL; +} + +static int +imap_message_info_save (CamelFolderSummary *summary, FILE *fout, CamelMessageInfo *info) +{ + CamelIMAPMessageInfo *minfo = (CamelIMAPMessageInfo *) info; + + if (CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->message_info_save (summary, fout, info) == -1) + return -1; + + if (camel_file_util_encode_uint32 (fout, minfo->server_flags) == -1) + return -1; + + return 0; +} + + +void +camel_imap_summary_set_exists (CamelFolderSummary *summary, guint32 exists) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP_SUMMARY (summary)); + + imap_summary->exists = exists; + + imap_summary->exists_changed = TRUE; +} + +void +camel_imap_summary_set_recent (CamelFolderSummary *summary, guint32 recent) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP_SUMMARY (summary)); + + imap_summary->recent = recent; +} + +void +camel_imap_summary_set_unseen (CamelFolderSummary *summary, guint32 unseen) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP_SUMMARY (summary)); + + imap_summary->unseen = unseen; +} + +void +camel_imap_summary_set_uidnext (CamelFolderSummary *summary, guint32 uidnext) +{ + g_return_if_fail (CAMEL_IS_IMAP_SUMMARY (summary)); + + summary->nextuid = uidnext; +} + +void +camel_imap_summary_set_uidvalidity (CamelFolderSummary *summary, guint32 uidvalidity) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP_SUMMARY (summary)); + + if (imap_summary->uidvalidity == uidvalidity) + return; + + /* FIXME: emit a signal or something first? */ + camel_folder_summary_clear (summary); + + imap_summary->uidvalidity = uidvalidity; + + imap_summary->uidvalidity_changed = TRUE; +} + +void +camel_imap_summary_expunge (CamelFolderSummary *summary, int seqid) +{ + CamelMessageInfo *info; + + g_return_if_fail (CAMEL_IS_IMAP_SUMMARY (summary)); + + if (!(info = camel_folder_summary_index (summary, seqid))) + return; + + /* FIXME: emit a signal or something that our Folder can proxy + * up to the app so that it can update its display and + * whatnot? */ + + /* emit signal */ + + camel_folder_summary_info_free (summary, info); + camel_folder_summary_remove_index (summary, seqid); +} + + +static int +info_uid_sort (const CamelMessageInfo **info0, const CamelMessageInfo **info1) +{ + guint32 uid0, uid1; + + uid0 = strtoul (camel_message_info_uid (*info0), NULL, 10); + uid1 = strtoul (camel_message_info_uid (*info1), NULL, 10); + + if (uid0 == uid1) + return 0; + + return uid0 < uid1 ? -1 : 1; +} + +int +camel_imap_summary_flush_updates (CamelFolderSummary *summary, CamelException *ex) +{ + CamelIMAPSummary *imap_summary = (CamelIMAPSummary *) summary; + CamelIMAPEngine *engine; + CamelIMAPCommand *ic; + guint32 first = 0; + int scount, id; + + g_return_val_if_fail (CAMEL_IS_IMAP_SUMMARY (summary), -1); + + engine = ((CamelIMAPStore *) imap_summary->folder->parent_store)->engine; + + if (imap_summary->uidvalidity_changed) { + first = 1; + } else if (imap_summary->exists_changed) { + scount = camel_folder_summary_count (summary); + ic = imap_summary_fetch_flags (summary, 1, scount); + + while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { + imap_fetch_all_free (ic->user_data); + camel_exception_xfer (ex, &ic->ex); + camel_imap_command_unref (ic); + return -1; + } + + if (!(first = imap_fetch_all_update (ic->user_data)) && imap_summary->exists > scount) + first = scount + 1; + + camel_imap_command_unref (ic); + } + + if (first != 0) { + ic = imap_summary_fetch_all (summary, first, 0); + + while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { + imap_fetch_all_free (ic->user_data); + camel_exception_xfer (ex, &ic->ex); + camel_imap_command_unref (ic); + return -1; + } + + imap_fetch_all_add (ic->user_data); + camel_imap_command_unref (ic); + + /* it's important for these to be sorted sequentially for EXPUNGE events to work */ + g_ptr_array_sort (summary->messages, (GCompareFunc) info_uid_sort); + } + + imap_summary->exists_changed = FALSE; + imap_summary->uidvalidity_changed = FALSE; + + return 0; +} diff --git a/camel/providers/imap4/camel-imap-summary.h b/camel/providers/imap4/camel-imap-summary.h new file mode 100644 index 0000000000..a8faff8522 --- /dev/null +++ b/camel/providers/imap4/camel-imap-summary.h @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* Camel + * Copyright (C) 1999-2004 Jeffrey Stedfast + * + * 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_IMAP_SUMMARY_H__ +#define __CAMEL_IMAP_SUMMARY_H__ + +#include <sys/types.h> + +#include <camel/camel-folder.h> +#include <camel/camel-folder-summary.h> + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_TYPE_IMAP_SUMMARY (camel_imap_summary_get_type ()) +#define CAMEL_IMAP_SUMMARY(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP_SUMMARY, CamelIMAPSummary)) +#define CAMEL_IMAP_SUMMARY_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP_SUMMARY, CamelIMAPSummaryClass)) +#define CAMEL_IS_IMAP_SUMMARY(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP_SUMMARY)) +#define CAMEL_IS_IMAP_SUMMARY_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP_SUMMARY)) +#define CAMEL_IMAP_SUMMARY_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_FOLDER_SUMMARY, CamelIMAPSummaryClass)) + +typedef struct _CamelIMAPMessageInfo CamelIMAPMessageInfo; +typedef struct _CamelIMAPSummary CamelIMAPSummary; +typedef struct _CamelIMAPSummaryClass CamelIMAPSummaryClass; + +struct _CamelIMAPMessageInfo { + CamelMessageInfo parent_info; + + guint32 server_flags; +}; + +struct _CamelIMAPSummary { + CamelFolderSummary parent_object; + + CamelFolder *folder; + + guint32 exists; + guint32 recent; + guint32 unseen; + + guint32 uidvalidity; + + guint uidvalidity_changed:1; + guint exists_changed:1; +}; + +struct _CamelIMAPSummaryClass { + CamelFolderSummaryClass parent_class; + +}; + + +CamelType camel_imap_summary_get_type (void); + +CamelFolderSummary *camel_imap_summary_new (CamelFolder *folder); + +void camel_imap_summary_set_exists (CamelFolderSummary *summary, guint32 exists); +void camel_imap_summary_set_recent (CamelFolderSummary *summary, guint32 recent); +void camel_imap_summary_set_unseen (CamelFolderSummary *summary, guint32 unseen); +void camel_imap_summary_set_uidnext (CamelFolderSummary *summary, guint32 uidnext); + +void camel_imap_summary_set_uidvalidity (CamelFolderSummary *summary, guint32 uidvalidity); + +void camel_imap_summary_expunge (CamelFolderSummary *summary, int seqid); + +int camel_imap_summary_flush_updates (CamelFolderSummary *summary, CamelException *ex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP_SUMMARY_H__ */ |