From bebb2583a8fc396f8e894f38471428c405164d37 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 18 Dec 2000 19:17:17 +0000 Subject: Change the semantics of fmt: Now %S (capital S) means an IMAP "string", * providers/imap/camel-imap-command.c (camel_imap_command): Change the semantics of fmt: Now %S (capital S) means an IMAP "string", (which can be sent as either a quoted string or a literal). If the server supports LITERAL+, these will be sent as extended literals (which don't require any special escaping). Otherwise they'll be sent as quoted strings (and it now properly deals with " or \ in the string). (imap_command_strdup_vprintf): Utility routine that does the real work for the functionality mentioned above. * providers/imap/camel-imap-utils.c (imap_quote_string): Turns a string into a proper IMAP "quoted string". * providers/imap/camel-imap-store.c: * providers/imap/camel-imap-folder.c: Use %S instead of "%s" where appropriate. svn path=/trunk/; revision=7070 --- camel/providers/imap/camel-imap-command.c | 123 +++++++++++++++++++++++++++++- camel/providers/imap/camel-imap-folder.c | 6 +- camel/providers/imap/camel-imap-store.c | 30 ++++---- camel/providers/imap/camel-imap-utils.c | 35 +++++++++ camel/providers/imap/camel-imap-utils.h | 2 + 5 files changed, 175 insertions(+), 21 deletions(-) (limited to 'camel/providers') diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c index f490ec0e1f..b61c9f8cd3 100644 --- a/camel/providers/imap/camel-imap-command.c +++ b/camel/providers/imap/camel-imap-command.c @@ -27,6 +27,7 @@ #include +#include #include #include @@ -39,6 +40,8 @@ static char *imap_read_untagged (CamelImapStore *store, char *line, CamelException *ex); static CamelImapResponse *imap_read_response (CamelImapStore *store, CamelException *ex); +static char *imap_command_strdup_vprintf (CamelImapStore *store, + const char *fmt, va_list ap); /** * camel_imap_command: Send a command to a IMAP server and get a response @@ -46,7 +49,7 @@ static CamelImapResponse *imap_read_response (CamelImapStore *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 + * @fmt: an sort of printf-style format string, followed by arguments * * This function makes sure that @folder (if non-%NULL) is the * currently-selected folder on @store and then sends the IMAP command @@ -56,6 +59,14 @@ static CamelImapResponse *imap_read_response (CamelImapStore *store, * As a special case, if @fmt is %NULL, it will just select @folder * and return the response from doing so. * + * @fmt can include the following %-escapes ONLY: + * %s, %d, %%: as with printf + * %S: an IMAP "string" (quoted string or literal) + * + * %S strings will be passed as literals if the server supports LITERAL+ + * and quoted strings otherwise. (%S does not support strings that + * contain newlines.) + * * 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(). @@ -72,8 +83,7 @@ camel_imap_command (CamelImapStore *store, CamelFolder *folder, CamelImapResponse *response; store->current_folder = NULL; - response = camel_imap_command (store, NULL, ex, - "SELECT \"%s\"", + response = camel_imap_command (store, NULL, ex, "SELECT %S", folder->full_name); if (!response) return NULL; @@ -87,7 +97,7 @@ camel_imap_command (CamelImapStore *store, CamelFolder *folder, /* Send the command */ va_start (ap, fmt); - cmdbuf = g_strdup_vprintf (fmt, ap); + cmdbuf = imap_command_strdup_vprintf (store, fmt, ap); va_end (ap); camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, @@ -445,3 +455,108 @@ camel_imap_response_extract_continuation (CamelImapResponse *response, camel_imap_response_free (response); return NULL; } + + +static char * +imap_command_strdup_vprintf (CamelImapStore *store, const char *fmt, + va_list ap) +{ + GPtrArray *args; + const char *p, *start; + char *out, *op, *string; + int num, len, i; + + args = g_ptr_array_new (); + + /* Determine the length of the data */ + len = strlen (fmt); + p = start = fmt; + while (*p) { + p = strchr (start, '%'); + if (!p) + break; + + switch (*++p) { + case 'd': + num = va_arg (ap, int); + g_ptr_array_add (args, GINT_TO_POINTER (num)); + start = p + 1; + len += 10; + break; + + case 's': + string = va_arg (ap, char *); + g_ptr_array_add (args, string); + start = p + 1; + len += strlen (string); + break; + + case 'S': + string = va_arg (ap, char *); + g_ptr_array_add (args, string); + if (store->capabilities & IMAP_CAPABILITY_LITERALPLUS) + len += strlen (string) + 15; + else + len += strlen (string) * 2; + start = p + 1; + break; + + case '%': + start = p; + break; + + default: + g_warning ("camel-imap-command is not printf. I don't " + "know what '%%%c' means.", *p); + start = *p ? p + 1 : p; + break; + } + } + + /* Now write out the string */ + op = out = g_malloc (len); + p = start = fmt; + i = 0; + while (*p) { + p = strchr (start, '%'); + if (!p) { + strcpy (op, start); + break; + } else { + strncpy (op, start, p - start); + op += p - start; + } + + switch (*++p) { + case 'd': + num = GPOINTER_TO_INT (args->pdata[i++]); + op += sprintf (op, "%d", num); + break; + + case 's': + string = args->pdata[i++]; + op += sprintf (op, "%s", string); + break; + + case 'S': + string = args->pdata[i++]; + if (store->capabilities & IMAP_CAPABILITY_LITERALPLUS) { + op += sprintf (op, "{%d+}\r\n%s", + strlen (string), string); + } else { + char *quoted = imap_quote_string (string); + op += sprintf (op, "%s", quoted); + g_free (quoted); + } + break; + + default: + *op++ = '%'; + *op++ = *p; + } + + start = *p ? p + 1 : p; + } + + return out; +} diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index c127df73de..ae3fe15822 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -469,7 +469,7 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message, 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}", + response = camel_imap_command (store, NULL, ex, "APPEND %S%s%s {%d}", folder->full_name, flagstr ? " " : "", flagstr ? flagstr : "", ba->len); g_free (flagstr); @@ -501,7 +501,7 @@ imap_copy_message_to (CamelFolder *source, const char *uid, CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); CamelImapResponse *response; - response = camel_imap_command (store, source, ex, "UID COPY %s \"%s\"", + response = camel_imap_command (store, source, ex, "UID COPY %s %S", uid, destination->full_name); camel_imap_response_free (response); @@ -521,7 +521,7 @@ imap_move_message_to (CamelFolder *source, const char *uid, CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); CamelImapResponse *response; - response = camel_imap_command (store, source, ex, "UID COPY %s \"%s\"", + response = camel_imap_command (store, source, ex, "UID COPY %s %S", uid, destination->full_name); camel_imap_response_free (response); diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index 729452f002..6a87aec3ad 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -352,7 +352,7 @@ imap_connect (CamelService *service, CamelException *ex) } response = camel_imap_command (store, NULL, ex, - "LOGIN \"%s\" \"%s\"", + "LOGIN %S %S", service->url->user, service->url->passwd); if (!response) { @@ -385,7 +385,7 @@ imap_connect (CamelService *service, CamelException *ex) * for the given path, even if that path doesn't exist. */ response = camel_imap_command (store, NULL, ex, - "LIST \"%s\" \"\"", + "LIST %S \"\"", namespace); } else { /* Plain IMAP4 doesn't have that idiom, so we fall back @@ -393,7 +393,7 @@ imap_connect (CamelService *service, CamelException *ex) * the folder doesn't exist (eg, if namespace is ""). */ response = camel_imap_command (store, NULL, ex, - "LIST \"\" \"%s\"", + "LIST \"\" %S", namespace); } if (!response) @@ -457,7 +457,7 @@ imap_folder_exists (CamelImapStore *store, const char *folder_name, return TRUE; } - response = camel_imap_command (store, NULL, ex, "LIST \"\" \"%s\"", + response = camel_imap_command (store, NULL, ex, "LIST \"\" %S", folder_name); if (!response) return FALSE; @@ -487,7 +487,7 @@ imap_create (CamelImapStore *store, const char *folder_name, { CamelImapResponse *response; - response = camel_imap_command (store, NULL, ex, "CREATE \"%s\"", + response = camel_imap_command (store, NULL, ex, "CREATE %S", folder_name); camel_imap_response_free (response); @@ -622,7 +622,7 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, name = ""; } response = camel_imap_command (imap_store, NULL, ex, - "LIST \"\" \"%s\"", name); + "LIST \"\" %S", name); if (!response) return FALSE; list = camel_imap_response_extract (response, "LIST", ex); @@ -637,15 +637,17 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, } if (!top && subscribed_only) - pattern = g_strdup (""); + pattern = g_strdup (recursive ? "*" : "%"); else if (*name) - pattern = g_strdup_printf ("%s%c", name, imap_store->dir_sep); + pattern = g_strdup_printf ("%s%c%c", name, imap_store->dir_sep, + recursive ? '*' : '%'); else - pattern = g_strdup (name); + pattern = g_strdup_printf ("%s%c", name, + recursive ? '*' : '%'); response = camel_imap_command (imap_store, NULL, ex, - "%s \"\" \"%s%c\"", + "%s \"\" %S", subscribed_only ? "LSUB" : "LIST", - pattern, recursive ? '*' : '%'); + pattern); g_free (pattern); if (!response) return NULL; @@ -696,7 +698,7 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, response = camel_imap_command ( imap_store, NULL, NULL, - "STATUS \"%s\" (MESSAGES UNSEEN)", + "STATUS %S (MESSAGES UNSEEN)", fi->full_name); if (!response) continue; @@ -749,7 +751,7 @@ subscribe_folder (CamelStore *store, const char *folder_name, CamelImapResponse *response; response = camel_imap_command (imap_store, NULL, ex, - "SUBSCRIBE \"%s\"", folder_name); + "SUBSCRIBE %S", folder_name); if (response) { g_hash_table_insert (imap_store->subscribed_folders, g_strdup (folder_name), @@ -767,7 +769,7 @@ unsubscribe_folder (CamelStore *store, const char *folder_name, gpointer key, value; response = camel_imap_command (imap_store, NULL, ex, - "UNSUBSCRIBE \"%s\"", folder_name); + "UNSUBSCRIBE %S", folder_name); if (response) { g_hash_table_lookup_extended (imap_store->subscribed_folders, folder_name, &key, &value); diff --git a/camel/providers/imap/camel-imap-utils.c b/camel/providers/imap/camel-imap-utils.c index 0b947e07da..05c9d01d5e 100644 --- a/camel/providers/imap/camel-imap-utils.c +++ b/camel/providers/imap/camel-imap-utils.c @@ -687,3 +687,38 @@ imap_parse_astring (char **str_p, int *len) *str_p += *len; return p; } + +/** + * imap_quote_string: + * @str: the string to quote, which must not contain CR or LF + * + * Return value: an IMAP "quoted" corresponding to the string, which + * the caller must free. + **/ +char * +imap_quote_string (const char *str) +{ + const char *p; + char *quoted, *q; + int len; + + len = strlen (str); + p = str; + while ((p = strpbrk (p, "\"\\"))) { + len++; + p++; + } + + quoted = q = g_malloc (len + 3); + *q++ = '"'; + while ((p = strpbrk (str, "\"\\"))) { + memcpy (q, str, p - str); + q += p - str; + *q++ = '\\'; + *q++ = *p++; + str = p; + } + sprintf (q, "%s\"", str); + + return quoted; +} diff --git a/camel/providers/imap/camel-imap-utils.h b/camel/providers/imap/camel-imap-utils.h index d0cc05832d..e7b009f426 100644 --- a/camel/providers/imap/camel-imap-utils.h +++ b/camel/providers/imap/camel-imap-utils.h @@ -46,6 +46,8 @@ guint32 imap_parse_flag_list (const char *flag_list); char *imap_parse_nstring (char **str_p, int *len); char *imap_parse_astring (char **str_p, int *len); +char *imap_quote_string (const char *str); + #ifdef __cplusplus } #endif /* __cplusplus */ -- cgit v1.2.3