]> git.llucax.com Git - software/mutt-debian.git/blobdiff - imap/command.c
Move Mutt with NNTP support to mutt-nntp package
[software/mutt-debian.git] / imap / command.c
index ef835ecf7e00e4b3cba4495b01f2bae695d97c7b..3330d989c52bfe540fc61fdad420f6ca8e08e36d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
  * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
- * Copyright (C) 1999-2006 Brendan Cully <brendan@kublai.com>
+ * 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
@@ -37,6 +37,9 @@
 #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);
@@ -67,47 +70,11 @@ static char *Capabilities[] = {
   NULL
 };
 
-/* imap_cmd_queue: Add command to command queue. Fails if the queue is full. */
-int imap_cmd_queue (IMAP_DATA* idata, const char* cmdstr)
-{
-  IMAP_COMMAND* cmd;
-
-  if (idata->status == IMAP_FATAL)
-  {
-    cmd_handle_fatal (idata);
-    return IMAP_CMD_BAD;
-  }
-
-  if (!(cmd = cmd_new (idata)))
-    return IMAP_CMD_BAD;
-
-  if (mutt_buffer_printf (idata->cmdbuf, "%s%s %s\r\n",
-      idata->state == IMAP_IDLE ? "DONE\r\n" : "", cmd->seq, cmdstr) < 0)
-    return IMAP_CMD_BAD;
-
-  if (idata->state == IMAP_IDLE)
-    idata->state = IMAP_SELECTED;
-
-  return 0;
-}
-
 /* 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)
 {
-  int rc;
-
-  if (cmdstr && (rc = imap_cmd_queue (idata, cmdstr)) < 0)
-    return rc;
-
-  /* don't write old or empty commands */
-  if (idata->cmdbuf->dptr == idata->cmdbuf->data)
-    return IMAP_CMD_BAD;
-
-  rc = mutt_socket_write (idata->conn, idata->cmdbuf->data);
-  idata->cmdbuf->dptr = idata->cmdbuf->data;
-
-  return (rc < 0) ? IMAP_CMD_BAD : 0;
+  return cmd_start (idata, cmdstr, 0);
 }
 
 /* imap_cmd_step: Reads server responses from an IMAP command, detects
@@ -190,7 +157,7 @@ int imap_cmd_step (IMAP_DATA* idata)
        if (!stillrunning)
        {
          /* first command in queue has finished - move queue pointer up */
-         idata->lastcmd = (idata->lastcmd + 1) % IMAP_PIPELINE_DEPTH;
+         idata->lastcmd = (idata->lastcmd + 1) % idata->cmdslots;
        }
        cmd->state = cmd_status (idata->buf);
        /* bogus - we don't know which command result to return here. Caller
@@ -201,7 +168,7 @@ int imap_cmd_step (IMAP_DATA* idata)
        stillrunning++;
     }
 
-    c = (c + 1) % IMAP_PIPELINE_DEPTH;
+    c = (c + 1) % idata->cmdslots;
   }
   while (c != idata->nextcmd);
 
@@ -223,40 +190,56 @@ int imap_code (const char *s)
   return cmd_status (s) == IMAP_CMD_OK;
 }
 
+/* imap_cmd_trailer: extra information after tagged command response if any */
+const char* imap_cmd_trailer (IMAP_DATA* idata)
+{
+  static const char* notrailer = "";
+  const char* s = idata->buf;
+
+  if (!s)
+  {
+    dprint (2, (debugfile, "imap_cmd_trailer: not a tagged response"));
+    return notrailer;
+  }
+
+  s = imap_next_word ((char *)s);
+  if (!s || (ascii_strncasecmp (s, "OK", 2) &&
+            ascii_strncasecmp (s, "NO", 2) &&
+            ascii_strncasecmp (s, "BAD", 3)))
+  {
+    dprint (2, (debugfile, "imap_cmd_trailer: not a command completion: %s",
+               idata->buf));
+    return notrailer;
+  }
+
+  s = imap_next_word ((char *)s);
+  if (!s)
+    return notrailer;
+
+  return s;
+}
+
 /* imap_exec: execute a command, and wait for the response from the server.
  * Also, handle untagged responses.
  * Flags:
  *   IMAP_CMD_FAIL_OK: the calling procedure can handle failure. This is used
  *     for checking for a mailbox on append and login
  *   IMAP_CMD_PASS: command contains a password. Suppress logging.
+ *   IMAP_CMD_QUEUE: only queue command, do not execute.
  * Return 0 on success, -1 on Failure, -2 on OK Failure
  */
 int imap_exec (IMAP_DATA* idata, const char* cmdstr, int flags)
 {
   int rc;
 
-  if (idata->status == IMAP_FATAL)
+  if ((rc = cmd_start (idata, cmdstr, flags)) < 0)
   {
     cmd_handle_fatal (idata);
     return -1;
   }
 
-  if (cmdstr && (rc = imap_cmd_queue (idata, cmdstr)) < 0)
-    return rc;
-  
-  /* don't write old or empty commands */
-  if (idata->cmdbuf->dptr == idata->cmdbuf->data)
-    return IMAP_CMD_BAD;
-  
-  rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1,
-    flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD);
-  idata->cmdbuf->dptr = idata->cmdbuf->data;
-
-  if (rc < 0)
-  {
-    cmd_handle_fatal (idata);
-    return -1;
-  }
+  if (flags & IMAP_CMD_QUEUE)
+    return 0;
 
   do
     rc = imap_cmd_step (idata);
@@ -323,20 +306,55 @@ void imap_cmd_finish (IMAP_DATA* idata)
   idata->status = 0;
 }
 
+/* imap_cmd_idle: Enter the IDLE state. */
+int imap_cmd_idle (IMAP_DATA* idata)
+{
+  int rc;
+
+  imap_cmd_start (idata, "IDLE");
+  do
+    rc = imap_cmd_step (idata);
+  while (rc == IMAP_CMD_CONTINUE);
+
+  if (rc == IMAP_CMD_RESPOND)
+  {
+    /* successfully entered IDLE state */
+    idata->state = IMAP_IDLE;
+    /* queue automatic exit when next command is issued */
+    mutt_buffer_printf (idata->cmdbuf, "DONE\r\n");
+    rc = IMAP_CMD_OK;
+  }
+  if (rc != IMAP_CMD_OK)
+  {
+    dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n"));
+    return -1;
+  }
+  
+  return 0;
+}
+
+static int cmd_queue_full (IMAP_DATA* idata)
+{
+  if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
+    return 1;
+
+  return 0;
+}
+
 /* sets up a new command control block and adds it to the queue.
  * Returns NULL if the pipeline is full. */
 static IMAP_COMMAND* cmd_new (IMAP_DATA* idata)
 {
   IMAP_COMMAND* cmd;
 
-  if ((idata->nextcmd + 1) % IMAP_PIPELINE_DEPTH == idata->lastcmd)
+  if (cmd_queue_full (idata))
   {
-    dprint (2, (debugfile, "cmd_new: IMAP command queue full\n"));
+    dprint (3, (debugfile, "cmd_new: IMAP command queue full\n"));
     return NULL;
   }
 
   cmd = idata->cmds + idata->nextcmd;
-  idata->nextcmd = (idata->nextcmd + 1) % IMAP_PIPELINE_DEPTH;
+  idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
 
   snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
   if (idata->seqno > 9999)
@@ -347,6 +365,61 @@ static IMAP_COMMAND* cmd_new (IMAP_DATA* idata)
   return cmd;
 }
 
+/* queues command. If the queue is full, attempts to drain it. */
+static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
+{
+  IMAP_COMMAND* cmd;
+  int rc;
+
+  if (cmd_queue_full (idata))
+  {
+    dprint (3, (debugfile, "Draining IMAP command pipeline\n"));
+
+    rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
+
+    if (rc < 0 && rc != -2)
+      return rc;
+  }
+
+  if (!(cmd = cmd_new (idata)))
+    return IMAP_CMD_BAD;
+
+  if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
+    return IMAP_CMD_BAD;
+
+  return 0;
+}
+
+static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
+{
+  int rc;
+
+  if (idata->status == IMAP_FATAL)
+  {
+    cmd_handle_fatal (idata);
+    return -1;
+  }
+
+  if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
+    return rc;
+
+  if (flags & IMAP_CMD_QUEUE)
+    return 0;
+
+  if (idata->cmdbuf->dptr == idata->cmdbuf->data)
+    return IMAP_CMD_BAD;
+
+  rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1,
+                            flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD);
+  idata->cmdbuf->dptr = idata->cmdbuf->data;
+
+  /* unidle when command queue is flushed */
+  if (idata->state == IMAP_IDLE)
+    idata->state = IMAP_SELECTED;
+
+  return (rc < 0) ? IMAP_CMD_BAD : 0;
+}
+
 /* parse response line for tagged OK/NO/BAD */
 static int cmd_status (const char *s)
 {
@@ -663,7 +736,7 @@ static void cmd_parse_list (IMAP_DATA* idata, char* s)
   if (list->name[0] == '\0')
   {
     idata->delim = list->delim;
-    dprint (2, (debugfile, "Root delimiter: %c\n", idata->delim));
+    dprint (3, (debugfile, "Root delimiter: %c\n", idata->delim));
   }
 }
 
@@ -692,11 +765,14 @@ static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
   if (!list.name)
     return;
 
-  dprint (2, (debugfile, "Subscribing to %s\n", list.name));
+  dprint (3, (debugfile, "Subscribing to %s\n", list.name));
 
   strfcpy (buf, "mailboxes \"", sizeof (buf));
   mutt_account_tourl (&idata->conn->account, &url);
-  url.path = list.name;
+  /* escape \ and " */
+  imap_quote_string(errstr, sizeof (errstr), list.name);
+  url.path = errstr + 1;
+  url.path[strlen(url.path) - 1] = '\0';
   if (!mutt_strcmp (url.user, ImapUser))
     url.user = NULL;
   url_ciss_tostring (&url, buf + 11, sizeof (buf) - 10, 0);
@@ -818,11 +894,30 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s)
   int count;
   IMAP_STATUS *status;
   unsigned int olduv, oldun;
+  long litlen;
 
   mailbox = imap_next_word (s);
-  s = imap_next_word (mailbox);
-  *(s - 1) = '\0';
-  imap_unmunge_mbox_name (mailbox);
+
+  /* We need a real tokenizer. */
+  if (!imap_get_literal_count (mailbox, &litlen))
+  {
+    if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
+    {
+      idata->status = IMAP_FATAL;
+      return;
+    }
+    mailbox = idata->buf;
+    s = mailbox + litlen;
+    *s = '\0';
+    s++;
+    SKIPWS(s);
+  }
+  else
+  {
+    s = imap_next_word (mailbox);
+    *(s - 1) = '\0';
+    imap_unmunge_mbox_name (mailbox);
+  }
 
   status = imap_mboxcache_get (idata, mailbox, 1);
   olduv = status->uidvalidity;
@@ -879,7 +974,7 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s)
     }
     /* dprint (2, (debugfile, "Buffy entry: [%s] mbox: [%s]\n", inc->path, NONULL(mx.mbox))); */
     
-    if (mutt_account_match (&idata->conn->account, &mx.account))
+    if (imap_account_match (&idata->conn->account, &mx.account))
     {
       if (mx.mbox)
       {
@@ -895,14 +990,19 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s)
         dprint (3, (debugfile, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
                     mailbox, olduv, oldun, status->unseen));
         
-        if (olduv && olduv == status->uidvalidity)
-        {
-          if (oldun < status->uidnext)
-            inc->new = status->unseen;
-        }
-        else if (!olduv && !oldun)
-         /* first check per session, use recent. might need a flag for this. */
-         inc->new = status->recent;
+       if (option(OPTMAILCHECKRECENT))
+       {
+         if (olduv && olduv == status->uidvalidity)
+         {
+           if (oldun < status->uidnext)
+             inc->new = status->unseen;
+         }
+         else if (!olduv && !oldun)
+           /* first check per session, use recent. might need a flag for this. */
+           inc->new = status->recent;
+         else
+           inc->new = status->unseen;
+       }
        else
           inc->new = status->unseen;