From 151fb928c3191fbf37d34e0f35ff242587fbb307 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Wed, 30 Aug 2000 21:01:59 +0000 Subject: Don't wrap printed strings in quotes, makes things messy 2000-08-30 Jeffrey Stedfast * camel-remote-store.c (remote_send_string): Don't wrap printed strings in quotes, makes things messy * providers/imap/camel-imap-folder.c (imap_get_message): Updated to use the camel_imap_fetch_command * providers/imap/camel-imap-stream.c (stream_read): Updated to use camel_imap_fetch_command * providers/imap/camel-imap-store.c (camel_imap_command_extended): No longer handles FETCH requests so no longer needs to be concerned with checking to make sure that server responses are valid (they have to be). (camel_imap_fetch_command): New convenience function that handles all FETCH requests svn path=/trunk/; revision=5122 --- camel/ChangeLog | 18 ++ camel/camel-remote-store.c | 2 +- camel/providers/imap/camel-imap-folder.c | 18 +- camel/providers/imap/camel-imap-store.c | 537 ++++++++++++++++++++++++------- camel/providers/imap/camel-imap-store.h | 3 + camel/providers/imap/camel-imap-stream.c | 8 +- 6 files changed, 452 insertions(+), 134 deletions(-) (limited to 'camel') diff --git a/camel/ChangeLog b/camel/ChangeLog index f272ee4293..b72bd0cf62 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,21 @@ +2000-08-30 Jeffrey Stedfast + + * camel-remote-store.c (remote_send_string): Don't wrap printed + strings in quotes, makes things messy + + * providers/imap/camel-imap-folder.c (imap_get_message): Updated + to use the camel_imap_fetch_command + + * providers/imap/camel-imap-stream.c (stream_read): Updated to use + camel_imap_fetch_command + + * providers/imap/camel-imap-store.c (camel_imap_command_extended): + No longer handles FETCH requests so no longer needs to be + concerned with checking to make sure that server responses are + valid (they have to be). + (camel_imap_fetch_command): New convenience function that handles + all FETCH requests + 2000-08-30 Peter Williams * camel-remote-store.c (remote_connect): Unify with remote_post_connect. diff --git a/camel/camel-remote-store.c b/camel/camel-remote-store.c index 5105045104..d11fd0bcbd 100644 --- a/camel/camel-remote-store.c +++ b/camel/camel-remote-store.c @@ -334,7 +334,7 @@ remote_send_string (CamelRemoteStore *store, CamelException *ex, char *fmt, va_l /* create the command */ cmdbuf = g_strdup_vprintf (fmt, ap); - d(fprintf (stderr, "sending : \"%s\"\n", cmdbuf)); + d(fprintf (stderr, "sending : %s", cmdbuf)); if (camel_stream_printf (store->ostream, "%s", cmdbuf) == -1) { CamelException dex; diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index ce15fd7e97..5744a61584 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -802,9 +802,9 @@ imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) else data_item = "RFC822.HEADER"; - status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, - &result, ex, "UID FETCH %s %s", uid, - data_item); + status = camel_imap_fetch_command (CAMEL_IMAP_STORE (folder->parent_store), folder, + &result, ex, "UID FETCH %s %s", uid, + data_item); if (!result || status != CAMEL_IMAP_OK) return NULL; @@ -855,9 +855,9 @@ imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) else data_item = "RFC822.TEXT"; - status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, - &result, ex, "UID FETCH %s %s", uid, - data_item); + status = camel_imap_fetch_command (CAMEL_IMAP_STORE (folder->parent_store), folder, + &result, ex, "UID FETCH %s %s", uid, + data_item); if (!result || status != CAMEL_IMAP_OK) { g_free (header); @@ -1024,6 +1024,7 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex) summary_specifier = imap_protocol_get_summary_specifier (folder); + /* We use camel_imap_command_extended here because it's safe */ if (num == 1) { status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, &result, ex, "FETCH 1 (%s)", summary_specifier); @@ -1083,7 +1084,7 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex) for (uid += 4; *uid && (*uid < '0' || *uid > '9'); uid++); /* advance to */ for (q = uid; *q && *q >= '0' && *q <= '9'; q++); /* find the end of the */ info->uid = g_strndup (uid, (gint)(q - uid)); - d(fprintf (stderr, "*** info->uid = %s\n", info->uid)); + /*d(fprintf (stderr, "*** info->uid = %s\n", info->uid));*/ /* now lets grab the FLAGS */ if (!(flags = strstr (headers->pdata[i], "FLAGS "))) { @@ -1096,7 +1097,7 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex) for (flags += 6; *flags && *flags != '('; flags++); /* advance to */ for (q = flags; *q && *q != ')'; q++); /* find the end of */ flags = g_strndup (flags, (gint)(q - flags + 1)); - d(fprintf (stderr, "*** info->flags = %s\n", flags)); + /*d(fprintf (stderr, "*** info->flags = %s\n", flags));*/ /* now we gotta parse for the flags */ info->flags = 0; @@ -1211,6 +1212,7 @@ imap_get_message_info_internal (CamelFolder *folder, guint id, CamelException *e /* we don't have a cached copy, so fetch it */ summary_specifier = imap_protocol_get_summary_specifier (folder); + /* again, we use camel_imap_command_extended here because it's safe to do so */ status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, &result, ex, "FETCH %d (%s)", id, summary_specifier); diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index c3b859c47e..98cfd9095f 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -181,7 +181,7 @@ imap_connect (CamelService *service, CamelException *ex) if (CAMEL_SERVICE_CLASS (remote_store_class)->connect (service, ex) == FALSE) return FALSE; - + store->command = 0; g_free (store->dir_sep); store->dir_sep = g_strdup ("/"); /* default dir sep */ @@ -524,7 +524,7 @@ check_current_folder (CamelImapStore *store, CamelFolder *folder, char *fmt, Cam * 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", 5)) + if (!folder || store->current_folder == folder || !strncmp (fmt, "CREATE ", 7)) return CAMEL_IMAP_OK; dir_sep = store->dir_sep; @@ -569,25 +569,142 @@ send_command (CamelImapStore *store, char **cmdid, char *fmt, va_list ap, CamelE return TRUE; } -static gint -slurp_response (CamelImapStore *store, CamelFolder *folder, char *cmdid, char **ret, - gboolean stop_on_plus, CamelException *ex) + +/** + * 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 + * @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, char **ret, CamelException *ex, char *fmt, ...) { gint status = CAMEL_IMAP_OK; GPtrArray *data, *expunged; - gchar *respbuf; + gchar *respbuf, *cmdid; guint32 len = 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 (); expunged = 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); + + for (i = 0; i < expunged->len; i++) + g_free (expunged->pdata[i]); g_ptr_array_free (expunged, TRUE); return CAMEL_IMAP_FAIL; @@ -596,31 +713,12 @@ slurp_response (CamelImapStore *store, CamelFolder *folder, char *cmdid, char ** g_ptr_array_add (data, respbuf); len += strlen (respbuf) + 1; - /* IMAP's last response starts with our command id or, sometimes, a plus */ - if (stop_on_plus && *respbuf == '+') { - status = CAMEL_IMAP_PLUS; - break; - } - + /* 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 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_free (expunged->pdata[i]); - g_ptr_array_remove_index (expunged, i); - } - } - /* Check for a RECENT in the untagged response */ if (*respbuf == '*') { if (strstr (respbuf, "RECENT")) { @@ -646,16 +744,10 @@ slurp_response (CamelImapStore *store, CamelFolder *folder, char *cmdid, char ** } } - /* Apply the 'recent' changes */ - if (folder && recent > 0) - camel_imap_folder_changed (folder, recent, expunged, ex); - - if (status == CAMEL_IMAP_OK || status == CAMEL_IMAP_PLUS) { + if (status == CAMEL_IMAP_OK) { gchar *p; - /* Command succeeded! Put the output into one big - * string of love. */ - + /* populate the return buffer with the server response */ *ret = g_new (char, len + 1); p = *ret; @@ -673,7 +765,7 @@ slurp_response (CamelImapStore *store, CamelFolder *folder, char *cmdid, char ** *p = '\0'; } else { - /* Bummer. Try to grab what the server said. */ + /* command failed */ if (respbuf) { char *word; @@ -683,13 +775,17 @@ slurp_response (CamelImapStore *store, CamelFolder *folder, char *cmdid, char ** 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"); } *ret = NULL; } - /* Can this be put into the 'if succeeded' bit? - * Or can a failed command generate untagged responses? */ + /* Update the summary */ + if (folder && (recent > 0 || expunged->len > 0)) + camel_imap_folder_changed (folder, recent, expunged, ex); for (i = 0; i < data->len; i++) g_free (data->pdata[i]); @@ -702,37 +798,48 @@ slurp_response (CamelImapStore *store, CamelFolder *folder, char *cmdid, char ** return status; } - /** - * camel_imap_command: Send a command to a IMAP server. + * 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 - * @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. + * 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_command (CamelImapStore *store, CamelFolder *folder, CamelException *ex, char *fmt, ...) +camel_imap_fetch_command (CamelImapStore *store, CamelFolder *folder, char **ret, CamelException *ex, char *fmt, ...) { - char *cmdid, *respbuf, *word; + /* Security Note/FIXME: 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, *expunged; + gchar *respbuf, *cmdid; + guint32 len = 0; + 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; @@ -741,87 +848,128 @@ camel_imap_command (CamelImapStore *store, CamelFolder *folder, CamelException * 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); + data = g_ptr_array_new (); + expunged = g_ptr_array_new (); - if (status == CAMEL_IMAP_OK) - return status; + /* 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); + + for (i = 0; i < expunged->len; i++) + g_free (expunged->pdata[i]); + g_ptr_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 (!strncmp (respbuf, cmdid, strlen (cmdid))) { + status = camel_imap_status (cmdid, respbuf); + break; + } + + /* 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_free (expunged->pdata[i]); + g_ptr_array_remove_index (expunged, i); + } + } + + /* 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); + } 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_ptr_array_add (expunged, g_strdup_printf ("%d", id)); + } + } + } + } - 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 */ + if (status == CAMEL_IMAP_OK) { + gchar *p; - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", word); + /* 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 { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - "IMAP command failed: %s", "Unknown"); + /* 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"); + } + + *ret = NULL; } - 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 - * @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, FETCH, 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, char **ret, CamelException *ex, char *fmt, ...) -{ - gint status = CAMEL_IMAP_OK; - gchar *cmdid; - va_list ap; + /* Update the summary */ + if (folder && (recent > 0 || expunged->len > 0)) + camel_imap_folder_changed (folder, recent, expunged, ex); - status = check_current_folder (store, folder, fmt, ex); - if (status != CAMEL_IMAP_OK) - return status; + for (i = 0; i < data->len; i++) + g_free (data->pdata[i]); + g_ptr_array_free (data, TRUE); - /* 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); + for (i = 0; i < expunged->len; i++) + g_free (expunged->pdata[i]); + g_ptr_array_free (expunged, TRUE); - return slurp_response (store, folder, cmdid, ret, FALSE, ex); + return status; } /** @@ -915,13 +1063,86 @@ camel_imap_command_preliminary (CamelImapStore *store, char **cmdid, CamelExcept 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; } - return slurp_response (store, NULL, cmdid, ret, TRUE, ex); + 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) { + 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 { + /* 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"); + } + + *ret = NULL; + } + + /* cleanup */ + for (i = 0; i < data->len; i++) + g_free (data->pdata[i]); + g_ptr_array_free (data, TRUE); + + return status; } /** @@ -949,11 +1170,85 @@ 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; } - return slurp_response (store, NULL, cmdid, ret, TRUE, ex); + 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) { + 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 { + /* 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"); + } + + *ret = NULL; + } + + /* cleanup */ + for (i = 0; i < data->len; i++) + g_free (data->pdata[i]); + g_ptr_array_free (data, TRUE); + + return status; } diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index eecc42a1e0..7aa911f394 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -89,6 +89,9 @@ gint camel_imap_command (CamelImapStore *store, CamelFolder *folder, gint camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, char **ret, CamelException *ex, char *fmt, ...); +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, diff --git a/camel/providers/imap/camel-imap-stream.c b/camel/providers/imap/camel-imap-stream.c index f27e782c0a..a59be149d9 100644 --- a/camel/providers/imap/camel-imap-stream.c +++ b/camel/providers/imap/camel-imap-stream.c @@ -123,10 +123,10 @@ stream_read (CamelStream *stream, char *buffer, size_t n) gint status, part_len; camel_exception_init (&ex); - status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), - CAMEL_FOLDER (imap_stream->folder), - &result, &ex, "%s\r\n", - imap_stream->command); + status = camel_imap_fetch_command (CAMEL_IMAP_STORE (folder->parent_store), + CAMEL_FOLDER (imap_stream->folder), + &result, &ex, "%s\r\n", + imap_stream->command); /* FIXME: exception is ignored */ camel_exception_clear (&ex); -- cgit