From 71c5ee30a81ea870329f1c2573cdf6f22cfc7d75 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Tue, 30 Mar 2004 04:24:02 +0000 Subject: Implemented. 2004-03-29 Jeffrey Stedfast * providers/imap4/camel-imap4-folder.c (camel_imap4_folder_new): Implemented. * providers/imap4/camel-imap4-engine.c (engine_parse_namespace): If the namespace begins with "INBOX", canonicalise the INBOX portion (ie, make it all caps). * providers/imap4/camel-imap4-store.c (imap4_noop): Implemented. svn path=/trunk/; revision=25237 --- camel/ChangeLog | 11 + camel/providers/Makefile.am | 6 +- camel/providers/imap4/Makefile.am | 34 +- camel/providers/imap4/camel-imap-command.c | 674 ----------- camel/providers/imap4/camel-imap-command.h | 144 --- camel/providers/imap4/camel-imap-engine.c | 1664 ------------------------- camel/providers/imap4/camel-imap-engine.h | 222 ---- camel/providers/imap4/camel-imap-folder.c | 768 ------------ camel/providers/imap4/camel-imap-folder.h | 66 - camel/providers/imap4/camel-imap-provider.c | 139 --- camel/providers/imap4/camel-imap-specials.c | 100 -- camel/providers/imap4/camel-imap-specials.h | 53 - camel/providers/imap4/camel-imap-store.c | 852 ------------- camel/providers/imap4/camel-imap-store.h | 61 - camel/providers/imap4/camel-imap-stream.c | 708 ----------- camel/providers/imap4/camel-imap-stream.h | 124 -- camel/providers/imap4/camel-imap-summary.c | 1107 ----------------- camel/providers/imap4/camel-imap-summary.h | 91 -- camel/providers/imap4/camel-imap-utils.c | 313 ----- camel/providers/imap4/camel-imap-utils.h | 72 -- camel/providers/imap4/camel-imap4-command.c | 674 +++++++++++ camel/providers/imap4/camel-imap4-command.h | 144 +++ camel/providers/imap4/camel-imap4-engine.c | 1669 ++++++++++++++++++++++++++ camel/providers/imap4/camel-imap4-engine.h | 222 ++++ camel/providers/imap4/camel-imap4-folder.c | 860 +++++++++++++ camel/providers/imap4/camel-imap4-folder.h | 65 + camel/providers/imap4/camel-imap4-provider.c | 139 +++ camel/providers/imap4/camel-imap4-specials.c | 100 ++ camel/providers/imap4/camel-imap4-specials.h | 53 + camel/providers/imap4/camel-imap4-store.c | 861 +++++++++++++ camel/providers/imap4/camel-imap4-store.h | 61 + camel/providers/imap4/camel-imap4-stream.c | 708 +++++++++++ camel/providers/imap4/camel-imap4-stream.h | 124 ++ camel/providers/imap4/camel-imap4-summary.c | 1107 +++++++++++++++++ camel/providers/imap4/camel-imap4-summary.h | 91 ++ camel/providers/imap4/camel-imap4-utils.c | 315 +++++ camel/providers/imap4/camel-imap4-utils.h | 67 ++ 37 files changed, 7293 insertions(+), 7176 deletions(-) delete mode 100644 camel/providers/imap4/camel-imap-command.c delete mode 100644 camel/providers/imap4/camel-imap-command.h delete mode 100644 camel/providers/imap4/camel-imap-engine.c delete mode 100644 camel/providers/imap4/camel-imap-engine.h delete mode 100644 camel/providers/imap4/camel-imap-folder.c delete mode 100644 camel/providers/imap4/camel-imap-folder.h delete mode 100644 camel/providers/imap4/camel-imap-provider.c delete mode 100644 camel/providers/imap4/camel-imap-specials.c delete mode 100644 camel/providers/imap4/camel-imap-specials.h delete mode 100644 camel/providers/imap4/camel-imap-store.c delete mode 100644 camel/providers/imap4/camel-imap-store.h delete mode 100644 camel/providers/imap4/camel-imap-stream.c delete mode 100644 camel/providers/imap4/camel-imap-stream.h delete mode 100644 camel/providers/imap4/camel-imap-summary.c delete mode 100644 camel/providers/imap4/camel-imap-summary.h delete mode 100644 camel/providers/imap4/camel-imap-utils.c delete mode 100644 camel/providers/imap4/camel-imap-utils.h create mode 100644 camel/providers/imap4/camel-imap4-command.c create mode 100644 camel/providers/imap4/camel-imap4-command.h create mode 100644 camel/providers/imap4/camel-imap4-engine.c create mode 100644 camel/providers/imap4/camel-imap4-engine.h create mode 100644 camel/providers/imap4/camel-imap4-folder.c create mode 100644 camel/providers/imap4/camel-imap4-folder.h create mode 100644 camel/providers/imap4/camel-imap4-provider.c create mode 100644 camel/providers/imap4/camel-imap4-specials.c create mode 100644 camel/providers/imap4/camel-imap4-specials.h create mode 100644 camel/providers/imap4/camel-imap4-store.c create mode 100644 camel/providers/imap4/camel-imap4-store.h create mode 100644 camel/providers/imap4/camel-imap4-stream.c create mode 100644 camel/providers/imap4/camel-imap4-stream.h create mode 100644 camel/providers/imap4/camel-imap4-summary.c create mode 100644 camel/providers/imap4/camel-imap4-summary.h create mode 100644 camel/providers/imap4/camel-imap4-utils.c create mode 100644 camel/providers/imap4/camel-imap4-utils.h (limited to 'camel') diff --git a/camel/ChangeLog b/camel/ChangeLog index d13e1e7001..95cf94a1e2 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,14 @@ +2004-03-29 Jeffrey Stedfast + + * providers/imap4/camel-imap4-folder.c (camel_imap4_folder_new): + Implemented. + + * providers/imap4/camel-imap4-engine.c (engine_parse_namespace): + If the namespace begins with "INBOX", canonicalise the INBOX + portion (ie, make it all caps). + + * providers/imap4/camel-imap4-store.c (imap4_noop): Implemented. + 2004-03-29 Jeffrey Stedfast * providers/imap/camel-imap-store.c (get_folder_status): Updated diff --git a/camel/providers/Makefile.am b/camel/providers/Makefile.am index 55ce850097..22ea481954 100644 --- a/camel/providers/Makefile.am +++ b/camel/providers/Makefile.am @@ -8,5 +8,9 @@ if ENABLE_IMAPP IMAPP_DIR=imapp endif -SUBDIRS = pop3 sendmail smtp imap $(NNTP_DIR) local $(IMAPP_DIR) groupwise +if ENABLE_IMAP4 +IMAP4_DIR=imap4 +endif + +SUBDIRS = pop3 sendmail smtp imap $(NNTP_DIR) local $(IMAPP_DIR) $(IMAP4_DIR) groupwise diff --git a/camel/providers/imap4/Makefile.am b/camel/providers/imap4/Makefile.am index b94c8f9564..d1bb57e150 100644 --- a/camel/providers/imap4/Makefile.am +++ b/camel/providers/imap4/Makefile.am @@ -17,23 +17,23 @@ INCLUDES = \ -DG_LOG_DOMAIN=\"camel-imap4-provider\" libcamelimap4_la_SOURCES = \ - camel-imap-command.c \ - camel-imap-command.h \ - camel-imap-engine.c \ - camel-imap-engine.h \ - camel-imap-folder.c \ - camel-imap-folder.h \ - camel-imap-provider.c \ - camel-imap-specials.c \ - camel-imap-specials.h \ - camel-imap-store.c \ - camel-imap-store.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 + camel-imap4-command.c \ + camel-imap4-command.h \ + camel-imap4-engine.c \ + camel-imap4-engine.h \ + camel-imap4-folder.c \ + camel-imap4-folder.h \ + camel-imap4-provider.c \ + camel-imap4-specials.c \ + camel-imap4-specials.h \ + camel-imap4-store.c \ + camel-imap4-store.h \ + camel-imap4-stream.c \ + camel-imap4-stream.h \ + camel-imap4-summary.c \ + camel-imap4-summary.h \ + camel-imap4-utils.c \ + camel-imap4-utils.h libcamelimap4_la_LDFLAGS = -avoid-version -module diff --git a/camel/providers/imap4/camel-imap-command.c b/camel/providers/imap4/camel-imap-command.c deleted file mode 100644 index e5668ccbc6..0000000000 --- a/camel/providers/imap4/camel-imap-command.c +++ /dev/null @@ -1,674 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include - -#include -#include -#include - -#include "camel-imap-stream.h" -#include "camel-imap-engine.h" -#include "camel-imap-folder.h" -#include "camel-imap-specials.h" - -#include "camel-imap-command.h" - - -#define d(x) x - - -enum { - IMAP_STRING_ATOM, - IMAP_STRING_QSTRING, - IMAP_STRING_LITERAL, -}; - -static int -imap_string_get_type (const char *str) -{ - int type = 0; - - while (*str) { - if (!is_atom (*str)) { - if (is_qsafe (*str)) - type = IMAP_STRING_QSTRING; - else - return IMAP_STRING_LITERAL; - } - str++; - } - - return type; -} - -#if 0 -static gboolean -imap_string_is_atom_safe (const char *str) -{ - while (is_atom (*str)) - str++; - - return *str == '\0'; -} - -static gboolean -imap_string_is_quote_safe (const char *str) -{ - while (is_qsafe (*str)) - str++; - - return *str == '\0'; -} -#endif - -static size_t -camel_imap_literal_length (CamelIMAPLiteral *literal) -{ - CamelStream *stream, *null; - CamelMimeFilter *crlf; - size_t len; - - if (literal->type == CAMEL_IMAP_LITERAL_STRING) - return strlen (literal->literal.string); - - null = camel_stream_null_new (); - crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); - stream = (CamelStream *) camel_stream_filter_new_with_stream (null); - camel_stream_filter_add ((CamelStreamFilter *) stream, crlf); - camel_object_unref (crlf); - - switch (literal->type) { - case CAMEL_IMAP_LITERAL_STREAM: - camel_stream_write_to_stream (literal->literal.stream, stream); - camel_stream_reset (literal->literal.stream); - break; - case CAMEL_IMAP_LITERAL_WRAPPER: - camel_data_wrapper_write_to_stream (literal->literal.wrapper, stream); - break; - default: - g_assert_not_reached (); - break; - } - - len = ((CamelStreamNull *) null)->written; - - camel_object_unref (stream); - camel_object_unref (null); - - return len; -} - -static CamelIMAPCommandPart * -command_part_new (void) -{ - CamelIMAPCommandPart *part; - - part = g_new (CamelIMAPCommandPart, 1); - part->next = NULL; - part->buffer = NULL; - part->buflen = 0; - part->literal = NULL; - - return part; -} - -static void -imap_command_append_string (CamelIMAPEngine *engine, CamelIMAPCommandPart **tail, GString *str, const char *string) -{ - CamelIMAPCommandPart *part; - CamelIMAPLiteral *literal; - - switch (imap_string_get_type (string)) { - case IMAP_STRING_ATOM: - /* string is safe as it is... */ - g_string_append (str, string); - break; - case IMAP_STRING_QSTRING: - /* we need to quote the string */ - /* FIXME: need to escape stuff */ - g_string_append_printf (str, "\"%s\"", string); - break; - case IMAP_STRING_LITERAL: - if (engine->capa & CAMEL_IMAP_CAPABILITY_LITERALPLUS) { - /* we have to send a literal, but the server supports LITERAL+ so use that */ - g_string_append_printf (str, "{%u+}\r\n%s", strlen (string), string); - } else { - /* we have to make it a literal */ - literal = g_new (CamelIMAPLiteral, 1); - literal->type = CAMEL_IMAP_LITERAL_STRING; - literal->literal.string = g_strdup (string); - - g_string_append_printf (str, "{%u}\r\n", strlen (string)); - - (*tail)->buffer = g_strdup (str->str); - (*tail)->buflen = str->len; - (*tail)->literal = literal; - - part = command_part_new (); - (*tail)->next = part; - (*tail) = part; - - g_string_truncate (str, 0); - } - break; - } -} - -CamelIMAPCommand * -camel_imap_command_newv (CamelIMAPEngine *engine, CamelIMAPFolder *imap_folder, const char *format, va_list args) -{ - CamelIMAPCommandPart *parts, *part, *tail; - CamelIMAPCommand *ic; - const char *start; - GString *str; - - tail = parts = command_part_new (); - - str = g_string_new (""); - start = format; - - while (*format) { - register char ch = *format++; - - if (ch == '%') { - CamelIMAPLiteral *literal; - CamelIMAPFolder *folder; - unsigned int u; - char *string; - size_t len; - void *obj; - int c, d; - - g_string_append_len (str, start, format - start - 1); - - switch (*format) { - case '%': - /* literal % */ - g_string_append_c (str, '%'); - break; - case 'c': - /* character */ - c = va_arg (args, int); - g_string_append_c (str, c); - break; - case 'd': - /* integer */ - d = va_arg (args, int); - g_string_append_printf (str, "%d", d); - break; - case 'u': - /* unsigned integer */ - u = va_arg (args, unsigned int); - g_string_append_printf (str, "%u", u); - break; - case 'F': - /* CamelIMAPFolder */ - folder = va_arg (args, CamelIMAPFolder *); - string = (char *) camel_imap_folder_utf7_name (folder); - imap_command_append_string (engine, &tail, str, string); - break; - case 'L': - /* Literal */ - obj = va_arg (args, void *); - - literal = g_new (CamelIMAPLiteral, 1); - if (CAMEL_IS_DATA_WRAPPER (obj)) { - literal->type = CAMEL_IMAP_LITERAL_WRAPPER; - literal->literal.wrapper = obj; - } else if (CAMEL_IS_STREAM (obj)) { - literal->type = CAMEL_IMAP_LITERAL_STREAM; - literal->literal.stream = obj; - } else { - g_assert_not_reached (); - } - - camel_object_ref (obj); - - /* FIXME: take advantage of LITERAL+? */ - len = camel_imap_literal_length (literal); - g_string_append_printf (str, "{%u}\r\n", len); - - tail->buffer = g_strdup (str->str); - tail->buflen = str->len; - tail->literal = literal; - - part = command_part_new (); - tail->next = part; - tail = part; - - g_string_truncate (str, 0); - - break; - case 'S': - /* string which may need to be quoted or made into a literal */ - string = va_arg (args, char *); - imap_command_append_string (engine, &tail, str, string); - break; - case 's': - /* safe atom string */ - string = va_arg (args, char *); - g_string_append (str, string); - break; - default: - g_warning ("unknown formatter %%%c", *format); - g_string_append_c (str, '%'); - g_string_append_c (str, *format); - break; - } - - format++; - - start = format; - } - } - - g_string_append (str, start); - tail->buffer = str->str; - tail->buflen = str->len; - tail->literal = NULL; - g_string_free (str, FALSE); - - ic = g_new0 (CamelIMAPCommand, 1); - ((EDListNode *) ic)->next = NULL; - ((EDListNode *) ic)->prev = NULL; - ic->untagged = g_hash_table_new (g_str_hash, g_str_equal); - ic->status = CAMEL_IMAP_COMMAND_QUEUED; - ic->resp_codes = g_ptr_array_new (); - ic->engine = engine; - ic->ref_count = 1; - ic->parts = parts; - ic->part = parts; - - camel_exception_init (&ic->ex); - - if (imap_folder) { - camel_object_ref (imap_folder); - ic->folder = imap_folder; - } else - ic->folder = NULL; - - return ic; -} - -CamelIMAPCommand * -camel_imap_command_new (CamelIMAPEngine *engine, CamelIMAPFolder *folder, const char *format, ...) -{ - CamelIMAPCommand *command; - va_list args; - - va_start (args, format); - command = camel_imap_command_newv (engine, folder, format, args); - va_end (args); - - return command; -} - -void -camel_imap_command_register_untagged (CamelIMAPCommand *ic, const char *atom, CamelIMAPUntaggedCallback untagged) -{ - g_hash_table_insert (ic->untagged, g_strdup (atom), untagged); -} - -void -camel_imap_command_ref (CamelIMAPCommand *ic) -{ - ic->ref_count++; -} - -void -camel_imap_command_unref (CamelIMAPCommand *ic) -{ - CamelIMAPCommandPart *part, *next; - int i; - - if (ic == NULL) - return; - - ic->ref_count--; - if (ic->ref_count == 0) { - if (ic->folder) - camel_object_unref (ic->folder); - - g_free (ic->tag); - - for (i = 0; i < ic->resp_codes->len; i++) { - CamelIMAPRespCode *resp_code; - - resp_code = ic->resp_codes->pdata[i]; - camel_imap_resp_code_free (resp_code); - } - g_ptr_array_free (ic->resp_codes, TRUE); - - g_hash_table_foreach (ic->untagged, (GHFunc) g_free, NULL); - g_hash_table_destroy (ic->untagged); - - camel_exception_clear (&ic->ex); - - part = ic->parts; - while (part != NULL) { - g_free (part->buffer); - if (part->literal) { - switch (part->literal->type) { - case CAMEL_IMAP_LITERAL_STRING: - g_free (part->literal->literal.string); - break; - case CAMEL_IMAP_LITERAL_STREAM: - camel_object_unref (part->literal->literal.stream); - break; - case CAMEL_IMAP_LITERAL_WRAPPER: - camel_object_unref (part->literal->literal.wrapper); - break; - } - - g_free (part->literal); - } - - next = part->next; - g_free (part); - part = next; - } - - g_free (ic); - } -} - - -static int -imap_literal_write_to_stream (CamelIMAPLiteral *literal, CamelStream *stream) -{ - CamelStream *istream, *ostream = NULL; - CamelDataWrapper *wrapper; - CamelMimeFilter *crlf; - char *string; - - if (literal->type == CAMEL_IMAP_LITERAL_STRING) { - string = literal->literal.string; - if (camel_stream_write (stream, string, strlen (string)) == -1) - return -1; - - return 0; - } - - crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); - ostream = (CamelStream *) camel_stream_filter_new_with_stream (stream); - camel_stream_filter_add ((CamelStreamFilter *) ostream, crlf); - camel_object_unref (crlf); - - /* write the literal */ - switch (literal->type) { - case CAMEL_IMAP_LITERAL_STREAM: - istream = literal->literal.stream; - if (camel_stream_write_to_stream (istream, ostream) == -1) - goto exception; - break; - case CAMEL_IMAP_LITERAL_WRAPPER: - wrapper = literal->literal.wrapper; - if (camel_data_wrapper_write_to_stream (wrapper, ostream) == -1) - goto exception; - break; - } - - camel_object_unref (ostream); - ostream = NULL; - -#if 0 - if (camel_stream_write (stream, "\r\n", 2) == -1) - return -1; -#endif - - return 0; - - exception: - - camel_object_unref (ostream); - - return -1; -} - - -static void -unexpected_token (camel_imap_token_t *token) -{ - switch (token->token) { - case CAMEL_IMAP_TOKEN_NO_DATA: - fprintf (stderr, "*** NO DATA ***"); - break; - case CAMEL_IMAP_TOKEN_ERROR: - fprintf (stderr, "*** ERROR ***"); - break; - case CAMEL_IMAP_TOKEN_NIL: - fprintf (stderr, "NIL"); - break; - case CAMEL_IMAP_TOKEN_ATOM: - fprintf (stderr, "%s", token->v.atom); - break; - case CAMEL_IMAP_TOKEN_QSTRING: - fprintf (stderr, "\"%s\"", token->v.qstring); - break; - case CAMEL_IMAP_TOKEN_LITERAL: - fprintf (stderr, "{%u}", token->v.literal); - break; - default: - fprintf (stderr, "%c", (unsigned char) (token->token & 0xff)); - break; - } -} - -int -camel_imap_command_step (CamelIMAPCommand *ic) -{ - CamelIMAPEngine *engine = ic->engine; - int result = CAMEL_IMAP_RESULT_NONE; - CamelIMAPLiteral *literal; - camel_imap_token_t token; - unsigned char *linebuf; - ssize_t nwritten; - size_t len; - - g_assert (ic->part != NULL); - - if (ic->part == ic->parts) { - ic->tag = g_strdup_printf ("%c%.5u", engine->tagprefix, engine->tag++); - camel_stream_printf (engine->ostream, "%s ", ic->tag); - d(fprintf (stderr, "sending : %s ", ic->tag)); - } - -#if d(!)0 - { - int sending = ic->part != ic->parts; - unsigned char *eoln, *eob; - - linebuf = ic->part->buffer; - eob = linebuf + ic->part->buflen; - - do { - eoln = linebuf; - while (eoln < eob && *eoln != '\n') - eoln++; - - if (eoln < eob) - eoln++; - - if (sending) - fwrite ("sending : ", 1, 10, stderr); - fwrite (linebuf, 1, eoln - linebuf, stderr); - - linebuf = eoln + 1; - sending = 1; - } while (linebuf < eob); - } -#endif - - linebuf = ic->part->buffer; - len = ic->part->buflen; - - if ((nwritten = camel_stream_write (engine->ostream, linebuf, len)) == -1) - goto exception; - - if (camel_stream_flush (engine->ostream) == -1) - goto exception; - - /* now we need to read the response(s) from the IMAP server */ - - do { - if (camel_imap_engine_next_token (engine, &token, &ic->ex) == -1) - goto exception; - - if (token.token == '+') { - /* we got a continuation response from the server */ - literal = ic->part->literal; - - if (camel_imap_engine_line (engine, &linebuf, &len, &ic->ex) == -1) - goto exception; - - if (literal) { - if (imap_literal_write_to_stream (literal, engine->ostream) == -1) - goto exception; - - g_free (linebuf); - linebuf = NULL; - - break; - } else if (ic->plus) { - /* command expected a '+' response - probably AUTHENTICATE? */ - if (ic->plus (engine, ic, linebuf, len, &ic->ex) == -1) { - g_free (linebuf); - return -1; - } - - /* now we need to wait for a " OK/NO/BAD" response */ - } else { - /* FIXME: error?? */ - g_assert_not_reached (); - } - - g_free (linebuf); - linebuf = NULL; - } else if (token.token == '*') { - /* we got an untagged response, let the engine handle this */ - if (camel_imap_engine_handle_untagged_1 (engine, &token, &ic->ex) == -1) - goto exception; - } else if (token.token == CAMEL_IMAP_TOKEN_ATOM && !strcmp (token.v.atom, ic->tag)) { - /* we got " OK/NO/BAD" */ - fprintf (stderr, "got %s response\n", token.v.atom); - - if (camel_imap_engine_next_token (engine, &token, &ic->ex) == -1) - goto exception; - - if (token.token == CAMEL_IMAP_TOKEN_ATOM) { - if (!strcmp (token.v.atom, "OK")) - result = CAMEL_IMAP_RESULT_OK; - else if (!strcmp (token.v.atom, "NO")) - result = CAMEL_IMAP_RESULT_NO; - else if (!strcmp (token.v.atom, "BAD")) - result = CAMEL_IMAP_RESULT_BAD; - - if (result == CAMEL_IMAP_RESULT_NONE) { - fprintf (stderr, "expected OK/NO/BAD but got %s\n", token.v.atom); - goto unexpected; - } - - if (camel_imap_engine_next_token (engine, &token, &ic->ex) == -1) - goto exception; - - if (token.token == '[') { - /* we have a response code */ - camel_imap_stream_unget_token (engine->istream, &token); - if (camel_imap_engine_parse_resp_code (engine, &ic->ex) == -1) - goto exception; - } else if (token.token != '\n') { - /* just gobble up the rest of the line */ - if (camel_imap_engine_line (engine, NULL, NULL, &ic->ex) == -1) - goto exception; - } - } else { - fprintf (stderr, "expected anything but this: "); - unexpected_token (&token); - fprintf (stderr, "\n"); - - goto unexpected; - } - - break; - } else { - fprintf (stderr, "wtf is this: "); - unexpected_token (&token); - fprintf (stderr, "\n"); - - unexpected: - - /* no fucking clue what we got... */ - if (camel_imap_engine_line (engine, &linebuf, &len, &ic->ex) == -1) - goto exception; - - camel_exception_setv (&ic->ex, CAMEL_EXCEPTION_SYSTEM, - _("Unexpected response from IMAP server %s: %s"), - engine->url->host, linebuf); - - g_free (linebuf); - - goto exception; - } - } while (1); - - /* status should always be ACTIVE here... */ - if (ic->status == CAMEL_IMAP_COMMAND_ACTIVE) { - ic->part = ic->part->next; - if (ic->part == NULL || result) { - ic->status = CAMEL_IMAP_COMMAND_COMPLETE; - ic->result = result; - return 1; - } - } - - return 0; - - exception: - - ic->status = CAMEL_IMAP_COMMAND_ERROR; - - return -1; -} - - -void -camel_imap_command_reset (CamelIMAPCommand *ic) -{ - int i; - - for (i = 0; i < ic->resp_codes->len; i++) - camel_imap_resp_code_free (ic->resp_codes->pdata[i]); - g_ptr_array_set_size (ic->resp_codes, 0); - - ic->status = CAMEL_IMAP_COMMAND_QUEUED; - ic->result = CAMEL_IMAP_RESULT_NONE; - ic->part = ic->parts; - g_free (ic->tag); - ic->tag = NULL; - - camel_exception_clear (&ic->ex); -} diff --git a/camel/providers/imap4/camel-imap-command.h b/camel/providers/imap4/camel-imap-command.h deleted file mode 100644 index af3ad9f927..0000000000 --- a/camel/providers/imap4/camel-imap-command.h +++ /dev/null @@ -1,144 +0,0 @@ -/* -*- 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_COMMAND_H__ -#define __CAMEL_IMAP_COMMAND_H__ - -#include - -#include - -#include - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -struct _CamelIMAPEngine; -struct _CamelIMAPFolder; -struct _camel_imap_token_t; - -typedef struct _CamelIMAPCommand CamelIMAPCommand; -typedef struct _CamelIMAPLiteral CamelIMAPLiteral; - -typedef int (* CamelIMAPPlusCallback) (struct _CamelIMAPEngine *engine, - CamelIMAPCommand *ic, - const unsigned char *linebuf, - size_t linelen, CamelException *ex); - -typedef int (* CamelIMAPUntaggedCallback) (struct _CamelIMAPEngine *engine, - CamelIMAPCommand *ic, - guint32 index, - struct _camel_imap_token_t *token, - CamelException *ex); - -enum { - CAMEL_IMAP_LITERAL_STRING, - CAMEL_IMAP_LITERAL_STREAM, - CAMEL_IMAP_LITERAL_WRAPPER, -}; - -struct _CamelIMAPLiteral { - int type; - union { - char *string; - CamelStream *stream; - CamelDataWrapper *wrapper; - } literal; -}; - -typedef struct _CamelIMAPCommandPart { - struct _CamelIMAPCommandPart *next; - unsigned char *buffer; - size_t buflen; - - CamelIMAPLiteral *literal; -} CamelIMAPCommandPart; - -enum { - CAMEL_IMAP_COMMAND_QUEUED, - CAMEL_IMAP_COMMAND_ACTIVE, - CAMEL_IMAP_COMMAND_COMPLETE, - CAMEL_IMAP_COMMAND_ERROR, -}; - -enum { - CAMEL_IMAP_RESULT_NONE, - CAMEL_IMAP_RESULT_OK, - CAMEL_IMAP_RESULT_NO, - CAMEL_IMAP_RESULT_BAD, -}; - -struct _CamelIMAPCommand { - EDListNode node; - - struct _CamelIMAPEngine *engine; - - unsigned int ref_count:26; - unsigned int status:3; - unsigned int result:3; - int id; - - char *tag; - - GPtrArray *resp_codes; - - struct _CamelIMAPFolder *folder; - CamelException ex; - - /* command parts - logical breaks in the overall command based on literals */ - CamelIMAPCommandPart *parts; - - /* current part */ - CamelIMAPCommandPart *part; - - /* untagged handlers */ - GHashTable *untagged; - - /* '+' callback/data */ - CamelIMAPPlusCallback plus; - void *user_data; -}; - -CamelIMAPCommand *camel_imap_command_new (struct _CamelIMAPEngine *engine, struct _CamelIMAPFolder *folder, - const char *format, ...); -CamelIMAPCommand *camel_imap_command_newv (struct _CamelIMAPEngine *engine, struct _CamelIMAPFolder *folder, - const char *format, va_list args); - -void camel_imap_command_register_untagged (CamelIMAPCommand *ic, const char *atom, CamelIMAPUntaggedCallback untagged); - -void camel_imap_command_ref (CamelIMAPCommand *ic); -void camel_imap_command_unref (CamelIMAPCommand *ic); - -/* returns 1 when complete, 0 if there is more to do, or -1 on error */ -int camel_imap_command_step (CamelIMAPCommand *ic); - -void camel_imap_command_reset (CamelIMAPCommand *ic); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __CAMEL_IMAP_COMMAND_H__ */ diff --git a/camel/providers/imap4/camel-imap-engine.c b/camel/providers/imap4/camel-imap-engine.c deleted file mode 100644 index 86afb12fd5..0000000000 --- a/camel/providers/imap4/camel-imap-engine.c +++ /dev/null @@ -1,1664 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include - -#include -#include - -#include "camel-imap-summary.h" -#include "camel-imap-command.h" -#include "camel-imap-stream.h" -#include "camel-imap-folder.h" -#include "camel-imap-utils.h" - -#include "camel-imap-engine.h" - -#define d(x) x - - -static void camel_imap_engine_class_init (CamelIMAPEngineClass *klass); -static void camel_imap_engine_init (CamelIMAPEngine *engine, CamelIMAPEngineClass *klass); -static void camel_imap_engine_finalize (CamelObject *object); - - -static CamelObjectClass *parent_class = NULL; - - -CamelType -camel_imap_engine_get_type (void) -{ - static CamelType type = 0; - - if (!type) { - type = camel_type_register (CAMEL_TYPE_IMAP_ENGINE, - "CamelIMAPEngine", - sizeof (CamelIMAPEngine), - sizeof (CamelIMAPEngineClass), - (CamelObjectClassInitFunc) camel_imap_engine_class_init, - NULL, - (CamelObjectInitFunc) camel_imap_engine_init, - (CamelObjectFinalizeFunc) camel_imap_engine_finalize); - } - - return type; -} - -static void -camel_imap_engine_class_init (CamelIMAPEngineClass *klass) -{ - parent_class = camel_type_get_global_classfuncs (CAMEL_OBJECT_TYPE); - - klass->tagprefix = 'A'; -} - -static void -camel_imap_engine_init (CamelIMAPEngine *engine, CamelIMAPEngineClass *klass) -{ - engine->state = CAMEL_IMAP_ENGINE_DISCONNECTED; - engine->level = CAMEL_IMAP_LEVEL_UNKNOWN; - - engine->session = NULL; - engine->url = NULL; - - engine->istream = NULL; - engine->ostream = NULL; - - engine->authtypes = g_hash_table_new (g_str_hash, g_str_equal); - - engine->capa = 0; - - /* this is the suggested default, impacts the max command line length we'll send */ - engine->maxlentype = CAMEL_IMAP_ENGINE_MAXLEN_LINE; - engine->maxlen = 1000; - - engine->namespaces.personal = NULL; - engine->namespaces.other = NULL; - engine->namespaces.shared = NULL; - - if (klass->tagprefix > 'Z') - klass->tagprefix = 'A'; - - engine->tagprefix = klass->tagprefix++; - engine->tag = 0; - - engine->nextid = 1; - - engine->folder = NULL; - - e_dlist_init (&engine->queue); -} - -static void -imap_namespace_clear (CamelIMAPNamespace **namespace) -{ - CamelIMAPNamespace *node, *next; - - node = *namespace; - while (node != NULL) { - next = node->next; - g_free (node->path); - g_free (node); - node = next; - } - - *namespace = NULL; -} - -static void -camel_imap_engine_finalize (CamelObject *object) -{ - CamelIMAPEngine *engine = (CamelIMAPEngine *) object; - EDListNode *node; - - if (engine->session) - camel_object_unref (engine->session); - - if (engine->istream) - camel_object_unref (engine->istream); - - if (engine->ostream) - camel_object_unref (engine->ostream); - - g_hash_table_foreach (engine->authtypes, (GHFunc) g_free, NULL); - g_hash_table_destroy (engine->authtypes); - - imap_namespace_clear (&engine->namespaces.personal); - imap_namespace_clear (&engine->namespaces.other); - imap_namespace_clear (&engine->namespaces.shared); - - if (engine->folder) - camel_object_unref (engine->folder); - - while ((node = e_dlist_remhead (&engine->queue))) { - node->next = NULL; - node->prev = NULL; - - camel_imap_command_unref ((CamelIMAPCommand *) node); - } -} - - -/** - * camel_imap_engine_new: - * @session: session - * @url: service url - * - * Returns a new imap engine - **/ -CamelIMAPEngine * -camel_imap_engine_new (CamelSession *session, CamelURL *url) -{ - CamelIMAPEngine *engine; - - g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); - - engine = (CamelIMAPEngine *) camel_object_new (CAMEL_TYPE_IMAP_ENGINE); - camel_object_ref (session); - engine->session = session; - engine->url = url; - - return engine; -} - - -/** - * camel_imap_engine_take_stream: - * @engine: imap engine - * @stream: tcp stream - * @ex: exception - * - * Gives ownership of @stream to @engine and reads the greeting from - * the stream. - * - * Returns 0 on success or -1 on fail. - * - * Note: on error, @stream will be unref'd. - **/ -int -camel_imap_engine_take_stream (CamelIMAPEngine *engine, CamelStream *stream, CamelException *ex) -{ - camel_imap_token_t token; - int code; - - g_return_val_if_fail (CAMEL_IS_IMAP_ENGINE (engine), -1); - g_return_val_if_fail (CAMEL_IS_STREAM (stream), -1); - - if (engine->istream) - camel_object_unref (engine->istream); - - if (engine->ostream) - camel_object_unref (engine->ostream); - - engine->istream = (CamelIMAPStream *) camel_imap_stream_new (stream); - engine->ostream = camel_stream_buffer_new (stream, CAMEL_STREAM_BUFFER_WRITE); - engine->state = CAMEL_IMAP_ENGINE_CONNECTED; - camel_object_unref (stream); - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - if (token.token != '*') { - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if ((code = camel_imap_engine_handle_untagged_1 (engine, &token, ex)) == -1) { - goto exception; - } else if (code != CAMEL_IMAP_UNTAGGED_OK && code != CAMEL_IMAP_UNTAGGED_PREAUTH) { - /* FIXME: set an error? */ - goto exception; - } - - return 0; - - exception: - - engine->state = CAMEL_IMAP_ENGINE_DISCONNECTED; - - camel_object_unref (engine->istream); - engine->istream = NULL; - camel_object_unref (engine->ostream); - engine->ostream = NULL; - - return -1; -} - - -/** - * camel_imap_engine_capability: - * @engine: IMAP engine - * @ex: exception - * - * Forces the IMAP engine to query the IMAP server for a list of capabilities. - * - * Returns 0 on success or -1 on fail. - **/ -int -camel_imap_engine_capability (CamelIMAPEngine *engine, CamelException *ex) -{ - CamelIMAPCommand *ic; - int id, retval = 0; - - ic = camel_imap_engine_queue (engine, NULL, "CAPABILITY\r\n"); - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - retval = -1; - } - - camel_imap_command_unref (ic); - - return retval; -} - - -/** - * camel_imap_engine_namespace: - * @engine: IMAP engine - * @ex: exception - * - * Forces the IMAP engine to query the IMAP server for a list of namespaces. - * - * Returns 0 on success or -1 on fail. - **/ -int -camel_imap_engine_namespace (CamelIMAPEngine *engine, CamelException *ex) -{ - camel_imap_list_t *list; - GPtrArray *array = NULL; - CamelIMAPCommand *ic; - int id, i; - - if (engine->capa & CAMEL_IMAP_CAPABILITY_NAMESPACE) { - ic = camel_imap_engine_queue (engine, NULL, "NAMESPACE\r\n"); - } else { - ic = camel_imap_engine_queue (engine, NULL, "LIST \"\" \"\"\r\n"); - camel_imap_command_register_untagged (ic, "LIST", camel_imap_untagged_list); - ic->user_data = array = g_ptr_array_new (); - } - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - return -1; - } - - if (array != NULL) { - if (ic->result == CAMEL_IMAP_RESULT_OK) { - CamelIMAPNamespace *namespace; - - g_assert (array->len == 1); - list = array->pdata[0]; - - namespace = g_new (CamelIMAPNamespace, 1); - namespace->next = NULL; - namespace->path = g_strdup (""); - namespace->sep = list->delim; - - engine->namespaces.personal = namespace; - } else { - /* should never *ever* happen */ - } - - for (i = 0; i < array->len; i++) { - list = array->pdata[i]; - g_free (list->name); - g_free (list); - } - - g_ptr_array_free (array, TRUE); - } - - camel_imap_command_unref (ic); - - return 0; -} - - -int -camel_imap_engine_select_folder (CamelIMAPEngine *engine, CamelFolder *folder, CamelException *ex) -{ - CamelIMAPRespCode *resp; - CamelIMAPCommand *ic; - int id, retval = 0; - int i; - - g_return_val_if_fail (CAMEL_IS_IMAP_ENGINE (engine), -1); - g_return_val_if_fail (CAMEL_IS_IMAP_FOLDER (folder), -1); - - /* POSSIBLE FIXME: if the folder to be selected will already - * be selected by the time the queue is emptied, simply - * no-op? */ - - ic = camel_imap_engine_queue (engine, folder, "SELECT %F\r\n", folder); - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - return -1; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_OK: - /*folder->mode = 0;*/ - for (i = 0; i < ic->resp_codes->len; i++) { - resp = ic->resp_codes->pdata[i]; - switch (resp->code) { - case CAMEL_IMAP_RESP_CODE_PERM_FLAGS: - folder->permanent_flags = resp->v.flags; - break; - case CAMEL_IMAP_RESP_CODE_READONLY: - /*folder->mode = CAMEL_FOLDER_MODE_READ_ONLY;*/ - break; - case CAMEL_IMAP_RESP_CODE_READWRITE: - /*folder->mode = CAMEL_FOLDER_MODE_READ_WRITE;*/ - break; - case CAMEL_IMAP_RESP_CODE_UIDNEXT: - camel_imap_summary_set_uidnext (folder->summary, resp->v.uidnext); - break; - case CAMEL_IMAP_RESP_CODE_UIDVALIDITY: - camel_imap_summary_set_uidvalidity (folder->summary, resp->v.uidvalidity); - break; - case CAMEL_IMAP_RESP_CODE_UNSEEN: - camel_imap_summary_set_unseen (folder->summary, resp->v.unseen); - break; - default: - break; - } - } - - /*if (folder->mode == 0) { - folder->mode = CAMEL_FOLDER_MODE_READ_ONLY; - g_warning ("Expected to find [READ-ONLY] or [READ-WRITE] in SELECT response"); - }*/ - - break; - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot select folder `%s': Invalid mailbox name"), - folder->full_name); - retval = -1; - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot select folder `%s': Bad command"), - folder->full_name); - retval = -1; - break; - default: - g_assert_not_reached (); - retval = -1; - } - - camel_imap_command_unref (ic); - - return retval; -} - - -static struct { - const char *name; - guint32 flag; -} imap_capabilities[] = { - { "IMAP4", CAMEL_IMAP_CAPABILITY_IMAP4 }, - { "IMAP4REV1", CAMEL_IMAP_CAPABILITY_IMAP4REV1 }, - { "STATUS", CAMEL_IMAP_CAPABILITY_STATUS }, - { "NAMESPACE", CAMEL_IMAP_CAPABILITY_NAMESPACE }, - { "UIDPLUS", CAMEL_IMAP_CAPABILITY_UIDPLUS }, - { "LITERAL+", CAMEL_IMAP_CAPABILITY_LITERALPLUS }, - { "LOGINDISABLED", CAMEL_IMAP_CAPABILITY_LOGINDISABLED }, - { "STARTTLS", CAMEL_IMAP_CAPABILITY_STARTTLS }, - { NULL, 0 } -}; - -static gboolean -auth_free (gpointer key, gpointer value, gpointer user_data) -{ - g_free (key); - return TRUE; -} - -static int -engine_parse_capability (CamelIMAPEngine *engine, int sentinel, CamelException *ex) -{ - camel_imap_token_t token; - int i; - - engine->capa = CAMEL_IMAP_CAPABILITY_utf8_search; - engine->level = 0; - - g_hash_table_foreach_remove (engine->authtypes, (GHRFunc) auth_free, NULL); - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - while (token.token == CAMEL_IMAP_TOKEN_ATOM) { - if (!strncasecmp ("AUTH=", token.v.atom, 5)) { - CamelServiceAuthType *auth; - - if ((auth = camel_sasl_authtype (token.v.atom + 5)) != NULL) - g_hash_table_insert (engine->authtypes, g_strdup (token.v.atom + 5), auth); - } else { - for (i = 0; imap_capabilities[i].name; i++) { - if (!strcasecmp (imap_capabilities[i].name, token.v.atom)) - engine->capa |= imap_capabilities[i].flag; - } - } - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - } - - if (token.token != sentinel) { - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - /* unget our sentinel token */ - camel_imap_stream_unget_token (engine->istream, &token); - - /* figure out which version of IMAP we are dealing with */ - if (engine->capa & CAMEL_IMAP_CAPABILITY_IMAP4REV1) { - engine->level = CAMEL_IMAP_LEVEL_IMAP4REV1; - engine->capa |= CAMEL_IMAP_CAPABILITY_STATUS; - } else if (engine->capa & CAMEL_IMAP_CAPABILITY_IMAP4) { - engine->level = CAMEL_IMAP_LEVEL_IMAP4; - } else { - engine->level = CAMEL_IMAP_LEVEL_UNKNOWN; - } - - return 0; -} - -static int -engine_parse_flags_list (CamelIMAPEngine *engine, CamelIMAPRespCode *resp, int perm, CamelException *ex) -{ - guint32 flags = 0; - - if (camel_imap_parse_flags_list (engine, &flags, ex) == -1) - return-1; - - if (resp != NULL) - resp->v.flags = flags; - - if (engine->current && engine->current->folder) { - if (perm) - ((CamelFolder *) engine->current->folder)->permanent_flags = flags; - /*else - ((CamelFolder *) engine->current->folder)->folder_flags = flags;*/ - } else if (engine->folder) { - if (perm) - ((CamelFolder *) engine->folder)->permanent_flags = flags; - /*else - ((CamelFolder *) engine->folder)->folder_flags = flags;*/ - } else { - fprintf (stderr, "We seem to be in a bit of a pickle. we've just parsed an untagged %s\n" - "response for a folder, yet we do not currently have a folder selected?\n", - perm ? "PERMANENTFLAGS" : "FLAGS"); - } - - return 0; -} - -static int -engine_parse_flags (CamelIMAPEngine *engine, CamelException *ex) -{ - camel_imap_token_t token; - - if (engine_parse_flags_list (engine, NULL, FALSE, ex) == -1) - return -1; - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != '\n') { - d(fprintf (stderr, "Expected to find a '\\n' token after the FLAGS response\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - return 0; -} - - -enum { - IMAP_STATUS_MESSAGES, - IMAP_STATUS_RECENT, - IMAP_STATUS_UIDNEXT, - IMAP_STATUS_UIDVALIDITY, - IMAP_STATUS_UNSEEN, - IMAP_STATUS_UNKNOWN -}; - -static struct { - const char *name; - int type; -} imap_status[] = { - { "MESSAGES", IMAP_STATUS_MESSAGES }, - { "RECENT", IMAP_STATUS_RECENT }, - { "UIDNEXT", IMAP_STATUS_UIDNEXT }, - { "UIDVALIDITY", IMAP_STATUS_UIDVALIDITY }, - { "UNSEEN", IMAP_STATUS_UNSEEN }, - { NULL, IMAP_STATUS_UNKNOWN }, -}; - -static int -engine_parse_status (CamelIMAPEngine *engine, CamelException *ex) -{ - camel_imap_token_t token; - char *mailbox; - size_t len; - int type; - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - switch (token.token) { - case CAMEL_IMAP_TOKEN_ATOM: - mailbox = g_strdup (token.v.atom); - break; - case CAMEL_IMAP_TOKEN_QSTRING: - mailbox = g_strdup (token.v.qstring); - break; - case CAMEL_IMAP_TOKEN_LITERAL: - if (camel_imap_engine_literal (engine, (unsigned char **) &mailbox, &len, ex) == -1) - return -1; - break; - default: - fprintf (stderr, "Unexpected token in IMAP untagged STATUS response: %s%c\n", - token.token == CAMEL_IMAP_TOKEN_NIL ? "NIL" : "", - (unsigned char) (token.token & 0xff)); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) { - g_free (mailbox); - return -1; - } - - if (token.token != '(') { - d(fprintf (stderr, "Expected to find a '(' token after the mailbox token in the STATUS response\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - g_free (mailbox); - return -1; - } - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) { - g_free (mailbox); - return -1; - } - - while (token.token == CAMEL_IMAP_TOKEN_ATOM) { - const unsigned char *inptr; - unsigned int v = 0; - - /* parse the status messages list */ - for (type = 0; imap_status[type].name; type++) { - if (!strcasecmp (imap_status[type].name, token.v.atom)) - break; - } - - if (type == IMAP_STATUS_UNKNOWN) - fprintf (stderr, "unrecognized token in STATUS list: %s\n", token.v.atom); - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) { - g_free (mailbox); - return -1; - } - - if (token.token != CAMEL_IMAP_TOKEN_ATOM) - break; - - if (type == IMAP_STATUS_UIDNEXT || type == IMAP_STATUS_UIDVALIDITY) { - /* these tokens should be numeric, but we - * treat them as strings internally so we are - * special-casing them here */ - - /* FIXME: save the UIDNEXT/UIDVALIDITY value */ - } else { - inptr = (const unsigned char *) token.v.atom; - while (*inptr && isdigit ((int) *inptr) && v < (UINT_MAX / 10)) - v = (v * 10) + (*inptr++ - '0'); - - if (*inptr != '\0') { - if (type == IMAP_STATUS_UNKNOWN) { - /* we'll let it slide... unget this token and continue */ - camel_imap_stream_unget_token (engine->istream, &token); - goto loop; - } - - d(fprintf (stderr, "Encountered non-numeric token after %s in untagged STATUS response: %s\n", - imap_status[type].name, token.v.atom)); - goto loop; - } - - switch (type) { - case IMAP_STATUS_MESSAGES: - /* FIXME: save value */ - break; - case IMAP_STATUS_RECENT: - /* FIXME: save value */ - break; - case IMAP_STATUS_UNSEEN: - /* FIXME: save value */ - break; - default: - g_assert_not_reached (); - } - } - - loop: - if (camel_imap_engine_next_token (engine, &token, ex) == -1) { - g_free (mailbox); - return -1; - } - } - - /* don't need this anymore... */ - g_free (mailbox); - - if (token.token != ')') { - d(fprintf (stderr, "Expected to find a ')' token terminating the untagged STATUS response\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != '\n') { - d(fprintf (stderr, "Expected to find a '\\n' token after the STATUS response\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - return 0; -} - -static int -engine_parse_namespace (CamelIMAPEngine *engine, CamelException *ex) -{ - CamelIMAPNamespace *namespaces[3], *node, *tail; - camel_imap_token_t token; - int i, n = 0; - - imap_namespace_clear (&engine->namespaces.personal); - imap_namespace_clear (&engine->namespaces.other); - imap_namespace_clear (&engine->namespaces.shared); - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - do { - namespaces[n] = NULL; - tail = (CamelIMAPNamespace *) &namespaces[n]; - - if (token.token == '(') { - /* decode the list of namespace pairs */ - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - while (token.token == '(') { - /* decode a namespace pair */ - - /* get the path name token */ - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - if (token.token != CAMEL_IMAP_TOKEN_QSTRING) { - d(fprintf (stderr, "Expected to find a qstring token as first element in NAMESPACE pair\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - node = g_new (CamelIMAPNamespace, 1); - node->next = NULL; - node->path = g_strdup (token.v.qstring); - - /* get the path delimiter token */ - if (camel_imap_engine_next_token (engine, &token, ex) == -1) { - g_free (node->path); - g_free (node); - - goto exception; - } - - if (token.token != CAMEL_IMAP_TOKEN_QSTRING || strlen (token.v.qstring) > 1) { - d(fprintf (stderr, "Expected to find a qstring token as second element in NAMESPACE pair\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - g_free (node->path); - g_free (node); - - goto exception; - } - - node->sep = *token.v.qstring; - tail->next = node; - tail = node; - - /* canonicalise the namespace path */ - if (node->path[strlen (node->path) - 1] == node->sep) - node->path[strlen (node->path) - 1] = '\0'; - - /* get the closing ')' for this namespace pair */ - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - if (token.token != ')') { - d(fprintf (stderr, "Expected to find a ')' token to close the current namespace pair\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - - goto exception; - } - - /* get the next token (should be either '(' or ')') */ - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - } - - if (token.token != ')') { - d(fprintf (stderr, "Expected to find a ')' to close the current namespace list\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - } else if (token.token == CAMEL_IMAP_TOKEN_NIL) { - /* namespace list is NIL */ - namespaces[n] = NULL; - } else { - d(fprintf (stderr, "Expected to find either NIL or '(' token in untagged NAMESPACE response\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - /* get the next token (should be either '(', NIL, or '\n') */ - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - n++; - } while (n < 3); - - engine->namespaces.personal = namespaces[0]; - engine->namespaces.other = namespaces[1]; - engine->namespaces.shared = namespaces[2]; - - return 0; - - exception: - - for (i = 0; i <= n; i++) - imap_namespace_clear (&namespaces[i]); - - return -1; -} - - -/** - * - * resp-text-code = "ALERT" / - * "BADCHARSET" [SP "(" astring *(SP astring) ")" ] / - * capability-data / "PARSE" / - * "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" / - * "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / - * "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number / - * "UNSEEN" SP nz-number / - * atom [SP 1*] - **/ - -static struct { - const char *name; - camel_imap_resp_code_t code; - int save; -} imap_resp_codes[] = { - { "ALERT", CAMEL_IMAP_RESP_CODE_ALERT, 0 }, - { "BADCHARSET", CAMEL_IMAP_RESP_CODE_BADCHARSET, 0 }, - { "CAPABILITY", CAMEL_IMAP_RESP_CODE_CAPABILITY, 0 }, - { "PARSE", CAMEL_IMAP_RESP_CODE_PARSE, 1 }, - { "PERMANENTFLAGS", CAMEL_IMAP_RESP_CODE_PERM_FLAGS, 1 }, - { "READ-ONLY", CAMEL_IMAP_RESP_CODE_READONLY, 1 }, - { "READ-WRITE", CAMEL_IMAP_RESP_CODE_READWRITE, 1 }, - { "TRYCREATE", CAMEL_IMAP_RESP_CODE_TRYCREATE, 1 }, - { "UIDNEXT", CAMEL_IMAP_RESP_CODE_UIDNEXT, 1 }, - { "UIDVALIDITY", CAMEL_IMAP_RESP_CODE_UIDVALIDITY, 1 }, - { "UNSEEN", CAMEL_IMAP_RESP_CODE_UNSEEN, 1 }, - { "NEWNAME", CAMEL_IMAP_RESP_CODE_NEWNAME, 1 }, - { "APPENDUID", CAMEL_IMAP_RESP_CODE_APPENDUID, 1 }, - { "COPYUID", CAMEL_IMAP_RESP_CODE_COPYUID, 1 }, - { NULL, CAMEL_IMAP_RESP_CODE_UNKNOWN, 0 } -}; - - -int -camel_imap_engine_parse_resp_code (CamelIMAPEngine *engine, CamelException *ex) -{ - CamelIMAPRespCode *resp = NULL; - camel_imap_resp_code_t code; - camel_imap_token_t token; - unsigned char *linebuf; - size_t len; - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != '[') { - d(fprintf (stderr, "Expected a '[' token (followed by a RESP-CODE)\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_ATOM) { - d(fprintf (stderr, "Expected an atom token containing a RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - for (code = 0; imap_resp_codes[code].name; code++) { - if (!strcmp (imap_resp_codes[code].name, token.v.atom)) { - if (engine->current && imap_resp_codes[code].save) { - resp = g_new0 (CamelIMAPRespCode, 1); - resp->code = code; - } - break; - } - } - - switch (code) { - case CAMEL_IMAP_RESP_CODE_BADCHARSET: - /* apparently we don't support UTF-8 afterall */ - engine->capa &= ~CAMEL_IMAP_CAPABILITY_utf8_search; - break; - case CAMEL_IMAP_RESP_CODE_CAPABILITY: - /* capability list follows */ - if (engine_parse_capability (engine, ']', ex) == -1) - goto exception; - break; - case CAMEL_IMAP_RESP_CODE_PERM_FLAGS: - /* flag list follows */ - if (engine_parse_flags_list (engine, resp, TRUE, ex) == -1) - goto exception; - break; - case CAMEL_IMAP_RESP_CODE_READONLY: - break; - case CAMEL_IMAP_RESP_CODE_READWRITE: - break; - case CAMEL_IMAP_RESP_CODE_TRYCREATE: - break; - case CAMEL_IMAP_RESP_CODE_UIDNEXT: - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - if (token.token != CAMEL_IMAP_TOKEN_NUMBER) { - d(fprintf (stderr, "Expected an nz_number token as an argument to the UIDNEXT RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.uidnext = token.v.number; - - break; - case CAMEL_IMAP_RESP_CODE_UIDVALIDITY: - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - if (token.token != CAMEL_IMAP_TOKEN_NUMBER) { - d(fprintf (stderr, "Expected an nz_number token as an argument to the UIDVALIDITY RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.uidvalidity = token.v.number; - - break; - case CAMEL_IMAP_RESP_CODE_UNSEEN: - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_NUMBER) { - d(fprintf (stderr, "Expected an nz_number token as an argument to the UNSEEN RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.unseen = token.v.number; - - break; - case CAMEL_IMAP_RESP_CODE_NEWNAME: - /* this RESP-CODE may actually be removed - see here: - * http://www.washington.edu/imap/listarch/2001/msg00058.html */ - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_ATOM && token.token != CAMEL_IMAP_TOKEN_QSTRING) { - d(fprintf (stderr, "Expected an atom or qstring token as the first argument to the NEWNAME RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.newname[0] = g_strdup (token.v.atom); - - if (token.token != CAMEL_IMAP_TOKEN_ATOM && token.token != CAMEL_IMAP_TOKEN_QSTRING) { - d(fprintf (stderr, "Expected an atom or qstring token as the second argument to the NEWNAME RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.newname[1] = g_strdup (token.v.atom); - - break; - case CAMEL_IMAP_RESP_CODE_APPENDUID: - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_NUMBER) { - d(fprintf (stderr, "Expected an nz_number token as the first argument to the APPENDUID RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.appenduid.uidvalidity = token.v.number; - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_NUMBER) { - d(fprintf (stderr, "Expected an nz_number token as the second argument to the APPENDUID RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.appenduid.uid = token.v.number; - - break; - case CAMEL_IMAP_RESP_CODE_COPYUID: - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_NUMBER) { - d(fprintf (stderr, "Expected an nz_number token as the first argument to the COPYUID RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.copyuid.uidvalidity = token.v.number; - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_ATOM) { - d(fprintf (stderr, "Expected an atom token as the second argument to the COPYUID RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.copyuid.srcset = g_strdup (token.v.atom); - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != CAMEL_IMAP_TOKEN_ATOM) { - d(fprintf (stderr, "Expected an atom token as the third argument to the APPENDUID RESP-CODE\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - goto exception; - } - - if (resp != NULL) - resp->v.copyuid.destset = g_strdup (token.v.atom); - - break; - default: - d(fprintf (stderr, "Unknown RESP-CODE encountered: %s\n", token.v.atom)); - - /* extensions are of the form: "[" atom [SPACE 1*] "]" */ - - /* eat up the TEXT_CHARs */ - while (token.token != ']' && token.token != '\n') { - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - } - - break; - } - - while (token.token != ']' && token.token != '\n') { - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - } - - if (token.token != ']') { - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - d(fprintf (stderr, "Expected to find a ']' token after the RESP-CODE\n")); - return -1; - } - - if (code == CAMEL_IMAP_RESP_CODE_ALERT) { - if (camel_imap_engine_line (engine, &linebuf, &len, ex) == -1) - goto exception; - - camel_session_alert_user (engine->session, CAMEL_SESSION_ALERT_INFO, linebuf, FALSE); - g_free (linebuf); - } else if (resp != NULL && code == CAMEL_IMAP_RESP_CODE_PARSE) { - if (camel_imap_engine_line (engine, &linebuf, &len, ex) == -1) - goto exception; - - resp->v.parse = linebuf; - } else { - /* eat up the rest of the response */ - if (camel_imap_engine_line (engine, NULL, NULL, ex) == -1) - goto exception; - } - - if (resp != NULL) - g_ptr_array_add (engine->current->resp_codes, resp); - - return 0; - - exception: - - if (resp != NULL) - camel_imap_resp_code_free (resp); - - return -1; -} - - - -/* returns -1 on error, or one of CAMEL_IMAP_UNTAGGED_[OK,NO,BAD,PREAUTH,HANDLED] on success */ -int -camel_imap_engine_handle_untagged_1 (CamelIMAPEngine *engine, camel_imap_token_t *token, CamelException *ex) -{ - int code = CAMEL_IMAP_UNTAGGED_HANDLED; - CamelIMAPCommand *ic = engine->current; - CamelIMAPUntaggedCallback untagged; - CamelFolder *folder; - unsigned int v; - - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - if (token->token == CAMEL_IMAP_TOKEN_ATOM) { - if (!strcmp ("BYE", token->v.atom)) { - /* we don't care if we fail here, either way we've been disconnected */ - camel_imap_engine_parse_resp_code (engine, NULL); - engine->state = CAMEL_IMAP_ENGINE_DISCONNECTED; - - /* FIXME: emit a "disconnected" signal for our Store? - * The Store could then initiate a reconnect if - * desirable. */ - - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("IMAP server %s unexpectedly disconnected: %s"), - engine->url->host, _("Got BYE response")); - - return -1; - } else if (!strcmp ("CAPABILITY", token->v.atom)) { - /* capability tokens follow */ - if (engine_parse_capability (engine, '\n', ex) == -1) - return -1; - - /* find the eoln token */ - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - if (token->token != '\n') { - camel_imap_utils_set_unexpected_token_error (ex, engine, token); - return -1; - } - } else if (!strcmp ("FLAGS", token->v.atom)) { - /* flags list follows */ - if (engine_parse_flags (engine, ex) == -1) - return -1; - } else if (!strcmp ("NAMESPACE", token->v.atom)) { - if (engine_parse_namespace (engine, ex) == -1) - return -1; - } else if (!strcmp ("NO", token->v.atom) || !strcmp ("BAD", token->v.atom)) { - code = !strcmp ("NO", token->v.atom) ? CAMEL_IMAP_UNTAGGED_NO : CAMEL_IMAP_UNTAGGED_BAD; - - /* our command has been rejected */ - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - if (token->token == '[') { - /* we have a resp code */ - camel_imap_stream_unget_token (engine->istream, token); - if (camel_imap_engine_parse_resp_code (engine, ex) == -1) - return -1; - } else if (token->token != '\n') { - /* we just have resp text */ - if (camel_imap_engine_line (engine, NULL, NULL, ex) == -1) - return -1; - } - } else if (!strcmp ("OK", token->v.atom)) { - code = CAMEL_IMAP_UNTAGGED_OK; - - if (engine->state == CAMEL_IMAP_ENGINE_CONNECTED) { - /* initial server greeting */ - engine->state = CAMEL_IMAP_ENGINE_PREAUTH; - } - - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - if (token->token == '[') { - /* we have a resp code */ - camel_imap_stream_unget_token (engine->istream, token); - if (camel_imap_engine_parse_resp_code (engine, ex) == -1) - return -1; - } else { - /* we just have resp text */ - if (camel_imap_engine_line (engine, NULL, NULL, ex) == -1) - return -1; - } - } else if (!strcmp ("PREAUTH", token->v.atom)) { - code = CAMEL_IMAP_UNTAGGED_PREAUTH; - - if (engine->state == CAMEL_IMAP_ENGINE_CONNECTED) - engine->state = CAMEL_IMAP_ENGINE_AUTHENTICATED; - - if (camel_imap_engine_parse_resp_code (engine, ex) == -1) - return -1; - } else if (!strcmp ("STATUS", token->v.atom)) { - /* FIXME: This should probably be removed... leave it - * up to the caller that sent the STATUS command to - * register an untagged response handler for this */ - - /* next token must be the mailbox name followed by a paren list */ - if (engine_parse_status (engine, ex) == -1) - return -1; - } else if (ic && (untagged = g_hash_table_lookup (ic->untagged, token->v.atom))) { - /* registered untagged handler for imap command */ - if (untagged (engine, ic, 0, token, ex) == -1) - return -1; - } else { - d(fprintf (stderr, "Unhandled atom token in untagged response: %s", token->v.atom)); - - if (camel_imap_engine_eat_line (engine, ex) == -1) - return -1; - } - } else if (token->token == CAMEL_IMAP_TOKEN_NUMBER) { - /* we probably have something like "* 1 EXISTS" */ - v = token->v.number; - - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - if (token->token != CAMEL_IMAP_TOKEN_ATOM) { - camel_imap_utils_set_unexpected_token_error (ex, engine, token); - - return -1; - } - - /* which folder is this EXISTS/EXPUNGE/RECENT acting on? */ - if (engine->current && engine->current->folder) - folder = (CamelFolder *) engine->current->folder; - else if (engine->folder) - folder = (CamelFolder *) engine->folder; - else - folder = NULL; - - /* NOTE: these can be over-ridden by a registered untagged response handler */ - if (!strcmp ("EXISTS", token->v.atom)) { - camel_imap_summary_set_exists (folder->summary, v); - } else if (!strcmp ("EXPUNGE", token->v.atom)) { - camel_imap_summary_expunge (folder->summary, (int) v); - } else if (!strcmp ("RECENT", token->v.atom)) { - camel_imap_summary_set_recent (folder->summary, v); - } else if (ic && (untagged = g_hash_table_lookup (ic->untagged, token->v.atom))) { - /* registered untagged handler for imap command */ - if (untagged (engine, ic, v, token, ex) == -1) - return -1; - } else { - d(fprintf (stderr, "Unrecognized untagged response: * %u %s\n", v, token->v.atom)); - } - - /* find the eoln token */ - if (camel_imap_engine_eat_line (engine, ex) == -1) - return -1; - } else { - camel_imap_utils_set_unexpected_token_error (ex, engine, token); - - return -1; - } - - return code; -} - - -void -camel_imap_engine_handle_untagged (CamelIMAPEngine *engine, CamelException *ex) -{ - camel_imap_token_t token; - - g_return_if_fail (CAMEL_IS_IMAP_ENGINE (engine)); - - do { - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - goto exception; - - if (token.token != '*') - break; - - if (camel_imap_engine_handle_untagged_1 (engine, &token, ex) == -1) - goto exception; - } while (1); - - camel_imap_stream_unget_token (engine->istream, &token); - - return; - - exception: - - engine->state = CAMEL_IMAP_ENGINE_DISCONNECTED; -} - - -static int -imap_process_command (CamelIMAPEngine *engine, CamelIMAPCommand *ic) -{ - int retval; - - while ((retval = camel_imap_command_step (ic)) == 0) - ; - - if (retval == -1) { - engine->state = CAMEL_IMAP_ENGINE_DISCONNECTED; - return -1; - } - - return 0; -} - - -static void -engine_prequeue_folder_select (CamelIMAPEngine *engine) -{ - CamelIMAPCommand *ic; - const char *cmd; - - ic = (CamelIMAPCommand *) engine->queue.head; - cmd = (const char *) ic->parts->buffer; - - if (!ic->folder || ic->folder == engine->folder || - !strncmp (cmd, "SELECT ", 7) || !strncmp (cmd, "EXAMINE ", 8)) { - /* no need to pre-queue a SELECT */ - return; - } - - /* we need to pre-queue a SELECT */ - ic = camel_imap_command_new (engine, ic->folder, "SELECT %F\r\n", ic->folder); - camel_imap_engine_prequeue (engine, ic); - ic->user_data = engine; - - camel_imap_command_unref (ic); -} - - -static int -engine_state_change (CamelIMAPEngine *engine, CamelIMAPCommand *ic) -{ - const char *cmd; - int retval = 0; - - cmd = ic->parts->buffer; - if (!strncmp (cmd, "SELECT ", 7) || !strncmp (cmd, "EXAMINE ", 8)) { - if (ic->result == CAMEL_IMAP_RESULT_OK) { - /* Update the selected folder */ - camel_object_ref (ic->folder); - if (engine->folder) - camel_object_unref (engine->folder); - engine->folder = ic->folder; - - engine->state = CAMEL_IMAP_ENGINE_SELECTED; - } else if (ic->user_data == engine) { - /* the engine pre-queued this SELECT command */ - retval = -1; - } - } else if (!strncmp (cmd, "CLOSE", 5)) { - if (ic->result == CAMEL_IMAP_RESULT_OK) - engine->state = CAMEL_IMAP_ENGINE_AUTHENTICATED; - } else if (!strncmp (cmd, "LOGOUT", 6)) { - engine->state = CAMEL_IMAP_ENGINE_DISCONNECTED; - } - - return retval; -} - -/** - * camel_imap_engine_iterate: - * @engine: IMAP engine - * - * Processes the first command in the queue. - * - * Returns the id of the processed command, 0 if there were no - * commands to process, or -1 on error. - * - * Note: more details on the error will be held on the - * CamelIMAPCommand that failed. - **/ -int -camel_imap_engine_iterate (CamelIMAPEngine *engine) -{ - CamelIMAPCommand *ic, *nic; - GPtrArray *resp_codes; - int retval = -1; - - if (e_dlist_empty (&engine->queue)) - return 0; - - /* check to see if we need to pre-queue a SELECT, if so do it */ - engine_prequeue_folder_select (engine); - - engine->current = ic = (CamelIMAPCommand *) e_dlist_remhead (&engine->queue); - ic->status = CAMEL_IMAP_COMMAND_ACTIVE; - - if (imap_process_command (engine, ic) != -1) { - if (engine_state_change (engine, ic) == -1) { - /* This can ONLY happen if @ic was the pre-queued SELECT command - * and it got a NO or BAD response. - * - * We have to pop the next imap command or we'll get into an - * infinite loop. In order to provide @nic's owner with as much - * information as possible, we move all @ic status information - * over to @nic and pretend we just processed @nic. - **/ - - nic = (CamelIMAPCommand *) e_dlist_remhead (&engine->queue); - - nic->status = ic->status; - nic->result = ic->result; - resp_codes = nic->resp_codes; - nic->resp_codes = ic->resp_codes; - ic->resp_codes = resp_codes; - - camel_exception_xfer (&nic->ex, &ic->ex); - - camel_imap_command_unref (ic); - ic = nic; - } - - retval = ic->id; - } - - camel_imap_command_unref (ic); - - return retval; -} - - -/** - * camel_imap_engine_queue: - * @engine: IMAP engine - * @folder: IMAP folder that the command will affect (or %NULL if it doesn't matter) - * @format: command format - * @Varargs: arguments - * - * Basically the same as #camel_imap_command_new() except that this - * function also places the command in the engine queue. - * - * Returns the CamelIMAPCommand. - **/ -CamelIMAPCommand * -camel_imap_engine_queue (CamelIMAPEngine *engine, CamelFolder *folder, const char *format, ...) -{ - CamelIMAPCommand *ic; - va_list args; - - va_start (args, format); - ic = camel_imap_command_newv (engine, (CamelIMAPFolder *) folder, format, args); - va_end (args); - - ic->id = engine->nextid++; - e_dlist_addtail (&engine->queue, (EDListNode *) ic); - camel_imap_command_ref (ic); - - return ic; -} - - -/** - * camel_imap_engine_prequeue: - * @engine: IMAP engine - * @ic: IMAP command to pre-queue - * - * Places @ic at the head of the queue of pending IMAP commands. - **/ -void -camel_imap_engine_prequeue (CamelIMAPEngine *engine, CamelIMAPCommand *ic) -{ - g_return_if_fail (CAMEL_IS_IMAP_ENGINE (engine)); - g_return_if_fail (ic != NULL); - - camel_imap_command_ref (ic); - - if (e_dlist_empty (&engine->queue)) { - e_dlist_addtail (&engine->queue, (EDListNode *) ic); - ic->id = engine->nextid++; - } else { - CamelIMAPCommand *nic; - EDListNode *node; - - node = (EDListNode *) ic; - e_dlist_addhead (&engine->queue, node); - nic = (CamelIMAPCommand *) node->next; - ic->id = nic->id - 1; - - if (ic->id == 0) { - /* increment all command ids */ - node = engine->queue.head; - while (node->next) { - nic = (CamelIMAPCommand *) node; - node = node->next; - nic->id++; - } - } - } -} - - -void -camel_imap_engine_dequeue (CamelIMAPEngine *engine, CamelIMAPCommand *ic) -{ - EDListNode *node = (EDListNode *) ic; - - if (node->next == NULL && node->prev == NULL) - return; - - e_dlist_remove (node); - node->next = NULL; - node->prev = NULL; - - camel_imap_command_unref (ic); -} - - -int -camel_imap_engine_next_token (CamelIMAPEngine *engine, camel_imap_token_t *token, CamelException *ex) -{ - if (camel_imap_stream_next_token (engine->istream, token) == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("IMAP server %s unexpectedly disconnected: %s"), - engine->url->host, errno ? g_strerror (errno) : _("Unknown")); - return -1; - } - - return 0; -} - - -int -camel_imap_engine_eat_line (CamelIMAPEngine *engine, CamelException *ex) -{ - camel_imap_token_t token; - unsigned char *literal; - int retval; - size_t n; - - do { - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token == CAMEL_IMAP_TOKEN_LITERAL) { - while ((retval = camel_imap_stream_literal (engine->istream, &literal, &n)) == 1) - ; - - if (retval == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("IMAP server %s unexpectedly disconnected: %s"), - engine->url->host, errno ? g_strerror (errno) : _("Unknown")); - - return -1; - } - } - } while (token.token != '\n'); - - return 0; -} - - -int -camel_imap_engine_line (CamelIMAPEngine *engine, unsigned char **line, size_t *len, CamelException *ex) -{ - GByteArray *linebuf = NULL; - unsigned char *buf; - size_t buflen; - int retval; - - if (line != NULL) - linebuf = g_byte_array_new (); - - while ((retval = camel_imap_stream_line (engine->istream, &buf, &buflen)) > 0) { - if (linebuf != NULL) - g_byte_array_append (linebuf, buf, buflen); - } - - if (retval == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("IMAP server %s unexpectedly disconnected: %s"), - engine->url->host, errno ? g_strerror (errno) : _("Unknown")); - - if (linebuf != NULL) - g_byte_array_free (linebuf, TRUE); - - return -1; - } - - if (linebuf != NULL) { - g_byte_array_append (linebuf, buf, buflen); - - *line = linebuf->data; - *len = linebuf->len; - - g_byte_array_free (linebuf, FALSE); - } - - return 0; -} - - -int -camel_imap_engine_literal (CamelIMAPEngine *engine, unsigned char **literal, size_t *len, CamelException *ex) -{ - GByteArray *literalbuf = NULL; - unsigned char *buf; - size_t buflen; - int retval; - - if (literal != NULL) - literalbuf = g_byte_array_new (); - - while ((retval = camel_imap_stream_literal (engine->istream, &buf, &buflen)) > 0) { - if (literalbuf != NULL) - g_byte_array_append (literalbuf, buf, buflen); - } - - if (retval == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("IMAP server %s unexpectedly disconnected: %s"), - engine->url->host, errno ? g_strerror (errno) : _("Unknown")); - - if (literalbuf != NULL) - g_byte_array_free (literalbuf, TRUE); - - return -1; - } - - if (literalbuf != NULL) { - g_byte_array_append (literalbuf, buf, buflen); - g_byte_array_append (literalbuf, "", 1); - - *literal = literalbuf->data; - *len = literalbuf->len - 1; - - g_byte_array_free (literalbuf, FALSE); - } - - return 0; -} - - -void -camel_imap_resp_code_free (CamelIMAPRespCode *rcode) -{ - switch (rcode->code) { - case CAMEL_IMAP_RESP_CODE_PARSE: - g_free (rcode->v.parse); - break; - case CAMEL_IMAP_RESP_CODE_NEWNAME: - g_free (rcode->v.newname[0]); - g_free (rcode->v.newname[1]); - break; - case CAMEL_IMAP_RESP_CODE_COPYUID: - g_free (rcode->v.copyuid.srcset); - g_free (rcode->v.copyuid.destset); - break; - default: - break; - } - - g_free (rcode); -} diff --git a/camel/providers/imap4/camel-imap-engine.h b/camel/providers/imap4/camel-imap-engine.h deleted file mode 100644 index 6612693243..0000000000 --- a/camel/providers/imap4/camel-imap-engine.h +++ /dev/null @@ -1,222 +0,0 @@ -/* -*- 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_ENGINE_H__ -#define __CAMEL_IMAP_ENGINE_H__ - -#include - -#include - -#include - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -#define CAMEL_TYPE_IMAP_ENGINE (camel_imap_engine_get_type ()) -#define CAMEL_IMAP_ENGINE(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP_ENGINE, CamelIMAPEngine)) -#define CAMEL_IMAP_ENGINE_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP_ENGINE, CamelIMAPEngineClass)) -#define CAMEL_IS_IMAP_ENGINE(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP_ENGINE)) -#define CAMEL_IS_IMAP_ENGINE_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP_ENGINE)) -#define CAMEL_IMAP_ENGINE_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_IMAP_ENGINE, CamelIMAPEngineClass)) - -typedef struct _CamelIMAPEngine CamelIMAPEngine; -typedef struct _CamelIMAPEngineClass CamelIMAPEngineClass; - -struct _camel_imap_token_t; -struct _CamelIMAPCommand; -struct _CamelIMAPFolder; -struct _CamelIMAPStream; - -typedef enum { - CAMEL_IMAP_ENGINE_DISCONNECTED, - CAMEL_IMAP_ENGINE_CONNECTED, - CAMEL_IMAP_ENGINE_PREAUTH, - CAMEL_IMAP_ENGINE_AUTHENTICATED, - CAMEL_IMAP_ENGINE_SELECTED, -} camel_imap_engine_t; - -typedef enum { - CAMEL_IMAP_LEVEL_UNKNOWN, - CAMEL_IMAP_LEVEL_IMAP4, - CAMEL_IMAP_LEVEL_IMAP4REV1 -} camel_imap_level_t; - -enum { - CAMEL_IMAP_CAPABILITY_IMAP4 = (1 << 0), - CAMEL_IMAP_CAPABILITY_IMAP4REV1 = (1 << 1), - CAMEL_IMAP_CAPABILITY_STATUS = (1 << 2), - CAMEL_IMAP_CAPABILITY_NAMESPACE = (1 << 3), - CAMEL_IMAP_CAPABILITY_UIDPLUS = (1 << 4), - CAMEL_IMAP_CAPABILITY_LITERALPLUS = (1 << 5), - CAMEL_IMAP_CAPABILITY_LOGINDISABLED = (1 << 6), - CAMEL_IMAP_CAPABILITY_STARTTLS = (1 << 7), - CAMEL_IMAP_CAPABILITY_useful_lsub = (1 << 8), - CAMEL_IMAP_CAPABILITY_utf8_search = (1 << 9), -}; - -typedef enum { - CAMEL_IMAP_RESP_CODE_ALERT, - CAMEL_IMAP_RESP_CODE_BADCHARSET, - CAMEL_IMAP_RESP_CODE_CAPABILITY, - CAMEL_IMAP_RESP_CODE_PARSE, - CAMEL_IMAP_RESP_CODE_PERM_FLAGS, - CAMEL_IMAP_RESP_CODE_READONLY, - CAMEL_IMAP_RESP_CODE_READWRITE, - CAMEL_IMAP_RESP_CODE_TRYCREATE, - CAMEL_IMAP_RESP_CODE_UIDNEXT, - CAMEL_IMAP_RESP_CODE_UIDVALIDITY, - CAMEL_IMAP_RESP_CODE_UNSEEN, - CAMEL_IMAP_RESP_CODE_NEWNAME, - CAMEL_IMAP_RESP_CODE_APPENDUID, - CAMEL_IMAP_RESP_CODE_COPYUID, - CAMEL_IMAP_RESP_CODE_UNKNOWN, -} camel_imap_resp_code_t; - -typedef struct _CamelIMAPRespCode { - camel_imap_resp_code_t code; - union { - guint32 flags; - char *parse; - guint32 uidnext; - guint32 uidvalidity; - guint32 unseen; - char *newname[2]; - struct { - guint32 uidvalidity; - guint32 uid; - } appenduid; - struct { - guint32 uidvalidity; - char *srcset; - char *destset; - } copyuid; - } v; -} CamelIMAPRespCode; - -enum { - CAMEL_IMAP_UNTAGGED_ERROR = -1, - CAMEL_IMAP_UNTAGGED_OK, - CAMEL_IMAP_UNTAGGED_NO, - CAMEL_IMAP_UNTAGGED_BAD, - CAMEL_IMAP_UNTAGGED_PREAUTH, - CAMEL_IMAP_UNTAGGED_HANDLED, -}; - -typedef struct _CamelIMAPNamespace { - struct _CamelIMAPNamespace *next; - char *path; - char sep; -} CamelIMAPNamespace; - -typedef struct _CamelIMAPNamespaceList { - CamelIMAPNamespace *personal; - CamelIMAPNamespace *other; - CamelIMAPNamespace *shared; -} CamelIMAPNamespaceList; - -enum { - CAMEL_IMAP_ENGINE_MAXLEN_LINE, - CAMEL_IMAP_ENGINE_MAXLEN_TOKEN -}; - -struct _CamelIMAPEngine { - CamelObject parent_object; - - CamelSession *session; - CamelURL *url; - - camel_imap_engine_t state; - camel_imap_level_t level; - guint32 capa; - - guint32 maxlen:31; - guint32 maxlentype:1; - - CamelIMAPNamespaceList namespaces; - GHashTable *authtypes; /* supported authtypes */ - - struct _CamelIMAPStream *istream; - CamelStream *ostream; - - unsigned char tagprefix; /* 'A'..'Z' */ - unsigned int tag; /* next command tag */ - int nextid; - - struct _CamelIMAPFolder *folder; /* currently selected folder */ - - EDList queue; /* queue of waiting commands */ - struct _CamelIMAPCommand *current; -}; - -struct _CamelIMAPEngineClass { - CamelObjectClass parent_class; - - unsigned char tagprefix; -}; - - -CamelType camel_imap_engine_get_type (void); - -CamelIMAPEngine *camel_imap_engine_new (CamelSession *session, CamelURL *url); - -/* returns 0 on success or -1 on error */ -int camel_imap_engine_take_stream (CamelIMAPEngine *engine, CamelStream *stream, CamelException *ex); - -int camel_imap_engine_capability (CamelIMAPEngine *engine, CamelException *ex); -int camel_imap_engine_namespace (CamelIMAPEngine *engine, CamelException *ex); - -int camel_imap_engine_select_folder (CamelIMAPEngine *engine, CamelFolder *folder, CamelException *ex); - -struct _CamelIMAPCommand *camel_imap_engine_queue (CamelIMAPEngine *engine, CamelFolder *folder, - const char *format, ...); -void camel_imap_engine_prequeue (CamelIMAPEngine *engine, struct _CamelIMAPCommand *ic); - -void camel_imap_engine_dequeue (CamelIMAPEngine *engine, struct _CamelIMAPCommand *ic); - -int camel_imap_engine_iterate (CamelIMAPEngine *engine); - - -/* untagged response utility functions */ -int camel_imap_engine_handle_untagged_1 (CamelIMAPEngine *engine, struct _camel_imap_token_t *token, CamelException *ex); -void camel_imap_engine_handle_untagged (CamelIMAPEngine *engine, CamelException *ex); - -/* stream wrapper utility functions */ -int camel_imap_engine_next_token (CamelIMAPEngine *engine, struct _camel_imap_token_t *token, CamelException *ex); -int camel_imap_engine_line (CamelIMAPEngine *engine, unsigned char **line, size_t *len, CamelException *ex); -int camel_imap_engine_literal (CamelIMAPEngine *engine, unsigned char **literal, size_t *len, CamelException *ex); -int camel_imap_engine_eat_line (CamelIMAPEngine *engine, CamelException *ex); - - -/* response code stuff */ -int camel_imap_engine_parse_resp_code (CamelIMAPEngine *engine, CamelException *ex); -void camel_imap_resp_code_free (CamelIMAPRespCode *rcode); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __CAMEL_IMAP_ENGINE_H__ */ diff --git a/camel/providers/imap4/camel-imap-folder.c b/camel/providers/imap4/camel-imap-folder.c deleted file mode 100644 index 04af9f3cb8..0000000000 --- a/camel/providers/imap4/camel-imap-folder.c +++ /dev/null @@ -1,768 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "camel-imap-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-summary.h" - -#define d(x) x - -static void camel_imap_folder_class_init (CamelIMAPFolderClass *klass); -static void camel_imap_folder_init (CamelIMAPFolder *folder, CamelIMAPFolderClass *klass); -static void camel_imap_folder_finalize (CamelObject *object); - -static void imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex); -static void imap_expunge (CamelFolder *folder, CamelException *ex); -static CamelMimeMessage *imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex); -static void imap_append_message (CamelFolder *folder, CamelMimeMessage *message, - const CamelMessageInfo *info, char **appended_uid, CamelException *ex); -static void imap_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest, - GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex); - - -static CamelFolderClass *parent_class = NULL; - - -CamelType -camel_imap_folder_get_type (void) -{ - static CamelType type = 0; - - if (!type) { - type = camel_type_register (CAMEL_TYPE_IMAP_FOLDER, - "CamelIMAPFolder", - sizeof (CamelIMAPFolder), - sizeof (CamelIMAPFolderClass), - (CamelObjectClassInitFunc) camel_imap_folder_class_init, - NULL, - (CamelObjectInitFunc) camel_imap_folder_init, - (CamelObjectFinalizeFunc) camel_imap_folder_finalize); - } - - return type; -} - -static void -camel_imap_folder_class_init (CamelIMAPFolderClass *klass) -{ - CamelFolderClass *folder_class = (CamelFolderClass *) klass; - - parent_class = (CamelFolderClass *) camel_type_get_global_classfuncs (CAMEL_FOLDER_TYPE); - - folder_class->sync = imap_sync; - folder_class->expunge = imap_expunge; - folder_class->get_message = imap_get_message; - folder_class->append_message = imap_append_message; - folder_class->transfer_messages_to = imap_transfer_messages_to; -} - -static void -camel_imap_folder_init (CamelIMAPFolder *folder, CamelIMAPFolderClass *klass) -{ - folder->utf7_name = NULL; -} - -static void -camel_imap_folder_finalize (CamelObject *object) -{ - CamelIMAPFolder *folder = (CamelIMAPFolder *) object; - - g_free (folder->utf7_name); -} - - -static struct { - const char *name; - guint32 flag; -} imap_flags[] = { - { "\\Answered", CAMEL_MESSAGE_ANSWERED }, - { "\\Deleted", CAMEL_MESSAGE_DELETED }, - { "\\Draft", CAMEL_MESSAGE_DRAFT }, - { "\\Flagged", CAMEL_MESSAGE_FLAGGED }, - /*{ "Forwarded", CAMEL_MESSAGE_FORWARDED },*/ - { "\\Seen", CAMEL_MESSAGE_SEEN }, -}; - -static int -imap_get_uid_set (CamelIMAPEngine *engine, CamelFolderSummary *summary, GPtrArray *infos, int cur, size_t linelen, char **set) -{ - CamelMessageInfo *info; - guint32 this, prev, next; - gboolean range = FALSE; - GString *uidset; - int scount, i; - size_t len; - - if (engine->maxlentype == CAMEL_IMAP_ENGINE_MAXLEN_LINE) - len = engine->maxlen - linelen; - else - len = engine->maxlen; - - i = cur + 1; - info = (CamelMessageInfo *) infos->pdata[cur]; - fflush (stdout); - uidset = g_string_new (""); - g_string_append (uidset, camel_message_info_uid (info)); - - if (!(i < infos->len)) - goto done; - - scount = summary->messages->len; - - /* init this info */ - for (this = 0; this < scount; this++) { - if (info == summary->messages->pdata[this]) - break; - } - - /* init next info */ - info = (CamelMessageInfo *) infos->pdata[i]; - for (next = this; next < scount; next++) { - if (info == summary->messages->pdata[next]) - break; - } - - for ( ; i < infos->len && uidset->len < len; i++) { - prev = this; - this = next; - - if (i + 1 < infos->len) { - info = infos->pdata[i + 1]; - for (next = this; next < scount; next++) { - if (info == summary->messages->pdata[next]) - break; - } - } else { - next = scount; - } - - if (this == (next - 1) || this == (prev + 1)) { - range = TRUE; - } else { - if (range) { - info = (CamelMessageInfo *) summary->messages->pdata[prev]; - g_string_append_printf (uidset, ":%s", camel_message_info_uid (info)); - range = FALSE; - } - - info = infos->pdata[i]; - g_string_append_printf (uidset, ",%s", camel_message_info_uid (info)); - } - } - - if (range) { - info = (CamelMessageInfo *) summary->messages->pdata[this]; - g_string_append_printf (uidset, ":%s", camel_message_info_uid (info)); - } - - done: - - *set = uidset->str; - g_string_free (uidset, FALSE); - - return (i - cur); -} - -static int -imap_sync_flag (CamelFolder *folder, GPtrArray *infos, char onoff, const char *flag, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) folder->parent_store)->engine; - CamelIMAPCommand *ic; - int i, id, retval = 0; - char *set = NULL; - - for (i = 0; i < infos->len; ) { - i += imap_get_uid_set (engine, folder->summary, infos, i, 30 + strlen (flag), &set); - - ic = camel_imap_engine_queue (engine, folder, "UID STORE %s %cFLAGS.SILENT (%s)\r\n", set, onoff, flag); - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - g_free (set); - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - - return -1; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot sync flags to folder `%s': Unknown"), - folder->full_name); - retval = -1; - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot sync flags to folder `%s': Bad command"), - folder->full_name); - retval = -1; - break; - } - - camel_imap_command_unref (ic); - - if (retval == -1) - return -1; - } - - return 0; -} - -static int -imap_sync_changes (CamelFolder *folder, GPtrArray *sync, CamelException *ex) -{ - CamelIMAPMessageInfo *iinfo; - GPtrArray *on_set, *off_set; - CamelMessageInfo *info; - flags_diff_t diff; - int retval = 0; - int i, j; - - on_set = g_ptr_array_new (); - off_set = g_ptr_array_new (); - - /* construct commands to sync system and user flags */ - for (i = 0; i < G_N_ELEMENTS (imap_flags); i++) { - if (!(imap_flags[i].flag & folder->permanent_flags)) - continue; - - for (j = 0; j < sync->len; j++) { - iinfo = (CamelIMAPMessageInfo *) info = sync->pdata[j]; - camel_imap_flags_diff (&diff, iinfo->server_flags, info->flags); - if (diff.changed & imap_flags[i].flag) { - if (diff.bits & imap_flags[i].flag) { - g_ptr_array_add (on_set, info); - } else { - g_ptr_array_add (off_set, info); - } - } - } - - if (on_set->len > 0) { - if ((retval = imap_sync_flag (folder, on_set, '+', imap_flags[i].name, ex)) == -1) - break; - - g_ptr_array_set_size (on_set, 0); - } - - if (off_set->len > 0) { - if ((retval = imap_sync_flag (folder, off_set, '-', imap_flags[i].name, ex)) == -1) - break; - - g_ptr_array_set_size (off_set, 0); - } - } - - g_ptr_array_free (on_set, TRUE); - g_ptr_array_free (off_set, TRUE); - - if (retval == -1) - return-1; - - for (i = 0; i < sync->len; i++) { - iinfo = (CamelIMAPMessageInfo *) info = sync->pdata[i]; - info->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; - iinfo->server_flags = info->flags & folder->permanent_flags; - } - - return 0; -} - -static void -imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) folder->parent_store)->engine; - CamelIMAPMessageInfo *iinfo; - CamelMessageInfo *info; - CamelIMAPCommand *ic; - flags_diff_t diff; - GPtrArray *sync; - int id, max, i; - int retval; - - /* gather a list of changes to sync to the server */ - sync = g_ptr_array_new (); - max = camel_folder_summary_count (folder->summary); - for (i = 0; i < max; i++) { - iinfo = (CamelIMAPMessageInfo *) info = camel_folder_summary_index (folder->summary, i); - expunge = expunge && (info->flags & CAMEL_MESSAGE_DELETED); - if (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) { - camel_imap_flags_diff (&diff, iinfo->server_flags, info->flags); - diff.changed &= folder->permanent_flags; - - /* weed out flag changes that we can't sync to the server */ - if (!diff.changed) - camel_folder_summary_info_free (folder->summary, info); - else - g_ptr_array_add (sync, info); - } else { - camel_folder_summary_info_free (folder->summary, info); - } - } - - if (sync->len > 0) { - retval = imap_sync_changes (folder, sync, ex); - - for (i = 0; i < sync->len; i++) - camel_folder_summary_info_free (folder->summary, sync->pdata[i]); - - g_ptr_array_free (sync, TRUE); - - if (retval == -1) - return; - } else { - g_ptr_array_free (sync, TRUE); - } - - if (expunge) { - ic = camel_imap_engine_queue (engine, folder, "EXPUNGE\r\n"); - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - switch (ic->result) { - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot expunge folder `%s': Unknown"), - folder->full_name); - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot expunge folder `%s': Bad command"), - folder->full_name); - break; - } - - camel_imap_command_unref (ic); - } - - camel_folder_summary_save (folder->summary); -} - -static void -imap_expunge (CamelFolder *folder, CamelException *ex) -{ - imap_sync (folder, TRUE, ex); -} - - -static int -untagged_fetch (CamelIMAPEngine *engine, CamelIMAPCommand *ic, guint32 index, camel_imap_token_t *token, CamelException *ex) -{ - CamelStream *fstream, *stream = ic->user_data; - CamelMimeFilter *crlf; - int left = 2; - - 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 != CAMEL_IMAP_TOKEN_ATOM) - goto unexpected; - - if (!strcmp (token->v.atom, "BODY[]")) { - if (camel_imap_engine_next_token (engine, token, ex) == -1) - goto exception; - - if (token->token != CAMEL_IMAP_TOKEN_LITERAL) - goto unexpected; - - fstream = (CamelStream *) camel_stream_filter_new_with_stream (stream); - crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_DECODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); - camel_stream_filter_add ((CamelStreamFilter *) fstream, crlf); - camel_object_unref (crlf); - - camel_stream_write_to_stream ((CamelStream *) engine->istream, fstream); - camel_stream_flush (fstream); - camel_object_unref (fstream); - - left--; - } 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; - - left--; - } else { - /* wtf? */ - fprintf (stderr, "huh? %s?...\n", token->v.atom); - } - } while (left); - - if (camel_imap_engine_next_token (engine, token, ex) == -1) - goto exception; - - if (token->token != ')') { - fprintf (stderr, "expected ')' to close untagged FETCH response\n"); - goto unexpected; - } - - return 0; - - unexpected: - - camel_imap_utils_set_unexpected_token_error (ex, engine, token); - - exception: - - return -1; -} - -static CamelMimeMessage * -imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) folder->parent_store)->engine; - CamelMimeMessage *message = NULL; - CamelIMAPCommand *ic; - CamelStream *stream; - int id; - - ic = camel_imap_engine_queue (engine, folder, "UID FETCH %s BODY.PEEK[]\r\n", uid); - camel_imap_command_register_untagged (ic, "FETCH", untagged_fetch); - ic->user_data = stream = camel_stream_mem_new (); - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - camel_object_unref (stream); - return NULL; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_OK: - camel_stream_reset (stream); - message = camel_mime_message_new (); - camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) message, stream); - break; - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot get message %s from folder `%s': No such message"), - uid, folder->full_name); - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot get message %s from folder `%s': Bad command"), - uid, folder->full_name); - break; - } - - camel_imap_command_unref (ic); - - camel_object_unref (stream); - - return message; -} - -static char *tm_months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static void -imap_append_message (CamelFolder *folder, CamelMimeMessage *message, - const CamelMessageInfo *info, char **appended_uid, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) folder->parent_store)->engine; - CamelIMAPSummary *summary = (CamelIMAPSummary *) folder->summary; - CamelIMAPRespCode *resp; - CamelIMAPCommand *ic; - CamelFolderInfo *fi; - CamelException lex; - char flags[100], *p; - char date[50]; - struct tm tm; - int id, i; - - /* construct the option flags list */ - if (info->flags & folder->permanent_flags) { - p = g_stpcpy (flags, " ("); - - for (i = 0; i < G_N_ELEMENTS (imap_flags); i++) { - if ((info->flags & imap_flags[i].flag) & folder->permanent_flags) { - p = g_stpcpy (p, imap_flags[i].name); - *p++ = ' '; - } - } - - p[-1] = ')'; - *p = '\0'; - } else { - flags[0] = '\0'; - } - - /* construct the optional date_time string */ - if (info->date_received != (time_t) -1) { - int tzone; - -#ifdef HAVE_LOCALTIME_R - localtime_r (&info->date_received, &tm); -#else - memcpy (&tm, localtime (&info->date_received), sizeof (tm)); -#endif - -#if defined (HAVE_TM_GMTOFF) - tzone = -tm.tm_gmtoff; -#elif defined (HAVE_TIMEZONE) - if (tm.tm_isdst > 0) { -#if defined (HAVE_ALTZONE) - tzone = altzone; -#else /* !defined (HAVE_ALTZONE) */ - tzone = (timezone - 3600); -#endif - } else { - tzone = timezone; - } -#else -#error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc. -#endif - - sprintf (date, " \"%02d-%s-%04d %02d:%02d:%02d %+05d\"", - tm.tm_mday, tm_months[tm.tm_mon], tm.tm_year + 1900, - tm.tm_hour, tm.tm_min, tm.tm_sec, tzone); - } else { - date[0] = '\0'; - } - - retry: - - if (engine->capa & CAMEL_IMAP_CAPABILITY_UIDPLUS) - ic = camel_imap_engine_queue (engine, NULL, "UID APPEND %F%s%s %L\r\n", flags, date, message); - else - ic = camel_imap_engine_queue (engine, NULL, "APPEND %F%s%s %L\r\n", flags, date, message); - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - return; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_OK: - if (!(engine->capa & CAMEL_IMAP_CAPABILITY_UIDPLUS)) - break; - - if (!appended_uid) - break; - - for (i = 0; i < ic->resp_codes->len; i++) { - resp = ic->resp_codes->pdata[i]; - if (resp->code == CAMEL_IMAP_RESP_CODE_APPENDUID) { - if (resp->v.appenduid.uidvalidity == summary->uidvalidity) - *appended_uid = g_strdup_printf ("%u", resp->v.appenduid.uid); - break; - } - } - break; - case CAMEL_IMAP_RESULT_NO: - /* FIXME: can we give the user any more information? */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot append message to folder `%s': Unknown error"), - folder->full_name); - - for (i = 0; i < ic->resp_codes->len; i++) { - resp = ic->resp_codes->pdata[i]; - if (resp->code == CAMEL_IMAP_RESP_CODE_TRYCREATE) { - char *parent_name, *p; - - parent_name = g_alloca (strlen (folder->full_name) + 1); - if (!(p = strrchr (parent_name, '/'))) - *parent_name = '\0'; - else - *p = '\0'; - - if (!(fi = camel_store_create_folder (folder->parent_store, parent_name, folder->name, &lex))) { - camel_exception_clear (&lex); - break; - } - - camel_store_free_folder_info (folder->parent_store, fi); - camel_imap_command_unref (ic); - camel_exception_clear (ex); - goto retry; - } - } - - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot append message to folder `%s': Bad command"), - folder->full_name); - - break; - default: - g_assert_not_reached (); - } - - camel_imap_command_unref (ic); -} - - -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; -} - -static void -imap_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest, - GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) src->parent_store)->engine; - int i, j, n, id, dest_namelen; - CamelMessageInfo *info; - CamelIMAPCommand *ic; - GPtrArray *infos; - char *set; - - infos = g_ptr_array_new (); - for (i = 0; i < uids->len; i++) { - if (!(info = camel_folder_summary_uid (src->summary, uids->pdata[i]))) - continue; - - g_ptr_array_add (infos, info); - } - - if (infos->len == 0) { - g_ptr_array_free (infos, TRUE); - return; - } - - g_ptr_array_sort (infos, (GCompareFunc) info_uid_sort); - - dest_namelen = strlen (camel_imap_folder_utf7_name ((CamelIMAPFolder *) dest)); - - for (i = 0; i < infos->len; i += n) { - n = imap_get_uid_set (engine, src->summary, infos, i, 10 + dest_namelen, &set); - - ic = camel_imap_engine_queue (engine, src, "UID COPY %s %F\r\n", set, dest); - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - g_free (set); - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - g_free (set); - goto done; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - if (delete_originals) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot move messages from folder `%s' to folder `%s': Unknown"), - src->full_name, dest->full_name); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot copy messages from folder `%s' to folder `%s': Unknown"), - src->full_name, dest->full_name); - } - - goto done; - case CAMEL_IMAP_RESULT_BAD: - if (delete_originals) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot move messages from folder `%s' to folder `%s': Bad command"), - src->full_name, dest->full_name); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot copy messages from folder `%s' to folder `%s': Bad command"), - src->full_name, dest->full_name); - } - - goto done; - } - - camel_imap_command_unref (ic); - - if (delete_originals) { - for (j = i; j < n; j++) { - info = infos->pdata[j]; - camel_folder_set_message_flags (src, camel_message_info_uid (info), - CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED); - } - - camel_folder_summary_touch (src->summary); - } - } - - done: - - for (i = 0; i < infos->len; i++) - camel_folder_summary_info_free (src->summary, infos->pdata[i]); - g_ptr_array_free (infos, TRUE); -} diff --git a/camel/providers/imap4/camel-imap-folder.h b/camel/providers/imap4/camel-imap-folder.h deleted file mode 100644 index a4f4e55528..0000000000 --- a/camel/providers/imap4/camel-imap-folder.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- 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_FOLDER_H__ -#define __CAMEL_IMAP_FOLDER_H__ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -#define CAMEL_TYPE_IMAP_FOLDER (camel_imap_folder_get_type ()) -#define CAMEL_IMAP_FOLDER(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP_FOLDER, CamelIMAPFolder)) -#define CAMEL_IMAP_FOLDER_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP_FOLDER, CamelIMAPFolderClass)) -#define CAMEL_IS_IMAP_FOLDER(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP_FOLDER)) -#define CAMEL_IS_IMAP_FOLDER_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP_FOLDER)) -#define CAMEL_IMAP_FOLDER_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_IMAP_FOLDER, CamelIMAPFolderClass)) - -typedef struct _CamelIMAPFolder CamelIMAPFolder; -typedef struct _CamelIMAPFolderClass CamelIMAPFolderClass; - -struct _CamelIMAPFolder { - CamelFolder parent_object; - - char *cachedir; - char *utf7_name; -}; - -struct _CamelIMAPFolderClass { - CamelFolderClass parent_class; - -}; - - -CamelType camel_imap_folder_get_type (void); - -CamelFolder *camel_imap_folder_new (CamelStore *store, const char *full_name, gboolean query, CamelException *ex); -CamelFolder *camel_imap_folder_new_utf7_name (CamelStore *store, const char *utf7_name, CamelException *ex); - -const char *camel_imap_folder_utf7_name (CamelIMAPFolder *folder); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __CAMEL_IMAP_FOLDER_H__ */ diff --git a/camel/providers/imap4/camel-imap-provider.c b/camel/providers/imap4/camel-imap-provider.c deleted file mode 100644 index 2cf7f6e1c7..0000000000 --- a/camel/providers/imap4/camel-imap-provider.c +++ /dev/null @@ -1,139 +0,0 @@ -/* -*- 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 -#endif - -#include - -#include -#include - -#include "camel-imap-store.h" - - -CamelProviderConfEntry imap4_conf_entries[] = { - { CAMEL_PROVIDER_CONF_SECTION_START, "mailcheck", NULL, - N_("Checking for new mail") }, - { CAMEL_PROVIDER_CONF_CHECKBOX, "check_all", NULL, - N_("Check for new messages in all folders"), "1" }, - { CAMEL_PROVIDER_CONF_SECTION_END }, - { CAMEL_PROVIDER_CONF_SECTION_START, "folders", NULL, - N_("Folders") }, - { CAMEL_PROVIDER_CONF_CHECKBOX, "use_lsub", NULL, - N_("Show only subscribed folders"), "1" }, - { CAMEL_PROVIDER_CONF_CHECKBOX, "override_namespace", NULL, - N_("Override server-supplied folder namespace"), "0" }, - { CAMEL_PROVIDER_CONF_ENTRY, "namespace", "override_namespace", - N_("Namespace") }, - { CAMEL_PROVIDER_CONF_SECTION_END }, - { CAMEL_PROVIDER_CONF_END } -}; - -static CamelProvider imap4_provider = { - "imap4", - N_("IMAPv4rev1"), - - N_("For reading and storing mail on IMAPv4rev1 servers."), - - "mail", - - CAMEL_PROVIDER_IS_REMOTE | CAMEL_PROVIDER_IS_SOURCE | - CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_SUPPORTS_SSL, - - CAMEL_URL_NEED_USER | CAMEL_URL_NEED_HOST | CAMEL_URL_ALLOW_AUTH, - - imap4_conf_entries, - - /* ... */ -}; - -CamelServiceAuthType camel_imap4_password_authtype = { - N_("Password"), - - N_("This option will connect to the IMAP server using a " - "plaintext password."), - - "", - TRUE -}; - - -static void -add_hash (guint *hash, char *s) -{ - if (s) - *hash ^= g_str_hash(s); -} - -static guint -imap_url_hash (gconstpointer key) -{ - const CamelURL *u = (CamelURL *)key; - guint hash = 0; - - add_hash (&hash, u->user); - add_hash (&hash, u->authmech); - add_hash (&hash, u->host); - hash ^= u->port; - - return hash; -} - -static gint -check_equal (char *s1, char *s2) -{ - if (s1 == NULL) { - if (s2 == NULL) - return TRUE; - else - return FALSE; - } - - if (s2 == NULL) - return FALSE; - - return strcmp (s1, s2) == 0; -} - -static gint -imap_url_equal (gconstpointer a, gconstpointer b) -{ - const CamelURL *u1 = a, *u2 = b; - - return check_equal (u1->user, u2->user) - && check_equal (u1->authmech, u2->authmech) - && check_equal (u1->host, u2->host) - && u1->port == u2->port; -} - - -void -camel_provider_module_init (void) -{ - imap4_provider.object_types[CAMEL_PROVIDER_STORE] = camel_imap_store_get_type (); - imap4_provider.url_hash = imap_url_hash; - imap4_provider.url_equal = imap_url_equal; - imap4_provider.authtypes = camel_sasl_authtype_list (FALSE); - imap4_provider.authtypes = g_list_prepend (imap4_provider.authtypes, &camel_imap4_password_authtype); - - camel_provider_register (&imap4_provider); -} diff --git a/camel/providers/imap4/camel-imap-specials.c b/camel/providers/imap4/camel-imap-specials.c deleted file mode 100644 index 5289394f19..0000000000 --- a/camel/providers/imap4/camel-imap-specials.c +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- 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 -#endif - -#include - -#include "camel-imap-specials.h" - -#define CHARS_ATOM_SPECIALS "(){]" -#define CHARS_LWSP " \t\r\n" -#define CHARS_QUOTED_SPECIALS "\\\"" -#define CHARS_LIST_WILDCARDS "*%" - -unsigned char camel_imap_specials[256] = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 6, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 20, 0, 8, 0, 0, 32, 0, 0, 1, 1, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -}; - - -static void -imap_init_bits (unsigned short bit, unsigned short bitcopy, int remove, unsigned char *vals) -{ - int i, len = strlen (vals); - - if (!remove) { - for (i = 0; i < len; i++) - camel_imap_specials[vals[i]] |= bit; - if (bitcopy) { - for (i = 0; i < 256; i++) { - if (camel_imap_specials[i] & bitcopy) - camel_imap_specials[i] |= bit; - } - } - } else { - for (i = 0; i < 256; i++) - camel_imap_specials[i] |= bit; - for (i = 0; i < len; i++) - camel_imap_specials[vals[i]] &= ~bit; - if (bitcopy) { - for (i = 0; i < 256; i++) { - if (camel_imap_specials[i] & bitcopy) - camel_imap_specials[i] &= ~bit; - } - } - } -} - - -void -camel_imap_specials_init (void) -{ - int i; - - for (i = 0; i < 256; i++) { - camel_imap_specials[i] = 0; - if (i <= 0x1f || i >= 0x7f) - camel_imap_specials[i] |= IS_CTRL; - } - - camel_imap_specials[' '] |= IS_SPACE; - - imap_init_bits (IS_LWSP, 0, 0, CHARS_LWSP); - imap_init_bits (IS_ASPECIAL, 0, 0, CHARS_ATOM_SPECIALS); - imap_init_bits (IS_QSPECIAL, 0, 0, CHARS_QUOTED_SPECIALS); - imap_init_bits (IS_WILDCARD, 0, 0, CHARS_LIST_WILDCARDS); -} diff --git a/camel/providers/imap4/camel-imap-specials.h b/camel/providers/imap4/camel-imap-specials.h deleted file mode 100644 index 6bae4d3ae3..0000000000 --- a/camel/providers/imap4/camel-imap-specials.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- 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_SPECIALS_H__ -#define __CAMEL_IMAP_SPECIALS_H__ - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -enum { - IS_ASPECIAL = (1 << 0), - IS_CTRL = (1 << 1), - IS_LWSP = (1 << 2), - IS_QSPECIAL = (1 << 3), - IS_SPACE = (1 << 4), - IS_WILDCARD = (1 << 5), -}; - -extern unsigned char camel_imap_specials[256]; - -#define is_atom(x) ((camel_imap_specials[(unsigned char)(x)] & (IS_ASPECIAL|IS_SPACE|IS_CTRL|IS_WILDCARD|IS_QSPECIAL)) == 0) -#define is_ctrl(x) ((camel_imap_specials[(unsigned char)(x)] & IS_CTRL) != 0) -#define is_lwsp(x) ((camel_imap_specials[(unsigned char)(x)] & IS_LWSP) != 0) -#define is_type(x, t) ((camel_imap_specials[(unsigned char)(x)] & (t)) != 0) -#define is_qsafe(x) ((camel_imap_specials[(unsigned char)(x)] & (IS_QSPECIAL|IS_CTRL)) == 0) -#define is_wild(x) ((camel_imap_specials[(unsigned char)(x)] & IS_WILDCARD) != 0) - -void camel_imap_specials_init (void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __CAMEL_IMAP_SPECIALS_H__ */ diff --git a/camel/providers/imap4/camel-imap-store.c b/camel/providers/imap4/camel-imap-store.c deleted file mode 100644 index 7373088f79..0000000000 --- a/camel/providers/imap4/camel-imap-store.c +++ /dev/null @@ -1,852 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#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" - - -static void camel_imap_store_class_init (CamelIMAPStoreClass *klass); -static void camel_imap_store_init (CamelIMAPStore *store, CamelIMAPStoreClass *klass); -static void camel_imap_store_finalize (CamelObject *object); - -/* service methods */ -static void imap_construct (CamelService *service, CamelSession *session, - CamelProvider *provider, CamelURL *url, - CamelException *ex); -static char *imap_get_name (CamelService *service, gboolean brief); -static gboolean imap_connect (CamelService *service, CamelException *ex); -static gboolean imap_disconnect (CamelService *service, gboolean clean, CamelException *ex); -static GList *imap_query_auth_types (CamelService *service, CamelException *ex); - -/* store methods */ -static CamelFolder *imap_get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex); -static CamelFolderInfo *imap_create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex); -static void imap_delete_folder (CamelStore *store, const char *folder_name, CamelException *ex); -static void imap_rename_folder (CamelStore *store, const char *old_name, const char *new_name, CamelException *ex); -static void imap_sync (CamelStore *store, gboolean expunge, CamelException *ex); -static CamelFolderInfo *imap_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex); -static void imap_subscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex); -static void imap_unsubscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex); -static void imap_noop (CamelStore *store, CamelException *ex); - - -static CamelStoreClass *parent_class = NULL; - - -CamelType -camel_imap_store_get_type (void) -{ - static CamelType type = 0; - - if (!type) { - type = camel_type_register (CAMEL_TYPE_IMAP_STORE, - "CamelIMAPStore", - sizeof (CamelIMAPStore), - sizeof (CamelIMAPStoreClass), - (CamelObjectClassInitFunc) camel_imap_store_class_init, - NULL, - (CamelObjectInitFunc) camel_imap_store_init, - (CamelObjectFinalizeFunc) camel_imap_store_finalize); - } - - return type; -} - -static guint -imap_hash_folder_name (gconstpointer key) -{ - if (g_ascii_strcasecmp (key, "INBOX") == 0) - return g_str_hash ("INBOX"); - else - return g_str_hash (key); -} - -static gint -imap_compare_folder_name (gconstpointer a, gconstpointer b) -{ - gconstpointer aname = a, bname = b; - - if (g_ascii_strcasecmp (a, "INBOX") == 0) - aname = "INBOX"; - if (g_ascii_strcasecmp (b, "INBOX") == 0) - bname = "INBOX"; - return g_str_equal (aname, bname); -} - -static void -camel_imap_store_class_init (CamelIMAPStoreClass *klass) -{ - CamelServiceClass *service_class = (CamelServiceClass *) klass; - CamelStoreClass *store_class = (CamelStoreClass *) klass; - - parent_class = (CamelStoreClass *) camel_type_get_global_classfuncs (CAMEL_STORE_TYPE); - - service_class->construct = imap_construct; - service_class->get_name = imap_get_name; - service_class->connect = imap_connect; - service_class->disconnect = imap_disconnect; - service_class->query_auth_types = imap_query_auth_types; - - store_class->hash_folder_name = imap_hash_folder_name; - store_class->compare_folder_name = imap_compare_folder_name; - - store_class->get_folder = imap_get_folder; - store_class->create_folder = imap_create_folder; - store_class->delete_folder = imap_delete_folder; - store_class->rename_folder = imap_rename_folder; - store_class->sync = imap_sync; - store_class->get_folder_info = imap_get_folder_info; - store_class->subscribe_folder = imap_subscribe_folder; - store_class->unsubscribe_folder = imap_unsubscribe_folder; - store_class->noop = imap_noop; -} - -static void -camel_imap_store_init (CamelIMAPStore *store, CamelIMAPStoreClass *klass) -{ - store->engine = NULL; -} - -static void -camel_imap_store_finalize (CamelObject *object) -{ - CamelIMAPStore *store = (CamelIMAPStore *) object; - - if (store->engine) - camel_object_unref (store->engine); -} - - -static void -imap_construct (CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex) -{ - CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex); -} - -static char * -imap_get_name (CamelService *service, gboolean brief) -{ - if (brief) - return g_strdup_printf (_("IMAP server %s"), service->url->host); - else - return g_strdup_printf (_("IMAP service for %s on %s"), - service->url->user, service->url->host); -} - -enum { - USE_SSL_NEVER, - USE_SSL_ALWAYS, - USE_SSL_WHEN_POSSIBLE -}; - -#define SSL_PORT_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3) -#define STARTTLS_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_TLS) - -static gboolean -connect_to_server (CamelService *service, struct hostent *host, int ssl_mode, int try_starttls, CamelException *ex) -{ - CamelIMAPStore *store = (CamelIMAPStore *) service; - CamelIMAPEngine *engine; - CamelStream *tcp_stream; - int port, ret; - - if (store->engine) { - camel_object_unref (store->engine); - store->engine = NULL; - } - - port = service->url->port ? service->url->port : 143; - - if (ssl_mode) { -#ifdef HAVE_SSL - if (try_starttls) { - tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, STARTTLS_FLAGS); - } else { - port = service->url->port ? service->url->port : 993; - tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, SSL_PORT_FLAGS); - } -#else - if (!try_starttls) - port = service->url->port ? service->url->port : 993; - - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - _("Could not connect to %s (port %d): %s"), - service->url->host, port, - _("SSL unavailable")); - - return FALSE; -#endif /* HAVE_SSL */ - } else { - tcp_stream = camel_tcp_stream_raw_new (); - } - - fprintf (stderr, "connecting to %s:%d\n", service->url->host, port); - if ((ret = camel_tcp_stream_connect ((CamelTcpStream *) tcp_stream, host, port)) == -1) { - if (errno == EINTR) - camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, - _("Connection cancelled")); - else - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - _("Could not connect to %s (port %d): %s"), - service->url->host, port, - g_strerror (errno)); - - camel_object_unref (tcp_stream); - - return FALSE; - } - - engine = camel_imap_engine_new (service->session, service->url); - if (camel_imap_engine_take_stream (engine, tcp_stream, ex) == -1) { - camel_object_unref (engine); - - return FALSE; - } - - if (camel_imap_engine_capability (engine, ex) == -1) { - camel_object_unref (engine); - - return FALSE; - } - - store->engine = engine; - -#ifdef HAVE_SSL - if (ssl_mode == USE_SSL_WHEN_POSSIBLE) { - /* try_starttls is always TRUE here */ - if (engine->capa & CAMEL_IMAP_CAPABILITY_STARTTLS) - goto starttls; - } else if (ssl_mode == USE_SSL_ALWAYS) { - if (try_starttls) { - if (engine->capa & CAMEL_IMAP_CAPABILITY_STARTTLS) { - goto starttls; - } else { - /* server doesn't support STARTTLS, abort */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - _("Failed to connect to IMAP server %s in secure mode: " - "Server does not support STARTTLS"), - service->url->host); - goto exception; - } - } - } -#endif /* HAVE_SSL */ - - return TRUE; - -#ifdef HAVE_SSL - starttls: - - if (1) { - CamelIMAPCommand *ic; - int id; - - ic = camel_imap_engine_queue (engine, NULL, "STARTTLS\r\n"); - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->result != CAMEL_IMAP_RESULT_OK) { - if (ic->result != CAMEL_IMAP_RESULT_OK) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to connect to IMAP server %s in secure mode: %s"), - service->url->host, _("Unknown error")); - } else { - camel_exception_xfer (ex, &ic->ex); - } - - camel_imap_command_unref (ic); - - goto exception; - } - - camel_imap_command_unref (ic); - } - - return TRUE; - - exception: - - camel_object_unref (store->engine); - store->engine = NULL; - - return FALSE; -#endif /* HAVE_SSL */ -} - -static struct { - char *value; - int mode; -} ssl_options[] = { - { "", USE_SSL_ALWAYS }, - { "always", USE_SSL_ALWAYS }, - { "when-possible", USE_SSL_WHEN_POSSIBLE }, - { "never", USE_SSL_NEVER }, - { NULL, USE_SSL_NEVER }, -}; - -static gboolean -connect_to_server_wrapper (CamelService *service, CamelException *ex) -{ - const char *use_ssl; - struct hostent *h; - int ssl_mode; - int ret, i; - - if (!(h = camel_service_gethost (service, ex))) - return FALSE; - - if ((use_ssl = camel_url_get_param (service->url, "use_ssl"))) { - for (i = 0; ssl_options[i].value; i++) - if (!strcmp (ssl_options[i].value, use_ssl)) - break; - ssl_mode = ssl_options[i].mode; - } else { - ssl_mode = USE_SSL_NEVER; - } - - if (ssl_mode == USE_SSL_ALWAYS) { - /* First try the ssl port */ - if (!(ret = connect_to_server (service, h, ssl_mode, FALSE, ex))) { - if (camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_UNAVAILABLE) { - /* The ssl port seems to be unavailable, lets try STARTTLS */ - camel_exception_clear (ex); - ret = connect_to_server (service, h, ssl_mode, TRUE, ex); - } - } - } else if (ssl_mode == USE_SSL_WHEN_POSSIBLE) { - /* If the server supports STARTTLS, use it */ - ret = connect_to_server (service, h, ssl_mode, TRUE, ex); - } else { - /* User doesn't care about SSL */ - ret = connect_to_server (service, h, USE_SSL_ALWAYS, FALSE, ex); - } - - camel_free_host (h); - - return ret; -} - -static int -sasl_auth (CamelIMAPEngine *engine, CamelIMAPCommand *ic, const unsigned char *linebuf, size_t linelen, CamelException *ex) -{ - /* Perform a single challenge iteration */ - CamelSasl *sasl = ic->user_data; - char *challenge; - - if (camel_sasl_authenticated (sasl)) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Cannot authenticate to IMAP server %s using the %s authentication mechanism"), - engine->url->host, engine->url->authmech); - return -1; - } - - while (isspace (*linebuf)) - linebuf++; - - if (*linebuf == '\0') - linebuf = NULL; - - if (!(challenge = camel_sasl_challenge_base64 (sasl, (const char *) linebuf, ex))) - return -1; - - fprintf (stderr, "sending : %s\r\n", challenge); - - if (camel_stream_printf (engine->ostream, "%s\r\n", challenge) == -1) { - g_free (challenge); - return -1; - } - - g_free (challenge); - - if (camel_stream_flush (engine->ostream) == -1) - return -1; - - return 0; -} - -static int -imap_try_authenticate (CamelService *service, gboolean reprompt, const char *errmsg, CamelException *ex) -{ - CamelIMAPStore *store = (CamelIMAPStore *) service; - CamelSession *session = service->session; - CamelSasl *sasl = NULL; - CamelIMAPCommand *ic; - int id; - - if (!service->url->passwd) { - guint32 flags = CAMEL_SESSION_PASSWORD_SECRET; - char *prompt; - - if (reprompt) - flags |= CAMEL_SESSION_PASSWORD_REPROMPT; - - prompt = g_strdup_printf (_("%sPlease enter the IMAP password for %s on host %s"), - errmsg ? errmsg : "", - service->url->user, - service->url->host); - - service->url->passwd = camel_session_get_password (session, prompt, flags, service, "password", ex); - - g_free (prompt); - - if (!service->url->passwd) - return FALSE; - } - - if (service->url->authmech) { - CamelServiceAuthType *mech; - - mech = g_hash_table_lookup (store->engine->authtypes, service->url->authmech); - sasl = camel_sasl_new ("imap", mech->authproto, service); - - ic = camel_imap_engine_queue (store->engine, NULL, "AUTHENTICATE %s\r\n", service->url->authmech); - ic->plus = sasl_auth; - ic->user_data = sasl; - } else { - ic = camel_imap_engine_queue (store->engine, NULL, "LOGIN %S %S\r\n", - service->url->user, service->url->passwd); - } - - while ((id = camel_imap_engine_iterate (store->engine)) < ic->id && id != -1) - ; - - if (sasl != NULL) - camel_object_unref (sasl); - - if (id == -1 || ic->status == CAMEL_IMAP_COMMAND_ERROR) { - /* unrecoverable error */ - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - - return FALSE; - } - - if (ic->result != CAMEL_IMAP_RESULT_OK) { - camel_imap_command_unref (ic); - - /* try again */ - - return TRUE; - } - - camel_imap_command_unref (ic); - - return FALSE; -} - -static gboolean -imap_connect (CamelService *service, CamelException *ex) -{ - CamelIMAPStore *store = (CamelIMAPStore *) service; - CamelServiceAuthType *mech; - gboolean reprompt = FALSE; - char *errmsg = NULL; - CamelException lex; - - CAMEL_SERVICE_LOCK (store, connect_lock); - - if (!connect_to_server_wrapper (service, ex)) { - CAMEL_SERVICE_UNLOCK (store, connect_lock); - return FALSE; - } - -#define CANT_USE_AUTHMECH (!(mech = g_hash_table_lookup (store->engine->authtypes, service->url->authmech))) - if (service->url->authmech && CANT_USE_AUTHMECH) { - /* Oops. We can't AUTH using the requested mechanism */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Cannot authenticate to IMAP server %s using %s"), - service->url->host, service->url->authmech); - - camel_object_unref (store->engine); - store->engine = NULL; - - CAMEL_SERVICE_UNLOCK (store, connect_lock); - - return FALSE; - } - - camel_exception_init (&lex); - while (imap_try_authenticate (service, reprompt, errmsg, &lex)) { - g_free (errmsg); - errmsg = g_strdup (lex.desc); - camel_exception_clear (&lex); - reprompt = TRUE; - } - g_free (errmsg); - - if (camel_exception_is_set (&lex)) { - camel_exception_xfer (ex, &lex); - camel_object_unref (store->engine); - store->engine = NULL; - - CAMEL_SERVICE_UNLOCK (store, connect_lock); - - return FALSE; - } - - if (camel_imap_engine_namespace (store->engine, ex) == -1) { - camel_object_unref (store->engine); - store->engine = NULL; - - CAMEL_SERVICE_UNLOCK (store, connect_lock); - - return FALSE; - } - - CAMEL_SERVICE_UNLOCK (store, connect_lock); - - return TRUE; -} - -static gboolean -imap_disconnect (CamelService *service, gboolean clean, CamelException *ex) -{ - CamelIMAPStore *store = (CamelIMAPStore *) service; - CamelIMAPCommand *ic; - int id; - - if (clean && !store->engine->istream->disconnected) { - ic = camel_imap_engine_queue (store->engine, NULL, "LOGOUT\r\n"); - while ((id = camel_imap_engine_iterate (store->engine)) < ic->id && id != -1) - ; - - camel_imap_command_unref (ic); - } - - camel_object_unref (store->engine); - - return 0; -} - -extern CamelServiceAuthType camel_imap_password_authtype; - -static GList * -imap_query_auth_types (CamelService *service, CamelException *ex) -{ - CamelIMAPStore *store = (CamelIMAPStore *) service; - CamelServiceAuthType *authtype; - GList *sasl_types, *t, *next; - gboolean connected; - - CAMEL_SERVICE_LOCK (store, connect_lock); - connected = connect_to_server_wrapper (service, ex); - CAMEL_SERVICE_UNLOCK (store, connect_lock); - if (!connected) - return NULL; - - sasl_types = camel_sasl_authtype_list (FALSE); - for (t = sasl_types; t; t = next) { - authtype = t->data; - next = t->next; - - if (!g_hash_table_lookup (store->engine->authtypes, authtype->authproto)) { - sasl_types = g_list_remove_link (sasl_types, t); - g_list_free_1 (t); - } - } - - return g_list_prepend (sasl_types, &camel_imap_password_authtype); -} - - -static char * -imap_folder_utf7_name (CamelStore *store, const char *folder_name) -{ - char *real_name, *p; - - if (store->dir_sep != '/') { - p = real_name = g_alloca (strlen (folder_name) + 1); - strcpy (real_name, folder_name); - while (*p != '\0') { - if (*p == '/') - *p = store->dir_sep; - p++; - } - - folder_name = real_name; - } - - return camel_utf8_utf7 (folder_name); -} - -static CamelFolder * -imap_get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) -{ - CamelIMAPStore *imap_store = (CamelIMAPStore *) store; - - /* FIXME: implement me */ - - return NULL; -} - -static CamelFolderInfo * -imap_create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex) -{ - /* FIXME: also need to deal with parent folders that can't - * contain subfolders - delete them and re-create with the - * proper hint */ - CamelIMAPEngine *engine = ((CamelIMAPStore *) store)->engine; - CamelFolderInfo *fi = NULL; - CamelIMAPCommand *ic; - char *utf7_name; - const char *c; - char *name; - int id; - - c = folder_name; - while (*c != '\0') { - if (*c == store->dir_sep || strchr ("#%*", *c)) { - camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_PATH, - _("The folder name \"%s\" is invalid because " - "it containes the character \"%c\""), - folder_name, *c); - return NULL; - } - - c++; - } - - if (parent_name != NULL && *parent_name) - name = g_strdup_printf ("%s/%s", parent_name, folder_name); - else - name = g_strdup (folder_name); - - utf7_name = imap_folder_utf7_name (store, name); - g_free (name); - - ic = camel_imap_engine_queue (engine, NULL, "CREATE %S\r\n", utf7_name); - g_free (utf7_name); - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - return NULL; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_OK: - /* FIXME: allocate fi */ - break; - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot create folder `%s': Invalid mailbox name"), - folder_name); - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot create folder `%s': Bad command"), - folder_name); - break; - default: - g_assert_not_reached (); - } - - camel_imap_command_unref (ic); - - return fi; -} - -static void -imap_delete_folder (CamelStore *store, const char *folder_name, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) store)->engine; - CamelIMAPCommand *ic; - char *utf7_name; - int id; - - if (!g_ascii_strcasecmp (folder_name, "INBOX")) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot delete folder `%s': Special folder"), - folder_name); - - return; - } - - utf7_name = imap_folder_utf7_name (store, folder_name); - ic = camel_imap_engine_queue (engine, NULL, "DELETE %S\r\n", utf7_name); - g_free (utf7_name); - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - return; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_OK: - /* deleted */ - /*fi = imap_build_folder_info (store, folder_name); - camel_object_trigger_event (store, "folder_deleted", fi); - camel_folder_info_free (fi);*/ - break; - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot delete folder `%s': Invalid mailbox name"), - folder_name); - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot delete folder `%s': Bad command"), - folder_name); - break; - } - - camel_imap_command_unref (ic); -} - -static void -imap_rename_folder (CamelStore *store, const char *old_name, const char *new_name, CamelException *ex) -{ - -} - -static void -imap_sync (CamelStore *store, gboolean expunge, CamelException *ex) -{ - -} - -static CamelFolderInfo * -imap_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex) -{ - return NULL; -} - -static void -imap_subscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) store)->engine; - CamelIMAPCommand *ic; - char *utf7_name; - int id; - - utf7_name = imap_folder_utf7_name (store, folder_name); - ic = camel_imap_engine_queue (engine, NULL, "SUBSCRIBE %S\r\n", utf7_name); - g_free (utf7_name); - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - return; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_OK: - /* subscribed */ - /*fi = imap_build_folder_info (store, folder_name); - fi->flags |= CAMEL_FOLDER_NOCHILDREN; - camel_object_trigger_event (store, "folder_subscribed", fi); - camel_folder_info_free (fi);*/ - break; - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot subscribe to folder `%s': Invalid mailbox name"), - folder_name); - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot subscribe to folder `%s': Bad command"), - folder_name); - break; - } - - camel_imap_command_unref (ic); -} - -static void -imap_unsubscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex) -{ - CamelIMAPEngine *engine = ((CamelIMAPStore *) store)->engine; - CamelIMAPCommand *ic; - char *utf7_name; - int id; - - utf7_name = imap_folder_utf7_name (store, folder_name); - ic = camel_imap_engine_queue (engine, NULL, "UNSUBSCRIBE %S\r\n", utf7_name); - g_free (utf7_name); - - while ((id = camel_imap_engine_iterate (engine)) < ic->id && id != -1) - ; - - if (id == -1 || ic->status != CAMEL_IMAP_COMMAND_COMPLETE) { - camel_exception_xfer (ex, &ic->ex); - camel_imap_command_unref (ic); - return; - } - - switch (ic->result) { - case CAMEL_IMAP_RESULT_OK: - /* unsubscribed */ - /*fi = imap_build_folder_info (store, folder_name); - camel_object_trigger_event (store, "folder_unsubscribed", fi); - camel_folder_info_free (fi);*/ - break; - case CAMEL_IMAP_RESULT_NO: - /* FIXME: would be good to save the NO reason into the err message */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot unsubscribe from folder `%s': Invalid mailbox name"), - folder_name); - break; - case CAMEL_IMAP_RESULT_BAD: - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot unsubscribe from folder `%s': Bad command"), - folder_name); - break; - } - - camel_imap_command_unref (ic); -} - -static void -imap_noop (CamelStore *store, CamelException *ex) -{ - -} diff --git a/camel/providers/imap4/camel-imap-store.h b/camel/providers/imap4/camel-imap-store.h deleted file mode 100644 index b4643451bf..0000000000 --- a/camel/providers/imap4/camel-imap-store.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- 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_STORE_H__ -#define __CAMEL_IMAP_STORE_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -#define CAMEL_TYPE_IMAP_STORE (camel_imap_store_get_type ()) -#define CAMEL_IMAP_STORE(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP_STORE, CamelIMAPStore)) -#define CAMEL_IMAP_STORE_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP_STORE, CamelIMAPStoreClass)) -#define CAMEL_IS_IMAP_STORE(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP_STORE)) -#define CAMEL_IS_IMAP_STORE_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP_STORE)) -#define CAMEL_IMAP_STORE_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_IMAP_STORE, CamelIMAPStoreClass)) - -typedef struct _CamelIMAPStore CamelIMAPStore; -typedef struct _CamelIMAPStoreClass CamelIMAPStoreClass; - -struct _CamelIMAPEngine; - -struct _CamelIMAPStore { - CamelStore parent_object; - - struct _CamelIMAPEngine *engine; -}; - -struct _CamelIMAPStoreClass { - CamelStoreClass parent_class; - -}; - - -CamelType camel_imap_store_get_type (void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __CAMEL_IMAP_STORE_H__ */ diff --git a/camel/providers/imap4/camel-imap-stream.c b/camel/providers/imap4/camel-imap-stream.c deleted file mode 100644 index be3baa7794..0000000000 --- a/camel/providers/imap4/camel-imap-stream.c +++ /dev/null @@ -1,708 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include -#include -#include - -#include "camel-imap-specials.h" - -#include "camel-imap-stream.h" - -#define d(x) x - -#define IMAP_TOKEN_LEN 128 - -static void camel_imap_stream_class_init (CamelIMAPStreamClass *klass); -static void camel_imap_stream_init (CamelIMAPStream *stream, CamelIMAPStreamClass *klass); -static void camel_imap_stream_finalize (CamelObject *object); - -static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n); -static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n); -static int stream_flush (CamelStream *stream); -static int stream_close (CamelStream *stream); -static gboolean stream_eos (CamelStream *stream); - - -static CamelStreamClass *parent_class = NULL; - - -CamelType -camel_imap_stream_get_type (void) -{ - static CamelType type = 0; - - if (!type) { - type = camel_type_register (CAMEL_TYPE_IMAP_STREAM, - "CamelIMAPStream", - sizeof (CamelIMAPStream), - sizeof (CamelIMAPStreamClass), - (CamelObjectClassInitFunc) camel_imap_stream_class_init, - NULL, - (CamelObjectInitFunc) camel_imap_stream_init, - (CamelObjectFinalizeFunc) camel_imap_stream_finalize); - } - - return type; -} - -static void -camel_imap_stream_class_init (CamelIMAPStreamClass *klass) -{ - CamelStreamClass *stream_class = (CamelStreamClass *) klass; - - parent_class = (CamelStreamClass *) camel_type_get_global_classfuncs (CAMEL_STREAM_TYPE); - - /* virtual method overload */ - stream_class->read = stream_read; - stream_class->write = stream_write; - stream_class->flush = stream_flush; - stream_class->close = stream_close; - stream_class->eos = stream_eos; -} - -static void -camel_imap_stream_init (CamelIMAPStream *imap, CamelIMAPStreamClass *klass) -{ - imap->stream = NULL; - - imap->mode = CAMEL_IMAP_STREAM_MODE_TOKEN; - imap->disconnected = FALSE; - imap->eol = FALSE; - - imap->literal = 0; - - imap->inbuf = imap->realbuf + IMAP_READ_PRELEN; - imap->inptr = imap->inbuf; - imap->inend = imap->inbuf; - - imap->tokenbuf = g_malloc (IMAP_TOKEN_LEN); - imap->tokenptr = imap->tokenbuf; - imap->tokenleft = IMAP_TOKEN_LEN; - - imap->unget = NULL; -} - -static void -camel_imap_stream_finalize (CamelObject *object) -{ - CamelIMAPStream *imap = (CamelIMAPStream *) object; - - if (imap->stream) - camel_object_unref (imap->stream); - - g_free (imap->tokenbuf); - g_free (imap->unget); -} - - -static ssize_t -imap_fill (CamelIMAPStream *imap) -{ - unsigned char *inbuf, *inptr, *inend; - ssize_t nread; - size_t inlen; - - if (imap->disconnected) { - errno = EINVAL; - return -1; - } - - inbuf = imap->inbuf; - inptr = imap->inptr; - inend = imap->inend; - inlen = inend - inptr; - - g_assert (inptr <= inend); - - /* attempt to align 'inend' with realbuf + SCAN_HEAD */ - if (inptr >= inbuf) { - inbuf -= inlen < IMAP_READ_PRELEN ? inlen : IMAP_READ_PRELEN; - memmove (inbuf, inptr, inlen); - inptr = inbuf; - inbuf += inlen; - } else if (inptr > imap->realbuf) { - size_t shift; - - shift = MIN (inptr - imap->realbuf, inend - inbuf); - memmove (inptr - shift, inptr, inlen); - inptr -= shift; - inbuf = inptr + inlen; - } else { - /* we can't shift... */ - inbuf = inend; - } - - imap->inptr = inptr; - imap->inend = inbuf; - inend = imap->realbuf + IMAP_READ_PRELEN + IMAP_READ_BUFLEN - 1; - - if ((nread = camel_stream_read (imap->stream, inbuf, inend - inbuf)) == -1) - return -1; - else if (nread == 0) - imap->disconnected = TRUE; - - imap->inend += nread; - - return imap->inend - imap->inptr; -} - -static ssize_t -stream_read (CamelStream *stream, char *buffer, size_t n) -{ - CamelIMAPStream *imap = (CamelIMAPStream *) stream; - ssize_t len, nread = 0; - - if (imap->mode == CAMEL_IMAP_STREAM_MODE_LITERAL) { - /* don't let our caller read past the end of the literal */ - n = MIN (n, imap->literal); - } - - if (imap->inptr < imap->inend) { - len = MIN (n, imap->inend - imap->inptr); - memcpy (buffer, imap->inptr, len); - imap->inptr += len; - nread = len; - } - - if (nread < n) { - if ((len = camel_stream_read (imap->stream, buffer + nread, n - nread)) == 0) - imap->disconnected = TRUE; - else if (len == -1) - return -1; - - nread += len; - } - - if (imap->mode == CAMEL_IMAP_STREAM_MODE_LITERAL) { - imap->literal -= nread; - - if (imap->literal == 0) { - imap->mode = CAMEL_IMAP_STREAM_MODE_TOKEN; - imap->eol = TRUE; - } - } - - return nread; -} - -static ssize_t -stream_write (CamelStream *stream, const char *buffer, size_t n) -{ - CamelIMAPStream *imap = (CamelIMAPStream *) stream; - ssize_t nwritten; - - if (imap->disconnected) { - errno = EINVAL; - return -1; - } - - if ((nwritten = camel_stream_write (imap->stream, buffer, n)) == 0) - imap->disconnected = TRUE; - - return nwritten; -} - -static int -stream_flush (CamelStream *stream) -{ - CamelIMAPStream *imap = (CamelIMAPStream *) stream; - - return camel_stream_flush (imap->stream); -} - -static int -stream_close (CamelStream *stream) -{ - CamelIMAPStream *imap = (CamelIMAPStream *) stream; - - if (camel_stream_close (imap->stream) == -1) - return -1; - - camel_object_unref (imap->stream); - imap->stream = NULL; - - imap->disconnected = TRUE; - - return 0; -} - -static gboolean -stream_eos (CamelStream *stream) -{ - CamelIMAPStream *imap = (CamelIMAPStream *) stream; - - if (imap->eol) - return TRUE; - - if (imap->disconnected && imap->inptr == imap->inend) - return TRUE; - - if (camel_stream_eos (imap->stream)) - return TRUE; - - return FALSE; -} - - -/** - * camel_imap_stream_new: - * @stream: tcp stream - * - * Returns a new imap stream - **/ -CamelStream * -camel_imap_stream_new (CamelStream *stream) -{ - CamelIMAPStream *imap; - - g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL); - - imap = (CamelIMAPStream *) camel_object_new (CAMEL_TYPE_IMAP_STREAM); - camel_object_ref (stream); - imap->stream = stream; - - return (CamelStream *) imap; -} - - - -#define token_save(imap, start, len) G_STMT_START { \ - if (imap->tokenleft <= len) { \ - unsigned int tlen, toff; \ - \ - tlen = toff = imap->tokenptr - imap->tokenbuf; \ - tlen = tlen ? tlen : 1; \ - \ - while (tlen < toff + len) \ - tlen <<= 1; \ - \ - imap->tokenbuf = g_realloc (imap->tokenbuf, tlen + 1); \ - imap->tokenptr = imap->tokenbuf + toff; \ - imap->tokenleft = tlen - toff; \ - } \ - \ - memcpy (imap->tokenptr, start, len); \ - imap->tokenptr += len; \ - imap->tokenleft -= len; \ -} G_STMT_END - -#define token_clear(imap) G_STMT_START { \ - imap->tokenleft += imap->tokenptr - imap->tokenbuf; \ - imap->tokenptr = imap->tokenbuf; \ - imap->literal = 0; \ -} G_STMT_END - - -/** - * camel_imap_stream_next_token: - * @stream: imap stream - * @token: imap token - * - * Reads the next token from the imap stream and saves it in @token. - * - * Returns 0 on success or -1 on fail. - **/ -int -camel_imap_stream_next_token (CamelIMAPStream *stream, camel_imap_token_t *token) -{ - register unsigned char *inptr; - unsigned char *inend, *start, *p; - gboolean escaped = FALSE; - size_t literal = 0; - guint32 nz_number; - int ret; - - g_return_val_if_fail (CAMEL_IS_IMAP_STREAM (stream), -1); - g_return_val_if_fail (stream->mode != CAMEL_IMAP_STREAM_MODE_LITERAL, -1); - g_return_val_if_fail (token != NULL, -1); - - if (stream->unget) { - memcpy (token, stream->unget, sizeof (camel_imap_token_t)); - g_free (stream->unget); - stream->unget = NULL; - return 0; - } - - token_clear (stream); - - inptr = stream->inptr; - inend = stream->inend; - *inend = '\0'; - - do { - if (inptr == inend) { - if ((ret = imap_fill (stream)) < 0) { - token->token = CAMEL_IMAP_TOKEN_ERROR; - return -1; - } else if (ret == 0) { - token->token = CAMEL_IMAP_TOKEN_NO_DATA; - return 0; - } - - inptr = stream->inptr; - inend = stream->inend; - *inend = '\0'; - } - - while (*inptr == ' ' || *inptr == '\r') - inptr++; - } while (inptr == inend); - - do { - if (inptr < inend) { - if (*inptr == '"') { - /* qstring token */ - escaped = FALSE; - start = inptr; - - /* eat the beginning " */ - inptr++; - - p = inptr; - while (inptr < inend) { - if (*inptr == '"' && !escaped) - break; - - if (*inptr == '\\' && !escaped) { - token_save (stream, p, inptr - p); - escaped = TRUE; - inptr++; - p = inptr; - } else { - inptr++; - escaped = FALSE; - } - } - - token_save (stream, p, inptr - p); - - if (inptr == inend) { - stream->inptr = start; - goto refill; - } - - /* eat the ending " */ - inptr++; - - /* nul-terminate the atom token */ - token_save (stream, "", 1); - - token->token = CAMEL_IMAP_TOKEN_QSTRING; - token->v.qstring = stream->tokenbuf; - - d(fprintf (stderr, "token: \"%s\"\n", token->v.qstring)); - - break; - } else if (strchr ("+*()[]\n", *inptr)) { - /* special character token */ - token->token = *inptr++; -#if d(!)0 - if (token->token != '\n') - fprintf (stderr, "token: %c\n", token->token); - else - fprintf (stderr, "token: \\n\n"); -#endif - break; - } else if (*inptr == '{') { - /* literal identifier token */ - if ((p = strchr (inptr, '}')) && strchr (p, '\n')) { - inptr++; - - while (isdigit ((int) *inptr) && literal < UINT_MAX / 10) - literal = (literal * 10) + (*inptr++ - '0'); - - if (*inptr != '}') { - if (isdigit ((int) *inptr)) - g_warning ("illegal literal identifier: literal too large"); - else if (*inptr != '+') - g_warning ("illegal literal identifier: garbage following size"); - - while (*inptr != '}') - inptr++; - } - - /* skip over '}' */ - inptr++; - - /* skip over any trailing whitespace */ - while (*inptr == ' ' || *inptr == '\r') - inptr++; - - if (*inptr != '\n') { - g_warning ("illegal token following literal identifier: %s", inptr); - - /* skip ahead to the eoln */ - inptr = strchr (inptr, '\n'); - } - - /* skip over '\n' */ - inptr++; - - token->token = CAMEL_IMAP_TOKEN_LITERAL; - token->v.literal = literal; - - d(fprintf (stderr, "token: {%u}\n", literal)); - - stream->mode = CAMEL_IMAP_STREAM_MODE_LITERAL; - stream->literal = literal; - stream->eol = FALSE; - - break; - } else { - stream->inptr = inptr; - goto refill; - } - } else if (*inptr >= '0' && *inptr <= '9') { - /* number token */ - *inend = '\0'; - nz_number = strtoul ((char *) inptr, (char **) &start, 10); - if (start == inend) - goto refill; - - if (*start == ':' || *start == ',') { - /* workaround for 'set' tokens (APPENDUID / COPYUID) */ - goto atom_token; - } - - inptr = start; - token->token = CAMEL_IMAP_TOKEN_NUMBER; - token->v.number = nz_number; - - d(fprintf (stderr, "token: %u\n", nz_number)); - - break; - } else if (is_atom (*inptr)) { - atom_token: - /* simple atom token */ - start = inptr; - - while (inptr < inend && is_atom (*inptr)) - inptr++; - - if (inptr == inend) { - stream->inptr = start; - goto refill; - } - - token_save (stream, start, inptr - start); - - /* nul-terminate the atom token */ - token_save (stream, "", 1); - - if (!strcmp (stream->tokenbuf, "NIL")) { - /* special atom token */ - token->token = CAMEL_IMAP_TOKEN_NIL; - d(fprintf (stderr, "token: NIL\n")); - } else { - token->token = CAMEL_IMAP_TOKEN_ATOM; - token->v.atom = stream->tokenbuf; - d(fprintf (stderr, "token: %s\n", token->v.atom)); - } - - break; - } else if (*inptr == '\\') { - /* possible flag token ("\" atom) */ - start = inptr++; - - while (inptr < inend && is_atom (*inptr)) - inptr++; - - if (inptr == inend) { - stream->inptr = start; - goto refill; - } - - if ((inptr - start) > 1) { - token_save (stream, start, inptr - start); - - /* nul-terminate the flag token */ - token_save (stream, "", 1); - - token->token = CAMEL_IMAP_TOKEN_FLAG; - token->v.atom = stream->tokenbuf; - d(fprintf (stderr, "token: %s\n", token->v.atom)); - } else { - token->token = '\\'; - d(fprintf (stderr, "token: %c\n", token->token)); - } - break; - } else if (is_lwsp (*inptr)) { - inptr++; - } else { - /* unknown character token? */ - token->token = *inptr++; - d(fprintf (stderr, "token: %c\n", token->token)); - break; - } - } else { - refill: - token_clear (stream); - - if (imap_fill (stream) <= 0) { - token->token = CAMEL_IMAP_TOKEN_ERROR; - return -1; - } - - inptr = stream->inptr; - inend = stream->inend; - *inend = '\0'; - } - } while (inptr < inend); - - stream->inptr = inptr; - - return 0; -} - - -/** - * camel_imap_stream_unget_token: - * @stream: imap stream - * @token: token to 'unget' - * - * Ungets an imap token (as in ungetc()). - * - * Note: you may *ONLY* unget a single token. Trying to unget another - * token will fail. - * - * Returns 0 on success or -1 on fail. - **/ -int -camel_imap_stream_unget_token (CamelIMAPStream *stream, camel_imap_token_t *token) -{ - camel_imap_token_t *unget; - - if (stream->unget) - return -1; - - if (token->token != CAMEL_IMAP_TOKEN_NO_DATA) { - stream->unget = unget = g_new (camel_imap_token_t, 1); - memcpy (unget, token, sizeof (camel_imap_token_t)); - } - - return 0; -} - - -/** - * camel_imap_stream_readline: - * @stream: imap stream - * @line: line pointer - * @len: line length - * - * Reads a single line from the imap stream and points @line at an - * internal buffer containing the line read and sets @len to the - * length of the line buffer. - * - * Returns -1 on error, 0 if the line read is complete, or 1 if the - * read is incomplete. - **/ -int -camel_imap_stream_line (CamelIMAPStream *stream, unsigned char **line, size_t *len) -{ - register unsigned char *inptr; - unsigned char *inend; - - g_return_val_if_fail (CAMEL_IS_IMAP_STREAM (stream), -1); - g_return_val_if_fail (stream->mode != CAMEL_IMAP_STREAM_MODE_LITERAL, -1); - g_return_val_if_fail (line != NULL, -1); - g_return_val_if_fail (len != NULL, -1); - - if ((stream->inend - stream->inptr) < 3) { - /* keep our buffer full to the optimal size */ - if (imap_fill (stream) == -1 && stream->inptr == stream->inend) - return -1; - } - - *line = stream->inptr; - inptr = stream->inptr; - inend = stream->inend; - *inend = '\n'; - - while (*inptr != '\n') - inptr++; - - *len = (inptr - stream->inptr); - if (inptr < inend) { - /* got the eoln */ - if (inptr > stream->inptr && inptr[-1] == '\r') - inptr[-1] = '\0'; - else - inptr[0] = '\0'; - - stream->inptr = inptr + 1; - *len += 1; - - return 0; - } - - stream->inptr = inptr; - - return 1; -} - - -int -camel_imap_stream_literal (CamelIMAPStream *stream, unsigned char **literal, size_t *len) -{ - unsigned char *inptr, *inend; - size_t nread; - - g_return_val_if_fail (CAMEL_IS_IMAP_STREAM (stream), -1); - g_return_val_if_fail (stream->mode == CAMEL_IMAP_STREAM_MODE_LITERAL, -1); - g_return_val_if_fail (literal != NULL, -1); - g_return_val_if_fail (len != NULL, -1); - - if (stream->eol) { - *len = 0; - return 0; - } - - if ((stream->inend - stream->inptr) < 1) { - /* keep our buffer full to the optimal size */ - if (imap_fill (stream) == -1 && stream->inptr == stream->inend) - return -1; - } - - *literal = inptr = stream->inptr; - inend = stream->inend; - if ((inend - inptr) > stream->literal) - inend = inptr + stream->literal; - else - inend = stream->inend; - - *len = nread = inend - inptr; - - stream->literal -= nread; - if (stream->literal == 0) { - stream->mode = CAMEL_IMAP_STREAM_MODE_TOKEN; - stream->eol = TRUE; - return 0; - } - - return 1; -} diff --git a/camel/providers/imap4/camel-imap-stream.h b/camel/providers/imap4/camel-imap-stream.h deleted file mode 100644 index 872cbf7141..0000000000 --- a/camel/providers/imap4/camel-imap-stream.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- 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_STREAM_H__ -#define __CAMEL_IMAP_STREAM_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -#define CAMEL_TYPE_IMAP_STREAM (camel_imap_stream_get_type ()) -#define CAMEL_IMAP_STREAM(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP_STREAM, CamelIMAPStream)) -#define CAMEL_IMAP_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_TYPE_IMAP_STREAM, CamelIMAPStreamClass)) -#define CAMEL_IS_IMAP_STREAM(o) (CAMEL_CHECK_TYPE((o), CAMEL_TYPE_IMAP_STREAM)) - -typedef struct _CamelIMAPStream CamelIMAPStream; -typedef struct _CamelIMAPStreamClass CamelIMAPStreamClass; - -#define IMAP_READ_PRELEN 128 -#define IMAP_READ_BUFLEN 4096 - -enum { - CAMEL_IMAP_TOKEN_NO_DATA = -8, - CAMEL_IMAP_TOKEN_ERROR = -7, - CAMEL_IMAP_TOKEN_NIL = -6, - CAMEL_IMAP_TOKEN_ATOM = -5, - CAMEL_IMAP_TOKEN_FLAG = -4, - CAMEL_IMAP_TOKEN_NUMBER = -3, - CAMEL_IMAP_TOKEN_QSTRING = -2, - CAMEL_IMAP_TOKEN_LITERAL = -1, - /* CAMEL_IMAP_TOKEN_CHAR would just be the char we got */ - CAMEL_IMAP_TOKEN_EOLN = '\n', - CAMEL_IMAP_TOKEN_LPAREN = '(', - CAMEL_IMAP_TOKEN_RPAREN = ')', - CAMEL_IMAP_TOKEN_ASTERISK = '*', - CAMEL_IMAP_TOKEN_PLUS = '+', - CAMEL_IMAP_TOKEN_LBRACKET = '[', - CAMEL_IMAP_TOKEN_RBRACKET = ']', -}; - -typedef struct _camel_imap_token_t { - int token; - union { - char *atom; - char *flag; - char *qstring; - size_t literal; - guint32 number; - } v; -} camel_imap_token_t; - -enum { - CAMEL_IMAP_STREAM_MODE_TOKEN = 0, - CAMEL_IMAP_STREAM_MODE_LITERAL = 1, -}; - -struct _CamelIMAPStream { - CamelStream parent_object; - - CamelStream *stream; - - guint disconnected:1; /* disconnected state */ - guint mode:1; /* TOKEN vs LITERAL */ - guint eol:1; /* end-of-literal */ - - size_t literal; - - /* i/o buffers */ - unsigned char realbuf[IMAP_READ_PRELEN + IMAP_READ_BUFLEN + 1]; - unsigned char *inbuf; - unsigned char *inptr; - unsigned char *inend; - - /* token buffers */ - unsigned char *tokenbuf; - unsigned char *tokenptr; - unsigned int tokenleft; - - camel_imap_token_t *unget; -}; - -struct _CamelIMAPStreamClass { - CamelStreamClass parent_class; - - /* Virtual methods */ -}; - - -/* Standard Camel function */ -CamelType camel_imap_stream_get_type (void); - -CamelStream *camel_imap_stream_new (CamelStream *stream); - -int camel_imap_stream_next_token (CamelIMAPStream *stream, camel_imap_token_t *token); -int camel_imap_stream_unget_token (CamelIMAPStream *stream, camel_imap_token_t *token); - -int camel_imap_stream_line (CamelIMAPStream *stream, unsigned char **line, size_t *len); -int camel_imap_stream_literal (CamelIMAPStream *stream, unsigned char **literal, size_t *len); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __CAMEL_IMAP_STREAM_H__ */ diff --git a/camel/providers/imap4/camel-imap-summary.c b/camel/providers/imap4/camel-imap-summary.c deleted file mode 100644 index d97c93b66c..0000000000 --- a/camel/providers/imap4/camel-imap-summary.c +++ /dev/null @@ -1,1107 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#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 deleted file mode 100644 index a8faff8522..0000000000 --- a/camel/providers/imap4/camel-imap-summary.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- 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 - -#include -#include - -#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__ */ diff --git a/camel/providers/imap4/camel-imap-utils.c b/camel/providers/imap4/camel-imap-utils.c deleted file mode 100644 index 129c7b83af..0000000000 --- a/camel/providers/imap4/camel-imap-utils.c +++ /dev/null @@ -1,313 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include - -#include "camel-imap-engine.h" -#include "camel-imap-stream.h" -#include "camel-imap-command.h" - -#include "camel-imap-utils.h" - -#define d(x) x - - -void -camel_imap_flags_diff (flags_diff_t *diff, guint32 old, guint32 new) -{ - diff->changed = old ^ new; - diff->bits = new & diff->changed; -} - - -guint32 -camel_imap_flags_merge (flags_diff_t *diff, guint32 flags) -{ - return (flags & ~diff->changed) | diff->bits; -} - - -/** - * camel_imap_merge_flags: - * @original: original server flags - * @local: local flags (after changes) - * @server: new server flags (another client updated the server flags) - * - * Merge the local flag changes into the new server flags. - * - * Returns the merged flags. - **/ -guint32 -camel_imap_merge_flags (guint32 original, guint32 local, guint32 server) -{ - flags_diff_t diff; - - camel_imap_flags_diff (&diff, original, local); - - return camel_imap_flags_merge (&diff, server); -} - - -void -camel_imap_utils_set_unexpected_token_error (CamelException *ex, CamelIMAPEngine *engine, camel_imap_token_t *token) -{ - GString *errmsg; - - if (ex == NULL) - return; - - errmsg = g_string_new (""); - g_string_append_printf (errmsg, _("Unexpected token in response from IMAP server %s: "), - engine->url->host); - - switch (token->token) { - case CAMEL_IMAP_TOKEN_NIL: - g_string_append (errmsg, "NIL"); - break; - case CAMEL_IMAP_TOKEN_ATOM: - g_string_append (errmsg, token->v.atom); - break; - case CAMEL_IMAP_TOKEN_FLAG: - g_string_append (errmsg, token->v.flag); - break; - case CAMEL_IMAP_TOKEN_QSTRING: - g_string_append (errmsg, token->v.qstring); - break; - case CAMEL_IMAP_TOKEN_LITERAL: - g_string_append_printf (errmsg, "{%u}", token->v.literal); - break; - case CAMEL_IMAP_TOKEN_NUMBER: - g_string_append_printf (errmsg, "%u", token->v.number); - break; - case CAMEL_IMAP_TOKEN_NO_DATA: - g_string_append (errmsg, _("No data")); - break; - default: - g_string_append_c (errmsg, (unsigned char) (token->token & 0xff)); - break; - } - - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, errmsg->str); - - g_string_free (errmsg, TRUE); -} - - -static struct { - const char *name; - guint32 flag; -} imap_flags[] = { - { "\\Answered", CAMEL_MESSAGE_ANSWERED }, - { "\\Deleted", CAMEL_MESSAGE_DELETED }, - { "\\Draft", CAMEL_MESSAGE_DRAFT }, - { "\\Flagged", CAMEL_MESSAGE_FLAGGED }, - { "\\Seen", CAMEL_MESSAGE_SEEN }, - /*{ "\\Recent", CAMEL_MESSAGE_RECENT },*/ - { "\\*", CAMEL_MESSAGE_USER }, -}; - -#if 0 -static struct { - const char *name; - guint32 flag; -} imap_user_flags[] = { - { "Forwarded", CAMEL_MESSAGE_FORWARDED }, -}; -#endif - - -int -camel_imap_parse_flags_list (CamelIMAPEngine *engine, guint32 *flags, CamelException *ex) -{ - camel_imap_token_t token; - guint32 new = 0; - int i; - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - if (token.token != '(') { - d(fprintf (stderr, "Expected to find a '(' token starting the flags list\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - - while (token.token == CAMEL_IMAP_TOKEN_ATOM || token.token == CAMEL_IMAP_TOKEN_FLAG) { - /* parse the flags list */ - for (i = 0; i < G_N_ELEMENTS (imap_flags); i++) { - if (!strcasecmp (imap_flags[i].name, token.v.atom)) { - new |= imap_flags[i].flag; - break; - } - } - -#if 0 - if (i == G_N_ELEMENTS (imap_flags)) { - for (i = 0; i < G_N_ELEMENTS (imap_user_flags); i++) { - if (!strcasecmp (imap_user_flags[i].name, token.v.atom)) { - new |= imap_user_flags[i].flag; - break; - } - } - - if (i == G_N_ELEMENTS (imap_user_flags)) - fprintf (stderr, "Encountered unknown flag: %s\n", token.v.atom); - } -#else - if (i == G_N_ELEMENTS (imap_flags)) - fprintf (stderr, "Encountered unknown flag: %s\n", token.v.atom); -#endif - - if (camel_imap_engine_next_token (engine, &token, ex) == -1) - return -1; - } - - if (token.token != ')') { - d(fprintf (stderr, "Expected to find a ')' token terminating the flags list\n")); - camel_imap_utils_set_unexpected_token_error (ex, engine, &token); - return -1; - } - - *flags = new; - - return 0; -} - - -struct { - const char *name; - guint32 flag; -} list_flags[] = { - { "\\Marked", CAMEL_IMAP_FOLDER_MARKED }, - { "\\Unmarked", CAMEL_IMAP_FOLDER_UNMARKED }, - { "\\Noselect", CAMEL_IMAP_FOLDER_NOSELECT }, - { "\\Noinferiors", CAMEL_IMAP_FOLDER_NOINFERIORS }, - { "\\HasChildren", CAMEL_IMAP_FOLDER_HAS_CHILDREN }, - { "\\HasNoChildren", CAMEL_IMAP_FOLDER_HAS_NO_CHILDREN }, -}; - -int -camel_imap_untagged_list (CamelIMAPEngine *engine, CamelIMAPCommand *ic, guint32 index, camel_imap_token_t *token, CamelException *ex) -{ - GPtrArray *array = ic->user_data; - camel_imap_list_t *list; - unsigned char *buf; - guint32 flags = 0; - GString *literal; - char delim; - size_t n; - int i; - - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - /* parse the flag list */ - if (token->token != '(') - goto unexpected; - - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - while (token->token == CAMEL_IMAP_TOKEN_FLAG || token->token == CAMEL_IMAP_TOKEN_ATOM) { - for (i = 0; i < G_N_ELEMENTS (list_flags); i++) { - if (!g_ascii_strcasecmp (list_flags[i].name, token->v.atom)) { - flags |= list_flags[i].flag; - break; - } - } - - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - } - - if (token->token != ')') - goto unexpected; - - /* parse the path delimiter */ - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - switch (token->token) { - case CAMEL_IMAP_TOKEN_NIL: - delim = '\0'; - break; - case CAMEL_IMAP_TOKEN_QSTRING: - delim = *token->v.qstring; - break; - default: - goto unexpected; - } - - /* parse the folder name */ - if (camel_imap_engine_next_token (engine, token, ex) == -1) - return -1; - - list = g_new (camel_imap_list_t, 1); - list->flags = flags; - list->delim = delim; - - switch (token->token) { - case CAMEL_IMAP_TOKEN_ATOM: - list->name = g_strdup (token->v.atom); - break; - case CAMEL_IMAP_TOKEN_QSTRING: - list->name = g_strdup (token->v.qstring); - break; - case CAMEL_IMAP_TOKEN_LITERAL: - literal = g_string_new (""); - while ((i = camel_imap_stream_literal (engine->istream, &buf, &n)) == 1) - g_string_append_len (literal, buf, n); - - if (i == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("IMAP server %s unexpectedly disconnected: %s"), - engine->url->host, errno ? g_strerror (errno) : _("Unknown")); - g_string_free (literal, TRUE); - return -1; - } - - g_string_append_len (literal, buf, n); - list->name = literal->str; - g_string_free (literal, FALSE); - break; - default: - g_free (list); - goto unexpected; - } - - g_ptr_array_add (array, list); - - return camel_imap_engine_eat_line (engine, ex); - - unexpected: - - camel_imap_utils_set_unexpected_token_error (ex, engine, token); - - return -1; -} diff --git a/camel/providers/imap4/camel-imap-utils.h b/camel/providers/imap4/camel-imap-utils.h deleted file mode 100644 index c2f87ae4e1..0000000000 --- a/camel/providers/imap4/camel-imap-utils.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- 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_UTILS_H__ -#define __CAMEL_IMAP_UTILS_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -/* IMAP flag merging */ -typedef struct { - guint32 changed; - guint32 bits; -} flags_diff_t; - -void camel_imap_flags_diff (flags_diff_t *diff, guint32 old, guint32 new); -guint32 camel_imap_flags_merge (flags_diff_t *diff, guint32 flags); -guint32 camel_imap_merge_flags (guint32 original, guint32 local, guint32 server); - - -struct _CamelIMAPEngine; -struct _CamelIMAPCommand; -struct _camel_imap_token_t; - -void camel_imap_utils_set_unexpected_token_error (CamelException *ex, struct _CamelIMAPEngine *engine, struct _camel_imap_token_t *token); - -int camel_imap_parse_flags_list (struct _CamelIMAPEngine *engine, guint32 *flags, CamelException *ex); - -enum { - CAMEL_IMAP_FOLDER_MARKED = (1 << 0), - CAMEL_IMAP_FOLDER_UNMARKED = (1 << 1), - CAMEL_IMAP_FOLDER_NOSELECT = (1 << 2), - CAMEL_IMAP_FOLDER_NOINFERIORS = (1 << 3), - CAMEL_IMAP_FOLDER_HAS_CHILDREN = (1 << 4), - CAMEL_IMAP_FOLDER_HAS_NO_CHILDREN = (1 << 5), -}; - -typedef struct { - guint32 flags; - char delim; - char *name; -} camel_imap_list_t; - -int camel_imap_untagged_list (struct _CamelIMAPEngine *engine, struct _CamelIMAPCommand *ic, - guint32 index, struct _camel_imap_token_t *token, CamelException *ex); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __CAMEL_IMAP_UTILS_H__ */ diff --git a/camel/providers/imap4/camel-imap4-command.c b/camel/providers/imap4/camel-imap4-command.c new file mode 100644 index 0000000000..c1ff3a289b --- /dev/null +++ b/camel/providers/imap4/camel-imap4-command.c @@ -0,0 +1,674 @@ +/* -*- 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 +#endif + +#include +#include +#include + +#include +#include +#include + +#include "camel-imap4-stream.h" +#include "camel-imap4-engine.h" +#include "camel-imap4-folder.h" +#include "camel-imap4-specials.h" + +#include "camel-imap4-command.h" + + +#define d(x) x + + +enum { + IMAP4_STRING_ATOM, + IMAP4_STRING_QSTRING, + IMAP4_STRING_LITERAL, +}; + +static int +imap4_string_get_type (const char *str) +{ + int type = 0; + + while (*str) { + if (!is_atom (*str)) { + if (is_qsafe (*str)) + type = IMAP4_STRING_QSTRING; + else + return IMAP4_STRING_LITERAL; + } + str++; + } + + return type; +} + +#if 0 +static gboolean +imap4_string_is_atom_safe (const char *str) +{ + while (is_atom (*str)) + str++; + + return *str == '\0'; +} + +static gboolean +imap4_string_is_quote_safe (const char *str) +{ + while (is_qsafe (*str)) + str++; + + return *str == '\0'; +} +#endif + +static size_t +camel_imap4_literal_length (CamelIMAP4Literal *literal) +{ + CamelStream *stream, *null; + CamelMimeFilter *crlf; + size_t len; + + if (literal->type == CAMEL_IMAP4_LITERAL_STRING) + return strlen (literal->literal.string); + + null = camel_stream_null_new (); + crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + stream = (CamelStream *) camel_stream_filter_new_with_stream (null); + camel_stream_filter_add ((CamelStreamFilter *) stream, crlf); + camel_object_unref (crlf); + + switch (literal->type) { + case CAMEL_IMAP4_LITERAL_STREAM: + camel_stream_write_to_stream (literal->literal.stream, stream); + camel_stream_reset (literal->literal.stream); + break; + case CAMEL_IMAP4_LITERAL_WRAPPER: + camel_data_wrapper_write_to_stream (literal->literal.wrapper, stream); + break; + default: + g_assert_not_reached (); + break; + } + + len = ((CamelStreamNull *) null)->written; + + camel_object_unref (stream); + camel_object_unref (null); + + return len; +} + +static CamelIMAP4CommandPart * +command_part_new (void) +{ + CamelIMAP4CommandPart *part; + + part = g_new (CamelIMAP4CommandPart, 1); + part->next = NULL; + part->buffer = NULL; + part->buflen = 0; + part->literal = NULL; + + return part; +} + +static void +imap4_command_append_string (CamelIMAP4Engine *engine, CamelIMAP4CommandPart **tail, GString *str, const char *string) +{ + CamelIMAP4CommandPart *part; + CamelIMAP4Literal *literal; + + switch (imap4_string_get_type (string)) { + case IMAP4_STRING_ATOM: + /* string is safe as it is... */ + g_string_append (str, string); + break; + case IMAP4_STRING_QSTRING: + /* we need to quote the string */ + /* FIXME: need to escape stuff */ + g_string_append_printf (str, "\"%s\"", string); + break; + case IMAP4_STRING_LITERAL: + if (engine->capa & CAMEL_IMAP4_CAPABILITY_LITERALPLUS) { + /* we have to send a literal, but the server supports LITERAL+ so use that */ + g_string_append_printf (str, "{%u+}\r\n%s", strlen (string), string); + } else { + /* we have to make it a literal */ + literal = g_new (CamelIMAP4Literal, 1); + literal->type = CAMEL_IMAP4_LITERAL_STRING; + literal->literal.string = g_strdup (string); + + g_string_append_printf (str, "{%u}\r\n", strlen (string)); + + (*tail)->buffer = g_strdup (str->str); + (*tail)->buflen = str->len; + (*tail)->literal = literal; + + part = command_part_new (); + (*tail)->next = part; + (*tail) = part; + + g_string_truncate (str, 0); + } + break; + } +} + +CamelIMAP4Command * +camel_imap4_command_newv (CamelIMAP4Engine *engine, CamelIMAP4Folder *imap4_folder, const char *format, va_list args) +{ + CamelIMAP4CommandPart *parts, *part, *tail; + CamelIMAP4Command *ic; + const char *start; + GString *str; + + tail = parts = command_part_new (); + + str = g_string_new (""); + start = format; + + while (*format) { + register char ch = *format++; + + if (ch == '%') { + CamelIMAP4Literal *literal; + CamelIMAP4Folder *folder; + unsigned int u; + char *string; + size_t len; + void *obj; + int c, d; + + g_string_append_len (str, start, format - start - 1); + + switch (*format) { + case '%': + /* literal % */ + g_string_append_c (str, '%'); + break; + case 'c': + /* character */ + c = va_arg (args, int); + g_string_append_c (str, c); + break; + case 'd': + /* integer */ + d = va_arg (args, int); + g_string_append_printf (str, "%d", d); + break; + case 'u': + /* unsigned integer */ + u = va_arg (args, unsigned int); + g_string_append_printf (str, "%u", u); + break; + case 'F': + /* CamelIMAP4Folder */ + folder = va_arg (args, CamelIMAP4Folder *); + string = (char *) camel_imap4_folder_utf7_name (folder); + imap4_command_append_string (engine, &tail, str, string); + break; + case 'L': + /* Literal */ + obj = va_arg (args, void *); + + literal = g_new (CamelIMAP4Literal, 1); + if (CAMEL_IS_DATA_WRAPPER (obj)) { + literal->type = CAMEL_IMAP4_LITERAL_WRAPPER; + literal->literal.wrapper = obj; + } else if (CAMEL_IS_STREAM (obj)) { + literal->type = CAMEL_IMAP4_LITERAL_STREAM; + literal->literal.stream = obj; + } else { + g_assert_not_reached (); + } + + camel_object_ref (obj); + + /* FIXME: take advantage of LITERAL+? */ + len = camel_imap4_literal_length (literal); + g_string_append_printf (str, "{%u}\r\n", len); + + tail->buffer = g_strdup (str->str); + tail->buflen = str->len; + tail->literal = literal; + + part = command_part_new (); + tail->next = part; + tail = part; + + g_string_truncate (str, 0); + + break; + case 'S': + /* string which may need to be quoted or made into a literal */ + string = va_arg (args, char *); + imap4_command_append_string (engine, &tail, str, string); + break; + case 's': + /* safe atom string */ + string = va_arg (args, char *); + g_string_append (str, string); + break; + default: + g_warning ("unknown formatter %%%c", *format); + g_string_append_c (str, '%'); + g_string_append_c (str, *format); + break; + } + + format++; + + start = format; + } + } + + g_string_append (str, start); + tail->buffer = str->str; + tail->buflen = str->len; + tail->literal = NULL; + g_string_free (str, FALSE); + + ic = g_new0 (CamelIMAP4Command, 1); + ((EDListNode *) ic)->next = NULL; + ((EDListNode *) ic)->prev = NULL; + ic->untagged = g_hash_table_new (g_str_hash, g_str_equal); + ic->status = CAMEL_IMAP4_COMMAND_QUEUED; + ic->resp_codes = g_ptr_array_new (); + ic->engine = engine; + ic->ref_count = 1; + ic->parts = parts; + ic->part = parts; + + camel_exception_init (&ic->ex); + + if (imap4_folder) { + camel_object_ref (imap4_folder); + ic->folder = imap4_folder; + } else + ic->folder = NULL; + + return ic; +} + +CamelIMAP4Command * +camel_imap4_command_new (CamelIMAP4Engine *engine, CamelIMAP4Folder *folder, const char *format, ...) +{ + CamelIMAP4Command *command; + va_list args; + + va_start (args, format); + command = camel_imap4_command_newv (engine, folder, format, args); + va_end (args); + + return command; +} + +void +camel_imap4_command_register_untagged (CamelIMAP4Command *ic, const char *atom, CamelIMAP4UntaggedCallback untagged) +{ + g_hash_table_insert (ic->untagged, g_strdup (atom), untagged); +} + +void +camel_imap4_command_ref (CamelIMAP4Command *ic) +{ + ic->ref_count++; +} + +void +camel_imap4_command_unref (CamelIMAP4Command *ic) +{ + CamelIMAP4CommandPart *part, *next; + int i; + + if (ic == NULL) + return; + + ic->ref_count--; + if (ic->ref_count == 0) { + if (ic->folder) + camel_object_unref (ic->folder); + + g_free (ic->tag); + + for (i = 0; i < ic->resp_codes->len; i++) { + CamelIMAP4RespCode *resp_code; + + resp_code = ic->resp_codes->pdata[i]; + camel_imap4_resp_code_free (resp_code); + } + g_ptr_array_free (ic->resp_codes, TRUE); + + g_hash_table_foreach (ic->untagged, (GHFunc) g_free, NULL); + g_hash_table_destroy (ic->untagged); + + camel_exception_clear (&ic->ex); + + part = ic->parts; + while (part != NULL) { + g_free (part->buffer); + if (part->literal) { + switch (part->literal->type) { + case CAMEL_IMAP4_LITERAL_STRING: + g_free (part->literal->literal.string); + break; + case CAMEL_IMAP4_LITERAL_STREAM: + camel_object_unref (part->literal->literal.stream); + break; + case CAMEL_IMAP4_LITERAL_WRAPPER: + camel_object_unref (part->literal->literal.wrapper); + break; + } + + g_free (part->literal); + } + + next = part->next; + g_free (part); + part = next; + } + + g_free (ic); + } +} + + +static int +imap4_literal_write_to_stream (CamelIMAP4Literal *literal, CamelStream *stream) +{ + CamelStream *istream, *ostream = NULL; + CamelDataWrapper *wrapper; + CamelMimeFilter *crlf; + char *string; + + if (literal->type == CAMEL_IMAP4_LITERAL_STRING) { + string = literal->literal.string; + if (camel_stream_write (stream, string, strlen (string)) == -1) + return -1; + + return 0; + } + + crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + ostream = (CamelStream *) camel_stream_filter_new_with_stream (stream); + camel_stream_filter_add ((CamelStreamFilter *) ostream, crlf); + camel_object_unref (crlf); + + /* write the literal */ + switch (literal->type) { + case CAMEL_IMAP4_LITERAL_STREAM: + istream = literal->literal.stream; + if (camel_stream_write_to_stream (istream, ostream) == -1) + goto exception; + break; + case CAMEL_IMAP4_LITERAL_WRAPPER: + wrapper = literal->literal.wrapper; + if (camel_data_wrapper_write_to_stream (wrapper, ostream) == -1) + goto exception; + break; + } + + camel_object_unref (ostream); + ostream = NULL; + +#if 0 + if (camel_stream_write (stream, "\r\n", 2) == -1) + return -1; +#endif + + return 0; + + exception: + + camel_object_unref (ostream); + + return -1; +} + + +static void +unexpected_token (camel_imap4_token_t *token) +{ + switch (token->token) { + case CAMEL_IMAP4_TOKEN_NO_DATA: + fprintf (stderr, "*** NO DATA ***"); + break; + case CAMEL_IMAP4_TOKEN_ERROR: + fprintf (stderr, "*** ERROR ***"); + break; + case CAMEL_IMAP4_TOKEN_NIL: + fprintf (stderr, "NIL"); + break; + case CAMEL_IMAP4_TOKEN_ATOM: + fprintf (stderr, "%s", token->v.atom); + break; + case CAMEL_IMAP4_TOKEN_QSTRING: + fprintf (stderr, "\"%s\"", token->v.qstring); + break; + case CAMEL_IMAP4_TOKEN_LITERAL: + fprintf (stderr, "{%u}", token->v.literal); + break; + default: + fprintf (stderr, "%c", (unsigned char) (token->token & 0xff)); + break; + } +} + +int +camel_imap4_command_step (CamelIMAP4Command *ic) +{ + CamelIMAP4Engine *engine = ic->engine; + int result = CAMEL_IMAP4_RESULT_NONE; + CamelIMAP4Literal *literal; + camel_imap4_token_t token; + unsigned char *linebuf; + ssize_t nwritten; + size_t len; + + g_assert (ic->part != NULL); + + if (ic->part == ic->parts) { + ic->tag = g_strdup_printf ("%c%.5u", engine->tagprefix, engine->tag++); + camel_stream_printf (engine->ostream, "%s ", ic->tag); + d(fprintf (stderr, "sending : %s ", ic->tag)); + } + +#if d(!)0 + { + int sending = ic->part != ic->parts; + unsigned char *eoln, *eob; + + linebuf = ic->part->buffer; + eob = linebuf + ic->part->buflen; + + do { + eoln = linebuf; + while (eoln < eob && *eoln != '\n') + eoln++; + + if (eoln < eob) + eoln++; + + if (sending) + fwrite ("sending : ", 1, 10, stderr); + fwrite (linebuf, 1, eoln - linebuf, stderr); + + linebuf = eoln + 1; + sending = 1; + } while (linebuf < eob); + } +#endif + + linebuf = ic->part->buffer; + len = ic->part->buflen; + + if ((nwritten = camel_stream_write (engine->ostream, linebuf, len)) == -1) + goto exception; + + if (camel_stream_flush (engine->ostream) == -1) + goto exception; + + /* now we need to read the response(s) from the IMAP4 server */ + + do { + if (camel_imap4_engine_next_token (engine, &token, &ic->ex) == -1) + goto exception; + + if (token.token == '+') { + /* we got a continuation response from the server */ + literal = ic->part->literal; + + if (camel_imap4_engine_line (engine, &linebuf, &len, &ic->ex) == -1) + goto exception; + + if (literal) { + if (imap4_literal_write_to_stream (literal, engine->ostream) == -1) + goto exception; + + g_free (linebuf); + linebuf = NULL; + + break; + } else if (ic->plus) { + /* command expected a '+' response - probably AUTHENTICATE? */ + if (ic->plus (engine, ic, linebuf, len, &ic->ex) == -1) { + g_free (linebuf); + return -1; + } + + /* now we need to wait for a " OK/NO/BAD" response */ + } else { + /* FIXME: error?? */ + g_assert_not_reached (); + } + + g_free (linebuf); + linebuf = NULL; + } else if (token.token == '*') { + /* we got an untagged response, let the engine handle this */ + if (camel_imap4_engine_handle_untagged_1 (engine, &token, &ic->ex) == -1) + goto exception; + } else if (token.token == CAMEL_IMAP4_TOKEN_ATOM && !strcmp (token.v.atom, ic->tag)) { + /* we got " OK/NO/BAD" */ + fprintf (stderr, "got %s response\n", token.v.atom); + + if (camel_imap4_engine_next_token (engine, &token, &ic->ex) == -1) + goto exception; + + if (token.token == CAMEL_IMAP4_TOKEN_ATOM) { + if (!strcmp (token.v.atom, "OK")) + result = CAMEL_IMAP4_RESULT_OK; + else if (!strcmp (token.v.atom, "NO")) + result = CAMEL_IMAP4_RESULT_NO; + else if (!strcmp (token.v.atom, "BAD")) + result = CAMEL_IMAP4_RESULT_BAD; + + if (result == CAMEL_IMAP4_RESULT_NONE) { + fprintf (stderr, "expected OK/NO/BAD but got %s\n", token.v.atom); + goto unexpected; + } + + if (camel_imap4_engine_next_token (engine, &token, &ic->ex) == -1) + goto exception; + + if (token.token == '[') { + /* we have a response code */ + camel_imap4_stream_unget_token (engine->istream, &token); + if (camel_imap4_engine_parse_resp_code (engine, &ic->ex) == -1) + goto exception; + } else if (token.token != '\n') { + /* just gobble up the rest of the line */ + if (camel_imap4_engine_line (engine, NULL, NULL, &ic->ex) == -1) + goto exception; + } + } else { + fprintf (stderr, "expected anything but this: "); + unexpected_token (&token); + fprintf (stderr, "\n"); + + goto unexpected; + } + + break; + } else { + fprintf (stderr, "wtf is this: "); + unexpected_token (&token); + fprintf (stderr, "\n"); + + unexpected: + + /* no fucking clue what we got... */ + if (camel_imap4_engine_line (engine, &linebuf, &len, &ic->ex) == -1) + goto exception; + + camel_exception_setv (&ic->ex, CAMEL_EXCEPTION_SYSTEM, + _("Unexpected response from IMAP4 server %s: %s"), + engine->url->host, linebuf); + + g_free (linebuf); + + goto exception; + } + } while (1); + + /* status should always be ACTIVE here... */ + if (ic->status == CAMEL_IMAP4_COMMAND_ACTIVE) { + ic->part = ic->part->next; + if (ic->part == NULL || result) { + ic->status = CAMEL_IMAP4_COMMAND_COMPLETE; + ic->result = result; + return 1; + } + } + + return 0; + + exception: + + ic->status = CAMEL_IMAP4_COMMAND_ERROR; + + return -1; +} + + +void +camel_imap4_command_reset (CamelIMAP4Command *ic) +{ + int i; + + for (i = 0; i < ic->resp_codes->len; i++) + camel_imap4_resp_code_free (ic->resp_codes->pdata[i]); + g_ptr_array_set_size (ic->resp_codes, 0); + + ic->status = CAMEL_IMAP4_COMMAND_QUEUED; + ic->result = CAMEL_IMAP4_RESULT_NONE; + ic->part = ic->parts; + g_free (ic->tag); + ic->tag = NULL; + + camel_exception_clear (&ic->ex); +} diff --git a/camel/providers/imap4/camel-imap4-command.h b/camel/providers/imap4/camel-imap4-command.h new file mode 100644 index 0000000000..49487b1b2d --- /dev/null +++ b/camel/providers/imap4/camel-imap4-command.h @@ -0,0 +1,144 @@ +/* -*- 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_IMAP4_COMMAND_H__ +#define __CAMEL_IMAP4_COMMAND_H__ + +#include + +#include + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +struct _CamelIMAP4Engine; +struct _CamelIMAP4Folder; +struct _camel_imap4_token_t; + +typedef struct _CamelIMAP4Command CamelIMAP4Command; +typedef struct _CamelIMAP4Literal CamelIMAP4Literal; + +typedef int (* CamelIMAP4PlusCallback) (struct _CamelIMAP4Engine *engine, + CamelIMAP4Command *ic, + const unsigned char *linebuf, + size_t linelen, CamelException *ex); + +typedef int (* CamelIMAP4UntaggedCallback) (struct _CamelIMAP4Engine *engine, + CamelIMAP4Command *ic, + guint32 index, + struct _camel_imap4_token_t *token, + CamelException *ex); + +enum { + CAMEL_IMAP4_LITERAL_STRING, + CAMEL_IMAP4_LITERAL_STREAM, + CAMEL_IMAP4_LITERAL_WRAPPER, +}; + +struct _CamelIMAP4Literal { + int type; + union { + char *string; + CamelStream *stream; + CamelDataWrapper *wrapper; + } literal; +}; + +typedef struct _CamelIMAP4CommandPart { + struct _CamelIMAP4CommandPart *next; + unsigned char *buffer; + size_t buflen; + + CamelIMAP4Literal *literal; +} CamelIMAP4CommandPart; + +enum { + CAMEL_IMAP4_COMMAND_QUEUED, + CAMEL_IMAP4_COMMAND_ACTIVE, + CAMEL_IMAP4_COMMAND_COMPLETE, + CAMEL_IMAP4_COMMAND_ERROR, +}; + +enum { + CAMEL_IMAP4_RESULT_NONE, + CAMEL_IMAP4_RESULT_OK, + CAMEL_IMAP4_RESULT_NO, + CAMEL_IMAP4_RESULT_BAD, +}; + +struct _CamelIMAP4Command { + EDListNode node; + + struct _CamelIMAP4Engine *engine; + + unsigned int ref_count:26; + unsigned int status:3; + unsigned int result:3; + int id; + + char *tag; + + GPtrArray *resp_codes; + + struct _CamelIMAP4Folder *folder; + CamelException ex; + + /* command parts - logical breaks in the overall command based on literals */ + CamelIMAP4CommandPart *parts; + + /* current part */ + CamelIMAP4CommandPart *part; + + /* untagged handlers */ + GHashTable *untagged; + + /* '+' callback/data */ + CamelIMAP4PlusCallback plus; + void *user_data; +}; + +CamelIMAP4Command *camel_imap4_command_new (struct _CamelIMAP4Engine *engine, struct _CamelIMAP4Folder *folder, + const char *format, ...); +CamelIMAP4Command *camel_imap4_command_newv (struct _CamelIMAP4Engine *engine, struct _CamelIMAP4Folder *folder, + const char *format, va_list args); + +void camel_imap4_command_register_untagged (CamelIMAP4Command *ic, const char *atom, CamelIMAP4UntaggedCallback untagged); + +void camel_imap4_command_ref (CamelIMAP4Command *ic); +void camel_imap4_command_unref (CamelIMAP4Command *ic); + +/* returns 1 when complete, 0 if there is more to do, or -1 on error */ +int camel_imap4_command_step (CamelIMAP4Command *ic); + +void camel_imap4_command_reset (CamelIMAP4Command *ic); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_COMMAND_H__ */ diff --git a/camel/providers/imap4/camel-imap4-engine.c b/camel/providers/imap4/camel-imap4-engine.c new file mode 100644 index 0000000000..5e3d93ddaf --- /dev/null +++ b/camel/providers/imap4/camel-imap4-engine.c @@ -0,0 +1,1669 @@ +/* -*- 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 +#endif + +#include +#include +#include + +#include +#include + +#include "camel-imap4-summary.h" +#include "camel-imap4-command.h" +#include "camel-imap4-stream.h" +#include "camel-imap4-folder.h" +#include "camel-imap4-utils.h" + +#include "camel-imap4-engine.h" + +#define d(x) x + + +static void camel_imap4_engine_class_init (CamelIMAP4EngineClass *klass); +static void camel_imap4_engine_init (CamelIMAP4Engine *engine, CamelIMAP4EngineClass *klass); +static void camel_imap4_engine_finalize (CamelObject *object); + + +static CamelObjectClass *parent_class = NULL; + + +CamelType +camel_imap4_engine_get_type (void) +{ + static CamelType type = 0; + + if (!type) { + type = camel_type_register (CAMEL_TYPE_IMAP4_ENGINE, + "CamelIMAP4Engine", + sizeof (CamelIMAP4Engine), + sizeof (CamelIMAP4EngineClass), + (CamelObjectClassInitFunc) camel_imap4_engine_class_init, + NULL, + (CamelObjectInitFunc) camel_imap4_engine_init, + (CamelObjectFinalizeFunc) camel_imap4_engine_finalize); + } + + return type; +} + +static void +camel_imap4_engine_class_init (CamelIMAP4EngineClass *klass) +{ + parent_class = camel_type_get_global_classfuncs (CAMEL_OBJECT_TYPE); + + klass->tagprefix = 'A'; +} + +static void +camel_imap4_engine_init (CamelIMAP4Engine *engine, CamelIMAP4EngineClass *klass) +{ + engine->state = CAMEL_IMAP4_ENGINE_DISCONNECTED; + engine->level = CAMEL_IMAP4_LEVEL_UNKNOWN; + + engine->session = NULL; + engine->url = NULL; + + engine->istream = NULL; + engine->ostream = NULL; + + engine->authtypes = g_hash_table_new (g_str_hash, g_str_equal); + + engine->capa = 0; + + /* this is the suggested default, impacts the max command line length we'll send */ + engine->maxlentype = CAMEL_IMAP4_ENGINE_MAXLEN_LINE; + engine->maxlen = 1000; + + engine->namespaces.personal = NULL; + engine->namespaces.other = NULL; + engine->namespaces.shared = NULL; + + if (klass->tagprefix > 'Z') + klass->tagprefix = 'A'; + + engine->tagprefix = klass->tagprefix++; + engine->tag = 0; + + engine->nextid = 1; + + engine->folder = NULL; + + e_dlist_init (&engine->queue); +} + +static void +imap4_namespace_clear (CamelIMAP4Namespace **namespace) +{ + CamelIMAP4Namespace *node, *next; + + node = *namespace; + while (node != NULL) { + next = node->next; + g_free (node->path); + g_free (node); + node = next; + } + + *namespace = NULL; +} + +static void +camel_imap4_engine_finalize (CamelObject *object) +{ + CamelIMAP4Engine *engine = (CamelIMAP4Engine *) object; + EDListNode *node; + + if (engine->session) + camel_object_unref (engine->session); + + if (engine->istream) + camel_object_unref (engine->istream); + + if (engine->ostream) + camel_object_unref (engine->ostream); + + g_hash_table_foreach (engine->authtypes, (GHFunc) g_free, NULL); + g_hash_table_destroy (engine->authtypes); + + imap4_namespace_clear (&engine->namespaces.personal); + imap4_namespace_clear (&engine->namespaces.other); + imap4_namespace_clear (&engine->namespaces.shared); + + if (engine->folder) + camel_object_unref (engine->folder); + + while ((node = e_dlist_remhead (&engine->queue))) { + node->next = NULL; + node->prev = NULL; + + camel_imap4_command_unref ((CamelIMAP4Command *) node); + } +} + + +/** + * camel_imap4_engine_new: + * @session: session + * @url: service url + * + * Returns a new imap4 engine + **/ +CamelIMAP4Engine * +camel_imap4_engine_new (CamelSession *session, CamelURL *url) +{ + CamelIMAP4Engine *engine; + + g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); + + engine = (CamelIMAP4Engine *) camel_object_new (CAMEL_TYPE_IMAP4_ENGINE); + camel_object_ref (session); + engine->session = session; + engine->url = url; + + return engine; +} + + +/** + * camel_imap4_engine_take_stream: + * @engine: imap4 engine + * @stream: tcp stream + * @ex: exception + * + * Gives ownership of @stream to @engine and reads the greeting from + * the stream. + * + * Returns 0 on success or -1 on fail. + * + * Note: on error, @stream will be unref'd. + **/ +int +camel_imap4_engine_take_stream (CamelIMAP4Engine *engine, CamelStream *stream, CamelException *ex) +{ + camel_imap4_token_t token; + int code; + + g_return_val_if_fail (CAMEL_IS_IMAP4_ENGINE (engine), -1); + g_return_val_if_fail (CAMEL_IS_STREAM (stream), -1); + + if (engine->istream) + camel_object_unref (engine->istream); + + if (engine->ostream) + camel_object_unref (engine->ostream); + + engine->istream = (CamelIMAP4Stream *) camel_imap4_stream_new (stream); + engine->ostream = camel_stream_buffer_new (stream, CAMEL_STREAM_BUFFER_WRITE); + engine->state = CAMEL_IMAP4_ENGINE_CONNECTED; + camel_object_unref (stream); + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + if (token.token != '*') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if ((code = camel_imap4_engine_handle_untagged_1 (engine, &token, ex)) == -1) { + goto exception; + } else if (code != CAMEL_IMAP4_UNTAGGED_OK && code != CAMEL_IMAP4_UNTAGGED_PREAUTH) { + /* FIXME: set an error? */ + goto exception; + } + + return 0; + + exception: + + engine->state = CAMEL_IMAP4_ENGINE_DISCONNECTED; + + camel_object_unref (engine->istream); + engine->istream = NULL; + camel_object_unref (engine->ostream); + engine->ostream = NULL; + + return -1; +} + + +/** + * camel_imap4_engine_capability: + * @engine: IMAP4 engine + * @ex: exception + * + * Forces the IMAP4 engine to query the IMAP4 server for a list of capabilities. + * + * Returns 0 on success or -1 on fail. + **/ +int +camel_imap4_engine_capability (CamelIMAP4Engine *engine, CamelException *ex) +{ + CamelIMAP4Command *ic; + int id, retval = 0; + + ic = camel_imap4_engine_queue (engine, NULL, "CAPABILITY\r\n"); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + retval = -1; + } + + camel_imap4_command_unref (ic); + + return retval; +} + + +/** + * camel_imap4_engine_namespace: + * @engine: IMAP4 engine + * @ex: exception + * + * Forces the IMAP4 engine to query the IMAP4 server for a list of namespaces. + * + * Returns 0 on success or -1 on fail. + **/ +int +camel_imap4_engine_namespace (CamelIMAP4Engine *engine, CamelException *ex) +{ + camel_imap4_list_t *list; + GPtrArray *array = NULL; + CamelIMAP4Command *ic; + int id, i; + + if (engine->capa & CAMEL_IMAP4_CAPABILITY_NAMESPACE) { + ic = camel_imap4_engine_queue (engine, NULL, "NAMESPACE\r\n"); + } else { + ic = camel_imap4_engine_queue (engine, NULL, "LIST \"\" \"\"\r\n"); + camel_imap4_command_register_untagged (ic, "LIST", camel_imap4_untagged_list); + ic->user_data = array = g_ptr_array_new (); + } + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return -1; + } + + if (array != NULL) { + if (ic->result == CAMEL_IMAP4_RESULT_OK) { + CamelIMAP4Namespace *namespace; + + g_assert (array->len == 1); + list = array->pdata[0]; + + namespace = g_new (CamelIMAP4Namespace, 1); + namespace->next = NULL; + namespace->path = g_strdup (""); + namespace->sep = list->delim; + + engine->namespaces.personal = namespace; + } else { + /* should never *ever* happen */ + } + + for (i = 0; i < array->len; i++) { + list = array->pdata[i]; + g_free (list->name); + g_free (list); + } + + g_ptr_array_free (array, TRUE); + } + + camel_imap4_command_unref (ic); + + return 0; +} + + +int +camel_imap4_engine_select_folder (CamelIMAP4Engine *engine, CamelFolder *folder, CamelException *ex) +{ + CamelIMAP4RespCode *resp; + CamelIMAP4Command *ic; + int id, retval = 0; + int i; + + g_return_val_if_fail (CAMEL_IS_IMAP4_ENGINE (engine), -1); + g_return_val_if_fail (CAMEL_IS_IMAP4_FOLDER (folder), -1); + + /* POSSIBLE FIXME: if the folder to be selected will already + * be selected by the time the queue is emptied, simply + * no-op? */ + + ic = camel_imap4_engine_queue (engine, folder, "SELECT %F\r\n", folder); + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return -1; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_OK: + /*folder->mode = 0;*/ + for (i = 0; i < ic->resp_codes->len; i++) { + resp = ic->resp_codes->pdata[i]; + switch (resp->code) { + case CAMEL_IMAP4_RESP_CODE_PERM_FLAGS: + folder->permanent_flags = resp->v.flags; + break; + case CAMEL_IMAP4_RESP_CODE_READONLY: + /*folder->mode = CAMEL_FOLDER_MODE_READ_ONLY;*/ + break; + case CAMEL_IMAP4_RESP_CODE_READWRITE: + /*folder->mode = CAMEL_FOLDER_MODE_READ_WRITE;*/ + break; + case CAMEL_IMAP4_RESP_CODE_UIDNEXT: + camel_imap4_summary_set_uidnext (folder->summary, resp->v.uidnext); + break; + case CAMEL_IMAP4_RESP_CODE_UIDVALIDITY: + camel_imap4_summary_set_uidvalidity (folder->summary, resp->v.uidvalidity); + break; + case CAMEL_IMAP4_RESP_CODE_UNSEEN: + camel_imap4_summary_set_unseen (folder->summary, resp->v.unseen); + break; + default: + break; + } + } + + /*if (folder->mode == 0) { + folder->mode = CAMEL_FOLDER_MODE_READ_ONLY; + g_warning ("Expected to find [READ-ONLY] or [READ-WRITE] in SELECT response"); + }*/ + + break; + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot select folder `%s': Invalid mailbox name"), + folder->full_name); + retval = -1; + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot select folder `%s': Bad command"), + folder->full_name); + retval = -1; + break; + default: + g_assert_not_reached (); + retval = -1; + } + + camel_imap4_command_unref (ic); + + return retval; +} + + +static struct { + const char *name; + guint32 flag; +} imap4_capabilities[] = { + { "IMAP44", CAMEL_IMAP4_CAPABILITY_IMAP44 }, + { "IMAP44REV1", CAMEL_IMAP4_CAPABILITY_IMAP44REV1 }, + { "STATUS", CAMEL_IMAP4_CAPABILITY_STATUS }, + { "NAMESPACE", CAMEL_IMAP4_CAPABILITY_NAMESPACE }, + { "UIDPLUS", CAMEL_IMAP4_CAPABILITY_UIDPLUS }, + { "LITERAL+", CAMEL_IMAP4_CAPABILITY_LITERALPLUS }, + { "LOGINDISABLED", CAMEL_IMAP4_CAPABILITY_LOGINDISABLED }, + { "STARTTLS", CAMEL_IMAP4_CAPABILITY_STARTTLS }, + { NULL, 0 } +}; + +static gboolean +auth_free (gpointer key, gpointer value, gpointer user_data) +{ + g_free (key); + return TRUE; +} + +static int +engine_parse_capability (CamelIMAP4Engine *engine, int sentinel, CamelException *ex) +{ + camel_imap4_token_t token; + int i; + + engine->capa = CAMEL_IMAP4_CAPABILITY_utf8_search; + engine->level = 0; + + g_hash_table_foreach_remove (engine->authtypes, (GHRFunc) auth_free, NULL); + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + while (token.token == CAMEL_IMAP4_TOKEN_ATOM) { + if (!strncasecmp ("AUTH=", token.v.atom, 5)) { + CamelServiceAuthType *auth; + + if ((auth = camel_sasl_authtype (token.v.atom + 5)) != NULL) + g_hash_table_insert (engine->authtypes, g_strdup (token.v.atom + 5), auth); + } else { + for (i = 0; imap4_capabilities[i].name; i++) { + if (!strcasecmp (imap4_capabilities[i].name, token.v.atom)) + engine->capa |= imap4_capabilities[i].flag; + } + } + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + } + + if (token.token != sentinel) { + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + /* unget our sentinel token */ + camel_imap4_stream_unget_token (engine->istream, &token); + + /* figure out which version of IMAP4 we are dealing with */ + if (engine->capa & CAMEL_IMAP4_CAPABILITY_IMAP44REV1) { + engine->level = CAMEL_IMAP4_LEVEL_IMAP44REV1; + engine->capa |= CAMEL_IMAP4_CAPABILITY_STATUS; + } else if (engine->capa & CAMEL_IMAP4_CAPABILITY_IMAP44) { + engine->level = CAMEL_IMAP4_LEVEL_IMAP44; + } else { + engine->level = CAMEL_IMAP4_LEVEL_UNKNOWN; + } + + return 0; +} + +static int +engine_parse_flags_list (CamelIMAP4Engine *engine, CamelIMAP4RespCode *resp, int perm, CamelException *ex) +{ + guint32 flags = 0; + + if (camel_imap4_parse_flags_list (engine, &flags, ex) == -1) + return-1; + + if (resp != NULL) + resp->v.flags = flags; + + if (engine->current && engine->current->folder) { + if (perm) + ((CamelFolder *) engine->current->folder)->permanent_flags = flags; + /*else + ((CamelFolder *) engine->current->folder)->folder_flags = flags;*/ + } else if (engine->folder) { + if (perm) + ((CamelFolder *) engine->folder)->permanent_flags = flags; + /*else + ((CamelFolder *) engine->folder)->folder_flags = flags;*/ + } else { + fprintf (stderr, "We seem to be in a bit of a pickle. we've just parsed an untagged %s\n" + "response for a folder, yet we do not currently have a folder selected?\n", + perm ? "PERMANENTFLAGS" : "FLAGS"); + } + + return 0; +} + +static int +engine_parse_flags (CamelIMAP4Engine *engine, CamelException *ex) +{ + camel_imap4_token_t token; + + if (engine_parse_flags_list (engine, NULL, FALSE, ex) == -1) + return -1; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != '\n') { + d(fprintf (stderr, "Expected to find a '\\n' token after the FLAGS response\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + return 0; +} + + +enum { + IMAP4_STATUS_MESSAGES, + IMAP4_STATUS_RECENT, + IMAP4_STATUS_UIDNEXT, + IMAP4_STATUS_UIDVALIDITY, + IMAP4_STATUS_UNSEEN, + IMAP4_STATUS_UNKNOWN +}; + +static struct { + const char *name; + int type; +} imap4_status[] = { + { "MESSAGES", IMAP4_STATUS_MESSAGES }, + { "RECENT", IMAP4_STATUS_RECENT }, + { "UIDNEXT", IMAP4_STATUS_UIDNEXT }, + { "UIDVALIDITY", IMAP4_STATUS_UIDVALIDITY }, + { "UNSEEN", IMAP4_STATUS_UNSEEN }, + { NULL, IMAP4_STATUS_UNKNOWN }, +}; + +static int +engine_parse_status (CamelIMAP4Engine *engine, CamelException *ex) +{ + camel_imap4_token_t token; + char *mailbox; + size_t len; + int type; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + switch (token.token) { + case CAMEL_IMAP4_TOKEN_ATOM: + mailbox = g_strdup (token.v.atom); + break; + case CAMEL_IMAP4_TOKEN_QSTRING: + mailbox = g_strdup (token.v.qstring); + break; + case CAMEL_IMAP4_TOKEN_LITERAL: + if (camel_imap4_engine_literal (engine, (unsigned char **) &mailbox, &len, ex) == -1) + return -1; + break; + default: + fprintf (stderr, "Unexpected token in IMAP4 untagged STATUS response: %s%c\n", + token.token == CAMEL_IMAP4_TOKEN_NIL ? "NIL" : "", + (unsigned char) (token.token & 0xff)); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) { + g_free (mailbox); + return -1; + } + + if (token.token != '(') { + d(fprintf (stderr, "Expected to find a '(' token after the mailbox token in the STATUS response\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + g_free (mailbox); + return -1; + } + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) { + g_free (mailbox); + return -1; + } + + while (token.token == CAMEL_IMAP4_TOKEN_ATOM) { + const unsigned char *inptr; + unsigned int v = 0; + + /* parse the status messages list */ + for (type = 0; imap4_status[type].name; type++) { + if (!strcasecmp (imap4_status[type].name, token.v.atom)) + break; + } + + if (type == IMAP4_STATUS_UNKNOWN) + fprintf (stderr, "unrecognized token in STATUS list: %s\n", token.v.atom); + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) { + g_free (mailbox); + return -1; + } + + if (token.token != CAMEL_IMAP4_TOKEN_ATOM) + break; + + if (type == IMAP4_STATUS_UIDNEXT || type == IMAP4_STATUS_UIDVALIDITY) { + /* these tokens should be numeric, but we + * treat them as strings internally so we are + * special-casing them here */ + + /* FIXME: save the UIDNEXT/UIDVALIDITY value */ + } else { + inptr = (const unsigned char *) token.v.atom; + while (*inptr && isdigit ((int) *inptr) && v < (UINT_MAX / 10)) + v = (v * 10) + (*inptr++ - '0'); + + if (*inptr != '\0') { + if (type == IMAP4_STATUS_UNKNOWN) { + /* we'll let it slide... unget this token and continue */ + camel_imap4_stream_unget_token (engine->istream, &token); + goto loop; + } + + d(fprintf (stderr, "Encountered non-numeric token after %s in untagged STATUS response: %s\n", + imap4_status[type].name, token.v.atom)); + goto loop; + } + + switch (type) { + case IMAP4_STATUS_MESSAGES: + /* FIXME: save value */ + break; + case IMAP4_STATUS_RECENT: + /* FIXME: save value */ + break; + case IMAP4_STATUS_UNSEEN: + /* FIXME: save value */ + break; + default: + g_assert_not_reached (); + } + } + + loop: + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) { + g_free (mailbox); + return -1; + } + } + + /* don't need this anymore... */ + g_free (mailbox); + + if (token.token != ')') { + d(fprintf (stderr, "Expected to find a ')' token terminating the untagged STATUS response\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != '\n') { + d(fprintf (stderr, "Expected to find a '\\n' token after the STATUS response\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + return 0; +} + +static int +engine_parse_namespace (CamelIMAP4Engine *engine, CamelException *ex) +{ + CamelIMAP4Namespace *namespaces[3], *node, *tail; + camel_imap4_token_t token; + int i, n = 0; + + imap4_namespace_clear (&engine->namespaces.personal); + imap4_namespace_clear (&engine->namespaces.other); + imap4_namespace_clear (&engine->namespaces.shared); + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + do { + namespaces[n] = NULL; + tail = (CamelIMAP4Namespace *) &namespaces[n]; + + if (token.token == '(') { + /* decode the list of namespace pairs */ + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + while (token.token == '(') { + /* decode a namespace pair */ + + /* get the path name token */ + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + if (token.token != CAMEL_IMAP4_TOKEN_QSTRING) { + d(fprintf (stderr, "Expected to find a qstring token as first element in NAMESPACE pair\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + node = g_new (CamelIMAP4Namespace, 1); + node->next = NULL; + node->path = g_strdup (token.v.qstring); + + /* get the path delimiter token */ + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) { + g_free (node->path); + g_free (node); + + goto exception; + } + + if (token.token != CAMEL_IMAP4_TOKEN_QSTRING || strlen (token.v.qstring) > 1) { + d(fprintf (stderr, "Expected to find a qstring token as second element in NAMESPACE pair\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + g_free (node->path); + g_free (node); + + goto exception; + } + + node->sep = *token.v.qstring; + tail->next = node; + tail = node; + + /* canonicalise the namespace path */ + if (node->path[strlen (node->path) - 1] == node->sep) + node->path[strlen (node->path) - 1] = '\0'; + + /* canonicalise if this is an INBOX namespace */ + if (!g_ascii_strncasecmp (node->path, "INBOX", 5) && + (node->path[6] == '\0' || node->path[6] == node->sep)) + memcpy (node->path, "INBOX", 5); + + /* get the closing ')' for this namespace pair */ + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + if (token.token != ')') { + d(fprintf (stderr, "Expected to find a ')' token to close the current namespace pair\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + + goto exception; + } + + /* get the next token (should be either '(' or ')') */ + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + } + + if (token.token != ')') { + d(fprintf (stderr, "Expected to find a ')' to close the current namespace list\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + } else if (token.token == CAMEL_IMAP4_TOKEN_NIL) { + /* namespace list is NIL */ + namespaces[n] = NULL; + } else { + d(fprintf (stderr, "Expected to find either NIL or '(' token in untagged NAMESPACE response\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + /* get the next token (should be either '(', NIL, or '\n') */ + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + n++; + } while (n < 3); + + engine->namespaces.personal = namespaces[0]; + engine->namespaces.other = namespaces[1]; + engine->namespaces.shared = namespaces[2]; + + return 0; + + exception: + + for (i = 0; i <= n; i++) + imap4_namespace_clear (&namespaces[i]); + + return -1; +} + + +/** + * + * resp-text-code = "ALERT" / + * "BADCHARSET" [SP "(" astring *(SP astring) ")" ] / + * capability-data / "PARSE" / + * "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" / + * "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + * "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number / + * "UNSEEN" SP nz-number / + * atom [SP 1*] + **/ + +static struct { + const char *name; + camel_imap4_resp_code_t code; + int save; +} imap4_resp_codes[] = { + { "ALERT", CAMEL_IMAP4_RESP_CODE_ALERT, 0 }, + { "BADCHARSET", CAMEL_IMAP4_RESP_CODE_BADCHARSET, 0 }, + { "CAPABILITY", CAMEL_IMAP4_RESP_CODE_CAPABILITY, 0 }, + { "PARSE", CAMEL_IMAP4_RESP_CODE_PARSE, 1 }, + { "PERMANENTFLAGS", CAMEL_IMAP4_RESP_CODE_PERM_FLAGS, 1 }, + { "READ-ONLY", CAMEL_IMAP4_RESP_CODE_READONLY, 1 }, + { "READ-WRITE", CAMEL_IMAP4_RESP_CODE_READWRITE, 1 }, + { "TRYCREATE", CAMEL_IMAP4_RESP_CODE_TRYCREATE, 1 }, + { "UIDNEXT", CAMEL_IMAP4_RESP_CODE_UIDNEXT, 1 }, + { "UIDVALIDITY", CAMEL_IMAP4_RESP_CODE_UIDVALIDITY, 1 }, + { "UNSEEN", CAMEL_IMAP4_RESP_CODE_UNSEEN, 1 }, + { "NEWNAME", CAMEL_IMAP4_RESP_CODE_NEWNAME, 1 }, + { "APPENDUID", CAMEL_IMAP4_RESP_CODE_APPENDUID, 1 }, + { "COPYUID", CAMEL_IMAP4_RESP_CODE_COPYUID, 1 }, + { NULL, CAMEL_IMAP4_RESP_CODE_UNKNOWN, 0 } +}; + + +int +camel_imap4_engine_parse_resp_code (CamelIMAP4Engine *engine, CamelException *ex) +{ + CamelIMAP4RespCode *resp = NULL; + camel_imap4_resp_code_t code; + camel_imap4_token_t token; + unsigned char *linebuf; + size_t len; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != '[') { + d(fprintf (stderr, "Expected a '[' token (followed by a RESP-CODE)\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_ATOM) { + d(fprintf (stderr, "Expected an atom token containing a RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + for (code = 0; imap4_resp_codes[code].name; code++) { + if (!strcmp (imap4_resp_codes[code].name, token.v.atom)) { + if (engine->current && imap4_resp_codes[code].save) { + resp = g_new0 (CamelIMAP4RespCode, 1); + resp->code = code; + } + break; + } + } + + switch (code) { + case CAMEL_IMAP4_RESP_CODE_BADCHARSET: + /* apparently we don't support UTF-8 afterall */ + engine->capa &= ~CAMEL_IMAP4_CAPABILITY_utf8_search; + break; + case CAMEL_IMAP4_RESP_CODE_CAPABILITY: + /* capability list follows */ + if (engine_parse_capability (engine, ']', ex) == -1) + goto exception; + break; + case CAMEL_IMAP4_RESP_CODE_PERM_FLAGS: + /* flag list follows */ + if (engine_parse_flags_list (engine, resp, TRUE, ex) == -1) + goto exception; + break; + case CAMEL_IMAP4_RESP_CODE_READONLY: + break; + case CAMEL_IMAP4_RESP_CODE_READWRITE: + break; + case CAMEL_IMAP4_RESP_CODE_TRYCREATE: + break; + case CAMEL_IMAP4_RESP_CODE_UIDNEXT: + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + if (token.token != CAMEL_IMAP4_TOKEN_NUMBER) { + d(fprintf (stderr, "Expected an nz_number token as an argument to the UIDNEXT RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.uidnext = token.v.number; + + break; + case CAMEL_IMAP4_RESP_CODE_UIDVALIDITY: + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + if (token.token != CAMEL_IMAP4_TOKEN_NUMBER) { + d(fprintf (stderr, "Expected an nz_number token as an argument to the UIDVALIDITY RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.uidvalidity = token.v.number; + + break; + case CAMEL_IMAP4_RESP_CODE_UNSEEN: + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_NUMBER) { + d(fprintf (stderr, "Expected an nz_number token as an argument to the UNSEEN RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.unseen = token.v.number; + + break; + case CAMEL_IMAP4_RESP_CODE_NEWNAME: + /* this RESP-CODE may actually be removed - see here: + * http://www.washington.edu/imap4/listarch/2001/msg00058.html */ + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_ATOM && token.token != CAMEL_IMAP4_TOKEN_QSTRING) { + d(fprintf (stderr, "Expected an atom or qstring token as the first argument to the NEWNAME RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.newname[0] = g_strdup (token.v.atom); + + if (token.token != CAMEL_IMAP4_TOKEN_ATOM && token.token != CAMEL_IMAP4_TOKEN_QSTRING) { + d(fprintf (stderr, "Expected an atom or qstring token as the second argument to the NEWNAME RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.newname[1] = g_strdup (token.v.atom); + + break; + case CAMEL_IMAP4_RESP_CODE_APPENDUID: + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_NUMBER) { + d(fprintf (stderr, "Expected an nz_number token as the first argument to the APPENDUID RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.appenduid.uidvalidity = token.v.number; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_NUMBER) { + d(fprintf (stderr, "Expected an nz_number token as the second argument to the APPENDUID RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.appenduid.uid = token.v.number; + + break; + case CAMEL_IMAP4_RESP_CODE_COPYUID: + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_NUMBER) { + d(fprintf (stderr, "Expected an nz_number token as the first argument to the COPYUID RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.copyuid.uidvalidity = token.v.number; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_ATOM) { + d(fprintf (stderr, "Expected an atom token as the second argument to the COPYUID RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.copyuid.srcset = g_strdup (token.v.atom); + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != CAMEL_IMAP4_TOKEN_ATOM) { + d(fprintf (stderr, "Expected an atom token as the third argument to the APPENDUID RESP-CODE\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + goto exception; + } + + if (resp != NULL) + resp->v.copyuid.destset = g_strdup (token.v.atom); + + break; + default: + d(fprintf (stderr, "Unknown RESP-CODE encountered: %s\n", token.v.atom)); + + /* extensions are of the form: "[" atom [SPACE 1*] "]" */ + + /* eat up the TEXT_CHARs */ + while (token.token != ']' && token.token != '\n') { + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + } + + break; + } + + while (token.token != ']' && token.token != '\n') { + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + } + + if (token.token != ']') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + d(fprintf (stderr, "Expected to find a ']' token after the RESP-CODE\n")); + return -1; + } + + if (code == CAMEL_IMAP4_RESP_CODE_ALERT) { + if (camel_imap4_engine_line (engine, &linebuf, &len, ex) == -1) + goto exception; + + camel_session_alert_user (engine->session, CAMEL_SESSION_ALERT_INFO, linebuf, FALSE); + g_free (linebuf); + } else if (resp != NULL && code == CAMEL_IMAP4_RESP_CODE_PARSE) { + if (camel_imap4_engine_line (engine, &linebuf, &len, ex) == -1) + goto exception; + + resp->v.parse = linebuf; + } else { + /* eat up the rest of the response */ + if (camel_imap4_engine_line (engine, NULL, NULL, ex) == -1) + goto exception; + } + + if (resp != NULL) + g_ptr_array_add (engine->current->resp_codes, resp); + + return 0; + + exception: + + if (resp != NULL) + camel_imap4_resp_code_free (resp); + + return -1; +} + + + +/* returns -1 on error, or one of CAMEL_IMAP4_UNTAGGED_[OK,NO,BAD,PREAUTH,HANDLED] on success */ +int +camel_imap4_engine_handle_untagged_1 (CamelIMAP4Engine *engine, camel_imap4_token_t *token, CamelException *ex) +{ + int code = CAMEL_IMAP4_UNTAGGED_HANDLED; + CamelIMAP4Command *ic = engine->current; + CamelIMAP4UntaggedCallback untagged; + CamelFolder *folder; + unsigned int v; + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token == CAMEL_IMAP4_TOKEN_ATOM) { + if (!strcmp ("BYE", token->v.atom)) { + /* we don't care if we fail here, either way we've been disconnected */ + camel_imap4_engine_parse_resp_code (engine, NULL); + engine->state = CAMEL_IMAP4_ENGINE_DISCONNECTED; + + /* FIXME: emit a "disconnected" signal for our Store? + * The Store could then initiate a reconnect if + * desirable. */ + + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("IMAP4 server %s unexpectedly disconnected: %s"), + engine->url->host, _("Got BYE response")); + + return -1; + } else if (!strcmp ("CAPABILITY", token->v.atom)) { + /* capability tokens follow */ + if (engine_parse_capability (engine, '\n', ex) == -1) + return -1; + + /* find the eoln token */ + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token != '\n') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + return -1; + } + } else if (!strcmp ("FLAGS", token->v.atom)) { + /* flags list follows */ + if (engine_parse_flags (engine, ex) == -1) + return -1; + } else if (!strcmp ("NAMESPACE", token->v.atom)) { + if (engine_parse_namespace (engine, ex) == -1) + return -1; + } else if (!strcmp ("NO", token->v.atom) || !strcmp ("BAD", token->v.atom)) { + code = !strcmp ("NO", token->v.atom) ? CAMEL_IMAP4_UNTAGGED_NO : CAMEL_IMAP4_UNTAGGED_BAD; + + /* our command has been rejected */ + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token == '[') { + /* we have a resp code */ + camel_imap4_stream_unget_token (engine->istream, token); + if (camel_imap4_engine_parse_resp_code (engine, ex) == -1) + return -1; + } else if (token->token != '\n') { + /* we just have resp text */ + if (camel_imap4_engine_line (engine, NULL, NULL, ex) == -1) + return -1; + } + } else if (!strcmp ("OK", token->v.atom)) { + code = CAMEL_IMAP4_UNTAGGED_OK; + + if (engine->state == CAMEL_IMAP4_ENGINE_CONNECTED) { + /* initial server greeting */ + engine->state = CAMEL_IMAP4_ENGINE_PREAUTH; + } + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token == '[') { + /* we have a resp code */ + camel_imap4_stream_unget_token (engine->istream, token); + if (camel_imap4_engine_parse_resp_code (engine, ex) == -1) + return -1; + } else { + /* we just have resp text */ + if (camel_imap4_engine_line (engine, NULL, NULL, ex) == -1) + return -1; + } + } else if (!strcmp ("PREAUTH", token->v.atom)) { + code = CAMEL_IMAP4_UNTAGGED_PREAUTH; + + if (engine->state == CAMEL_IMAP4_ENGINE_CONNECTED) + engine->state = CAMEL_IMAP4_ENGINE_AUTHENTICATED; + + if (camel_imap4_engine_parse_resp_code (engine, ex) == -1) + return -1; + } else if (!strcmp ("STATUS", token->v.atom)) { + /* FIXME: This should probably be removed... leave it + * up to the caller that sent the STATUS command to + * register an untagged response handler for this */ + + /* next token must be the mailbox name followed by a paren list */ + if (engine_parse_status (engine, ex) == -1) + return -1; + } else if (ic && (untagged = g_hash_table_lookup (ic->untagged, token->v.atom))) { + /* registered untagged handler for imap4 command */ + if (untagged (engine, ic, 0, token, ex) == -1) + return -1; + } else { + d(fprintf (stderr, "Unhandled atom token in untagged response: %s", token->v.atom)); + + if (camel_imap4_engine_eat_line (engine, ex) == -1) + return -1; + } + } else if (token->token == CAMEL_IMAP4_TOKEN_NUMBER) { + /* we probably have something like "* 1 EXISTS" */ + v = token->v.number; + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token != CAMEL_IMAP4_TOKEN_ATOM) { + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + + return -1; + } + + /* which folder is this EXISTS/EXPUNGE/RECENT acting on? */ + if (engine->current && engine->current->folder) + folder = (CamelFolder *) engine->current->folder; + else if (engine->folder) + folder = (CamelFolder *) engine->folder; + else + folder = NULL; + + /* NOTE: these can be over-ridden by a registered untagged response handler */ + if (!strcmp ("EXISTS", token->v.atom)) { + camel_imap4_summary_set_exists (folder->summary, v); + } else if (!strcmp ("EXPUNGE", token->v.atom)) { + camel_imap4_summary_expunge (folder->summary, (int) v); + } else if (!strcmp ("RECENT", token->v.atom)) { + camel_imap4_summary_set_recent (folder->summary, v); + } else if (ic && (untagged = g_hash_table_lookup (ic->untagged, token->v.atom))) { + /* registered untagged handler for imap4 command */ + if (untagged (engine, ic, v, token, ex) == -1) + return -1; + } else { + d(fprintf (stderr, "Unrecognized untagged response: * %u %s\n", v, token->v.atom)); + } + + /* find the eoln token */ + if (camel_imap4_engine_eat_line (engine, ex) == -1) + return -1; + } else { + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + + return -1; + } + + return code; +} + + +void +camel_imap4_engine_handle_untagged (CamelIMAP4Engine *engine, CamelException *ex) +{ + camel_imap4_token_t token; + + g_return_if_fail (CAMEL_IS_IMAP4_ENGINE (engine)); + + do { + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + goto exception; + + if (token.token != '*') + break; + + if (camel_imap4_engine_handle_untagged_1 (engine, &token, ex) == -1) + goto exception; + } while (1); + + camel_imap4_stream_unget_token (engine->istream, &token); + + return; + + exception: + + engine->state = CAMEL_IMAP4_ENGINE_DISCONNECTED; +} + + +static int +imap4_process_command (CamelIMAP4Engine *engine, CamelIMAP4Command *ic) +{ + int retval; + + while ((retval = camel_imap4_command_step (ic)) == 0) + ; + + if (retval == -1) { + engine->state = CAMEL_IMAP4_ENGINE_DISCONNECTED; + return -1; + } + + return 0; +} + + +static void +engine_prequeue_folder_select (CamelIMAP4Engine *engine) +{ + CamelIMAP4Command *ic; + const char *cmd; + + ic = (CamelIMAP4Command *) engine->queue.head; + cmd = (const char *) ic->parts->buffer; + + if (!ic->folder || ic->folder == engine->folder || + !strncmp (cmd, "SELECT ", 7) || !strncmp (cmd, "EXAMINE ", 8)) { + /* no need to pre-queue a SELECT */ + return; + } + + /* we need to pre-queue a SELECT */ + ic = camel_imap4_command_new (engine, ic->folder, "SELECT %F\r\n", ic->folder); + camel_imap4_engine_prequeue (engine, ic); + ic->user_data = engine; + + camel_imap4_command_unref (ic); +} + + +static int +engine_state_change (CamelIMAP4Engine *engine, CamelIMAP4Command *ic) +{ + const char *cmd; + int retval = 0; + + cmd = ic->parts->buffer; + if (!strncmp (cmd, "SELECT ", 7) || !strncmp (cmd, "EXAMINE ", 8)) { + if (ic->result == CAMEL_IMAP4_RESULT_OK) { + /* Update the selected folder */ + camel_object_ref (ic->folder); + if (engine->folder) + camel_object_unref (engine->folder); + engine->folder = ic->folder; + + engine->state = CAMEL_IMAP4_ENGINE_SELECTED; + } else if (ic->user_data == engine) { + /* the engine pre-queued this SELECT command */ + retval = -1; + } + } else if (!strncmp (cmd, "CLOSE", 5)) { + if (ic->result == CAMEL_IMAP4_RESULT_OK) + engine->state = CAMEL_IMAP4_ENGINE_AUTHENTICATED; + } else if (!strncmp (cmd, "LOGOUT", 6)) { + engine->state = CAMEL_IMAP4_ENGINE_DISCONNECTED; + } + + return retval; +} + +/** + * camel_imap4_engine_iterate: + * @engine: IMAP4 engine + * + * Processes the first command in the queue. + * + * Returns the id of the processed command, 0 if there were no + * commands to process, or -1 on error. + * + * Note: more details on the error will be held on the + * CamelIMAP4Command that failed. + **/ +int +camel_imap4_engine_iterate (CamelIMAP4Engine *engine) +{ + CamelIMAP4Command *ic, *nic; + GPtrArray *resp_codes; + int retval = -1; + + if (e_dlist_empty (&engine->queue)) + return 0; + + /* check to see if we need to pre-queue a SELECT, if so do it */ + engine_prequeue_folder_select (engine); + + engine->current = ic = (CamelIMAP4Command *) e_dlist_remhead (&engine->queue); + ic->status = CAMEL_IMAP4_COMMAND_ACTIVE; + + if (imap4_process_command (engine, ic) != -1) { + if (engine_state_change (engine, ic) == -1) { + /* This can ONLY happen if @ic was the pre-queued SELECT command + * and it got a NO or BAD response. + * + * We have to pop the next imap4 command or we'll get into an + * infinite loop. In order to provide @nic's owner with as much + * information as possible, we move all @ic status information + * over to @nic and pretend we just processed @nic. + **/ + + nic = (CamelIMAP4Command *) e_dlist_remhead (&engine->queue); + + nic->status = ic->status; + nic->result = ic->result; + resp_codes = nic->resp_codes; + nic->resp_codes = ic->resp_codes; + ic->resp_codes = resp_codes; + + camel_exception_xfer (&nic->ex, &ic->ex); + + camel_imap4_command_unref (ic); + ic = nic; + } + + retval = ic->id; + } + + camel_imap4_command_unref (ic); + + return retval; +} + + +/** + * camel_imap4_engine_queue: + * @engine: IMAP4 engine + * @folder: IMAP4 folder that the command will affect (or %NULL if it doesn't matter) + * @format: command format + * @Varargs: arguments + * + * Basically the same as #camel_imap4_command_new() except that this + * function also places the command in the engine queue. + * + * Returns the CamelIMAP4Command. + **/ +CamelIMAP4Command * +camel_imap4_engine_queue (CamelIMAP4Engine *engine, CamelFolder *folder, const char *format, ...) +{ + CamelIMAP4Command *ic; + va_list args; + + va_start (args, format); + ic = camel_imap4_command_newv (engine, (CamelIMAP4Folder *) folder, format, args); + va_end (args); + + ic->id = engine->nextid++; + e_dlist_addtail (&engine->queue, (EDListNode *) ic); + camel_imap4_command_ref (ic); + + return ic; +} + + +/** + * camel_imap4_engine_prequeue: + * @engine: IMAP4 engine + * @ic: IMAP4 command to pre-queue + * + * Places @ic at the head of the queue of pending IMAP4 commands. + **/ +void +camel_imap4_engine_prequeue (CamelIMAP4Engine *engine, CamelIMAP4Command *ic) +{ + g_return_if_fail (CAMEL_IS_IMAP4_ENGINE (engine)); + g_return_if_fail (ic != NULL); + + camel_imap4_command_ref (ic); + + if (e_dlist_empty (&engine->queue)) { + e_dlist_addtail (&engine->queue, (EDListNode *) ic); + ic->id = engine->nextid++; + } else { + CamelIMAP4Command *nic; + EDListNode *node; + + node = (EDListNode *) ic; + e_dlist_addhead (&engine->queue, node); + nic = (CamelIMAP4Command *) node->next; + ic->id = nic->id - 1; + + if (ic->id == 0) { + /* increment all command ids */ + node = engine->queue.head; + while (node->next) { + nic = (CamelIMAP4Command *) node; + node = node->next; + nic->id++; + } + } + } +} + + +void +camel_imap4_engine_dequeue (CamelIMAP4Engine *engine, CamelIMAP4Command *ic) +{ + EDListNode *node = (EDListNode *) ic; + + if (node->next == NULL && node->prev == NULL) + return; + + e_dlist_remove (node); + node->next = NULL; + node->prev = NULL; + + camel_imap4_command_unref (ic); +} + + +int +camel_imap4_engine_next_token (CamelIMAP4Engine *engine, camel_imap4_token_t *token, CamelException *ex) +{ + if (camel_imap4_stream_next_token (engine->istream, token) == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("IMAP4 server %s unexpectedly disconnected: %s"), + engine->url->host, errno ? g_strerror (errno) : _("Unknown")); + return -1; + } + + return 0; +} + + +int +camel_imap4_engine_eat_line (CamelIMAP4Engine *engine, CamelException *ex) +{ + camel_imap4_token_t token; + unsigned char *literal; + int retval; + size_t n; + + do { + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token == CAMEL_IMAP4_TOKEN_LITERAL) { + while ((retval = camel_imap4_stream_literal (engine->istream, &literal, &n)) == 1) + ; + + if (retval == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("IMAP4 server %s unexpectedly disconnected: %s"), + engine->url->host, errno ? g_strerror (errno) : _("Unknown")); + + return -1; + } + } + } while (token.token != '\n'); + + return 0; +} + + +int +camel_imap4_engine_line (CamelIMAP4Engine *engine, unsigned char **line, size_t *len, CamelException *ex) +{ + GByteArray *linebuf = NULL; + unsigned char *buf; + size_t buflen; + int retval; + + if (line != NULL) + linebuf = g_byte_array_new (); + + while ((retval = camel_imap4_stream_line (engine->istream, &buf, &buflen)) > 0) { + if (linebuf != NULL) + g_byte_array_append (linebuf, buf, buflen); + } + + if (retval == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("IMAP4 server %s unexpectedly disconnected: %s"), + engine->url->host, errno ? g_strerror (errno) : _("Unknown")); + + if (linebuf != NULL) + g_byte_array_free (linebuf, TRUE); + + return -1; + } + + if (linebuf != NULL) { + g_byte_array_append (linebuf, buf, buflen); + + *line = linebuf->data; + *len = linebuf->len; + + g_byte_array_free (linebuf, FALSE); + } + + return 0; +} + + +int +camel_imap4_engine_literal (CamelIMAP4Engine *engine, unsigned char **literal, size_t *len, CamelException *ex) +{ + GByteArray *literalbuf = NULL; + unsigned char *buf; + size_t buflen; + int retval; + + if (literal != NULL) + literalbuf = g_byte_array_new (); + + while ((retval = camel_imap4_stream_literal (engine->istream, &buf, &buflen)) > 0) { + if (literalbuf != NULL) + g_byte_array_append (literalbuf, buf, buflen); + } + + if (retval == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("IMAP4 server %s unexpectedly disconnected: %s"), + engine->url->host, errno ? g_strerror (errno) : _("Unknown")); + + if (literalbuf != NULL) + g_byte_array_free (literalbuf, TRUE); + + return -1; + } + + if (literalbuf != NULL) { + g_byte_array_append (literalbuf, buf, buflen); + g_byte_array_append (literalbuf, "", 1); + + *literal = literalbuf->data; + *len = literalbuf->len - 1; + + g_byte_array_free (literalbuf, FALSE); + } + + return 0; +} + + +void +camel_imap4_resp_code_free (CamelIMAP4RespCode *rcode) +{ + switch (rcode->code) { + case CAMEL_IMAP4_RESP_CODE_PARSE: + g_free (rcode->v.parse); + break; + case CAMEL_IMAP4_RESP_CODE_NEWNAME: + g_free (rcode->v.newname[0]); + g_free (rcode->v.newname[1]); + break; + case CAMEL_IMAP4_RESP_CODE_COPYUID: + g_free (rcode->v.copyuid.srcset); + g_free (rcode->v.copyuid.destset); + break; + default: + break; + } + + g_free (rcode); +} diff --git a/camel/providers/imap4/camel-imap4-engine.h b/camel/providers/imap4/camel-imap4-engine.h new file mode 100644 index 0000000000..25ae7ed113 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-engine.h @@ -0,0 +1,222 @@ +/* -*- 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_IMAP4_ENGINE_H__ +#define __CAMEL_IMAP4_ENGINE_H__ + +#include + +#include + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_TYPE_IMAP4_ENGINE (camel_imap4_engine_get_type ()) +#define CAMEL_IMAP4_ENGINE(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP4_ENGINE, CamelIMAP4Engine)) +#define CAMEL_IMAP4_ENGINE_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP4_ENGINE, CamelIMAP4EngineClass)) +#define CAMEL_IS_IMAP4_ENGINE(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP4_ENGINE)) +#define CAMEL_IS_IMAP4_ENGINE_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP4_ENGINE)) +#define CAMEL_IMAP4_ENGINE_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_IMAP4_ENGINE, CamelIMAP4EngineClass)) + +typedef struct _CamelIMAP4Engine CamelIMAP4Engine; +typedef struct _CamelIMAP4EngineClass CamelIMAP4EngineClass; + +struct _camel_imap4_token_t; +struct _CamelIMAP4Command; +struct _CamelIMAP4Folder; +struct _CamelIMAP4Stream; + +typedef enum { + CAMEL_IMAP4_ENGINE_DISCONNECTED, + CAMEL_IMAP4_ENGINE_CONNECTED, + CAMEL_IMAP4_ENGINE_PREAUTH, + CAMEL_IMAP4_ENGINE_AUTHENTICATED, + CAMEL_IMAP4_ENGINE_SELECTED, +} camel_imap4_engine_t; + +typedef enum { + CAMEL_IMAP4_LEVEL_UNKNOWN, + CAMEL_IMAP4_LEVEL_IMAP44, + CAMEL_IMAP4_LEVEL_IMAP44REV1 +} camel_imap4_level_t; + +enum { + CAMEL_IMAP4_CAPABILITY_IMAP44 = (1 << 0), + CAMEL_IMAP4_CAPABILITY_IMAP44REV1 = (1 << 1), + CAMEL_IMAP4_CAPABILITY_STATUS = (1 << 2), + CAMEL_IMAP4_CAPABILITY_NAMESPACE = (1 << 3), + CAMEL_IMAP4_CAPABILITY_UIDPLUS = (1 << 4), + CAMEL_IMAP4_CAPABILITY_LITERALPLUS = (1 << 5), + CAMEL_IMAP4_CAPABILITY_LOGINDISABLED = (1 << 6), + CAMEL_IMAP4_CAPABILITY_STARTTLS = (1 << 7), + CAMEL_IMAP4_CAPABILITY_useful_lsub = (1 << 8), + CAMEL_IMAP4_CAPABILITY_utf8_search = (1 << 9), +}; + +typedef enum { + CAMEL_IMAP4_RESP_CODE_ALERT, + CAMEL_IMAP4_RESP_CODE_BADCHARSET, + CAMEL_IMAP4_RESP_CODE_CAPABILITY, + CAMEL_IMAP4_RESP_CODE_PARSE, + CAMEL_IMAP4_RESP_CODE_PERM_FLAGS, + CAMEL_IMAP4_RESP_CODE_READONLY, + CAMEL_IMAP4_RESP_CODE_READWRITE, + CAMEL_IMAP4_RESP_CODE_TRYCREATE, + CAMEL_IMAP4_RESP_CODE_UIDNEXT, + CAMEL_IMAP4_RESP_CODE_UIDVALIDITY, + CAMEL_IMAP4_RESP_CODE_UNSEEN, + CAMEL_IMAP4_RESP_CODE_NEWNAME, + CAMEL_IMAP4_RESP_CODE_APPENDUID, + CAMEL_IMAP4_RESP_CODE_COPYUID, + CAMEL_IMAP4_RESP_CODE_UNKNOWN, +} camel_imap4_resp_code_t; + +typedef struct _CamelIMAP4RespCode { + camel_imap4_resp_code_t code; + union { + guint32 flags; + char *parse; + guint32 uidnext; + guint32 uidvalidity; + guint32 unseen; + char *newname[2]; + struct { + guint32 uidvalidity; + guint32 uid; + } appenduid; + struct { + guint32 uidvalidity; + char *srcset; + char *destset; + } copyuid; + } v; +} CamelIMAP4RespCode; + +enum { + CAMEL_IMAP4_UNTAGGED_ERROR = -1, + CAMEL_IMAP4_UNTAGGED_OK, + CAMEL_IMAP4_UNTAGGED_NO, + CAMEL_IMAP4_UNTAGGED_BAD, + CAMEL_IMAP4_UNTAGGED_PREAUTH, + CAMEL_IMAP4_UNTAGGED_HANDLED, +}; + +typedef struct _CamelIMAP4Namespace { + struct _CamelIMAP4Namespace *next; + char *path; + char sep; +} CamelIMAP4Namespace; + +typedef struct _CamelIMAP4NamespaceList { + CamelIMAP4Namespace *personal; + CamelIMAP4Namespace *other; + CamelIMAP4Namespace *shared; +} CamelIMAP4NamespaceList; + +enum { + CAMEL_IMAP4_ENGINE_MAXLEN_LINE, + CAMEL_IMAP4_ENGINE_MAXLEN_TOKEN +}; + +struct _CamelIMAP4Engine { + CamelObject parent_object; + + CamelSession *session; + CamelURL *url; + + camel_imap4_engine_t state; + camel_imap4_level_t level; + guint32 capa; + + guint32 maxlen:31; + guint32 maxlentype:1; + + CamelIMAP4NamespaceList namespaces; + GHashTable *authtypes; /* supported authtypes */ + + struct _CamelIMAP4Stream *istream; + CamelStream *ostream; + + unsigned char tagprefix; /* 'A'..'Z' */ + unsigned int tag; /* next command tag */ + int nextid; + + struct _CamelIMAP4Folder *folder; /* currently selected folder */ + + EDList queue; /* queue of waiting commands */ + struct _CamelIMAP4Command *current; +}; + +struct _CamelIMAP4EngineClass { + CamelObjectClass parent_class; + + unsigned char tagprefix; +}; + + +CamelType camel_imap4_engine_get_type (void); + +CamelIMAP4Engine *camel_imap4_engine_new (CamelSession *session, CamelURL *url); + +/* returns 0 on success or -1 on error */ +int camel_imap4_engine_take_stream (CamelIMAP4Engine *engine, CamelStream *stream, CamelException *ex); + +int camel_imap4_engine_capability (CamelIMAP4Engine *engine, CamelException *ex); +int camel_imap4_engine_namespace (CamelIMAP4Engine *engine, CamelException *ex); + +int camel_imap4_engine_select_folder (CamelIMAP4Engine *engine, CamelFolder *folder, CamelException *ex); + +struct _CamelIMAP4Command *camel_imap4_engine_queue (CamelIMAP4Engine *engine, CamelFolder *folder, + const char *format, ...); +void camel_imap4_engine_prequeue (CamelIMAP4Engine *engine, struct _CamelIMAP4Command *ic); + +void camel_imap4_engine_dequeue (CamelIMAP4Engine *engine, struct _CamelIMAP4Command *ic); + +int camel_imap4_engine_iterate (CamelIMAP4Engine *engine); + + +/* untagged response utility functions */ +int camel_imap4_engine_handle_untagged_1 (CamelIMAP4Engine *engine, struct _camel_imap4_token_t *token, CamelException *ex); +void camel_imap4_engine_handle_untagged (CamelIMAP4Engine *engine, CamelException *ex); + +/* stream wrapper utility functions */ +int camel_imap4_engine_next_token (CamelIMAP4Engine *engine, struct _camel_imap4_token_t *token, CamelException *ex); +int camel_imap4_engine_line (CamelIMAP4Engine *engine, unsigned char **line, size_t *len, CamelException *ex); +int camel_imap4_engine_literal (CamelIMAP4Engine *engine, unsigned char **literal, size_t *len, CamelException *ex); +int camel_imap4_engine_eat_line (CamelIMAP4Engine *engine, CamelException *ex); + + +/* response code stuff */ +int camel_imap4_engine_parse_resp_code (CamelIMAP4Engine *engine, CamelException *ex); +void camel_imap4_resp_code_free (CamelIMAP4RespCode *rcode); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_ENGINE_H__ */ diff --git a/camel/providers/imap4/camel-imap4-folder.c b/camel/providers/imap4/camel-imap4-folder.c new file mode 100644 index 0000000000..2169f053ce --- /dev/null +++ b/camel/providers/imap4/camel-imap4-folder.c @@ -0,0 +1,860 @@ +/* -*- 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 +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "camel-imap4-utils.h" +#include "camel-imap4-store.h" +#include "camel-imap4-engine.h" +#include "camel-imap4-folder.h" +#include "camel-imap4-stream.h" +#include "camel-imap4-command.h" +#include "camel-imap4-summary.h" + +#define d(x) x + +static void camel_imap4_folder_class_init (CamelIMAP4FolderClass *klass); +static void camel_imap4_folder_init (CamelIMAP4Folder *folder, CamelIMAP4FolderClass *klass); +static void camel_imap4_folder_finalize (CamelObject *object); + +static void imap4_sync (CamelFolder *folder, gboolean expunge, CamelException *ex); +static void imap4_expunge (CamelFolder *folder, CamelException *ex); +static CamelMimeMessage *imap4_get_message (CamelFolder *folder, const char *uid, CamelException *ex); +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 CamelFolderClass *parent_class = NULL; + + +CamelType +camel_imap4_folder_get_type (void) +{ + static CamelType type = 0; + + if (!type) { + type = camel_type_register (CAMEL_TYPE_IMAP4_FOLDER, + "CamelIMAP4Folder", + sizeof (CamelIMAP4Folder), + sizeof (CamelIMAP4FolderClass), + (CamelObjectClassInitFunc) camel_imap4_folder_class_init, + NULL, + (CamelObjectInitFunc) camel_imap4_folder_init, + (CamelObjectFinalizeFunc) camel_imap4_folder_finalize); + } + + return type; +} + +static void +camel_imap4_folder_class_init (CamelIMAP4FolderClass *klass) +{ + CamelFolderClass *folder_class = (CamelFolderClass *) klass; + + parent_class = (CamelFolderClass *) camel_type_get_global_classfuncs (CAMEL_FOLDER_TYPE); + + folder_class->sync = imap4_sync; + folder_class->expunge = imap4_expunge; + folder_class->get_message = imap4_get_message; + folder_class->append_message = imap4_append_message; + folder_class->transfer_messages_to = imap4_transfer_messages_to; +} + +static void +camel_imap4_folder_init (CamelIMAP4Folder *folder, CamelIMAP4FolderClass *klass) +{ + folder->utf7_name = NULL; +} + +static void +camel_imap4_folder_finalize (CamelObject *object) +{ + CamelIMAP4Folder *folder = (CamelIMAP4Folder *) object; + + g_free (folder->utf7_name); +} + +static char +imap4_get_path_delim (CamelIMAP4Engine *engine, const char *full_name) +{ + CamelIMAP4Namespace *namespace; + const char *slash; + size_t len; + char *top; + + if ((slash = strchr (full_name, '/'))) + len = (slash - full_name); + else + len = strlen (full_name); + + top = g_alloca (len + 1); + memcpy (top, full_name, len); + top[len] = '\0'; + + if (!g_ascii_strcasecmp (top, "INBOX")) + top = "INBOX"; + + retry: + namespace = engine->namespaces.personal; + while (namespace != NULL) { + if (!strcmp (namespace->path, top)) + return namespace->sep; + namespace = namespace->next; + } + + namespace = engine->namespaces.other; + while (namespace != NULL) { + if (!strcmp (namespace->path, top)) + return namespace->sep; + namespace = namespace->next; + } + + namespace = engine->namespaces.shared; + while (namespace != NULL) { + if (!strcmp (namespace->path, top)) + return namespace->sep; + namespace = namespace->next; + } + + if (top[0] != '\0') { + /* look for a default namespace? */ + top[0] = '\0'; + goto retry; + } + + return '/'; +} + +CamelFolder * +camel_imap4_folder_new (CamelStore *store, const char *full_name, CamelException *ex) +{ + CamelIMAP4Folder *imap_folder; + char *utf7_name, *name, *p; + char sep; + + if (!(p = strrchr (full_name, '/'))) + p = (char *) full_name; + else + p++; + + name = g_alloca (strlen (p) + 1); + strcpy (name, p); + + utf7_name = g_alloca (strlen (full_name) + 1); + strcpy (utf7_name, full_name); + + sep = imap4_get_path_delim (((CamelIMAP4Store *) store)->engine, full_name); + if (sep != '/') { + p = utf7_name; + while (*p != '\0') { + if (*p == '/') + *p = sep; + p++; + } + } + + utf7_name = camel_utf8_utf7 (utf7_name); + + imap_folder = (CamelIMAP4Folder *) camel_object_new (CAMEL_TYPE_IMAP4_FOLDER); + camel_folder_construct ((CamelFolder *) imap_folder, store, full_name, name); + imap_folder->utf7_name = utf7_name; + + return (CamelFolder *) imap_folder; +} + + + +static struct { + const char *name; + guint32 flag; +} imap4_flags[] = { + { "\\Answered", CAMEL_MESSAGE_ANSWERED }, + { "\\Deleted", CAMEL_MESSAGE_DELETED }, + { "\\Draft", CAMEL_MESSAGE_DRAFT }, + { "\\Flagged", CAMEL_MESSAGE_FLAGGED }, + /*{ "Forwarded", CAMEL_MESSAGE_FORWARDED },*/ + { "\\Seen", CAMEL_MESSAGE_SEEN }, +}; + +static int +imap4_get_uid_set (CamelIMAP4Engine *engine, CamelFolderSummary *summary, GPtrArray *infos, int cur, size_t linelen, char **set) +{ + CamelMessageInfo *info; + guint32 this, prev, next; + gboolean range = FALSE; + GString *uidset; + int scount, i; + size_t len; + + if (engine->maxlentype == CAMEL_IMAP4_ENGINE_MAXLEN_LINE) + len = engine->maxlen - linelen; + else + len = engine->maxlen; + + i = cur + 1; + info = (CamelMessageInfo *) infos->pdata[cur]; + fflush (stdout); + uidset = g_string_new (""); + g_string_append (uidset, camel_message_info_uid (info)); + + if (!(i < infos->len)) + goto done; + + scount = summary->messages->len; + + /* init this info */ + for (this = 0; this < scount; this++) { + if (info == summary->messages->pdata[this]) + break; + } + + /* init next info */ + info = (CamelMessageInfo *) infos->pdata[i]; + for (next = this; next < scount; next++) { + if (info == summary->messages->pdata[next]) + break; + } + + for ( ; i < infos->len && uidset->len < len; i++) { + prev = this; + this = next; + + if (i + 1 < infos->len) { + info = infos->pdata[i + 1]; + for (next = this; next < scount; next++) { + if (info == summary->messages->pdata[next]) + break; + } + } else { + next = scount; + } + + if (this == (next - 1) || this == (prev + 1)) { + range = TRUE; + } else { + if (range) { + info = (CamelMessageInfo *) summary->messages->pdata[prev]; + g_string_append_printf (uidset, ":%s", camel_message_info_uid (info)); + range = FALSE; + } + + info = infos->pdata[i]; + g_string_append_printf (uidset, ",%s", camel_message_info_uid (info)); + } + } + + if (range) { + info = (CamelMessageInfo *) summary->messages->pdata[this]; + g_string_append_printf (uidset, ":%s", camel_message_info_uid (info)); + } + + done: + + *set = uidset->str; + g_string_free (uidset, FALSE); + + return (i - cur); +} + +static int +imap4_sync_flag (CamelFolder *folder, GPtrArray *infos, char onoff, const char *flag, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) folder->parent_store)->engine; + CamelIMAP4Command *ic; + int i, id, retval = 0; + char *set = NULL; + + for (i = 0; i < infos->len; ) { + i += 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) + ; + + g_free (set); + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + + return -1; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot sync flags to folder `%s': Unknown"), + folder->full_name); + retval = -1; + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot sync flags to folder `%s': Bad command"), + folder->full_name); + retval = -1; + break; + } + + camel_imap4_command_unref (ic); + + if (retval == -1) + return -1; + } + + return 0; +} + +static int +imap4_sync_changes (CamelFolder *folder, GPtrArray *sync, CamelException *ex) +{ + CamelIMAP4MessageInfo *iinfo; + GPtrArray *on_set, *off_set; + CamelMessageInfo *info; + flags_diff_t diff; + int retval = 0; + int i, j; + + on_set = g_ptr_array_new (); + off_set = g_ptr_array_new (); + + /* construct commands to sync system and user flags */ + for (i = 0; i < G_N_ELEMENTS (imap4_flags); i++) { + if (!(imap4_flags[i].flag & folder->permanent_flags)) + continue; + + for (j = 0; j < sync->len; j++) { + iinfo = (CamelIMAP4MessageInfo *) info = sync->pdata[j]; + camel_imap4_flags_diff (&diff, iinfo->server_flags, info->flags); + if (diff.changed & imap4_flags[i].flag) { + if (diff.bits & imap4_flags[i].flag) { + g_ptr_array_add (on_set, info); + } else { + g_ptr_array_add (off_set, info); + } + } + } + + if (on_set->len > 0) { + if ((retval = imap4_sync_flag (folder, on_set, '+', imap4_flags[i].name, ex)) == -1) + break; + + g_ptr_array_set_size (on_set, 0); + } + + if (off_set->len > 0) { + if ((retval = imap4_sync_flag (folder, off_set, '-', imap4_flags[i].name, ex)) == -1) + break; + + g_ptr_array_set_size (off_set, 0); + } + } + + g_ptr_array_free (on_set, TRUE); + g_ptr_array_free (off_set, TRUE); + + if (retval == -1) + return-1; + + for (i = 0; i < sync->len; i++) { + iinfo = (CamelIMAP4MessageInfo *) info = sync->pdata[i]; + info->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; + iinfo->server_flags = info->flags & folder->permanent_flags; + } + + return 0; +} + +static void +imap4_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) folder->parent_store)->engine; + CamelIMAP4MessageInfo *iinfo; + CamelMessageInfo *info; + CamelIMAP4Command *ic; + flags_diff_t diff; + GPtrArray *sync; + int id, max, i; + int retval; + + /* gather a list of changes to sync to the server */ + sync = g_ptr_array_new (); + max = camel_folder_summary_count (folder->summary); + for (i = 0; i < max; i++) { + iinfo = (CamelIMAP4MessageInfo *) info = camel_folder_summary_index (folder->summary, i); + expunge = expunge && (info->flags & CAMEL_MESSAGE_DELETED); + if (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) { + camel_imap4_flags_diff (&diff, iinfo->server_flags, info->flags); + diff.changed &= folder->permanent_flags; + + /* weed out flag changes that we can't sync to the server */ + if (!diff.changed) + camel_folder_summary_info_free (folder->summary, info); + else + g_ptr_array_add (sync, info); + } else { + camel_folder_summary_info_free (folder->summary, info); + } + } + + if (sync->len > 0) { + retval = imap4_sync_changes (folder, sync, ex); + + for (i = 0; i < sync->len; i++) + camel_folder_summary_info_free (folder->summary, sync->pdata[i]); + + g_ptr_array_free (sync, TRUE); + + if (retval == -1) + return; + } else { + g_ptr_array_free (sync, TRUE); + } + + if (expunge) { + ic = camel_imap4_engine_queue (engine, folder, "EXPUNGE\r\n"); + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot expunge folder `%s': Unknown"), + folder->full_name); + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot expunge folder `%s': Bad command"), + folder->full_name); + break; + } + + camel_imap4_command_unref (ic); + } + + camel_folder_summary_save (folder->summary); +} + +static void +imap4_expunge (CamelFolder *folder, CamelException *ex) +{ + imap4_sync (folder, TRUE, ex); +} + + +static int +untagged_fetch (CamelIMAP4Engine *engine, CamelIMAP4Command *ic, guint32 index, camel_imap4_token_t *token, CamelException *ex) +{ + CamelStream *fstream, *stream = ic->user_data; + CamelMimeFilter *crlf; + int left = 2; + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + /* parse the FETCH response list */ + if (token->token != '(') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + return -1; + } + + do { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != CAMEL_IMAP4_TOKEN_ATOM) + goto unexpected; + + if (!strcmp (token->v.atom, "BODY[]")) { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != CAMEL_IMAP4_TOKEN_LITERAL) + goto unexpected; + + fstream = (CamelStream *) camel_stream_filter_new_with_stream (stream); + crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_DECODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + camel_stream_filter_add ((CamelStreamFilter *) fstream, crlf); + camel_object_unref (crlf); + + camel_stream_write_to_stream ((CamelStream *) engine->istream, fstream); + camel_stream_flush (fstream); + camel_object_unref (fstream); + + left--; + } else if (!strcmp (token->v.atom, "UID")) { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != CAMEL_IMAP4_TOKEN_NUMBER || token->v.number == 0) + goto unexpected; + + left--; + } else { + /* wtf? */ + fprintf (stderr, "huh? %s?...\n", token->v.atom); + } + } while (left); + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != ')') { + fprintf (stderr, "expected ')' to close untagged FETCH response\n"); + goto unexpected; + } + + return 0; + + unexpected: + + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + + exception: + + return -1; +} + +static CamelMimeMessage * +imap4_get_message (CamelFolder *folder, const char *uid, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) folder->parent_store)->engine; + CamelMimeMessage *message = NULL; + CamelIMAP4Command *ic; + CamelStream *stream; + int id; + + ic = camel_imap4_engine_queue (engine, folder, "UID FETCH %s BODY.PEEK[]\r\n", uid); + camel_imap4_command_register_untagged (ic, "FETCH", untagged_fetch); + ic->user_data = stream = camel_stream_mem_new (); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + camel_object_unref (stream); + return NULL; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_OK: + camel_stream_reset (stream); + message = camel_mime_message_new (); + camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) message, stream); + break; + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot get message %s from folder `%s': No such message"), + uid, folder->full_name); + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot get message %s from folder `%s': Bad command"), + uid, folder->full_name); + break; + } + + camel_imap4_command_unref (ic); + + camel_object_unref (stream); + + return message; +} + +static char *tm_months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static void +imap4_append_message (CamelFolder *folder, CamelMimeMessage *message, + const CamelMessageInfo *info, char **appended_uid, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) folder->parent_store)->engine; + CamelIMAP4Summary *summary = (CamelIMAP4Summary *) folder->summary; + CamelIMAP4RespCode *resp; + CamelIMAP4Command *ic; + CamelFolderInfo *fi; + CamelException lex; + char flags[100], *p; + char date[50]; + struct tm tm; + int id, i; + + /* construct the option flags list */ + if (info->flags & folder->permanent_flags) { + p = g_stpcpy (flags, " ("); + + for (i = 0; i < G_N_ELEMENTS (imap4_flags); i++) { + if ((info->flags & imap4_flags[i].flag) & folder->permanent_flags) { + p = g_stpcpy (p, imap4_flags[i].name); + *p++ = ' '; + } + } + + p[-1] = ')'; + *p = '\0'; + } else { + flags[0] = '\0'; + } + + /* construct the optional date_time string */ + if (info->date_received != (time_t) -1) { + int tzone; + +#ifdef HAVE_LOCALTIME_R + localtime_r (&info->date_received, &tm); +#else + memcpy (&tm, localtime (&info->date_received), sizeof (tm)); +#endif + +#if defined (HAVE_TM_GMTOFF) + tzone = -tm.tm_gmtoff; +#elif defined (HAVE_TIMEZONE) + if (tm.tm_isdst > 0) { +#if defined (HAVE_ALTZONE) + tzone = altzone; +#else /* !defined (HAVE_ALTZONE) */ + tzone = (timezone - 3600); +#endif + } else { + tzone = timezone; + } +#else +#error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc. +#endif + + sprintf (date, " \"%02d-%s-%04d %02d:%02d:%02d %+05d\"", + tm.tm_mday, tm_months[tm.tm_mon], tm.tm_year + 1900, + tm.tm_hour, tm.tm_min, tm.tm_sec, tzone); + } else { + date[0] = '\0'; + } + + retry: + + if (engine->capa & CAMEL_IMAP4_CAPABILITY_UIDPLUS) + ic = camel_imap4_engine_queue (engine, NULL, "UID APPEND %F%s%s %L\r\n", flags, date, message); + else + ic = camel_imap4_engine_queue (engine, NULL, "APPEND %F%s%s %L\r\n", flags, date, message); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_OK: + if (!(engine->capa & CAMEL_IMAP4_CAPABILITY_UIDPLUS)) + break; + + if (!appended_uid) + break; + + for (i = 0; i < ic->resp_codes->len; i++) { + resp = ic->resp_codes->pdata[i]; + if (resp->code == CAMEL_IMAP4_RESP_CODE_APPENDUID) { + if (resp->v.appenduid.uidvalidity == summary->uidvalidity) + *appended_uid = g_strdup_printf ("%u", resp->v.appenduid.uid); + break; + } + } + break; + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: can we give the user any more information? */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot append message to folder `%s': Unknown error"), + folder->full_name); + + for (i = 0; i < ic->resp_codes->len; i++) { + resp = ic->resp_codes->pdata[i]; + if (resp->code == CAMEL_IMAP4_RESP_CODE_TRYCREATE) { + char *parent_name, *p; + + parent_name = g_alloca (strlen (folder->full_name) + 1); + if (!(p = strrchr (parent_name, '/'))) + *parent_name = '\0'; + else + *p = '\0'; + + if (!(fi = camel_store_create_folder (folder->parent_store, parent_name, folder->name, &lex))) { + camel_exception_clear (&lex); + break; + } + + camel_store_free_folder_info (folder->parent_store, fi); + camel_imap4_command_unref (ic); + camel_exception_clear (ex); + goto retry; + } + } + + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot append message to folder `%s': Bad command"), + folder->full_name); + + break; + default: + g_assert_not_reached (); + } + + camel_imap4_command_unref (ic); +} + + +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; +} + +static void +imap4_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest, + GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) src->parent_store)->engine; + int i, j, n, id, dest_namelen; + CamelMessageInfo *info; + CamelIMAP4Command *ic; + GPtrArray *infos; + char *set; + + infos = g_ptr_array_new (); + for (i = 0; i < uids->len; i++) { + if (!(info = camel_folder_summary_uid (src->summary, uids->pdata[i]))) + continue; + + g_ptr_array_add (infos, info); + } + + if (infos->len == 0) { + g_ptr_array_free (infos, TRUE); + return; + } + + g_ptr_array_sort (infos, (GCompareFunc) info_uid_sort); + + 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); + + 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) + ; + + g_free (set); + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + g_free (set); + goto done; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + if (delete_originals) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot move messages from folder `%s' to folder `%s': Unknown"), + src->full_name, dest->full_name); + } else { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot copy messages from folder `%s' to folder `%s': Unknown"), + src->full_name, dest->full_name); + } + + goto done; + case CAMEL_IMAP4_RESULT_BAD: + if (delete_originals) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot move messages from folder `%s' to folder `%s': Bad command"), + src->full_name, dest->full_name); + } else { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot copy messages from folder `%s' to folder `%s': Bad command"), + src->full_name, dest->full_name); + } + + goto done; + } + + camel_imap4_command_unref (ic); + + if (delete_originals) { + for (j = i; j < n; j++) { + info = infos->pdata[j]; + camel_folder_set_message_flags (src, camel_message_info_uid (info), + CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED); + } + + camel_folder_summary_touch (src->summary); + } + } + + done: + + for (i = 0; i < infos->len; i++) + camel_folder_summary_info_free (src->summary, infos->pdata[i]); + g_ptr_array_free (infos, TRUE); +} diff --git a/camel/providers/imap4/camel-imap4-folder.h b/camel/providers/imap4/camel-imap4-folder.h new file mode 100644 index 0000000000..203c80074a --- /dev/null +++ b/camel/providers/imap4/camel-imap4-folder.h @@ -0,0 +1,65 @@ +/* -*- 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_IMAP4_FOLDER_H__ +#define __CAMEL_IMAP4_FOLDER_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_TYPE_IMAP4_FOLDER (camel_imap4_folder_get_type ()) +#define CAMEL_IMAP4_FOLDER(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP4_FOLDER, CamelIMAP4Folder)) +#define CAMEL_IMAP4_FOLDER_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP4_FOLDER, CamelIMAP4FolderClass)) +#define CAMEL_IS_IMAP4_FOLDER(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP4_FOLDER)) +#define CAMEL_IS_IMAP4_FOLDER_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP4_FOLDER)) +#define CAMEL_IMAP4_FOLDER_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_IMAP4_FOLDER, CamelIMAP4FolderClass)) + +typedef struct _CamelIMAP4Folder CamelIMAP4Folder; +typedef struct _CamelIMAP4FolderClass CamelIMAP4FolderClass; + +struct _CamelIMAP4Folder { + CamelFolder parent_object; + + char *cachedir; + char *utf7_name; +}; + +struct _CamelIMAP4FolderClass { + CamelFolderClass parent_class; + +}; + + +CamelType camel_imap4_folder_get_type (void); + +CamelFolder *camel_imap4_folder_new (CamelStore *store, const char *full_name, CamelException *ex); + +const char *camel_imap4_folder_utf7_name (CamelIMAP4Folder *folder); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_FOLDER_H__ */ diff --git a/camel/providers/imap4/camel-imap4-provider.c b/camel/providers/imap4/camel-imap4-provider.c new file mode 100644 index 0000000000..185053f032 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-provider.c @@ -0,0 +1,139 @@ +/* -*- 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 +#endif + +#include + +#include +#include + +#include "camel-imap4-store.h" + + +CamelProviderConfEntry imap4_conf_entries[] = { + { CAMEL_PROVIDER_CONF_SECTION_START, "mailcheck", NULL, + N_("Checking for new mail") }, + { CAMEL_PROVIDER_CONF_CHECKBOX, "check_all", NULL, + N_("Check for new messages in all folders"), "1" }, + { CAMEL_PROVIDER_CONF_SECTION_END }, + { CAMEL_PROVIDER_CONF_SECTION_START, "folders", NULL, + N_("Folders") }, + { CAMEL_PROVIDER_CONF_CHECKBOX, "use_lsub", NULL, + N_("Show only subscribed folders"), "1" }, + { CAMEL_PROVIDER_CONF_CHECKBOX, "override_namespace", NULL, + N_("Override server-supplied folder namespace"), "0" }, + { CAMEL_PROVIDER_CONF_ENTRY, "namespace", "override_namespace", + N_("Namespace") }, + { CAMEL_PROVIDER_CONF_SECTION_END }, + { CAMEL_PROVIDER_CONF_END } +}; + +static CamelProvider imap4_provider = { + "imap4", + N_("IMAPv4rev1"), + + N_("For reading and storing mail on IMAPv4rev1 servers. EXPERIMENTAL !!"), + + "mail", + + CAMEL_PROVIDER_IS_REMOTE | CAMEL_PROVIDER_IS_SOURCE | + CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_SUPPORTS_SSL, + + CAMEL_URL_NEED_USER | CAMEL_URL_NEED_HOST | CAMEL_URL_ALLOW_AUTH, + + imap4_conf_entries, + + /* ... */ +}; + +CamelServiceAuthType camel_imap4_password_authtype = { + N_("Password"), + + N_("This option will connect to the IMAPv4rev1 server using a " + "plaintext password."), + + "", + TRUE +}; + + +static void +add_hash (guint *hash, char *s) +{ + if (s) + *hash ^= g_str_hash(s); +} + +static guint +imap4_url_hash (gconstpointer key) +{ + const CamelURL *u = (CamelURL *)key; + guint hash = 0; + + add_hash (&hash, u->user); + add_hash (&hash, u->authmech); + add_hash (&hash, u->host); + hash ^= u->port; + + return hash; +} + +static gint +check_equal (char *s1, char *s2) +{ + if (s1 == NULL) { + if (s2 == NULL) + return TRUE; + else + return FALSE; + } + + if (s2 == NULL) + return FALSE; + + return strcmp (s1, s2) == 0; +} + +static gint +imap4_url_equal (gconstpointer a, gconstpointer b) +{ + const CamelURL *u1 = a, *u2 = b; + + return check_equal (u1->user, u2->user) + && check_equal (u1->authmech, u2->authmech) + && check_equal (u1->host, u2->host) + && u1->port == u2->port; +} + + +void +camel_provider_module_init (void) +{ + imap4_provider.object_types[CAMEL_PROVIDER_STORE] = camel_imap4_store_get_type (); + imap4_provider.url_hash = imap4_url_hash; + imap4_provider.url_equal = imap4_url_equal; + imap4_provider.authtypes = camel_sasl_authtype_list (FALSE); + imap4_provider.authtypes = g_list_prepend (imap4_provider.authtypes, &camel_imap4_password_authtype); + + camel_provider_register (&imap4_provider); +} diff --git a/camel/providers/imap4/camel-imap4-specials.c b/camel/providers/imap4/camel-imap4-specials.c new file mode 100644 index 0000000000..4682a403e4 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-specials.c @@ -0,0 +1,100 @@ +/* -*- 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 +#endif + +#include + +#include "camel-imap4-specials.h" + +#define CHARS_ATOM_SPECIALS "(){]" +#define CHARS_LWSP " \t\r\n" +#define CHARS_QUOTED_SPECIALS "\\\"" +#define CHARS_LIST_WILDCARDS "*%" + +unsigned char camel_imap4_specials[256] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 6, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 20, 0, 8, 0, 0, 32, 0, 0, 1, 1, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + + +static void +imap4_init_bits (unsigned short bit, unsigned short bitcopy, int remove, unsigned char *vals) +{ + int i, len = strlen (vals); + + if (!remove) { + for (i = 0; i < len; i++) + camel_imap4_specials[vals[i]] |= bit; + if (bitcopy) { + for (i = 0; i < 256; i++) { + if (camel_imap4_specials[i] & bitcopy) + camel_imap4_specials[i] |= bit; + } + } + } else { + for (i = 0; i < 256; i++) + camel_imap4_specials[i] |= bit; + for (i = 0; i < len; i++) + camel_imap4_specials[vals[i]] &= ~bit; + if (bitcopy) { + for (i = 0; i < 256; i++) { + if (camel_imap4_specials[i] & bitcopy) + camel_imap4_specials[i] &= ~bit; + } + } + } +} + + +void +camel_imap4_specials_init (void) +{ + int i; + + for (i = 0; i < 256; i++) { + camel_imap4_specials[i] = 0; + if (i <= 0x1f || i >= 0x7f) + camel_imap4_specials[i] |= IS_CTRL; + } + + camel_imap4_specials[' '] |= IS_SPACE; + + imap4_init_bits (IS_LWSP, 0, 0, CHARS_LWSP); + imap4_init_bits (IS_ASPECIAL, 0, 0, CHARS_ATOM_SPECIALS); + imap4_init_bits (IS_QSPECIAL, 0, 0, CHARS_QUOTED_SPECIALS); + imap4_init_bits (IS_WILDCARD, 0, 0, CHARS_LIST_WILDCARDS); +} diff --git a/camel/providers/imap4/camel-imap4-specials.h b/camel/providers/imap4/camel-imap4-specials.h new file mode 100644 index 0000000000..36765cdc6b --- /dev/null +++ b/camel/providers/imap4/camel-imap4-specials.h @@ -0,0 +1,53 @@ +/* -*- 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_IMAP4_SPECIALS_H__ +#define __CAMEL_IMAP4_SPECIALS_H__ + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +enum { + IS_ASPECIAL = (1 << 0), + IS_CTRL = (1 << 1), + IS_LWSP = (1 << 2), + IS_QSPECIAL = (1 << 3), + IS_SPACE = (1 << 4), + IS_WILDCARD = (1 << 5), +}; + +extern unsigned char camel_imap4_specials[256]; + +#define is_atom(x) ((camel_imap4_specials[(unsigned char)(x)] & (IS_ASPECIAL|IS_SPACE|IS_CTRL|IS_WILDCARD|IS_QSPECIAL)) == 0) +#define is_ctrl(x) ((camel_imap4_specials[(unsigned char)(x)] & IS_CTRL) != 0) +#define is_lwsp(x) ((camel_imap4_specials[(unsigned char)(x)] & IS_LWSP) != 0) +#define is_type(x, t) ((camel_imap4_specials[(unsigned char)(x)] & (t)) != 0) +#define is_qsafe(x) ((camel_imap4_specials[(unsigned char)(x)] & (IS_QSPECIAL|IS_CTRL)) == 0) +#define is_wild(x) ((camel_imap4_specials[(unsigned char)(x)] & IS_WILDCARD) != 0) + +void camel_imap4_specials_init (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_SPECIALS_H__ */ diff --git a/camel/providers/imap4/camel-imap4-store.c b/camel/providers/imap4/camel-imap4-store.c new file mode 100644 index 0000000000..ffc8c91408 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-store.c @@ -0,0 +1,861 @@ +/* -*- 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 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "camel-imap4-store.h" +#include "camel-imap4-engine.h" +#include "camel-imap4-folder.h" +#include "camel-imap4-stream.h" +#include "camel-imap4-command.h" + + +static void camel_imap4_store_class_init (CamelIMAP4StoreClass *klass); +static void camel_imap4_store_init (CamelIMAP4Store *store, CamelIMAP4StoreClass *klass); +static void camel_imap4_store_finalize (CamelObject *object); + +/* service methods */ +static void imap4_construct (CamelService *service, CamelSession *session, + CamelProvider *provider, CamelURL *url, + CamelException *ex); +static char *imap4_get_name (CamelService *service, gboolean brief); +static gboolean imap4_connect (CamelService *service, CamelException *ex); +static gboolean imap4_disconnect (CamelService *service, gboolean clean, CamelException *ex); +static GList *imap4_query_auth_types (CamelService *service, CamelException *ex); + +/* store methods */ +static CamelFolder *imap4_get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex); +static CamelFolderInfo *imap4_create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex); +static void imap4_delete_folder (CamelStore *store, const char *folder_name, CamelException *ex); +static void imap4_rename_folder (CamelStore *store, const char *old_name, const char *new_name, CamelException *ex); +static void imap4_sync (CamelStore *store, gboolean expunge, CamelException *ex); +static CamelFolderInfo *imap4_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex); +static void imap4_subscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex); +static void imap4_unsubscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex); +static void imap4_noop (CamelStore *store, CamelException *ex); + + +static CamelStoreClass *parent_class = NULL; + + +CamelType +camel_imap4_store_get_type (void) +{ + static CamelType type = 0; + + if (!type) { + type = camel_type_register (CAMEL_TYPE_IMAP4_STORE, + "CamelIMAP4Store", + sizeof (CamelIMAP4Store), + sizeof (CamelIMAP4StoreClass), + (CamelObjectClassInitFunc) camel_imap4_store_class_init, + NULL, + (CamelObjectInitFunc) camel_imap4_store_init, + (CamelObjectFinalizeFunc) camel_imap4_store_finalize); + } + + return type; +} + +static guint +imap4_hash_folder_name (gconstpointer key) +{ + if (g_ascii_strcasecmp (key, "INBOX") == 0) + return g_str_hash ("INBOX"); + else + return g_str_hash (key); +} + +static gint +imap4_compare_folder_name (gconstpointer a, gconstpointer b) +{ + gconstpointer aname = a, bname = b; + + if (g_ascii_strcasecmp (a, "INBOX") == 0) + aname = "INBOX"; + if (g_ascii_strcasecmp (b, "INBOX") == 0) + bname = "INBOX"; + return g_str_equal (aname, bname); +} + +static void +camel_imap4_store_class_init (CamelIMAP4StoreClass *klass) +{ + CamelServiceClass *service_class = (CamelServiceClass *) klass; + CamelStoreClass *store_class = (CamelStoreClass *) klass; + + parent_class = (CamelStoreClass *) camel_type_get_global_classfuncs (CAMEL_STORE_TYPE); + + service_class->construct = imap4_construct; + service_class->get_name = imap4_get_name; + service_class->connect = imap4_connect; + service_class->disconnect = imap4_disconnect; + service_class->query_auth_types = imap4_query_auth_types; + + store_class->hash_folder_name = imap4_hash_folder_name; + store_class->compare_folder_name = imap4_compare_folder_name; + + store_class->get_folder = imap4_get_folder; + store_class->create_folder = imap4_create_folder; + store_class->delete_folder = imap4_delete_folder; + store_class->rename_folder = imap4_rename_folder; + store_class->sync = imap4_sync; + store_class->get_folder_info = imap4_get_folder_info; + store_class->subscribe_folder = imap4_subscribe_folder; + store_class->unsubscribe_folder = imap4_unsubscribe_folder; + store_class->noop = imap4_noop; +} + +static void +camel_imap4_store_init (CamelIMAP4Store *store, CamelIMAP4StoreClass *klass) +{ + store->engine = NULL; +} + +static void +camel_imap4_store_finalize (CamelObject *object) +{ + CamelIMAP4Store *store = (CamelIMAP4Store *) object; + + if (store->engine) + camel_object_unref (store->engine); +} + + +static void +imap4_construct (CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex) +{ + CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex); +} + +static char * +imap4_get_name (CamelService *service, gboolean brief) +{ + if (brief) + return g_strdup_printf (_("IMAP server %s"), service->url->host); + else + return g_strdup_printf (_("IMAP service for %s on %s"), + service->url->user, service->url->host); +} + +enum { + USE_SSL_NEVER, + USE_SSL_ALWAYS, + USE_SSL_WHEN_POSSIBLE +}; + +#define SSL_PORT_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3) +#define STARTTLS_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_TLS) + +static gboolean +connect_to_server (CamelService *service, struct hostent *host, int ssl_mode, int try_starttls, CamelException *ex) +{ + CamelIMAP4Store *store = (CamelIMAP4Store *) service; + CamelIMAP4Engine *engine; + CamelStream *tcp_stream; + int port, ret; + + if (store->engine) { + camel_object_unref (store->engine); + store->engine = NULL; + } + + port = service->url->port ? service->url->port : 143; + + if (ssl_mode) { +#ifdef HAVE_SSL + if (try_starttls) { + tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, STARTTLS_FLAGS); + } else { + port = service->url->port ? service->url->port : 993; + tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, SSL_PORT_FLAGS); + } +#else + if (!try_starttls) + port = service->url->port ? service->url->port : 993; + + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Could not connect to %s (port %d): %s"), + service->url->host, port, + _("SSL unavailable")); + + return FALSE; +#endif /* HAVE_SSL */ + } else { + tcp_stream = camel_tcp_stream_raw_new (); + } + + fprintf (stderr, "connecting to %s:%d\n", service->url->host, port); + if ((ret = camel_tcp_stream_connect ((CamelTcpStream *) tcp_stream, host, port)) == -1) { + if (errno == EINTR) + camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, + _("Connection cancelled")); + else + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Could not connect to %s (port %d): %s"), + service->url->host, port, + g_strerror (errno)); + + camel_object_unref (tcp_stream); + + return FALSE; + } + + engine = camel_imap4_engine_new (service->session, service->url); + if (camel_imap4_engine_take_stream (engine, tcp_stream, ex) == -1) { + camel_object_unref (engine); + + return FALSE; + } + + if (camel_imap4_engine_capability (engine, ex) == -1) { + camel_object_unref (engine); + + return FALSE; + } + + store->engine = engine; + +#ifdef HAVE_SSL + if (ssl_mode == USE_SSL_WHEN_POSSIBLE) { + /* try_starttls is always TRUE here */ + if (engine->capa & CAMEL_IMAP4_CAPABILITY_STARTTLS) + goto starttls; + } else if (ssl_mode == USE_SSL_ALWAYS) { + if (try_starttls) { + if (engine->capa & CAMEL_IMAP4_CAPABILITY_STARTTLS) { + goto starttls; + } else { + /* server doesn't support STARTTLS, abort */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Failed to connect to IMAP server %s in secure mode: " + "Server does not support STARTTLS"), + service->url->host); + goto exception; + } + } + } +#endif /* HAVE_SSL */ + + return TRUE; + +#ifdef HAVE_SSL + starttls: + + if (1) { + CamelIMAP4Command *ic; + int id; + + ic = camel_imap4_engine_queue (engine, NULL, "STARTTLS\r\n"); + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->result != CAMEL_IMAP4_RESULT_OK) { + if (ic->result != CAMEL_IMAP4_RESULT_OK) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to connect to IMAP server %s in secure mode: %s"), + service->url->host, _("Unknown error")); + } else { + camel_exception_xfer (ex, &ic->ex); + } + + camel_imap4_command_unref (ic); + + goto exception; + } + + camel_imap4_command_unref (ic); + } + + return TRUE; + + exception: + + camel_object_unref (store->engine); + store->engine = NULL; + + return FALSE; +#endif /* HAVE_SSL */ +} + +static struct { + char *value; + int mode; +} ssl_options[] = { + { "", USE_SSL_ALWAYS }, + { "always", USE_SSL_ALWAYS }, + { "when-possible", USE_SSL_WHEN_POSSIBLE }, + { "never", USE_SSL_NEVER }, + { NULL, USE_SSL_NEVER }, +}; + +static gboolean +connect_to_server_wrapper (CamelService *service, CamelException *ex) +{ + const char *use_ssl; + struct hostent *h; + int ssl_mode; + int ret, i; + + if (!(h = camel_service_gethost (service, ex))) + return FALSE; + + if ((use_ssl = camel_url_get_param (service->url, "use_ssl"))) { + for (i = 0; ssl_options[i].value; i++) + if (!strcmp (ssl_options[i].value, use_ssl)) + break; + ssl_mode = ssl_options[i].mode; + } else { + ssl_mode = USE_SSL_NEVER; + } + + if (ssl_mode == USE_SSL_ALWAYS) { + /* First try the ssl port */ + if (!(ret = connect_to_server (service, h, ssl_mode, FALSE, ex))) { + if (camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_UNAVAILABLE) { + /* The ssl port seems to be unavailable, lets try STARTTLS */ + camel_exception_clear (ex); + ret = connect_to_server (service, h, ssl_mode, TRUE, ex); + } + } + } else if (ssl_mode == USE_SSL_WHEN_POSSIBLE) { + /* If the server supports STARTTLS, use it */ + ret = connect_to_server (service, h, ssl_mode, TRUE, ex); + } else { + /* User doesn't care about SSL */ + ret = connect_to_server (service, h, USE_SSL_ALWAYS, FALSE, ex); + } + + camel_free_host (h); + + return ret; +} + +static int +sasl_auth (CamelIMAP4Engine *engine, CamelIMAP4Command *ic, const unsigned char *linebuf, size_t linelen, CamelException *ex) +{ + /* Perform a single challenge iteration */ + CamelSasl *sasl = ic->user_data; + char *challenge; + + if (camel_sasl_authenticated (sasl)) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Cannot authenticate to IMAP server %s using the %s authentication mechanism"), + engine->url->host, engine->url->authmech); + return -1; + } + + while (isspace (*linebuf)) + linebuf++; + + if (*linebuf == '\0') + linebuf = NULL; + + if (!(challenge = camel_sasl_challenge_base64 (sasl, (const char *) linebuf, ex))) + return -1; + + fprintf (stderr, "sending : %s\r\n", challenge); + + if (camel_stream_printf (engine->ostream, "%s\r\n", challenge) == -1) { + g_free (challenge); + return -1; + } + + g_free (challenge); + + if (camel_stream_flush (engine->ostream) == -1) + return -1; + + return 0; +} + +static int +imap4_try_authenticate (CamelService *service, gboolean reprompt, const char *errmsg, CamelException *ex) +{ + CamelIMAP4Store *store = (CamelIMAP4Store *) service; + CamelSession *session = service->session; + CamelSasl *sasl = NULL; + CamelIMAP4Command *ic; + int id; + + if (!service->url->passwd) { + guint32 flags = CAMEL_SESSION_PASSWORD_SECRET; + char *prompt; + + if (reprompt) + flags |= CAMEL_SESSION_PASSWORD_REPROMPT; + + prompt = g_strdup_printf (_("%sPlease enter the IMAP password for %s on host %s"), + errmsg ? errmsg : "", + service->url->user, + service->url->host); + + service->url->passwd = camel_session_get_password (session, prompt, flags, service, "password", ex); + + g_free (prompt); + + if (!service->url->passwd) + return FALSE; + } + + if (service->url->authmech) { + CamelServiceAuthType *mech; + + mech = g_hash_table_lookup (store->engine->authtypes, service->url->authmech); + sasl = camel_sasl_new ("imap4", mech->authproto, service); + + ic = camel_imap4_engine_queue (store->engine, NULL, "AUTHENTICATE %s\r\n", service->url->authmech); + ic->plus = sasl_auth; + ic->user_data = sasl; + } else { + ic = camel_imap4_engine_queue (store->engine, NULL, "LOGIN %S %S\r\n", + service->url->user, service->url->passwd); + } + + while ((id = camel_imap4_engine_iterate (store->engine)) < ic->id && id != -1) + ; + + if (sasl != NULL) + camel_object_unref (sasl); + + if (id == -1 || ic->status == CAMEL_IMAP4_COMMAND_ERROR) { + /* unrecoverable error */ + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + + return FALSE; + } + + if (ic->result != CAMEL_IMAP4_RESULT_OK) { + camel_imap4_command_unref (ic); + + /* try again */ + + return TRUE; + } + + camel_imap4_command_unref (ic); + + return FALSE; +} + +static gboolean +imap4_connect (CamelService *service, CamelException *ex) +{ + CamelIMAP4Store *store = (CamelIMAP4Store *) service; + CamelServiceAuthType *mech; + gboolean reprompt = FALSE; + char *errmsg = NULL; + CamelException lex; + + CAMEL_SERVICE_LOCK (store, connect_lock); + + if (!connect_to_server_wrapper (service, ex)) { + CAMEL_SERVICE_UNLOCK (store, connect_lock); + return FALSE; + } + +#define CANT_USE_AUTHMECH (!(mech = g_hash_table_lookup (store->engine->authtypes, service->url->authmech))) + if (service->url->authmech && CANT_USE_AUTHMECH) { + /* Oops. We can't AUTH using the requested mechanism */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Cannot authenticate to IMAP server %s using %s"), + service->url->host, service->url->authmech); + + camel_object_unref (store->engine); + store->engine = NULL; + + CAMEL_SERVICE_UNLOCK (store, connect_lock); + + return FALSE; + } + + camel_exception_init (&lex); + while (imap4_try_authenticate (service, reprompt, errmsg, &lex)) { + g_free (errmsg); + errmsg = g_strdup (lex.desc); + camel_exception_clear (&lex); + reprompt = TRUE; + } + g_free (errmsg); + + if (camel_exception_is_set (&lex)) { + camel_exception_xfer (ex, &lex); + camel_object_unref (store->engine); + store->engine = NULL; + + CAMEL_SERVICE_UNLOCK (store, connect_lock); + + return FALSE; + } + + if (camel_imap4_engine_namespace (store->engine, ex) == -1) { + camel_object_unref (store->engine); + store->engine = NULL; + + CAMEL_SERVICE_UNLOCK (store, connect_lock); + + return FALSE; + } + + CAMEL_SERVICE_UNLOCK (store, connect_lock); + + return TRUE; +} + +static gboolean +imap4_disconnect (CamelService *service, gboolean clean, CamelException *ex) +{ + CamelIMAP4Store *store = (CamelIMAP4Store *) service; + CamelIMAP4Command *ic; + int id; + + if (clean && !store->engine->istream->disconnected) { + ic = camel_imap4_engine_queue (store->engine, NULL, "LOGOUT\r\n"); + while ((id = camel_imap4_engine_iterate (store->engine)) < ic->id && id != -1) + ; + + camel_imap4_command_unref (ic); + } + + camel_object_unref (store->engine); + + return 0; +} + +extern CamelServiceAuthType camel_imap4_password_authtype; + +static GList * +imap4_query_auth_types (CamelService *service, CamelException *ex) +{ + CamelIMAP4Store *store = (CamelIMAP4Store *) service; + CamelServiceAuthType *authtype; + GList *sasl_types, *t, *next; + gboolean connected; + + CAMEL_SERVICE_LOCK (store, connect_lock); + connected = connect_to_server_wrapper (service, ex); + CAMEL_SERVICE_UNLOCK (store, connect_lock); + if (!connected) + return NULL; + + sasl_types = camel_sasl_authtype_list (FALSE); + for (t = sasl_types; t; t = next) { + authtype = t->data; + next = t->next; + + if (!g_hash_table_lookup (store->engine->authtypes, authtype->authproto)) { + sasl_types = g_list_remove_link (sasl_types, t); + g_list_free_1 (t); + } + } + + return g_list_prepend (sasl_types, &camel_imap4_password_authtype); +} + + +static char * +imap4_folder_utf7_name (CamelStore *store, const char *folder_name) +{ + char *real_name, *p; + + if (store->dir_sep != '/') { + p = real_name = g_alloca (strlen (folder_name) + 1); + strcpy (real_name, folder_name); + while (*p != '\0') { + if (*p == '/') + *p = store->dir_sep; + p++; + } + + folder_name = real_name; + } + + return camel_utf8_utf7 (folder_name); +} + +static CamelFolder * +imap4_get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) +{ + /* FIXME: implement me */ + + return NULL; +} + +static CamelFolderInfo * +imap4_create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex) +{ + /* FIXME: also need to deal with parent folders that can't + * contain subfolders - delete them and re-create with the + * proper hint */ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) store)->engine; + CamelFolderInfo *fi = NULL; + CamelIMAP4Command *ic; + char *utf7_name; + const char *c; + char *name; + int id; + + c = folder_name; + while (*c != '\0') { + if (*c == store->dir_sep || strchr ("#%*", *c)) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_PATH, + _("The folder name \"%s\" is invalid because " + "it containes the character \"%c\""), + folder_name, *c); + return NULL; + } + + c++; + } + + if (parent_name != NULL && *parent_name) + name = g_strdup_printf ("%s/%s", parent_name, folder_name); + else + name = g_strdup (folder_name); + + utf7_name = imap4_folder_utf7_name (store, name); + g_free (name); + + ic = camel_imap4_engine_queue (engine, NULL, "CREATE %S\r\n", utf7_name); + g_free (utf7_name); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return NULL; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_OK: + /* FIXME: allocate fi */ + break; + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot create folder `%s': Invalid mailbox name"), + folder_name); + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot create folder `%s': Bad command"), + folder_name); + break; + default: + g_assert_not_reached (); + } + + camel_imap4_command_unref (ic); + + return fi; +} + +static void +imap4_delete_folder (CamelStore *store, const char *folder_name, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) store)->engine; + CamelIMAP4Command *ic; + char *utf7_name; + int id; + + if (!g_ascii_strcasecmp (folder_name, "INBOX")) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot delete folder `%s': Special folder"), + folder_name); + + return; + } + + utf7_name = imap4_folder_utf7_name (store, folder_name); + ic = camel_imap4_engine_queue (engine, NULL, "DELETE %S\r\n", utf7_name); + g_free (utf7_name); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_OK: + /* deleted */ + /*fi = imap4_build_folder_info (store, folder_name); + camel_object_trigger_event (store, "folder_deleted", fi); + camel_folder_info_free (fi);*/ + break; + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot delete folder `%s': Invalid mailbox name"), + folder_name); + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot delete folder `%s': Bad command"), + folder_name); + break; + } + + camel_imap4_command_unref (ic); +} + +static void +imap4_rename_folder (CamelStore *store, const char *old_name, const char *new_name, CamelException *ex) +{ + +} + +static void +imap4_sync (CamelStore *store, gboolean expunge, CamelException *ex) +{ + +} + +static CamelFolderInfo * +imap4_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex) +{ + return NULL; +} + +static void +imap4_subscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) store)->engine; + CamelIMAP4Command *ic; + char *utf7_name; + int id; + + utf7_name = imap4_folder_utf7_name (store, folder_name); + ic = camel_imap4_engine_queue (engine, NULL, "SUBSCRIBE %S\r\n", utf7_name); + g_free (utf7_name); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_OK: + /* subscribed */ + /*fi = imap4_build_folder_info (store, folder_name); + fi->flags |= CAMEL_FOLDER_NOCHILDREN; + camel_object_trigger_event (store, "folder_subscribed", fi); + camel_folder_info_free (fi);*/ + break; + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot subscribe to folder `%s': Invalid mailbox name"), + folder_name); + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot subscribe to folder `%s': Bad command"), + folder_name); + break; + } + + camel_imap4_command_unref (ic); +} + +static void +imap4_unsubscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) store)->engine; + CamelIMAP4Command *ic; + char *utf7_name; + int id; + + utf7_name = imap4_folder_utf7_name (store, folder_name); + ic = camel_imap4_engine_queue (engine, NULL, "UNSUBSCRIBE %S\r\n", utf7_name); + g_free (utf7_name); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return; + } + + switch (ic->result) { + case CAMEL_IMAP4_RESULT_OK: + /* unsubscribed */ + /*fi = imap4_build_folder_info (store, folder_name); + camel_object_trigger_event (store, "folder_unsubscribed", fi); + camel_folder_info_free (fi);*/ + break; + case CAMEL_IMAP4_RESULT_NO: + /* FIXME: would be good to save the NO reason into the err message */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot unsubscribe from folder `%s': Invalid mailbox name"), + folder_name); + break; + case CAMEL_IMAP4_RESULT_BAD: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot unsubscribe from folder `%s': Bad command"), + folder_name); + break; + } + + camel_imap4_command_unref (ic); +} + +static void +imap4_noop (CamelStore *store, CamelException *ex) +{ + CamelIMAP4Engine *engine = ((CamelIMAP4Store *) store)->engine; + CamelIMAP4Command *ic; + int id; + + ic = camel_imap4_engine_queue (engine, NULL, "NOOP\r\n"); + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) + camel_exception_xfer (ex, &ic->ex); + + camel_imap4_command_unref (ic); +} diff --git a/camel/providers/imap4/camel-imap4-store.h b/camel/providers/imap4/camel-imap4-store.h new file mode 100644 index 0000000000..d8d758ab67 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-store.h @@ -0,0 +1,61 @@ +/* -*- 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_IMAP4_STORE_H__ +#define __CAMEL_IMAP4_STORE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_TYPE_IMAP4_STORE (camel_imap4_store_get_type ()) +#define CAMEL_IMAP4_STORE(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP4_STORE, CamelIMAP4Store)) +#define CAMEL_IMAP4_STORE_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP4_STORE, CamelIMAP4StoreClass)) +#define CAMEL_IS_IMAP4_STORE(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP4_STORE)) +#define CAMEL_IS_IMAP4_STORE_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP4_STORE)) +#define CAMEL_IMAP4_STORE_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_IMAP4_STORE, CamelIMAP4StoreClass)) + +typedef struct _CamelIMAP4Store CamelIMAP4Store; +typedef struct _CamelIMAP4StoreClass CamelIMAP4StoreClass; + +struct _CamelIMAP4Engine; + +struct _CamelIMAP4Store { + CamelStore parent_object; + + struct _CamelIMAP4Engine *engine; +}; + +struct _CamelIMAP4StoreClass { + CamelStoreClass parent_class; + +}; + + +CamelType camel_imap4_store_get_type (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_STORE_H__ */ diff --git a/camel/providers/imap4/camel-imap4-stream.c b/camel/providers/imap4/camel-imap4-stream.c new file mode 100644 index 0000000000..f2b2cbeb4b --- /dev/null +++ b/camel/providers/imap4/camel-imap4-stream.c @@ -0,0 +1,708 @@ +/* -*- 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 +#endif + +#include +#include +#include +#include +#include + +#include "camel-imap4-specials.h" + +#include "camel-imap4-stream.h" + +#define d(x) x + +#define IMAP4_TOKEN_LEN 128 + +static void camel_imap4_stream_class_init (CamelIMAP4StreamClass *klass); +static void camel_imap4_stream_init (CamelIMAP4Stream *stream, CamelIMAP4StreamClass *klass); +static void camel_imap4_stream_finalize (CamelObject *object); + +static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n); +static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n); +static int stream_flush (CamelStream *stream); +static int stream_close (CamelStream *stream); +static gboolean stream_eos (CamelStream *stream); + + +static CamelStreamClass *parent_class = NULL; + + +CamelType +camel_imap4_stream_get_type (void) +{ + static CamelType type = 0; + + if (!type) { + type = camel_type_register (CAMEL_TYPE_IMAP4_STREAM, + "CamelIMAP4Stream", + sizeof (CamelIMAP4Stream), + sizeof (CamelIMAP4StreamClass), + (CamelObjectClassInitFunc) camel_imap4_stream_class_init, + NULL, + (CamelObjectInitFunc) camel_imap4_stream_init, + (CamelObjectFinalizeFunc) camel_imap4_stream_finalize); + } + + return type; +} + +static void +camel_imap4_stream_class_init (CamelIMAP4StreamClass *klass) +{ + CamelStreamClass *stream_class = (CamelStreamClass *) klass; + + parent_class = (CamelStreamClass *) camel_type_get_global_classfuncs (CAMEL_STREAM_TYPE); + + /* virtual method overload */ + stream_class->read = stream_read; + stream_class->write = stream_write; + stream_class->flush = stream_flush; + stream_class->close = stream_close; + stream_class->eos = stream_eos; +} + +static void +camel_imap4_stream_init (CamelIMAP4Stream *imap4, CamelIMAP4StreamClass *klass) +{ + imap4->stream = NULL; + + imap4->mode = CAMEL_IMAP4_STREAM_MODE_TOKEN; + imap4->disconnected = FALSE; + imap4->eol = FALSE; + + imap4->literal = 0; + + imap4->inbuf = imap4->realbuf + IMAP4_READ_PRELEN; + imap4->inptr = imap4->inbuf; + imap4->inend = imap4->inbuf; + + imap4->tokenbuf = g_malloc (IMAP4_TOKEN_LEN); + imap4->tokenptr = imap4->tokenbuf; + imap4->tokenleft = IMAP4_TOKEN_LEN; + + imap4->unget = NULL; +} + +static void +camel_imap4_stream_finalize (CamelObject *object) +{ + CamelIMAP4Stream *imap4 = (CamelIMAP4Stream *) object; + + if (imap4->stream) + camel_object_unref (imap4->stream); + + g_free (imap4->tokenbuf); + g_free (imap4->unget); +} + + +static ssize_t +imap4_fill (CamelIMAP4Stream *imap4) +{ + unsigned char *inbuf, *inptr, *inend; + ssize_t nread; + size_t inlen; + + if (imap4->disconnected) { + errno = EINVAL; + return -1; + } + + inbuf = imap4->inbuf; + inptr = imap4->inptr; + inend = imap4->inend; + inlen = inend - inptr; + + g_assert (inptr <= inend); + + /* attempt to align 'inend' with realbuf + SCAN_HEAD */ + if (inptr >= inbuf) { + inbuf -= inlen < IMAP4_READ_PRELEN ? inlen : IMAP4_READ_PRELEN; + memmove (inbuf, inptr, inlen); + inptr = inbuf; + inbuf += inlen; + } else if (inptr > imap4->realbuf) { + size_t shift; + + shift = MIN (inptr - imap4->realbuf, inend - inbuf); + memmove (inptr - shift, inptr, inlen); + inptr -= shift; + inbuf = inptr + inlen; + } else { + /* we can't shift... */ + inbuf = inend; + } + + imap4->inptr = inptr; + imap4->inend = inbuf; + inend = imap4->realbuf + IMAP4_READ_PRELEN + IMAP4_READ_BUFLEN - 1; + + if ((nread = camel_stream_read (imap4->stream, inbuf, inend - inbuf)) == -1) + return -1; + else if (nread == 0) + imap4->disconnected = TRUE; + + imap4->inend += nread; + + return imap4->inend - imap4->inptr; +} + +static ssize_t +stream_read (CamelStream *stream, char *buffer, size_t n) +{ + CamelIMAP4Stream *imap4 = (CamelIMAP4Stream *) stream; + ssize_t len, nread = 0; + + if (imap4->mode == CAMEL_IMAP4_STREAM_MODE_LITERAL) { + /* don't let our caller read past the end of the literal */ + n = MIN (n, imap4->literal); + } + + if (imap4->inptr < imap4->inend) { + len = MIN (n, imap4->inend - imap4->inptr); + memcpy (buffer, imap4->inptr, len); + imap4->inptr += len; + nread = len; + } + + if (nread < n) { + if ((len = camel_stream_read (imap4->stream, buffer + nread, n - nread)) == 0) + imap4->disconnected = TRUE; + else if (len == -1) + return -1; + + nread += len; + } + + if (imap4->mode == CAMEL_IMAP4_STREAM_MODE_LITERAL) { + imap4->literal -= nread; + + if (imap4->literal == 0) { + imap4->mode = CAMEL_IMAP4_STREAM_MODE_TOKEN; + imap4->eol = TRUE; + } + } + + return nread; +} + +static ssize_t +stream_write (CamelStream *stream, const char *buffer, size_t n) +{ + CamelIMAP4Stream *imap4 = (CamelIMAP4Stream *) stream; + ssize_t nwritten; + + if (imap4->disconnected) { + errno = EINVAL; + return -1; + } + + if ((nwritten = camel_stream_write (imap4->stream, buffer, n)) == 0) + imap4->disconnected = TRUE; + + return nwritten; +} + +static int +stream_flush (CamelStream *stream) +{ + CamelIMAP4Stream *imap4 = (CamelIMAP4Stream *) stream; + + return camel_stream_flush (imap4->stream); +} + +static int +stream_close (CamelStream *stream) +{ + CamelIMAP4Stream *imap4 = (CamelIMAP4Stream *) stream; + + if (camel_stream_close (imap4->stream) == -1) + return -1; + + camel_object_unref (imap4->stream); + imap4->stream = NULL; + + imap4->disconnected = TRUE; + + return 0; +} + +static gboolean +stream_eos (CamelStream *stream) +{ + CamelIMAP4Stream *imap4 = (CamelIMAP4Stream *) stream; + + if (imap4->eol) + return TRUE; + + if (imap4->disconnected && imap4->inptr == imap4->inend) + return TRUE; + + if (camel_stream_eos (imap4->stream)) + return TRUE; + + return FALSE; +} + + +/** + * camel_imap4_stream_new: + * @stream: tcp stream + * + * Returns a new imap4 stream + **/ +CamelStream * +camel_imap4_stream_new (CamelStream *stream) +{ + CamelIMAP4Stream *imap4; + + g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL); + + imap4 = (CamelIMAP4Stream *) camel_object_new (CAMEL_TYPE_IMAP4_STREAM); + camel_object_ref (stream); + imap4->stream = stream; + + return (CamelStream *) imap4; +} + + + +#define token_save(imap4, start, len) G_STMT_START { \ + if (imap4->tokenleft <= len) { \ + unsigned int tlen, toff; \ + \ + tlen = toff = imap4->tokenptr - imap4->tokenbuf; \ + tlen = tlen ? tlen : 1; \ + \ + while (tlen < toff + len) \ + tlen <<= 1; \ + \ + imap4->tokenbuf = g_realloc (imap4->tokenbuf, tlen + 1); \ + imap4->tokenptr = imap4->tokenbuf + toff; \ + imap4->tokenleft = tlen - toff; \ + } \ + \ + memcpy (imap4->tokenptr, start, len); \ + imap4->tokenptr += len; \ + imap4->tokenleft -= len; \ +} G_STMT_END + +#define token_clear(imap4) G_STMT_START { \ + imap4->tokenleft += imap4->tokenptr - imap4->tokenbuf; \ + imap4->tokenptr = imap4->tokenbuf; \ + imap4->literal = 0; \ +} G_STMT_END + + +/** + * camel_imap4_stream_next_token: + * @stream: imap4 stream + * @token: imap4 token + * + * Reads the next token from the imap4 stream and saves it in @token. + * + * Returns 0 on success or -1 on fail. + **/ +int +camel_imap4_stream_next_token (CamelIMAP4Stream *stream, camel_imap4_token_t *token) +{ + register unsigned char *inptr; + unsigned char *inend, *start, *p; + gboolean escaped = FALSE; + size_t literal = 0; + guint32 nz_number; + int ret; + + g_return_val_if_fail (CAMEL_IS_IMAP4_STREAM (stream), -1); + g_return_val_if_fail (stream->mode != CAMEL_IMAP4_STREAM_MODE_LITERAL, -1); + g_return_val_if_fail (token != NULL, -1); + + if (stream->unget) { + memcpy (token, stream->unget, sizeof (camel_imap4_token_t)); + g_free (stream->unget); + stream->unget = NULL; + return 0; + } + + token_clear (stream); + + inptr = stream->inptr; + inend = stream->inend; + *inend = '\0'; + + do { + if (inptr == inend) { + if ((ret = imap4_fill (stream)) < 0) { + token->token = CAMEL_IMAP4_TOKEN_ERROR; + return -1; + } else if (ret == 0) { + token->token = CAMEL_IMAP4_TOKEN_NO_DATA; + return 0; + } + + inptr = stream->inptr; + inend = stream->inend; + *inend = '\0'; + } + + while (*inptr == ' ' || *inptr == '\r') + inptr++; + } while (inptr == inend); + + do { + if (inptr < inend) { + if (*inptr == '"') { + /* qstring token */ + escaped = FALSE; + start = inptr; + + /* eat the beginning " */ + inptr++; + + p = inptr; + while (inptr < inend) { + if (*inptr == '"' && !escaped) + break; + + if (*inptr == '\\' && !escaped) { + token_save (stream, p, inptr - p); + escaped = TRUE; + inptr++; + p = inptr; + } else { + inptr++; + escaped = FALSE; + } + } + + token_save (stream, p, inptr - p); + + if (inptr == inend) { + stream->inptr = start; + goto refill; + } + + /* eat the ending " */ + inptr++; + + /* nul-terminate the atom token */ + token_save (stream, "", 1); + + token->token = CAMEL_IMAP4_TOKEN_QSTRING; + token->v.qstring = stream->tokenbuf; + + d(fprintf (stderr, "token: \"%s\"\n", token->v.qstring)); + + break; + } else if (strchr ("+*()[]\n", *inptr)) { + /* special character token */ + token->token = *inptr++; +#if d(!)0 + if (token->token != '\n') + fprintf (stderr, "token: %c\n", token->token); + else + fprintf (stderr, "token: \\n\n"); +#endif + break; + } else if (*inptr == '{') { + /* literal identifier token */ + if ((p = strchr (inptr, '}')) && strchr (p, '\n')) { + inptr++; + + while (isdigit ((int) *inptr) && literal < UINT_MAX / 10) + literal = (literal * 10) + (*inptr++ - '0'); + + if (*inptr != '}') { + if (isdigit ((int) *inptr)) + g_warning ("illegal literal identifier: literal too large"); + else if (*inptr != '+') + g_warning ("illegal literal identifier: garbage following size"); + + while (*inptr != '}') + inptr++; + } + + /* skip over '}' */ + inptr++; + + /* skip over any trailing whitespace */ + while (*inptr == ' ' || *inptr == '\r') + inptr++; + + if (*inptr != '\n') { + g_warning ("illegal token following literal identifier: %s", inptr); + + /* skip ahead to the eoln */ + inptr = strchr (inptr, '\n'); + } + + /* skip over '\n' */ + inptr++; + + token->token = CAMEL_IMAP4_TOKEN_LITERAL; + token->v.literal = literal; + + d(fprintf (stderr, "token: {%u}\n", literal)); + + stream->mode = CAMEL_IMAP4_STREAM_MODE_LITERAL; + stream->literal = literal; + stream->eol = FALSE; + + break; + } else { + stream->inptr = inptr; + goto refill; + } + } else if (*inptr >= '0' && *inptr <= '9') { + /* number token */ + *inend = '\0'; + nz_number = strtoul ((char *) inptr, (char **) &start, 10); + if (start == inend) + goto refill; + + if (*start == ':' || *start == ',') { + /* workaround for 'set' tokens (APPENDUID / COPYUID) */ + goto atom_token; + } + + inptr = start; + token->token = CAMEL_IMAP4_TOKEN_NUMBER; + token->v.number = nz_number; + + d(fprintf (stderr, "token: %u\n", nz_number)); + + break; + } else if (is_atom (*inptr)) { + atom_token: + /* simple atom token */ + start = inptr; + + while (inptr < inend && is_atom (*inptr)) + inptr++; + + if (inptr == inend) { + stream->inptr = start; + goto refill; + } + + token_save (stream, start, inptr - start); + + /* nul-terminate the atom token */ + token_save (stream, "", 1); + + if (!strcmp (stream->tokenbuf, "NIL")) { + /* special atom token */ + token->token = CAMEL_IMAP4_TOKEN_NIL; + d(fprintf (stderr, "token: NIL\n")); + } else { + token->token = CAMEL_IMAP4_TOKEN_ATOM; + token->v.atom = stream->tokenbuf; + d(fprintf (stderr, "token: %s\n", token->v.atom)); + } + + break; + } else if (*inptr == '\\') { + /* possible flag token ("\" atom) */ + start = inptr++; + + while (inptr < inend && is_atom (*inptr)) + inptr++; + + if (inptr == inend) { + stream->inptr = start; + goto refill; + } + + if ((inptr - start) > 1) { + token_save (stream, start, inptr - start); + + /* nul-terminate the flag token */ + token_save (stream, "", 1); + + token->token = CAMEL_IMAP4_TOKEN_FLAG; + token->v.atom = stream->tokenbuf; + d(fprintf (stderr, "token: %s\n", token->v.atom)); + } else { + token->token = '\\'; + d(fprintf (stderr, "token: %c\n", token->token)); + } + break; + } else if (is_lwsp (*inptr)) { + inptr++; + } else { + /* unknown character token? */ + token->token = *inptr++; + d(fprintf (stderr, "token: %c\n", token->token)); + break; + } + } else { + refill: + token_clear (stream); + + if (imap4_fill (stream) <= 0) { + token->token = CAMEL_IMAP4_TOKEN_ERROR; + return -1; + } + + inptr = stream->inptr; + inend = stream->inend; + *inend = '\0'; + } + } while (inptr < inend); + + stream->inptr = inptr; + + return 0; +} + + +/** + * camel_imap4_stream_unget_token: + * @stream: imap4 stream + * @token: token to 'unget' + * + * Ungets an imap4 token (as in ungetc()). + * + * Note: you may *ONLY* unget a single token. Trying to unget another + * token will fail. + * + * Returns 0 on success or -1 on fail. + **/ +int +camel_imap4_stream_unget_token (CamelIMAP4Stream *stream, camel_imap4_token_t *token) +{ + camel_imap4_token_t *unget; + + if (stream->unget) + return -1; + + if (token->token != CAMEL_IMAP4_TOKEN_NO_DATA) { + stream->unget = unget = g_new (camel_imap4_token_t, 1); + memcpy (unget, token, sizeof (camel_imap4_token_t)); + } + + return 0; +} + + +/** + * camel_imap4_stream_readline: + * @stream: imap4 stream + * @line: line pointer + * @len: line length + * + * Reads a single line from the imap4 stream and points @line at an + * internal buffer containing the line read and sets @len to the + * length of the line buffer. + * + * Returns -1 on error, 0 if the line read is complete, or 1 if the + * read is incomplete. + **/ +int +camel_imap4_stream_line (CamelIMAP4Stream *stream, unsigned char **line, size_t *len) +{ + register unsigned char *inptr; + unsigned char *inend; + + g_return_val_if_fail (CAMEL_IS_IMAP4_STREAM (stream), -1); + g_return_val_if_fail (stream->mode != CAMEL_IMAP4_STREAM_MODE_LITERAL, -1); + g_return_val_if_fail (line != NULL, -1); + g_return_val_if_fail (len != NULL, -1); + + if ((stream->inend - stream->inptr) < 3) { + /* keep our buffer full to the optimal size */ + if (imap4_fill (stream) == -1 && stream->inptr == stream->inend) + return -1; + } + + *line = stream->inptr; + inptr = stream->inptr; + inend = stream->inend; + *inend = '\n'; + + while (*inptr != '\n') + inptr++; + + *len = (inptr - stream->inptr); + if (inptr < inend) { + /* got the eoln */ + if (inptr > stream->inptr && inptr[-1] == '\r') + inptr[-1] = '\0'; + else + inptr[0] = '\0'; + + stream->inptr = inptr + 1; + *len += 1; + + return 0; + } + + stream->inptr = inptr; + + return 1; +} + + +int +camel_imap4_stream_literal (CamelIMAP4Stream *stream, unsigned char **literal, size_t *len) +{ + unsigned char *inptr, *inend; + size_t nread; + + g_return_val_if_fail (CAMEL_IS_IMAP4_STREAM (stream), -1); + g_return_val_if_fail (stream->mode == CAMEL_IMAP4_STREAM_MODE_LITERAL, -1); + g_return_val_if_fail (literal != NULL, -1); + g_return_val_if_fail (len != NULL, -1); + + if (stream->eol) { + *len = 0; + return 0; + } + + if ((stream->inend - stream->inptr) < 1) { + /* keep our buffer full to the optimal size */ + if (imap4_fill (stream) == -1 && stream->inptr == stream->inend) + return -1; + } + + *literal = inptr = stream->inptr; + inend = stream->inend; + if ((inend - inptr) > stream->literal) + inend = inptr + stream->literal; + else + inend = stream->inend; + + *len = nread = inend - inptr; + + stream->literal -= nread; + if (stream->literal == 0) { + stream->mode = CAMEL_IMAP4_STREAM_MODE_TOKEN; + stream->eol = TRUE; + return 0; + } + + return 1; +} diff --git a/camel/providers/imap4/camel-imap4-stream.h b/camel/providers/imap4/camel-imap4-stream.h new file mode 100644 index 0000000000..7ef8422008 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-stream.h @@ -0,0 +1,124 @@ +/* -*- 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_IMAP4_STREAM_H__ +#define __CAMEL_IMAP4_STREAM_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_TYPE_IMAP4_STREAM (camel_imap4_stream_get_type ()) +#define CAMEL_IMAP4_STREAM(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP4_STREAM, CamelIMAP4Stream)) +#define CAMEL_IMAP4_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_TYPE_IMAP4_STREAM, CamelIMAP4StreamClass)) +#define CAMEL_IS_IMAP4_STREAM(o) (CAMEL_CHECK_TYPE((o), CAMEL_TYPE_IMAP4_STREAM)) + +typedef struct _CamelIMAP4Stream CamelIMAP4Stream; +typedef struct _CamelIMAP4StreamClass CamelIMAP4StreamClass; + +#define IMAP4_READ_PRELEN 128 +#define IMAP4_READ_BUFLEN 4096 + +enum { + CAMEL_IMAP4_TOKEN_NO_DATA = -8, + CAMEL_IMAP4_TOKEN_ERROR = -7, + CAMEL_IMAP4_TOKEN_NIL = -6, + CAMEL_IMAP4_TOKEN_ATOM = -5, + CAMEL_IMAP4_TOKEN_FLAG = -4, + CAMEL_IMAP4_TOKEN_NUMBER = -3, + CAMEL_IMAP4_TOKEN_QSTRING = -2, + CAMEL_IMAP4_TOKEN_LITERAL = -1, + /* CAMEL_IMAP4_TOKEN_CHAR would just be the char we got */ + CAMEL_IMAP4_TOKEN_EOLN = '\n', + CAMEL_IMAP4_TOKEN_LPAREN = '(', + CAMEL_IMAP4_TOKEN_RPAREN = ')', + CAMEL_IMAP4_TOKEN_ASTERISK = '*', + CAMEL_IMAP4_TOKEN_PLUS = '+', + CAMEL_IMAP4_TOKEN_LBRACKET = '[', + CAMEL_IMAP4_TOKEN_RBRACKET = ']', +}; + +typedef struct _camel_imap4_token_t { + int token; + union { + char *atom; + char *flag; + char *qstring; + size_t literal; + guint32 number; + } v; +} camel_imap4_token_t; + +enum { + CAMEL_IMAP4_STREAM_MODE_TOKEN = 0, + CAMEL_IMAP4_STREAM_MODE_LITERAL = 1, +}; + +struct _CamelIMAP4Stream { + CamelStream parent_object; + + CamelStream *stream; + + guint disconnected:1; /* disconnected state */ + guint mode:1; /* TOKEN vs LITERAL */ + guint eol:1; /* end-of-literal */ + + size_t literal; + + /* i/o buffers */ + unsigned char realbuf[IMAP4_READ_PRELEN + IMAP4_READ_BUFLEN + 1]; + unsigned char *inbuf; + unsigned char *inptr; + unsigned char *inend; + + /* token buffers */ + unsigned char *tokenbuf; + unsigned char *tokenptr; + unsigned int tokenleft; + + camel_imap4_token_t *unget; +}; + +struct _CamelIMAP4StreamClass { + CamelStreamClass parent_class; + + /* Virtual methods */ +}; + + +/* Standard Camel function */ +CamelType camel_imap4_stream_get_type (void); + +CamelStream *camel_imap4_stream_new (CamelStream *stream); + +int camel_imap4_stream_next_token (CamelIMAP4Stream *stream, camel_imap4_token_t *token); +int camel_imap4_stream_unget_token (CamelIMAP4Stream *stream, camel_imap4_token_t *token); + +int camel_imap4_stream_line (CamelIMAP4Stream *stream, unsigned char **line, size_t *len); +int camel_imap4_stream_literal (CamelIMAP4Stream *stream, unsigned char **literal, size_t *len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_STREAM_H__ */ diff --git a/camel/providers/imap4/camel-imap4-summary.c b/camel/providers/imap4/camel-imap4-summary.c new file mode 100644 index 0000000000..fdc1e3cd52 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "camel-imap4-store.h" +#include "camel-imap4-engine.h" +#include "camel-imap4-folder.h" +#include "camel-imap4-stream.h" +#include "camel-imap4-command.h" +#include "camel-imap4-utils.h" + +#include "camel-imap4-summary.h" + +#define IMAP4_SUMMARY_VERSION 1 + +static void camel_imap4_summary_class_init (CamelIMAP4SummaryClass *klass); +static void camel_imap4_summary_init (CamelIMAP4Summary *summary, CamelIMAP4SummaryClass *klass); +static void camel_imap4_summary_finalize (CamelObject *object); + +static int imap4_header_load (CamelFolderSummary *summary, FILE *fin); +static int imap4_header_save (CamelFolderSummary *summary, FILE *fout); +static CamelMessageInfo *imap4_message_info_new (CamelFolderSummary *summary, struct _camel_header_raw *header); +static CamelMessageInfo *imap4_message_info_load (CamelFolderSummary *summary, FILE *fin); +static int imap4_message_info_save (CamelFolderSummary *summary, FILE *fout, CamelMessageInfo *info); + + +static CamelFolderSummaryClass *parent_class = NULL; + + +CamelType +camel_imap4_summary_get_type (void) +{ + static CamelType type = 0; + + if (!type) { + type = camel_type_register (CAMEL_TYPE_IMAP4_SUMMARY, + "CamelIMAP4Summary", + sizeof (CamelIMAP4Summary), + sizeof (CamelIMAP4SummaryClass), + (CamelObjectClassInitFunc) camel_imap4_summary_class_init, + NULL, + (CamelObjectInitFunc) camel_imap4_summary_init, + (CamelObjectFinalizeFunc) camel_imap4_summary_finalize); + } + + return type; +} + + +static void +camel_imap4_summary_class_init (CamelIMAP4SummaryClass *klass) +{ + CamelFolderSummaryClass *summary_class = (CamelFolderSummaryClass *) klass; + + parent_class = (CamelFolderSummaryClass *) camel_type_get_global_classfuncs (camel_folder_summary_get_type ()); + + summary_class->summary_header_load = imap4_header_load; + summary_class->summary_header_save = imap4_header_save; + summary_class->message_info_new = imap4_message_info_new; + summary_class->message_info_load = imap4_message_info_load; + summary_class->message_info_save = imap4_message_info_save; +} + +static void +camel_imap4_summary_init (CamelIMAP4Summary *summary, CamelIMAP4SummaryClass *klass) +{ + CamelFolderSummary *folder_summary = (CamelFolderSummary *) summary; + + folder_summary->version += IMAP4_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 (CamelIMAP4MessageInfo); +} + +static void +camel_imap4_summary_finalize (CamelObject *object) +{ + ; +} + + +CamelFolderSummary * +camel_imap4_summary_new (CamelFolder *folder) +{ + CamelFolderSummary *summary; + + summary = (CamelFolderSummary *) camel_object_new (CAMEL_TYPE_IMAP4_SUMMARY); + ((CamelIMAP4Summary *) summary)->folder = folder; + + return summary; +} + +static int +imap4_header_load (CamelFolderSummary *summary, FILE *fin) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + + if (CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->summary_header_load (summary, fin) == -1) + return -1; + + if (camel_file_util_decode_uint32 (fin, &imap4_summary->uidvalidity) == -1) + return -1; + + return 0; +} + +static int +imap4_header_save (CamelFolderSummary *summary, FILE *fout) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + + if (CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->summary_header_save (summary, fout) == -1) + return -1; + + if (camel_file_util_encode_uint32 (fout, imap4_summary->uidvalidity) == -1) + return -1; + + return 0; +} + +static int +envelope_decode_address (CamelIMAP4Engine *engine, GString *addrs, CamelException *ex) +{ + camel_imap4_token_t token; + gboolean had_name = FALSE; + int part = 0; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token == CAMEL_IMAP4_TOKEN_NIL) { + return 0; + } else if (token.token != '(') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + if (addrs->len > 0) + g_string_append (addrs, ", "); + + do { + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + switch (token.token) { + case CAMEL_IMAP4_TOKEN_NIL: + break; + case CAMEL_IMAP4_TOKEN_ATOM: + case CAMEL_IMAP4_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_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + part++; + } while (part < 4); + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != ')') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + return 0; +} + +static int +envelope_decode_addresses (CamelIMAP4Engine *engine, char **addrlist, CamelException *ex) +{ + camel_imap4_token_t token; + GString *addrs; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token == CAMEL_IMAP4_TOKEN_NIL) { + *addrlist = NULL; + return 0; + } else if (token.token != '(') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + addrs = g_string_new (""); + + do { + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) { + g_string_free (addrs, TRUE); + return -1; + } + + if (token.token == '(') { + camel_imap4_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_imap4_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 (CamelIMAP4Engine *engine, time_t *date, CamelException *ex) +{ + camel_imap4_token_t token; + const char *nstring; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + switch (token.token) { + case CAMEL_IMAP4_TOKEN_NIL: + *date = (time_t) -1; + return 0; + case CAMEL_IMAP4_TOKEN_ATOM: + nstring = token.v.atom; + break; + case CAMEL_IMAP4_TOKEN_QSTRING: + nstring = token.v.qstring; + break; + default: + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + *date = camel_header_decode_date (nstring, NULL); + + return 0; +} + +static int +envelope_decode_nstring (CamelIMAP4Engine *engine, char **nstring, CamelException *ex) +{ + camel_imap4_token_t token; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + switch (token.token) { + case CAMEL_IMAP4_TOKEN_NIL: + *nstring = NULL; + break; + case CAMEL_IMAP4_TOKEN_ATOM: + *nstring = g_strdup (token.v.atom); + break; + case CAMEL_IMAP4_TOKEN_QSTRING: + *nstring = g_strdup (token.v.qstring); + break; + default: + camel_imap4_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 (CamelIMAP4Engine *engine, CamelMessageInfo *info, camel_imap4_token_t *token, CamelException *ex) +{ + unsigned char md5sum[16]; + char *nstring; + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token != '(') { + camel_imap4_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_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + if (token->token != ')') { + camel_imap4_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 { + IMAP4_FETCH_ENVELOPE = (1 << 1), + IMAP4_FETCH_FLAGS = (1 << 2), + IMAP4_FETCH_INTERNALDATE = (1 << 3), + IMAP4_FETCH_RFC822SIZE = (1 << 4), + IMAP4_FETCH_UID = (1 << 5), +}; + +#define IMAP4_FETCH_ALL (IMAP4_FETCH_ENVELOPE | IMAP4_FETCH_FLAGS | IMAP4_FETCH_INTERNALDATE | IMAP4_FETCH_RFC822SIZE | IMAP4_FETCH_UID) + +struct imap4_envelope_t { + CamelMessageInfo *info; + guint changed; +}; + +struct imap4_fetch_all_t { + CamelFolderSummary *summary; + GHashTable *uid_hash; + GPtrArray *added; +}; + +static void +imap4_fetch_all_free (struct imap4_fetch_all_t *fetch) +{ + struct imap4_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 +imap4_fetch_all_add (struct imap4_fetch_all_t *fetch) +{ + struct imap4_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 != IMAP4_FETCH_ALL) { + fprintf (stderr, "Hmmm, IMAP4 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 +imap4_fetch_all_update (struct imap4_fetch_all_t *fetch) +{ + CamelIMAP4MessageInfo *iinfo, *new_iinfo; + struct imap4_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 & IMAP4_FETCH_FLAGS) { + /* update it with the new flags */ + new_iinfo = (CamelIMAP4MessageInfo *) envelope->info; + iinfo = (CamelIMAP4MessageInfo *) info; + + info->flags = camel_imap4_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 (CamelIMAP4Engine *engine, CamelIMAP4Command *ic, guint32 index, camel_imap4_token_t *token, CamelException *ex) +{ + struct imap4_fetch_all_t *fetch = ic->user_data; + CamelFolderSummary *summary = fetch->summary; + struct imap4_envelope_t *envelope; + GPtrArray *added = fetch->added; + CamelIMAP4MessageInfo *iinfo; + CamelMessageInfo *info; + char uid[12]; + + if (index > added->len) + g_ptr_array_set_size (added, index); + + if (!(envelope = added->pdata[index - 1])) { + iinfo = (CamelIMAP4MessageInfo *) info = camel_folder_summary_info_new (summary); + envelope = g_new (struct imap4_envelope_t, 1); + added->pdata[index - 1] = envelope; + envelope->info = info; + envelope->changed = 0; + } else { + iinfo = (CamelIMAP4MessageInfo *) info = envelope->info; + } + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + /* parse the FETCH response list */ + if (token->token != '(') { + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + return -1; + } + + do { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token == ')' || token->token == '\n') + break; + + if (token->token != CAMEL_IMAP4_TOKEN_ATOM) + goto unexpected; + + if (!strcmp (token->v.atom, "ENVELOPE")) { + if (decode_envelope (engine, info, token, ex) == -1) + goto exception; + + envelope->changed |= IMAP4_FETCH_ENVELOPE; + } else if (!strcmp (token->v.atom, "FLAGS")) { + guint32 server_flags = 0; + + if (camel_imap4_parse_flags_list (engine, &server_flags, ex) == -1) + return -1; + + info->flags = camel_imap4_merge_flags (iinfo->server_flags, info->flags, server_flags); + iinfo->server_flags = server_flags; + + envelope->changed |= IMAP4_FETCH_FLAGS; + } else if (!strcmp (token->v.atom, "INTERNALDATE")) { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + switch (token->token) { + case CAMEL_IMAP4_TOKEN_NIL: + info->date_received = (time_t) -1; + break; + case CAMEL_IMAP4_TOKEN_ATOM: + case CAMEL_IMAP4_TOKEN_QSTRING: + info->date_received = decode_internaldate (token->v.qstring); + break; + default: + goto unexpected; + } + + envelope->changed |= IMAP4_FETCH_INTERNALDATE; + } else if (!strcmp (token->v.atom, "RFC822.SIZE")) { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != CAMEL_IMAP4_TOKEN_NUMBER) + goto unexpected; + + info->size = token->v.number; + + envelope->changed |= IMAP4_FETCH_RFC822SIZE; + } else if (!strcmp (token->v.atom, "UID")) { + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + goto exception; + + if (token->token != CAMEL_IMAP4_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 |= IMAP4_FETCH_UID; + } else { + /* wtf? */ + fprintf (stderr, "huh? %s?...\n", token->v.atom); + } + } while (1); + + if (token->token != ')') + goto unexpected; + + return 0; + + unexpected: + + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + + exception: + + return -1; +} + +static CamelIMAP4Command * +imap4_summary_fetch_all (CamelFolderSummary *summary, guint32 first, guint32 last) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + CamelFolder *folder = imap4_summary->folder; + struct imap4_fetch_all_t *fetch; + CamelIMAP4Engine *engine; + CamelIMAP4Command *ic; + + engine = ((CamelIMAP4Store *) folder->parent_store)->engine; + + fetch = g_new (struct imap4_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_imap4_engine_queue (engine, folder, "FETCH %u:%u (UID ALL)\r\n", first, last); + else + ic = camel_imap4_engine_queue (engine, folder, "FETCH %u:* (UID ALL)\r\n", first); + + camel_imap4_command_register_untagged (ic, "FETCH", untagged_fetch_all); + ic->user_data = fetch; + + return ic; +} + +static CamelIMAP4Command * +imap4_summary_fetch_flags (CamelFolderSummary *summary, guint32 first, guint32 last) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + CamelFolder *folder = imap4_summary->folder; + struct imap4_fetch_all_t *fetch; + CamelIMAP4Engine *engine; + CamelIMAP4Command *ic; + + engine = ((CamelIMAP4Store *) folder->parent_store)->engine; + + fetch = g_new (struct imap4_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_imap4_engine_queue (engine, folder, "FETCH %u:%u (UID FLAGS)\r\n", first, last); + else + ic = camel_imap4_engine_queue (engine, folder, "FETCH %u:* (UID FLAGS)\r\n", first); + + camel_imap4_command_register_untagged (ic, "FETCH", untagged_fetch_all); + ic->user_data = fetch; + + return ic; +} + +#if 0 +static int +imap4_build_summary (CamelFolderSummary *summary, guint32 first, guint32 last) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + CamelFolder *folder = imap4_summary->folder; + struct imap4_fetch_all_t *fetch; + CamelIMAP4Engine *engine; + CamelIMAP4Command *ic; + int id; + + engine = ((CamelIMAP4Store *) folder->store)->engine; + + ic = imap4_summary_fetch_all (summary, first, last); + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + fetch = ic->user_data; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + camel_imap4_command_unref (ic); + imap4_fetch_all_free (fetch); + return -1; + } + + imap4_fetch_all_add (fetch); + + camel_imap4_command_unref (ic); + + return 0; +} +#endif + +static CamelMessageInfo * +imap4_message_info_new (CamelFolderSummary *summary, struct _camel_header_raw *header) +{ + CamelMessageInfo *info; + + info = CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->message_info_new (summary, header); + + ((CamelIMAP4MessageInfo *) info)->server_flags = 0; + + return info; +} + +static CamelMessageInfo * +imap4_message_info_load (CamelFolderSummary *summary, FILE *fin) +{ + CamelIMAP4MessageInfo *minfo; + CamelMessageInfo *info; + + if (!(info = CAMEL_FOLDER_SUMMARY_CLASS (parent_class)->message_info_load (summary, fin))) + return NULL; + + minfo = (CamelIMAP4MessageInfo *) 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 +imap4_message_info_save (CamelFolderSummary *summary, FILE *fout, CamelMessageInfo *info) +{ + CamelIMAP4MessageInfo *minfo = (CamelIMAP4MessageInfo *) 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_imap4_summary_set_exists (CamelFolderSummary *summary, guint32 exists) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP4_SUMMARY (summary)); + + imap4_summary->exists = exists; + + imap4_summary->exists_changed = TRUE; +} + +void +camel_imap4_summary_set_recent (CamelFolderSummary *summary, guint32 recent) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP4_SUMMARY (summary)); + + imap4_summary->recent = recent; +} + +void +camel_imap4_summary_set_unseen (CamelFolderSummary *summary, guint32 unseen) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP4_SUMMARY (summary)); + + imap4_summary->unseen = unseen; +} + +void +camel_imap4_summary_set_uidnext (CamelFolderSummary *summary, guint32 uidnext) +{ + g_return_if_fail (CAMEL_IS_IMAP4_SUMMARY (summary)); + + summary->nextuid = uidnext; +} + +void +camel_imap4_summary_set_uidvalidity (CamelFolderSummary *summary, guint32 uidvalidity) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + + g_return_if_fail (CAMEL_IS_IMAP4_SUMMARY (summary)); + + if (imap4_summary->uidvalidity == uidvalidity) + return; + + /* FIXME: emit a signal or something first? */ + camel_folder_summary_clear (summary); + + imap4_summary->uidvalidity = uidvalidity; + + imap4_summary->uidvalidity_changed = TRUE; +} + +void +camel_imap4_summary_expunge (CamelFolderSummary *summary, int seqid) +{ + CamelMessageInfo *info; + + g_return_if_fail (CAMEL_IS_IMAP4_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_imap4_summary_flush_updates (CamelFolderSummary *summary, CamelException *ex) +{ + CamelIMAP4Summary *imap4_summary = (CamelIMAP4Summary *) summary; + CamelIMAP4Engine *engine; + CamelIMAP4Command *ic; + guint32 first = 0; + int scount, id; + + g_return_val_if_fail (CAMEL_IS_IMAP4_SUMMARY (summary), -1); + + engine = ((CamelIMAP4Store *) imap4_summary->folder->parent_store)->engine; + + if (imap4_summary->uidvalidity_changed) { + first = 1; + } else if (imap4_summary->exists_changed) { + scount = camel_folder_summary_count (summary); + ic = imap4_summary_fetch_flags (summary, 1, scount); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + imap4_fetch_all_free (ic->user_data); + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return -1; + } + + if (!(first = imap4_fetch_all_update (ic->user_data)) && imap4_summary->exists > scount) + first = scount + 1; + + camel_imap4_command_unref (ic); + } + + if (first != 0) { + ic = imap4_summary_fetch_all (summary, first, 0); + + while ((id = camel_imap4_engine_iterate (engine)) < ic->id && id != -1) + ; + + if (id == -1 || ic->status != CAMEL_IMAP4_COMMAND_COMPLETE) { + imap4_fetch_all_free (ic->user_data); + camel_exception_xfer (ex, &ic->ex); + camel_imap4_command_unref (ic); + return -1; + } + + imap4_fetch_all_add (ic->user_data); + camel_imap4_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); + } + + imap4_summary->exists_changed = FALSE; + imap4_summary->uidvalidity_changed = FALSE; + + return 0; +} diff --git a/camel/providers/imap4/camel-imap4-summary.h b/camel/providers/imap4/camel-imap4-summary.h new file mode 100644 index 0000000000..6b5e906960 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-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_IMAP4_SUMMARY_H__ +#define __CAMEL_IMAP4_SUMMARY_H__ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_TYPE_IMAP4_SUMMARY (camel_imap4_summary_get_type ()) +#define CAMEL_IMAP4_SUMMARY(obj) (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP4_SUMMARY, CamelIMAP4Summary)) +#define CAMEL_IMAP4_SUMMARY_CLASS(klass) (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP4_SUMMARY, CamelIMAP4SummaryClass)) +#define CAMEL_IS_IMAP4_SUMMARY(obj) (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP4_SUMMARY)) +#define CAMEL_IS_IMAP4_SUMMARY_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP4_SUMMARY)) +#define CAMEL_IMAP4_SUMMARY_GET_CLASS(obj) (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_FOLDER_SUMMARY, CamelIMAP4SummaryClass)) + +typedef struct _CamelIMAP4MessageInfo CamelIMAP4MessageInfo; +typedef struct _CamelIMAP4Summary CamelIMAP4Summary; +typedef struct _CamelIMAP4SummaryClass CamelIMAP4SummaryClass; + +struct _CamelIMAP4MessageInfo { + CamelMessageInfo parent_info; + + guint32 server_flags; +}; + +struct _CamelIMAP4Summary { + CamelFolderSummary parent_object; + + CamelFolder *folder; + + guint32 exists; + guint32 recent; + guint32 unseen; + + guint32 uidvalidity; + + guint uidvalidity_changed:1; + guint exists_changed:1; +}; + +struct _CamelIMAP4SummaryClass { + CamelFolderSummaryClass parent_class; + +}; + + +CamelType camel_imap4_summary_get_type (void); + +CamelFolderSummary *camel_imap4_summary_new (CamelFolder *folder); + +void camel_imap4_summary_set_exists (CamelFolderSummary *summary, guint32 exists); +void camel_imap4_summary_set_recent (CamelFolderSummary *summary, guint32 recent); +void camel_imap4_summary_set_unseen (CamelFolderSummary *summary, guint32 unseen); +void camel_imap4_summary_set_uidnext (CamelFolderSummary *summary, guint32 uidnext); + +void camel_imap4_summary_set_uidvalidity (CamelFolderSummary *summary, guint32 uidvalidity); + +void camel_imap4_summary_expunge (CamelFolderSummary *summary, int seqid); + +int camel_imap4_summary_flush_updates (CamelFolderSummary *summary, CamelException *ex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_SUMMARY_H__ */ diff --git a/camel/providers/imap4/camel-imap4-utils.c b/camel/providers/imap4/camel-imap4-utils.c new file mode 100644 index 0000000000..67dd828134 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-utils.c @@ -0,0 +1,315 @@ +/* -*- 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 +#endif + +#include +#include +#include + +#include + +#include "camel-imap4-engine.h" +#include "camel-imap4-stream.h" +#include "camel-imap4-command.h" + +#include "camel-imap4-utils.h" + +#define d(x) x + + +void +camel_imap4_flags_diff (flags_diff_t *diff, guint32 old, guint32 new) +{ + diff->changed = old ^ new; + diff->bits = new & diff->changed; +} + + +guint32 +camel_imap4_flags_merge (flags_diff_t *diff, guint32 flags) +{ + return (flags & ~diff->changed) | diff->bits; +} + + +/** + * camel_imap4_merge_flags: + * @original: original server flags + * @local: local flags (after changes) + * @server: new server flags (another client updated the server flags) + * + * Merge the local flag changes into the new server flags. + * + * Returns the merged flags. + **/ +guint32 +camel_imap4_merge_flags (guint32 original, guint32 local, guint32 server) +{ + flags_diff_t diff; + + camel_imap4_flags_diff (&diff, original, local); + + return camel_imap4_flags_merge (&diff, server); +} + + +void +camel_imap4_utils_set_unexpected_token_error (CamelException *ex, CamelIMAP4Engine *engine, camel_imap4_token_t *token) +{ + GString *errmsg; + + if (ex == NULL) + return; + + errmsg = g_string_new (""); + g_string_append_printf (errmsg, _("Unexpected token in response from IMAP server %s: "), + engine->url->host); + + switch (token->token) { + case CAMEL_IMAP4_TOKEN_NIL: + g_string_append (errmsg, "NIL"); + break; + case CAMEL_IMAP4_TOKEN_ATOM: + g_string_append (errmsg, token->v.atom); + break; + case CAMEL_IMAP4_TOKEN_FLAG: + g_string_append (errmsg, token->v.flag); + break; + case CAMEL_IMAP4_TOKEN_QSTRING: + g_string_append (errmsg, token->v.qstring); + break; + case CAMEL_IMAP4_TOKEN_LITERAL: + g_string_append_printf (errmsg, "{%u}", token->v.literal); + break; + case CAMEL_IMAP4_TOKEN_NUMBER: + g_string_append_printf (errmsg, "%u", token->v.number); + break; + case CAMEL_IMAP4_TOKEN_NO_DATA: + g_string_append (errmsg, _("No data")); + break; + default: + g_string_append_c (errmsg, (unsigned char) (token->token & 0xff)); + break; + } + + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, errmsg->str); + + g_string_free (errmsg, TRUE); +} + + +static struct { + const char *name; + guint32 flag; +} imap4_flags[] = { + { "\\Answered", CAMEL_MESSAGE_ANSWERED }, + { "\\Deleted", CAMEL_MESSAGE_DELETED }, + { "\\Draft", CAMEL_MESSAGE_DRAFT }, + { "\\Flagged", CAMEL_MESSAGE_FLAGGED }, + { "\\Seen", CAMEL_MESSAGE_SEEN }, + /*{ "\\Recent", CAMEL_MESSAGE_RECENT },*/ + { "\\*", CAMEL_MESSAGE_USER }, +}; + +#if 0 +static struct { + const char *name; + guint32 flag; +} imap4_user_flags[] = { + { "Forwarded", CAMEL_MESSAGE_FORWARDED }, +}; +#endif + + +int +camel_imap4_parse_flags_list (CamelIMAP4Engine *engine, guint32 *flags, CamelException *ex) +{ + camel_imap4_token_t token; + guint32 new = 0; + int i; + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + if (token.token != '(') { + d(fprintf (stderr, "Expected to find a '(' token starting the flags list\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + + while (token.token == CAMEL_IMAP4_TOKEN_ATOM || token.token == CAMEL_IMAP4_TOKEN_FLAG) { + /* parse the flags list */ + for (i = 0; i < G_N_ELEMENTS (imap4_flags); i++) { + if (!strcasecmp (imap4_flags[i].name, token.v.atom)) { + new |= imap4_flags[i].flag; + break; + } + } + +#if 0 + if (i == G_N_ELEMENTS (imap4_flags)) { + for (i = 0; i < G_N_ELEMENTS (imap4_user_flags); i++) { + if (!strcasecmp (imap4_user_flags[i].name, token.v.atom)) { + new |= imap4_user_flags[i].flag; + break; + } + } + + if (i == G_N_ELEMENTS (imap4_user_flags)) + fprintf (stderr, "Encountered unknown flag: %s\n", token.v.atom); + } +#else + if (i == G_N_ELEMENTS (imap4_flags)) + fprintf (stderr, "Encountered unknown flag: %s\n", token.v.atom); +#endif + + if (camel_imap4_engine_next_token (engine, &token, ex) == -1) + return -1; + } + + if (token.token != ')') { + d(fprintf (stderr, "Expected to find a ')' token terminating the flags list\n")); + camel_imap4_utils_set_unexpected_token_error (ex, engine, &token); + return -1; + } + + *flags = new; + + return 0; +} + + +struct { + const char *name; + guint32 flag; +} list_flags[] = { + { "\\Marked", CAMEL_IMAP4_FOLDER_MARKED }, + { "\\Unmarked", CAMEL_IMAP4_FOLDER_UNMARKED }, + { "\\Noselect", CAMEL_FOLDER_NOSELECT }, + { "\\Noinferiors", CAMEL_FOLDER_NOINFERIORS }, + { "\\HasChildren", CAMEL_FOLDER_CHILDREN }, + { "\\HasNoChildren", CAMEL_FOLDER_NOCHILDREN }, +}; + +int +camel_imap4_untagged_list (CamelIMAP4Engine *engine, CamelIMAP4Command *ic, guint32 index, camel_imap4_token_t *token, CamelException *ex) +{ + GPtrArray *array = ic->user_data; + camel_imap4_list_t *list; + unsigned char *buf; + guint32 flags = 0; + GString *literal; + char delim; + size_t n; + int i; + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + /* parse the flag list */ + if (token->token != '(') + goto unexpected; + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + while (token->token == CAMEL_IMAP4_TOKEN_FLAG || token->token == CAMEL_IMAP4_TOKEN_ATOM) { + for (i = 0; i < G_N_ELEMENTS (list_flags); i++) { + if (!g_ascii_strcasecmp (list_flags[i].name, token->v.atom)) { + flags |= list_flags[i].flag; + break; + } + } + + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + } + + if (token->token != ')') + goto unexpected; + + /* parse the path delimiter */ + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + switch (token->token) { + case CAMEL_IMAP4_TOKEN_NIL: + delim = '\0'; + break; + case CAMEL_IMAP4_TOKEN_QSTRING: + delim = *token->v.qstring; + break; + default: + goto unexpected; + } + + /* parse the folder name */ + if (camel_imap4_engine_next_token (engine, token, ex) == -1) + return -1; + + list = g_new (camel_imap4_list_t, 1); + list->flags = flags; + list->delim = delim; + + switch (token->token) { + case CAMEL_IMAP4_TOKEN_ATOM: + list->name = g_strdup (token->v.atom); + break; + case CAMEL_IMAP4_TOKEN_QSTRING: + list->name = g_strdup (token->v.qstring); + break; + case CAMEL_IMAP4_TOKEN_LITERAL: + literal = g_string_new (""); + while ((i = camel_imap4_stream_literal (engine->istream, &buf, &n)) == 1) + g_string_append_len (literal, buf, n); + + if (i == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("IMAP server %s unexpectedly disconnected: %s"), + engine->url->host, errno ? g_strerror (errno) : _("Unknown")); + g_string_free (literal, TRUE); + return -1; + } + + g_string_append_len (literal, buf, n); + list->name = literal->str; + g_string_free (literal, FALSE); + break; + default: + g_free (list); + goto unexpected; + } + + g_ptr_array_add (array, list); + + return camel_imap4_engine_eat_line (engine, ex); + + unexpected: + + camel_imap4_utils_set_unexpected_token_error (ex, engine, token); + + return -1; +} diff --git a/camel/providers/imap4/camel-imap4-utils.h b/camel/providers/imap4/camel-imap4-utils.h new file mode 100644 index 0000000000..ace7b4cad0 --- /dev/null +++ b/camel/providers/imap4/camel-imap4-utils.h @@ -0,0 +1,67 @@ +/* -*- 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_IMAP4_UTILS_H__ +#define __CAMEL_IMAP4_UTILS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +/* IMAP4 flag merging */ +typedef struct { + guint32 changed; + guint32 bits; +} flags_diff_t; + +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; + +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); + +/* Note: make sure these don't clash with any bit flags in camel-store.h */ +#define CAMEL_IMAP4_FOLDER_MARKED (1 << 17) +#define CAMEL_IMAP4_FOLDER_UNMARKED (1 << 18) + +typedef struct { + guint32 flags; + char delim; + char *name; +} camel_imap4_list_t; + +int camel_imap4_untagged_list (struct _CamelIMAP4Engine *engine, struct _CamelIMAP4Command *ic, + guint32 index, struct _camel_imap4_token_t *token, CamelException *ex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAMEL_IMAP4_UTILS_H__ */ -- cgit v1.2.3