New file containing camel_imap_command and friends. Major

* providers/imap/camel-imap-command.c: New file containing
        camel_imap_command and friends. Major camel_imap_command rewrite
        to remove duplicated code, make the parsing of literals be
        more safe/correct, deal with RECENT/EXPUNGE responses more
        consistently, and make it possible to implement the AUTHENTICATE
        command.

        * providers/imap/camel-imap-utils.c (imap_parse_nstring): New
        function, to parse an IMAP "nstring".

        * providers/imap/camel-imap-store.c: Move command stuff to
        camel-imap-command.c. Update for camel_imap_command changes.

        * providers/imap/camel-imap-folder.c: Update for
        camel_imap_command changes.
        (imap_append_message): CRLF filter the message before sending it.

        * providers/imap/Makefile.am: Add camel-imap-command.[ch], remove
        camel-imap-stream.[ch] for now.

svn path=/trunk/; revision=5693
This commit is contained in:
Dan Winship
2000-10-03 20:06:14 +00:00
parent 9a0fc2bc8d
commit 00f2a8d64e
9 changed files with 739 additions and 1198 deletions

View File

@ -1,3 +1,25 @@
2000-10-03 Dan Winship <danw@helixcode.com>
* providers/imap/camel-imap-command.c: New file containing
camel_imap_command and friends. Major camel_imap_command rewrite
to remove duplicated code, make the parsing of literals be
more safe/correct, deal with RECENT/EXPUNGE responses more
consistently, and make it possible to implement the AUTHENTICATE
command.
* providers/imap/camel-imap-utils.c (imap_parse_nstring): New
function, to parse an IMAP "nstring".
* providers/imap/camel-imap-store.c: Move command stuff to
camel-imap-command.c. Update for camel_imap_command changes.
* providers/imap/camel-imap-folder.c: Update for
camel_imap_command changes.
(imap_append_message): CRLF filter the message before sending it.
* providers/imap/Makefile.am: Add camel-imap-command.[ch], remove
camel-imap-stream.[ch] for now.
2000-10-02 Jeffrey Stedfast <fejj@helixcode.com>
* camel-mime-message.c (camel_mime_message_has_8bit_parts): New

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}
/* 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)
result = camel_imap_response_extract_continuation (response, ex);
if (!result) {
g_byte_array_free (ba, TRUE);
return;
camel_object_unref (CAMEL_OBJECT (memstream));
camel_imap_folder_changed (folder, 1, NULL, ex);
}
g_free (result);
/* send the rest of our data - the mime message */
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_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)
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;
/* 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 */
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);
}
if (!p) {
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
"Could not find message body in FETCH "
"response.");
g_free (result);
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);
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);
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 */
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;

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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 */