warning fixes
[apps/madmutt.git] / imap / imap.c
index 0abf32b..db9901a 100644 (file)
@@ -16,7 +16,8 @@
 #endif
 
 #include "mutt.h"
-#include "mutt_curses.h"
+#include "ascii.h"
+#include "buffer.h"
 #include "mx.h"
 #include "globals.h"
 #include "sort.h"
@@ -65,7 +66,7 @@ int imap_access (const char *path, int flags)
   if (!(idata = imap_conn_find (&mx.account,
                                 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW :
                                 0))) {
-    FREE (&mx.mbox);
+    mem_free (&mx.mbox);
     return -1;
   }
 
@@ -73,11 +74,11 @@ int imap_access (const char *path, int flags)
 
   /* we may already be in the folder we're checking */
   if (!ascii_strcmp(idata->mailbox, mx.mbox)) {
-    FREE (&mx.mbox);
+    mem_free (&mx.mbox);
     return 0;
   }
 
-  FREE (&mx.mbox);
+  mem_free (&mx.mbox);
   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
 
   if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
@@ -137,7 +138,7 @@ int imap_delete_mailbox (CONTEXT * ctx, IMAP_MBOX mx)
     if (!(idata = imap_conn_find (&mx.account,
                                   option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW
                                   : 0))) {
-      FREE (&mx.mbox);
+      mem_free (&mx.mbox);
       return -1;
     }
   }
@@ -181,7 +182,7 @@ void imap_logout_all (void)
 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
  *   buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
  *   Apparently even literals use \r\n-terminated strings ?! */
-int imap_read_literal (FILE * fp, IMAP_DATA * idata, long bytes)
+int imap_read_literal (FILE * fp, IMAP_DATA * idata, long bytes, progress_t* bar)
 {
   long pos;
   char c;
@@ -210,8 +211,10 @@ int imap_read_literal (FILE * fp, IMAP_DATA * idata, long bytes)
       r = 0;
 #endif
     fputc (c, fp);
+    if (bar && !(pos % 1024))
+      mutt_progress_bar (bar, pos);
 #ifdef DEBUG
-    if (DebugLevel >= IMAP_LOG_LTRL)
+    if (DebugFile && DebugLevel >= IMAP_LOG_LTRL)
       fputc (c, DebugFile);
 #endif
   }
@@ -240,7 +243,7 @@ void imap_expunge_mailbox (IMAP_DATA * idata)
       if (idata->cache[cacheno].uid == HEADER_DATA (h)->uid &&
           idata->cache[cacheno].path) {
         unlink (idata->cache[cacheno].path);
-        FREE (&idata->cache[cacheno].path);
+        mem_free (&idata->cache[cacheno].path);
       }
 
       imap_free_header_data (&h->data);
@@ -333,6 +336,7 @@ IMAP_DATA *imap_conn_find (const ACCOUNT * account, int flags)
   CONNECTION *conn;
   IMAP_DATA *idata;
   ACCOUNT *creds;
+  int new = 0;
 
   if (!(conn = mutt_conn_find (NULL, account)))
     return NULL;
@@ -369,6 +373,7 @@ IMAP_DATA *imap_conn_find (const ACCOUNT * account, int flags)
 
     conn->data = idata;
     idata->conn = conn;
+    new = 1;
   }
 
   if (idata->state == IMAP_DISCONNECTED)
@@ -382,10 +387,15 @@ IMAP_DATA *imap_conn_find (const ACCOUNT * account, int flags)
     else
       mutt_account_unsetpass (&idata->conn->account);
 
-    FREE (&idata->capstr);
+    mem_free (&idata->capstr);
   }
-  if (idata->state == IMAP_AUTHENTICATED)
+  if (new && idata->state == IMAP_AUTHENTICATED) {
     imap_get_delim (idata);
+    if (option (OPTIMAPCHECKSUBSCRIBED)) {
+      mutt_message _("Checking mailbox subscriptions");
+      imap_exec (idata, "LSUB \"\" \"*\"", 0);
+    }
+  }
 
   return idata;
 }
@@ -424,10 +434,8 @@ int imap_open_connection (IMAP_DATA * idata)
         if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
           goto bail;
         if (rc != -2) {
-#ifdef USE_SSL
+#if defined (USE_SSL) || defined (USE_GNUTLS)
           if (mutt_ssl_starttls (idata->conn))
-#elif USE_GNUTLS
-          if (mutt_gnutls_starttls (idata->conn))
 #endif
           {
             mutt_error (_("Could not negotiate TLS connection"));
@@ -454,7 +462,7 @@ int imap_open_connection (IMAP_DATA * idata)
     idata->state = IMAP_AUTHENTICATED;
     if (imap_check_capabilities (idata) != 0)
       goto bail;
-    FREE (&idata->capstr);
+    mem_free (&idata->capstr);
   }
   else {
     imap_error ("imap_open_connection()", buf);
@@ -467,7 +475,7 @@ err_close_conn:
   mutt_socket_close (idata->conn);
   idata->state = IMAP_DISCONNECTED;
 bail:
-  FREE (&idata->capstr);
+  mem_free (&idata->capstr);
   return -1;
 }
 
@@ -549,11 +557,11 @@ int imap_open_mailbox (CONTEXT * ctx)
 
   /* Clean up path and replace the one in the ctx */
   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
-  FREE (&(idata->mailbox));
+  mem_free (&(idata->mailbox));
   idata->mailbox = str_dup (buf);
   imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
 
-  FREE (&(ctx->path));
+  mem_free (&(ctx->path));
   ctx->path = str_dup (buf);
 
   idata->ctx = ctx;
@@ -682,8 +690,8 @@ int imap_open_mailbox (CONTEXT * ctx)
   }
 
   ctx->hdrmax = count;
-  ctx->hdrs = safe_calloc (count, sizeof (HEADER *));
-  ctx->v2r = safe_calloc (count, sizeof (int));
+  ctx->hdrs = mem_calloc (count, sizeof (HEADER *));
+  ctx->v2r = mem_calloc (count, sizeof (int));
   ctx->msgcount = 0;
   if (count && (imap_read_headers (idata, 0, count - 1) < 0)) {
     mutt_error _("Error opening mailbox");
@@ -693,14 +701,14 @@ int imap_open_mailbox (CONTEXT * ctx)
   }
 
   debug_print (2, ("msgcount is %d\n", ctx->msgcount));
-  FREE (&mx.mbox);
+  mem_free (&mx.mbox);
   return 0;
 
 fail:
   if (idata->state == IMAP_SELECTED)
     idata->state = IMAP_AUTHENTICATED;
 fail_noidata:
-  FREE (&mx.mbox);
+  mem_free (&mx.mbox);
   return -1;
 }
 
@@ -719,7 +727,7 @@ int imap_open_mailbox_append (CONTEXT * ctx)
    * ctx is brand new and mostly empty */
 
   if (!(idata = imap_conn_find (&(mx.account), 0))) {
-    FREE(&mx.mbox);
+    mem_free(&mx.mbox);
     return (-1);
   }
   conn = idata->conn;
@@ -729,7 +737,7 @@ int imap_open_mailbox_append (CONTEXT * ctx)
 
   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
 
-  FREE(&mx.mbox);
+  mem_free(&mx.mbox);
 
   /* really we should also check for W_OK */
   if (!imap_access (ctx->path, F_OK))
@@ -753,8 +761,8 @@ void imap_logout (IMAP_DATA * idata)
   idata->status = IMAP_BYE;
   imap_cmd_start (idata, "LOGOUT");
   while (imap_cmd_step (idata) == IMAP_CMD_CONTINUE);
-  FREE (&idata->cmd.buf);
-  FREE (&idata);
+  mem_free (&idata->cmd.buf);
+  mem_free (&idata);
 }
 
 /*
@@ -806,7 +814,7 @@ int imap_make_msg_set (IMAP_DATA * idata, BUFFER * buf, int flag, int changed)
   int started = 0;
 
   /* make copy of header pointers to sort in natural order */
-  hdrs = safe_calloc (idata->ctx->msgcount, sizeof (HEADER *));
+  hdrs = mem_calloc (idata->ctx->msgcount, sizeof (HEADER *));
   memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER *));
 
   if (Sort != SORT_ORDER) {
@@ -862,7 +870,7 @@ int imap_make_msg_set (IMAP_DATA * idata, BUFFER * buf, int flag, int changed)
     }
   }
 
-  FREE (&hdrs);
+  mem_free (&hdrs);
 
   return count;
 }
@@ -1045,10 +1053,10 @@ int imap_sync_mailbox (CONTEXT * ctx, int expunge, int *index_hint)
   rc = 0;
 out:
   if (cmd.data)
-    FREE (&cmd.data);
+    mem_free (&cmd.data);
   if (appendctx) {
     mx_fastclose_mailbox (appendctx);
-    FREE (&appendctx);
+    mem_free (&appendctx);
   }
   return rc;
 }
@@ -1074,7 +1082,7 @@ void imap_close_mailbox (CONTEXT * ctx)
     }
 
     idata->reopen &= IMAP_REOPEN_ALLOW;
-    FREE (&(idata->mailbox));
+    mem_free (&(idata->mailbox));
     mutt_free_list (&idata->flags);
     idata->ctx = NULL;
   }
@@ -1086,7 +1094,7 @@ void imap_close_mailbox (CONTEXT * ctx)
   for (i = 0; i < IMAP_CACHE_LEN; i++) {
     if (idata->cache[i].path) {
       unlink (idata->cache[i].path);
-      FREE (&idata->cache[i].path);
+      mem_free (&idata->cache[i].path);
     }
   }
 }
@@ -1108,7 +1116,7 @@ int imap_check_mailbox (CONTEXT * ctx, int *index_hint, int force)
 
   idata = (IMAP_DATA *) ctx->data;
 
-  if ((force || time (NULL) > idata->lastread + Timeout)
+  if ((force || time (NULL) >= idata->lastread + Timeout)
       && imap_exec (idata, "NOOP", 0) != 0)
     return -1;
 
@@ -1128,9 +1136,12 @@ int imap_check_mailbox (CONTEXT * ctx, int *index_hint, int force)
   return result;
 }
 
-/* returns count of recent messages if new = 1, else count of total messages.
- * (useful for at least postponed function)
- * Question of taste: use RECENT or UNSEEN for new?
+/*
+ * count messages:
+ *      new == 1:  recent
+ *      new == 2:  unseen
+ *      otherwise: total
+ * return:
  *   0+   number of messages in mailbox
  *  -1    error while polling mailboxes
  */
@@ -1155,13 +1166,13 @@ int imap_mailbox_check (char *path, int new)
     connflags = M_IMAP_CONN_NONEW;
 
   if (!(idata = imap_conn_find (&(mx.account), connflags))) {
-    FREE (&mx.mbox);
+    mem_free (&mx.mbox);
     return -1;
   }
   conn = idata->conn;
 
   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
-  FREE (&mx.mbox);
+  mem_free (&mx.mbox);
 
   imap_munge_mbox_name (mbox, sizeof (mbox), buf);
   strfcpy (mbox_unquoted, buf, sizeof (mbox_unquoted));
@@ -1178,7 +1189,7 @@ int imap_mailbox_check (char *path, int new)
   else if (mutt_bit_isset (idata->capabilities, IMAP4REV1) ||
            mutt_bit_isset (idata->capabilities, STATUS)) {
     snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox,
-              new ? "RECENT" : "MESSAGES");
+              new == 1 ? "RECENT" : (new == 2 ? "UNSEEN" : "MESSAGES"));
   }
   else
     /* Server does not support STATUS, and this is not the current mailbox.
@@ -1217,6 +1228,138 @@ int imap_mailbox_check (char *path, int new)
   return msgcount;
 }
 
+/* returns number of patterns in the search that should be done server-side
+ * (eg are full-text) */
+static int do_search (const pattern_t* search, int allpats)
+{
+  int rc = 0;
+  const pattern_t* pat;
+
+  for (pat = search; pat; pat = pat->next) {
+    switch (pat->op) {
+      case M_BODY:
+      case M_HEADER:
+      case M_WHOLE_MSG:
+        if (pat->stringmatch)
+          rc++;
+        break;
+      default:
+      if (pat->child && do_search (pat->child, 1))
+        rc++;
+    }
+
+    if (!allpats)
+      break;
+  }
+
+  return rc;
+}
+
+/* convert mutt pattern_t to IMAP SEARCH command containing only elements
+* that require full-text search (mutt already has what it needs for most
+* match types, and does a better job (eg server doesn't support regexps). */
+static int imap_compile_search (const pattern_t* pat, BUFFER* buf)
+{
+  if (! do_search (pat, 0))
+    return 0;
+
+  if (pat->not)
+    mutt_buffer_addstr (buf, "NOT ");
+
+  if (pat->child) {
+    int clauses;
+
+    if ((clauses = do_search (pat->child, 1)) > 0) {
+      const pattern_t* clause = pat->child;
+
+      mutt_buffer_addch (buf, '(');
+
+      while (clauses) {
+        if (do_search (clause, 0)) {
+          if (pat->op == M_OR && clauses > 1)
+            mutt_buffer_addstr (buf, "OR ");
+          clauses--;
+          if (imap_compile_search (clause, buf) < 0)
+            return -1;
+
+          if (clauses)
+            mutt_buffer_addch (buf, ' ');
+          
+          clause = clause->next;
+        }
+      }
+
+      mutt_buffer_addch (buf, ')');
+    }
+  } else {
+    char term[STRING];
+    char *delim;
+
+    switch (pat->op) {
+      case M_HEADER:
+        mutt_buffer_addstr (buf, "HEADER ");
+
+        /* extract header name */
+        if (! (delim = strchr (pat->str, ':'))) {
+          mutt_error (_("Header search without header name: %s"), pat->str);
+          return -1;
+        }
+        *delim = '\0';
+        imap_quote_string (term, sizeof (term), pat->str);
+        mutt_buffer_addstr (buf, term);
+        mutt_buffer_addch (buf, ' ');
+
+        /* and field */
+        *delim = ':';
+        delim++;
+        SKIPWS(delim);
+        imap_quote_string (term, sizeof (term), delim);
+        mutt_buffer_addstr (buf, term);
+        break;
+
+      case M_BODY:
+        mutt_buffer_addstr (buf, "BODY ");
+        imap_quote_string (term, sizeof (term), pat->str);
+        mutt_buffer_addstr (buf, term);
+        break;
+
+      case M_WHOLE_MSG:
+        mutt_buffer_addstr (buf, "TEXT ");
+        imap_quote_string (term, sizeof (term), pat->str);
+        mutt_buffer_addstr (buf, term);
+      break;
+    }
+  }
+
+  return 0;
+}
+
+int imap_search (CONTEXT* ctx, const pattern_t* pat) {
+  BUFFER buf;
+  IMAP_DATA* idata = (IMAP_DATA*)ctx->data;
+  int i;
+
+  for (i = 0; i < ctx->msgcount; i++)
+    ctx->hdrs[i]->matched = 0;
+
+  if (!do_search (pat, 1))
+    return 0;
+
+  memset (&buf, 0, sizeof (buf));
+  mutt_buffer_addstr (&buf, "UID SEARCH ");
+  if (imap_compile_search (pat, &buf) < 0) {
+    mem_free (&buf.data);
+    return -1;
+  }
+  if (imap_exec (idata, buf.data, 0) < 0) {
+    mem_free (&buf.data);
+    return -1;
+  }
+
+  mem_free (&buf.data);
+  return 0;
+}
+
 /* all this listing/browsing is a mess. I don't like that name is a pointer
  *   into idata->buf (used to be a pointer into the passed in buffer, just
  *   as bad), nor do I like the fact that the fetch is done here. This
@@ -1297,37 +1440,49 @@ int imap_subscribe (char *path, int subscribe)
   IMAP_DATA *idata;
   char buf[LONG_STRING];
   char mbox[LONG_STRING];
+  char errstr[STRING];
+  BUFFER err, token;
   IMAP_MBOX mx;
 
-  if (mx_get_magic (path) == M_IMAP || imap_parse_path (path, &mx)) {
+  if (mx_get_magic (path) != M_IMAP || imap_parse_path (path, &mx) < 0) {
     mutt_error (_("Bad mailbox name"));
     return -1;
   }
 
-
   if (!(idata = imap_conn_find (&(mx.account), 0)))
     goto fail;
 
   conn = idata->conn;
 
   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
+
+  if (option (OPTIMAPCHECKSUBSCRIBED)) {
+    memset (&token, 0, sizeof (token));
+    err.data = errstr;
+    err.dsize = sizeof (errstr);
+    snprintf (mbox, sizeof (mbox), "%smailboxes \"%s\"",
+              subscribe ? "" : "un", path);
+    if (mutt_parse_rc_line (mbox, &token, &err))
+      debug_print (1, ("Error adding subscribed mailbox: %s\n", errstr));
+    mem_free (&token.data);
+  }
+
   if (subscribe)
     mutt_message (_("Subscribing to %s..."), buf);
   else
     mutt_message (_("Unsubscribing to %s..."), buf);
   imap_munge_mbox_name (mbox, sizeof (mbox), buf);
 
-  snprintf (buf, sizeof (buf), "%s %s", subscribe ? "SUBSCRIBE" :
-            "UNSUBSCRIBE", mbox);
+  snprintf (buf, sizeof (buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mbox);
 
   if (imap_exec (idata, buf, 0) < 0)
     goto fail;
 
-  FREE (&mx.mbox);
+  mem_free (&mx.mbox);
   return 0;
 
 fail:
-  FREE (&mx.mbox);
+  mem_free (&mx.mbox);
   return -1;
 }
 
@@ -1367,7 +1522,7 @@ static int imap_complete_hosts (char *dest, size_t len) {
     }
   }
 
-  for (conn = mutt_socket_head (); conn->next; conn = conn->next) {
+  for (conn = mutt_socket_head (); conn && conn->next; conn = conn->next) {
     ciss_url_t url;
     char urlstr[LONG_STRING];
 
@@ -1414,7 +1569,7 @@ int imap_complete (char *dest, size_t dlen, char *path) {
   /* don't open a new socket just for completion. Instead complete over
    * known mailboxes/hooks/etc */
   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW))) {
-    FREE (&mx.mbox);
+    mem_free (&mx.mbox);
     strfcpy (dest, path, dlen);
     return imap_complete_hosts (dest, dlen);
   }
@@ -1470,7 +1625,7 @@ int imap_complete (char *dest, size_t dlen, char *path) {
     imap_qualify_path (dest, dlen, &mx, completion);
     mutt_pretty_mailbox (dest);
 
-    FREE (&mx.mbox);
+    mem_free (&mx.mbox);
     return 0;
   }