diff options
Diffstat (limited to 'camel/providers/imap')
-rw-r--r-- | camel/providers/imap/Makefile.am | 4 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-command.c | 391 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-command.h | 58 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-folder.c | 372 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store.c | 962 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store.h | 44 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-utils.c | 74 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-utils.h | 2 |
8 files changed, 713 insertions, 1194 deletions
diff --git a/camel/providers/imap/Makefile.am b/camel/providers/imap/Makefile.am index 5ff249739f..15e1b850e8 100644 --- a/camel/providers/imap/Makefile.am +++ b/camel/providers/imap/Makefile.am @@ -20,16 +20,16 @@ INCLUDES = -I.. \ -DG_LOG_DOMAIN=\"camel-imap-provider\" libcamelimap_la_SOURCES = \ + camel-imap-command.c \ camel-imap-folder.c \ camel-imap-provider.c \ camel-imap-store.c \ - camel-imap-stream.c \ camel-imap-utils.c libcamelimapinclude_HEADERS = \ + camel-imap-command.h \ camel-imap-folder.h \ camel-imap-store.h \ - camel-imap-stream.h \ camel-imap-utils.h libcamelimap_la_LDFLAGS = -version-info 0:0:0 diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c new file mode 100644 index 0000000000..e06de34db4 --- /dev/null +++ b/camel/providers/imap/camel-imap-command.c @@ -0,0 +1,391 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-imap-command.c: IMAP command sending/parsing routines */ + +/* + * Authors: + * Dan Winship <danw@helixcode.com> + * Jeffrey Stedfast <fejj@helixcode.com> + * + * Copyright 2000 Helix Code, Inc. (www.helixcode.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + + +#include <config.h> + +#include <stdio.h> +#include <string.h> + +#include "camel-imap-command.h" +#include "camel-imap-utils.h" +#include "camel-imap-folder.h" +#include <camel/camel-exception.h> + +static char *imap_read_untagged (CamelImapStore *store, char *line, + CamelException *ex); +static CamelImapResponse *imap_read_response (CamelImapStore *store, + CamelException *ex); + +/** + * camel_imap_command: Send a command to a IMAP server and get a response + * @store: the IMAP store + * @folder: The folder to perform the operation in (or %NULL if not + * relevant). + * @ex: a CamelException + * @fmt: a printf-style format string, followed by arguments + * + * This camel method sends the IMAP command specified by @fmt and the + * following arguments to the IMAP store specified by @store. It then + * reads the server's response(s) and parses the final result. + * + * Return value: %NULL if an error occurred (in which case @ex will + * be set). Otherwise, a CamelImapResponse describing the server's + * response, which the caller must free with camel_imap_response_free(). + **/ +CamelImapResponse * +camel_imap_command (CamelImapStore *store, CamelFolder *folder, + CamelException *ex, const char *fmt, ...) +{ + gchar *cmdbuf; + va_list ap; + + /* Check for current folder */ + if (folder && folder != store->current_folder) { + char *folder_path; + CamelImapResponse *response; + + folder_path = camel_imap_store_folder_path (store, + folder->full_name); + response = camel_imap_command (store, NULL, ex, + "SELECT %s", folder_path); + g_free (folder_path); + + if (!response) { + store->current_folder = NULL; + return NULL; + } + camel_imap_response_free (response); + + store->current_folder = folder; + } + + /* Send the command */ + va_start (ap, fmt); + cmdbuf = g_strdup_vprintf (fmt, ap); + va_end (ap); + + camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, + "A%.5d %s\r\n", store->command++, + cmdbuf); + g_free (cmdbuf); + if (camel_exception_is_set (ex)) + return NULL; + + /* Read the response. */ + return imap_read_response (store, ex); +} + +/** + * camel_imap_command_continuation: Send more command data to the IMAP server + * @store: the IMAP store + * @ex: a CamelException + * @cmdbuf: buffer containing the response/request data + * + * This method is for sending continuing responses to the IMAP server + * after camel_imap_command returns a CAMEL_IMAP_PLUS response. + * + * Return value: as for camel_imap_command() + **/ +CamelImapResponse * +camel_imap_command_continuation (CamelImapStore *store, CamelException *ex, + const char *cmdbuf) +{ + if (camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, + "%s\r\n", cmdbuf) < 0) + return NULL; + + return imap_read_response (store, ex); +} + +/* Read the response to an IMAP command. */ +static CamelImapResponse * +imap_read_response (CamelImapStore *store, CamelException *ex) +{ + CamelImapResponse *response; + int number, recent = 0; + GArray *expunged = NULL; + char *respbuf, *retcode, *p; + + /* Read first line */ + if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), + &respbuf, ex) < 0) + return NULL; + + response = g_new0 (CamelImapResponse, 1); + response->untagged = g_ptr_array_new (); + + /* Check for untagged data */ + while (!strncmp (respbuf, "* ", 2)) { + /* Read the rest of the response if it is multi-line. */ + respbuf = imap_read_untagged (store, respbuf, ex); + if (camel_exception_is_set (ex)) + break; + + /* If it starts with a number, we might deal with + * it ourselves. + */ + number = strtoul (respbuf + 2, &p, 10); + if (p != respbuf + 2) { + p = imap_next_word (p); + if (!g_strcasecmp (p, "RECENT")) { + recent = number; + g_free (respbuf); + goto next; + } else if (!g_strcasecmp (p, "EXPUNGE")) { + if (!expunged) { + expunged = g_array_new (FALSE, FALSE, + sizeof (int)); + } + g_array_append_val (expunged, number); + g_free (respbuf); + goto next; + } + } + + g_ptr_array_add (response->untagged, respbuf); + next: + if (camel_remote_store_recv_line ( + CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) + break; + } + + /* Update the summary */ + if (store->current_folder && (recent > 0 || expunged)) { + camel_imap_folder_changed (store->current_folder, recent, + expunged, NULL); + } + if (expunged) + g_array_free (expunged, TRUE); + + if (camel_exception_is_set (ex)) { + camel_imap_response_free (response); + return NULL; + } + + response->status = respbuf; + + /* Check for OK or continuation response. */ + if (!strncmp (respbuf, "+ ", 2)) + return response; + retcode = imap_next_word (respbuf); + if (!strncmp (retcode, "OK", 2)) + return response; + + /* We should never get BAD, or anything else but +, OK, or NO + * for that matter. + */ + if (strncmp (retcode, "NO", 2) != 0) { + g_warning ("Unexpected response from IMAP server: %s", + respbuf); + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Unexpected response from IMAP server: " + "%s", respbuf); + camel_imap_response_free (response); + return NULL; + } + + retcode = imap_next_word (retcode); + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "IMAP command failed: %s", + retcode ? retcode : "Unknown error"); + camel_imap_response_free (response); + return NULL; +} + +/* Given a line that is the start of an untagged response, read and + * return the complete response. (This will be a no-op if the line + * in question doesn't end with a literal.) + * + * FIXME: this won't deal with multiple literals in a single response. + */ +static char * +imap_read_untagged (CamelImapStore *store, char *line, CamelException *ex) +{ + int fulllen, length, left, i; + GPtrArray *data; + char *end, *p; + + p = strrchr (line, '{'); + if (!p) + return line; + + length = strtoul (p + 1, &end, 10); + if (*end != '}' || *(end + 1) || end == p + 1) + return line; + + fulllen = length + strlen (line) + 1; + + /* OK. We have a literal. @length is the length including CRLF + * pairs, which camel_remote_store_recv_line won't return. + */ + data = g_ptr_array_new (); + g_ptr_array_add (data, line); + left = length; + while (1) { + if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), + &line, ex) < 0) { + for (i = 0; i < data->len; i++) + g_free (data->pdata[i]); + g_ptr_array_free (data, TRUE); + return NULL; + } + g_ptr_array_add (data, line); + + if (left <= 0) + break; + + left -= strlen (line) + 2; + + /* The output string will have only LF, not CRLF, so + * decrement the length by one. + */ + length--; + } + + /* p points to the "{" in the line that starts the literal. + * The length of the CR-less response must be less than or + * equal to the length of the response with CRs, therefore + * overwriting the old value with the new value cannot cause + * an overrun. + */ + sprintf (p, "{%d}", length); + + /* Now reassemble the data. */ + p = line = g_malloc (fulllen + 1); + for (i = 0; i < data->len; i++) { + length = strlen (data->pdata[i]); + memcpy (p, data->pdata[i], length); + g_free (data->pdata[i]); + p += length; + *p++ = '\n'; + } + *p = '\0'; + g_ptr_array_free (data, TRUE); + return line; +} + + +/** + * camel_imap_response_free: + * response: a CamelImapResponse: + * + * Frees all of the data in @response. + **/ +void +camel_imap_response_free (CamelImapResponse *response) +{ + int i; + + if (!response) + return; + for (i = 0; i < response->untagged->len; i++) + g_free (response->untagged->pdata[i]); + g_ptr_array_free (response->untagged, TRUE); + g_free (response->status); + g_free (response); +} + +/** + * camel_imap_response_extract: + * @response: the response data returned from camel_imap_command + * @type: the response type to extract + * @ex: a CamelException + * + * This checks that @response contains a single untagged response of + * type @type and returns just that response data. If @response + * doesn't contain the right information, the function will set @ex and + * return %NULL. Either way, @response will be freed. + * + * Return value: the desired response string, which the caller must free. + **/ +char * +camel_imap_response_extract (CamelImapResponse *response, const char *type, + CamelException *ex) +{ + int len = strlen (type), i; + char *resp; + + for (i = 0; i < response->untagged->len; i++) { + resp = response->untagged->pdata[i]; + /* Skip "* ", and initial sequence number, if present */ + strtoul (resp + 2, &resp, 10); + if (*resp == ' ') + resp++; + + if (!g_strncasecmp (resp, type, len)) + break; + g_free (resp); + } + + if (i < response->untagged->len) { + resp = response->untagged->pdata[i]; + for (i++; i < response->untagged->len; i++) + g_free (response->untagged->pdata[i]); + } else { + resp = NULL; + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "IMAP server response did not contain " + "%s information", type); + } + + g_ptr_array_free (response->untagged, TRUE); + g_free (response->status); + g_free (response); + return resp; +} + +/** + * camel_imap_response_extract_continuation: + * @response: the response data returned from camel_imap_command + * @ex: a CamelException + * + * This checks that @response contains a continuation response, and + * returns just that data. If @response doesn't contain a continuation + * response, the function will set @ex and return %NULL. Either way, + * @response will be freed. + * + * Return value: the desired response string, which the caller must free. + **/ +char * +camel_imap_response_extract_continuation (CamelImapResponse *response, + CamelException *ex) +{ + char *status; + + if (response->status && !strncmp (response->status, "+ ", 2)) { + status = response->status; + response->status = NULL; + camel_imap_response_free (response); + return status; + } + + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Unexpected OK response from IMAP server: %s", + response->status); + camel_imap_response_free (response); + return NULL; +} diff --git a/camel/providers/imap/camel-imap-command.h b/camel/providers/imap/camel-imap-command.h new file mode 100644 index 0000000000..38e290c379 --- /dev/null +++ b/camel/providers/imap/camel-imap-command.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-imap-command.h: IMAP command sending/parsing routines */ + +/* + * Authors: + * Dan Winship <danw@helixcode.com> + * Jeffrey Stedfast <fejj@helixcode.com> + * + * Copyright (C) 2000 Helix Code, Inc. + * + * 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 Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + + +#ifndef CAMEL_IMAP_COMMAND_H +#define CAMEL_IMAP_COMMAND_H 1 + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +#include "camel-imap-store.h" + +typedef struct { + GPtrArray *untagged; + char *status; +} CamelImapResponse; + +CamelImapResponse *camel_imap_command (CamelImapStore *store, + CamelFolder *folder, + CamelException *ex, + const char *fmt, ...); +CamelImapResponse *camel_imap_command_continuation (CamelImapStore *store, + CamelException *ex, + const char *cmdbuf); + +void camel_imap_response_free (CamelImapResponse *response); +char *camel_imap_response_extract (CamelImapResponse *response, + const char *type, + CamelException *ex); +char *camel_imap_response_extract_continuation (CamelImapResponse *response, + CamelException *ex); + +#endif /* CAMEL_IMAP_COMMAND_H */ diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index ab2d7f88b9..21f4d3bfc2 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -37,6 +37,7 @@ #include <gal/util/e-util.h> #include "camel-imap-folder.h" +#include "camel-imap-command.h" #include "camel-imap-store.h" #include "camel-imap-stream.h" #include "camel-imap-utils.h" @@ -236,6 +237,7 @@ imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); + CamelImapResponse *response; gint i, max; if (expunge) { @@ -249,20 +251,20 @@ imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) for (i = 0; i < max; i++) { CamelMessageInfo *info; - info = (CamelMessageInfo *) g_ptr_array_index (imap_folder->summary, i); + info = g_ptr_array_index (imap_folder->summary, i); if (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) { char *flags; flags = imap_create_flag_list (info->flags); if (flags) { - gint s; - - s = camel_imap_command_extended (store, folder, NULL, ex, - "UID STORE %s FLAGS.SILENT %s", - info->uid, flags); - if (s != CAMEL_IMAP_OK) - return; + response = camel_imap_command ( + store, folder, ex, + "UID STORE %s FLAGS.SILENT %s", + info->uid, flags); g_free (flags); + if (!response) + return; + camel_imap_response_free (response); } info->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; } @@ -273,30 +275,32 @@ imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) static void imap_expunge (CamelFolder *folder, CamelException *ex) { + CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapResponse *response; + imap_sync (folder, FALSE, ex); - camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, NULL, ex, "EXPUNGE"); + response = camel_imap_command (store, folder, ex, "EXPUNGE"); + camel_imap_response_free (response); } static gint imap_get_message_count_internal (CamelFolder *folder, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); - gchar *result, *msg_count, *folder_path; - GPtrArray *response; - gint status, count = 0; + char *result, *msg_count, *folder_path; + CamelImapResponse *response; + int count = 0; folder_path = camel_imap_store_folder_path (store, folder->full_name); - if (store->has_status_capability) - status = camel_imap_command_extended (store, folder, &response, ex, - "STATUS \"%s\" (MESSAGES)", folder_path); + response = camel_imap_command (store, folder, ex, + "STATUS \"%s\" (MESSAGES)", + folder_path); else - status = camel_imap_command_extended (store, folder, &response, ex, - "EXAMINE \"%s\"", folder_path); - + response = camel_imap_command (store, folder, ex, + "EXAMINE \"%s\"", folder_path); g_free (folder_path); - - if (status != CAMEL_IMAP_OK) + if (!response) return 0; /* parse out the message count */ @@ -368,14 +372,16 @@ imap_get_unread_message_count (CamelFolder *folder) } static void -imap_append_message (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex) +imap_append_message (CamelFolder *folder, CamelMimeMessage *message, + const CamelMessageInfo *info, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapResponse *response; CamelStream *memstream; + CamelMimeFilter *crlf_filter; + CamelStreamFilter *streamfilter; GByteArray *ba; - gchar *cmdid; - gchar *folder_path, *flagstr; - gint status; + char *folder_path, *flagstr, *result; folder_path = camel_imap_store_folder_path (store, folder->full_name); @@ -384,74 +390,83 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message, const Camel flagstr = imap_create_flag_list (info->flags); else flagstr = NULL; - + + /* FIXME: We could avoid this if we knew how big the message was. */ + memstream = camel_stream_mem_new (); ba = g_byte_array_new (); - memstream = camel_stream_mem_new_with_byte_array (ba); - /* FIXME: we need to crlf/dot filter */ - camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), memstream); - camel_stream_write_string (memstream, "\r\n"); - camel_stream_reset (memstream); - - status = camel_imap_command_preliminary (store, &cmdid, ex, "APPEND %s%s%s {%d}", - folder_path, flagstr ? " " : "", - flagstr ? flagstr : "", ba->len - 2); + camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (memstream), ba); + + streamfilter = camel_stream_filter_new_with_stream (memstream); + crlf_filter = camel_mime_filter_crlf_new ( + CAMEL_MIME_FILTER_CRLF_ENCODE, + CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + camel_stream_filter_add (streamfilter, crlf_filter); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), + CAMEL_STREAM (streamfilter)); + camel_object_unref (CAMEL_OBJECT (streamfilter)); + camel_object_unref (CAMEL_OBJECT (crlf_filter)); + camel_object_unref (CAMEL_OBJECT (memstream)); + + response = camel_imap_command (store, NULL, ex, "APPEND %s%s%s {%d}", + folder_path, flagstr ? " " : "", + flagstr ? flagstr : "", ba->len); g_free (folder_path); g_free (flagstr); - if (status != CAMEL_IMAP_PLUS) { - g_free (cmdid); - camel_object_unref (CAMEL_OBJECT (memstream)); + if (!response) { + g_byte_array_free (ba, TRUE); return; } - + result = camel_imap_response_extract_continuation (response, ex); + if (!result) { + g_byte_array_free (ba, TRUE); + return; + } + g_free (result); + /* send the rest of our data - the mime message */ - status = camel_imap_command_continuation_with_stream (store, NULL, cmdid, memstream, ex); - g_free (cmdid); - - if (status != CAMEL_IMAP_OK) + g_byte_array_append (ba, "\0", 3); + response = camel_imap_command_continuation (store, ex, ba->data); + g_byte_array_free (ba, TRUE); + if (!response) return; - - camel_object_unref (CAMEL_OBJECT (memstream)); - camel_imap_folder_changed (folder, 1, NULL, ex); + camel_imap_response_free (response); } static void -imap_copy_message_to (CamelFolder *source, const char *uid, CamelFolder *destination, CamelException *ex) +imap_copy_message_to (CamelFolder *source, const char *uid, + CamelFolder *destination, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); + CamelImapResponse *response; char *folder_path; - int status; folder_path = camel_imap_store_folder_path (store, destination->full_name); - status = camel_imap_command_extended (store, source, NULL, ex, - "UID COPY %s %s", uid, folder_path); + response = camel_imap_command (store, source, ex, "UID COPY %s %s", + uid, folder_path); + camel_imap_response_free (response); g_free (folder_path); - - if (status != CAMEL_IMAP_OK) - return; - - camel_imap_folder_changed (destination, 1, NULL, ex); } /* FIXME: Duplication of code! */ static void -imap_move_message_to (CamelFolder *source, const char *uid, CamelFolder *destination, CamelException *ex) +imap_move_message_to (CamelFolder *source, const char *uid, + CamelFolder *destination, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); + CamelImapResponse *response; char *folder_path; - int status; - + folder_path = camel_imap_store_folder_path (store, destination->full_name); - status = camel_imap_command_extended (store, source, NULL, ex, - "UID COPY %s %s", uid, folder_path); + response = camel_imap_command (store, source, ex, "UID COPY %s %s", + uid, folder_path); + camel_imap_response_free (response); g_free (folder_path); - - if (status != CAMEL_IMAP_OK) + + if (camel_exception_is_set (ex)) return; - + camel_folder_delete_message (source, uid); - - camel_imap_folder_changed (destination, 1, NULL, ex); } static GPtrArray * @@ -480,142 +495,41 @@ static CamelMimeMessage * imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); - CamelStream *msgstream = NULL; - CamelMimeMessage *msg = NULL; - gchar *result, *header, *body, *mesg, *p, *q, *data_item; - int status, part_len; - - if (store->server_level >= IMAP_LEVEL_IMAP4REV1) - data_item = "BODY.PEEK[HEADER]"; - else - data_item = "RFC822.HEADER"; - - status = camel_imap_fetch_command (store, folder, &result, ex, - "UID FETCH %s %s", uid, data_item); - - if (!result || status != CAMEL_IMAP_OK) - return NULL; - - /* parse out the message part */ - for (p = result; *p && *p != '{' && *p != '"' && *p != '\n'; p++); - switch (*p) { - case '"': - /* a quoted string - section 4.3 */ - p++; - for (q = p; *q && *q != '"' && *q != '\n'; q++); - part_len = (gint) (q - p); - - break; - case '{': - /* a literal string - section 4.3 */ - part_len = atoi (p + 1); - for ( ; *p && *p != '\n'; p++); - if (*p != '\n') { - g_free (result); - return NULL; - } - - /* advance to the beginning of the actual data */ - p++; - - /* calculate the new part-length */ - for (q = p; *q && (q - p) <= part_len; q++) { - if (*q == '\n') - part_len--; - } - - /* FIXME: This is a hack for IMAP daemons that send us a UID at the end of each FETCH */ - for ( ; q > p && *(q-1) != '\n'; q--, part_len--); - part_len++; - - break; - default: - /* Bad input */ - g_free (result); + CamelImapResponse *response; + CamelStream *msgstream; + CamelMimeMessage *msg; + char *result, *mesg, *p; + int len; + + response = camel_imap_command (store, folder, ex, + "UID FETCH %s RFC822", uid); + if (!response) return NULL; - } - - header = g_strndup (p, part_len); - - g_free (result); - d(fprintf (stderr, "*** We got the header ***\n")); - - if (store->server_level >= IMAP_LEVEL_IMAP4REV1) - data_item = "BODY[TEXT]"; - else - data_item = "RFC822.TEXT"; - - status = camel_imap_fetch_command (store, folder, &result, ex, - "UID FETCH %s %s", uid, data_item); - - if (!result || status != CAMEL_IMAP_OK) { - g_free (header); + result = camel_imap_response_extract (response, "FETCH", ex); + if (!result) return NULL; + + p = strstr (result, "RFC822"); + if (p) { + p += 7; + mesg = imap_parse_nstring (&p, &len); } - - /* parse out the message part */ - for (p = result; *p && *p != '{' && *p != '"' && *p != '\n'; p++); - switch (*p) { - case '"': - /* a quoted string - section 4.3 */ - p++; - for (q = p; *q && *q != '"' && *q != '\n'; q++); - part_len = (gint) (q - p); - - break; - case '{': - /* a literal string - section 4.3 */ - part_len = atoi (p + 1); - for ( ; *p && *p != '\n'; p++); - if (*p != '\n') { - g_free (result); - g_free (header); - return NULL; - } - - /* advance to the beginning of the actual data */ - p++; - - /* calculate the new part-length */ - for (q = p; *q && (q - p) <= part_len; q++) { - if (*q == '\n') - part_len--; - } - - /* FIXME: This is a hack for IMAP daemons that send us a UID at the end of each FETCH */ - for ( ; q > p && *(q-1) != '\n'; q--, part_len--); - part_len++; - - break; - default: - /* Bad input */ + if (!p) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Could not find message body in FETCH " + "response."); g_free (result); - g_free (header); return NULL; } - - body = g_strndup (p, part_len); - g_free (result); - d(fprintf (stderr, "*** We got the body ***\n")); - - mesg = g_strdup_printf ("%s\n%s", header, body); - g_free (header); - g_free (body); - d(fprintf (stderr, "*** We got the mesg ***\n")); - - d(fprintf (stderr, "Message:\n%s\n", mesg)); - - msgstream = camel_stream_mem_new_with_buffer (mesg, strlen (mesg) + 1); + + msgstream = camel_stream_mem_new_with_buffer (mesg, len); msg = camel_mime_message_new (); - d(fprintf (stderr, "*** We created the camel_mime_message ***\n")); - - camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), msgstream); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), + msgstream); camel_object_unref (CAMEL_OBJECT (msgstream)); - - d(fprintf (stderr, "*** We're returning... ***\n")); - g_free (mesg); + return msg; } @@ -673,7 +587,8 @@ imap_protocol_get_summary_specifier (CamelImapStore *store) sect_end = ""; } - return g_strdup_printf ("UID FLAGS %s (%s)%s", sect_begin, headers_wanted, sect_end); + return g_strdup_printf ("UID FLAGS %s (%s)%s", sect_begin, + headers_wanted, sect_end); } static GPtrArray * @@ -682,10 +597,11 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex) /* This ALWAYS updates the summary except on fail */ CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); + CamelImapResponse *response; GPtrArray *summary = NULL, *headers = NULL; GHashTable *hash = NULL; - gint num, i, j, status = 0; - char *result, *q, *node; + int num, i, j; + char *q; const char *received; char *summary_specifier; struct _header_raw *h = NULL, *tail = NULL; @@ -706,17 +622,18 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex) } summary_specifier = imap_protocol_get_summary_specifier (store); - if (num == 1) { - status = camel_imap_fetch_command (store, folder, &result, ex, - "FETCH 1 (%s)", summary_specifier); + response = camel_imap_command (store, folder, ex, + "FETCH 1 (%s)", + summary_specifier); } else { - status = camel_imap_fetch_command (store, folder, &result, ex, - "FETCH 1:%d (%s)", num, summary_specifier); + response = camel_imap_command (store, folder, ex, + "FETCH 1:%d (%s)", num, + summary_specifier); } g_free (summary_specifier); - if (status != CAMEL_IMAP_OK) { + if (!response) { if (!imap_folder->summary) { imap_folder->summary = g_ptr_array_new (); imap_folder->summary_hash = g_hash_table_new (g_str_hash, g_str_equal); @@ -724,32 +641,12 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex) return imap_folder->summary; } - + headers = response->untagged; + /* initialize our new summary-to-be */ summary = g_ptr_array_new (); hash = g_hash_table_new (g_str_hash, g_str_equal); - /* create our array of headers from the server response */ - headers = g_ptr_array_new (); - node = result; - for (i = 1; node; i++) { - char *end; - - if ((end = strstr (node + 2, "\n*"))) { - g_ptr_array_add (headers, g_strndup (node, (gint)(end - node))); - } else { - g_ptr_array_add (headers, g_strdup (node)); - } - node = end; - } - if (i < num) { - d(fprintf (stderr, "IMAP server didn't respond with as many headers as we expected...\n")); - /* should we error?? */ - } - - g_free (result); - result = NULL; - for (i = 0; i < headers->len; i++) { CamelMessageInfo *info; char *uid, *flags, *header; @@ -834,10 +731,7 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex) g_ptr_array_add (summary, info); g_hash_table_insert (hash, info->uid, info); } - - for (i = 0; i < headers->len; i++) - g_free (headers->pdata[i]); - g_ptr_array_free (headers, TRUE); + camel_imap_response_free (response); /* clean up any previous summary data */ imap_folder_summary_free (imap_folder); @@ -861,22 +755,24 @@ static CamelMessageInfo * imap_get_message_info_internal (CamelFolder *folder, guint id, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapResponse *response; CamelMessageInfo *info = NULL; struct _header_raw *h, *tail = NULL; const char *received; char *result, *uid, *flags, *header, *q; char *summary_specifier; - int j, status; + int j; /* we don't have a cached copy, so fetch it */ summary_specifier = imap_protocol_get_summary_specifier (store); - - status = camel_imap_fetch_command (store, folder, &result, ex, - "FETCH %d (%s)", id, summary_specifier); - + response = camel_imap_command (store, folder, ex, + "FETCH %d (%s)", id, summary_specifier); g_free (summary_specifier); - - if (status != CAMEL_IMAP_OK) + + if (!response) + return NULL; + result = camel_imap_response_extract (response, "FETCH", ex); + if (!result) return NULL; /* lets grab the UID... */ @@ -977,9 +873,9 @@ imap_get_message_info (CamelFolder *folder, const char *uid) static GPtrArray * imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex) { - GPtrArray *response, *uids = NULL; + CamelImapResponse *response; + GPtrArray *uids = NULL; char *result, *sexp, *p; - int status; d(fprintf (stderr, "camel sexp: '%s'\n", expression)); sexp = imap_translate_sexp (expression); @@ -992,12 +888,12 @@ imap_search_by_expression (CamelFolder *folder, const char *expression, CamelExc return uids; } - status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, - &response, NULL, "UID SEARCH %s", sexp); + response = camel_imap_command (CAMEL_IMAP_STORE (folder->parent_store), + folder, NULL, "UID SEARCH %s", sexp); g_free (sexp); - - if (status != CAMEL_IMAP_OK) + if (!response) return uids; + result = camel_imap_response_extract (response, "SEARCH", NULL); if (!result) return uids; diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index dcb3782e89..cb7df3d1cb 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -35,6 +35,7 @@ #include "camel-imap-store.h" #include "camel-imap-folder.h" #include "camel-imap-utils.h" +#include "camel-imap-command.h" #include "camel-folder.h" #include "camel-exception.h" #include "camel-session.h" @@ -64,8 +65,6 @@ static CamelFolderInfo *get_folder_info (CamelStore *store, const char *top, gboolean fast, gboolean recursive, CamelException *ex); static void imap_keepalive (CamelRemoteStore *store); -/*static gboolean stream_is_alive (CamelStream *istream);*/ -static int camel_imap_status (char *cmdid, char *respbuf); static void camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) @@ -189,9 +188,8 @@ imap_connect (CamelService *service, CamelException *ex) CamelImapStore *store = CAMEL_IMAP_STORE (service); CamelSession *session = camel_service_get_session (CAMEL_SERVICE (store)); gchar *result, *buf, *errbuf = NULL; - GPtrArray *response; + CamelImapResponse *response; gboolean authenticated = FALSE; - gint status; if (CAMEL_SERVICE_CLASS (remote_store_class)->connect (service, ex) == FALSE) return FALSE; @@ -237,11 +235,11 @@ imap_connect (CamelService *service, CamelException *ex) } } - status = camel_imap_command (store, NULL, ex, "LOGIN \"%s\" \"%s\"", - service->url->user, - service->url->passwd); - - if (status != CAMEL_IMAP_OK) { + response = camel_imap_command (store, NULL, ex, + "LOGIN \"%s\" \"%s\"", + service->url->user, + service->url->passwd); + if (!response) { errbuf = g_strdup_printf ("Unable to authenticate to IMAP server.\n" "%s\n\n", camel_exception_get_description (ex)); @@ -249,14 +247,14 @@ imap_connect (CamelService *service, CamelException *ex) } else { g_message ("IMAP Service sucessfully authenticated user %s", service->url->user); authenticated = TRUE; + camel_imap_response_free (response); } } /* Now lets find out the IMAP capabilities */ - status = camel_imap_command_extended (store, NULL, &response, ex, "CAPABILITY"); - if (status != CAMEL_IMAP_OK) + response = camel_imap_command (store, NULL, ex, "CAPABILITY"); + if (!response) return FALSE; - result = camel_imap_response_extract (response, "CAPABILITY", ex); if (!result) return FALSE; @@ -277,8 +275,8 @@ imap_connect (CamelService *service, CamelException *ex) g_free (result); /* We now need to find out which directory separator this daemon uses */ - status = camel_imap_command_extended (store, NULL, &response, ex, "LIST \"\" \"\""); - if (status != CAMEL_IMAP_OK) + response = camel_imap_command (store, NULL, ex, "LIST \"\" \"\""); + if (!response) return FALSE; result = camel_imap_response_extract (response, "LIST", ex); if (!result) @@ -308,10 +306,12 @@ static gboolean imap_disconnect (CamelService *service, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (service); - + CamelImapResponse *response; + /* send the logout command */ - camel_imap_command_extended (store, NULL, NULL, ex, "LOGOUT"); - + response = camel_imap_command (store, NULL, ex, "LOGOUT"); + camel_imap_response_free (response); + g_free (store->dir_sep); store->dir_sep = NULL; @@ -342,9 +342,8 @@ camel_imap_store_folder_path (CamelImapStore *store, const char *name) static gboolean imap_folder_exists (CamelImapStore *store, const char *folder_path, gboolean *selectable, CamelException *ex) { - GPtrArray *response; + CamelImapResponse *response; char *result, *flags, *sep, *dirname; - gint status; if (!g_strcasecmp (folder_path, "INBOX")) { if (selectable) @@ -356,9 +355,9 @@ imap_folder_exists (CamelImapStore *store, const char *folder_path, gboolean *se if (selectable) *selectable = FALSE; - status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), NULL, - &response, ex, "LIST \"\" \"%s\"", folder_path); - if (status != CAMEL_IMAP_OK) + response = camel_imap_command (store, NULL, ex, + "LIST \"\" \"%s\"", folder_path); + if (!response) return FALSE; result = camel_imap_response_extract (response, "LIST", ex); if (!result) @@ -387,12 +386,13 @@ imap_folder_exists (CamelImapStore *store, const char *folder_path, gboolean *se static gboolean imap_create (CamelImapStore *store, const char *folder_path, CamelException *ex) { - gint status; + CamelImapResponse *response; - status = camel_imap_command_extended (store, NULL, NULL, ex, - "CREATE \"%s\"", folder_path); - - return status == CAMEL_IMAP_OK; + response = camel_imap_command (store, NULL, ex, + "CREATE \"%s\"", folder_path); + camel_imap_response_free (response); + + return !camel_exception_is_set (ex); } static CamelFolder * @@ -485,8 +485,9 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelURL *url = CAMEL_SERVICE (store)->url; gboolean found_inbox = FALSE; - int status, len, i; - GPtrArray *response; + int len, i; + CamelImapResponse *response; + GPtrArray *folders; char *dir_sep, *namespace, *base_url, *list; CamelFolderInfo *topfi, *fi; @@ -508,9 +509,9 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, base_url[len + 1] = '\0'; } - status = camel_imap_command_extended (imap_store, NULL, &response, ex, - "LIST \"\" \"%s\"", namespace); - if (status != CAMEL_IMAP_OK) { + response = camel_imap_command (imap_store, NULL, ex, + "LIST \"\" \"%s\"", namespace); + if (!response) { g_free (namespace); g_free (base_url); return NULL; @@ -524,12 +525,11 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, topfi = parse_list_response_as_folder_info (list, namespace, base_url); g_free (list); - status = camel_imap_command_extended (imap_store, NULL, &response, ex, - "LIST \"\" \"%s%s%c\"", - namespace, - *namespace ? dir_sep : "", - recursive ? '*' : '%'); - if (status != CAMEL_IMAP_OK) { + response = camel_imap_command (imap_store, NULL, ex, + "LIST \"\" \"%s%s%c\"", + namespace, *namespace ? dir_sep : "", + recursive ? '*' : '%'); + if (!response) { g_free (namespace); g_free (base_url); return NULL; @@ -538,21 +538,19 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, /* Turn responses into CamelFolderInfo and remove any * extraneous responses. */ - for (i = 0; i < response->len; i++) { - list = response->pdata[i]; - response->pdata[i] = fi = - parse_list_response_as_folder_info (list, namespace, - base_url); - g_free (list); - - if (!response->pdata[i]) { - g_ptr_array_remove_index_fast (response, i--); + folders = g_ptr_array_new (); + for (i = 0; i < response->untagged->len; i++) { + list = response->untagged->pdata[i]; + fi = parse_list_response_as_folder_info (list, namespace, + base_url); + if (!fi) continue; - } + g_ptr_array_add (folders, fi); if (!g_strcasecmp (fi->full_name, "INBOX")) found_inbox = TRUE; } + camel_imap_response_free (response); /* Add INBOX, if necessary */ if (!*top && !found_inbox) { @@ -562,12 +560,12 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, fi->url = g_strdup_printf ("%sINBOX", base_url); /* FIXME: read/unread msg count */ - g_ptr_array_add (response, fi); + g_ptr_array_add (folders, fi); } /* And assemble */ - camel_folder_info_build (response, topfi, *dir_sep, TRUE); - g_ptr_array_free (response, FALSE); + camel_folder_info_build (folders, topfi, *dir_sep, TRUE); + g_ptr_array_free (folders, FALSE); /* Remove the top if it's the root of the store. */ if (!*top && !topfi->sibling) { @@ -586,864 +584,8 @@ static void imap_keepalive (CamelRemoteStore *store) { CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); - - camel_imap_command_extended (imap_store, NULL, NULL, NULL, "NOOP"); -} - -static int -camel_imap_status (char *cmdid, char *respbuf) -{ - char *retcode; - - if (respbuf) { - if (!strncmp (respbuf, cmdid, strlen (cmdid))) { - retcode = imap_next_word (respbuf); - - if (!strncmp (retcode, "OK", 2)) - return CAMEL_IMAP_OK; - else if (!strncmp (retcode, "NO", 2)) - return CAMEL_IMAP_NO; - else if (!strncmp (retcode, "BAD", 3)) - return CAMEL_IMAP_BAD; - } - } - - return CAMEL_IMAP_FAIL; -} - -static gint -check_current_folder (CamelImapStore *store, CamelFolder *folder, char *fmt, CamelException *ex) -{ - char *folder_path; - int status; - - /* return OK if we meet one of the following criteria: - * 1. the command doesn't care about which folder we're in (folder == NULL) - * 2. if we're already in the right folder (store->current_folder == folder) - * 3. we're going to create a new folder */ - if (!folder || store->current_folder == folder || !strncmp (fmt, "CREATE ", 7)) - return CAMEL_IMAP_OK; - - folder_path = camel_imap_store_folder_path (store, folder->full_name); - status = camel_imap_command_extended (store, NULL, NULL, ex, "SELECT \"%s\"", folder_path); - g_free (folder_path); - - if (status != CAMEL_IMAP_OK) { - store->current_folder = NULL; - return status; - } - - /* remember our currently selected folder */ - store->current_folder = folder; - - return CAMEL_IMAP_OK; -} - -static gboolean -send_command (CamelImapStore *store, char **cmdid, char *fmt, va_list ap, CamelException *ex) -{ - gchar *cmdbuf; - - *cmdid = g_strdup_printf ("A%.5d", store->command++); - - cmdbuf = g_strdup_vprintf (fmt, ap); - - if (camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, "%s %s\r\n", *cmdid, cmdbuf) < 0) { - g_free (cmdbuf); - g_free (*cmdid); - *cmdid = NULL; - return FALSE; - } - - g_free (cmdbuf); - return TRUE; -} - - -/** - * camel_imap_command: Send a command to a IMAP server. - * @store: the IMAP store - * @folder: The folder to perform the operation in - * @ret: a pointer to return the full server response in - * @ex: a CamelException. - * @fmt: a printf-style format string, followed by arguments - * - * This camel method sends the command specified by @fmt and the following - * arguments to the connected IMAP store specified by @store. It then - * reads the server's response and parses out the status code. If - * the caller passed a non-NULL pointer for @ret, camel_imap_command - * will set it to point to a buffer containing the rest of the - * response from the IMAP server. (If @ret was passed but there was - * no extended response, @ret will be set to NULL.) The caller function is - * responsible for freeing @ret. - * - * Return value: one of CAMEL_IMAP_OK (command executed successfully), - * CAMEL_IMAP_NO (operational error message), CAMEL_IMAP_BAD (error - * message from the server), or CAMEL_IMAP_FAIL (a protocol-level error - * occurred, and Camel is uncertain of the result of the command.) - **/ -gint -camel_imap_command (CamelImapStore *store, CamelFolder *folder, CamelException *ex, char *fmt, ...) -{ - char *cmdid, *respbuf, *word; - gint status = CAMEL_IMAP_OK; - va_list ap; - - /* check for current folder */ - status = check_current_folder (store, folder, fmt, ex); - if (status != CAMEL_IMAP_OK) - return status; - - /* send the command */ - va_start (ap, fmt); - if (!send_command (store, &cmdid, fmt, ap, ex)) { - va_end (ap); - g_free (cmdid); - return CAMEL_IMAP_FAIL; - } - va_end (ap); - - /* read single line response */ - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) { - g_free (cmdid); - return CAMEL_IMAP_FAIL; - } - - status = camel_imap_status (cmdid, respbuf); - g_free (cmdid); - - if (status == CAMEL_IMAP_OK) - return status; - - if (respbuf) { - /* get error response and set exception accordingly */ - word = imap_next_word (respbuf); /* points to status */ - word = imap_next_word (word); /* points to fail message, if there is one */ - - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", word); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", "Unknown"); - } - - return status; -} - -/** - * camel_imap_command_extended: Send a command to a IMAP server and get - * a multi-line response. - * @store: the IMAP store - * @folder: The folder to perform the operation in - * @ret: a pointer to return the full server response in, or %NULL - * @fmt: a printf-style format string, followed by arguments - * - * This camel method sends the IMAP command specified by @fmt and the - * following arguments to the IMAP store specified by @store. If the - * store is in a disconnected state, camel_imap_command_extended will first - * re-connect the store before sending the specified IMAP command. It then - * reads the server's response and parses out the status code. If the caller - * passed a non-NULL pointer for @ret, camel_imap_command_extended will set - * it to point to a buffer containing the rest of the response from the IMAP - * server. (If @ret was passed but there was no extended response, @ret will - * be set to NULL.) The caller function is responsible for freeing @ret. - * - * This camel method gets the additional data returned by "multi-line" IMAP - * commands, such as SELECT, LIST, and various other commands. - * The returned data is un-byte-stuffed, and has lines termined by - * newlines rather than CR/LF pairs. - * - * Return value: one of CAMEL_IMAP_OK (command executed successfully), - * CAMEL_IMAP_NO (operational error message), CAMEL_IMAP_BAD (error - * message from the server), or CAMEL_IMAP_FAIL (a protocol-level error - * occurred, and Camel is uncertain of the result of the command.) - **/ - -gint -camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, GPtrArray **ret, CamelException *ex, char *fmt, ...) -{ - gint status = CAMEL_IMAP_OK; - GPtrArray *data = NULL; - GArray *expunged; - gchar *respbuf, *cmdid; - gint recent = 0; - va_list ap; - gint i; - - /* check for current folder */ - status = check_current_folder (store, folder, fmt, ex); - if (status != CAMEL_IMAP_OK) - return status; - - /* send the command */ - va_start (ap, fmt); - if (!send_command (store, &cmdid, fmt, ap, ex)) { - va_end (ap); - return CAMEL_IMAP_FAIL; - } - va_end (ap); - - expunged = g_array_new (FALSE, FALSE, sizeof (int)); - if (ret) - data = g_ptr_array_new (); - - /* read multi-line response */ - while (1) { - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) { - /* cleanup */ - if (ret) { - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - } - g_array_free (expunged, TRUE); - - return CAMEL_IMAP_FAIL; - } - - /* IMAPs multi-line response ends with the cmdid string at the beginning of the line */ - if (!strncmp (respbuf, cmdid, strlen (cmdid))) { - status = camel_imap_status (cmdid, respbuf); - break; - } - - /* Check for a RECENT in the untagged response */ - if (*respbuf == '*') { - if (strstr (respbuf, "RECENT")) { - char *rcnt; - - d(fprintf (stderr, "*** We may have found a 'RECENT' flag: %s\n", respbuf)); - /* Make sure it's in the form: "* %d RECENT" */ - rcnt = imap_next_word (respbuf); - if (*rcnt >= '0' && *rcnt <= '9' && !strncmp ("RECENT", imap_next_word (rcnt), 6)) - recent = atoi (rcnt); - g_free (respbuf); - continue; - } else if (strstr (respbuf, "EXPUNGE")) { - char *id_str; - int id; - - d(fprintf (stderr, "*** We may have found an 'EXPUNGE' flag: %s\n", respbuf)); - /* Make sure it's in the form: "* %d EXPUNGE" */ - id_str = imap_next_word (respbuf); - if (*id_str >= '0' && *id_str <= '9' && !strncmp ("EXPUNGE", imap_next_word (id_str), 7)) { - id = atoi (id_str); - g_array_append_val (expunged, id); - } - g_free (respbuf); - continue; - } - } - if (ret) - g_ptr_array_add (data, respbuf); - else - g_free (respbuf); - } - - if (status == CAMEL_IMAP_OK) { - if (ret) - *ret = data; - } else { - /* command failed */ - if (respbuf) { - char *word; - - word = imap_next_word (respbuf); /* should now point to status */ - - word = imap_next_word (word); /* points to fail message, if there is one */ - - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", word); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: Unknown"); - } - - if (ret) { - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - } - } - - if (respbuf) - g_free (respbuf); + CamelImapResponse *response; - /* Update the summary */ - if (folder && (recent > 0 || expunged->len > 0)) { - CamelException dex; - - camel_exception_init (&dex); - camel_imap_folder_changed (folder, recent, expunged, &dex); - camel_exception_clear (&dex); - } - g_array_free (expunged, TRUE); - - return status; -} - -/** - * camel_imap_response_free: - * @response: the result data returned from camel_imap_command_extended - * - * Frees the data. - **/ -void -camel_imap_response_free (GPtrArray *response) -{ - int i; - - for (i = 0; i < response->len; i++) - g_free (response->pdata[i]); - g_ptr_array_free (response, TRUE); -} - -/** - * camel_imap_response_extract: - * @response: the result data returned from camel_imap_command_extended - * @type: the response type to extract - * @ex: a CamelException - * - * This checks that @response contains a single untagged response of - * type @type and returns just that response data. If @response - * doesn't contain the right information, the function will set @ex and - * return %NULL. Either way, @response will be freed. - * - * Return value: the desired response string, which the caller must free. - **/ -char * -camel_imap_response_extract (GPtrArray *response, const char *type, - CamelException *ex) -{ - int len = strlen (type), i; - char *resp; - - for (i = 0; i < response->len; i++) { - resp = response->pdata[i]; - if (strncmp (resp, "* ", 2) != 0) { - g_free (resp); - continue; - } - - /* Skip inititial sequence number, if present */ - strtoul (resp + 2, &resp, 10); - if (*resp == ' ') - resp++; - - if (!g_strncasecmp (resp, type, len)) - break; - - g_free (resp); - } - - if (i < response->len) { - resp = response->pdata[i]; - for (i++; i < response->len; i++) - g_free (response->pdata[i]); - } else { - resp = NULL; - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP server response did not contain " - "%s information", type); - } - - g_ptr_array_free (response, TRUE); - return resp; -} - -/** - * camel_imap_fetch_command: Send a FETCH request to an IMAP server and get - * a multi-line response. - * @store: the IMAP store - * @folder: The folder to perform the operation in - * @ret: a pointer to return the full server response in, or %NULL - * @fmt: a printf-style format string, followed by arguments - * - * This camel method sends the IMAP FETCH command specified by @fmt and the - * following arguments to the IMAP store specified by @store. If the - * store is in a disconnected state, camel_imap_fetch_command will first - * re-connect the store before sending the specified IMAP command. It then - * reads the server's response and parses out the status code. If the caller - * passed a non-NULL pointer for @ret, camel_imap_fetch_command will set - * it to point to a buffer containing the rest of the response from the IMAP - * server. (If @ret was passed but there was no extended response, @ret will - * be set to NULL.) The caller function is responsible for freeing @ret. - * - * Return value: one of CAMEL_IMAP_OK (command executed successfully), - * CAMEL_IMAP_NO (operational error message), CAMEL_IMAP_BAD (error - * message from the server), or CAMEL_IMAP_FAIL (a protocol-level error - * occurred, and Camel is uncertain of the result of the command.) - **/ - -gint -camel_imap_fetch_command (CamelImapStore *store, CamelFolder *folder, char **ret, CamelException *ex, char *fmt, ...) -{ - /* Security Note: We have to be careful about assuming - * that a server response is valid as the command we are - * calling may require a literal string response which could - * possibly contain strings that appear to be valid server - * responses but aren't. We should, therefor, find a way to - * determine whether we are actually reading server responses. - */ - gint status = CAMEL_IMAP_OK; - GPtrArray *data; - GArray *expunged; - gboolean is_notification; - gchar *respbuf, *cmdid; - guint32 len = 0; - gint partlen = 0; - gint recent = 0; - va_list ap; - gint i; - - status = check_current_folder (store, folder, fmt, ex); - if (status != CAMEL_IMAP_OK) - return status; - - /* send the command */ - va_start (ap, fmt); - if (!send_command (store, &cmdid, fmt, ap, ex)) { - va_end (ap); - return CAMEL_IMAP_FAIL; - } - va_end (ap); - - data = g_ptr_array_new (); - - /* get first response line */ - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) != -1) { - char *p, *q; - - g_ptr_array_add (data, respbuf); - len += strlen (respbuf) + 1; - - for (p = respbuf; *p && *p != '{' && *p != '"' && *p != '\n'; p++); - switch (*p) { - case '"': - /* a quoted string - section 4.3 */ - p++; - for (q = p; *q && *q != '"'; q++); - partlen = (guint32) (q - p); - - is_notification = TRUE; - - break; - case '{': - /* a literal string - section 4.3 */ - partlen = atoi (p + 1); - - /* add len to partlen because the partlen - doesn't count the first response buffer */ - partlen += len; - - is_notification = FALSE; - - break; - default: - /* bad input */ - g_ptr_array_free (data, TRUE); - return CAMEL_IMAP_FAIL; - } - } else { - g_ptr_array_free (data, TRUE); - return CAMEL_IMAP_FAIL; - } - - expunged = g_array_new (FALSE, FALSE, sizeof (int)); - - /* read multi-line response */ - while (1) { - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) { - /* cleanup */ - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - g_array_free (expunged, TRUE); - - return CAMEL_IMAP_FAIL; - } - - g_ptr_array_add (data, respbuf); - len += strlen (respbuf) + 1; - - /* IMAPs multi-line response ends with the cmdid string at the beginning of the line */ - if (is_notification && !strncmp (respbuf, cmdid, strlen (cmdid))) { - status = camel_imap_status (cmdid, respbuf); - break; - } - - /* FIXME: this is redundant */ - /* If recent or expunge flags were somehow set and this - response doesn't begin with a '*' then - recent/expunged must have been misdetected */ - if ((recent || expunged->len > 0) && *respbuf != '*') { - d(fprintf (stderr, "hmmm, someone tried to pull a fast one on us.\n")); - - recent = 0; - - for (i = 0; i < expunged->len; i++) - g_array_remove_index (expunged, i); - } - - /* Check for a RECENT in the untagged response */ - if (*respbuf == '*' && is_notification) { - if (strstr (respbuf, "RECENT")) { - char *rcnt; - - d(fprintf (stderr, "*** We may have found a 'RECENT' flag: %s\n", respbuf)); - /* Make sure it's in the form: "* %d RECENT" */ - rcnt = imap_next_word (respbuf); - if (*rcnt >= '0' && *rcnt <= '9' && !strncmp ("RECENT", imap_next_word (rcnt), 6)) - recent = atoi (rcnt); - } else if (strstr (respbuf, "EXPUNGE")) { - char *id_str; - int id; - - d(fprintf (stderr, "*** We may have found an 'EXPUNGE' flag: %s\n", respbuf)); - /* Make sure it's in the form: "* %d EXPUNGE" */ - id_str = imap_next_word (respbuf); - if (*id_str >= '0' && *id_str <= '9' && !strncmp ("EXPUNGE", imap_next_word (id_str), 7)) { - id = atoi (id_str); - g_array_append_val (expunged, id); - } - } - } - - if (!is_notification) { - partlen--; - if (len >= partlen) - is_notification = TRUE; - } - } - - if (status == CAMEL_IMAP_OK && ret) { - gchar *p; - - /* populate the return buffer with the server response */ - *ret = g_new (char, len + 1); - p = *ret; - - for (i = 0; i < data->len; i++) { - char *datap; - - datap = (char *) data->pdata[i]; - if (*datap == '.') - datap++; - len = strlen (datap); - memcpy (p, datap, len); - p += len; - *p++ = '\n'; - } - - *p = '\0'; - } else if (status != CAMEL_IMAP_OK) { - /* command failed */ - if (respbuf) { - char *word; - - word = imap_next_word (respbuf); /* should now point to status */ - - word = imap_next_word (word); /* points to fail message, if there is one */ - - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", word); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: Unknown"); - } - - if (ret) - *ret = NULL; - } - - /* Update the summary */ - if (folder && (recent > 0 || expunged->len > 0)) { - CamelException dex; - - camel_exception_init (&dex); - camel_imap_folder_changed (folder, recent, expunged, &dex); - camel_exception_clear (&dex); - } - - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - g_array_free (expunged, TRUE); - - return status; -} - -/** - * camel_imap_command_preliminary: Send a preliminary command to the - * IMAP server. - * @store: the IMAP store - * @cmdid: a pointer to return the command identifier (for use in - * camel_imap_command_continuation) - * @fmt: a printf-style format string, followed by arguments - * - * This camel method sends a preliminary IMAP command specified by - * @fmt and the following arguments to the IMAP store specified by - * @store. This function is meant for use with multi-transactional - * IMAP communications like Kerberos authentication and APPEND. - * - * Return value: one of CAMEL_IMAP_PLUS, CAMEL_IMAP_NO, CAMEL_IMAP_BAD - * or CAMEL_IMAP_FAIL - * - * Note: on success (CAMEL_IMAP_PLUS), you will need to follow up with - * a camel_imap_command_continuation call. - **/ -gint -camel_imap_command_preliminary (CamelImapStore *store, char **cmdid, CamelException *ex, char *fmt, ...) -{ - char *respbuf, *word; - gint status = CAMEL_IMAP_OK; - va_list ap; - - /* send the command */ - va_start (ap, fmt); - if (!send_command (store, cmdid, fmt, ap, ex)) { - va_end (ap); - return CAMEL_IMAP_FAIL; - } - va_end (ap); - - /* read single line response */ - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) - return CAMEL_IMAP_FAIL; - - /* Check for '+' which indicates server is ready for command continuation */ - if (*respbuf == '+') - return CAMEL_IMAP_PLUS; - - status = camel_imap_status (*cmdid, respbuf); - - if (respbuf) { - /* get error response and set exception accordingly */ - word = imap_next_word (respbuf); /* points to status */ - word = imap_next_word (word); /* points to fail message, if there is one */ - - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", word); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", "Unknown"); - } - - return status; -} - -/** - * camel_imap_command_continuation: Handle another transaction with the IMAP - * server and possibly get a multi-line response. - * @store: the IMAP store - * @cmdid: The command identifier returned from camel_imap_command_preliminary - * @ret: a pointer to return the full server response in, or %NULL - * @cmdbuf: buffer containing the response/request data - * - * This method is for sending continuing responses to the IMAP server. - * Meant to be used as a followup to camel_imap_command_preliminary. - * If @ret is non-%NULL camel_imap_command_continuation will set it to - * point to a buffer containing the rest of the response from the IMAP - * server. The caller function is responsible for freeing @ret. - * - * Return value: one of CAMEL_IMAP_PLUS (command requires additional data), - * CAMEL_IMAP_OK (command executed successfully), - * CAMEL_IMAP_NO (operational error message), - * CAMEL_IMAP_BAD (error message from the server), or - * CAMEL_IMAP_FAIL (a protocol-level error occurred, and Camel is uncertain - * of the result of the command.) - **/ -gint -camel_imap_command_continuation (CamelImapStore *store, char **ret, char *cmdid, char *cmdbuf, CamelException *ex) -{ - gint status = CAMEL_IMAP_OK; - GPtrArray *data; - gchar *respbuf; - guint32 len = 0; - gint i; - - if (camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, "%s\r\n", cmdbuf) < 0) { - if (ret) - *ret = NULL; - return CAMEL_IMAP_FAIL; - } - - data = g_ptr_array_new (); - - /* read multi-line response */ - while (1) { - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) { - /* cleanup */ - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - - return CAMEL_IMAP_FAIL; - } - - g_ptr_array_add (data, respbuf); - len += strlen (respbuf) + 1; - - /* IMAPs multi-line response ends with the cmdid string at the beginning of the line */ - if (!strncmp (respbuf, cmdid, strlen (cmdid))) { - status = camel_imap_status (cmdid, respbuf); - break; - } - } - - if (status == CAMEL_IMAP_OK && ret) { - gchar *p; - - /* populate the return buffer with the server response */ - *ret = g_new (char, len + 1); - p = *ret; - - for (i = 0; i < data->len; i++) { - char *datap; - - datap = (char *) data->pdata[i]; - if (*datap == '.') - datap++; - len = strlen (datap); - memcpy (p, datap, len); - p += len; - *p++ = '\n'; - } - - *p = '\0'; - } else if (status != CAMEL_IMAP_OK) { - /* command failed */ - if (respbuf) { - char *word; - - word = imap_next_word (respbuf); /* should now point to status */ - - word = imap_next_word (word); /* points to fail message, if there is one */ - - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", word); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: Unknown"); - } - - if (ret) - *ret = NULL; - } - - /* cleanup */ - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - - return status; -} - -/** - * camel_imap_command_continuation_with_stream: Handle another transaction with the IMAP - * server and possibly get a multi-line response. - * @store: the IMAP store - * @cmdid: The command identifier returned from camel_imap_command_preliminary - * @ret: a pointer to return the full server response in, or %NULL - * @cstream: a CamelStream containing a continuation response. - * - * This method is for sending continuing responses to the IMAP server. - * Meant to be used as a followup to camel_imap_command_preliminary. - * If @ret is not %NULL, camel_imap_command_continuation will set it - * to point to a buffer containing the rest of the response from the - * IMAP server. The caller function is responsible for freeing @ret. - * - * Return value: one of CAMEL_IMAP_PLUS (command requires additional data), - * CAMEL_IMAP_OK (command executed successfully), - * CAMEL_IMAP_NO (operational error message), - * CAMEL_IMAP_BAD (error message from the server), or - * CAMEL_IMAP_FAIL (a protocol-level error occurred, and Camel is uncertain - * of the result of the command.) - **/ -gint -camel_imap_command_continuation_with_stream (CamelImapStore *store, char **ret, char *cmdid, - CamelStream *cstream, CamelException *ex) -{ - gint status = CAMEL_IMAP_OK; - GPtrArray *data; - gchar *respbuf; - guint32 len = 0; - gint i; - - /* send stream */ - if (camel_remote_store_send_stream (CAMEL_REMOTE_STORE (store), cstream, ex) < 0) { - if (ret) - *ret = NULL; - return CAMEL_IMAP_FAIL; - } - - data = g_ptr_array_new (); - - /* read the servers multi-line response */ - while (1) { - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) { - /* cleanup */ - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - - return CAMEL_IMAP_FAIL; - } - - g_ptr_array_add (data, respbuf); - len += strlen (respbuf) + 1; - - /* IMAPs multi-line response ends with the cmdid string at the beginning of the line */ - if (!strncmp (respbuf, cmdid, strlen (cmdid))) { - status = camel_imap_status (cmdid, respbuf); - break; - } - } - - if (status == CAMEL_IMAP_OK && ret) { - gchar *p; - - /* populate the return buffer with the server response */ - *ret = g_new (char, len + 1); - p = *ret; - - for (i = 0; i < data->len; i++) { - char *datap; - - datap = (char *) data->pdata[i]; - if (*datap == '.') - datap++; - len = strlen (datap); - memcpy (p, datap, len); - p += len; - *p++ = '\n'; - } - - *p = '\0'; - } else if (status != CAMEL_IMAP_OK) { - /* command failed */ - if (respbuf) { - char *word; - - word = imap_next_word (respbuf); /* should now point to status */ - - word = imap_next_word (word); /* points to fail message, if there is one */ - - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", word); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: Unknown error"); - } - - if (ret) - *ret = NULL; - } - - /* cleanup */ - for (i = 0; i < data->len; i++) - g_free (data->pdata[i]); - g_ptr_array_free (data, TRUE); - - return status; + response = camel_imap_command (imap_store, NULL, NULL, "NOOP"); + camel_imap_response_free (response); } diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index 1d9a07ff96..3382a8940b 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -66,50 +66,6 @@ typedef struct { } CamelImapStoreClass; -/* public methods */ -void camel_imap_store_open (CamelImapStore *store, CamelException *ex); -void camel_imap_store_close (CamelImapStore *store, gboolean expunge, CamelException *ex); - -/* support functions */ - -enum { - CAMEL_IMAP_OK = 0, - CAMEL_IMAP_NO, - CAMEL_IMAP_BAD, - CAMEL_IMAP_PLUS, - CAMEL_IMAP_FAIL -}; - -gint camel_imap_command (CamelImapStore *store, CamelFolder *folder, - CamelException *ex, char *fmt, ...); - -gint camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, - GPtrArray **ret, CamelException *ex, char *fmt, ...); -void camel_imap_response_free (GPtrArray *response); -char *camel_imap_response_extract (GPtrArray *response, const char *type, - CamelException *ex); - -gint camel_imap_fetch_command (CamelImapStore *store, CamelFolder *folder, - char **ret, CamelException *ex, char *fmt, ...); - -/* multi-transactional commands... */ -gint camel_imap_command_preliminary (CamelImapStore *store, - char **cmdid, - CamelException *ex, - char *fmt, ...); - -gint camel_imap_command_continuation (CamelImapStore *store, - char **ret, - char *cmdid, - char *cmdbuf, - CamelException *ex); - -gint camel_imap_command_continuation_with_stream (CamelImapStore *store, - char **ret, - char *cmdid, - CamelStream *cstream, - CamelException *ex); - /* Standard Camel function */ CamelType camel_imap_store_get_type (void); diff --git a/camel/providers/imap/camel-imap-utils.c b/camel/providers/imap/camel-imap-utils.c index 4658341e6a..98c8d63dd2 100644 --- a/camel/providers/imap/camel-imap-utils.c +++ b/camel/providers/imap/camel-imap-utils.c @@ -558,3 +558,77 @@ imap_parse_flag_list (const char *flag_list) return flags; } + +/** + * imap_parse_nstring: + * @str_p: a pointer to a string + * @len: a pointer to an int to return the length in + * + * This parses an "nstring" (NIL, a quoted string, or a literal) + * starting at *@str_p. On success, *@str_p will point to the first + * character after the end of the nstring, and *@len will contain + * the length of the returned string. On failure, *@str_p will be + * set to %NULL. + * + * This assumes that the string is in the form returned by + * camel_imap_command(): that line breaks are indicated by LF rather + * than CRLF. + * + * Return value: the parsed string, or %NULL if a NIL or no string + * was parsed. (In the former case, *@str_p will be %NULL; in the + * latter, it will point to the character after the NIL.) + **/ +char * +imap_parse_nstring (char **str_p, int *len) +{ + char *str = *str_p; + char *out; + + if (!str) + return NULL; + else if (*str == '"') { + char *p; + int size; + + str++; + size = strcspn (str, "\"") + 1; + p = out = g_malloc (size); + + while (*str && *str != '"') { + if (*str == '\\') + str++; + *p++ = *str++; + if (p - out == size) { + out = g_realloc (out, size * 2); + p = out + size; + size *= 2; + } + } + if (*str != '"') { + *str_p = NULL; + g_free (out); + return NULL; + } + *p = '\0'; + *str_p = str + 1; + *len = strlen (out); + return out; + } else if (*str == '{') { + *len = strtoul (str + 1, (char **)&str, 10); + if (*str++ != '}' || *str++ != '\n' || strlen (str) < *len) { + *str_p = NULL; + return NULL; + } + out = g_strndup (str, *len); + *str_p = str + *len; + return out; + } else if (!g_strncasecmp (str, "nil", 3)) { + *str_p += 3; + *len = 0; + return NULL; + } else { + *str_p = NULL; + return NULL; + } +} + diff --git a/camel/providers/imap/camel-imap-utils.h b/camel/providers/imap/camel-imap-utils.h index b08c179926..28dea3cc9f 100644 --- a/camel/providers/imap/camel-imap-utils.h +++ b/camel/providers/imap/camel-imap-utils.h @@ -39,6 +39,8 @@ char *imap_translate_sexp (const char *expression); char *imap_create_flag_list (guint32 flags); guint32 imap_parse_flag_list (const char *flag_list); +char *imap_parse_nstring (char **str_p, int *len); + #ifdef __cplusplus } #endif /* __cplusplus */ |