/*
* Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
* Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
* Copyright (C) 1999-2009 Brendan Cully <brendan@kublai.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* command.c: routines for sending commands to an IMAP server and parsing
* responses */
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include "mutt.h"
#include "imap_private.h"
#include "message.h"
#include "mx.h"
#include "buffy.h"
#include <ctype.h>
#include <stdlib.h>
#define IMAP_CMD_BUFSIZE 512
/* forward declarations */
static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags);
static int cmd_queue_full (IMAP_DATA* idata);
static int cmd_queue (IMAP_DATA* idata, const char* cmdstr);
static IMAP_COMMAND* cmd_new (IMAP_DATA* idata);
static int cmd_status (const char *s);
static void cmd_handle_fatal (IMAP_DATA* idata);
static int cmd_handle_untagged (IMAP_DATA* idata);
static void cmd_parse_capability (IMAP_DATA* idata, char* s);
static void cmd_parse_expunge (IMAP_DATA* idata, const char* s);
static void cmd_parse_list (IMAP_DATA* idata, char* s);
static void cmd_parse_lsub (IMAP_DATA* idata, char* s);
static void cmd_parse_fetch (IMAP_DATA* idata, char* s);
static void cmd_parse_myrights (IMAP_DATA* idata, const char* s);
static void cmd_parse_search (IMAP_DATA* idata, const char* s);
static void cmd_parse_status (IMAP_DATA* idata, char* s);
static char *Capabilities[] = {
"IMAP4",
"IMAP4rev1",
"STATUS",
"ACL",
"NAMESPACE",
"AUTH=CRAM-MD5",
"AUTH=GSSAPI",
"AUTH=ANONYMOUS",
"STARTTLS",
"LOGINDISABLED",
"IDLE",
"SASL-IR",
NULL
};
/* imap_cmd_start: Given an IMAP command, send it to the server.
* If cmdstr is NULL, sends queued commands. */
int imap_cmd_start (IMAP_DATA* idata, const char* cmdstr)
{
return cmd_start (idata, cmdstr, 0);
}
/* imap_cmd_step: Reads server responses from an IMAP command, detects
* tagged completion response, handles untagged messages, can read
* arbitrarily large strings (using malloc, so don't make it _too_
* large!). */
int imap_cmd_step (IMAP_DATA* idata)
{
size_t len = 0;
int c;
int rc;
int stillrunning = 0;
IMAP_COMMAND* cmd;
if (idata->status == IMAP_FATAL)
{
cmd_handle_fatal (idata);
return IMAP_CMD_BAD;
}
/* read into buffer, expanding buffer as necessary until we have a full
* line */
do
{
if (len == idata->blen)
{
safe_realloc (&idata->buf, idata->blen + IMAP_CMD_BUFSIZE);
idata->blen = idata->blen + IMAP_CMD_BUFSIZE;
dprint (3, (debugfile, "imap_cmd_step: grew buffer to %u bytes\n",
idata->blen));
}
/* back up over '\0' */
if (len)
len--;
c = mutt_socket_readln (idata->buf + len, idata->blen - len, idata->conn);
if (c <= 0)
{
dprint (1, (debugfile, "imap_cmd_step: Error reading server response.\n"));
cmd_handle_fatal (idata);
return IMAP_CMD_BAD;
}
len += c;
}
/* if we've read all the way to the end of the buffer, we haven't read a
* full line (mutt_socket_readln strips the \r, so we always have at least
* one character free when we've read a full line) */
while (len == idata->blen);
/* don't let one large string make cmd->buf hog memory forever */
if ((idata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
{
safe_realloc (&idata->buf, IMAP_CMD_BUFSIZE);
idata->blen = IMAP_CMD_BUFSIZE;
dprint (3, (debugfile, "imap_cmd_step: shrank buffer to %u bytes\n", idata->blen));
}
idata->lastread = time (NULL);
/* handle untagged messages. The caller still gets its shot afterwards. */
if ((!ascii_strncmp (idata->buf, "* ", 2)
|| !ascii_strncmp (imap_next_word (idata->buf), "OK [", 4))
&& cmd_handle_untagged (<