]> granicus.if.org Git - neomutt/commitdiff
tidy: rearrange code to avoid forward declarations
authorRichard Russon <rich@flatcap.org>
Wed, 15 Mar 2017 03:50:02 +0000 (03:50 +0000)
committerRichard Russon <rich@flatcap.org>
Wed, 22 Mar 2017 16:14:06 +0000 (16:14 +0000)
This change, though huge, is quite simple.

It simply rearranges functions, within files, to remove the need to
forward-declare them.  There were ~90 such declarations.

Forward declarations are rarely *needed* and just add to the cruft that
needs to be maintained.

30 files changed:
buffy.c
color.c
compose.c
curs_main.c
dotlock.c
imap/browse.c
imap/command.c
imap/message.c
init.c
lib.c
mbox.c
mbyte.c
mh.c
mutt_sasl.c
mutt_socket.c
mutt_ssl.c
mutt_ssl_gnutls.c
mutt_tunnel.c
newsrc.c
nntp.c
pager.c
pattern.c
pgppubring.c
query.c
remailer.c
rfc2231.c
sendlib.c
sidebar.c
smtp.c
thread.c

diff --git a/buffy.c b/buffy.c
index 17749ea79394efc4d3234ef7321fde1335944aa4..02db2997bcbcd9c8d95d12b96a059991acb4f324 100644 (file)
--- a/buffy.c
+++ b/buffy.c
@@ -53,8 +53,6 @@ time_t BuffyDoneTime = 0;     /* last time we knew for sure how much mail there was.
 static short BuffyCount = 0;   /* how many boxes with new mail */
 static short BuffyNotify = 0;  /* # of unnotified new boxes */
 
-static BUFFY* buffy_get (const char *path);
-
 /* Find the last message in the file.
  * upon success return 0. If no message found - return -1 */
 
@@ -144,63 +142,6 @@ static int test_new_folder (const char *path)
   return rc;
 }
 
-void mutt_buffy_cleanup (const char *buf, struct stat *st)
-{
-  struct utimbuf ut;
-  BUFFY *tmp;
-
-  if (option(OPTCHECKMBOXSIZE))
-  {
-    tmp = mutt_find_mailbox (buf);
-    if (tmp && !tmp->new)
-      mutt_update_mailbox (tmp);
-  }
-  else
-  {
-    /* fix up the times so buffy won't get confused */
-    if (st->st_mtime > st->st_atime)
-    {
-      ut.actime = st->st_atime;
-      ut.modtime = time (NULL);
-      utime (buf, &ut);
-    }
-    else
-      utime (buf, NULL);
-  }
-}
-
-BUFFY *mutt_find_mailbox (const char *path)
-{
-  BUFFY *tmp = NULL;
-  struct stat sb;
-  struct stat tmp_sb;
-
-  if (stat (path,&sb) != 0)
-    return NULL;
-
-  for (tmp = Incoming; tmp; tmp = tmp->next)
-  {
-    if (stat (tmp->path,&tmp_sb) ==0 &&
-       sb.st_dev == tmp_sb.st_dev && sb.st_ino == tmp_sb.st_ino)
-      break;
-  }
-  return tmp;
-}
-
-void mutt_update_mailbox (BUFFY * b)
-{
-  struct stat sb;
-
-  if (!b)
-    return;
-
-  if (stat (b->path, &sb) == 0)
-    b->size = (off_t) sb.st_size;
-  else
-    b->size = 0;
-  return;
-}
-
 static BUFFY *buffy_new (const char *path)
 {
   BUFFY* buffy;
@@ -224,91 +165,6 @@ static void buffy_free (BUFFY **mailbox)
   FREE (mailbox); /* __FREE_CHECKED__ */
 }
 
-int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
-{
-  BUFFY **tmp,*tmp1;
-  char buf[_POSIX_PATH_MAX];
-  struct stat sb;
-  char f1[PATH_MAX];
-  char *p;
-
-  while (MoreArgs (s))
-  {
-    mutt_extract_token (path, s, 0);
-    strfcpy (buf, path->data, sizeof (buf));
-
-    if(data == MUTT_UNMAILBOXES && mutt_strcmp(buf,"*") == 0)
-    {
-      for (tmp = &Incoming; *tmp;)
-      {
-        tmp1=(*tmp)->next;
-#ifdef USE_SIDEBAR
-       mutt_sb_notify_mailbox (*tmp, 0);
-#endif
-        buffy_free (tmp);
-        *tmp=tmp1;
-      }
-      return 0;
-    }
-
-    mutt_expand_path (buf, sizeof (buf));
-
-    /* Skip empty tokens. */
-    if(!*buf) continue;
-
-    /* avoid duplicates */
-    p = realpath (buf, f1);
-    for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next))
-    {
-      if (mutt_strcmp (p ? p : buf, (*tmp)->realpath) == 0)
-      {
-        mutt_debug (3, "mailbox '%s' already registered as '%s'\n",
-                    buf, (*tmp)->path);
-       break;
-      }
-    }
-
-    if(data == MUTT_UNMAILBOXES)
-    {
-      if(*tmp)
-      {
-        tmp1=(*tmp)->next;
-#ifdef USE_SIDEBAR
-       mutt_sb_notify_mailbox (*tmp, 0);
-#endif
-        buffy_free (tmp);
-        *tmp=tmp1;
-      }
-      continue;
-    }
-
-    if (!*tmp) {
-      *tmp = buffy_new (buf);
-#ifdef USE_SIDEBAR
-      mutt_sb_notify_mailbox (*tmp, 1);
-#endif
-    }
-
-    (*tmp)->new = 0;
-    (*tmp)->notified = 1;
-    (*tmp)->newly_created = 0;
-
-    /* for check_mbox_size, it is important that if the folder is new (tested by
-     * reading it), the size is set to 0 so that later when we check we see
-     * that it increased .  without check_mbox_size we probably don't care.
-     */
-    if (option(OPTCHECKMBOXSIZE) &&
-       stat ((*tmp)->path, &sb) == 0 && !test_new_folder ((*tmp)->path))
-    {
-      /* some systems out there don't have an off_t type */
-      (*tmp)->size = (off_t) sb.st_size;
-    }
-    else
-      (*tmp)->size = 0;
-  }
-  return 0;
-}
-
 /* Checks the specified maildir subdir (cur or new) for new mail or mail counts.
  * check_new:   if true, check for new mail.
  * check_stats: if true, count total, new, and flagged messages.
@@ -467,109 +323,6 @@ static int buffy_mbox_check (BUFFY* mailbox, struct stat *sb, int check_stats)
   return rc;
 }
 
-#ifdef USE_NOTMUCH
-int mutt_parse_virtual_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
-{
-  BUFFY **tmp;
-  char buf[_POSIX_PATH_MAX + LONG_STRING + 32];   /* path to DB + query + URI "decoration" */
-
-  while (MoreArgs (s))
-  {
-    char *desc;
-
-    mutt_extract_token (path, s, 0);
-    if (path->data && *path->data)
-      desc = safe_strdup( path->data);
-    else
-      continue;
-
-    mutt_extract_token (path, s, 0);
-    nm_normalize_uri(buf, path->data, sizeof(buf));
-
-    /* Skip empty tokens. */
-    if(!*buf) {
-           FREE(&desc);
-           continue;
-    }
-
-    /* avoid duplicates */
-    for (tmp = &VirtIncoming; *tmp; tmp = &((*tmp)->next))
-    {
-      if (mutt_strcmp (buf, (*tmp)->path) == 0)
-      {
-        mutt_debug (3, "virtual mailbox '%s' already registered as '%s'\n",
-                    buf, (*tmp)->path);
-       break;
-      }
-    }
-
-    if (!*tmp)
-      *tmp = buffy_new (buf);
-
-    (*tmp)->magic = MUTT_NOTMUCH;
-    (*tmp)->new = 0;
-    (*tmp)->notified = 1;
-    (*tmp)->newly_created = 0;
-    (*tmp)->size = 0;
-    (*tmp)->desc = desc;
-#ifdef USE_SIDEBAR
-    mutt_sb_notify_mailbox (*tmp, 1);
-#endif
-  }
-#ifdef USE_SIDEBAR
-  mutt_sb_draw();
-#endif
-  return 0;
-}
-
-int mutt_parse_unvirtual_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
-{
-  BUFFY **tmp, *tmp1;
-
-  while (MoreArgs (s))
-  {
-    mutt_extract_token (path, s, 0);
-
-    if (mutt_strcmp (path->data, "*") == 0)
-    {
-      for (tmp = &VirtIncoming; *tmp;)
-      {
-        tmp1 = (*tmp)->next;
-#ifdef USE_SIDEBAR
-        mutt_sb_notify_mailbox (*tmp, 0);
-#endif
-        buffy_free (tmp);
-        *tmp = tmp1;
-      }
-#ifdef USE_SIDEBAR
-      mutt_sb_draw();
-#endif
-      return 0;
-    }
-
-    for (tmp = &VirtIncoming; *tmp; tmp = &((*tmp)->next))
-    {
-      if ((mutt_strcasecmp (path->data, (*tmp)->path) == 0) ||
-          (mutt_strcasecmp (path->data, (*tmp)->desc) == 0))
-      {
-        tmp1 = (*tmp)->next;
-#ifdef USE_SIDEBAR
-        mutt_sb_notify_mailbox (*tmp, 0);
-#endif
-        buffy_free (tmp);
-        *tmp = tmp1;
-        break;
-      }
-    }
-  }
-
-#ifdef USE_SIDEBAR
-  mutt_sb_draw();
-#endif
-  return 0;
-}
-#endif
-
 static void buffy_check (BUFFY *tmp, struct stat *contex_sb, int check_stats)
 {
     struct stat sb;
@@ -678,15 +431,287 @@ static void buffy_check (BUFFY *tmp, struct stat *contex_sb, int check_stats)
       BuffyNotify++;
 }
 
-/* Check all Incoming for new mail and total/new/flagged messages
- * force: if true, ignore BuffyTimeout and check for new mail anyway
- */
-int mutt_buffy_check (int force)
+/* fetch buffy object for given path, if present */
+static BUFFY* buffy_get (const char *path)
 {
-  BUFFY *tmp;
-  struct stat contex_sb;
-  time_t t;
-  int check_stats = 0;
+  BUFFY *cur;
+  char *epath;
+
+  if (!path)
+    return NULL;
+
+  epath = safe_strdup(path);
+  mutt_expand_path(epath, mutt_strlen(epath));
+
+  for (cur = Incoming; cur; cur = cur->next)
+  {
+    /* must be done late because e.g. IMAP delimiter may change */
+    mutt_expand_path (cur->path, sizeof (cur->path));
+    if (!mutt_strcmp(cur->path, path))
+    {
+      FREE (&epath);
+      return cur;
+    }
+  }
+
+  FREE (&epath);
+  return NULL;
+}
+
+void mutt_buffy_cleanup (const char *buf, struct stat *st)
+{
+  struct utimbuf ut;
+  BUFFY *tmp;
+
+  if (option(OPTCHECKMBOXSIZE))
+  {
+    tmp = mutt_find_mailbox (buf);
+    if (tmp && !tmp->new)
+      mutt_update_mailbox (tmp);
+  }
+  else
+  {
+    /* fix up the times so buffy won't get confused */
+    if (st->st_mtime > st->st_atime)
+    {
+      ut.actime = st->st_atime;
+      ut.modtime = time (NULL);
+      utime (buf, &ut);
+    }
+    else
+      utime (buf, NULL);
+  }
+}
+
+BUFFY *mutt_find_mailbox (const char *path)
+{
+  BUFFY *tmp = NULL;
+  struct stat sb;
+  struct stat tmp_sb;
+
+  if (stat (path,&sb) != 0)
+    return NULL;
+
+  for (tmp = Incoming; tmp; tmp = tmp->next)
+  {
+    if (stat (tmp->path,&tmp_sb) ==0 &&
+       sb.st_dev == tmp_sb.st_dev && sb.st_ino == tmp_sb.st_ino)
+      break;
+  }
+  return tmp;
+}
+
+void mutt_update_mailbox (BUFFY * b)
+{
+  struct stat sb;
+
+  if (!b)
+    return;
+
+  if (stat (b->path, &sb) == 0)
+    b->size = (off_t) sb.st_size;
+  else
+    b->size = 0;
+  return;
+}
+
+int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
+{
+  BUFFY **tmp,*tmp1;
+  char buf[_POSIX_PATH_MAX];
+  struct stat sb;
+  char f1[PATH_MAX];
+  char *p;
+
+  while (MoreArgs (s))
+  {
+    mutt_extract_token (path, s, 0);
+    strfcpy (buf, path->data, sizeof (buf));
+
+    if(data == MUTT_UNMAILBOXES && mutt_strcmp(buf,"*") == 0)
+    {
+      for (tmp = &Incoming; *tmp;)
+      {
+        tmp1=(*tmp)->next;
+#ifdef USE_SIDEBAR
+       mutt_sb_notify_mailbox (*tmp, 0);
+#endif
+        buffy_free (tmp);
+        *tmp=tmp1;
+      }
+      return 0;
+    }
+
+    mutt_expand_path (buf, sizeof (buf));
+
+    /* Skip empty tokens. */
+    if(!*buf) continue;
+
+    /* avoid duplicates */
+    p = realpath (buf, f1);
+    for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next))
+    {
+      if (mutt_strcmp (p ? p : buf, (*tmp)->realpath) == 0)
+      {
+        mutt_debug (3, "mailbox '%s' already registered as '%s'\n",
+                    buf, (*tmp)->path);
+       break;
+      }
+    }
+
+    if(data == MUTT_UNMAILBOXES)
+    {
+      if(*tmp)
+      {
+        tmp1=(*tmp)->next;
+#ifdef USE_SIDEBAR
+       mutt_sb_notify_mailbox (*tmp, 0);
+#endif
+        buffy_free (tmp);
+        *tmp=tmp1;
+      }
+      continue;
+    }
+
+    if (!*tmp) {
+      *tmp = buffy_new (buf);
+#ifdef USE_SIDEBAR
+      mutt_sb_notify_mailbox (*tmp, 1);
+#endif
+    }
+
+    (*tmp)->new = 0;
+    (*tmp)->notified = 1;
+    (*tmp)->newly_created = 0;
+
+    /* for check_mbox_size, it is important that if the folder is new (tested by
+     * reading it), the size is set to 0 so that later when we check we see
+     * that it increased .  without check_mbox_size we probably don't care.
+     */
+    if (option(OPTCHECKMBOXSIZE) &&
+       stat ((*tmp)->path, &sb) == 0 && !test_new_folder ((*tmp)->path))
+    {
+      /* some systems out there don't have an off_t type */
+      (*tmp)->size = (off_t) sb.st_size;
+    }
+    else
+      (*tmp)->size = 0;
+  }
+  return 0;
+}
+
+#ifdef USE_NOTMUCH
+int mutt_parse_virtual_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
+{
+  BUFFY **tmp;
+  char buf[_POSIX_PATH_MAX + LONG_STRING + 32];   /* path to DB + query + URI "decoration" */
+
+  while (MoreArgs (s))
+  {
+    char *desc;
+
+    mutt_extract_token (path, s, 0);
+    if (path->data && *path->data)
+      desc = safe_strdup( path->data);
+    else
+      continue;
+
+    mutt_extract_token (path, s, 0);
+    nm_normalize_uri(buf, path->data, sizeof(buf));
+
+    /* Skip empty tokens. */
+    if(!*buf) {
+           FREE(&desc);
+           continue;
+    }
+
+    /* avoid duplicates */
+    for (tmp = &VirtIncoming; *tmp; tmp = &((*tmp)->next))
+    {
+      if (mutt_strcmp (buf, (*tmp)->path) == 0)
+      {
+        mutt_debug (3, "virtual mailbox '%s' already registered as '%s'\n",
+                    buf, (*tmp)->path);
+       break;
+      }
+    }
+
+    if (!*tmp)
+      *tmp = buffy_new (buf);
+
+    (*tmp)->magic = MUTT_NOTMUCH;
+    (*tmp)->new = 0;
+    (*tmp)->notified = 1;
+    (*tmp)->newly_created = 0;
+    (*tmp)->size = 0;
+    (*tmp)->desc = desc;
+#ifdef USE_SIDEBAR
+    mutt_sb_notify_mailbox (*tmp, 1);
+#endif
+  }
+#ifdef USE_SIDEBAR
+  mutt_sb_draw();
+#endif
+  return 0;
+}
+
+int mutt_parse_unvirtual_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
+{
+  BUFFY **tmp, *tmp1;
+
+  while (MoreArgs (s))
+  {
+    mutt_extract_token (path, s, 0);
+
+    if (mutt_strcmp (path->data, "*") == 0)
+    {
+      for (tmp = &VirtIncoming; *tmp;)
+      {
+        tmp1 = (*tmp)->next;
+#ifdef USE_SIDEBAR
+        mutt_sb_notify_mailbox (*tmp, 0);
+#endif
+        buffy_free (tmp);
+        *tmp = tmp1;
+      }
+#ifdef USE_SIDEBAR
+      mutt_sb_draw();
+#endif
+      return 0;
+    }
+
+    for (tmp = &VirtIncoming; *tmp; tmp = &((*tmp)->next))
+    {
+      if ((mutt_strcasecmp (path->data, (*tmp)->path) == 0) ||
+          (mutt_strcasecmp (path->data, (*tmp)->desc) == 0))
+      {
+        tmp1 = (*tmp)->next;
+#ifdef USE_SIDEBAR
+        mutt_sb_notify_mailbox (*tmp, 0);
+#endif
+        buffy_free (tmp);
+        *tmp = tmp1;
+        break;
+      }
+    }
+  }
+
+#ifdef USE_SIDEBAR
+  mutt_sb_draw();
+#endif
+  return 0;
+}
+#endif
+
+/* Check all Incoming for new mail and total/new/flagged messages
+ * force: if true, ignore BuffyTimeout and check for new mail anyway
+ */
+int mutt_buffy_check (int force)
+{
+  BUFFY *tmp;
+  struct stat contex_sb;
+  time_t t;
+  int check_stats = 0;
   contex_sb.st_dev=0;
   contex_sb.st_ino=0;
 
@@ -886,29 +911,3 @@ void mutt_buffy_vfolder (char *s, size_t slen)
 }
 #endif
 
-/* fetch buffy object for given path, if present */
-static BUFFY* buffy_get (const char *path)
-{
-  BUFFY *cur;
-  char *epath;
-
-  if (!path)
-    return NULL;
-
-  epath = safe_strdup(path);
-  mutt_expand_path(epath, mutt_strlen(epath));
-
-  for (cur = Incoming; cur; cur = cur->next)
-  {
-    /* must be done late because e.g. IMAP delimiter may change */
-    mutt_expand_path (cur->path, sizeof (cur->path));
-    if (!mutt_strcmp(cur->path, path))
-    {
-      FREE (&epath);
-      return cur;
-    }
-  }
-
-  FREE (&epath);
-  return NULL;
-}
diff --git a/color.c b/color.c
index b67674368b1076162536c3a641fed179034abee4..7fa4f4c3e949631e1b10d8349837ee875848110f 100644 (file)
--- a/color.c
+++ b/color.c
@@ -391,31 +391,6 @@ parse_color_name (const char *s, int *col, int *attr, int is_fg, BUFFER *err)
 #endif
 
 
-/* usage: uncolor index pattern [pattern...]
- *       unmono  index pattern [pattern...]
- */
-
-static int
-_mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err,
-                        short parse_uncolor);
-
-
-#ifdef HAVE_COLOR
-
-int mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data,
-                       BUFFER *err)
-{
-  return _mutt_parse_uncolor(buf, s, data, err, 1);
-}
-
-#endif
-
-int mutt_parse_unmono (BUFFER *buf, BUFFER *s, unsigned long data,
-                      BUFFER *err)
-{
-  return _mutt_parse_uncolor(buf, s, data, err, 0);
-}
-
 void
 mutt_do_uncolor (BUFFER *buf, BUFFER *s, COLOR_LINE **ColorList,
                  int *do_cache, int parse_uncolor)
@@ -467,6 +442,10 @@ mutt_do_uncolor (BUFFER *buf, BUFFER *s, COLOR_LINE **ColorList,
   } while (MoreArgs (s));
 }
 
+/* usage: uncolor index pattern [pattern...]
+ *       unmono  index pattern [pattern...]
+ */
+
 static int _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data,
                                BUFFER *err, short parse_uncolor)
 {
@@ -555,6 +534,22 @@ static int _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data,
   return (0);
 }
 
+#ifdef HAVE_COLOR
+
+int mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data,
+                       BUFFER *err)
+{
+  return _mutt_parse_uncolor(buf, s, data, err, 1);
+}
+
+#endif
+
+int mutt_parse_unmono (BUFFER *buf, BUFFER *s, unsigned long data,
+                      BUFFER *err)
+{
+  return _mutt_parse_uncolor(buf, s, data, err, 0);
+}
+
 
 static int
 add_pattern (COLOR_LINE **top, const char *s, int sensitive,
index 0f261757667ebbf83da21169e9f9807ca81d7b2e..1f5b0770a2083c4f70750c55adc51285b007e7ad 100644 (file)
--- a/compose.c
+++ b/compose.c
@@ -454,8 +454,7 @@ static unsigned long cum_attachs_size (MUTTMENU *menu)
 }
 
 /* prototype for use below */
-static void compose_status_line (char *buf, size_t buflen, size_t col, int cols, MUTTMENU *menu,
-      const char *p);
+static void compose_status_line (char *buf, size_t buflen, size_t col, int cols, MUTTMENU *menu, const char *p);
 
 /*
  * compose_format_str()
@@ -527,7 +526,6 @@ static void compose_status_line (char *buf, size_t buflen, size_t col, int cols,
         (unsigned long) menu, 0);
 }
 
-
 /* return values:
  *
  * 1   message should be postponed
index 7f0d8e1deeb848d0e7a0e5df2de8e1414df1113a..2b56ce2bcb343b6567d929b12d8a05aba86bb4c5 100644 (file)
@@ -190,166 +190,6 @@ static void collapse_all(MUTTMENU *menu, int toggle)
   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
 }
 
-/* terminal status capability check. terminfo must have been initialized. */
-short mutt_ts_capability(void)
-{
-  char *term = getenv("TERM");
-  char *tcaps;
-#ifdef HAVE_USE_EXTENDED_NAMES
-  int tcapi;
-#endif
-  char **termp;
-  char *known[] = {
-    "color-xterm",
-    "cygwin",
-    "eterm",
-    "kterm",
-    "nxterm",
-    "putty",
-    "rxvt",
-    "screen",
-    "xterm",
-    NULL
-  };
-
-  /* If tsl is set, then terminfo says that status lines work. */
-  tcaps = tigetstr("tsl");
-  if (tcaps && tcaps != (char *)-1 && *tcaps)
-  {
-    /* update the static defns of tsl/fsl from terminfo */
-    tsl = safe_strdup(tcaps);
-
-    tcaps = tigetstr("fsl");
-    if (tcaps && tcaps != (char *)-1 && *tcaps)
-      fsl = safe_strdup(tcaps);
-
-    return 1;
-  }
-
-  /* If XT (boolean) is set, then this terminal supports the standard escape. */
-  /* Beware: tigetflag returns -1 if XT is invalid or not a boolean. */
-#ifdef HAVE_USE_EXTENDED_NAMES
-  use_extended_names (true);
-  tcapi = tigetflag("XT");
-  if (tcapi == 1)
-    return 1;
-#endif /* HAVE_USE_EXTENDED_NAMES */
-
-  /* Check term types that are known to support the standard escape without
-   * necessarily asserting it in terminfo. */
-  for (termp = known; termp; termp++)
-  {
-    if (term && *termp && mutt_strncasecmp (term, *termp, strlen(*termp)))
-      return 1;
-  }
-
-  /* not supported */
-  return 0;
-}
-
-void mutt_ts_status(char *str)
-{
-  /* If empty, do not set.  To clear, use a single space. */
-  if (str == NULL || *str == '\0')
-    return;
-  fprintf(stderr, "%s%s%s", tsl, str, fsl);
-}
-
-void mutt_ts_icon(char *str)
-{
-  /* If empty, do not set.  To clear, use a single space. */
-  if (str == NULL || *str == '\0')
-    return;
-
-  /* icon setting is not supported in terminfo, so hardcode the escape - yuck */
-  fprintf(stderr, "\033]1;%s\007", str);
-}
-
-void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num)
-{
-  if (!Context || !menu || (num < 0) || (num >= Context->hdrmax))
-    return;
-
-  HEADER *h = Context->hdrs[Context->v2r[num]];
-  if (!h)
-    return;
-
-  format_flag flag = MUTT_FORMAT_MAKEPRINT | MUTT_FORMAT_ARROWCURSOR | MUTT_FORMAT_INDEX;
-  int edgemsgno, reverse = Sort & SORT_REVERSE;
-  THREAD *tmp;
-
-  if ((Sort & SORT_MASK) == SORT_THREADS && h->tree)
-  {
-    flag |= MUTT_FORMAT_TREE; /* display the thread tree */
-    if (h->display_subject)
-      flag |= MUTT_FORMAT_FORCESUBJ;
-    else
-    {
-      if (reverse)
-      {
-       if (menu->top + menu->pagelen > menu->max)
-         edgemsgno = Context->v2r[menu->max - 1];
-       else
-         edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
-      }
-      else
-       edgemsgno = Context->v2r[menu->top];
-
-      for (tmp = h->thread->parent; tmp; tmp = tmp->parent)
-      {
-       if (!tmp->message)
-         continue;
-
-       /* if no ancestor is visible on current screen, provisionally force
-        * subject... */
-       if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
-       {
-         flag |= MUTT_FORMAT_FORCESUBJ;
-         break;
-       }
-       else if (tmp->message->virtual >= 0)
-         break;
-      }
-      if (flag & MUTT_FORMAT_FORCESUBJ)
-      {
-       for (tmp = h->thread->prev; tmp; tmp = tmp->prev)
-       {
-         if (!tmp->message)
-           continue;
-
-         /* ...but if a previous sibling is available, don't force it */
-         if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
-           break;
-         else if (tmp->message->virtual >= 0)
-         {
-           flag &= ~MUTT_FORMAT_FORCESUBJ;
-           break;
-         }
-       }
-      }
-    }
-  }
-
-  _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
-}
-
-int index_color (int index_no)
-{
-  if (!Context || (index_no < 0))
-    return 0;
-
-  HEADER *h = Context->hdrs[Context->v2r[index_no]];
-
-  if (h && h->pair)
-    return h->pair;
-
-  mutt_set_header_color (Context, h);
-  if (h)
-    return h->pair;
-
-  return 0;
-}
-
 static int ci_next_undeleted (int msgno)
 {
   int i;
@@ -433,6 +273,33 @@ static int mx_toggle_write (CONTEXT *ctx)
   return 0;
 }
 
+static void resort_index (MUTTMENU *menu)
+{
+  int i;
+  HEADER *current = CURHDR;
+
+  menu->current = -1;
+  mutt_sort_headers (Context, 0);
+  /* Restore the current message */
+
+  for (i = 0; i < Context->vcount; i++)
+  {
+    if (Context->hdrs[Context->v2r[i]] == current)
+    {
+      menu->current = i;
+      break;
+    }
+  }
+
+  if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
+    menu->current = mutt_parent_message (Context, current, 0);
+
+  if (menu->current < 0)
+    menu->current = ci_first_message ();
+
+  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+}
+
 void update_index (MUTTMENU *menu, CONTEXT *ctx, int check,
                          int oldcount, int index_hint)
 {
@@ -544,31 +411,246 @@ void update_index (MUTTMENU *menu, CONTEXT *ctx, int check,
 
 }
 
-static void resort_index (MUTTMENU *menu)
+static int main_change_folder(MUTTMENU *menu, int op, char *buf, size_t bufsz,
+                         int *oldcount, int *index_hint, int flags)
 {
-  int i;
-  HEADER *current = CURHDR;
+#ifdef USE_NNTP
+  if (option (OPTNEWS))
+  {
+    unset_option (OPTNEWS);
+    nntp_expand_path (buf, bufsz, &CurrentNewsSrv->conn->account);
+  }
+  else
+#endif
+  mutt_expand_path (buf, bufsz);
+  if (mx_get_magic (buf) <= 0)
+  {
+    mutt_error (_("%s is not a mailbox."), buf);
+    return -1;
+  }
+  mutt_str_replace (&CurrentFolder, buf);
 
-  menu->current = -1;
-  mutt_sort_headers (Context, 0);
-  /* Restore the current message */
+  /* keepalive failure in mutt_enter_fname may kill connection. #3028 */
+  if (Context && !Context->path)
+    FREE (&Context);
 
-  for (i = 0; i < Context->vcount; i++)
+  if (Context)
   {
-    if (Context->hdrs[Context->v2r[i]] == current)
+    int check;
+
+#ifdef USE_COMPRESSED
+         if (Context->compress_info && Context->realpath)
+           mutt_str_replace (&LastFolder, Context->realpath);
+         else
+#endif
+    mutt_str_replace (&LastFolder, Context->path);
+    *oldcount = Context ? Context->msgcount : 0;
+
+    if ((check = mx_close_mailbox (Context, index_hint)) != 0)
     {
-      menu->current = i;
-      break;
+      if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
+        update_index (menu, Context, check, *oldcount, *index_hint);
+
+      set_option (OPTSEARCHINVALID);
+      menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+      return 0;
     }
+    FREE (&Context);
   }
 
-  if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
-    menu->current = mutt_parent_message (Context, current, 0);
+  mutt_sleep (0);
 
-  if (menu->current < 0)
+  /* Set CurrentMenu to MENU_MAIN before executing any folder
+   * hooks so that all the index menu functions are available to
+   * the exec command.
+   */
+
+  CurrentMenu = MENU_MAIN;
+  mutt_folder_hook (buf);
+
+  if ((Context = mx_open_mailbox (buf,
+               (option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
+               MUTT_READONLY : 0, NULL)) != NULL)
+  {
     menu->current = ci_first_message ();
+  }
+  else
+    menu->current = 0;
 
-  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+  if (((Sort & SORT_MASK) == SORT_THREADS) && option (OPTCOLLAPSEALL))
+    collapse_all (menu, 0);
+
+#ifdef USE_SIDEBAR
+        mutt_sb_set_open_buffy ();
+#endif
+
+  mutt_clear_error ();
+  mutt_buffy_check(1); /* force the buffy check after we have changed the folder */
+  menu->redraw = REDRAW_FULL;
+  set_option (OPTSEARCHINVALID);
+
+  return 0;
+}
+
+
+/* terminal status capability check. terminfo must have been initialized. */
+short mutt_ts_capability(void)
+{
+  char *term = getenv("TERM");
+  char *tcaps;
+#ifdef HAVE_USE_EXTENDED_NAMES
+  int tcapi;
+#endif
+  char **termp;
+  char *known[] = {
+    "color-xterm",
+    "cygwin",
+    "eterm",
+    "kterm",
+    "nxterm",
+    "putty",
+    "rxvt",
+    "screen",
+    "xterm",
+    NULL
+  };
+
+  /* If tsl is set, then terminfo says that status lines work. */
+  tcaps = tigetstr("tsl");
+  if (tcaps && tcaps != (char *)-1 && *tcaps)
+  {
+    /* update the static defns of tsl/fsl from terminfo */
+    tsl = safe_strdup(tcaps);
+
+    tcaps = tigetstr("fsl");
+    if (tcaps && tcaps != (char *)-1 && *tcaps)
+      fsl = safe_strdup(tcaps);
+
+    return 1;
+  }
+
+  /* If XT (boolean) is set, then this terminal supports the standard escape. */
+  /* Beware: tigetflag returns -1 if XT is invalid or not a boolean. */
+#ifdef HAVE_USE_EXTENDED_NAMES
+  use_extended_names (true);
+  tcapi = tigetflag("XT");
+  if (tcapi == 1)
+    return 1;
+#endif /* HAVE_USE_EXTENDED_NAMES */
+
+  /* Check term types that are known to support the standard escape without
+   * necessarily asserting it in terminfo. */
+  for (termp = known; termp; termp++)
+  {
+    if (term && *termp && mutt_strncasecmp (term, *termp, strlen(*termp)))
+      return 1;
+  }
+
+  /* not supported */
+  return 0;
+}
+
+void mutt_ts_status(char *str)
+{
+  /* If empty, do not set.  To clear, use a single space. */
+  if (str == NULL || *str == '\0')
+    return;
+  fprintf(stderr, "%s%s%s", tsl, str, fsl);
+}
+
+void mutt_ts_icon(char *str)
+{
+  /* If empty, do not set.  To clear, use a single space. */
+  if (str == NULL || *str == '\0')
+    return;
+
+  /* icon setting is not supported in terminfo, so hardcode the escape - yuck */
+  fprintf(stderr, "\033]1;%s\007", str);
+}
+
+void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num)
+{
+  if (!Context || !menu || (num < 0) || (num >= Context->hdrmax))
+    return;
+
+  HEADER *h = Context->hdrs[Context->v2r[num]];
+  if (!h)
+    return;
+
+  format_flag flag = MUTT_FORMAT_MAKEPRINT | MUTT_FORMAT_ARROWCURSOR | MUTT_FORMAT_INDEX;
+  int edgemsgno, reverse = Sort & SORT_REVERSE;
+  THREAD *tmp;
+
+  if ((Sort & SORT_MASK) == SORT_THREADS && h->tree)
+  {
+    flag |= MUTT_FORMAT_TREE; /* display the thread tree */
+    if (h->display_subject)
+      flag |= MUTT_FORMAT_FORCESUBJ;
+    else
+    {
+      if (reverse)
+      {
+       if (menu->top + menu->pagelen > menu->max)
+         edgemsgno = Context->v2r[menu->max - 1];
+       else
+         edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
+      }
+      else
+       edgemsgno = Context->v2r[menu->top];
+
+      for (tmp = h->thread->parent; tmp; tmp = tmp->parent)
+      {
+       if (!tmp->message)
+         continue;
+
+       /* if no ancestor is visible on current screen, provisionally force
+        * subject... */
+       if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
+       {
+         flag |= MUTT_FORMAT_FORCESUBJ;
+         break;
+       }
+       else if (tmp->message->virtual >= 0)
+         break;
+      }
+      if (flag & MUTT_FORMAT_FORCESUBJ)
+      {
+       for (tmp = h->thread->prev; tmp; tmp = tmp->prev)
+       {
+         if (!tmp->message)
+           continue;
+
+         /* ...but if a previous sibling is available, don't force it */
+         if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
+           break;
+         else if (tmp->message->virtual >= 0)
+         {
+           flag &= ~MUTT_FORMAT_FORCESUBJ;
+           break;
+         }
+       }
+      }
+    }
+  }
+
+  _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
+}
+
+int index_color (int index_no)
+{
+  if (!Context || (index_no < 0))
+    return 0;
+
+  HEADER *h = Context->hdrs[Context->v2r[index_no]];
+
+  if (h && h->pair)
+    return h->pair;
+
+  mutt_set_header_color (Context, h);
+  if (h)
+    return h->pair;
+
+  return 0;
 }
 
 /**
@@ -705,87 +787,6 @@ dsl_finish:
   FREE(&syntax);
 }
 
-static int main_change_folder(MUTTMENU *menu, int op, char *buf, size_t bufsz,
-                         int *oldcount, int *index_hint, int flags)
-{
-#ifdef USE_NNTP
-  if (option (OPTNEWS))
-  {
-    unset_option (OPTNEWS);
-    nntp_expand_path (buf, bufsz, &CurrentNewsSrv->conn->account);
-  }
-  else
-#endif
-  mutt_expand_path (buf, bufsz);
-  if (mx_get_magic (buf) <= 0)
-  {
-    mutt_error (_("%s is not a mailbox."), buf);
-    return -1;
-  }
-  mutt_str_replace (&CurrentFolder, buf);
-
-  /* keepalive failure in mutt_enter_fname may kill connection. #3028 */
-  if (Context && !Context->path)
-    FREE (&Context);
-
-  if (Context)
-  {
-    int check;
-
-#ifdef USE_COMPRESSED
-         if (Context->compress_info && Context->realpath)
-           mutt_str_replace (&LastFolder, Context->realpath);
-         else
-#endif
-    mutt_str_replace (&LastFolder, Context->path);
-    *oldcount = Context ? Context->msgcount : 0;
-
-    if ((check = mx_close_mailbox (Context, index_hint)) != 0)
-    {
-      if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
-        update_index (menu, Context, check, *oldcount, *index_hint);
-
-      set_option (OPTSEARCHINVALID);
-      menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
-      return 0;
-    }
-    FREE (&Context);
-  }
-
-  mutt_sleep (0);
-
-  /* Set CurrentMenu to MENU_MAIN before executing any folder
-   * hooks so that all the index menu functions are available to
-   * the exec command.
-   */
-
-  CurrentMenu = MENU_MAIN;
-  mutt_folder_hook (buf);
-
-  if ((Context = mx_open_mailbox (buf,
-               (option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
-               MUTT_READONLY : 0, NULL)) != NULL)
-  {
-    menu->current = ci_first_message ();
-  }
-  else
-    menu->current = 0;
-
-  if (((Sort & SORT_MASK) == SORT_THREADS) && option (OPTCOLLAPSEALL))
-    collapse_all (menu, 0);
-
-#ifdef USE_SIDEBAR
-        mutt_sb_set_open_buffy ();
-#endif
-
-  mutt_clear_error ();
-  mutt_buffy_check(1); /* force the buffy check after we have changed the folder */
-  menu->redraw = REDRAW_FULL;
-  set_option (OPTSEARCHINVALID);
-
-  return 0;
-}
-
 static const struct mapping_t IndexHelp[] = {
   { N_("Quit"),  OP_QUIT },
   { N_("Del"),   OP_DELETE },
index cda9b511c14c856824dbc9f9401a3c3172c3301f..84afbcf3a7b68ad82425d5cbfda8e05518836a28 100644 (file)
--- a/dotlock.c
+++ b/dotlock.c
@@ -99,81 +99,13 @@ static gid_t UserGid;
 static gid_t MailGid;
 #endif
 
-static int dotlock_deference_symlink (char *, size_t, const char *);
-static int dotlock_prepare (char *, size_t, const char *, int fd);
-static int dotlock_check_stats (struct stat *, struct stat *);
-static int dotlock_dispatch (const char *, int fd);
-
-#ifdef DL_STANDALONE
-static int dotlock_init_privs (void);
-static void usage (const char *);
-#endif
-
-static void dotlock_expand_link (char *, const char *, const char *);
-static void BEGIN_PRIVILEGED (void);
-static void END_PRIVILEGED (void);
-
-/* These functions work on the current directory.
+/* These functions work on the current directory:
+ *   dotlock_try dotlock_unlock dotlock_unlink dotlock_lock
  * Invoke dotlock_prepare () before and check their
  * return value.
  */
 
-static int dotlock_try (void);
-static int dotlock_unlock (const char *);
-static int dotlock_unlink (const char *);
-static int dotlock_lock (const char *);
-
-
 #ifdef DL_STANDALONE
-
-#define check_flags(a) if (a & DL_FL_ACTIONS) usage (argv[0])
-
-int main (int argc, char **argv)
-{
-  int i;
-  char *p;
-
-  /* first, drop privileges */
-
-  if (dotlock_init_privs () == -1)
-    return DL_EX_ERROR;
-
-
-  /* determine the system's host name */
-
-  uname (&utsname);
-  if ((p = strchr (utsname.nodename, '.')))
-    *p = '\0';
-
-
-  /* parse the command line options. */
-  DotlockFlags = 0;
-
-  while ((i = getopt (argc, argv, "dtfupr:")) != EOF)
-  {
-    switch (i)
-    {
-      /* actions, mutually exclusive */
-      case 't': check_flags (DotlockFlags); DotlockFlags |= DL_FL_TRY; break;
-      case 'd': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLINK; break;
-      case 'u': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLOCK; break;
-
-      /* other flags */
-      case 'f': DotlockFlags |= DL_FL_FORCE; break;
-      case 'p': DotlockFlags |= DL_FL_USEPRIV; break;
-      case 'r': DotlockFlags |= DL_FL_RETRY; Retry = atoi (optarg); break;
-
-      default: usage (argv[0]);
-    }
-  }
-
-  if (optind == argc || Retry < 0)
-    usage (argv[0]);
-
-  return dotlock_dispatch (argv[optind], -1);
-}
-
-
 /*
  * Determine our effective group ID, and drop
  * privileges.
@@ -184,8 +116,6 @@ int main (int argc, char **argv)
  * -1 - we couldn't drop privileges.
  *
  */
-
-
 static int
 dotlock_init_privs (void)
 {
@@ -202,71 +132,7 @@ dotlock_init_privs (void)
 
   return 0;
 }
-
-
-#else  /* DL_STANDALONE */
-
-/*
- * This function is intended to be invoked from within
- * mutt instead of mx.c's invoke_dotlock ().
- */
-
-int dotlock_invoke (const char *path, int fd, int flags, int retry)
-{
-  int currdir;
-  int r;
-
-  DotlockFlags = flags;
-
-  if ((currdir = open (".", O_RDONLY)) == -1)
-    return DL_EX_ERROR;
-
-  if (!(DotlockFlags & DL_FL_RETRY) || retry)
-    Retry = MAXLOCKATTEMPT;
-  else
-    Retry = 0;
-
-  r = dotlock_dispatch (path, fd);
-
-  fchdir (currdir);
-  close (currdir);
-
-  return r;
-}
-
-#endif  /* DL_STANDALONE */
-
-
-static int dotlock_dispatch (const char *f, int fd)
-{
-  char realpath[_POSIX_PATH_MAX];
-
-  /* If dotlock_prepare () succeeds [return value == 0],
-   * realpath contains the basename of f, and we have
-   * successfully changed our working directory to
-   * `dirname $f`.  Additionally, f has been opened for
-   * reading to verify that the user has at least read
-   * permissions on that file.
-   *
-   * For a more detailed explanation of all this, see the
-   * lengthy comment below.
-   */
-
-  if (dotlock_prepare (realpath, sizeof (realpath), f, fd) != 0)
-    return DL_EX_ERROR;
-
-  /* Actually perform the locking operation. */
-
-  if (DotlockFlags & DL_FL_TRY)
-    return dotlock_try ();
-  else if (DotlockFlags & DL_FL_UNLOCK)
-    return dotlock_unlock (realpath);
-  else if (DotlockFlags & DL_FL_UNLINK)
-    return dotlock_unlink (realpath);
-  else /* lock */
-    return dotlock_lock (realpath);
-}
-
+#endif /* DL_STANDALONE */
 
 /*
  * Get privileges
@@ -278,7 +144,6 @@ static int dotlock_dispatch (const char *f, int fd)
  * BEGIN_PRIVILEGES () won't return if an error occurs.
  *
  */
-
 static void
 BEGIN_PRIVILEGED (void)
 {
@@ -301,7 +166,6 @@ BEGIN_PRIVILEGED (void)
  * END_PRIVILEGED () won't return if an error occurs.
  *
  */
-
 static void
 END_PRIVILEGED (void)
 {
@@ -317,14 +181,12 @@ END_PRIVILEGED (void)
 }
 
 #ifdef DL_STANDALONE
-
 /*
  * Usage information.
  *
  * This function doesn't return.
  *
  */
-
 static void
 usage (const char *av0)
 {
@@ -346,7 +208,6 @@ usage (const char *av0)
 
   exit (DL_EX_ERROR);
 }
-
 #endif
 
 /*
@@ -396,7 +257,6 @@ usage (const char *av0)
  *
  * tlr, Jul 15 1998
  */
-
 static int
 dotlock_check_stats (struct stat *fsb, struct stat *lsb)
 {
@@ -424,61 +284,6 @@ dotlock_check_stats (struct stat *fsb, struct stat *lsb)
   return 0;
 }
 
-static int
-dotlock_prepare (char *bn, size_t l, const char *f, int _fd)
-{
-  struct stat fsb, lsb;
-  char realpath[_POSIX_PATH_MAX];
-  char *basename, *dirname;
-  char *p;
-  int fd;
-  int r;
-
-  if (dotlock_deference_symlink (realpath, sizeof (realpath), f) == -1)
-    return -1;
-
-  if ((p = strrchr (realpath, '/')))
-  {
-    *p = '\0';
-    basename = p + 1;
-    dirname = realpath;
-  }
-  else
-  {
-    basename = realpath;
-    dirname = ".";
-  }
-
-  if (strlen (basename) + 1 > l)
-    return -1;
-
-  strfcpy (bn, basename, l);
-
-  if (chdir (dirname) == -1)
-    return -1;
-
-  if (_fd != -1)
-    fd = _fd;
-  else if ((fd = open (basename, O_RDONLY)) == -1)
-    return -1;
-
-  r = fstat (fd, &fsb);
-
-  if (_fd == -1)
-    close (fd);
-
-  if (r == -1)
-    return -1;
-
-  if (lstat (basename, &lsb) == -1)
-    return -1;
-
-  if (dotlock_check_stats (&fsb, &lsb) == -1)
-    return -1;
-
-  return 0;
-}
-
 /*
  * Expand a symbolic link.
  *
@@ -486,7 +291,6 @@ dotlock_prepare (char *bn, size_t l, const char *f, int _fd)
  * at least _POSIX_PATH_MAX characters.
  *
  */
-
 static void
 dotlock_expand_link (char *newpath, const char *path, const char *link)
 {
@@ -512,6 +316,32 @@ dotlock_expand_link (char *newpath, const char *path, const char *link)
   strfcpy (newpath + len, link, _POSIX_PATH_MAX - len);
 }
 
+/*
+ * Check if a file can be locked at all.
+ *
+ * The same comment as for dotlock_lock () applies here.
+ *
+ */
+static int
+dotlock_try (void)
+{
+#ifdef USE_SETGID
+  struct stat sb;
+#endif
+
+  if (access (".", W_OK) == 0)
+    return DL_EX_OK;
+
+#ifdef USE_SETGID
+  if (stat (".", &sb) == 0)
+  {
+    if ((sb.st_mode & S_IWGRP) == S_IWGRP && sb.st_gid == MailGid)
+      return DL_EX_NEED_PRIVS;
+  }
+#endif
+
+  return DL_EX_IMPOSSIBLE;
+}
 
 /*
  * Deference a chain of symbolic links
@@ -519,7 +349,6 @@ dotlock_expand_link (char *newpath, const char *path, const char *link)
  * The final path is written to d.
  *
  */
-
 static int
 dotlock_deference_symlink (char *d, size_t l, const char *path)
 {
@@ -559,6 +388,63 @@ dotlock_deference_symlink (char *d, size_t l, const char *path)
   return 0;
 }
 
+static int
+dotlock_prepare (char *bn, size_t l, const char *f, int _fd)
+{
+  struct stat fsb, lsb;
+  char realpath[_POSIX_PATH_MAX];
+  char *basename, *dirname;
+  char *p;
+  int fd;
+  int r;
+
+  if (dotlock_deference_symlink (realpath, sizeof (realpath), f) == -1)
+    return -1;
+
+  if ((p = strrchr (realpath, '/')))
+  {
+    *p = '\0';
+    basename = p + 1;
+    dirname = realpath;
+  }
+  else
+  {
+    basename = realpath;
+    dirname = ".";
+  }
+
+  if (strlen (basename) + 1 > l)
+    return -1;
+
+  strfcpy (bn, basename, l);
+
+  if (chdir (dirname) == -1)
+    return -1;
+
+  if (_fd != -1)
+    fd = _fd;
+  else if ((fd = open (basename, O_RDONLY)) == -1)
+    return -1;
+
+  r = fstat (fd, &fsb);
+
+  if (_fd == -1)
+    close (fd);
+
+  if (r == -1)
+    return -1;
+
+  if (lstat (basename, &lsb) == -1)
+    return -1;
+
+  if (dotlock_check_stats (&fsb, &lsb) == -1)
+    return -1;
+
+  return 0;
+}
+
+#define HARDMAXATTEMPTS 10
+
 /*
  * Dotlock a file.
  *
@@ -567,9 +453,6 @@ dotlock_deference_symlink (char *d, size_t l, const char *path)
  * dotlock_prepare () before using this function!
  *
  */
-
-#define HARDMAXATTEMPTS 10
-
 static int
 dotlock_lock (const char *realpath)
 {
@@ -592,7 +475,6 @@ dotlock_lock (const char *realpath)
        (int) getpid ());
   snprintf (lockfile, sizeof (lockfile), "%s.lock", realpath);
 
-
   BEGIN_PRIVILEGED ();
 
   unlink (nfslockfile);
@@ -601,19 +483,16 @@ dotlock_lock (const char *realpath)
   {
     END_PRIVILEGED ();
 
-
     if (errno != EAGAIN)
     {
       return DL_EX_ERROR;
     }
 
-
     BEGIN_PRIVILEGED ();
   }
 
   END_PRIVILEGED ();
 
-
   close (fd);
 
   while (hard_count++ < HARDMAXATTEMPTS)
@@ -673,14 +552,12 @@ dotlock_lock (const char *realpath)
   return DL_EX_OK;
 }
 
-
 /*
  * Unlock a file.
  *
  * The same comment as for dotlock_lock () applies here.
  *
  */
-
 static int
 dotlock_unlock (const char *realpath)
 {
@@ -719,31 +596,111 @@ dotlock_unlink (const char *realpath)
   return (i == 0) ?  DL_EX_OK : DL_EX_ERROR;
 }
 
+static int dotlock_dispatch (const char *f, int fd)
+{
+  char realpath[_POSIX_PATH_MAX];
+
+  /* If dotlock_prepare () succeeds [return value == 0],
+   * realpath contains the basename of f, and we have
+   * successfully changed our working directory to
+   * `dirname $f`.  Additionally, f has been opened for
+   * reading to verify that the user has at least read
+   * permissions on that file.
+   *
+   * For a more detailed explanation of all this, see the
+   * lengthy comment below.
+   */
 
+  if (dotlock_prepare (realpath, sizeof (realpath), f, fd) != 0)
+    return DL_EX_ERROR;
+
+  /* Actually perform the locking operation. */
+
+  if (DotlockFlags & DL_FL_TRY)
+    return dotlock_try ();
+  else if (DotlockFlags & DL_FL_UNLOCK)
+    return dotlock_unlock (realpath);
+  else if (DotlockFlags & DL_FL_UNLINK)
+    return dotlock_unlink (realpath);
+  else /* lock */
+    return dotlock_lock (realpath);
+}
+
+#ifndef DL_STANDALONE
 /*
- * Check if a file can be locked at all.
- *
- * The same comment as for dotlock_lock () applies here.
- *
+ * This function is intended to be invoked from within
+ * mutt instead of mx.c's invoke_dotlock ().
  */
+int dotlock_invoke (const char *path, int fd, int flags, int retry)
+{
+  int currdir;
+  int r;
 
-static int
-dotlock_try (void)
+  DotlockFlags = flags;
+
+  if ((currdir = open (".", O_RDONLY)) == -1)
+    return DL_EX_ERROR;
+
+  if (!(DotlockFlags & DL_FL_RETRY) || retry)
+    Retry = MAXLOCKATTEMPT;
+  else
+    Retry = 0;
+
+  r = dotlock_dispatch (path, fd);
+
+  fchdir (currdir);
+  close (currdir);
+
+  return r;
+}
+#endif  /* !DL_STANDALONE */
+
+
+#ifdef DL_STANDALONE
+
+#define check_flags(a) if (a & DL_FL_ACTIONS) usage (argv[0])
+
+int main (int argc, char **argv)
 {
-#ifdef USE_SETGID
-  struct stat sb;
-#endif
+  int i;
+  char *p;
 
-  if (access (".", W_OK) == 0)
-    return DL_EX_OK;
+  /* first, drop privileges */
 
-#ifdef USE_SETGID
-  if (stat (".", &sb) == 0)
+  if (dotlock_init_privs () == -1)
+    return DL_EX_ERROR;
+
+  /* determine the system's host name */
+
+  uname (&utsname);
+  if ((p = strchr (utsname.nodename, '.')))
+    *p = '\0';
+
+  /* parse the command line options. */
+  DotlockFlags = 0;
+
+  while ((i = getopt (argc, argv, "dtfupr:")) != EOF)
   {
-    if ((sb.st_mode & S_IWGRP) == S_IWGRP && sb.st_gid == MailGid)
-      return DL_EX_NEED_PRIVS;
+    switch (i)
+    {
+      /* actions, mutually exclusive */
+      case 't': check_flags (DotlockFlags); DotlockFlags |= DL_FL_TRY; break;
+      case 'd': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLINK; break;
+      case 'u': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLOCK; break;
+
+      /* other flags */
+      case 'f': DotlockFlags |= DL_FL_FORCE; break;
+      case 'p': DotlockFlags |= DL_FL_USEPRIV; break;
+      case 'r': DotlockFlags |= DL_FL_RETRY; Retry = atoi (optarg); break;
+
+      default: usage (argv[0]);
+    }
   }
-#endif
 
-  return DL_EX_IMPOSSIBLE;
+  if (optind == argc || Retry < 0)
+    usage (argv[0]);
+
+  return dotlock_dispatch (argv[optind], -1);
 }
+#endif
+
index b32c05938f5d81ffc48a3091a769b443974cfb4f..1bb7f51b380f2fccebef4fb0e8e3d1113dd5821b 100644 (file)
 #include "buffy.h"
 #include "imap_private.h"
 
-/* -- forward declarations -- */
-static int browse_add_list_result (IMAP_DATA* idata, const char* cmd,
-  struct browser_state* state, short isparent);
+/* imap_add_folder:
+ * add a folder name to the browser list, formatting it as necessary.
+ *
+ * The folder parameter should already be 'unmunged' via
+ * imap_unmunge_mbox_name().
+ */
 static void imap_add_folder (char delim, char *folder, int noselect,
-  int noinferiors, struct browser_state *state, short isparent);
+  int noinferiors, struct browser_state *state, short isparent)
+{
+  char tmp[LONG_STRING];
+  char relpath[LONG_STRING];
+  IMAP_MBOX mx;
+  BUFFY *b;
+
+  if (imap_parse_path (state->folder, &mx))
+    return;
+
+  if (state->entrylen + 1 == state->entrymax)
+  {
+    safe_realloc (&state->entry,
+      sizeof (struct folder_file) * (state->entrymax += 256));
+    memset (state->entry + state->entrylen, 0,
+      (sizeof (struct folder_file) * (state->entrymax - state->entrylen)));
+  }
+
+  /* render superiors as unix-standard ".." */
+  if (isparent)
+    strfcpy (relpath, "../", sizeof (relpath));
+  /* strip current folder from target, to render a relative path */
+  else if (!mutt_strncmp (mx.mbox, folder, mutt_strlen (mx.mbox)))
+    strfcpy (relpath, folder + mutt_strlen (mx.mbox), sizeof (relpath));
+  else
+    strfcpy (relpath, folder, sizeof (relpath));
+
+  /* apply filemask filter. This should really be done at menu setup rather
+   * than at scan, since it's so expensive to scan. But that's big changes
+   * to browser.c */
+  if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not))
+  {
+    FREE (&mx.mbox);
+    return;
+  }
+
+  imap_qualify_path (tmp, sizeof (tmp), &mx, folder);
+  (state->entry)[state->entrylen].name = safe_strdup (tmp);
+
+  /* mark desc with delim in browser if it can have subfolders */
+  if (!isparent && !noinferiors && strlen (relpath) < sizeof (relpath) - 1)
+  {
+    relpath[strlen (relpath) + 1] = '\0';
+    relpath[strlen (relpath)] = delim;
+  }
+
+  (state->entry)[state->entrylen].desc = safe_strdup (relpath);
+
+  (state->entry)[state->entrylen].imap = 1;
+  /* delimiter at the root is useless. */
+  if (folder[0] == '\0')
+    delim = '\0';
+  (state->entry)[state->entrylen].delim = delim;
+  (state->entry)[state->entrylen].selectable = !noselect;
+  (state->entry)[state->entrylen].inferiors = !noinferiors;
+
+  b = Incoming;
+  while (b && mutt_strcmp (tmp, b->path))
+    b = b->next;
+  if (b)
+  {
+    if (Context &&
+        !mutt_strcmp (b->realpath, Context->realpath))
+    {
+      b->msg_count = Context->msgcount;
+      b->msg_unread = Context->unread;
+    }
+    (state->entry)[state->entrylen].has_buffy = 1;
+    (state->entry)[state->entrylen].new = b->new;
+    (state->entry)[state->entrylen].msg_count = b->msg_count;
+    (state->entry)[state->entrylen].msg_unread = b->msg_unread;
+  }
+
+  (state->entrylen)++;
+
+  FREE (&mx.mbox);
+}
+
+static int browse_add_list_result (IMAP_DATA* idata, const char* cmd,
+  struct browser_state* state, short isparent)
+{
+  IMAP_LIST list;
+  IMAP_MBOX mx;
+  int rc;
+
+  if (imap_parse_path (state->folder, &mx))
+  {
+    mutt_debug (2, "browse_add_list_result: current folder %s makes no sense\n",
+                state->folder);
+    return -1;
+  }
+
+  imap_cmd_start (idata, cmd);
+  idata->cmdtype = IMAP_CT_LIST;
+  idata->cmddata = &list;
+  do
+  {
+    list.name = NULL;
+    rc = imap_cmd_step (idata);
+
+    if (rc == IMAP_CMD_CONTINUE && list.name)
+    {
+      /* Let a parent folder never be selectable for navigation */
+      if (isparent)
+        list.noselect = 1;
+      /* prune current folder from output */
+      if (isparent || mutt_strncmp (list.name, mx.mbox, strlen (list.name)))
+        imap_add_folder (list.delim, list.name, list.noselect, list.noinferiors,
+                         state, isparent);
+    }
+  }
+  while (rc == IMAP_CMD_CONTINUE);
+  idata->cmddata = NULL;
+
+  FREE (&mx.mbox);
+  return rc == IMAP_CMD_OK ? 0 : -1;
+}
 
 /* imap_browse: IMAP hook into the folder browser, fills out browser_state,
  *   given a current folder to browse */
@@ -313,127 +432,3 @@ int imap_mailbox_rename(const char* mailbox)
   return -1;
 }
 
-static int browse_add_list_result (IMAP_DATA* idata, const char* cmd,
-  struct browser_state* state, short isparent)
-{
-  IMAP_LIST list;
-  IMAP_MBOX mx;
-  int rc;
-
-  if (imap_parse_path (state->folder, &mx))
-  {
-    mutt_debug (2, "browse_add_list_result: current folder %s makes no sense\n",
-                state->folder);
-    return -1;
-  }
-
-  imap_cmd_start (idata, cmd);
-  idata->cmdtype = IMAP_CT_LIST;
-  idata->cmddata = &list;
-  do
-  {
-    list.name = NULL;
-    rc = imap_cmd_step (idata);
-
-    if (rc == IMAP_CMD_CONTINUE && list.name)
-    {
-      /* Let a parent folder never be selectable for navigation */
-      if (isparent)
-        list.noselect = 1;
-      /* prune current folder from output */
-      if (isparent || mutt_strncmp (list.name, mx.mbox, strlen (list.name)))
-        imap_add_folder (list.delim, list.name, list.noselect, list.noinferiors,
-                         state, isparent);
-    }
-  }
-  while (rc == IMAP_CMD_CONTINUE);
-  idata->cmddata = NULL;
-
-  FREE (&mx.mbox);
-  return rc == IMAP_CMD_OK ? 0 : -1;
-}
-
-/* imap_add_folder:
- * add a folder name to the browser list, formatting it as necessary.
- *
- * The folder parameter should already be 'unmunged' via
- * imap_unmunge_mbox_name().
- */
-static void imap_add_folder (char delim, char *folder, int noselect,
-  int noinferiors, struct browser_state *state, short isparent)
-{
-  char tmp[LONG_STRING];
-  char relpath[LONG_STRING];
-  IMAP_MBOX mx;
-  BUFFY *b;
-
-  if (imap_parse_path (state->folder, &mx))
-    return;
-
-  if (state->entrylen + 1 == state->entrymax)
-  {
-    safe_realloc (&state->entry,
-      sizeof (struct folder_file) * (state->entrymax += 256));
-    memset (state->entry + state->entrylen, 0,
-      (sizeof (struct folder_file) * (state->entrymax - state->entrylen)));
-  }
-
-  /* render superiors as unix-standard ".." */
-  if (isparent)
-    strfcpy (relpath, "../", sizeof (relpath));
-  /* strip current folder from target, to render a relative path */
-  else if (!mutt_strncmp (mx.mbox, folder, mutt_strlen (mx.mbox)))
-    strfcpy (relpath, folder + mutt_strlen (mx.mbox), sizeof (relpath));
-  else
-    strfcpy (relpath, folder, sizeof (relpath));
-
-  /* apply filemask filter. This should really be done at menu setup rather
-   * than at scan, since it's so expensive to scan. But that's big changes
-   * to browser.c */
-  if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not))
-  {
-    FREE (&mx.mbox);
-    return;
-  }
-
-  imap_qualify_path (tmp, sizeof (tmp), &mx, folder);
-  (state->entry)[state->entrylen].name = safe_strdup (tmp);
-
-  /* mark desc with delim in browser if it can have subfolders */
-  if (!isparent && !noinferiors && strlen (relpath) < sizeof (relpath) - 1)
-  {
-    relpath[strlen (relpath) + 1] = '\0';
-    relpath[strlen (relpath)] = delim;
-  }
-
-  (state->entry)[state->entrylen].desc = safe_strdup (relpath);
-
-  (state->entry)[state->entrylen].imap = 1;
-  /* delimiter at the root is useless. */
-  if (folder[0] == '\0')
-    delim = '\0';
-  (state->entry)[state->entrylen].delim = delim;
-  (state->entry)[state->entrylen].selectable = !noselect;
-  (state->entry)[state->entrylen].inferiors = !noinferiors;
-
-  b = Incoming;
-  while (b && mutt_strcmp (tmp, b->path))
-    b = b->next;
-  if (b)
-  {
-    if (Context &&
-        !mutt_strcmp (b->realpath, Context->realpath))
-    {
-      b->msg_count = Context->msgcount;
-      b->msg_unread = Context->unread;
-    }
-    (state->entry)[state->entrylen].has_buffy = 1;
-    (state->entry)[state->entrylen].new = b->new;
-    (state->entry)[state->entrylen].msg_count = b->msg_count;
-    (state->entry)[state->entrylen].msg_unread = b->msg_unread;
-  }
-
-  (state->entrylen)++;
-
-  FREE (&mx.mbox);
-}
index 92575c68e09763c8bbddd3ea63e6f210800aa5bb..50f2b6a567a0b3d0dbb81266141afd16d9a4b740 100644 (file)
 
 #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 void cmd_parse_enabled (IMAP_DATA* idata, const char* s);
-
 static const char * const Capabilities[] = {
   "IMAP4",
   "IMAP4rev1",
@@ -69,392 +51,611 @@ static const char * const Capabilities[] = {
   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)
+static int cmd_queue_full (IMAP_DATA* idata)
 {
-  return cmd_start (idata, cmdstr, 0);
+  if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
+    return 1;
+
+  return 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)
+/* 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)
 {
-  size_t len = 0;
-  int c;
-  int rc;
-  int stillrunning = 0;
   IMAP_COMMAND* cmd;
 
-  if (idata->status == IMAP_FATAL)
+  if (cmd_queue_full (idata))
   {
-    cmd_handle_fatal (idata);
-    return IMAP_CMD_BAD;
+    mutt_debug (3, "cmd_new: IMAP command queue full\n");
+    return NULL;
   }
 
-  /* 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;
-      mutt_debug (3, "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)
-    {
-      mutt_debug (1, "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);
+  cmd = idata->cmds + idata->nextcmd;
+  idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
 
-  /* 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;
-    mutt_debug (3, "imap_cmd_step: shrank buffer to %u bytes\n", idata->blen);
-  }
+  snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
+  if (idata->seqno > 9999)
+    idata->seqno = 0;
 
-  idata->lastread = time (NULL);
+  cmd->state = IMAP_CMD_NEW;
 
-  /* 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 (idata))
-    return IMAP_CMD_BAD;
+  return cmd;
+}
 
-  /* server demands a continuation response from us */
-  if (idata->buf[0] == '+')
-    return IMAP_CMD_RESPOND;
+/* 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;
 
-  /* look for tagged command completions */
-  rc = IMAP_CMD_CONTINUE;
-  c = idata->lastcmd;
-  do
+  if (cmd_queue_full (idata))
   {
-    cmd = &idata->cmds[c];
-    if (cmd->state == IMAP_CMD_NEW)
-    {
-      if (!ascii_strncmp (idata->buf, cmd->seq, SEQLEN)) {
-       if (!stillrunning)
-       {
-         /* first command in queue has finished - move queue pointer up */
-         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
-        * should provide a tag. */
-       rc = cmd->state;
-      }
-      else
-       stillrunning++;
-    }
+    mutt_debug (3, "Draining IMAP command pipeline\n");
 
-    c = (c + 1) % idata->cmdslots;
-  }
-  while (c != idata->nextcmd);
+    rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
 
-  if (stillrunning)
-    rc = IMAP_CMD_CONTINUE;
-  else
-  {
-    mutt_debug (3, "IMAP queue drained\n");
-    imap_cmd_finish (idata);
+    if (rc < 0 && rc != -2)
+      return rc;
   }
 
+  if (!(cmd = cmd_new (idata)))
+    return IMAP_CMD_BAD;
 
-  return rc;
-}
+  if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
+    return IMAP_CMD_BAD;
 
-/* imap_code: returns 1 if the command result was OK, or 0 if NO or BAD */
-int imap_code (const char *s)
-{
-  return cmd_status (s) == IMAP_CMD_OK;
+  return 0;
 }
 
-/* imap_cmd_trailer: extra information after tagged command response if any */
-const char* imap_cmd_trailer (IMAP_DATA* idata)
+/* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
+static void cmd_handle_fatal (IMAP_DATA* idata)
 {
-  static const char* notrailer = "";
-  const char* s = idata->buf;
+  idata->status = IMAP_FATAL;
 
-  if (!s)
+  if ((idata->state >= IMAP_SELECTED) &&
+      (idata->reopen & IMAP_REOPEN_ALLOW))
   {
-    mutt_debug (2, "imap_cmd_trailer: not a tagged response");
-    return notrailer;
+    mx_fastclose_mailbox (idata->ctx);
+    mutt_error (_("Mailbox closed"));
+    mutt_sleep (1);
+    idata->state = IMAP_DISCONNECTED;
   }
 
-  s = imap_next_word ((char *)s);
-  if (!s || (ascii_strncasecmp (s, "OK", 2) &&
-            ascii_strncasecmp (s, "NO", 2) &&
-            ascii_strncasecmp (s, "BAD", 3)))
+  imap_close_connection (idata);
+  if (!idata->recovering)
   {
-    mutt_debug (2, "imap_cmd_trailer: not a command completion: %s",
-                idata->buf);
-    return notrailer;
+    idata->recovering = 1;
+    if (imap_conn_find (&idata->conn->account, 0))
+      mutt_clear_error ();
+    idata->recovering = 0;
   }
-
-  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)
+static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
 {
   int rc;
 
-  if ((rc = cmd_start (idata, cmdstr, flags)) < 0)
+  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;
 
-  // Allow interruptions, particularly useful if there are network problems.
-  mutt_allow_interrupt (1);
-  do
-    rc = imap_cmd_step (idata);
-  while (rc == IMAP_CMD_CONTINUE);
-  mutt_allow_interrupt (0);
+  if (idata->cmdbuf->dptr == idata->cmdbuf->data)
+    return IMAP_CMD_BAD;
 
-  if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
-    return -2;
+  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 != IMAP_CMD_OK)
-  {
-    if ((flags & IMAP_CMD_FAIL_OK) && idata->status != IMAP_FATAL)
-      return -2;
+  /* unidle when command queue is flushed */
+  if (idata->state == IMAP_IDLE)
+    idata->state = IMAP_SELECTED;
 
-    mutt_debug (1, "imap_exec: command failed: %s\n", idata->buf);
-    return -1;
-  }
+  return (rc < 0) ? IMAP_CMD_BAD : 0;
+}
 
-  return 0;
+/* parse response line for tagged OK/NO/BAD */
+static int cmd_status (const char *s)
+{
+  s = imap_next_word((char*)s);
+
+  if (!ascii_strncasecmp("OK", s, 2))
+    return IMAP_CMD_OK;
+  if (!ascii_strncasecmp("NO", s, 2))
+    return IMAP_CMD_NO;
+
+  return IMAP_CMD_BAD;
 }
 
-/* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
- *   detected, do expunge). Called automatically by imap_cmd_step, but
- *   may be called at any time. Called by imap_check_mailbox just before
- *   the index is refreshed, for instance. */
-void imap_cmd_finish (IMAP_DATA* idata)
+/* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
+ *   be reopened at our earliest convenience */
+static void cmd_parse_expunge (IMAP_DATA* idata, const char* s)
 {
-  if (idata->status == IMAP_FATAL)
-  {
-    cmd_handle_fatal (idata);
-    return;
-  }
+  int expno, cur;
+  HEADER* h;
 
-  if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
-    return;
+  mutt_debug (2, "Handling EXPUNGE\n");
 
-  if (idata->reopen & IMAP_REOPEN_ALLOW)
+  expno = atoi (s);
+
+  /* walk headers, zero seqno of expunged message, decrement seqno of those
+   * above. Possibly we could avoid walking the whole list by resorting
+   * and guessing a good starting point, but I'm guessing the resort would
+   * nullify the gains */
+  for (cur = 0; cur < idata->ctx->msgcount; cur++)
   {
-    int count = idata->newMailCount;
+    h = idata->ctx->hdrs[cur];
 
-    if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
-       (idata->reopen & IMAP_NEWMAIL_PENDING)
-       && count > idata->ctx->msgcount)
-    {
-      /* read new mail messages */
-      mutt_debug (2, "imap_cmd_finish: Fetching new mail\n");
-      /* check_status: curs_main uses imap_check_mailbox to detect
-       *   whether the index needs updating */
-      idata->check_status = IMAP_NEWMAIL_PENDING;
-      imap_read_headers (idata, idata->ctx->msgcount, count-1);
-    }
-    else if (idata->reopen & IMAP_EXPUNGE_PENDING)
-    {
-      mutt_debug (2, "imap_cmd_finish: Expunging mailbox\n");
-      imap_expunge_mailbox (idata);
-      /* Detect whether we've gotten unexpected EXPUNGE messages */
-      if ((idata->reopen & IMAP_EXPUNGE_PENDING) &&
-         !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
-       idata->check_status = IMAP_EXPUNGE_PENDING;
-      idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
-                        IMAP_EXPUNGE_EXPECTED);
-    }
+    if (h->index+1 == expno)
+      h->index = -1;
+    else if (h->index+1 > expno)
+      h->index--;
   }
 
-  idata->status = 0;
+  idata->reopen |= IMAP_EXPUNGE_PENDING;
 }
 
-/* imap_cmd_idle: Enter the IDLE state. */
-int imap_cmd_idle (IMAP_DATA* idata)
+/* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
+ *   handles unanticipated FETCH responses, and only FLAGS data. We get
+ *   these if another client has changed flags for a mailbox we've selected.
+ *   Of course, a lot of code here duplicates code in message.c. */
+static void cmd_parse_fetch (IMAP_DATA* idata, char* s)
 {
-  int rc;
-
-  imap_cmd_start (idata, "IDLE");
-  do
-    rc = imap_cmd_step (idata);
-  while (rc == IMAP_CMD_CONTINUE);
+  int msgno, cur;
+  HEADER* h = NULL;
 
-  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)
-  {
-    mutt_debug (1, "imap_cmd_idle: error starting IDLE\n");
-    return -1;
-  }
+  mutt_debug (3, "Handling FETCH\n");
 
-  return 0;
-}
+  msgno = atoi (s);
 
-static int cmd_queue_full (IMAP_DATA* idata)
-{
-  if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
-    return 1;
+  if (msgno <= idata->ctx->msgcount)
+  /* see cmd_parse_expunge */
+    for (cur = 0; cur < idata->ctx->msgcount; cur++)
+    {
+      h = idata->ctx->hdrs[cur];
 
-  return 0;
-}
+      if (h && h->active && h->index+1 == msgno)
+      {
+       mutt_debug (2, "Message UID %d updated\n", HEADER_DATA(h)->uid);
+       break;
+      }
 
-/* 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;
+      h = NULL;
+    }
 
-  if (cmd_queue_full (idata))
+  if (!h)
   {
-    mutt_debug (3, "cmd_new: IMAP command queue full\n");
-    return NULL;
+    mutt_debug (3, "FETCH response ignored for this message\n");
+    return;
   }
 
-  cmd = idata->cmds + idata->nextcmd;
-  idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
+  /* skip FETCH */
+  s = imap_next_word (s);
+  s = imap_next_word (s);
 
-  snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
-  if (idata->seqno > 9999)
-    idata->seqno = 0;
+  if (*s != '(')
+  {
+    mutt_debug (1, "Malformed FETCH response");
+    return;
+  }
+  s++;
 
-  cmd->state = IMAP_CMD_NEW;
+  if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
+  {
+    mutt_debug (2, "Only handle FLAGS updates\n");
+    return;
+  }
 
-  return cmd;
+  /* If server flags could conflict with mutt's flags, reopen the mailbox. */
+  if (h->changed)
+    idata->reopen |= IMAP_EXPUNGE_PENDING;
+  else {
+    imap_set_flags (idata, h, s);
+    idata->check_status = IMAP_FLAGS_PENDING;
+  }
 }
 
-/* queues command. If the queue is full, attempts to drain it. */
-static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
+/* cmd_parse_capabilities: set capability bits according to CAPABILITY
+ *   response */
+static void cmd_parse_capability (IMAP_DATA* idata, char* s)
 {
-  IMAP_COMMAND* cmd;
-  int rc;
-
-  if (cmd_queue_full (idata))
-  {
-    mutt_debug (3, "Draining IMAP command pipeline\n");
-
-    rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
+  int x;
+  char* bracket;
 
-    if (rc < 0 && rc != -2)
-      return rc;
-  }
+  mutt_debug (3, "Handling CAPABILITY\n");
 
-  if (!(cmd = cmd_new (idata)))
-    return IMAP_CMD_BAD;
+  s = imap_next_word (s);
+  if ((bracket = strchr (s, ']')))
+    *bracket = '\0';
+  FREE(&idata->capstr);
+  idata->capstr = safe_strdup (s);
 
-  if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
-    return IMAP_CMD_BAD;
+  memset (idata->capabilities, 0, sizeof (idata->capabilities));
 
-  return 0;
+  while (*s)
+  {
+    for (x = 0; x < CAPMAX; x++)
+      if (imap_wordcasecmp(Capabilities[x], s) == 0)
+      {
+       mutt_bit_set (idata->capabilities, x);
+       break;
+      }
+    s = imap_next_word (s);
+  }
 }
 
-static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
+static void cmd_parse_list (IMAP_DATA* idata, char* s)
 {
-  int rc;
+  IMAP_LIST* list;
+  IMAP_LIST lb;
+  char delimbuf[5]; /* worst case: "\\"\0 */
+  long litlen;
 
-  if (idata->status == IMAP_FATAL)
-  {
-    cmd_handle_fatal (idata);
-    return -1;
-  }
+  if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
+    list = (IMAP_LIST*)idata->cmddata;
+  else
+    list = &lb;
 
-  if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
-    return rc;
+  memset (list, 0, sizeof (IMAP_LIST));
 
-  if (flags & IMAP_CMD_QUEUE)
-    return 0;
+  /* flags */
+  s = imap_next_word (s);
+  if (*s != '(')
+  {
+    mutt_debug (1, "Bad LIST response\n");
+    return;
+  }
+  s++;
+  while (*s)
+  {
+    if (!ascii_strncasecmp (s, "\\NoSelect", 9))
+      list->noselect = 1;
+    else if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
+      list->noinferiors = 1;
+    /* See draft-gahrns-imap-child-mailbox-?? */
+    else if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
+      list->noinferiors = 1;
 
-  if (idata->cmdbuf->dptr == idata->cmdbuf->data)
-    return IMAP_CMD_BAD;
+    s = imap_next_word (s);
+    if (*(s - 2) == ')')
+      break;
+  }
 
-  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;
+  /* Delimiter */
+  if (ascii_strncasecmp (s, "NIL", 3))
+  {
+    delimbuf[0] = '\0';
+    safe_strcat (delimbuf, 5, s);
+    imap_unquote_string (delimbuf);
+    list->delim = delimbuf[0];
+  }
 
-  /* unidle when command queue is flushed */
-  if (idata->state == IMAP_IDLE)
-    idata->state = IMAP_SELECTED;
+  /* Name */
+  s = imap_next_word (s);
+  /* Notes often responds with literals here. We need a real tokenizer. */
+  if (!imap_get_literal_count (s, &litlen))
+  {
+    if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
+    {
+      idata->status = IMAP_FATAL;
+      return;
+    }
+    list->name = idata->buf;
+  }
+  else
+  {
+    imap_unmunge_mbox_name (idata, s);
+    list->name = s;
+  }
 
-  return (rc < 0) ? IMAP_CMD_BAD : 0;
+  if (list->name[0] == '\0')
+  {
+    idata->delim = list->delim;
+    mutt_debug (3, "Root delimiter: %c\n", idata->delim);
+  }
 }
 
-/* parse response line for tagged OK/NO/BAD */
-static int cmd_status (const char *s)
+static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
 {
-  s = imap_next_word((char*)s);
+  char buf[STRING];
+  char errstr[STRING];
+  BUFFER err, token;
+  ciss_url_t url;
+  IMAP_LIST list;
 
-  if (!ascii_strncasecmp("OK", s, 2))
-    return IMAP_CMD_OK;
-  if (!ascii_strncasecmp("NO", s, 2))
-    return IMAP_CMD_NO;
+  if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
+  {
+    /* caller will handle response itself */
+    cmd_parse_list (idata, s);
+    return;
+  }
 
-  return IMAP_CMD_BAD;
-}
+  if (!option (OPTIMAPCHECKSUBSCRIBED))
+    return;
 
-/* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
-static void cmd_handle_fatal (IMAP_DATA* idata)
-{
-  idata->status = IMAP_FATAL;
+  idata->cmdtype = IMAP_CT_LIST;
+  idata->cmddata = &list;
+  cmd_parse_list (idata, s);
+  idata->cmddata = NULL;
+  /* noselect is for a gmail quirk (#3445) */
+  if (!list.name || list.noselect)
+    return;
 
-  if ((idata->state >= IMAP_SELECTED) &&
-      (idata->reopen & IMAP_REOPEN_ALLOW))
+  mutt_debug (3, "Subscribing to %s\n", list.name);
+
+  strfcpy (buf, "mailboxes \"", sizeof (buf));
+  mutt_account_tourl (&idata->conn->account, &url);
+  /* 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) - 11, 0);
+  safe_strcat (buf, sizeof (buf), "\"");
+  mutt_buffer_init (&token);
+  mutt_buffer_init (&err);
+  err.data = errstr;
+  err.dsize = sizeof (errstr);
+  if (mutt_parse_rc_line (buf, &token, &err))
+    mutt_debug (1, "Error adding subscribed mailbox: %s\n", errstr);
+  FREE (&token.data);
+}
+
+/* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
+static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
+{
+  mutt_debug (2, "Handling MYRIGHTS\n");
+
+  s = imap_next_word ((char*)s);
+  s = imap_next_word ((char*)s);
+
+  /* zero out current rights set */
+  memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
+
+  while (*s && !isspace((unsigned char) *s))
   {
-    mx_fastclose_mailbox (idata->ctx);
-    mutt_error (_("Mailbox closed"));
-    mutt_sleep (1);
-    idata->state = IMAP_DISCONNECTED;
+    switch (*s)
+    {
+      case 'l':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_LOOKUP);
+       break;
+      case 'r':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_READ);
+       break;
+      case 's':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_SEEN);
+       break;
+      case 'w':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_WRITE);
+       break;
+      case 'i':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_INSERT);
+       break;
+      case 'p':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_POST);
+       break;
+      case 'a':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_ADMIN);
+       break;
+      case 'k':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_CREATE);
+        break;
+      case 'x':
+        mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELMX);
+        break;
+      case 't':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELETE);
+        break;
+      case 'e':
+        mutt_bit_set (idata->ctx->rights, MUTT_ACL_EXPUNGE);
+        break;
+
+        /* obsolete rights */
+      case 'c':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_CREATE);
+        mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELMX);
+       break;
+      case 'd':
+       mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELETE);
+        mutt_bit_set (idata->ctx->rights, MUTT_ACL_EXPUNGE);
+       break;
+      default:
+        mutt_debug (1, "Unknown right: %c\n", *s);
+    }
+    s++;
   }
+}
 
-  imap_close_connection (idata);
-  if (!idata->recovering)
+/* cmd_parse_search: store SEARCH response for later use */
+static void cmd_parse_search (IMAP_DATA* idata, const char* s)
+{
+  unsigned int uid;
+  HEADER *h;
+
+  mutt_debug (2, "Handling SEARCH\n");
+
+  while ((s = imap_next_word ((char*)s)) && *s != '\0')
   {
-    idata->recovering = 1;
-    if (imap_conn_find (&idata->conn->account, 0))
-      mutt_clear_error ();
-    idata->recovering = 0;
+    uid = (unsigned int)atoi (s);
+    h = (HEADER *)int_hash_find (idata->uid_hash, uid);
+    if (h)
+      h->matched = 1;
+  }
+}
+
+/* first cut: just do buffy update. Later we may wish to cache all
+ * mailbox information, even that not desired by buffy */
+static void cmd_parse_status (IMAP_DATA* idata, char* s)
+{
+  char* mailbox;
+  char* value;
+  BUFFY* inc;
+  IMAP_MBOX mx;
+  int count;
+  IMAP_STATUS *status;
+  unsigned int olduv, oldun;
+  long litlen;
+  short new = 0;
+  short new_msg_count = 0;
+
+  mailbox = imap_next_word (s);
+
+  /* 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 (idata, mailbox);
+  }
+
+  status = imap_mboxcache_get (idata, mailbox, 1);
+  olduv = status->uidvalidity;
+  oldun = status->uidnext;
+
+  if (*s++ != '(')
+  {
+    mutt_debug (1, "Error parsing STATUS\n");
+    return;
+  }
+  while (*s && *s != ')')
+  {
+    value = imap_next_word (s);
+    count = strtol (value, &value, 10);
+
+    if (!ascii_strncmp ("MESSAGES", s, 8))
+    {
+      status->messages = count;
+      new_msg_count = 1;
+    }
+    else if (!ascii_strncmp ("RECENT", s, 6))
+      status->recent = count;
+    else if (!ascii_strncmp ("UIDNEXT", s, 7))
+      status->uidnext = count;
+    else if (!ascii_strncmp ("UIDVALIDITY", s, 11))
+      status->uidvalidity = count;
+    else if (!ascii_strncmp ("UNSEEN", s, 6))
+      status->unseen = count;
+
+    s = value;
+    if (*s && *s != ')')
+      s = imap_next_word (s);
+  }
+  mutt_debug (3, "%s (UIDVALIDITY: %d, UIDNEXT: %d) %d messages, %d recent, %d unseen\n",
+              status->name, status->uidvalidity, status->uidnext,
+              status->messages, status->recent, status->unseen);
+
+  /* caller is prepared to handle the result herself */
+  if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
+  {
+    memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
+    return;
+  }
+
+  mutt_debug (3, "Running default STATUS handler\n");
+
+  /* should perhaps move this code back to imap_buffy_check */
+  for (inc = Incoming; inc; inc = inc->next)
+  {
+    if (inc->magic != MUTT_IMAP)
+      continue;
+
+    if (imap_parse_path (inc->path, &mx) < 0)
+    {
+      mutt_debug (1, "Error parsing mailbox %s, skipping\n", inc->path);
+      continue;
+    }
+
+    if (imap_account_match (&idata->conn->account, &mx.account))
+    {
+      if (mx.mbox)
+      {
+       value = safe_strdup (mx.mbox);
+       imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
+       FREE (&mx.mbox);
+      }
+      else
+       value = safe_strdup ("INBOX");
+
+      if (value && !imap_mxcmp (mailbox, value))
+      {
+        mutt_debug (3, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
+                    mailbox, olduv, oldun, status->unseen);
+
+       if (option(OPTMAILCHECKRECENT))
+       {
+         if (olduv && olduv == status->uidvalidity)
+         {
+           if (oldun < status->uidnext)
+             new = (status->unseen > 0);
+         }
+         else if (!olduv && !oldun)
+           /* first check per session, use recent. might need a flag for this. */
+           new = (status->recent > 0);
+         else
+           new = (status->unseen > 0);
+       }
+       else
+          new = (status->unseen > 0);
+
+#ifdef USE_SIDEBAR
+        if ((inc->new != new) ||
+            (inc->msg_count != status->messages) ||
+            (inc->msg_unread != status->unseen))
+          SidebarNeedsRedraw = 1;
+#endif
+        inc->new = new;
+        if (new_msg_count)
+          inc->msg_count = status->messages;
+        inc->msg_unread = status->unseen;
+
+       if (inc->new)
+         /* force back to keep detecting new mail until the mailbox is
+            opened */
+         status->uidnext = oldun;
+
+        FREE (&value);
+        return;
+      }
+
+      FREE (&value);
+    }
+
+    FREE (&mx.mbox);
+  }
+}
+
+/* cmd_parse_enabled: record what the server has enabled */
+static void cmd_parse_enabled (IMAP_DATA* idata, const char* s)
+{
+  mutt_debug (2, "Handling ENABLED\n");
+
+  while ((s = imap_next_word ((char*)s)) && *s != '\0')
+  {
+    if (ascii_strncasecmp(s, "UTF8=ACCEPT", 11) == 0 ||
+        ascii_strncasecmp(s, "UTF8=ONLY", 9) == 0)
+      idata->unicode = 1;
   }
 }
 
@@ -528,518 +729,300 @@ static int cmd_handle_untagged (IMAP_DATA* idata)
   else if (ascii_strncasecmp ("SEARCH", s, 6) == 0)
     cmd_parse_search (idata, s);
   else if (ascii_strncasecmp ("STATUS", s, 6) == 0)
-    cmd_parse_status (idata, s);
-  else if (ascii_strncasecmp ("ENABLED", s, 7) == 0)
-    cmd_parse_enabled (idata, s);
-  else if (ascii_strncasecmp ("BYE", s, 3) == 0)
-  {
-    mutt_debug (2, "Handling BYE\n");
-
-    /* check if we're logging out */
-    if (idata->status == IMAP_BYE)
-      return 0;
-
-    /* server shut down our connection */
-    s += 3;
-    SKIPWS (s);
-    mutt_error ("%s", s);
-    mutt_sleep (2);
-    cmd_handle_fatal (idata);
-
-    return -1;
-  }
-  else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
-  {
-    mutt_debug (2, "Handling untagged NO\n");
-
-    /* Display the warning message from the server */
-    mutt_error ("%s", s+3);
-    mutt_sleep (2);
-  }
-
-  return 0;
-}
-
-/* cmd_parse_capabilities: set capability bits according to CAPABILITY
- *   response */
-static void cmd_parse_capability (IMAP_DATA* idata, char* s)
-{
-  int x;
-  char* bracket;
-
-  mutt_debug (3, "Handling CAPABILITY\n");
-
-  s = imap_next_word (s);
-  if ((bracket = strchr (s, ']')))
-    *bracket = '\0';
-  FREE(&idata->capstr);
-  idata->capstr = safe_strdup (s);
-
-  memset (idata->capabilities, 0, sizeof (idata->capabilities));
-
-  while (*s)
-  {
-    for (x = 0; x < CAPMAX; x++)
-      if (imap_wordcasecmp(Capabilities[x], s) == 0)
-      {
-       mutt_bit_set (idata->capabilities, x);
-       break;
-      }
-    s = imap_next_word (s);
-  }
-}
-
-/* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
- *   be reopened at our earliest convenience */
-static void cmd_parse_expunge (IMAP_DATA* idata, const char* s)
-{
-  int expno, cur;
-  HEADER* h;
-
-  mutt_debug (2, "Handling EXPUNGE\n");
-
-  expno = atoi (s);
-
-  /* walk headers, zero seqno of expunged message, decrement seqno of those
-   * above. Possibly we could avoid walking the whole list by resorting
-   * and guessing a good starting point, but I'm guessing the resort would
-   * nullify the gains */
-  for (cur = 0; cur < idata->ctx->msgcount; cur++)
-  {
-    h = idata->ctx->hdrs[cur];
-
-    if (h->index+1 == expno)
-      h->index = -1;
-    else if (h->index+1 > expno)
-      h->index--;
-  }
-
-  idata->reopen |= IMAP_EXPUNGE_PENDING;
-}
-
-/* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
- *   handles unanticipated FETCH responses, and only FLAGS data. We get
- *   these if another client has changed flags for a mailbox we've selected.
- *   Of course, a lot of code here duplicates code in message.c. */
-static void cmd_parse_fetch (IMAP_DATA* idata, char* s)
-{
-  int msgno, cur;
-  HEADER* h = NULL;
-
-  mutt_debug (3, "Handling FETCH\n");
-
-  msgno = atoi (s);
-
-  if (msgno <= idata->ctx->msgcount)
-  /* see cmd_parse_expunge */
-    for (cur = 0; cur < idata->ctx->msgcount; cur++)
-    {
-      h = idata->ctx->hdrs[cur];
-
-      if (h && h->active && h->index+1 == msgno)
-      {
-       mutt_debug (2, "Message UID %d updated\n", HEADER_DATA(h)->uid);
-       break;
-      }
-
-      h = NULL;
-    }
-
-  if (!h)
-  {
-    mutt_debug (3, "FETCH response ignored for this message\n");
-    return;
-  }
-
-  /* skip FETCH */
-  s = imap_next_word (s);
-  s = imap_next_word (s);
-
-  if (*s != '(')
-  {
-    mutt_debug (1, "Malformed FETCH response");
-    return;
-  }
-  s++;
-
-  if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
-  {
-    mutt_debug (2, "Only handle FLAGS updates\n");
-    return;
-  }
-
-  /* If server flags could conflict with mutt's flags, reopen the mailbox. */
-  if (h->changed)
-    idata->reopen |= IMAP_EXPUNGE_PENDING;
-  else {
-    imap_set_flags (idata, h, s);
-    idata->check_status = IMAP_FLAGS_PENDING;
-  }
-}
-
-static void cmd_parse_list (IMAP_DATA* idata, char* s)
-{
-  IMAP_LIST* list;
-  IMAP_LIST lb;
-  char delimbuf[5]; /* worst case: "\\"\0 */
-  long litlen;
-
-  if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
-    list = (IMAP_LIST*)idata->cmddata;
-  else
-    list = &lb;
-
-  memset (list, 0, sizeof (IMAP_LIST));
-
-  /* flags */
-  s = imap_next_word (s);
-  if (*s != '(')
-  {
-    mutt_debug (1, "Bad LIST response\n");
-    return;
-  }
-  s++;
-  while (*s)
+    cmd_parse_status (idata, s);
+  else if (ascii_strncasecmp ("ENABLED", s, 7) == 0)
+    cmd_parse_enabled (idata, s);
+  else if (ascii_strncasecmp ("BYE", s, 3) == 0)
   {
-    if (!ascii_strncasecmp (s, "\\NoSelect", 9))
-      list->noselect = 1;
-    else if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
-      list->noinferiors = 1;
-    /* See draft-gahrns-imap-child-mailbox-?? */
-    else if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
-      list->noinferiors = 1;
+    mutt_debug (2, "Handling BYE\n");
 
-    s = imap_next_word (s);
-    if (*(s - 2) == ')')
-      break;
-  }
+    /* check if we're logging out */
+    if (idata->status == IMAP_BYE)
+      return 0;
 
-  /* Delimiter */
-  if (ascii_strncasecmp (s, "NIL", 3))
-  {
-    delimbuf[0] = '\0';
-    safe_strcat (delimbuf, 5, s);
-    imap_unquote_string (delimbuf);
-    list->delim = delimbuf[0];
-  }
+    /* server shut down our connection */
+    s += 3;
+    SKIPWS (s);
+    mutt_error ("%s", s);
+    mutt_sleep (2);
+    cmd_handle_fatal (idata);
 
-  /* Name */
-  s = imap_next_word (s);
-  /* Notes often responds with literals here. We need a real tokenizer. */
-  if (!imap_get_literal_count (s, &litlen))
-  {
-    if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
-    {
-      idata->status = IMAP_FATAL;
-      return;
-    }
-    list->name = idata->buf;
+    return -1;
   }
-  else
+  else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
   {
-    imap_unmunge_mbox_name (idata, s);
-    list->name = s;
-  }
+    mutt_debug (2, "Handling untagged NO\n");
 
-  if (list->name[0] == '\0')
-  {
-    idata->delim = list->delim;
-    mutt_debug (3, "Root delimiter: %c\n", idata->delim);
+    /* Display the warning message from the server */
+    mutt_error ("%s", s+3);
+    mutt_sleep (2);
   }
+
+  return 0;
 }
 
-static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
+/* 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)
 {
-  char buf[STRING];
-  char errstr[STRING];
-  BUFFER err, token;
-  ciss_url_t url;
-  IMAP_LIST list;
+  return cmd_start (idata, cmdstr, 0);
+}
 
-  if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
+/* 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)
   {
-    /* caller will handle response itself */
-    cmd_parse_list (idata, s);
-    return;
+    cmd_handle_fatal (idata);
+    return IMAP_CMD_BAD;
   }
 
-  if (!option (OPTIMAPCHECKSUBSCRIBED))
-    return;
+  /* 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;
+      mutt_debug (3, "imap_cmd_step: grew buffer to %u bytes\n", idata->blen);
+    }
 
-  idata->cmdtype = IMAP_CT_LIST;
-  idata->cmddata = &list;
-  cmd_parse_list (idata, s);
-  idata->cmddata = NULL;
-  /* noselect is for a gmail quirk (#3445) */
-  if (!list.name || list.noselect)
-    return;
+    /* back up over '\0' */
+    if (len)
+      len--;
+    c = mutt_socket_readln (idata->buf + len, idata->blen - len, idata->conn);
+    if (c <= 0)
+    {
+      mutt_debug (1, "imap_cmd_step: Error reading server response.\n");
+      cmd_handle_fatal (idata);
+      return IMAP_CMD_BAD;
+    }
 
-  mutt_debug (3, "Subscribing to %s\n", list.name);
+    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);
 
-  strfcpy (buf, "mailboxes \"", sizeof (buf));
-  mutt_account_tourl (&idata->conn->account, &url);
-  /* 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) - 11, 0);
-  safe_strcat (buf, sizeof (buf), "\"");
-  mutt_buffer_init (&token);
-  mutt_buffer_init (&err);
-  err.data = errstr;
-  err.dsize = sizeof (errstr);
-  if (mutt_parse_rc_line (buf, &token, &err))
-    mutt_debug (1, "Error adding subscribed mailbox: %s\n", errstr);
-  FREE (&token.data);
-}
+  /* 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;
+    mutt_debug (3, "imap_cmd_step: shrank buffer to %u bytes\n", idata->blen);
+  }
 
-/* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
-static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
-{
-  mutt_debug (2, "Handling MYRIGHTS\n");
+  idata->lastread = time (NULL);
 
-  s = imap_next_word ((char*)s);
-  s = imap_next_word ((char*)s);
+  /* 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 (idata))
+    return IMAP_CMD_BAD;
 
-  /* zero out current rights set */
-  memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
+  /* server demands a continuation response from us */
+  if (idata->buf[0] == '+')
+    return IMAP_CMD_RESPOND;
 
-  while (*s && !isspace((unsigned char) *s))
+  /* look for tagged command completions */
+  rc = IMAP_CMD_CONTINUE;
+  c = idata->lastcmd;
+  do
   {
-    switch (*s)
+    cmd = &idata->cmds[c];
+    if (cmd->state == IMAP_CMD_NEW)
     {
-      case 'l':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_LOOKUP);
-       break;
-      case 'r':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_READ);
-       break;
-      case 's':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_SEEN);
-       break;
-      case 'w':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_WRITE);
-       break;
-      case 'i':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_INSERT);
-       break;
-      case 'p':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_POST);
-       break;
-      case 'a':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_ADMIN);
-       break;
-      case 'k':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_CREATE);
-        break;
-      case 'x':
-        mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELMX);
-        break;
-      case 't':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELETE);
-        break;
-      case 'e':
-        mutt_bit_set (idata->ctx->rights, MUTT_ACL_EXPUNGE);
-        break;
-
-        /* obsolete rights */
-      case 'c':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_CREATE);
-        mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELMX);
-       break;
-      case 'd':
-       mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELETE);
-        mutt_bit_set (idata->ctx->rights, MUTT_ACL_EXPUNGE);
-       break;
-      default:
-        mutt_debug (1, "Unknown right: %c\n", *s);
+      if (!ascii_strncmp (idata->buf, cmd->seq, SEQLEN)) {
+       if (!stillrunning)
+       {
+         /* first command in queue has finished - move queue pointer up */
+         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
+        * should provide a tag. */
+       rc = cmd->state;
+      }
+      else
+       stillrunning++;
     }
-    s++;
-  }
-}
-
-/* cmd_parse_search: store SEARCH response for later use */
-static void cmd_parse_search (IMAP_DATA* idata, const char* s)
-{
-  unsigned int uid;
-  HEADER *h;
 
-  mutt_debug (2, "Handling SEARCH\n");
+    c = (c + 1) % idata->cmdslots;
+  }
+  while (c != idata->nextcmd);
 
-  while ((s = imap_next_word ((char*)s)) && *s != '\0')
-  {
-    uid = (unsigned int)atoi (s);
-    h = (HEADER *)int_hash_find (idata->uid_hash, uid);
-    if (h)
-      h->matched = 1;
+  if (stillrunning)
+    rc = IMAP_CMD_CONTINUE;
+  else
+  {
+    mutt_debug (3, "IMAP queue drained\n");
+    imap_cmd_finish (idata);
   }
+
+
+  return rc;
 }
 
-/* first cut: just do buffy update. Later we may wish to cache all
- * mailbox information, even that not desired by buffy */
-static void cmd_parse_status (IMAP_DATA* idata, char* s)
+/* imap_code: returns 1 if the command result was OK, or 0 if NO or BAD */
+int imap_code (const char *s)
 {
-  char* mailbox;
-  char* value;
-  BUFFY* inc;
-  IMAP_MBOX mx;
-  int count;
-  IMAP_STATUS *status;
-  unsigned int olduv, oldun;
-  long litlen;
-  short new = 0;
-  short new_msg_count = 0;
+  return cmd_status (s) == IMAP_CMD_OK;
+}
 
-  mailbox = imap_next_word (s);
+/* 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;
 
-  /* We need a real tokenizer. */
-  if (!imap_get_literal_count (mailbox, &litlen))
+  if (!s)
   {
-    if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
-    {
-      idata->status = IMAP_FATAL;
-      return;
-    }
-    mailbox = idata->buf;
-    s = mailbox + litlen;
-    *s = '\0';
-    s++;
-    SKIPWS(s);
+    mutt_debug (2, "imap_cmd_trailer: not a tagged response");
+    return notrailer;
   }
-  else
+
+  s = imap_next_word ((char *)s);
+  if (!s || (ascii_strncasecmp (s, "OK", 2) &&
+            ascii_strncasecmp (s, "NO", 2) &&
+            ascii_strncasecmp (s, "BAD", 3)))
   {
-    s = imap_next_word (mailbox);
-    *(s - 1) = '\0';
-    imap_unmunge_mbox_name (idata, mailbox);
+    mutt_debug (2, "imap_cmd_trailer: not a command completion: %s",
+                idata->buf);
+    return notrailer;
   }
 
-  status = imap_mboxcache_get (idata, mailbox, 1);
-  olduv = status->uidvalidity;
-  oldun = status->uidnext;
+  s = imap_next_word ((char *)s);
+  if (!s)
+    return notrailer;
 
-  if (*s++ != '(')
+  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 ((rc = cmd_start (idata, cmdstr, flags)) < 0)
   {
-    mutt_debug (1, "Error parsing STATUS\n");
-    return;
+    cmd_handle_fatal (idata);
+    return -1;
   }
-  while (*s && *s != ')')
-  {
-    value = imap_next_word (s);
-    count = strtol (value, &value, 10);
 
-    if (!ascii_strncmp ("MESSAGES", s, 8))
-    {
-      status->messages = count;
-      new_msg_count = 1;
-    }
-    else if (!ascii_strncmp ("RECENT", s, 6))
-      status->recent = count;
-    else if (!ascii_strncmp ("UIDNEXT", s, 7))
-      status->uidnext = count;
-    else if (!ascii_strncmp ("UIDVALIDITY", s, 11))
-      status->uidvalidity = count;
-    else if (!ascii_strncmp ("UNSEEN", s, 6))
-      status->unseen = count;
+  if (flags & IMAP_CMD_QUEUE)
+    return 0;
 
-    s = value;
-    if (*s && *s != ')')
-      s = imap_next_word (s);
+  // Allow interruptions, particularly useful if there are network problems.
+  mutt_allow_interrupt (1);
+  do
+    rc = imap_cmd_step (idata);
+  while (rc == IMAP_CMD_CONTINUE);
+  mutt_allow_interrupt (0);
+
+  if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
+    return -2;
+
+  if (rc != IMAP_CMD_OK)
+  {
+    if ((flags & IMAP_CMD_FAIL_OK) && idata->status != IMAP_FATAL)
+      return -2;
+
+    mutt_debug (1, "imap_exec: command failed: %s\n", idata->buf);
+    return -1;
   }
-  mutt_debug (3, "%s (UIDVALIDITY: %d, UIDNEXT: %d) %d messages, %d recent, %d unseen\n",
-              status->name, status->uidvalidity, status->uidnext,
-              status->messages, status->recent, status->unseen);
 
-  /* caller is prepared to handle the result herself */
-  if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
+  return 0;
+}
+
+/* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
+ *   detected, do expunge). Called automatically by imap_cmd_step, but
+ *   may be called at any time. Called by imap_check_mailbox just before
+ *   the index is refreshed, for instance. */
+void imap_cmd_finish (IMAP_DATA* idata)
+{
+  if (idata->status == IMAP_FATAL)
   {
-    memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
+    cmd_handle_fatal (idata);
     return;
   }
 
-  mutt_debug (3, "Running default STATUS handler\n");
+  if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
+    return;
 
-  /* should perhaps move this code back to imap_buffy_check */
-  for (inc = Incoming; inc; inc = inc->next)
+  if (idata->reopen & IMAP_REOPEN_ALLOW)
   {
-    if (inc->magic != MUTT_IMAP)
-      continue;
+    int count = idata->newMailCount;
 
-    if (imap_parse_path (inc->path, &mx) < 0)
+    if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
+       (idata->reopen & IMAP_NEWMAIL_PENDING)
+       && count > idata->ctx->msgcount)
     {
-      mutt_debug (1, "Error parsing mailbox %s, skipping\n", inc->path);
-      continue;
+      /* read new mail messages */
+      mutt_debug (2, "imap_cmd_finish: Fetching new mail\n");
+      /* check_status: curs_main uses imap_check_mailbox to detect
+       *   whether the index needs updating */
+      idata->check_status = IMAP_NEWMAIL_PENDING;
+      imap_read_headers (idata, idata->ctx->msgcount, count-1);
     }
-
-    if (imap_account_match (&idata->conn->account, &mx.account))
+    else if (idata->reopen & IMAP_EXPUNGE_PENDING)
     {
-      if (mx.mbox)
-      {
-       value = safe_strdup (mx.mbox);
-       imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
-       FREE (&mx.mbox);
-      }
-      else
-       value = safe_strdup ("INBOX");
-
-      if (value && !imap_mxcmp (mailbox, value))
-      {
-        mutt_debug (3, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
-                    mailbox, olduv, oldun, status->unseen);
-
-       if (option(OPTMAILCHECKRECENT))
-       {
-         if (olduv && olduv == status->uidvalidity)
-         {
-           if (oldun < status->uidnext)
-             new = (status->unseen > 0);
-         }
-         else if (!olduv && !oldun)
-           /* first check per session, use recent. might need a flag for this. */
-           new = (status->recent > 0);
-         else
-           new = (status->unseen > 0);
-       }
-       else
-          new = (status->unseen > 0);
-
-#ifdef USE_SIDEBAR
-        if ((inc->new != new) ||
-            (inc->msg_count != status->messages) ||
-            (inc->msg_unread != status->unseen))
-          SidebarNeedsRedraw = 1;
-#endif
-        inc->new = new;
-        if (new_msg_count)
-          inc->msg_count = status->messages;
-        inc->msg_unread = status->unseen;
-
-       if (inc->new)
-         /* force back to keep detecting new mail until the mailbox is
-            opened */
-         status->uidnext = oldun;
-
-        FREE (&value);
-        return;
-      }
-
-      FREE (&value);
+      mutt_debug (2, "imap_cmd_finish: Expunging mailbox\n");
+      imap_expunge_mailbox (idata);
+      /* Detect whether we've gotten unexpected EXPUNGE messages */
+      if ((idata->reopen & IMAP_EXPUNGE_PENDING) &&
+         !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
+       idata->check_status = IMAP_EXPUNGE_PENDING;
+      idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
+                        IMAP_EXPUNGE_EXPECTED);
     }
-
-    FREE (&mx.mbox);
   }
+
+  idata->status = 0;
 }
 
-/* cmd_parse_enabled: record what the server has enabled */
-static void cmd_parse_enabled (IMAP_DATA* idata, const char* s)
+/* imap_cmd_idle: Enter the IDLE state. */
+int imap_cmd_idle (IMAP_DATA* idata)
 {
-  mutt_debug (2, "Handling ENABLED\n");
+  int rc;
 
-  while ((s = imap_next_word ((char*)s)) && *s != '\0')
+  imap_cmd_start (idata, "IDLE");
+  do
+    rc = imap_cmd_step (idata);
+  while (rc == IMAP_CMD_CONTINUE);
+
+  if (rc == IMAP_CMD_RESPOND)
   {
-    if (ascii_strncasecmp(s, "UTF8=ACCEPT", 11) == 0 ||
-        ascii_strncasecmp(s, "UTF8=ONLY", 9) == 0)
-      idata->unicode = 1;
+    /* 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)
+  {
+    mutt_debug (1, "imap_cmd_idle: error starting IDLE\n");
+    return -1;
+  }
+
+  return 0;
 }
+
index 4cf71f494020bef90e2fb80136a3ab171f162100..c15b12b56be9ce29630c3ebea8d3e1adc2ad17f3 100644 (file)
 
 #include "bcache.h"
 
-static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h);
-static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h);
-static int msg_cache_commit (IMAP_DATA* idata, HEADER* h);
-
-static void flush_buffer(char* buf, size_t* len, CONNECTION* conn);
-static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf,
-  FILE* fp);
-static int msg_parse_fetch (IMAP_HEADER* h, char* s);
-static char* msg_parse_flags (IMAP_HEADER* h, char* s);
-
 static void imap_update_context (IMAP_DATA *idata, int oldmsgcount)
 {
   CONTEXT *ctx;
@@ -67,6 +57,303 @@ static void imap_update_context (IMAP_DATA *idata, int oldmsgcount)
   }
 }
 
+static body_cache_t *msg_cache_open (IMAP_DATA *idata)
+{
+  char mailbox[_POSIX_PATH_MAX];
+
+  if (idata->bcache)
+    return idata->bcache;
+
+  imap_cachepath (idata, idata->mailbox, mailbox, sizeof (mailbox));
+
+  return mutt_bcache_open (&idata->conn->account, mailbox);
+}
+
+static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h)
+{
+  char id[_POSIX_PATH_MAX];
+
+  if (!idata || !h)
+    return NULL;
+
+  idata->bcache = msg_cache_open (idata);
+  snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
+  return mutt_bcache_get (idata->bcache, id);
+}
+
+static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h)
+{
+  char id[_POSIX_PATH_MAX];
+
+  if (!idata || !h)
+    return NULL;
+
+  idata->bcache = msg_cache_open (idata);
+  snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
+  return mutt_bcache_put (idata->bcache, id, 1);
+}
+
+static int msg_cache_commit (IMAP_DATA* idata, HEADER* h)
+{
+  char id[_POSIX_PATH_MAX];
+
+  if (!idata || !h)
+    return -1;
+
+  idata->bcache = msg_cache_open (idata);
+  snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
+
+  return mutt_bcache_commit (idata->bcache, id);
+}
+
+static int msg_cache_clean_cb (const char* id, body_cache_t* bcache, void* data)
+{
+  unsigned int uv, uid, n;
+  IMAP_DATA* idata = data;
+
+  if (sscanf (id, "%u-%u", &uv, &uid) != 2)
+    return 0;
+
+  /* bad UID */
+  if (uv != idata->uid_validity)
+    mutt_bcache_del (bcache, id);
+
+  /* TODO: presort UIDs, walk in order */
+  for (n = 0; n < idata->ctx->msgcount; n++)
+  {
+    if (uid == HEADER_DATA(idata->ctx->hdrs[n])->uid)
+      return 0;
+  }
+  mutt_bcache_del (bcache, id);
+
+  return 0;
+}
+
+/* msg_parse_flags: read a FLAGS token into an IMAP_HEADER */
+static char* msg_parse_flags (IMAP_HEADER* h, char* s)
+{
+  IMAP_HEADER_DATA* hd = h->data;
+
+  /* sanity-check string */
+  if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
+  {
+    mutt_debug (1, "msg_parse_flags: not a FLAGS response: %s\n", s);
+    return NULL;
+  }
+  s += 5;
+  SKIPWS(s);
+  if (*s != '(')
+  {
+    mutt_debug (1, "msg_parse_flags: bogus FLAGS response: %s\n", s);
+    return NULL;
+  }
+  s++;
+
+  mutt_free_list (&hd->keywords);
+  hd->deleted = hd->flagged = hd->replied = hd->read = hd->old = 0;
+
+  /* start parsing */
+  while (*s && *s != ')')
+  {
+    if (ascii_strncasecmp ("\\deleted", s, 8) == 0)
+    {
+      s += 8;
+      hd->deleted = 1;
+    }
+    else if (ascii_strncasecmp ("\\flagged", s, 8) == 0)
+    {
+      s += 8;
+      hd->flagged = 1;
+    }
+    else if (ascii_strncasecmp ("\\answered", s, 9) == 0)
+    {
+      s += 9;
+      hd->replied = 1;
+    }
+    else if (ascii_strncasecmp ("\\seen", s, 5) == 0)
+    {
+      s += 5;
+      hd->read = 1;
+    }
+    else if (ascii_strncasecmp ("\\recent", s, 7) == 0)
+      s += 7;
+    else if (ascii_strncasecmp ("old", s, 3) == 0)
+    {
+      s += 3;
+      hd->old = 1;
+    }
+    else
+    {
+      /* store custom flags as well */
+      char ctmp;
+      char* flag_word = s;
+
+      if (!hd->keywords)
+        hd->keywords = mutt_new_list ();
+
+      while (*s && !ISSPACE (*s) && *s != ')')
+        s++;
+      ctmp = *s;
+      *s = '\0';
+      mutt_add_list (hd->keywords, flag_word);
+      *s = ctmp;
+    }
+    SKIPWS(s);
+  }
+
+  /* wrap up, or note bad flags response */
+  if (*s == ')')
+    s++;
+  else
+  {
+    mutt_debug (1, "msg_parse_flags: Unterminated FLAGS response: %s\n", s);
+    return NULL;
+  }
+
+  return s;
+}
+
+/* msg_parse_fetch: handle headers returned from header fetch */
+static int msg_parse_fetch (IMAP_HEADER *h, char *s)
+{
+  char tmp[SHORT_STRING];
+  char *ptmp;
+
+  if (!s)
+    return -1;
+
+  while (*s)
+  {
+    SKIPWS (s);
+
+    if (ascii_strncasecmp ("FLAGS", s, 5) == 0)
+    {
+      if ((s = msg_parse_flags (h, s)) == NULL)
+        return -1;
+    }
+    else if (ascii_strncasecmp ("UID", s, 3) == 0)
+    {
+      s += 3;
+      SKIPWS (s);
+      h->data->uid = (unsigned int) atoi (s);
+
+      s = imap_next_word (s);
+    }
+    else if (ascii_strncasecmp ("INTERNALDATE", s, 12) == 0)
+    {
+      s += 12;
+      SKIPWS (s);
+      if (*s != '\"')
+      {
+        mutt_debug (1, "msg_parse_fetch(): bogus INTERNALDATE entry: %s\n", s);
+        return -1;
+      }
+      s++;
+      ptmp = tmp;
+      while (*s && *s != '\"')
+        *ptmp++ = *s++;
+      if (*s != '\"')
+        return -1;
+      s++; /* skip past the trailing " */
+      *ptmp = 0;
+      h->received = imap_parse_date (tmp);
+    }
+    else if (ascii_strncasecmp ("RFC822.SIZE", s, 11) == 0)
+    {
+      s += 11;
+      SKIPWS (s);
+      ptmp = tmp;
+      while (isdigit ((unsigned char) *s))
+        *ptmp++ = *s++;
+      *ptmp = 0;
+      h->content_length = atoi (tmp);
+    }
+    else if (!ascii_strncasecmp ("BODY", s, 4) ||
+      !ascii_strncasecmp ("RFC822.HEADER", s, 13))
+    {
+      /* handle above, in msg_fetch_header */
+      return -2;
+    }
+    else if (*s == ')')
+      s++; /* end of request */
+    else if (*s)
+    {
+      /* got something i don't understand */
+      imap_error ("msg_parse_fetch", s);
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+/* msg_fetch_header: import IMAP FETCH response into an IMAP_HEADER.
+ *   Expects string beginning with * n FETCH.
+ *   Returns:
+ *      0 on success
+ *     -1 if the string is not a fetch response
+ *     -2 if the string is a corrupt fetch response */
+static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp)
+{
+  IMAP_DATA* idata;
+  long bytes;
+  int rc = -1; /* default now is that string isn't FETCH response*/
+
+  idata = ctx->data;
+
+  if (buf[0] != '*')
+    return rc;
+
+  /* skip to message number */
+  buf = imap_next_word (buf);
+  h->sid = atoi (buf);
+
+  /* find FETCH tag */
+  buf = imap_next_word (buf);
+  if (ascii_strncasecmp ("FETCH", buf, 5))
+    return rc;
+
+  rc = -2; /* we've got a FETCH response, for better or worse */
+  if (!(buf = strchr (buf, '(')))
+    return rc;
+  buf++;
+
+  /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
+   *   read header lines and call it again. Silly. */
+  if ((rc = msg_parse_fetch (h, buf)) != -2 || !fp)
+    return rc;
+
+  if (imap_get_literal_count (buf, &bytes) == 0)
+  {
+    imap_read_literal (fp, idata, bytes, NULL);
+
+    /* we may have other fields of the FETCH _after_ the literal
+     * (eg Domino puts FLAGS here). Nothing wrong with that, either.
+     * This all has to go - we should accept literals and nonliterals
+     * interchangeably at any time. */
+    if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
+      return rc;
+
+    if (msg_parse_fetch (h, idata->buf) == -1)
+      return rc;
+  }
+
+  rc = 0; /* success */
+
+  /* subtract headers from message size - unfortunately only the subset of
+   * headers we've requested. */
+  h->content_length -= bytes;
+
+  return rc;
+}
+
+static void flush_buffer(char *buf, size_t *len, CONNECTION *conn)
+{
+  buf[*len] = '\0';
+  mutt_socket_write_n(conn, buf, *len);
+  *len = 0;
+}
+
 /* imap_read_headers:
  * Changed to read many headers instead of just one. It will return the
  * msgno of the last message read. It will return a value other than
@@ -933,63 +1220,14 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete)
 
   rc = 0;
 
- out:
-  if (cmd.data)
-    FREE (&cmd.data);
-  if (sync_cmd.data)
-    FREE (&sync_cmd.data);
-  FREE (&mx.mbox);
-
-  return rc < 0 ? -1 : rc;
-}
-
-static body_cache_t *msg_cache_open (IMAP_DATA *idata)
-{
-  char mailbox[_POSIX_PATH_MAX];
-
-  if (idata->bcache)
-    return idata->bcache;
-
-  imap_cachepath (idata, idata->mailbox, mailbox, sizeof (mailbox));
-
-  return mutt_bcache_open (&idata->conn->account, mailbox);
-}
-
-static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h)
-{
-  char id[_POSIX_PATH_MAX];
-
-  if (!idata || !h)
-    return NULL;
-
-  idata->bcache = msg_cache_open (idata);
-  snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
-  return mutt_bcache_get (idata->bcache, id);
-}
-
-static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h)
-{
-  char id[_POSIX_PATH_MAX];
-
-  if (!idata || !h)
-    return NULL;
-
-  idata->bcache = msg_cache_open (idata);
-  snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
-  return mutt_bcache_put (idata->bcache, id, 1);
-}
-
-static int msg_cache_commit (IMAP_DATA* idata, HEADER* h)
-{
-  char id[_POSIX_PATH_MAX];
-
-  if (!idata || !h)
-    return -1;
-
-  idata->bcache = msg_cache_open (idata);
-  snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
-
-  return mutt_bcache_commit (idata->bcache, id);
+ out:
+  if (cmd.data)
+    FREE (&cmd.data);
+  if (sync_cmd.data)
+    FREE (&sync_cmd.data);
+  FREE (&mx.mbox);
+
+  return rc < 0 ? -1 : rc;
 }
 
 int imap_cache_del (IMAP_DATA* idata, HEADER* h)
@@ -1004,29 +1242,6 @@ int imap_cache_del (IMAP_DATA* idata, HEADER* h)
   return mutt_bcache_del (idata->bcache, id);
 }
 
-static int msg_cache_clean_cb (const char* id, body_cache_t* bcache, void* data)
-{
-  unsigned int uv, uid, n;
-  IMAP_DATA* idata = data;
-
-  if (sscanf (id, "%u-%u", &uv, &uid) != 2)
-    return 0;
-
-  /* bad UID */
-  if (uv != idata->uid_validity)
-    mutt_bcache_del (bcache, id);
-
-  /* TODO: presort UIDs, walk in order */
-  for (n = 0; n < idata->ctx->msgcount; n++)
-  {
-    if (uid == HEADER_DATA(idata->ctx->hdrs[n])->uid)
-      return 0;
-  }
-  mutt_bcache_del (bcache, id);
-
-  return 0;
-}
-
 int imap_cache_clean (IMAP_DATA* idata)
 {
   idata->bcache = msg_cache_open (idata);
@@ -1109,227 +1324,3 @@ char* imap_set_flags (IMAP_DATA* idata, HEADER* h, char* s)
 }
 
 
-/* msg_fetch_header: import IMAP FETCH response into an IMAP_HEADER.
- *   Expects string beginning with * n FETCH.
- *   Returns:
- *      0 on success
- *     -1 if the string is not a fetch response
- *     -2 if the string is a corrupt fetch response */
-static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp)
-{
-  IMAP_DATA* idata;
-  long bytes;
-  int rc = -1; /* default now is that string isn't FETCH response*/
-
-  idata = ctx->data;
-
-  if (buf[0] != '*')
-    return rc;
-
-  /* skip to message number */
-  buf = imap_next_word (buf);
-  h->sid = atoi (buf);
-
-  /* find FETCH tag */
-  buf = imap_next_word (buf);
-  if (ascii_strncasecmp ("FETCH", buf, 5))
-    return rc;
-
-  rc = -2; /* we've got a FETCH response, for better or worse */
-  if (!(buf = strchr (buf, '(')))
-    return rc;
-  buf++;
-
-  /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
-   *   read header lines and call it again. Silly. */
-  if ((rc = msg_parse_fetch (h, buf)) != -2 || !fp)
-    return rc;
-
-  if (imap_get_literal_count (buf, &bytes) == 0)
-  {
-    imap_read_literal (fp, idata, bytes, NULL);
-
-    /* we may have other fields of the FETCH _after_ the literal
-     * (eg Domino puts FLAGS here). Nothing wrong with that, either.
-     * This all has to go - we should accept literals and nonliterals
-     * interchangeably at any time. */
-    if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
-      return rc;
-
-    if (msg_parse_fetch (h, idata->buf) == -1)
-      return rc;
-  }
-
-  rc = 0; /* success */
-
-  /* subtract headers from message size - unfortunately only the subset of
-   * headers we've requested. */
-  h->content_length -= bytes;
-
-  return rc;
-}
-
-/* msg_parse_fetch: handle headers returned from header fetch */
-static int msg_parse_fetch (IMAP_HEADER *h, char *s)
-{
-  char tmp[SHORT_STRING];
-  char *ptmp;
-
-  if (!s)
-    return -1;
-
-  while (*s)
-  {
-    SKIPWS (s);
-
-    if (ascii_strncasecmp ("FLAGS", s, 5) == 0)
-    {
-      if ((s = msg_parse_flags (h, s)) == NULL)
-        return -1;
-    }
-    else if (ascii_strncasecmp ("UID", s, 3) == 0)
-    {
-      s += 3;
-      SKIPWS (s);
-      h->data->uid = (unsigned int) atoi (s);
-
-      s = imap_next_word (s);
-    }
-    else if (ascii_strncasecmp ("INTERNALDATE", s, 12) == 0)
-    {
-      s += 12;
-      SKIPWS (s);
-      if (*s != '\"')
-      {
-        mutt_debug (1, "msg_parse_fetch(): bogus INTERNALDATE entry: %s\n", s);
-        return -1;
-      }
-      s++;
-      ptmp = tmp;
-      while (*s && *s != '\"')
-        *ptmp++ = *s++;
-      if (*s != '\"')
-        return -1;
-      s++; /* skip past the trailing " */
-      *ptmp = 0;
-      h->received = imap_parse_date (tmp);
-    }
-    else if (ascii_strncasecmp ("RFC822.SIZE", s, 11) == 0)
-    {
-      s += 11;
-      SKIPWS (s);
-      ptmp = tmp;
-      while (isdigit ((unsigned char) *s))
-        *ptmp++ = *s++;
-      *ptmp = 0;
-      h->content_length = atoi (tmp);
-    }
-    else if (!ascii_strncasecmp ("BODY", s, 4) ||
-      !ascii_strncasecmp ("RFC822.HEADER", s, 13))
-    {
-      /* handle above, in msg_fetch_header */
-      return -2;
-    }
-    else if (*s == ')')
-      s++; /* end of request */
-    else if (*s)
-    {
-      /* got something i don't understand */
-      imap_error ("msg_parse_fetch", s);
-      return -1;
-    }
-  }
-
-  return 0;
-}
-
-/* msg_parse_flags: read a FLAGS token into an IMAP_HEADER */
-static char* msg_parse_flags (IMAP_HEADER* h, char* s)
-{
-  IMAP_HEADER_DATA* hd = h->data;
-
-  /* sanity-check string */
-  if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
-  {
-    mutt_debug (1, "msg_parse_flags: not a FLAGS response: %s\n", s);
-    return NULL;
-  }
-  s += 5;
-  SKIPWS(s);
-  if (*s != '(')
-  {
-    mutt_debug (1, "msg_parse_flags: bogus FLAGS response: %s\n", s);
-    return NULL;
-  }
-  s++;
-
-  mutt_free_list (&hd->keywords);
-  hd->deleted = hd->flagged = hd->replied = hd->read = hd->old = 0;
-
-  /* start parsing */
-  while (*s && *s != ')')
-  {
-    if (ascii_strncasecmp ("\\deleted", s, 8) == 0)
-    {
-      s += 8;
-      hd->deleted = 1;
-    }
-    else if (ascii_strncasecmp ("\\flagged", s, 8) == 0)
-    {
-      s += 8;
-      hd->flagged = 1;
-    }
-    else if (ascii_strncasecmp ("\\answered", s, 9) == 0)
-    {
-      s += 9;
-      hd->replied = 1;
-    }
-    else if (ascii_strncasecmp ("\\seen", s, 5) == 0)
-    {
-      s += 5;
-      hd->read = 1;
-    }
-    else if (ascii_strncasecmp ("\\recent", s, 7) == 0)
-      s += 7;
-    else if (ascii_strncasecmp ("old", s, 3) == 0)
-    {
-      s += 3;
-      hd->old = 1;
-    }
-    else
-    {
-      /* store custom flags as well */
-      char ctmp;
-      char* flag_word = s;
-
-      if (!hd->keywords)
-        hd->keywords = mutt_new_list ();
-
-      while (*s && !ISSPACE (*s) && *s != ')')
-        s++;
-      ctmp = *s;
-      *s = '\0';
-      mutt_add_list (hd->keywords, flag_word);
-      *s = ctmp;
-    }
-    SKIPWS(s);
-  }
-
-  /* wrap up, or note bad flags response */
-  if (*s == ')')
-    s++;
-  else
-  {
-    mutt_debug (1, "msg_parse_flags: Unterminated FLAGS response: %s\n", s);
-    return NULL;
-  }
-
-  return s;
-}
-
-static void flush_buffer(char *buf, size_t *len, CONNECTION *conn)
-{
-  buf[*len] = '\0';
-  mutt_socket_write_n(conn, buf, *len);
-  *len = 0;
-}
diff --git a/init.c b/init.c
index 1ba65b994c96f02fc1487de9556ba7845c3ebb69..6b7e9fcc6d9a4805014cdea34868d52e981640a7 100644 (file)
--- a/init.c
+++ b/init.c
@@ -73,8 +73,43 @@ typedef struct myvar
 
 static myvar_t* MyVars;
 
-static void myvar_set (const char* var, const char* val);
-static void myvar_del (const char* var);
+static void myvar_set (const char* var, const char* val)
+{
+  myvar_t** cur;
+
+  for (cur = &MyVars; *cur; cur = &((*cur)->next))
+    if (!mutt_strcmp ((*cur)->name, var))
+      break;
+
+  if (!*cur)
+    *cur = safe_calloc (1, sizeof (myvar_t));
+
+  if (!(*cur)->name)
+    (*cur)->name = safe_strdup (var);
+
+  mutt_str_replace (&(*cur)->value, val);
+}
+
+static void myvar_del (const char* var)
+{
+  myvar_t **cur;
+  myvar_t *tmp;
+
+
+  for (cur = &MyVars; *cur; cur = &((*cur)->next))
+    if (!mutt_strcmp ((*cur)->name, var))
+      break;
+
+  if (*cur)
+  {
+    tmp = (*cur)->next;
+    FREE (&(*cur)->name);
+    FREE (&(*cur)->value);
+    FREE (cur);                /* __FREE_CHECKED__ */
+    *cur = tmp;
+  }
+}
+
 
 #ifdef USE_NOTMUCH
 /* List of tags found in last call to mutt_nm_query_complete(). */
@@ -92,6 +127,57 @@ static void toggle_quadoption (int opt)
   QuadOptions[n] ^= (1 << b);
 }
 
+static int parse_regex(int idx, BUFFER *tmp, BUFFER *err)
+{
+  int e, flags = 0;
+  const char *p;
+  regex_t *rx;
+  REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
+
+  if (!ptr->pattern || (mutt_strcmp(ptr->pattern, tmp->data) != 0))
+  {
+    int not = 0;
+
+    /* $mask is case-sensitive */
+    if (mutt_strcmp(MuttVars[idx].option, "mask") != 0)
+      flags |= mutt_which_case(tmp->data);
+
+    p = tmp->data;
+    if (mutt_strcmp(MuttVars[idx].option, "mask") == 0)
+    {
+      if (*p == '!')
+      {
+        not = 1;
+        p++;
+      }
+    }
+
+    rx = safe_malloc(sizeof(regex_t));
+    if ((e = REGCOMP(rx, p, flags)) != 0)
+    {
+      regerror(e, rx, err->data, err->dsize);
+      FREE(&rx);
+      return 0;
+    }
+
+    /* get here only if everything went smoothly */
+    if (ptr->pattern)
+    {
+      FREE(&ptr->pattern);
+      regfree((regex_t *) ptr->rx);
+      FREE(&ptr->rx);
+    }
+
+    ptr->pattern = safe_strdup(tmp->data);
+    ptr->rx = rx;
+    ptr->not = not;
+
+    return 1;
+  }
+  return 0;
+}
+
+
 void set_quadoption (int opt, int flag)
 {
   int n = opt/4;
@@ -175,62 +261,87 @@ const struct option_t *mutt_option_get(const char *s)
 }
 #endif
 
-static int parse_regex(int idx, BUFFER *tmp, BUFFER *err)
+static void free_mbchar_table (mbchar_table **t)
 {
-  int e, flags = 0;
-  const char *p;
-  regex_t *rx;
-  REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
+  if (!t || !*t)
+    return;
 
-  if (!ptr->pattern || (mutt_strcmp(ptr->pattern, tmp->data) != 0))
-  {
-    int not = 0;
+  FREE (&(*t)->chars);
+  FREE (&(*t)->segmented_str);
+  FREE (&(*t)->orig_str);
+  FREE (t);            /* __FREE_CHECKED__ */
+}
 
-    /* $mask is case-sensitive */
-    if (mutt_strcmp(MuttVars[idx].option, "mask") != 0)
-      flags |= mutt_which_case(tmp->data);
+static mbchar_table *parse_mbchar_table (const char *s)
+{
+  mbchar_table *t;
+  size_t slen, k;
+  mbstate_t mbstate;
+  char *d;
 
-    p = tmp->data;
-    if (mutt_strcmp(MuttVars[idx].option, "mask") == 0)
-    {
-      if (*p == '!')
-      {
-        not = 1;
-        p++;
-      }
-    }
+  t = safe_calloc (1, sizeof (mbchar_table));
+  slen = mutt_strlen (s);
+  if (!slen)
+    return t;
 
-    rx = safe_malloc(sizeof(regex_t));
-    if ((e = REGCOMP(rx, p, flags)) != 0)
-    {
-      regerror(e, rx, err->data, err->dsize);
-      FREE(&rx);
-      return 0;
-    }
+  t->orig_str = safe_strdup (s);
+  /* This could be more space efficient.  However, being used on tiny
+   * strings (Tochars and StChars), the overhead is not great. */
+  t->chars = safe_calloc (slen, sizeof (char *));
+  d = t->segmented_str = safe_calloc (slen * 2, sizeof (char));
 
-    /* get here only if everything went smoothly */
-    if (ptr->pattern)
+  memset (&mbstate, 0, sizeof (mbstate));
+  while (slen && (k = mbrtowc (NULL, s, slen, &mbstate)))
+  {
+    if (k == (size_t)(-1) || k == (size_t)(-2))
     {
-      FREE(&ptr->pattern);
-      regfree((regex_t *) ptr->rx);
-      FREE(&ptr->rx);
+      mutt_debug (1,
+                  "parse_mbchar_table: mbrtowc returned %d converting %s in %s\n",
+                  (k == (size_t)(-1)) ? -1 : -2, s, t->orig_str);
+      if (k == (size_t)(-1))
+        memset (&mbstate, 0, sizeof (mbstate));
+      k = (k == (size_t)(-1)) ? 1 : slen;
     }
 
-    ptr->pattern = safe_strdup(tmp->data);
-    ptr->rx = rx;
-    ptr->not = not;
+    slen -= k;
+    t->chars[t->len++] = d;
+    while (k--)
+      *d++ = *s++;
+    *d++ = '\0';
+  }
 
-    return 1;
+  return t;
+}
+
+static int
+parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
+{
+  int i, flags = 0;
+
+  if (mutt_strncmp ("reverse-", s, 8) == 0)
+  {
+    s += 8;
+    flags = SORT_REVERSE;
   }
+
+  if (mutt_strncmp ("last-", s, 5) == 0)
+  {
+    s += 5;
+    flags |= SORT_LAST;
+  }
+
+  if ((i = mutt_getvaluebyname (s, map)) == -1)
+  {
+    snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
+    return (-1);
+  }
+
+  *val = i | flags;
+
   return 0;
 }
 
 #ifdef USE_LUA
-static void free_mbchar_table(mbchar_table **t);
-static mbchar_table *parse_mbchar_table(const char *s);
-static int parse_sort(short *val, const char *s, const struct mapping_t *map,
-                      BUFFER *err);
-
 int mutt_option_set(const struct option_t *val, BUFFER *err)
 {
   mutt_debug(2, " * mutt_option_set()\n");
@@ -496,7 +607,42 @@ int mutt_add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
   return 0;
 }
 
-static int remove_from_replace_list (REPLACE_LIST **list, const char *pat);
+static int remove_from_replace_list (REPLACE_LIST **list, const char *pat)
+{
+  REPLACE_LIST *cur, *prev;
+  int nremoved = 0;
+
+  /* Being first is a special case. */
+  cur = *list;
+  if (!cur)
+    return 0;
+  if (cur->rx && !mutt_strcmp(cur->rx->pattern, pat))
+  {
+    *list = cur->next;
+    mutt_free_regexp(&cur->rx);
+    FREE(&cur->template);
+    FREE(&cur);
+    return 1;
+  }
+
+  prev = cur;
+  for (cur = prev->next; cur;)
+  {
+    if (!mutt_strcmp(cur->rx->pattern, pat))
+    {
+      prev->next = cur->next;
+      mutt_free_regexp(&cur->rx);
+      FREE(&cur->template);
+      FREE(&cur);
+      cur = prev->next;
+      ++nremoved;
+    }
+    else
+      cur = cur->next;
+  }
+
+  return nremoved;
+}
 
 static int add_to_replace_list (REPLACE_LIST **list, const char *pat, const char *templ, BUFFER *err)
 {
@@ -581,43 +727,6 @@ static int add_to_replace_list (REPLACE_LIST **list, const char *pat, const char
   return 0;
 }
 
-static int remove_from_replace_list (REPLACE_LIST **list, const char *pat)
-{
-  REPLACE_LIST *cur, *prev;
-  int nremoved = 0;
-
-  /* Being first is a special case. */
-  cur = *list;
-  if (!cur)
-    return 0;
-  if (cur->rx && !mutt_strcmp(cur->rx->pattern, pat))
-  {
-    *list = cur->next;
-    mutt_free_regexp(&cur->rx);
-    FREE(&cur->template);
-    FREE(&cur);
-    return 1;
-  }
-
-  prev = cur;
-  for (cur = prev->next; cur;)
-  {
-    if (!mutt_strcmp(cur->rx->pattern, pat))
-    {
-      prev->next = cur->next;
-      mutt_free_regexp(&cur->rx);
-      FREE(&cur->template);
-      FREE(&cur);
-      cur = prev->next;
-      ++nremoved;
-    }
-    else
-      cur = cur->next;
-  }
-
-  return nremoved;
-}
-
 
 static void remove_from_list (LIST **l, const char *str)
 {
@@ -768,58 +877,6 @@ static int parse_ifdef (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
   return 0;
 }
 
-static void free_mbchar_table (mbchar_table **t)
-{
-  if (!t || !*t)
-    return;
-
-  FREE (&(*t)->chars);
-  FREE (&(*t)->segmented_str);
-  FREE (&(*t)->orig_str);
-  FREE (t);            /* __FREE_CHECKED__ */
-}
-
-static mbchar_table *parse_mbchar_table (const char *s)
-{
-  mbchar_table *t;
-  size_t slen, k;
-  mbstate_t mbstate;
-  char *d;
-
-  t = safe_calloc (1, sizeof (mbchar_table));
-  slen = mutt_strlen (s);
-  if (!slen)
-    return t;
-
-  t->orig_str = safe_strdup (s);
-  /* This could be more space efficient.  However, being used on tiny
-   * strings (Tochars and StChars), the overhead is not great. */
-  t->chars = safe_calloc (slen, sizeof (char *));
-  d = t->segmented_str = safe_calloc (slen * 2, sizeof (char));
-
-  memset (&mbstate, 0, sizeof (mbstate));
-  while (slen && (k = mbrtowc (NULL, s, slen, &mbstate)))
-  {
-    if (k == (size_t)(-1) || k == (size_t)(-2))
-    {
-      mutt_debug (1,
-                  "parse_mbchar_table: mbrtowc returned %d converting %s in %s\n",
-                  (k == (size_t)(-1)) ? -1 : -2, s, t->orig_str);
-      if (k == (size_t)(-1))
-        memset (&mbstate, 0, sizeof (mbstate));
-      k = (k == (size_t)(-1)) ? 1 : slen;
-    }
-
-    slen -= k;
-    t->chars[t->len++] = d;
-    while (k--)
-      *d++ = *s++;
-    *d++ = '\0';
-  }
-
-  return t;
-}
-
 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
 {
   do
@@ -1773,35 +1830,7 @@ static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err
   return 0;
 }
 
-static int
-parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
-{
-  int i, flags = 0;
-
-  if (mutt_strncmp ("reverse-", s, 8) == 0)
-  {
-    s += 8;
-    flags = SORT_REVERSE;
-  }
-
-  if (mutt_strncmp ("last-", s, 5) == 0)
-  {
-    s += 5;
-    flags |= SORT_LAST;
-  }
-
-  if ((i = mutt_getvaluebyname (s, map)) == -1)
-  {
-    snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
-    return (-1);
-  }
-
-  *val = i | flags;
-
-  return 0;
-}
-
-static void mutt_set_default (struct option_t *p)
+static void set_default (struct option_t *p)
 {
   switch (p->type & DT_MASK)
   {
@@ -3881,7 +3910,7 @@ void mutt_init (int skip_sys_rc, LIST *commands)
   /* Set standard defaults */
   for (i = 0; MuttVars[i].option; i++)
   {
-    mutt_set_default (&MuttVars[i]);
+    set_default (&MuttVars[i]);
     mutt_restore_default (&MuttVars[i]);
   }
 
@@ -4145,43 +4174,6 @@ static int parse_tag_formats (BUFFER *b, BUFFER *s, unsigned long data, BUFFER *
 }
 #endif
 
-static void myvar_set (const char* var, const char* val)
-{
-  myvar_t** cur;
-
-  for (cur = &MyVars; *cur; cur = &((*cur)->next))
-    if (!mutt_strcmp ((*cur)->name, var))
-      break;
-
-  if (!*cur)
-    *cur = safe_calloc (1, sizeof (myvar_t));
-
-  if (!(*cur)->name)
-    (*cur)->name = safe_strdup (var);
-
-  mutt_str_replace (&(*cur)->value, val);
-}
-
-static void myvar_del (const char* var)
-{
-  myvar_t **cur;
-  myvar_t *tmp;
-
-
-  for (cur = &MyVars; *cur; cur = &((*cur)->next))
-    if (!mutt_strcmp ((*cur)->name, var))
-      break;
-
-  if (*cur)
-  {
-    tmp = (*cur)->next;
-    FREE (&(*cur)->name);
-    FREE (&(*cur)->value);
-    FREE (cur);                /* __FREE_CHECKED__ */
-    *cur = tmp;
-  }
-}
-
 const char* myvar_get (const char* var)
 {
   myvar_t* cur;
diff --git a/lib.c b/lib.c
index 40d830ceb20ed6ea503bab43e038e2a48f05fb68..7dd1a62465cfcb8a1b5b3bbb675384d0519a3ca8 100644 (file)
--- a/lib.c
+++ b/lib.c
@@ -48,8 +48,6 @@
 
 #include "lib.h"
 
-static int mutt_atol (const char *str, long *dst);
-
 static const struct sysexits
 {
   int v;
@@ -1057,6 +1055,26 @@ void mutt_debug (int level, const char *fmt, ...)
 }
 #endif
 
+static int mutt_atol (const char *str, long *dst)
+{
+  long r;
+  long *res = dst ? dst : &r;
+  char *e = NULL;
+
+  /* no input: 0 */
+  if (!str || !*str)
+  {
+    *res = 0;
+    return 0;
+  }
+
+  *res = strtol (str, &e, 10);
+  if ((*res == LONG_MAX && errno == ERANGE) ||
+      (e && *e != '\0'))
+    return -1;
+  return 0;
+}
+
 int mutt_atos (const char *str, short *dst)
 {
   int rc;
@@ -1093,26 +1111,6 @@ int mutt_atoi (const char *str, int *dst)
   return 0;
 }
 
-static int mutt_atol (const char *str, long *dst)
-{
-  long r;
-  long *res = dst ? dst : &r;
-  char *e = NULL;
-
-  /* no input: 0 */
-  if (!str || !*str)
-  {
-    *res = 0;
-    return 0;
-  }
-
-  *res = strtol (str, &e, 10);
-  if ((*res == LONG_MAX && errno == ERANGE) ||
-      (e && *e != '\0'))
-    return -1;
-  return 0;
-}
-
 /**
  * mutt_inbox_cmp - check whether two folders share the same path and one is
  * an inbox.
diff --git a/mbox.c b/mbox.c
index ce270c01f70a51c623e26942360b7a6aae778442..eef2387849e6a707b55f9bf3c6cd6d5314961e31 100644 (file)
--- a/mbox.c
+++ b/mbox.c
@@ -36,8 +36,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-static int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint);
-
 /* struct used by mutt_sync_mailbox() to store new offsets */
 struct m_update_t
 {
@@ -666,6 +664,184 @@ int mbox_strict_cmp_headers (const HEADER *h1, const HEADER *h2)
   }
 }
 
+static int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint)
+{
+  int (*cmp_headers) (const HEADER *, const HEADER *) = NULL;
+  HEADER **old_hdrs;
+  int old_msgcount;
+  int msg_mod = 0;
+  int index_hint_set;
+  int i, j;
+  int rc = -1;
+
+  /* silent operations */
+  ctx->quiet = 1;
+
+  if (!ctx->quiet)
+    mutt_message (_("Reopening mailbox..."));
+
+  /* our heuristics require the old mailbox to be unsorted */
+  if (Sort != SORT_ORDER)
+  {
+    short old_sort;
+
+    old_sort = Sort;
+    Sort = SORT_ORDER;
+    mutt_sort_headers (ctx, 1);
+    Sort = old_sort;
+  }
+
+  old_hdrs = NULL;
+  old_msgcount = 0;
+
+  /* simulate a close */
+  if (ctx->id_hash)
+    hash_destroy (&ctx->id_hash, NULL);
+  if (ctx->subj_hash)
+    hash_destroy (&ctx->subj_hash, NULL);
+  hash_destroy (&ctx->label_hash, NULL);
+  mutt_clear_threads (ctx);
+  FREE (&ctx->v2r);
+  if (ctx->readonly)
+  {
+    for (i = 0; i < ctx->msgcount; i++)
+      mutt_free_header (&(ctx->hdrs[i])); /* nothing to do! */
+    FREE (&ctx->hdrs);
+  }
+  else
+  {
+      /* save the old headers */
+    old_msgcount = ctx->msgcount;
+    old_hdrs = ctx->hdrs;
+    ctx->hdrs = NULL;
+  }
+
+  ctx->hdrmax = 0;     /* force allocation of new headers */
+  ctx->msgcount = 0;
+  ctx->vcount = 0;
+  ctx->tagged = 0;
+  ctx->deleted = 0;
+  ctx->new = 0;
+  ctx->unread = 0;
+  ctx->flagged = 0;
+  ctx->changed = 0;
+  ctx->id_hash = NULL;
+  ctx->subj_hash = NULL;
+  mutt_make_label_hash (ctx);
+
+  switch (ctx->magic)
+  {
+    case MUTT_MBOX:
+    case MUTT_MMDF:
+      cmp_headers = mbox_strict_cmp_headers;
+      safe_fclose (&ctx->fp);
+      if (!(ctx->fp = safe_fopen (ctx->path, "r")))
+       rc = -1;
+      else
+       rc = ((ctx->magic == MUTT_MBOX) ? mbox_parse_mailbox
+                                      : mmdf_parse_mailbox) (ctx);
+      break;
+
+    default:
+      rc = -1;
+      break;
+  }
+
+  if (rc == -1)
+  {
+    /* free the old headers */
+    for (j = 0; j < old_msgcount; j++)
+      mutt_free_header (&(old_hdrs[j]));
+    FREE (&old_hdrs);
+
+    ctx->quiet = 0;
+    return (-1);
+  }
+
+  mutt_touch_atime (fileno (ctx->fp));
+
+  /* now try to recover the old flags */
+
+  index_hint_set = (index_hint == NULL);
+
+  if (!ctx->readonly)
+  {
+    for (i = 0; i < ctx->msgcount; i++)
+    {
+      int found = 0;
+
+      /* some messages have been deleted, and new  messages have been
+       * appended at the end; the heuristic is that old messages have then
+       * "advanced" towards the beginning of the folder, so we begin the
+       * search at index "i"
+       */
+      for (j = i; j < old_msgcount; j++)
+      {
+       if (old_hdrs[j] == NULL)
+         continue;
+       if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
+       {
+         found = 1;
+         break;
+       }
+      }
+      if (!found)
+      {
+       for (j = 0; j < i && j < old_msgcount; j++)
+       {
+         if (old_hdrs[j] == NULL)
+           continue;
+         if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
+         {
+           found = 1;
+           break;
+         }
+       }
+      }
+
+      if (found)
+      {
+       /* this is best done here */
+       if (!index_hint_set && *index_hint == j)
+         *index_hint = i;
+
+       if (old_hdrs[j]->changed)
+       {
+         /* Only update the flags if the old header was changed;
+          * otherwise, the header may have been modified externally,
+          * and we don't want to lose _those_ changes
+          */
+         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_FLAG, old_hdrs[j]->flagged);
+         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_REPLIED, old_hdrs[j]->replied);
+         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_OLD, old_hdrs[j]->old);
+         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_READ, old_hdrs[j]->read);
+       }
+       mutt_set_flag (ctx, ctx->hdrs[i], MUTT_DELETE, old_hdrs[j]->deleted);
+       mutt_set_flag (ctx, ctx->hdrs[i], MUTT_PURGE, old_hdrs[j]->purge);
+       mutt_set_flag (ctx, ctx->hdrs[i], MUTT_TAG, old_hdrs[j]->tagged);
+
+       /* we don't need this header any more */
+       mutt_free_header (&(old_hdrs[j]));
+      }
+    }
+
+    /* free the remaining old headers */
+    for (j = 0; j < old_msgcount; j++)
+    {
+      if (old_hdrs[j])
+      {
+       mutt_free_header (&(old_hdrs[j]));
+       msg_mod = 1;
+      }
+    }
+    FREE (&old_hdrs);
+  }
+
+  ctx->quiet = 0;
+
+  return ((ctx->changed || msg_mod) ? MUTT_REOPENED : MUTT_NEW_MAIL);
+}
+
 /* check to see if the mailbox has changed on disk.
  *
  * return values:
@@ -1192,184 +1368,6 @@ bail:  /* Come here in case of disaster */
   return rc;
 }
 
-static int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint)
-{
-  int (*cmp_headers) (const HEADER *, const HEADER *) = NULL;
-  HEADER **old_hdrs;
-  int old_msgcount;
-  int msg_mod = 0;
-  int index_hint_set;
-  int i, j;
-  int rc = -1;
-
-  /* silent operations */
-  ctx->quiet = 1;
-
-  if (!ctx->quiet)
-    mutt_message (_("Reopening mailbox..."));
-
-  /* our heuristics require the old mailbox to be unsorted */
-  if (Sort != SORT_ORDER)
-  {
-    short old_sort;
-
-    old_sort = Sort;
-    Sort = SORT_ORDER;
-    mutt_sort_headers (ctx, 1);
-    Sort = old_sort;
-  }
-
-  old_hdrs = NULL;
-  old_msgcount = 0;
-
-  /* simulate a close */
-  if (ctx->id_hash)
-    hash_destroy (&ctx->id_hash, NULL);
-  if (ctx->subj_hash)
-    hash_destroy (&ctx->subj_hash, NULL);
-  hash_destroy (&ctx->label_hash, NULL);
-  mutt_clear_threads (ctx);
-  FREE (&ctx->v2r);
-  if (ctx->readonly)
-  {
-    for (i = 0; i < ctx->msgcount; i++)
-      mutt_free_header (&(ctx->hdrs[i])); /* nothing to do! */
-    FREE (&ctx->hdrs);
-  }
-  else
-  {
-      /* save the old headers */
-    old_msgcount = ctx->msgcount;
-    old_hdrs = ctx->hdrs;
-    ctx->hdrs = NULL;
-  }
-
-  ctx->hdrmax = 0;     /* force allocation of new headers */
-  ctx->msgcount = 0;
-  ctx->vcount = 0;
-  ctx->tagged = 0;
-  ctx->deleted = 0;
-  ctx->new = 0;
-  ctx->unread = 0;
-  ctx->flagged = 0;
-  ctx->changed = 0;
-  ctx->id_hash = NULL;
-  ctx->subj_hash = NULL;
-  mutt_make_label_hash (ctx);
-
-  switch (ctx->magic)
-  {
-    case MUTT_MBOX:
-    case MUTT_MMDF:
-      cmp_headers = mbox_strict_cmp_headers;
-      safe_fclose (&ctx->fp);
-      if (!(ctx->fp = safe_fopen (ctx->path, "r")))
-       rc = -1;
-      else
-       rc = ((ctx->magic == MUTT_MBOX) ? mbox_parse_mailbox
-                                      : mmdf_parse_mailbox) (ctx);
-      break;
-
-    default:
-      rc = -1;
-      break;
-  }
-
-  if (rc == -1)
-  {
-    /* free the old headers */
-    for (j = 0; j < old_msgcount; j++)
-      mutt_free_header (&(old_hdrs[j]));
-    FREE (&old_hdrs);
-
-    ctx->quiet = 0;
-    return (-1);
-  }
-
-  mutt_touch_atime (fileno (ctx->fp));
-
-  /* now try to recover the old flags */
-
-  index_hint_set = (index_hint == NULL);
-
-  if (!ctx->readonly)
-  {
-    for (i = 0; i < ctx->msgcount; i++)
-    {
-      int found = 0;
-
-      /* some messages have been deleted, and new  messages have been
-       * appended at the end; the heuristic is that old messages have then
-       * "advanced" towards the beginning of the folder, so we begin the
-       * search at index "i"
-       */
-      for (j = i; j < old_msgcount; j++)
-      {
-       if (old_hdrs[j] == NULL)
-         continue;
-       if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
-       {
-         found = 1;
-         break;
-       }
-      }
-      if (!found)
-      {
-       for (j = 0; j < i && j < old_msgcount; j++)
-       {
-         if (old_hdrs[j] == NULL)
-           continue;
-         if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
-         {
-           found = 1;
-           break;
-         }
-       }
-      }
-
-      if (found)
-      {
-       /* this is best done here */
-       if (!index_hint_set && *index_hint == j)
-         *index_hint = i;
-
-       if (old_hdrs[j]->changed)
-       {
-         /* Only update the flags if the old header was changed;
-          * otherwise, the header may have been modified externally,
-          * and we don't want to lose _those_ changes
-          */
-         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_FLAG, old_hdrs[j]->flagged);
-         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_REPLIED, old_hdrs[j]->replied);
-         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_OLD, old_hdrs[j]->old);
-         mutt_set_flag (ctx, ctx->hdrs[i], MUTT_READ, old_hdrs[j]->read);
-       }
-       mutt_set_flag (ctx, ctx->hdrs[i], MUTT_DELETE, old_hdrs[j]->deleted);
-       mutt_set_flag (ctx, ctx->hdrs[i], MUTT_PURGE, old_hdrs[j]->purge);
-       mutt_set_flag (ctx, ctx->hdrs[i], MUTT_TAG, old_hdrs[j]->tagged);
-
-       /* we don't need this header any more */
-       mutt_free_header (&(old_hdrs[j]));
-      }
-    }
-
-    /* free the remaining old headers */
-    for (j = 0; j < old_msgcount; j++)
-    {
-      if (old_hdrs[j])
-      {
-       mutt_free_header (&(old_hdrs[j]));
-       msg_mod = 1;
-      }
-    }
-    FREE (&old_hdrs);
-  }
-
-  ctx->quiet = 0;
-
-  return ((ctx->changed || msg_mod) ? MUTT_REOPENED : MUTT_NEW_MAIL);
-}
-
 /*
  * Returns:
  * 1 if the mailbox is not empty
diff --git a/mbyte.c b/mbyte.c
index c08185f60add3e536a95db4c003c70951fd87fd7..e39c217ddca811662d29f8bbc1088e79139faf1a 100644 (file)
--- a/mbyte.c
+++ b/mbyte.c
@@ -42,10 +42,6 @@ static iconv_t charset_to_utf8 = (iconv_t)(-1);
 static iconv_t charset_from_utf8 = (iconv_t)(-1);
 #endif
 
-#ifndef HAVE_WC_FUNCS
-static size_t utf8rtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *_ps);
-#endif
-
 void mutt_set_charset (char *charset)
 {
   char buffer[STRING];
@@ -93,6 +89,93 @@ void mutt_set_charset (char *charset)
 
 #ifndef HAVE_WC_FUNCS
 
+static size_t utf8rtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *_ps)
+{
+  static wchar_t mbstate;
+  wchar_t *ps = (wchar_t *)_ps;
+  size_t k = 1;
+  unsigned char c;
+  wchar_t wc;
+  int count;
+
+  if (!ps)
+    ps = &mbstate;
+
+  if (!s)
+  {
+    *ps = 0;
+    return 0;
+  }
+  if (!n)
+    return (size_t)-2;
+
+  if (!*ps)
+  {
+    c = (unsigned char)*s;
+    if (c < 0x80)
+    {
+      if (pwc)
+       *pwc = c;
+      return (c != 0);
+    }
+    else if (c < 0xc2)
+    {
+      errno = EILSEQ;
+      return (size_t)-1;
+    }
+    else if (c < 0xe0)
+      wc = ((c & 0x1f) << 6) + (count = 0);
+    else if (c < 0xf0)
+      wc = ((c & 0x0f) << 12) + (count = 1);
+    else if (c < 0xf8)
+      wc = ((c & 0x07) << 18) + (count = 2);
+    else if (c < 0xfc)
+      wc = ((c & 0x03) << 24) + (count = 3);
+    else if (c < 0xfe)
+      wc = ((c & 0x01) << 30) + (count = 4);
+    else
+    {
+      errno = EILSEQ;
+      return (size_t)-1;
+    }
+    ++s, --n, ++k;
+  }
+  else
+  {
+    wc = *ps & 0x7fffffff;
+    count = wc & 7; /* if count > 4 it will be caught below */
+  }
+
+  for (; n; ++s, --n, ++k)
+  {
+    c = (unsigned char)*s;
+    if (0x80 <= c && c < 0xc0)
+    {
+      wc |= (c & 0x3f) << (6 * count);
+      if (!count)
+      {
+       if (pwc)
+         *pwc = wc;
+       *ps = 0;
+       return wc ? k : 0;
+      }
+      --count, --wc;
+      if (!(wc >> (11+count*5)))
+      {
+       errno = count < 4 ? EILSEQ : EINVAL;
+       return (size_t)-1;
+      }
+    }
+    else
+    {
+      errno = EILSEQ;
+      return (size_t)-1;
+    }
+  }
+  *ps = wc;
+  return (size_t)-2;
+}
+
 /*
  * For systems that don't have them, we provide here our own
  * implementations of wcrtomb(), mbrtowc(), iswprint() and wcwidth().
@@ -436,93 +519,6 @@ int wcwidth (wchar_t wc)
   return wcwidth_ucs (wc);
 }
 
-static size_t utf8rtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *_ps)
-{
-  static wchar_t mbstate;
-  wchar_t *ps = (wchar_t *)_ps;
-  size_t k = 1;
-  unsigned char c;
-  wchar_t wc;
-  int count;
-
-  if (!ps)
-    ps = &mbstate;
-
-  if (!s)
-  {
-    *ps = 0;
-    return 0;
-  }
-  if (!n)
-    return (size_t)-2;
-
-  if (!*ps)
-  {
-    c = (unsigned char)*s;
-    if (c < 0x80)
-    {
-      if (pwc)
-       *pwc = c;
-      return (c != 0);
-    }
-    else if (c < 0xc2)
-    {
-      errno = EILSEQ;
-      return (size_t)-1;
-    }
-    else if (c < 0xe0)
-      wc = ((c & 0x1f) << 6) + (count = 0);
-    else if (c < 0xf0)
-      wc = ((c & 0x0f) << 12) + (count = 1);
-    else if (c < 0xf8)
-      wc = ((c & 0x07) << 18) + (count = 2);
-    else if (c < 0xfc)
-      wc = ((c & 0x03) << 24) + (count = 3);
-    else if (c < 0xfe)
-      wc = ((c & 0x01) << 30) + (count = 4);
-    else
-    {
-      errno = EILSEQ;
-      return (size_t)-1;
-    }
-    ++s, --n, ++k;
-  }
-  else
-  {
-    wc = *ps & 0x7fffffff;
-    count = wc & 7; /* if count > 4 it will be caught below */
-  }
-
-  for (; n; ++s, --n, ++k)
-  {
-    c = (unsigned char)*s;
-    if (0x80 <= c && c < 0xc0)
-    {
-      wc |= (c & 0x3f) << (6 * count);
-      if (!count)
-      {
-       if (pwc)
-         *pwc = wc;
-       *ps = 0;
-       return wc ? k : 0;
-      }
-      --count, --wc;
-      if (!(wc >> (11+count*5)))
-      {
-       errno = count < 4 ? EILSEQ : EINVAL;
-       return (size_t)-1;
-      }
-    }
-    else
-    {
-      errno = EILSEQ;
-      return (size_t)-1;
-    }
-  }
-  *ps = wc;
-  return (size_t)-2;
-}
-
 #endif /* !HAVE_WC_FUNCS */
 
 wchar_t replacement_char (void)
diff --git a/mh.c b/mh.c
index 7362ad9abc7104bbc007faa426c76ef50c1de29a..36c78c275756ad80a18122d9eced058cf059d396 100644 (file)
--- a/mh.c
+++ b/mh.c
 
 #define                INS_SORT_THRESHOLD              6
 
-static int maildir_check_mailbox (CONTEXT * ctx, int *index_hint);
-static int mh_check_mailbox (CONTEXT * ctx, int *index_hint);
-static int mh_valid_message (const char *s);
-
 struct maildir
 {
   HEADER *h;
@@ -258,6 +254,21 @@ static int mh_already_notified(BUFFY *b, int msgno)
   return -1;
 }
 
+/* Ignore the garbage files.  A valid MH message consists of only
+ * digits.  Deleted message get moved to a filename with a comma before
+ * it.
+ */
+
+static int mh_valid_message (const char *s)
+{
+  for (; *s; s++)
+  {
+    if (!isdigit ((unsigned char) *s))
+      return 0;
+  }
+  return 1;
+}
+
 /* Checks new mail for a mh mailbox.
  * check_stats: if true, also count total, new, and flagged messages.
  * Returns 1 if the mailbox has new mail.
@@ -791,21 +802,6 @@ HEADER *maildir_parse_message (int magic, const char *fname,
   return NULL;
 }
 
-/* Ignore the garbage files.  A valid MH message consists of only
- * digits.  Deleted message get moved to a filename with a comma before
- * it.
- */
-
-static int mh_valid_message (const char *s)
-{
-  for (; *s; s++)
-  {
-    if (!isdigit ((unsigned char) *s))
-      return 0;
-  }
-  return 1;
-}
-
 static int maildir_parse_dir (CONTEXT * ctx, struct maildir ***last,
                              const char *subdir, int *count,
                              progress_t *progress)
@@ -1984,82 +1980,6 @@ int mh_sync_mailbox_message (CONTEXT * ctx, int msgno)
     return 0;
 }
 
-static int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
-{
-  int i, j;
-#ifdef USE_HCACHE
-  header_cache_t *hc = NULL;
-#endif /* USE_HCACHE */
-  char msgbuf[STRING];
-  progress_t progress;
-
-  if (ctx->magic == MUTT_MH)
-    i = mh_check_mailbox (ctx, index_hint);
-  else
-    i = maildir_check_mailbox (ctx, index_hint);
-
-  if (i != 0)
-    return i;
-
-#ifdef USE_HCACHE
-  if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
-    hc = mutt_hcache_open(HeaderCache, ctx->path, NULL);
-#endif /* USE_HCACHE */
-
-  if (!ctx->quiet)
-  {
-    snprintf (msgbuf, sizeof (msgbuf), _("Writing %s..."), ctx->path);
-    mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, WriteInc, ctx->msgcount);
-  }
-
-  for (i = 0; i < ctx->msgcount; i++)
-  {
-    if (!ctx->quiet)
-      mutt_progress_update (&progress, i, -1);
-
-#ifdef USE_HCACHE
-    if (mh_sync_mailbox_message (ctx, i, hc) == -1)
-      goto err;
-#else
-    if (mh_sync_mailbox_message (ctx, i) == -1)
-      goto err;
-#endif
-  }
-
-#ifdef USE_HCACHE
-  if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
-    mutt_hcache_close (hc);
-#endif /* USE_HCACHE */
-
-  if (ctx->magic == MUTT_MH)
-    mh_update_sequences (ctx);
-
-  /* XXX race condition? */
-
-  maildir_update_mtime (ctx);
-
-  /* adjust indices */
-
-  if (ctx->deleted)
-  {
-    for (i = 0, j = 0; i < ctx->msgcount; i++)
-    {
-      if (!ctx->hdrs[i]->deleted
-         || (ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH)))
-       ctx->hdrs[i]->index = j++;
-    }
-  }
-
-  return 0;
-
-err:
-#ifdef USE_HCACHE
-  if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
-    mutt_hcache_close (hc);
-#endif /* USE_HCACHE */
-  return -1;
-}
-
 static char *maildir_canon_filename (char *dest, const char *src, size_t l)
 {
   char *t, *u;
@@ -2102,43 +2022,6 @@ static void maildir_update_tables (CONTEXT *ctx, int *index_hint)
   mutt_clear_threads (ctx);
 }
 
-void maildir_update_flags (CONTEXT *ctx, HEADER *o, HEADER *n)
-{
-  /* save the global state here so we can reset it at the
-   * end of list block if required.
-   */
-  int context_changed = ctx->changed;
-
-  /* user didn't modify this message.  alter the flags to match the
-   * current state on disk.  This may not actually do
-   * anything. mutt_set_flag() will just ignore the call if the status
-   * bits are already properly set, but it is still faster not to pass
-   * through it */
-  if (o->flagged != n->flagged)
-    mutt_set_flag (ctx, o, MUTT_FLAG, n->flagged);
-  if (o->replied != n->replied)
-    mutt_set_flag (ctx, o, MUTT_REPLIED, n->replied);
-  if (o->read != n->read)
-    mutt_set_flag (ctx, o, MUTT_READ, n->read);
-  if (o->old != n->old)
-    mutt_set_flag (ctx, o, MUTT_OLD, n->old);
-
-  /* mutt_set_flag() will set this, but we don't need to
-   * sync the changes we made because we just updated the
-   * context to match the current on-disk state of the
-   * message.
-   */
-  o->changed = 0;
-
-  /* if the mailbox was not modified before we made these
-   * changes, unset the changed flag since nothing needs to
-   * be synchronized.
-   */
-  if (!context_changed)
-    ctx->changed = 0;
-}
-
-
 /* This function handles arrival of new mail and reopening of
  * maildir folders.  The basic idea here is we check to see if either
  * the new or cur subdirectories have changed, and if so, we scan them
@@ -2395,6 +2278,119 @@ static int mh_check_mailbox (CONTEXT * ctx, int *index_hint)
   return occult ? MUTT_REOPENED : (have_new ? MUTT_NEW_MAIL : 0);
 }
 
+static int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
+{
+  int i, j;
+#ifdef USE_HCACHE
+  header_cache_t *hc = NULL;
+#endif /* USE_HCACHE */
+  char msgbuf[STRING];
+  progress_t progress;
+
+  if (ctx->magic == MUTT_MH)
+    i = mh_check_mailbox (ctx, index_hint);
+  else
+    i = maildir_check_mailbox (ctx, index_hint);
+
+  if (i != 0)
+    return i;
+
+#ifdef USE_HCACHE
+  if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
+    hc = mutt_hcache_open(HeaderCache, ctx->path, NULL);
+#endif /* USE_HCACHE */
+
+  if (!ctx->quiet)
+  {
+    snprintf (msgbuf, sizeof (msgbuf), _("Writing %s..."), ctx->path);
+    mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, WriteInc, ctx->msgcount);
+  }
+
+  for (i = 0; i < ctx->msgcount; i++)
+  {
+    if (!ctx->quiet)
+      mutt_progress_update (&progress, i, -1);
+
+#ifdef USE_HCACHE
+    if (mh_sync_mailbox_message (ctx, i, hc) == -1)
+      goto err;
+#else
+    if (mh_sync_mailbox_message (ctx, i) == -1)
+      goto err;
+#endif
+  }
+
+#ifdef USE_HCACHE
+  if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
+    mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
+
+  if (ctx->magic == MUTT_MH)
+    mh_update_sequences (ctx);
+
+  /* XXX race condition? */
+
+  maildir_update_mtime (ctx);
+
+  /* adjust indices */
+
+  if (ctx->deleted)
+  {
+    for (i = 0, j = 0; i < ctx->msgcount; i++)
+    {
+      if (!ctx->hdrs[i]->deleted
+         || (ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH)))
+       ctx->hdrs[i]->index = j++;
+    }
+  }
+
+  return 0;
+
+err:
+#ifdef USE_HCACHE
+  if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
+    mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
+  return -1;
+}
+
+void maildir_update_flags (CONTEXT *ctx, HEADER *o, HEADER *n)
+{
+  /* save the global state here so we can reset it at the
+   * end of list block if required.
+   */
+  int context_changed = ctx->changed;
+
+  /* user didn't modify this message.  alter the flags to match the
+   * current state on disk.  This may not actually do
+   * anything. mutt_set_flag() will just ignore the call if the status
+   * bits are already properly set, but it is still faster not to pass
+   * through it */
+  if (o->flagged != n->flagged)
+    mutt_set_flag (ctx, o, MUTT_FLAG, n->flagged);
+  if (o->replied != n->replied)
+    mutt_set_flag (ctx, o, MUTT_REPLIED, n->replied);
+  if (o->read != n->read)
+    mutt_set_flag (ctx, o, MUTT_READ, n->read);
+  if (o->old != n->old)
+    mutt_set_flag (ctx, o, MUTT_OLD, n->old);
+
+  /* mutt_set_flag() will set this, but we don't need to
+   * sync the changes we made because we just updated the
+   * context to match the current on-disk state of the
+   * message.
+   */
+  o->changed = 0;
+
+  /* if the mailbox was not modified before we made these
+   * changes, unset the changed flag since nothing needs to
+   * be synchronized.
+   */
+  if (!context_changed)
+    ctx->changed = 0;
+}
+
+
 
 
 
index 12c8e80e2e0e5b87d3479da71fefd217f243380a..9281c1d67c72890d83b7211dacc45b49045a2bda 100644 (file)
@@ -31,8 +31,6 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 
-static sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account);
-
 static int getnameinfo_err(int ret)
 {
   int err;
@@ -86,23 +84,6 @@ static sasl_callback_t mutt_sasl_callbacks[5];
 
 static sasl_secret_t *secret_ptr = NULL;
 
-static int mutt_sasl_start (void);
-
-/* callbacks */
-static int mutt_sasl_cb_log (void* context, int priority, const char* message);
-static int mutt_sasl_cb_authname (void* context, int id, const char** result,
-  unsigned int* len);
-static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
-  sasl_secret_t** psecret);
-
-/* socket wrappers for a SASL security layer */
-static int mutt_sasl_conn_open (CONNECTION* conn);
-static int mutt_sasl_conn_close (CONNECTION* conn);
-static int mutt_sasl_conn_read (CONNECTION* conn, char* buf, size_t len);
-static int mutt_sasl_conn_write (CONNECTION* conn, const char* buf,
-  size_t count);
-static int mutt_sasl_conn_poll (CONNECTION* conn);
-
 /* utility function, stolen from sasl2 sample code */
 static int iptostring(const struct sockaddr *addr, socklen_t addrlen,
                       char *out, unsigned outlen)
@@ -130,6 +111,14 @@ static int iptostring(const struct sockaddr *addr, socklen_t addrlen,
   return SASL_OK;
 }
 
+/* mutt_sasl_cb_log: callback to log SASL messages */
+static int mutt_sasl_cb_log (void* context, int priority, const char* message)
+{
+  mutt_debug (priority, "SASL: %s\n", message);
+
+  return SASL_OK;
+}
+
 /* mutt_sasl_start: called before doing a SASL exchange - initialises library
  *   (if necessary). */
 static int mutt_sasl_start (void)
@@ -164,6 +153,103 @@ static int mutt_sasl_start (void)
   return SASL_OK;
 }
 
+/* mutt_sasl_cb_authname: callback to retrieve authname or user from ACCOUNT */
+static int mutt_sasl_cb_authname (void* context, int id, const char** result,
+  unsigned* len)
+{
+  ACCOUNT* account = (ACCOUNT*) context;
+
+  if (!result)
+    return SASL_FAIL;
+
+  *result = NULL;
+  if (len)
+    *len = 0;
+
+  if (!account)
+    return SASL_BADPARAM;
+
+  mutt_debug (2, "mutt_sasl_cb_authname: getting %s for %s:%u\n",
+             id == SASL_CB_AUTHNAME ? "authname" : "user",
+             account->host, account->port);
+
+  if (id == SASL_CB_AUTHNAME)
+  {
+    if (mutt_account_getlogin (account))
+      return SASL_FAIL;
+    *result = account->login;
+  }
+  else
+  {
+    if (mutt_account_getuser (account))
+      return SASL_FAIL;
+    *result = account->user;
+  }
+
+  if (len)
+    *len = strlen (*result);
+
+  return SASL_OK;
+}
+
+static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
+  sasl_secret_t** psecret)
+{
+  ACCOUNT* account = (ACCOUNT*) context;
+  int len;
+
+  if (!account || !psecret)
+    return SASL_BADPARAM;
+
+  mutt_debug (2, "mutt_sasl_cb_pass: getting password for %s@%s:%u\n",
+              account->login, account->host, account->port);
+
+  if (mutt_account_getpass (account))
+    return SASL_FAIL;
+
+  len = strlen (account->pass);
+
+  safe_realloc (&secret_ptr, sizeof (sasl_secret_t) + len);
+  memcpy ((char *) secret_ptr->data, account->pass, (size_t) len);
+  secret_ptr->len = len;
+  *psecret = secret_ptr;
+
+  return SASL_OK;
+}
+
+static sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account)
+{
+  sasl_callback_t* callback;
+
+  callback = mutt_sasl_callbacks;
+
+  callback->id = SASL_CB_USER;
+  callback->proc = (int (*)(void))mutt_sasl_cb_authname;
+  callback->context = account;
+  callback++;
+
+  callback->id = SASL_CB_AUTHNAME;
+  callback->proc = (int (*)(void))mutt_sasl_cb_authname;
+  callback->context = account;
+  callback++;
+
+  callback->id = SASL_CB_PASS;
+  callback->proc = (int (*)(void))mutt_sasl_cb_pass;
+  callback->context = account;
+  callback++;
+
+  callback->id = SASL_CB_GETREALM;
+  callback->proc = NULL;
+  callback->context = NULL;
+  callback++;
+
+  callback->id = SASL_CB_LIST_END;
+  callback->proc = NULL;
+  callback->context = NULL;
+
+  return mutt_sasl_callbacks;
+}
+
 /* mutt_sasl_client_new: wrapper for sasl_client_new which also sets various
  * security properties. If this turns out to be fine for POP too we can
  * probably stop exporting mutt_sasl_get_callbacks(). */
@@ -270,39 +356,6 @@ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn)
   return 0;
 }
 
-static sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account)
-{
-  sasl_callback_t* callback;
-
-  callback = mutt_sasl_callbacks;
-
-  callback->id = SASL_CB_USER;
-  callback->proc = (int (*)(void))mutt_sasl_cb_authname;
-  callback->context = account;
-  callback++;
-
-  callback->id = SASL_CB_AUTHNAME;
-  callback->proc = (int (*)(void))mutt_sasl_cb_authname;
-  callback->context = account;
-  callback++;
-
-  callback->id = SASL_CB_PASS;
-  callback->proc = (int (*)(void))mutt_sasl_cb_pass;
-  callback->context = account;
-  callback++;
-
-  callback->id = SASL_CB_GETREALM;
-  callback->proc = NULL;
-  callback->context = NULL;
-  callback++;
-
-  callback->id = SASL_CB_LIST_END;
-  callback->proc = NULL;
-  callback->context = NULL;
-
-  return mutt_sasl_callbacks;
-}
-
 int mutt_sasl_interact (sasl_interact_t* interaction)
 {
   char prompt[SHORT_STRING];
@@ -327,138 +380,6 @@ int mutt_sasl_interact (sasl_interact_t* interaction)
   return SASL_OK;
 }
 
-/* SASL can stack a protection layer on top of an existing connection.
- * To handle this, we store a saslconn_t in conn->sockdata, and write
- * wrappers which en/decode the read/write stream, then replace sockdata
- * with an embedded copy of the old sockdata and call the underlying
- * functions (which we've also preserved). I thought about trying to make
- * a general stackable connection system, but it seemed like overkill -
- * something is wrong if we have 15 filters on top of a socket. Anyway,
- * anything else which wishes to stack can use the same method. The only
- * disadvantage is we have to write wrappers for all the socket methods,
- * even if we only stack over read and write. Thinking about it, the
- * abstraction problem is that there is more in CONNECTION than there
- * needs to be. Ideally it would have only (void*)data and methods. */
-
-/* mutt_sasl_setup_conn: replace connection methods, sockdata with
- *   SASL wrappers, for protection layers. Also get ssf, as a fastpath
- *   for the read/write methods. */
-void mutt_sasl_setup_conn (CONNECTION* conn, sasl_conn_t* saslconn)
-{
-  SASL_DATA* sasldata = safe_malloc (sizeof (SASL_DATA));
-  /* work around sasl_getprop aliasing issues */
-  const void* tmp;
-
-  sasldata->saslconn = saslconn;
-  /* get ssf so we know whether we have to (en|de)code read/write */
-  sasl_getprop (saslconn, SASL_SSF, &tmp);
-  sasldata->ssf = tmp;
-  mutt_debug (3, "SASL protection strength: %u\n", *sasldata->ssf);
-  /* Add SASL SSF to transport SSF */
-  conn->ssf += *sasldata->ssf;
-  sasl_getprop (saslconn, SASL_MAXOUTBUF, &tmp);
-  sasldata->pbufsize = tmp;
-  mutt_debug (3, "SASL protection buffer size: %u\n", *sasldata->pbufsize);
-
-  /* clear input buffer */
-  sasldata->buf = NULL;
-  sasldata->bpos = 0;
-  sasldata->blen = 0;
-
-  /* preserve old functions */
-  sasldata->sockdata = conn->sockdata;
-  sasldata->msasl_open = conn->conn_open;
-  sasldata->msasl_close = conn->conn_close;
-  sasldata->msasl_read = conn->conn_read;
-  sasldata->msasl_write = conn->conn_write;
-  sasldata->msasl_poll = conn->conn_poll;
-
-  /* and set up new functions */
-  conn->sockdata = sasldata;
-  conn->conn_open = mutt_sasl_conn_open;
-  conn->conn_close = mutt_sasl_conn_close;
-  conn->conn_read = mutt_sasl_conn_read;
-  conn->conn_write = mutt_sasl_conn_write;
-  conn->conn_poll = mutt_sasl_conn_poll;
-}
-
-/* mutt_sasl_cb_log: callback to log SASL messages */
-static int mutt_sasl_cb_log (void* context, int priority, const char* message)
-{
-  mutt_debug (priority, "SASL: %s\n", message);
-
-  return SASL_OK;
-}
-
-void mutt_sasl_done (void)
-{
-  sasl_done ();
-}
-
-/* mutt_sasl_cb_authname: callback to retrieve authname or user from ACCOUNT */
-static int mutt_sasl_cb_authname (void* context, int id, const char** result,
-  unsigned* len)
-{
-  ACCOUNT* account = (ACCOUNT*) context;
-
-  if (!result)
-    return SASL_FAIL;
-
-  *result = NULL;
-  if (len)
-    *len = 0;
-
-  if (!account)
-    return SASL_BADPARAM;
-
-  mutt_debug (2, "mutt_sasl_cb_authname: getting %s for %s:%u\n",
-             id == SASL_CB_AUTHNAME ? "authname" : "user",
-             account->host, account->port);
-
-  if (id == SASL_CB_AUTHNAME)
-  {
-    if (mutt_account_getlogin (account))
-      return SASL_FAIL;
-    *result = account->login;
-  }
-  else
-  {
-    if (mutt_account_getuser (account))
-      return SASL_FAIL;
-    *result = account->user;
-  }
-
-  if (len)
-    *len = strlen (*result);
-
-  return SASL_OK;
-}
-
-static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
-  sasl_secret_t** psecret)
-{
-  ACCOUNT* account = (ACCOUNT*) context;
-  int len;
-
-  if (!account || !psecret)
-    return SASL_BADPARAM;
-
-  mutt_debug (2, "mutt_sasl_cb_pass: getting password for %s@%s:%u\n",
-              account->login, account->host, account->port);
-
-  if (mutt_account_getpass (account))
-    return SASL_FAIL;
-
-  len = strlen (account->pass);
-
-  safe_realloc (&secret_ptr, sizeof (sasl_secret_t) + len);
-  memcpy ((char *) secret_ptr->data, account->pass, (size_t) len);
-  secret_ptr->len = len;
-  *psecret = secret_ptr;
-
-  return SASL_OK;
-}
-
 /* mutt_sasl_conn_open: empty wrapper for underlying open function. We
  *   don't know in advance that a connection will use SASL, so we
  *   replace conn's methods with sasl methods when authentication
@@ -628,3 +549,63 @@ static int mutt_sasl_conn_poll (CONNECTION* conn)
 
   return rc;
 }
+/* SASL can stack a protection layer on top of an existing connection.
+ * To handle this, we store a saslconn_t in conn->sockdata, and write
+ * wrappers which en/decode the read/write stream, then replace sockdata
+ * with an embedded copy of the old sockdata and call the underlying
+ * functions (which we've also preserved). I thought about trying to make
+ * a general stackable connection system, but it seemed like overkill -
+ * something is wrong if we have 15 filters on top of a socket. Anyway,
+ * anything else which wishes to stack can use the same method. The only
+ * disadvantage is we have to write wrappers for all the socket methods,
+ * even if we only stack over read and write. Thinking about it, the
+ * abstraction problem is that there is more in CONNECTION than there
+ * needs to be. Ideally it would have only (void*)data and methods. */
+
+/* mutt_sasl_setup_conn: replace connection methods, sockdata with
+ *   SASL wrappers, for protection layers. Also get ssf, as a fastpath
+ *   for the read/write methods. */
+void mutt_sasl_setup_conn (CONNECTION* conn, sasl_conn_t* saslconn)
+{
+  SASL_DATA* sasldata = safe_malloc (sizeof (SASL_DATA));
+  /* work around sasl_getprop aliasing issues */
+  const void* tmp;
+
+  sasldata->saslconn = saslconn;
+  /* get ssf so we know whether we have to (en|de)code read/write */
+  sasl_getprop (saslconn, SASL_SSF, &tmp);
+  sasldata->ssf = tmp;
+  mutt_debug (3, "SASL protection strength: %u\n", *sasldata->ssf);
+  /* Add SASL SSF to transport SSF */
+  conn->ssf += *sasldata->ssf;
+  sasl_getprop (saslconn, SASL_MAXOUTBUF, &tmp);
+  sasldata->pbufsize = tmp;
+  mutt_debug (3, "SASL protection buffer size: %u\n", *sasldata->pbufsize);
+
+  /* clear input buffer */
+  sasldata->buf = NULL;
+  sasldata->bpos = 0;
+  sasldata->blen = 0;
+
+  /* preserve old functions */
+  sasldata->sockdata = conn->sockdata;
+  sasldata->msasl_open = conn->conn_open;
+  sasldata->msasl_close = conn->conn_close;
+  sasldata->msasl_read = conn->conn_read;
+  sasldata->msasl_write = conn->conn_write;
+  sasldata->msasl_poll = conn->conn_poll;
+
+  /* and set up new functions */
+  conn->sockdata = sasldata;
+  conn->conn_open = mutt_sasl_conn_open;
+  conn->conn_close = mutt_sasl_conn_close;
+  conn->conn_read = mutt_sasl_conn_read;
+  conn->conn_write = mutt_sasl_conn_write;
+  conn->conn_poll = mutt_sasl_conn_poll;
+}
+
+void mutt_sasl_done (void)
+{
+  sasl_done ();
+}
+
index 22b561fa6dd974d05523937dd6df8d2a0665be9f..fb12d60db58054af79096dc7d04d98e1a8df20d4 100644 (file)
 /* support for multiple socket connections */
 static CONNECTION *Connections = NULL;
 
-/* forward declarations */
-static int socket_preconnect (void);
-static int socket_connect (int fd, struct sockaddr* sa);
-static CONNECTION* socket_new_conn (void);
+static int socket_preconnect (void)
+{
+  int rc;
+  int save_errno;
+
+  if (mutt_strlen (Preconnect))
+  {
+    mutt_debug (2, "Executing preconnect: %s\n", Preconnect);
+    rc = mutt_system (Preconnect);
+    mutt_debug (2, "Preconnect result: %d\n", rc);
+    if (rc)
+    {
+      save_errno = errno;
+      mutt_perror (_("Preconnect command failed."));
+      mutt_sleep (1);
+
+      return save_errno;
+    }
+  }
+
+  return 0;
+}
 
 /* Wrappers */
 int mutt_socket_open (CONNECTION* conn)
@@ -228,6 +246,17 @@ void mutt_socket_free (CONNECTION* conn)
   }
 }
 
+/* socket_new_conn: allocate and initialise a new connection. */
+static CONNECTION* socket_new_conn (void)
+{
+  CONNECTION* conn;
+
+  conn = safe_calloc (1, sizeof (CONNECTION));
+  conn->fd = -1;
+
+  return conn;
+}
+
 /* mutt_conn_find: find a connection off the list of connections whose
  *   account matches account. If start is not null, only search for
  *   connections after the given connection (allows higher level socket code
@@ -289,79 +318,6 @@ CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account)
   return conn;
 }
 
-static int socket_preconnect (void)
-{
-  int rc;
-  int save_errno;
-
-  if (mutt_strlen (Preconnect))
-  {
-    mutt_debug (2, "Executing preconnect: %s\n", Preconnect);
-    rc = mutt_system (Preconnect);
-    mutt_debug (2, "Preconnect result: %d\n", rc);
-    if (rc)
-    {
-      save_errno = errno;
-      mutt_perror (_("Preconnect command failed."));
-      mutt_sleep (1);
-
-      return save_errno;
-    }
-  }
-
-  return 0;
-}
-
-/* socket_connect: set up to connect to a socket fd. */
-static int socket_connect (int fd, struct sockaddr* sa)
-{
-  int sa_size;
-  int save_errno;
-
-  if (sa->sa_family == AF_INET)
-    sa_size = sizeof (struct sockaddr_in);
-#ifdef HAVE_GETADDRINFO
-  else if (sa->sa_family == AF_INET6)
-    sa_size = sizeof (struct sockaddr_in6);
-#endif
-  else
-  {
-    mutt_debug (1, "Unknown address family!\n");
-    return -1;
-  }
-
-  if (ConnectTimeout > 0)
-      alarm (ConnectTimeout);
-
-  mutt_allow_interrupt (1);
-
-  save_errno = 0;
-
-  if (connect (fd, sa, sa_size) < 0)
-  {
-      save_errno = errno;
-      mutt_debug (2, "Connection failed. errno: %d...\n", errno);
-      SigInt = 0;      /* reset in case we caught SIGINTR while in connect() */
-  }
-
-  if (ConnectTimeout > 0)
-      alarm (0);
-  mutt_allow_interrupt (0);
-
-  return save_errno;
-}
-
-/* socket_new_conn: allocate and initialise a new connection. */
-static CONNECTION* socket_new_conn (void)
-{
-  CONNECTION* conn;
-
-  conn = safe_calloc (1, sizeof (CONNECTION));
-  conn->fd = -1;
-
-  return conn;
-}
-
 int raw_socket_close (CONNECTION *conn)
 {
   return close (conn->fd);
@@ -431,6 +387,45 @@ int raw_socket_poll (CONNECTION* conn)
   return select (conn->fd + 1, &rfds, NULL, NULL, &tv);
 }
 
+/* socket_connect: set up to connect to a socket fd. */
+static int socket_connect (int fd, struct sockaddr* sa)
+{
+  int sa_size;
+  int save_errno;
+
+  if (sa->sa_family == AF_INET)
+    sa_size = sizeof (struct sockaddr_in);
+#ifdef HAVE_GETADDRINFO
+  else if (sa->sa_family == AF_INET6)
+    sa_size = sizeof (struct sockaddr_in6);
+#endif
+  else
+  {
+    mutt_debug (1, "Unknown address family!\n");
+    return -1;
+  }
+
+  if (ConnectTimeout > 0)
+      alarm (ConnectTimeout);
+
+  mutt_allow_interrupt (1);
+
+  save_errno = 0;
+
+  if (connect (fd, sa, sa_size) < 0)
+  {
+      save_errno = errno;
+      mutt_debug (2, "Connection failed. errno: %d...\n", errno);
+      SigInt = 0;      /* reset in case we caught SIGINTR while in connect() */
+  }
+
+  if (ConnectTimeout > 0)
+      alarm (0);
+  mutt_allow_interrupt (0);
+
+  return save_errno;
+}
+
 int raw_socket_open (CONNECTION* conn)
 {
   int rc;
index 862d473c3d6c29753a93eba7d3eeff6732994133..2338c64dd0d202e0696eb77f90dd78c1d23c9aee 100644 (file)
@@ -75,23 +75,6 @@ typedef struct
 }
 sslsockdata;
 
-/* local prototypes */
-static int ssl_init (void);
-static int add_entropy (const char *file);
-static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len);
-static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len);
-static int ssl_socket_open (CONNECTION * conn);
-static int ssl_socket_close (CONNECTION * conn);
-static int tls_close (CONNECTION* conn);
-static void ssl_err (sslsockdata *data, int err);
-static void ssl_dprint_err_stack (void);
-static int ssl_cache_trusted_cert (X509 *cert);
-static int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx);
-static int interactive_check_cert (X509 *cert, int idx, int len, SSL *ssl, int allow_always);
-static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn);
-static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
-static int ssl_negotiate (CONNECTION *conn, sslsockdata*);
-
 /* ssl certificate verification can behave strangely if there are expired
  * certs loaded into the trusted store.  This function filters out expired
  * certs.
@@ -172,172 +155,6 @@ static int ssl_set_verify_partial (SSL_CTX *ctx)
   return rv;
 }
 
-/* mutt_ssl_starttls: Negotiate TLS over an already opened connection.
- *   TODO: Merge this code better with ssl_socket_open. */
-int mutt_ssl_starttls (CONNECTION* conn)
-{
-  sslsockdata* ssldata;
-  int maxbits;
-  long ssl_options = 0;
-
-  if (ssl_init())
-    goto bail;
-
-  ssldata = safe_calloc (1, sizeof (sslsockdata));
-  /* the ssl_use_xxx protocol options don't apply. We must use TLS in TLS.
-   *
-   * However, we need to be able to negotiate amongst various TLS versions,
-   * which at present can only be done with the SSLv23_client_method;
-   * TLSv1_client_method gives us explicitly TLSv1.0, not 1.1 or 1.2 (True as
-   * of OpenSSL 1.0.1c)
-   */
-  if (! (ssldata->ctx = SSL_CTX_new (SSLv23_client_method())))
-  {
-    mutt_debug (1, "mutt_ssl_starttls: Error allocating SSL_CTX\n");
-    goto bail_ssldata;
-  }
-#ifdef SSL_OP_NO_TLSv1_2
-  if (!option(OPTTLSV1_2))
-    ssl_options |= SSL_OP_NO_TLSv1_2;
-#endif
-#ifdef SSL_OP_NO_TLSv1_1
-  if (!option(OPTTLSV1_1))
-    ssl_options |= SSL_OP_NO_TLSv1_1;
-#endif
-#ifdef SSL_OP_NO_TLSv1
-  if (!option(OPTTLSV1))
-    ssl_options |= SSL_OP_NO_TLSv1;
-#endif
-  /* these are always set */
-#ifdef SSL_OP_NO_SSLv3
-  ssl_options |= SSL_OP_NO_SSLv3;
-#endif
-#ifdef SSL_OP_NO_SSLv2
-  ssl_options |= SSL_OP_NO_SSLv2;
-#endif
-  if (! SSL_CTX_set_options(ssldata->ctx, ssl_options))
-  {
-    mutt_debug (1, "mutt_ssl_starttls: Error setting options to %ld\n",
-                ssl_options);
-    goto bail_ctx;
-  }
-
-  if (option (OPTSSLSYSTEMCERTS))
-  {
-    if (! SSL_CTX_set_default_verify_paths (ssldata->ctx))
-    {
-      mutt_debug (1, "mutt_ssl_starttls: Error setting default verify paths\n");
-      goto bail_ctx;
-    }
-  }
-
-  if (SslCertFile && !ssl_load_certificates (ssldata->ctx))
-    mutt_debug (1, "mutt_ssl_starttls: Error loading trusted certificates\n");
-
-  ssl_get_client_cert(ssldata, conn);
-
-  if (SslCiphers) {
-    if (!SSL_CTX_set_cipher_list (ssldata->ctx, SslCiphers)) {
-      mutt_debug (1, "mutt_ssl_starttls: Could not select preferred ciphers\n");
-      goto bail_ctx;
-    }
-  }
-
-  if (ssl_set_verify_partial (ssldata->ctx))
-  {
-    mutt_error (_("Warning: error enabling ssl_verify_partial_chains"));
-    mutt_sleep (2);
-  }
-
-  if (! (ssldata->ssl = SSL_new (ssldata->ctx)))
-  {
-    mutt_debug (1, "mutt_ssl_starttls: Error allocating SSL\n");
-    goto bail_ctx;
-  }
-
-  if (SSL_set_fd (ssldata->ssl, conn->fd) != 1)
-  {
-    mutt_debug (1, "mutt_ssl_starttls: Error setting fd\n");
-    goto bail_ssl;
-  }
-
-  if (ssl_negotiate (conn, ssldata))
-    goto bail_ssl;
-
-  ssldata->isopen = 1;
-
-  /* hmm. watch out if we're starting TLS over any method other than raw. */
-  conn->sockdata = ssldata;
-  conn->conn_read = ssl_socket_read;
-  conn->conn_write = ssl_socket_write;
-  conn->conn_close = tls_close;
-
-  conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (ssldata->ssl),
-    &maxbits);
-
-  return 0;
-
- bail_ssl:
-  FREE (&ssldata->ssl);
- bail_ctx:
-  FREE (&ssldata->ctx);
- bail_ssldata:
-  FREE (&ssldata);
- bail:
-  return -1;
-}
-
-/*
- * OpenSSL library needs to be fed with sufficient entropy. On systems
- * with /dev/urandom, this is done transparently by the library itself,
- * on other systems we need to fill the entropy pool ourselves.
- *
- * Even though only OpenSSL 0.9.5 and later will complain about the
- * lack of entropy, we try to our best and fill the pool with older
- * versions also. (That's the reason for the ugly #ifdefs and macros,
- * otherwise I could have simply #ifdef'd the whole ssl_init funcion)
- */
-static int ssl_init (void)
-{
-  char path[_POSIX_PATH_MAX];
-  static unsigned char init_complete = 0;
-
-  if (init_complete)
-    return 0;
-
-  if (! HAVE_ENTROPY())
-  {
-    /* load entropy from files */
-    add_entropy (SslEntropyFile);
-    add_entropy (RAND_file_name (path, sizeof (path)));
-
-    /* load entropy from egd sockets */
-#ifdef HAVE_RAND_EGD
-    add_entropy (getenv ("EGDSOCKET"));
-    snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir));
-    add_entropy (path);
-    add_entropy ("/tmp/entropy");
-#endif
-
-    /* shuffle $RANDFILE (or ~/.rnd if unset) */
-    RAND_write_file (RAND_file_name (path, sizeof (path)));
-    mutt_clear_error ();
-    if (! HAVE_ENTROPY())
-    {
-      mutt_error (_("Failed to find enough entropy on your system"));
-      mutt_sleep (2);
-      return -1;
-    }
-  }
-
-  /* I don't think you can do this just before reading the error. The call
-   * itself might clobber the last SSL error. */
-  SSL_load_error_strings();
-  SSL_library_init();
-  init_complete = 1;
-  return 0;
-}
-
 static int add_entropy (const char *file)
 {
   struct stat st;
@@ -373,409 +190,377 @@ static int add_entropy (const char *file)
   return n;
 }
 
-static int ssl_socket_open_err (CONNECTION *conn)
-{
-  mutt_error (_("SSL disabled due to the lack of entropy"));
-  mutt_sleep (2);
-  return -1;
-}
-
-
-int mutt_ssl_socket_setup (CONNECTION * conn)
-{
-  if (ssl_init() < 0)
-  {
-    conn->conn_open = ssl_socket_open_err;
-    return -1;
-  }
-
-  conn->conn_open      = ssl_socket_open;
-  conn->conn_read      = ssl_socket_read;
-  conn->conn_write     = ssl_socket_write;
-  conn->conn_close     = ssl_socket_close;
-  conn->conn_poll       = raw_socket_poll;
-
-  return 0;
-}
-
-static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len)
+static void ssl_err (sslsockdata *data, int err)
 {
-  sslsockdata *data = conn->sockdata;
-  int rc;
+  const char* errmsg;
+  unsigned long sslerr;
 
-  rc = SSL_read (data->ssl, buf, len);
-  if (rc <= 0 || errno == EINTR)
+  switch (SSL_get_error (data->ssl, err))
   {
-    if (errno == EINTR)
+  case SSL_ERROR_NONE:
+    return;
+  case SSL_ERROR_ZERO_RETURN:
+    errmsg = "SSL connection closed";
+    data->isopen = 0;
+    break;
+  case SSL_ERROR_WANT_READ:
+    errmsg = "retry read";
+    break;
+  case SSL_ERROR_WANT_WRITE:
+    errmsg = "retry write";
+    break;
+  case SSL_ERROR_WANT_CONNECT:
+    errmsg = "retry connect";
+    break;
+  case SSL_ERROR_WANT_ACCEPT:
+    errmsg = "retry accept";
+    break;
+  case SSL_ERROR_WANT_X509_LOOKUP:
+    errmsg = "retry x509 lookup";
+    break;
+  case SSL_ERROR_SYSCALL:
+    errmsg = "I/O error";
+    data->isopen = 0;
+    break;
+  case SSL_ERROR_SSL:
+    sslerr = ERR_get_error ();
+    switch (sslerr)
     {
-      rc = -1;
+    case 0:
+      switch (err)
+      {
+      case 0:
+       errmsg = "EOF";
+       break;
+      default:
+       errmsg = strerror(errno);
+      }
+      break;
+    default:
+      errmsg = ERR_error_string (sslerr, NULL);
     }
-    data->isopen = 0;
-    ssl_err (data, rc);
+    break;
+  default:
+    errmsg = "unknown error";
   }
 
-  return rc;
+  mutt_debug (1, "SSL error: %s\n", errmsg);
 }
 
-static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len)
+static void ssl_dprint_err_stack (void)
 {
-  sslsockdata *data = conn->sockdata;
-  int rc;
-
-  rc = SSL_write (data->ssl, buf, len);
-  if (rc <= 0 || errno == EINTR) {
-    if (errno == EINTR)
-    {
-      rc = -1;
-    }
-    ssl_err (data, rc);
-  }
+#ifdef DEBUG
+  BIO *bio;
+  char *buf = NULL;
+  long buflen;
+  char *output;
 
-  return rc;
+  if (! (bio = BIO_new (BIO_s_mem ())))
+    return;
+  ERR_print_errors (bio);
+  if ((buflen = BIO_get_mem_data (bio, &buf)) > 0)
+  {
+    output = safe_malloc (buflen + 1);
+    memcpy (output, buf, buflen);
+    output[buflen] = '\0';
+    mutt_debug (1, "SSL error stack: %s\n", output);
+    FREE (&output);
+  }
+  BIO_free (bio);
+#endif
 }
 
-static int ssl_socket_open (CONNECTION * conn)
+static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
 {
-  sslsockdata *data;
-  int maxbits;
+  ACCOUNT *account = (ACCOUNT*)userdata;
 
-  if (raw_socket_open (conn) < 0)
-    return -1;
+  if (mutt_account_getuser (account))
+    return 0;
 
-  data = safe_calloc (1, sizeof (sslsockdata));
-  conn->sockdata = data;
+  mutt_debug (2, "ssl_passwd_cb: getting password for %s@%s:%u\n",
+              account->user, account->host, account->port);
 
-  if (! (data->ctx = SSL_CTX_new (SSLv23_client_method ())))
-  {
-    /* L10N: an SSL context is a data structure returned by the OpenSSL
-             function SSL_CTX_new().  In this case it returned NULL: an
-             error condition.  */
-    mutt_error (_("Unable to create SSL context"));
-    ssl_dprint_err_stack ();
-    mutt_socket_close (conn);
-    return -1;
-  }
+  if (mutt_account_getpass (account))
+    return 0;
 
-  /* disable SSL protocols as needed */
-  if (!option(OPTTLSV1))
-  {
-    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1);
-  }
-  /* TLSv1.1/1.2 support was added in OpenSSL 1.0.1, but some OS distros such
-   * as Fedora 17 are on OpenSSL 1.0.0.
-   */
-#ifdef SSL_OP_NO_TLSv1_1
-  if (!option(OPTTLSV1_1))
-  {
-    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_1);
-  }
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
-  if (!option(OPTTLSV1_2))
-  {
-    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_2);
-  }
-#endif
-  if (!option(OPTSSLV2))
+  return snprintf(buf, size, "%s", account->pass);
+}
+
+static int ssl_socket_open_err (CONNECTION *conn)
+{
+  mutt_error (_("SSL disabled due to the lack of entropy"));
+  mutt_sleep (2);
+  return -1;
+}
+
+static int ssl_socket_close (CONNECTION * conn)
+{
+  sslsockdata *data = conn->sockdata;
+  if (data)
   {
-    SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2);
+    if (data->isopen)
+      SSL_shutdown (data->ssl);
+
+    /* hold onto this for the life of mutt, in case we want to reconnect.
+     * The purist in me wants a mutt_exit hook. */
+    SSL_free (data->ssl);
+    SSL_CTX_free (data->ctx);
+    FREE (&conn->sockdata);
   }
-  if (!option(OPTSSLV3))
+
+  return raw_socket_close (conn);
+}
+
+static char *x509_get_part (X509_NAME *name, int nid)
+{
+  static char ret[SHORT_STRING];
+
+  if (!name ||
+      X509_NAME_get_text_by_NID (name, nid, ret, sizeof (ret)) < 0)
+    strfcpy (ret, _("Unknown"), sizeof (ret));
+
+  return ret;
+}
+
+static void x509_fingerprint (char *s, int l, X509 * cert, const EVP_MD *(*hashfunc)(void))
+{
+  unsigned char md[EVP_MAX_MD_SIZE];
+  unsigned int n;
+  int j;
+
+  if (!X509_digest (cert, hashfunc(), md, &n))
   {
-    SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3);
+    snprintf (s, l, _("[unable to calculate]"));
   }
-
-  if (option (OPTSSLSYSTEMCERTS))
+  else
   {
-    if (! SSL_CTX_set_default_verify_paths (data->ctx))
+    for (j = 0; j < (int) n; j++)
     {
-      mutt_debug (1, "ssl_socket_open: Error setting default verify paths\n");
-      mutt_socket_close (conn);
-      return -1;
+      char ch[8];
+      snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
+      safe_strcat (s, l, ch);
     }
   }
+}
 
-  if (SslCertFile && !ssl_load_certificates (data->ctx))
-    mutt_debug (1, "ssl_socket_open: Error loading trusted certificates\n");
-
-  ssl_get_client_cert(data, conn);
+static char *asn1time_to_string (ASN1_UTCTIME *tm)
+{
+  static char buf[64];
+  BIO *bio;
 
-  if (SslCiphers) {
-    SSL_CTX_set_cipher_list (data->ctx, SslCiphers);
-  }
+  strfcpy (buf, _("[invalid date]"), sizeof (buf));
 
-  if (ssl_set_verify_partial (data->ctx))
+  bio = BIO_new (BIO_s_mem());
+  if (bio)
   {
-    mutt_error (_("Warning: error enabling ssl_verify_partial_chains"));
-    mutt_sleep (2);
+    if (ASN1_TIME_print (bio, tm))
+      (void) BIO_read (bio, buf, sizeof (buf));
+    BIO_free (bio);
   }
 
-  data->ssl = SSL_new (data->ctx);
-  SSL_set_fd (data->ssl, conn->fd);
+  return buf;
+}
 
-  if (ssl_negotiate(conn, data))
-  {
-    mutt_socket_close (conn);
+static int compare_certificates (X509 *cert, X509 *peercert,
+  unsigned char *peermd, unsigned int peermdlen)
+{
+  unsigned char md[EVP_MAX_MD_SIZE];
+  unsigned int mdlen;
+
+  /* Avoid CPU-intensive digest calculation if the certificates are
+    * not even remotely equal.
+    */
+  if (X509_subject_name_cmp (cert, peercert) != 0 ||
+      X509_issuer_name_cmp (cert, peercert) != 0)
     return -1;
-  }
 
-  data->isopen = 1;
+  if (!X509_digest (cert, EVP_sha256(), md, &mdlen) || peermdlen != mdlen)
+    return -1;
 
-  conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (data->ssl),
-    &maxbits);
+  if (memcmp(peermd, md, mdlen) != 0)
+    return -1;
 
   return 0;
 }
 
-/* ssl_negotiate: After SSL state has been initialized, attempt to negotiate
- *   SSL over the wire, including certificate checks. */
-static int ssl_negotiate (CONNECTION *conn, sslsockdata* ssldata)
+static int check_certificate_expiration (X509 *peercert, int silent)
 {
-  int err;
-  const char* errmsg;
-
-  if ((HostExDataIndex = SSL_get_ex_new_index (0, "host", NULL, NULL, NULL)) == -1)
+  if (option (OPTSSLVERIFYDATES) != MUTT_NO)
   {
-    mutt_debug (1, "failed to get index for application specific data\n");
-    return -1;
+    if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0)
+    {
+      if (!silent)
+      {
+        mutt_debug (2, "Server certificate is not yet valid\n");
+        mutt_error (_("Server certificate is not yet valid"));
+        mutt_sleep (2);
+      }
+      return 0;
+    }
+    if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0)
+    {
+      if (!silent)
+      {
+        mutt_debug (2, "Server certificate has expired\n");
+        mutt_error (_("Server certificate has expired"));
+        mutt_sleep (2);
+      }
+      return 0;
+    }
   }
 
-  if (! SSL_set_ex_data (ssldata->ssl, HostExDataIndex, conn->account.host))
+  return 1;
+}
+
+/* port to mutt from msmtp's tls.c */
+static int hostname_match (const char *hostname, const char *certname)
+{
+  const char *cmp1, *cmp2;
+
+  if (strncmp(certname, "*.", 2) == 0)
   {
-    mutt_debug (1, "failed to save hostname in SSL structure\n");
-    return -1;
+    cmp1 = certname + 2;
+    cmp2 = strchr(hostname, '.');
+    if (!cmp2)
+    {
+      return 0;
+    }
+    else
+    {
+      cmp2++;
+    }
   }
-
-  if ((SkipModeExDataIndex = SSL_get_ex_new_index (0, "skip", NULL, NULL, NULL)) == -1)
+  else
   {
-    mutt_debug (1, "failed to get index for application specific data\n");
-    return -1;
+    cmp1 = certname;
+    cmp2 = hostname;
   }
 
-  if (! SSL_set_ex_data (ssldata->ssl, SkipModeExDataIndex, NULL))
+  if (*cmp1 == '\0' || *cmp2 == '\0')
   {
-    mutt_debug (1, "failed to save skip mode in SSL structure\n");
-    return -1;
+    return 0;
   }
 
-  SSL_set_verify (ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback);
-  SSL_set_mode (ssldata->ssl, SSL_MODE_AUTO_RETRY);
-
-  if (!SSL_set_tlsext_host_name (ssldata->ssl, conn->account.host))
+  if (strcasecmp(cmp1, cmp2) != 0)
   {
-    /* L10N: This is a warning when trying to set the host name for
-     * TLS Server Name Indication (SNI).  This allows the server to present
-     * the correct certificate if it supports multiple hosts. */
-    mutt_error(_("Warning: unable to set TLS SNI host name"));
-    mutt_sleep (1);
+    return 0;
   }
 
-  ERR_clear_error ();
-
-  if ((err = SSL_connect (ssldata->ssl)) != 1)
-  {
-    switch (SSL_get_error (ssldata->ssl, err))
-    {
-    case SSL_ERROR_SYSCALL:
-      errmsg = _("I/O error");
-      break;
-    case SSL_ERROR_SSL:
-      errmsg = ERR_error_string (ERR_get_error (), NULL);
-      break;
-    default:
-      errmsg = _("unknown error");
-    }
-
-    mutt_error (_("SSL failed: %s"), errmsg);
-    mutt_sleep (1);
-
-    return -1;
-  }
-
-  return 0;
-}
-
-static int ssl_socket_close (CONNECTION * conn)
-{
-  sslsockdata *data = conn->sockdata;
-  if (data)
-  {
-    if (data->isopen)
-      SSL_shutdown (data->ssl);
-
-    /* hold onto this for the life of mutt, in case we want to reconnect.
-     * The purist in me wants a mutt_exit hook. */
-    SSL_free (data->ssl);
-    SSL_CTX_free (data->ctx);
-    FREE (&conn->sockdata);
-  }
-
-  return raw_socket_close (conn);
+  return 1;
 }
 
-static int tls_close (CONNECTION* conn)
+/*
+ * OpenSSL library needs to be fed with sufficient entropy. On systems
+ * with /dev/urandom, this is done transparently by the library itself,
+ * on other systems we need to fill the entropy pool ourselves.
+ *
+ * Even though only OpenSSL 0.9.5 and later will complain about the
+ * lack of entropy, we try to our best and fill the pool with older
+ * versions also. (That's the reason for the ugly #ifdefs and macros,
+ * otherwise I could have simply #ifdef'd the whole ssl_init funcion)
+ */
+static int ssl_init (void)
 {
-  int rc;
+  char path[_POSIX_PATH_MAX];
+  static unsigned char init_complete = 0;
 
-  rc = ssl_socket_close (conn);
-  conn->conn_read = raw_socket_read;
-  conn->conn_write = raw_socket_write;
-  conn->conn_close = raw_socket_close;
+  if (init_complete)
+    return 0;
 
-  return rc;
-}
+  if (! HAVE_ENTROPY())
+  {
+    /* load entropy from files */
+    add_entropy (SslEntropyFile);
+    add_entropy (RAND_file_name (path, sizeof (path)));
 
-static void ssl_err (sslsockdata *data, int err)
-{
-  const char* errmsg;
-  unsigned long sslerr;
+    /* load entropy from egd sockets */
+#ifdef HAVE_RAND_EGD
+    add_entropy (getenv ("EGDSOCKET"));
+    snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir));
+    add_entropy (path);
+    add_entropy ("/tmp/entropy");
+#endif
 
-  switch (SSL_get_error (data->ssl, err))
-  {
-  case SSL_ERROR_NONE:
-    return;
-  case SSL_ERROR_ZERO_RETURN:
-    errmsg = "SSL connection closed";
-    data->isopen = 0;
-    break;
-  case SSL_ERROR_WANT_READ:
-    errmsg = "retry read";
-    break;
-  case SSL_ERROR_WANT_WRITE:
-    errmsg = "retry write";
-    break;
-  case SSL_ERROR_WANT_CONNECT:
-    errmsg = "retry connect";
-    break;
-  case SSL_ERROR_WANT_ACCEPT:
-    errmsg = "retry accept";
-    break;
-  case SSL_ERROR_WANT_X509_LOOKUP:
-    errmsg = "retry x509 lookup";
-    break;
-  case SSL_ERROR_SYSCALL:
-    errmsg = "I/O error";
-    data->isopen = 0;
-    break;
-  case SSL_ERROR_SSL:
-    sslerr = ERR_get_error ();
-    switch (sslerr)
+    /* shuffle $RANDFILE (or ~/.rnd if unset) */
+    RAND_write_file (RAND_file_name (path, sizeof (path)));
+    mutt_clear_error ();
+    if (! HAVE_ENTROPY())
     {
-    case 0:
-      switch (err)
-      {
-      case 0:
-       errmsg = "EOF";
-       break;
-      default:
-       errmsg = strerror(errno);
-      }
-      break;
-    default:
-      errmsg = ERR_error_string (sslerr, NULL);
+      mutt_error (_("Failed to find enough entropy on your system"));
+      mutt_sleep (2);
+      return -1;
     }
-    break;
-  default:
-    errmsg = "unknown error";
   }
 
-  mutt_debug (1, "SSL error: %s\n", errmsg);
+  /* I don't think you can do this just before reading the error. The call
+   * itself might clobber the last SSL error. */
+  SSL_load_error_strings();
+  SSL_library_init();
+  init_complete = 1;
+  return 0;
 }
 
-static void ssl_dprint_err_stack (void)
+static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len)
 {
-#ifdef DEBUG
-  BIO *bio;
-  char *buf = NULL;
-  long buflen;
-  char *output;
+  sslsockdata *data = conn->sockdata;
+  int rc;
 
-  if (! (bio = BIO_new (BIO_s_mem ())))
-    return;
-  ERR_print_errors (bio);
-  if ((buflen = BIO_get_mem_data (bio, &buf)) > 0)
+  rc = SSL_read (data->ssl, buf, len);
+  if (rc <= 0 || errno == EINTR)
   {
-    output = safe_malloc (buflen + 1);
-    memcpy (output, buf, buflen);
-    output[buflen] = '\0';
-    mutt_debug (1, "SSL error stack: %s\n", output);
-    FREE (&output);
+    if (errno == EINTR)
+    {
+      rc = -1;
+    }
+    data->isopen = 0;
+    ssl_err (data, rc);
   }
-  BIO_free (bio);
-#endif
-}
-
-
-static char *x509_get_part (X509_NAME *name, int nid)
-{
-  static char ret[SHORT_STRING];
-
-  if (!name ||
-      X509_NAME_get_text_by_NID (name, nid, ret, sizeof (ret)) < 0)
-    strfcpy (ret, _("Unknown"), sizeof (ret));
 
-  return ret;
+  return rc;
 }
 
-static void x509_fingerprint (char *s, int l, X509 * cert, const EVP_MD *(*hashfunc)(void))
+static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len)
 {
-  unsigned char md[EVP_MAX_MD_SIZE];
-  unsigned int n;
-  int j;
+  sslsockdata *data = conn->sockdata;
+  int rc;
 
-  if (!X509_digest (cert, hashfunc(), md, &n))
-  {
-    snprintf (s, l, _("[unable to calculate]"));
-  }
-  else
-  {
-    for (j = 0; j < (int) n; j++)
+  rc = SSL_write (data->ssl, buf, len);
+  if (rc <= 0 || errno == EINTR) {
+    if (errno == EINTR)
     {
-      char ch[8];
-      snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
-      safe_strcat (s, l, ch);
+      rc = -1;
     }
+    ssl_err (data, rc);
   }
+
+  return rc;
 }
 
-static char *asn1time_to_string (ASN1_UTCTIME *tm)
+static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn)
 {
-  static char buf[64];
-  BIO *bio;
-
-  strfcpy (buf, _("[invalid date]"), sizeof (buf));
-
-  bio = BIO_new (BIO_s_mem());
-  if (bio)
+  if (SslClientCert)
   {
-    if (ASN1_TIME_print (bio, tm))
-      (void) BIO_read (bio, buf, sizeof (buf));
-    BIO_free (bio);
-  }
+    mutt_debug (2, "Using client certificate %s\n", SslClientCert);
+    SSL_CTX_set_default_passwd_cb_userdata(ssldata->ctx, &conn->account);
+    SSL_CTX_set_default_passwd_cb(ssldata->ctx, ssl_passwd_cb);
+    SSL_CTX_use_certificate_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
+    SSL_CTX_use_PrivateKey_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
 
-  return buf;
+    /* if we are using a client cert, SASL may expect an external auth name */
+    mutt_account_getuser (&conn->account);
+  }
 }
 
-static int compare_certificates (X509 *cert, X509 *peercert,
-  unsigned char *peermd, unsigned int peermdlen)
+static int tls_close (CONNECTION* conn)
 {
-  unsigned char md[EVP_MAX_MD_SIZE];
-  unsigned int mdlen;
-
-  /* Avoid CPU-intensive digest calculation if the certificates are
-    * not even remotely equal.
-    */
-  if (X509_subject_name_cmp (cert, peercert) != 0 ||
-      X509_issuer_name_cmp (cert, peercert) != 0)
-    return -1;
-
-  if (!X509_digest (cert, EVP_sha256(), md, &mdlen) || peermdlen != mdlen)
-    return -1;
+  int rc;
 
-  if (memcmp(peermd, md, mdlen) != 0)
-    return -1;
+  rc = ssl_socket_close (conn);
+  conn->conn_read = raw_socket_read;
+  conn->conn_write = raw_socket_write;
+  conn->conn_close = raw_socket_close;
 
-  return 0;
+  return rc;
 }
 
 static int check_certificate_cache (X509 *peercert)
@@ -803,36 +588,7 @@ static int check_certificate_cache (X509 *peercert)
   return 0;
 }
 
-static int check_certificate_expiration (X509 *peercert, int silent)
-{
-  if (option (OPTSSLVERIFYDATES) != MUTT_NO)
-  {
-    if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0)
-    {
-      if (!silent)
-      {
-        mutt_debug (2, "Server certificate is not yet valid\n");
-        mutt_error (_("Server certificate is not yet valid"));
-        mutt_sleep (2);
-      }
-      return 0;
-    }
-    if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0)
-    {
-      if (!silent)
-      {
-        mutt_debug (2, "Server certificate has expired\n");
-        mutt_error (_("Server certificate has expired"));
-        mutt_sleep (2);
-      }
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
-static int check_certificate_file (X509 *peercert)
+static int check_certificate_file (X509 *peercert)
 {
   unsigned char peermd[EVP_MAX_MD_SIZE];
   unsigned int peermdlen;
@@ -870,49 +626,6 @@ static int check_certificate_file (X509 *peercert)
   return pass;
 }
 
-static int check_certificate_by_digest (X509 *peercert)
-{
-  return check_certificate_expiration (peercert, 0) &&
-    check_certificate_file (peercert);
-}
-
-/* port to mutt from msmtp's tls.c */
-static int hostname_match (const char *hostname, const char *certname)
-{
-  const char *cmp1, *cmp2;
-
-  if (strncmp(certname, "*.", 2) == 0)
-  {
-    cmp1 = certname + 2;
-    cmp2 = strchr(hostname, '.');
-    if (!cmp2)
-    {
-      return 0;
-    }
-    else
-    {
-      cmp2++;
-    }
-  }
-  else
-  {
-    cmp1 = certname;
-    cmp2 = hostname;
-  }
-
-  if (*cmp1 == '\0' || *cmp2 == '\0')
-  {
-    return 0;
-  }
-
-  if (strcasecmp(cmp1, cmp2) != 0)
-  {
-    return 0;
-  }
-
-  return 1;
-}
-
 /* port to mutt from msmtp's tls.c */
 static int check_host (X509 *x509cert, const char *hostname, char *err, size_t errlen)
 {
@@ -1018,6 +731,12 @@ out:
   return rc;
 }
 
+static int check_certificate_by_digest (X509 *peercert)
+{
+  return check_certificate_expiration (peercert, 0) &&
+    check_certificate_file (peercert);
+}
+
 static int ssl_cache_trusted_cert (X509 *c)
 {
   mutt_debug (1, "ssl_cache_trusted_cert: trusted\n");
@@ -1026,125 +745,6 @@ static int ssl_cache_trusted_cert (X509 *c)
   return (sk_X509_push (SslSessionCerts, X509_dup(c)));
 }
 
-/* certificate verification callback, called for each certificate in the chain
- * sent by the peer, starting from the root; returning 1 means that the given
- * certificate is trusted, returning 0 immediately aborts the SSL connection */
-static int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx)
-{
-  char buf[STRING];
-  const char *host;
-  int len, pos;
-  X509 *cert;
-  SSL *ssl;
-  int skip_mode;
-#ifdef HAVE_SSL_PARTIAL_CHAIN
-  static int last_pos = 0;
-  static X509 *last_cert = NULL;
-  unsigned char last_cert_md[EVP_MAX_MD_SIZE];
-  unsigned int last_cert_mdlen;
-#endif
-
-  if (! (ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ())))
-  {
-    mutt_debug (1, "ssl_verify_callback: failed to retrieve SSL structure from X509_STORE_CTX\n");
-    return 0;
-  }
-  if (! (host = SSL_get_ex_data (ssl, HostExDataIndex)))
-  {
-    mutt_debug (1, "ssl_verify_callback: failed to retrieve hostname from SSL structure\n");
-    return 0;
-  }
-
-  /* This is true when a previous entry in the certificate chain did
-   * not verify and the user manually chose to skip it via the
-   * $ssl_verify_partial_chains option.
-   * In this case, all following certificates need to be treated as non-verified
-   * until one is actually verified.
-   */
-  skip_mode = (SSL_get_ex_data (ssl, SkipModeExDataIndex) != NULL);
-
-  cert = X509_STORE_CTX_get_current_cert (ctx);
-  pos = X509_STORE_CTX_get_error_depth (ctx);
-  len = sk_X509_num (X509_STORE_CTX_get_chain (ctx));
-
-  mutt_debug (1, "ssl_verify_callback: checking cert chain entry %s (preverify: %d skipmode: %d)\n",
-              X509_NAME_oneline (X509_get_subject_name (cert), buf, sizeof (buf)),
-              preverify_ok, skip_mode);
-
-#ifdef HAVE_SSL_PARTIAL_CHAIN
-  /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it
-   * a second time with preverify_ok = 1.  Don't show it or the user
-   * will think their "s" key is broken.
-   */
-  if (option (OPTSSLVERIFYPARTIAL))
-  {
-    if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
-    {
-      if (X509_digest (last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
-          !compare_certificates (cert, last_cert, last_cert_md, last_cert_mdlen))
-      {
-        mutt_debug (2, "ssl_verify_callback: ignoring duplicate skipped certificate.\n");
-        return 1;
-      }
-    }
-
-    last_pos = pos;
-    if (last_cert)
-      X509_free (last_cert);
-    last_cert = X509_dup (cert);
-  }
-#endif
-
-  /* check session cache first */
-  if (check_certificate_cache (cert))
-  {
-    mutt_debug (2, "ssl_verify_callback: using cached certificate\n");
-    SSL_set_ex_data (ssl, SkipModeExDataIndex, NULL);
-    return 1;
-  }
-
-  /* check hostname only for the leaf certificate */
-  buf[0] = 0;
-  if (pos == 0 && option (OPTSSLVERIFYHOST) != MUTT_NO)
-  {
-    if (!check_host (cert, host, buf, sizeof (buf)))
-    {
-      mutt_error (_("Certificate host check failed: %s"), buf);
-      mutt_sleep (2);
-      /* we disallow (a)ccept always in the prompt, because it will have no effect
-       * for hostname mismatches. */
-      return interactive_check_cert (cert, pos, len, ssl, 0);
-    }
-    mutt_debug (2, "ssl_verify_callback: hostname check passed\n");
-  }
-
-  if (!preverify_ok || skip_mode)
-  {
-    /* automatic check from user's database */
-    if (SslCertFile && check_certificate_by_digest (cert))
-    {
-      mutt_debug (2, "ssl_verify_callback: digest check passed\n");
-      SSL_set_ex_data (ssl, SkipModeExDataIndex, NULL);
-      return 1;
-    }
-
-#ifdef DEBUG
-    /* log verification error */
-    {
-      int err = X509_STORE_CTX_get_error (ctx);
-      snprintf (buf, sizeof (buf), "%s (%d)",
-         X509_verify_cert_error_string (err), err);
-      mutt_debug (2, "X509_verify_cert: %s\n", buf);
-    }
-#endif
-
-   /* prompt user */
-    return interactive_check_cert (cert, pos, len, ssl, 1);
-  }
-
-  return 1;
-}
-
 static int interactive_check_cert (X509 *cert, int idx, int len, SSL *ssl, int allow_always)
 {
   static const int part[] =
@@ -1304,33 +904,415 @@ static int interactive_check_cert (X509 *cert, int idx, int len, SSL *ssl, int a
   return (done == 2);
 }
 
-static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn)
+/* certificate verification callback, called for each certificate in the chain
+ * sent by the peer, starting from the root; returning 1 means that the given
+ * certificate is trusted, returning 0 immediately aborts the SSL connection */
+static int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx)
 {
-  if (SslClientCert)
-  {
-    mutt_debug (2, "Using client certificate %s\n", SslClientCert);
-    SSL_CTX_set_default_passwd_cb_userdata(ssldata->ctx, &conn->account);
-    SSL_CTX_set_default_passwd_cb(ssldata->ctx, ssl_passwd_cb);
-    SSL_CTX_use_certificate_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
-    SSL_CTX_use_PrivateKey_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
+  char buf[STRING];
+  const char *host;
+  int len, pos;
+  X509 *cert;
+  SSL *ssl;
+  int skip_mode;
+#ifdef HAVE_SSL_PARTIAL_CHAIN
+  static int last_pos = 0;
+  static X509 *last_cert = NULL;
+  unsigned char last_cert_md[EVP_MAX_MD_SIZE];
+  unsigned int last_cert_mdlen;
+#endif
 
-    /* if we are using a client cert, SASL may expect an external auth name */
-    mutt_account_getuser (&conn->account);
+  if (! (ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ())))
+  {
+    mutt_debug (1, "ssl_verify_callback: failed to retrieve SSL structure from X509_STORE_CTX\n");
+    return 0;
   }
-}
-
-static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
-{
-  ACCOUNT *account = (ACCOUNT*)userdata;
-
-  if (mutt_account_getuser (account))
+  if (! (host = SSL_get_ex_data (ssl, HostExDataIndex)))
+  {
+    mutt_debug (1, "ssl_verify_callback: failed to retrieve hostname from SSL structure\n");
     return 0;
+  }
 
-  mutt_debug (2, "ssl_passwd_cb: getting password for %s@%s:%u\n",
-              account->user, account->host, account->port);
+  /* This is true when a previous entry in the certificate chain did
+   * not verify and the user manually chose to skip it via the
+   * $ssl_verify_partial_chains option.
+   * In this case, all following certificates need to be treated as non-verified
+   * until one is actually verified.
+   */
+  skip_mode = (SSL_get_ex_data (ssl, SkipModeExDataIndex) != NULL);
 
-  if (mutt_account_getpass (account))
-    return 0;
+  cert = X509_STORE_CTX_get_current_cert (ctx);
+  pos = X509_STORE_CTX_get_error_depth (ctx);
+  len = sk_X509_num (X509_STORE_CTX_get_chain (ctx));
+
+  mutt_debug (1, "ssl_verify_callback: checking cert chain entry %s (preverify: %d skipmode: %d)\n",
+              X509_NAME_oneline (X509_get_subject_name (cert), buf, sizeof (buf)),
+              preverify_ok, skip_mode);
+
+#ifdef HAVE_SSL_PARTIAL_CHAIN
+  /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it
+   * a second time with preverify_ok = 1.  Don't show it or the user
+   * will think their "s" key is broken.
+   */
+  if (option (OPTSSLVERIFYPARTIAL))
+  {
+    if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
+    {
+      if (X509_digest (last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
+          !compare_certificates (cert, last_cert, last_cert_md, last_cert_mdlen))
+      {
+        mutt_debug (2, "ssl_verify_callback: ignoring duplicate skipped certificate.\n");
+        return 1;
+      }
+    }
+
+    last_pos = pos;
+    if (last_cert)
+      X509_free (last_cert);
+    last_cert = X509_dup (cert);
+  }
+#endif
+
+  /* check session cache first */
+  if (check_certificate_cache (cert))
+  {
+    mutt_debug (2, "ssl_verify_callback: using cached certificate\n");
+    SSL_set_ex_data (ssl, SkipModeExDataIndex, NULL);
+    return 1;
+  }
+
+  /* check hostname only for the leaf certificate */
+  buf[0] = 0;
+  if (pos == 0 && option (OPTSSLVERIFYHOST) != MUTT_NO)
+  {
+    if (!check_host (cert, host, buf, sizeof (buf)))
+    {
+      mutt_error (_("Certificate host check failed: %s"), buf);
+      mutt_sleep (2);
+      /* we disallow (a)ccept always in the prompt, because it will have no effect
+       * for hostname mismatches. */
+      return interactive_check_cert (cert, pos, len, ssl, 0);
+    }
+    mutt_debug (2, "ssl_verify_callback: hostname check passed\n");
+  }
+
+  if (!preverify_ok || skip_mode)
+  {
+    /* automatic check from user's database */
+    if (SslCertFile && check_certificate_by_digest (cert))
+    {
+      mutt_debug (2, "ssl_verify_callback: digest check passed\n");
+      SSL_set_ex_data (ssl, SkipModeExDataIndex, NULL);
+      return 1;
+    }
+
+#ifdef DEBUG
+    /* log verification error */
+    {
+      int err = X509_STORE_CTX_get_error (ctx);
+      snprintf (buf, sizeof (buf), "%s (%d)",
+         X509_verify_cert_error_string (err), err);
+      mutt_debug (2, "X509_verify_cert: %s\n", buf);
+    }
+#endif
+
+   /* prompt user */
+    return interactive_check_cert (cert, pos, len, ssl, 1);
+  }
+
+  return 1;
+}
+
+/* ssl_negotiate: After SSL state has been initialized, attempt to negotiate
+ *   SSL over the wire, including certificate checks. */
+static int ssl_negotiate (CONNECTION *conn, sslsockdata* ssldata)
+{
+  int err;
+  const char* errmsg;
+
+  if ((HostExDataIndex = SSL_get_ex_new_index (0, "host", NULL, NULL, NULL)) == -1)
+  {
+    mutt_debug (1, "failed to get index for application specific data\n");
+    return -1;
+  }
+
+  if (! SSL_set_ex_data (ssldata->ssl, HostExDataIndex, conn->account.host))
+  {
+    mutt_debug (1, "failed to save hostname in SSL structure\n");
+    return -1;
+  }
+
+  if ((SkipModeExDataIndex = SSL_get_ex_new_index (0, "skip", NULL, NULL, NULL)) == -1)
+  {
+    mutt_debug (1, "failed to get index for application specific data\n");
+    return -1;
+  }
+
+  if (! SSL_set_ex_data (ssldata->ssl, SkipModeExDataIndex, NULL))
+  {
+    mutt_debug (1, "failed to save skip mode in SSL structure\n");
+    return -1;
+  }
+
+  SSL_set_verify (ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback);
+  SSL_set_mode (ssldata->ssl, SSL_MODE_AUTO_RETRY);
+
+  if (!SSL_set_tlsext_host_name (ssldata->ssl, conn->account.host))
+  {
+    /* L10N: This is a warning when trying to set the host name for
+     * TLS Server Name Indication (SNI).  This allows the server to present
+     * the correct certificate if it supports multiple hosts. */
+    mutt_error(_("Warning: unable to set TLS SNI host name"));
+    mutt_sleep (1);
+  }
+
+  ERR_clear_error ();
+
+  if ((err = SSL_connect (ssldata->ssl)) != 1)
+  {
+    switch (SSL_get_error (ssldata->ssl, err))
+    {
+    case SSL_ERROR_SYSCALL:
+      errmsg = _("I/O error");
+      break;
+    case SSL_ERROR_SSL:
+      errmsg = ERR_error_string (ERR_get_error (), NULL);
+      break;
+    default:
+      errmsg = _("unknown error");
+    }
+
+    mutt_error (_("SSL failed: %s"), errmsg);
+    mutt_sleep (1);
+
+    return -1;
+  }
+
+  return 0;
+}
+
+static int ssl_socket_open (CONNECTION * conn)
+{
+  sslsockdata *data;
+  int maxbits;
+
+  if (raw_socket_open (conn) < 0)
+    return -1;
+
+  data = safe_calloc (1, sizeof (sslsockdata));
+  conn->sockdata = data;
+
+  if (! (data->ctx = SSL_CTX_new (SSLv23_client_method ())))
+  {
+    /* L10N: an SSL context is a data structure returned by the OpenSSL
+             function SSL_CTX_new().  In this case it returned NULL: an
+             error condition.  */
+    mutt_error (_("Unable to create SSL context"));
+    ssl_dprint_err_stack ();
+    mutt_socket_close (conn);
+    return -1;
+  }
+
+  /* disable SSL protocols as needed */
+  if (!option(OPTTLSV1))
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1);
+  }
+  /* TLSv1.1/1.2 support was added in OpenSSL 1.0.1, but some OS distros such
+   * as Fedora 17 are on OpenSSL 1.0.0.
+   */
+#ifdef SSL_OP_NO_TLSv1_1
+  if (!option(OPTTLSV1_1))
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_1);
+  }
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+  if (!option(OPTTLSV1_2))
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_2);
+  }
+#endif
+  if (!option(OPTSSLV2))
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2);
+  }
+  if (!option(OPTSSLV3))
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3);
+  }
+
+  if (option (OPTSSLSYSTEMCERTS))
+  {
+    if (! SSL_CTX_set_default_verify_paths (data->ctx))
+    {
+      mutt_debug (1, "ssl_socket_open: Error setting default verify paths\n");
+      mutt_socket_close (conn);
+      return -1;
+    }
+  }
+
+  if (SslCertFile && !ssl_load_certificates (data->ctx))
+    mutt_debug (1, "ssl_socket_open: Error loading trusted certificates\n");
+
+  ssl_get_client_cert(data, conn);
+
+  if (SslCiphers) {
+    SSL_CTX_set_cipher_list (data->ctx, SslCiphers);
+  }
+
+  if (ssl_set_verify_partial (data->ctx))
+  {
+    mutt_error (_("Warning: error enabling ssl_verify_partial_chains"));
+    mutt_sleep (2);
+  }
+
+  data->ssl = SSL_new (data->ctx);
+  SSL_set_fd (data->ssl, conn->fd);
+
+  if (ssl_negotiate(conn, data))
+  {
+    mutt_socket_close (conn);
+    return -1;
+  }
+
+  data->isopen = 1;
+
+  conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (data->ssl),
+    &maxbits);
+
+  return 0;
+}
+
+/* mutt_ssl_starttls: Negotiate TLS over an already opened connection.
+ *   TODO: Merge this code better with ssl_socket_open. */
+int mutt_ssl_starttls (CONNECTION* conn)
+{
+  sslsockdata* ssldata;
+  int maxbits;
+  long ssl_options = 0;
+
+  if (ssl_init())
+    goto bail;
+
+  ssldata = safe_calloc (1, sizeof (sslsockdata));
+  /* the ssl_use_xxx protocol options don't apply. We must use TLS in TLS.
+   *
+   * However, we need to be able to negotiate amongst various TLS versions,
+   * which at present can only be done with the SSLv23_client_method;
+   * TLSv1_client_method gives us explicitly TLSv1.0, not 1.1 or 1.2 (True as
+   * of OpenSSL 1.0.1c)
+   */
+  if (! (ssldata->ctx = SSL_CTX_new (SSLv23_client_method())))
+  {
+    mutt_debug (1, "mutt_ssl_starttls: Error allocating SSL_CTX\n");
+    goto bail_ssldata;
+  }
+#ifdef SSL_OP_NO_TLSv1_2
+  if (!option(OPTTLSV1_2))
+    ssl_options |= SSL_OP_NO_TLSv1_2;
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+  if (!option(OPTTLSV1_1))
+    ssl_options |= SSL_OP_NO_TLSv1_1;
+#endif
+#ifdef SSL_OP_NO_TLSv1
+  if (!option(OPTTLSV1))
+    ssl_options |= SSL_OP_NO_TLSv1;
+#endif
+  /* these are always set */
+#ifdef SSL_OP_NO_SSLv3
+  ssl_options |= SSL_OP_NO_SSLv3;
+#endif
+#ifdef SSL_OP_NO_SSLv2
+  ssl_options |= SSL_OP_NO_SSLv2;
+#endif
+  if (! SSL_CTX_set_options(ssldata->ctx, ssl_options))
+  {
+    mutt_debug (1, "mutt_ssl_starttls: Error setting options to %ld\n",
+                ssl_options);
+    goto bail_ctx;
+  }
+
+  if (option (OPTSSLSYSTEMCERTS))
+  {
+    if (! SSL_CTX_set_default_verify_paths (ssldata->ctx))
+    {
+      mutt_debug (1, "mutt_ssl_starttls: Error setting default verify paths\n");
+      goto bail_ctx;
+    }
+  }
+
+  if (SslCertFile && !ssl_load_certificates (ssldata->ctx))
+    mutt_debug (1, "mutt_ssl_starttls: Error loading trusted certificates\n");
+
+  ssl_get_client_cert(ssldata, conn);
+
+  if (SslCiphers) {
+    if (!SSL_CTX_set_cipher_list (ssldata->ctx, SslCiphers)) {
+      mutt_debug (1, "mutt_ssl_starttls: Could not select preferred ciphers\n");
+      goto bail_ctx;
+    }
+  }
+
+  if (ssl_set_verify_partial (ssldata->ctx))
+  {
+    mutt_error (_("Warning: error enabling ssl_verify_partial_chains"));
+    mutt_sleep (2);
+  }
+
+  if (! (ssldata->ssl = SSL_new (ssldata->ctx)))
+  {
+    mutt_debug (1, "mutt_ssl_starttls: Error allocating SSL\n");
+    goto bail_ctx;
+  }
+
+  if (SSL_set_fd (ssldata->ssl, conn->fd) != 1)
+  {
+    mutt_debug (1, "mutt_ssl_starttls: Error setting fd\n");
+    goto bail_ssl;
+  }
+
+  if (ssl_negotiate (conn, ssldata))
+    goto bail_ssl;
+
+  ssldata->isopen = 1;
+
+  /* hmm. watch out if we're starting TLS over any method other than raw. */
+  conn->sockdata = ssldata;
+  conn->conn_read = ssl_socket_read;
+  conn->conn_write = ssl_socket_write;
+  conn->conn_close = tls_close;
+
+  conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (ssldata->ssl),
+    &maxbits);
+
+  return 0;
+
+ bail_ssl:
+  FREE (&ssldata->ssl);
+ bail_ctx:
+  FREE (&ssldata->ctx);
+ bail_ssldata:
+  FREE (&ssldata);
+ bail:
+  return -1;
+}
+
+int mutt_ssl_socket_setup (CONNECTION * conn)
+{
+  if (ssl_init() < 0)
+  {
+    conn->conn_open = ssl_socket_open_err;
+    return -1;
+  }
+
+  conn->conn_open      = ssl_socket_open;
+  conn->conn_read      = ssl_socket_read;
+  conn->conn_write     = ssl_socket_write;
+  conn->conn_close     = ssl_socket_close;
+  conn->conn_poll       = raw_socket_poll;
+
+  return 0;
+}
 
-  return snprintf(buf, size, "%s", account->pass);
-}
index 6953ae44dff16618731dd6df95d18e127e3e3204..db0a468c6156cb19a8e2d83854a9bcf32a1a12e8 100644 (file)
@@ -41,6 +41,8 @@
 #define CERTERR_SIGNERNOTCA 32
 #define CERTERR_INSECUREALG 64
 
+#define CERT_SEP "-----BEGIN"
+
 /* deprecated types compatibility */
 
 #ifndef HAVE_GNUTLS_CERTIFICATE_CREDENTIALS_T
@@ -79,18 +81,6 @@ typedef struct _tlssockdata
 }
 tlssockdata;
 
-/* local prototypes */
-static int tls_socket_read (CONNECTION* conn, char* buf, size_t len);
-static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len);
-static int tls_socket_open (CONNECTION* conn);
-static int tls_socket_close (CONNECTION* conn);
-static int tls_starttls_close (CONNECTION* conn);
-
-static int tls_init (void);
-static int tls_negotiate (CONNECTION* conn);
-static int tls_check_certificate (CONNECTION* conn);
-
-
 static int tls_init (void)
 {
   static unsigned char init_complete = 0;
@@ -111,20 +101,6 @@ static int tls_init (void)
   return 0;
 }
 
-int mutt_ssl_socket_setup (CONNECTION* conn)
-{
-  if (tls_init() < 0)
-    return -1;
-
-  conn->conn_open      = tls_socket_open;
-  conn->conn_read      = tls_socket_read;
-  conn->conn_write     = tls_socket_write;
-  conn->conn_close     = tls_socket_close;
-  conn->conn_poll       = raw_socket_poll;
-
-  return 0;
-}
-
 static int tls_socket_read (CONNECTION* conn, char* buf, size_t len)
 {
   tlssockdata *data = conn->sockdata;
@@ -185,357 +161,164 @@ static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len)
   return sent;
 }
 
-static int tls_socket_open (CONNECTION* conn)
+static int tls_socket_close (CONNECTION* conn)
 {
-  if (raw_socket_open (conn) < 0)
-    return -1;
-
-  if (tls_negotiate (conn) < 0)
+  tlssockdata *data = conn->sockdata;
+  if (data)
   {
-    tls_socket_close (conn);
-    return -1;
+    /* shut down only the write half to avoid hanging waiting for the remote to respond.
+     *
+     * RFC5246 7.2.1. "Closure Alerts"
+     *
+     * It is not required for the initiator of the close to wait for the
+     * responding close_notify alert before closing the read side of the
+     * connection.
+     */
+    gnutls_bye (data->state, GNUTLS_SHUT_WR);
+
+    gnutls_certificate_free_credentials (data->xcred);
+    gnutls_deinit (data->state);
+    FREE (&conn->sockdata);
   }
 
-  return 0;
+  return raw_socket_close (conn);
 }
 
-int mutt_ssl_starttls (CONNECTION* conn)
+static int tls_starttls_close (CONNECTION* conn)
 {
-  if (tls_init() < 0)
-    return -1;
-
-  if (tls_negotiate (conn) < 0)
-    return -1;
+  int rc;
 
-  conn->conn_read      = tls_socket_read;
-  conn->conn_write     = tls_socket_write;
-  conn->conn_close     = tls_starttls_close;
+  rc = tls_socket_close (conn);
+  conn->conn_read = raw_socket_read;
+  conn->conn_write = raw_socket_write;
+  conn->conn_close = raw_socket_close;
 
-  return 0;
+  return rc;
 }
 
-static void tls_get_client_cert (CONNECTION* conn)
+/* sanity-checking wrapper for gnutls_certificate_verify_peers */
+static gnutls_certificate_status_t tls_verify_peers (gnutls_session_t tlsstate)
 {
-  tlssockdata *data = conn->sockdata;
-  const gnutls_datum_t* crtdata;
-  gnutls_x509_crt_t clientcrt;
-  char* dn;
-  char* cn;
-  char* cnend;
-  size_t dnlen;
+  int verify_ret;
+  unsigned int status;
 
-  /* get our cert CN if we have one */
-  if (!(crtdata = gnutls_certificate_get_ours (data->state)))
-    return;
+  verify_ret = gnutls_certificate_verify_peers2 (tlsstate, &status);
+  if (!verify_ret)
+    return status;
 
-  if (gnutls_x509_crt_init (&clientcrt) < 0)
-  {
-    mutt_debug (1, "Failed to init gnutls crt\n");
-    return;
-  }
-  if (gnutls_x509_crt_import (clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
+  if (status == GNUTLS_E_NO_CERTIFICATE_FOUND)
   {
-    mutt_debug (1, "Failed to import gnutls client crt\n");
-    goto err_crt;
+    mutt_error (_("Unable to get certificate from peer"));
+    mutt_sleep (2);
+    return 0;
   }
-  /* get length of DN */
-  dnlen = 0;
-  gnutls_x509_crt_get_dn (clientcrt, NULL, &dnlen);
-  if (!(dn = calloc (1, dnlen)))
+  if (verify_ret < 0)
   {
-    mutt_debug (1, "could not allocate DN\n");
-    goto err_crt;
+    mutt_error (_("Certificate verification error (%s)"),
+                gnutls_strerror (status));
+    mutt_sleep (2);
+    return 0;
   }
-  gnutls_x509_crt_get_dn (clientcrt, dn, &dnlen);
-  mutt_debug (2, "client certificate DN: %s\n", dn);
 
-  /* extract CN to use as external user name */
-  if (!(cn = strstr (dn, "CN=")))
+  /* We only support X.509 certificates (not OpenPGP) at the moment */
+  if (gnutls_certificate_type_get (tlsstate) != GNUTLS_CRT_X509)
   {
-    mutt_debug (1, "no CN found in DN\n");
-    goto err_dn;
+    mutt_error (_("Certificate is not X.509"));
+    mutt_sleep (2);
+    return 0;
   }
-  cn += 3;
-
-  if ((cnend = strstr (dn, ",EMAIL=")))
-    *cnend = '\0';
-
-  /* if we are using a client cert, SASL may expect an external auth name */
-  mutt_account_getuser (&conn->account);
 
-err_dn:
-  FREE (&dn);
-err_crt:
-  gnutls_x509_crt_deinit (clientcrt);
+  return status;
 }
 
-#ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
-static int tls_set_priority(tlssockdata *data)
+static void tls_fingerprint (gnutls_digest_algorithm_t algo,
+                             char* s, int l, const gnutls_datum_t* data)
 {
-  size_t nproto = 4;
-  char *priority;
-  size_t priority_size;
-  int err;
-
-  priority_size = SHORT_STRING + mutt_strlen (SslCiphers);
-  priority = safe_malloc (priority_size);
+  unsigned char md[36];
+  size_t n;
+  int j;
 
-  priority[0] = 0;
-  if (SslCiphers)
-    safe_strcat (priority, priority_size, SslCiphers);
-  else
-    safe_strcat (priority, priority_size, "NORMAL");
+  n = 36;
 
-  if (! option(OPTTLSV1_2))
-  {
-    nproto--;
-    safe_strcat (priority, priority_size, ":-VERS-TLS1.2");
-  }
-  if (! option(OPTTLSV1_1))
-  {
-    nproto--;
-    safe_strcat (priority, priority_size, ":-VERS-TLS1.1");
-  }
-  if (! option(OPTTLSV1))
+  if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0)
   {
-    nproto--;
-    safe_strcat (priority, priority_size, ":-VERS-TLS1.0");
+    snprintf (s, l, _("[unable to calculate]"));
   }
-  if (! option(OPTSSLV3))
+  else
   {
-    nproto--;
-    safe_strcat (priority, priority_size, ":-VERS-SSL3.0");
+    for (j = 0; j < (int) n; j++)
+    {
+      char ch[8];
+      snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
+      safe_strcat (s, l, ch);
+    }
+    s[2*n+n/2-1] = '\0'; /* don't want trailing space */
   }
+}
 
-  if (nproto == 0)
-  {
-    mutt_error (_("All available protocols for TLS/SSL connection disabled"));
-    FREE (&priority);
-    return -1;
-  }
+static int tls_check_stored_hostname (const gnutls_datum_t *cert,
+                                      const char *hostname)
+{
+  char buf[80];
+  FILE *fp;
+  char *linestr = NULL;
+  size_t linestrsize;
+  int linenum = 0;
+  regex_t preg;
+  regmatch_t pmatch[3];
 
-  if ((err = gnutls_priority_set_direct (data->state, priority, NULL)) < 0)
+  /* try checking against names stored in stored certs file */
+  if ((fp = fopen (SslCertFile, "r")))
   {
-    mutt_error ("gnutls_priority_set_direct(%s): %s", priority, gnutls_strerror(err));
-    mutt_sleep (2);
-    FREE (&priority);
-    return -1;
+    if (REGCOMP(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
+                REG_ICASE) != 0)
+    {
+       return 0;
+    }
+
+    buf[0] = '\0';
+    tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
+    while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum, 0)) != NULL)
+    {
+      if(linestr[0] == '#' && linestr[1] == 'H')
+      {
+        if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
+        {
+          linestr[pmatch[1].rm_eo] = '\0';
+          linestr[pmatch[2].rm_eo] = '\0';
+          if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
+              strcmp(linestr + pmatch[2].rm_so, buf) == 0)
+          {
+            regfree(&preg);
+            FREE(&linestr);
+            safe_fclose (&fp);
+            return 1;
+          }
+        }
+      }
+    }
+
+    regfree(&preg);
+    safe_fclose (&fp);
   }
 
-  FREE (&priority);
+  /* not found a matching name */
   return 0;
 }
-#else
-/* This array needs to be large enough to hold all the possible values support
- * by Mutt.  The initialized values are just placeholders--the array gets
- * overwritten in tls_negotiate() depending on the $ssl_use_* options.
- */
-static int protocol_priority[] = {GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1, GNUTLS_SSL3, 0};
 
-static int tls_set_priority(tlssockdata *data)
+/* this bit is based on read_ca_file() in gnutls */
+static int tls_compare_certificates (const gnutls_datum_t *peercert)
 {
-  size_t nproto = 0; /* number of tls/ssl protocols */
+  gnutls_datum_t cert;
+  unsigned char *ptr;
+  FILE *fd1;
+  int ret;
+  gnutls_datum_t b64_data;
+  unsigned char *b64_data_data;
+  struct stat filestat;
 
-  if (option(OPTTLSV1_2))
-    protocol_priority[nproto++] = GNUTLS_TLS1_2;
-  if (option(OPTTLSV1_1))
-    protocol_priority[nproto++] = GNUTLS_TLS1_1;
-  if (option(OPTTLSV1))
-    protocol_priority[nproto++] = GNUTLS_TLS1;
-  if (option(OPTSSLV3))
-    protocol_priority[nproto++] = GNUTLS_SSL3;
-  protocol_priority[nproto] = 0;
-
-  if (nproto == 0)
-  {
-    mutt_error (_("All available protocols for TLS/SSL connection disabled"));
-    return -1;
-  }
-
-  if (SslCiphers)
-  {
-    mutt_error (_("Explicit ciphersuite selection via $ssl_ciphers not supported"));
-    mutt_sleep (2);
-  }
-
-  /* We use default priorities (see gnutls documentation),
-     except for protocol version */
-  gnutls_set_default_priority (data->state);
-  gnutls_protocol_set_priority (data->state, protocol_priority);
-  return 0;
-}
-#endif
-
-/* tls_negotiate: After TLS state has been initialized, attempt to negotiate
- *   TLS over the wire, including certificate checks. */
-static int tls_negotiate (CONNECTION * conn)
-{
-  tlssockdata *data;
-  int err;
-
-  data = safe_calloc (1, sizeof (tlssockdata));
-  conn->sockdata = data;
-  err = gnutls_certificate_allocate_credentials (&data->xcred);
-  if (err < 0)
-  {
-    FREE(&conn->sockdata);
-    mutt_error ("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
-    mutt_sleep (2);
-    return -1;
-  }
-
-  gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
-                                         GNUTLS_X509_FMT_PEM);
-  /* ignore errors, maybe file doesn't exist yet */
-
-  if (SslCACertFile)
-  {
-    gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
-                                            GNUTLS_X509_FMT_PEM);
-  }
-
-  if (SslClientCert)
-  {
-    mutt_debug (2, "Using client certificate %s\n", SslClientCert);
-    gnutls_certificate_set_x509_key_file (data->xcred, SslClientCert,
-                                          SslClientCert, GNUTLS_X509_FMT_PEM);
-  }
-
-#ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS
-  /* disable checking certificate activation/expiration times
-     in gnutls, we do the checks ourselves */
-  gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
-#endif
-
-  if ((err = gnutls_init(&data->state, GNUTLS_CLIENT)))
-  {
-    mutt_error ("gnutls_handshake: %s", gnutls_strerror(err));
-    mutt_sleep (2);
-    goto fail;
-  }
-
-  /* set socket */
-  gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr_t)(long)conn->fd);
-
-  if (gnutls_server_name_set (data->state, GNUTLS_NAME_DNS, conn->account.host,
-                              mutt_strlen (conn->account.host)))
-  {
-    mutt_error(_("Warning: unable to set TLS SNI host name"));
-    mutt_sleep (1);
-  }
-
-  if (tls_set_priority(data) < 0) {
-    goto fail;
-  }
-
-  if (SslDHPrimeBits > 0)
-  {
-    gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
-  }
-
-/*
-  gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
-*/
-
-  gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
-
-  err = gnutls_handshake(data->state);
-
-  while (err == GNUTLS_E_AGAIN)
-  {
-    err = gnutls_handshake(data->state);
-  }
-  if (err < 0) {
-    if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
-    {
-      mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
-                gnutls_alert_get_name(gnutls_alert_get(data->state)));
-    }
-    else
-    {
-      mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
-    }
-    mutt_sleep (2);
-    goto fail;
-  }
-
-  if (!tls_check_certificate(conn))
-    goto fail;
-
-  /* set Security Strength Factor (SSF) for SASL */
-  /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
-  conn->ssf = gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
-
-  tls_get_client_cert (conn);
-
-  if (!option(OPTNOCURSES)) {
-    mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
-                  gnutls_protocol_get_name (gnutls_protocol_get_version (data->state)),
-                  gnutls_kx_get_name (gnutls_kx_get (data->state)),
-                  gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
-                  gnutls_mac_get_name (gnutls_mac_get (data->state)));
-    mutt_sleep (0);
-  }
-
-  return 0;
-
- fail:
-  gnutls_certificate_free_credentials (data->xcred);
-  gnutls_deinit (data->state);
-  FREE(&conn->sockdata);
-  return -1;
-}
-
-static int tls_socket_close (CONNECTION* conn)
-{
-  tlssockdata *data = conn->sockdata;
-  if (data)
-  {
-    /* shut down only the write half to avoid hanging waiting for the remote to respond.
-     *
-     * RFC5246 7.2.1. "Closure Alerts"
-     *
-     * It is not required for the initiator of the close to wait for the
-     * responding close_notify alert before closing the read side of the
-     * connection.
-     */
-    gnutls_bye (data->state, GNUTLS_SHUT_WR);
-
-    gnutls_certificate_free_credentials (data->xcred);
-    gnutls_deinit (data->state);
-    FREE (&conn->sockdata);
-  }
-
-  return raw_socket_close (conn);
-}
-
-static int tls_starttls_close (CONNECTION* conn)
-{
-  int rc;
-
-  rc = tls_socket_close (conn);
-  conn->conn_read = raw_socket_read;
-  conn->conn_write = raw_socket_write;
-  conn->conn_close = raw_socket_close;
-
-  return rc;
-}
-
-#define CERT_SEP "-----BEGIN"
-
-/* this bit is based on read_ca_file() in gnutls */
-static int tls_compare_certificates (const gnutls_datum_t *peercert)
-{
-  gnutls_datum_t cert;
-  unsigned char *ptr;
-  FILE *fd1;
-  int ret;
-  gnutls_datum_t b64_data;
-  unsigned char *b64_data_data;
-  struct stat filestat;
-
-  if (stat(SslCertFile, &filestat) == -1)
-    return 0;
+  if (stat(SslCertFile, &filestat) == -1)
+    return 0;
 
   b64_data.size = filestat.st_size+1;
   b64_data_data = safe_calloc (1, b64_data.size);
@@ -591,137 +374,48 @@ static int tls_compare_certificates (const gnutls_datum_t *peercert)
   return 0;
 }
 
-static void tls_fingerprint (gnutls_digest_algorithm_t algo,
-                             char* s, int l, const gnutls_datum_t* data)
+static int tls_check_preauth (const gnutls_datum_t *certdata,
+                              gnutls_certificate_status_t certstat,
+                              const char *hostname, int chainidx, int* certerr,
+                              int* savedcert)
 {
-  unsigned char md[36];
-  size_t n;
-  int j;
+  gnutls_x509_crt_t cert;
 
-  n = 36;
+  *certerr = CERTERR_VALID;
+  *savedcert = 0;
 
-  if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0)
+  if (gnutls_x509_crt_init (&cert) < 0)
   {
-    snprintf (s, l, _("[unable to calculate]"));
+    mutt_error (_("Error initialising gnutls certificate data"));
+    mutt_sleep (2);
+    return -1;
   }
-  else
+
+  if (gnutls_x509_crt_import (cert, certdata, GNUTLS_X509_FMT_DER) < 0)
   {
-    for (j = 0; j < (int) n; j++)
-    {
-      char ch[8];
-      snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
-      safe_strcat (s, l, ch);
-    }
-    s[2*n+n/2-1] = '\0'; /* don't want trailing space */
+    mutt_error (_("Error processing certificate data"));
+    mutt_sleep (2);
+    gnutls_x509_crt_deinit (cert);
+    return -1;
   }
-}
 
-static char *tls_make_date (time_t t, char *s, size_t len)
-{
-  struct tm *l = gmtime (&t);
+  if (option (OPTSSLVERIFYDATES) != MUTT_NO)
+  {
+    if (gnutls_x509_crt_get_expiration_time (cert) < time(NULL))
+      *certerr |= CERTERR_EXPIRED;
+    if (gnutls_x509_crt_get_activation_time (cert) > time(NULL))
+      *certerr |= CERTERR_NOTYETVALID;
+  }
 
-  if (l)
-    snprintf (s, len,  "%s, %d %s %d %02d:%02d:%02d UTC",
-             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
-             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
-  else
-    strfcpy (s, _("[invalid date]"), len);
+  if (chainidx == 0 && option (OPTSSLVERIFYHOST) != MUTT_NO
+      && !gnutls_x509_crt_check_hostname (cert, hostname)
+      && !tls_check_stored_hostname (certdata, hostname))
+    *certerr |= CERTERR_HOSTNAME;
 
-  return (s);
-}
-
-static int tls_check_stored_hostname (const gnutls_datum_t *cert,
-                                      const char *hostname)
-{
-  char buf[80];
-  FILE *fp;
-  char *linestr = NULL;
-  size_t linestrsize;
-  int linenum = 0;
-  regex_t preg;
-  regmatch_t pmatch[3];
-
-  /* try checking against names stored in stored certs file */
-  if ((fp = fopen (SslCertFile, "r")))
-  {
-    if (REGCOMP(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
-                REG_ICASE) != 0)
-    {
-       return 0;
-    }
-
-    buf[0] = '\0';
-    tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
-    while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum, 0)) != NULL)
-    {
-      if(linestr[0] == '#' && linestr[1] == 'H')
-      {
-        if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
-        {
-          linestr[pmatch[1].rm_eo] = '\0';
-          linestr[pmatch[2].rm_eo] = '\0';
-          if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
-              strcmp(linestr + pmatch[2].rm_so, buf) == 0)
-          {
-            regfree(&preg);
-            FREE(&linestr);
-            safe_fclose (&fp);
-            return 1;
-          }
-        }
-      }
-    }
-
-    regfree(&preg);
-    safe_fclose (&fp);
-  }
-
-  /* not found a matching name */
-  return 0;
-}
-
-static int tls_check_preauth (const gnutls_datum_t *certdata,
-                              gnutls_certificate_status_t certstat,
-                              const char *hostname, int chainidx, int* certerr,
-                              int* savedcert)
-{
-  gnutls_x509_crt_t cert;
-
-  *certerr = CERTERR_VALID;
-  *savedcert = 0;
-
-  if (gnutls_x509_crt_init (&cert) < 0)
-  {
-    mutt_error (_("Error initialising gnutls certificate data"));
-    mutt_sleep (2);
-    return -1;
-  }
-
-  if (gnutls_x509_crt_import (cert, certdata, GNUTLS_X509_FMT_DER) < 0)
-  {
-    mutt_error (_("Error processing certificate data"));
-    mutt_sleep (2);
-    gnutls_x509_crt_deinit (cert);
-    return -1;
-  }
-
-  if (option (OPTSSLVERIFYDATES) != MUTT_NO)
-  {
-    if (gnutls_x509_crt_get_expiration_time (cert) < time(NULL))
-      *certerr |= CERTERR_EXPIRED;
-    if (gnutls_x509_crt_get_activation_time (cert) > time(NULL))
-      *certerr |= CERTERR_NOTYETVALID;
-  }
-
-  if (chainidx == 0 && option (OPTSSLVERIFYHOST) != MUTT_NO
-      && !gnutls_x509_crt_check_hostname (cert, hostname)
-      && !tls_check_stored_hostname (certdata, hostname))
-    *certerr |= CERTERR_HOSTNAME;
-
-  /* see whether certificate is in our cache (certificates file) */
-  if (tls_compare_certificates (certdata))
-  {
-    *savedcert = 1;
+  /* see whether certificate is in our cache (certificates file) */
+  if (tls_compare_certificates (certdata))
+  {
+    *savedcert = 1;
 
     if (chainidx == 0 && (certstat & GNUTLS_CERT_INVALID))
     {
@@ -798,6 +492,20 @@ static int tls_check_preauth (const gnutls_datum_t *certdata,
   return -1;
 }
 
+static char *tls_make_date (time_t t, char *s, size_t len)
+{
+  struct tm *l = gmtime (&t);
+
+  if (l)
+    snprintf (s, len,  "%s, %d %s %d %02d:%02d:%02d UTC",
+             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
+             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
+  else
+    strfcpy (s, _("[invalid date]"), len);
+
+  return (s);
+}
+
 /*
  * Returns 0 on failure, nonzero on success.
  */
@@ -1071,41 +779,6 @@ static int tls_check_one_certificate (const gnutls_datum_t *certdata,
   return (done == 2);
 }
 
-/* sanity-checking wrapper for gnutls_certificate_verify_peers */
-static gnutls_certificate_status_t tls_verify_peers (gnutls_session_t tlsstate)
-{
-  int verify_ret;
-  unsigned int status;
-
-  verify_ret = gnutls_certificate_verify_peers2 (tlsstate, &status);
-  if (!verify_ret)
-    return status;
-
-  if (status == GNUTLS_E_NO_CERTIFICATE_FOUND)
-  {
-    mutt_error (_("Unable to get certificate from peer"));
-    mutt_sleep (2);
-    return 0;
-  }
-  if (verify_ret < 0)
-  {
-    mutt_error (_("Certificate verification error (%s)"),
-                gnutls_strerror (status));
-    mutt_sleep (2);
-    return 0;
-  }
-
-  /* We only support X.509 certificates (not OpenPGP) at the moment */
-  if (gnutls_certificate_type_get (tlsstate) != GNUTLS_CRT_X509)
-  {
-    mutt_error (_("Certificate is not X.509"));
-    mutt_sleep (2);
-    return 0;
-  }
-
-  return status;
-}
-
 static int tls_check_certificate (CONNECTION* conn)
 {
   tlssockdata *data = conn->sockdata;
@@ -1182,3 +855,319 @@ static int tls_check_certificate (CONNECTION* conn)
 
   return rc;
 }
+
+static void tls_get_client_cert (CONNECTION* conn)
+{
+  tlssockdata *data = conn->sockdata;
+  const gnutls_datum_t* crtdata;
+  gnutls_x509_crt_t clientcrt;
+  char* dn;
+  char* cn;
+  char* cnend;
+  size_t dnlen;
+
+  /* get our cert CN if we have one */
+  if (!(crtdata = gnutls_certificate_get_ours (data->state)))
+    return;
+
+  if (gnutls_x509_crt_init (&clientcrt) < 0)
+  {
+    mutt_debug (1, "Failed to init gnutls crt\n");
+    return;
+  }
+  if (gnutls_x509_crt_import (clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
+  {
+    mutt_debug (1, "Failed to import gnutls client crt\n");
+    goto err_crt;
+  }
+  /* get length of DN */
+  dnlen = 0;
+  gnutls_x509_crt_get_dn (clientcrt, NULL, &dnlen);
+  if (!(dn = calloc (1, dnlen)))
+  {
+    mutt_debug (1, "could not allocate DN\n");
+    goto err_crt;
+  }
+  gnutls_x509_crt_get_dn (clientcrt, dn, &dnlen);
+  mutt_debug (2, "client certificate DN: %s\n", dn);
+
+  /* extract CN to use as external user name */
+  if (!(cn = strstr (dn, "CN=")))
+  {
+    mutt_debug (1, "no CN found in DN\n");
+    goto err_dn;
+  }
+  cn += 3;
+
+  if ((cnend = strstr (dn, ",EMAIL=")))
+    *cnend = '\0';
+
+  /* if we are using a client cert, SASL may expect an external auth name */
+  mutt_account_getuser (&conn->account);
+
+err_dn:
+  FREE (&dn);
+err_crt:
+  gnutls_x509_crt_deinit (clientcrt);
+}
+
+#ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
+static int tls_set_priority(tlssockdata *data)
+{
+  size_t nproto = 4;
+  char *priority;
+  size_t priority_size;
+  int err;
+
+  priority_size = SHORT_STRING + mutt_strlen (SslCiphers);
+  priority = safe_malloc (priority_size);
+
+  priority[0] = 0;
+  if (SslCiphers)
+    safe_strcat (priority, priority_size, SslCiphers);
+  else
+    safe_strcat (priority, priority_size, "NORMAL");
+
+  if (! option(OPTTLSV1_2))
+  {
+    nproto--;
+    safe_strcat (priority, priority_size, ":-VERS-TLS1.2");
+  }
+  if (! option(OPTTLSV1_1))
+  {
+    nproto--;
+    safe_strcat (priority, priority_size, ":-VERS-TLS1.1");
+  }
+  if (! option(OPTTLSV1))
+  {
+    nproto--;
+    safe_strcat (priority, priority_size, ":-VERS-TLS1.0");
+  }
+  if (! option(OPTSSLV3))
+  {
+    nproto--;
+    safe_strcat (priority, priority_size, ":-VERS-SSL3.0");
+  }
+
+  if (nproto == 0)
+  {
+    mutt_error (_("All available protocols for TLS/SSL connection disabled"));
+    FREE (&priority);
+    return -1;
+  }
+
+  if ((err = gnutls_priority_set_direct (data->state, priority, NULL)) < 0)
+  {
+    mutt_error ("gnutls_priority_set_direct(%s): %s", priority, gnutls_strerror(err));
+    mutt_sleep (2);
+    FREE (&priority);
+    return -1;
+  }
+
+  FREE (&priority);
+  return 0;
+}
+#else
+/* This array needs to be large enough to hold all the possible values support
+ * by Mutt.  The initialized values are just placeholders--the array gets
+ * overwritten in tls_negotiate() depending on the $ssl_use_* options.
+ */
+static int protocol_priority[] = {GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1, GNUTLS_SSL3, 0};
+
+static int tls_set_priority(tlssockdata *data)
+{
+  size_t nproto = 0; /* number of tls/ssl protocols */
+
+  if (option(OPTTLSV1_2))
+    protocol_priority[nproto++] = GNUTLS_TLS1_2;
+  if (option(OPTTLSV1_1))
+    protocol_priority[nproto++] = GNUTLS_TLS1_1;
+  if (option(OPTTLSV1))
+    protocol_priority[nproto++] = GNUTLS_TLS1;
+  if (option(OPTSSLV3))
+    protocol_priority[nproto++] = GNUTLS_SSL3;
+  protocol_priority[nproto] = 0;
+
+  if (nproto == 0)
+  {
+    mutt_error (_("All available protocols for TLS/SSL connection disabled"));
+    return -1;
+  }
+
+  if (SslCiphers)
+  {
+    mutt_error (_("Explicit ciphersuite selection via $ssl_ciphers not supported"));
+    mutt_sleep (2);
+  }
+
+  /* We use default priorities (see gnutls documentation),
+     except for protocol version */
+  gnutls_set_default_priority (data->state);
+  gnutls_protocol_set_priority (data->state, protocol_priority);
+  return 0;
+}
+#endif
+
+/* tls_negotiate: After TLS state has been initialized, attempt to negotiate
+ *   TLS over the wire, including certificate checks. */
+static int tls_negotiate (CONNECTION * conn)
+{
+  tlssockdata *data;
+  int err;
+
+  data = safe_calloc (1, sizeof (tlssockdata));
+  conn->sockdata = data;
+  err = gnutls_certificate_allocate_credentials (&data->xcred);
+  if (err < 0)
+  {
+    FREE(&conn->sockdata);
+    mutt_error ("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
+    mutt_sleep (2);
+    return -1;
+  }
+
+  gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
+                                         GNUTLS_X509_FMT_PEM);
+  /* ignore errors, maybe file doesn't exist yet */
+
+  if (SslCACertFile)
+  {
+    gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
+                                            GNUTLS_X509_FMT_PEM);
+  }
+
+  if (SslClientCert)
+  {
+    mutt_debug (2, "Using client certificate %s\n", SslClientCert);
+    gnutls_certificate_set_x509_key_file (data->xcred, SslClientCert,
+                                          SslClientCert, GNUTLS_X509_FMT_PEM);
+  }
+
+#ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS
+  /* disable checking certificate activation/expiration times
+     in gnutls, we do the checks ourselves */
+  gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+#endif
+
+  if ((err = gnutls_init(&data->state, GNUTLS_CLIENT)))
+  {
+    mutt_error ("gnutls_handshake: %s", gnutls_strerror(err));
+    mutt_sleep (2);
+    goto fail;
+  }
+
+  /* set socket */
+  gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr_t)(long)conn->fd);
+
+  if (gnutls_server_name_set (data->state, GNUTLS_NAME_DNS, conn->account.host,
+                              mutt_strlen (conn->account.host)))
+  {
+    mutt_error(_("Warning: unable to set TLS SNI host name"));
+    mutt_sleep (1);
+  }
+
+  if (tls_set_priority(data) < 0) {
+    goto fail;
+  }
+
+  if (SslDHPrimeBits > 0)
+  {
+    gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
+  }
+
+/*
+  gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
+*/
+
+  gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
+
+  err = gnutls_handshake(data->state);
+
+  while (err == GNUTLS_E_AGAIN)
+  {
+    err = gnutls_handshake(data->state);
+  }
+  if (err < 0) {
+    if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
+    {
+      mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
+                gnutls_alert_get_name(gnutls_alert_get(data->state)));
+    }
+    else
+    {
+      mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
+    }
+    mutt_sleep (2);
+    goto fail;
+  }
+
+  if (!tls_check_certificate(conn))
+    goto fail;
+
+  /* set Security Strength Factor (SSF) for SASL */
+  /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
+  conn->ssf = gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
+
+  tls_get_client_cert (conn);
+
+  if (!option(OPTNOCURSES)) {
+    mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
+                  gnutls_protocol_get_name (gnutls_protocol_get_version (data->state)),
+                  gnutls_kx_get_name (gnutls_kx_get (data->state)),
+                  gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
+                  gnutls_mac_get_name (gnutls_mac_get (data->state)));
+    mutt_sleep (0);
+  }
+
+  return 0;
+
+ fail:
+  gnutls_certificate_free_credentials (data->xcred);
+  gnutls_deinit (data->state);
+  FREE(&conn->sockdata);
+  return -1;
+}
+
+static int tls_socket_open (CONNECTION* conn)
+{
+  if (raw_socket_open (conn) < 0)
+    return -1;
+
+  if (tls_negotiate (conn) < 0)
+  {
+    tls_socket_close (conn);
+    return -1;
+  }
+
+  return 0;
+}
+
+int mutt_ssl_socket_setup (CONNECTION* conn)
+{
+  if (tls_init() < 0)
+    return -1;
+
+  conn->conn_open      = tls_socket_open;
+  conn->conn_read      = tls_socket_read;
+  conn->conn_write     = tls_socket_write;
+  conn->conn_close     = tls_socket_close;
+  conn->conn_poll       = raw_socket_poll;
+
+  return 0;
+}
+
+int mutt_ssl_starttls (CONNECTION* conn)
+{
+  if (tls_init() < 0)
+    return -1;
+
+  if (tls_negotiate (conn) < 0)
+    return -1;
+
+  conn->conn_read      = tls_socket_read;
+  conn->conn_write     = tls_socket_write;
+  conn->conn_close     = tls_starttls_close;
+
+  return 0;
+}
+
index 9fe3a7f5cd8d15d739b54037272f4e65c998ba52..885c821bd6a0c5ea6ffb3cadd802fb266e3957f4 100644 (file)
@@ -38,25 +38,6 @@ typedef struct
   int writefd;
 } TUNNEL_DATA;
 
-/* forward declarations */
-static int tunnel_socket_open (CONNECTION*);
-static int tunnel_socket_close (CONNECTION*);
-static int tunnel_socket_read (CONNECTION* conn, char* buf, size_t len);
-static int tunnel_socket_write (CONNECTION* conn, const char* buf, size_t len);
-static int tunnel_socket_poll (CONNECTION* conn);
-
-/* -- public functions -- */
-int mutt_tunnel_socket_setup (CONNECTION *conn)
-{
-  conn->conn_open = tunnel_socket_open;
-  conn->conn_close = tunnel_socket_close;
-  conn->conn_read = tunnel_socket_read;
-  conn->conn_write = tunnel_socket_write;
-  conn->conn_poll = tunnel_socket_poll;
-
-  return 0;
-}
-
 static int tunnel_socket_open (CONNECTION *conn)
 {
   TUNNEL_DATA* tunnel;
@@ -199,3 +180,16 @@ static int tunnel_socket_poll (CONNECTION* conn)
 
   return rc;
 }
+
+/* -- public functions -- */
+int mutt_tunnel_socket_setup (CONNECTION *conn)
+{
+  conn->conn_open = tunnel_socket_open;
+  conn->conn_close = tunnel_socket_close;
+  conn->conn_read = tunnel_socket_read;
+  conn->conn_write = tunnel_socket_write;
+  conn->conn_poll = tunnel_socket_poll;
+
+  return 0;
+}
+
index 2eb8e704aadc19a307774ec0da0e46f3543ad6ce..33898d0faa39f077b76993f17c748efabc9c1f00 100644 (file)
--- a/newsrc.c
+++ b/newsrc.c
@@ -45,8 +45,6 @@
 #include <dirent.h>
 #include <errno.h>
 
-void nntp_group_unread_stat (NNTP_DATA *nntp_data);
-
 /* Find NNTP_DATA for given newsgroup or add it */
 static NNTP_DATA *nntp_data_find (NNTP_SERVER *nserv, const char *group)
 {
@@ -118,6 +116,31 @@ void nntp_newsrc_close (NNTP_SERVER *nserv)
   safe_fclose (&nserv->newsrc_fp);
 }
 
+/* calculate number of unread articles using .newsrc data */
+void nntp_group_unread_stat (NNTP_DATA *nntp_data)
+{
+  unsigned int i;
+  anum_t first, last;
+
+  nntp_data->unread = 0;
+  if (nntp_data->lastMessage == 0 ||
+      nntp_data->firstMessage > nntp_data->lastMessage)
+    return;
+
+  nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
+  for (i = 0; i < nntp_data->newsrc_len; i++)
+  {
+    first = nntp_data->newsrc_ent[i].first;
+    if (first < nntp_data->firstMessage)
+      first = nntp_data->firstMessage;
+    last = nntp_data->newsrc_ent[i].last;
+    if (last > nntp_data->lastMessage)
+      last = nntp_data->lastMessage;
+    if (first <= last)
+      nntp_data->unread -= last - first + 1;
+  }
+}
+
 /* Parse .newsrc file:
  *  0 - not changed
  *  1 - parsed
@@ -1095,31 +1118,6 @@ void nntp_article_status (CONTEXT *ctx, HEADER *hdr, char *group, anum_t anum)
     hdr->old = 1;
 }
 
-/* calculate number of unread articles using .newsrc data */
-void nntp_group_unread_stat (NNTP_DATA *nntp_data)
-{
-  unsigned int i;
-  anum_t first, last;
-
-  nntp_data->unread = 0;
-  if (nntp_data->lastMessage == 0 ||
-      nntp_data->firstMessage > nntp_data->lastMessage)
-    return;
-
-  nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
-  for (i = 0; i < nntp_data->newsrc_len; i++)
-  {
-    first = nntp_data->newsrc_ent[i].first;
-    if (first < nntp_data->firstMessage)
-      first = nntp_data->firstMessage;
-    last = nntp_data->newsrc_ent[i].last;
-    if (last > nntp_data->lastMessage)
-      last = nntp_data->lastMessage;
-    if (first <= last)
-      nntp_data->unread -= last - first + 1;
-  }
-}
-
 /* Subscribe newsgroup */
 NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *nserv, char *group)
 {
diff --git a/nntp.c b/nntp.c
index 028290fb462e50c09931fb652c688659d8998faa..8423c6d67854c3ed5f71ac31938b6a3edcfee538 100644 (file)
--- a/nntp.c
+++ b/nntp.c
@@ -59,8 +59,6 @@
 #include "mutt_sasl.h"
 #endif
 
-static int nntp_check_mailbox (CONTEXT *ctx, int *index_hint);
-
 static int nntp_connect_error (NNTP_SERVER *nserv)
 {
   nserv->status = NNTP_NONE;
@@ -1743,165 +1741,6 @@ int nntp_post (const char *msg) {
   return 0;
 }
 
-/* Save changes to .newsrc and cache */
-static int nntp_sync_mailbox (CONTEXT *ctx, int *index_hint)
-{
-  NNTP_DATA *nntp_data = ctx->data;
-  int rc, i;
-#ifdef USE_HCACHE
-  header_cache_t *hc;
-#endif
-
-  /* check for new articles */
-  nntp_data->nserv->check_time = 0;
-  rc = nntp_check_mailbox (ctx, index_hint);
-  if (rc)
-    return rc;
-
-#ifdef USE_HCACHE
-  nntp_data->lastCached = 0;
-  hc = nntp_hcache_open (nntp_data);
-#endif
-
-  nntp_data->unread = ctx->unread;
-  for (i = 0; i < ctx->msgcount; i++)
-  {
-    HEADER *hdr = ctx->hdrs[i];
-    char buf[16];
-
-    snprintf (buf, sizeof (buf), "%d", NHDR (hdr)->article_num);
-    if (nntp_data->bcache && hdr->deleted)
-    {
-      mutt_debug (2, "nntp_sync_mailbox: mutt_bcache_del %s\n", buf);
-      mutt_bcache_del (nntp_data->bcache, buf);
-    }
-
-#ifdef USE_HCACHE
-    if (hc && (hdr->changed || hdr->deleted))
-    {
-      if (hdr->deleted && !hdr->read)
-       nntp_data->unread--;
-      mutt_debug (2, "nntp_sync_mailbox: mutt_hcache_store %s\n", buf);
-      mutt_hcache_store (hc, buf, strlen(buf), hdr, 0);
-    }
-#endif
-  }
-
-#ifdef USE_HCACHE
-  if (hc)
-  {
-    mutt_hcache_close (hc);
-    nntp_data->lastCached = nntp_data->lastLoaded;
-  }
-#endif
-
-  /* save .newsrc entries */
-  nntp_newsrc_gen_entries (ctx);
-  nntp_newsrc_update (nntp_data->nserv);
-  nntp_newsrc_close (nntp_data->nserv);
-  return 0;
-}
-
-/* Free up memory associated with the newsgroup context */
-static int nntp_fastclose_mailbox (CONTEXT *ctx)
-{
-  NNTP_DATA *nntp_data = ctx->data, *nntp_tmp;
-
-  if (!nntp_data)
-    return 0;
-
-  nntp_acache_free (nntp_data);
-  if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
-    return 0;
-
-  nntp_tmp = hash_find (nntp_data->nserv->groups_hash, nntp_data->group);
-  if (nntp_tmp == NULL || nntp_tmp != nntp_data)
-    nntp_data_free (nntp_data);
-  return 0;
-}
-
-/* Get date and time from server */
-static int nntp_date (NNTP_SERVER *nserv, time_t *now)
-{
-  if (nserv->hasDATE)
-  {
-    NNTP_DATA nntp_data;
-    char buf[LONG_STRING];
-    struct tm tm;
-
-    nntp_data.nserv = nserv;
-    nntp_data.group = NULL;
-    strfcpy (buf, "DATE\r\n", sizeof (buf));
-    if (nntp_query (&nntp_data, buf, sizeof (buf)) < 0)
-      return -1;
-
-    if (sscanf (buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
-               &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
-    {
-      tm.tm_year -= 1900;
-      tm.tm_mon--;
-      *now = timegm (&tm);
-      if (*now >= 0)
-      {
-       mutt_debug (1, "nntp_date: server time is %d\n", *now);
-       return 0;
-      }
-    }
-  }
-  time (now);
-  return 0;
-}
-
-/* Fetch list of all newsgroups from server */
-int nntp_active_fetch (NNTP_SERVER *nserv)
-{
-  NNTP_DATA nntp_data;
-  char msg[SHORT_STRING];
-  char buf[LONG_STRING];
-  unsigned int i;
-  int rc;
-
-  snprintf (msg, sizeof (msg), _("Loading list of groups from server %s..."),
-           nserv->conn->account.host);
-  mutt_message (msg);
-  if (nntp_date (nserv, &nserv->newgroups_time) < 0)
-    return -1;
-
-  nntp_data.nserv = nserv;
-  nntp_data.group = NULL;
-  strfcpy (buf, "LIST\r\n", sizeof (buf));
-  rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
-                        nntp_add_group, nserv);
-  if (rc)
-  {
-    if (rc > 0)
-    {
-      mutt_error ("LIST: %s", buf);
-      mutt_sleep (2);
-    }
-    return -1;
-  }
-
-  if (option (OPTLOADDESC) &&
-      get_description (&nntp_data, "*", _("Loading descriptions...")) < 0)
-    return -1;
-
-  for (i = 0; i < nserv->groups_num; i++)
-  {
-    NNTP_DATA *nntp_data = nserv->groups_list[i];
-
-    if (nntp_data && nntp_data->deleted && !nntp_data->newsrc_ent)
-    {
-      nntp_delete_group_cache (nntp_data);
-      hash_delete (nserv->groups_hash, nntp_data->group, NULL, nntp_data_free);
-      nserv->groups_list[i] = NULL;
-    }
-  }
-  nntp_active_save_cache (nserv);
-  mutt_clear_error ();
-  return 0;
-}
-
 /* Check newsgroup for new articles:
  *  1 - new articles found
  *  0 - no change
@@ -2163,6 +2002,165 @@ static int nntp_check_mailbox (CONTEXT *ctx, int *index_hint)
   return ret;
 }
 
+/* Save changes to .newsrc and cache */
+static int nntp_sync_mailbox (CONTEXT *ctx, int *index_hint)
+{
+  NNTP_DATA *nntp_data = ctx->data;
+  int rc, i;
+#ifdef USE_HCACHE
+  header_cache_t *hc;
+#endif
+
+  /* check for new articles */
+  nntp_data->nserv->check_time = 0;
+  rc = nntp_check_mailbox (ctx, index_hint);
+  if (rc)
+    return rc;
+
+#ifdef USE_HCACHE
+  nntp_data->lastCached = 0;
+  hc = nntp_hcache_open (nntp_data);
+#endif
+
+  nntp_data->unread = ctx->unread;
+  for (i = 0; i < ctx->msgcount; i++)
+  {
+    HEADER *hdr = ctx->hdrs[i];
+    char buf[16];
+
+    snprintf (buf, sizeof (buf), "%d", NHDR (hdr)->article_num);
+    if (nntp_data->bcache && hdr->deleted)
+    {
+      mutt_debug (2, "nntp_sync_mailbox: mutt_bcache_del %s\n", buf);
+      mutt_bcache_del (nntp_data->bcache, buf);
+    }
+
+#ifdef USE_HCACHE
+    if (hc && (hdr->changed || hdr->deleted))
+    {
+      if (hdr->deleted && !hdr->read)
+       nntp_data->unread--;
+      mutt_debug (2, "nntp_sync_mailbox: mutt_hcache_store %s\n", buf);
+      mutt_hcache_store (hc, buf, strlen(buf), hdr, 0);
+    }
+#endif
+  }
+
+#ifdef USE_HCACHE
+  if (hc)
+  {
+    mutt_hcache_close (hc);
+    nntp_data->lastCached = nntp_data->lastLoaded;
+  }
+#endif
+
+  /* save .newsrc entries */
+  nntp_newsrc_gen_entries (ctx);
+  nntp_newsrc_update (nntp_data->nserv);
+  nntp_newsrc_close (nntp_data->nserv);
+  return 0;
+}
+
+/* Free up memory associated with the newsgroup context */
+static int nntp_fastclose_mailbox (CONTEXT *ctx)
+{
+  NNTP_DATA *nntp_data = ctx->data, *nntp_tmp;
+
+  if (!nntp_data)
+    return 0;
+
+  nntp_acache_free (nntp_data);
+  if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
+    return 0;
+
+  nntp_tmp = hash_find (nntp_data->nserv->groups_hash, nntp_data->group);
+  if (nntp_tmp == NULL || nntp_tmp != nntp_data)
+    nntp_data_free (nntp_data);
+  return 0;
+}
+
+/* Get date and time from server */
+static int nntp_date (NNTP_SERVER *nserv, time_t *now)
+{
+  if (nserv->hasDATE)
+  {
+    NNTP_DATA nntp_data;
+    char buf[LONG_STRING];
+    struct tm tm;
+
+    nntp_data.nserv = nserv;
+    nntp_data.group = NULL;
+    strfcpy (buf, "DATE\r\n", sizeof (buf));
+    if (nntp_query (&nntp_data, buf, sizeof (buf)) < 0)
+      return -1;
+
+    if (sscanf (buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
+               &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
+    {
+      tm.tm_year -= 1900;
+      tm.tm_mon--;
+      *now = timegm (&tm);
+      if (*now >= 0)
+      {
+       mutt_debug (1, "nntp_date: server time is %d\n", *now);
+       return 0;
+      }
+    }
+  }
+  time (now);
+  return 0;
+}
+
+/* Fetch list of all newsgroups from server */
+int nntp_active_fetch (NNTP_SERVER *nserv)
+{
+  NNTP_DATA nntp_data;
+  char msg[SHORT_STRING];
+  char buf[LONG_STRING];
+  unsigned int i;
+  int rc;
+
+  snprintf (msg, sizeof (msg), _("Loading list of groups from server %s..."),
+           nserv->conn->account.host);
+  mutt_message (msg);
+  if (nntp_date (nserv, &nserv->newgroups_time) < 0)
+    return -1;
+
+  nntp_data.nserv = nserv;
+  nntp_data.group = NULL;
+  strfcpy (buf, "LIST\r\n", sizeof (buf));
+  rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
+                        nntp_add_group, nserv);
+  if (rc)
+  {
+    if (rc > 0)
+    {
+      mutt_error ("LIST: %s", buf);
+      mutt_sleep (2);
+    }
+    return -1;
+  }
+
+  if (option (OPTLOADDESC) &&
+      get_description (&nntp_data, "*", _("Loading descriptions...")) < 0)
+    return -1;
+
+  for (i = 0; i < nserv->groups_num; i++)
+  {
+    NNTP_DATA *nntp_data = nserv->groups_list[i];
+
+    if (nntp_data && nntp_data->deleted && !nntp_data->newsrc_ent)
+    {
+      nntp_delete_group_cache (nntp_data);
+      hash_delete (nserv->groups_hash, nntp_data->group, NULL, nntp_data_free);
+      nserv->groups_list[i] = NULL;
+    }
+  }
+  nntp_active_save_cache (nserv);
+  mutt_clear_error ();
+  return 0;
+}
+
 /* Check for new groups and new articles in subscribed groups:
  *  1 - new groups found
  *  0 - no new groups
diff --git a/pager.c b/pager.c
index 7c5890b612d2b4696f4f984b4199c0cb96d69b01..70987b3db94586c43490ad3ebbf2728379a37a4d 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -702,7 +702,14 @@ classify_quote (struct q_class_t **QuoteList, const char *qptr,
 static int brailleLine = -1;
 static int brailleCol = -1;
 
-static int check_attachment_marker (char *);
+static int check_attachment_marker (char *p)
+{
+  char *q = AttachmentMarker;
+
+  for (;*p == *q && *q && *p && *q != '\a' && *p != '\a'; p++, q++)
+    ;
+  return (int) (*p - *q);
+}
 
 static void
 resolve_types (char *buf, char *raw, struct line_t *lineInfo, int n, int last,
@@ -972,15 +979,6 @@ static int is_ansi (unsigned char *buf)
   return (*buf == 'm');
 }
 
-static int check_attachment_marker (char *p)
-{
-  char *q = AttachmentMarker;
-
-  for (;*p == *q && *q && *p && *q != '\a' && *p != '\a'; p++, q++)
-    ;
-  return (int) (*p - *q);
-}
-
 static int grok_ansi(unsigned char *buf, int pos, ansi_attr *a)
 {
   int x = pos;
index 036de6e5d10f305ef6c624c566e06ad63ed5a781..6bd1468169da3fd2cc5778bfee790fd8efa6a6ec 100644 (file)
--- a/pattern.c
+++ b/pattern.c
 #include "mutt_notmuch.h"
 #endif
 
-static int eat_regexp (pattern_t *pat, BUFFER *, BUFFER *);
-static int eat_date (pattern_t *pat, BUFFER *, BUFFER *);
-static int eat_range (pattern_t *pat, BUFFER *, BUFFER *);
-static int eat_message_range (pattern_t *pat, BUFFER *s, BUFFER *err);
-static int patmatch (const pattern_t *pat, const char *buf);
-
-static const struct pattern_flags
-{
-  int tag;     /* character used to represent this op */
-  int op;      /* operation to perform */
-  int class;
-  int (*eat_arg) (pattern_t *, BUFFER *, BUFFER *);
-}
-Flags[] =
+/* Error codes for eat_range_by_regexp */
+enum
 {
-  { 'A', MUTT_ALL,                     0,              NULL },
-  { 'b', MUTT_BODY,            MUTT_FULL_MSG,  eat_regexp },
-  { 'B', MUTT_WHOLE_MSG,               MUTT_FULL_MSG,  eat_regexp },
-  { 'c', MUTT_CC,                      0,              eat_regexp },
-  { 'C', MUTT_RECIPIENT,               0,              eat_regexp },
-  { 'd', MUTT_DATE,            0,              eat_date },
-  { 'D', MUTT_DELETED,         0,              NULL },
-  { 'e', MUTT_SENDER,          0,              eat_regexp },
-  { 'E', MUTT_EXPIRED,         0,              NULL },
-  { 'f', MUTT_FROM,            0,              eat_regexp },
-  { 'F', MUTT_FLAG,            0,              NULL },
-  { 'g', MUTT_CRYPT_SIGN,              0,              NULL },
-  { 'G', MUTT_CRYPT_ENCRYPT,   0,              NULL },
-  { 'h', MUTT_HEADER,          MUTT_FULL_MSG,  eat_regexp },
-  { 'H', MUTT_HORMEL,          0,              eat_regexp },
-  { 'i', MUTT_ID,                      0,              eat_regexp },
-  { 'k', MUTT_PGP_KEY,         0,              NULL },
-  { 'l', MUTT_LIST,            0,              NULL },
-  { 'L', MUTT_ADDRESS,         0,              eat_regexp },
-  { 'm', MUTT_MESSAGE,         0,              eat_message_range },
-  { 'n', MUTT_SCORE,           0,              eat_range },
-  { 'N', MUTT_NEW,                     0,              NULL },
-  { 'O', MUTT_OLD,                     0,              NULL },
-  { 'p', MUTT_PERSONAL_RECIP,  0,              NULL },
-  { 'P', MUTT_PERSONAL_FROM,   0,              NULL },
-  { 'Q', MUTT_REPLIED,         0,              NULL },
-  { 'r', MUTT_DATE_RECEIVED,   0,              eat_date },
-  { 'R', MUTT_READ,            0,              NULL },
-  { 's', MUTT_SUBJECT,         0,              eat_regexp },
-  { 'S', MUTT_SUPERSEDED,              0,              NULL },
-  { 't', MUTT_TO,                      0,              eat_regexp },
-  { 'T', MUTT_TAG,                     0,              NULL },
-  { 'u', MUTT_SUBSCRIBED_LIST, 0,              NULL },
-  { 'U', MUTT_UNREAD,          0,              NULL },
-  { 'v', MUTT_COLLAPSED,               0,              NULL },
-  { 'V', MUTT_CRYPT_VERIFIED,  0,              NULL },
-#ifdef USE_NNTP
-  { 'w', MUTT_NEWSGROUPS,              0,              eat_regexp },
-#endif
-  { 'x', MUTT_REFERENCE,               0,              eat_regexp },
-  { 'X', MUTT_MIMEATTACH,              0,              eat_range },
-  { 'y', MUTT_XLABEL,          0,              eat_regexp },
-#ifdef USE_NOTMUCH
-  { 'Y', MUTT_NOTMUCH_LABEL,   0,              eat_regexp },
-#endif
-  { 'z', MUTT_SIZE,            0,              eat_range },
-  { '=', MUTT_DUPLICATED,              0,              NULL },
-  { '$', MUTT_UNREFERENCED,    0,              NULL },
-  { 0,   0,                    0,              NULL }
+  RANGE_E_OK,
+  RANGE_E_SYNTAX,
+  RANGE_E_CTX,
 };
 
-static pattern_t *SearchPattern = NULL; /* current search pattern */
-static char LastSearch[STRING] = { 0 };        /* last pattern searched for */
-static char LastSearchExpn[LONG_STRING] = { 0 }; /* expanded version of
-                                                   LastSearch */
-
-#define MUTT_MAXRANGE -1
-
-/* constants for parse_date_range() */
-#define MUTT_PDR_NONE  0x0000
-#define MUTT_PDR_MINUS 0x0001
-#define MUTT_PDR_PLUS  0x0002
-#define MUTT_PDR_WINDOW        0x0004
-#define MUTT_PDR_ABSOLUTE      0x0008
-#define MUTT_PDR_DONE  0x0010
-#define MUTT_PDR_ERROR 0x0100
-#define MUTT_PDR_ERRORDONE     (MUTT_PDR_ERROR | MUTT_PDR_DONE)
-
-
-/* if no uppercase letters are given, do a case-insensitive search */
-int mutt_which_case (const char *s)
-{
-  wchar_t w;
-  mbstate_t mb;
-  size_t l;
-
-  memset (&mb, 0, sizeof (mb));
-
-  for (; (l = mbrtowc (&w, s, MB_CUR_MAX, &mb)) != 0; s += l)
-  {
-    if (l == (size_t) -2)
-      continue; /* shift sequences */
-    if (l == (size_t) -1)
-      return 0; /* error; assume case-sensitive */
-    if (iswalpha ((wint_t) w) && iswupper ((wint_t) w))
-      return 0; /* case-sensitive */
-  }
-
-  return REG_ICASE; /* case-insensitive */
-}
-
-static int
-msg_search (CONTEXT *ctx, pattern_t* pat, int msgno)
-{
-  MESSAGE *msg = NULL;
-  STATE s;
-  FILE *fp = NULL;
-  long lng = 0;
-  int match = 0;
-  HEADER *h = ctx->hdrs[msgno];
-  char *buf;
-  size_t blen;
-#ifdef USE_FMEMOPEN
-  char *temp;
-  size_t tempsize;
-#else
-  char tempfile[_POSIX_PATH_MAX];
-  struct stat st;
-#endif
-
-  if ((msg = mx_open_message (ctx, msgno)) != NULL)
-  {
-    if (option (OPTTHOROUGHSRC))
-    {
-      /* decode the header / body */
-      memset (&s, 0, sizeof (s));
-      s.fpin = msg->fp;
-      s.flags = MUTT_CHARCONV;
-#ifdef USE_FMEMOPEN
-      s.fpout = open_memstream (&temp, &tempsize);
-      if (!s.fpout) {
-       mutt_perror (_("Error opening memstream"));
-       return 0;
-      }
-#else
-      mutt_mktemp (tempfile, sizeof (tempfile));
-      if ((s.fpout = safe_fopen (tempfile, "w+")) == NULL)
-      {
-       mutt_perror (tempfile);
-       return (0);
-      }
-#endif
-
-      if (pat->op != MUTT_BODY)
-       mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
-
-      if (pat->op != MUTT_HEADER)
-      {
-       mutt_parse_mime_message (ctx, h);
-
-       if (WithCrypto && (h->security & ENCRYPT)
-            && !crypt_valid_passphrase(h->security))
-       {
-         mx_close_message (ctx, &msg);
-         if (s.fpout)
-         {
-           safe_fclose (&s.fpout);
-#ifdef USE_FMEMOPEN
-            FREE(&temp);
-#else
-           unlink (tempfile);
-#endif
-         }
-         return (0);
-       }
-
-       fseeko (msg->fp, h->offset, 0);
-       mutt_body_handler (h->content, &s);
-      }
-
-#ifdef USE_FMEMOPEN
-      fclose (s.fpout);
-      lng = tempsize;
-
-      if (tempsize) {
-        fp = fmemopen (temp, tempsize, "r");
-        if (!fp) {
-          mutt_perror (_("Error re-opening memstream"));
-          return 0;
-        }
-      } else { /* fmemopen cannot handle empty buffers */
-        fp = safe_fopen ("/dev/null", "r");
-        if (!fp) {
-          mutt_perror (_("Error opening /dev/null"));
-          return 0;
-        }
-      }
-#else
-      fp = s.fpout;
-      fflush (fp);
-      fseek (fp, 0, 0);
-      fstat (fileno (fp), &st);
-      lng = (long) st.st_size;
-#endif
-    }
-    else
-    {
-      /* raw header / body */
-      fp = msg->fp;
-      if (pat->op != MUTT_BODY)
-      {
-       fseeko (fp, h->offset, 0);
-       lng = h->content->offset - h->offset;
-      }
-      if (pat->op != MUTT_HEADER)
-      {
-       if (pat->op == MUTT_BODY)
-         fseeko (fp, h->content->offset, 0);
-       lng += h->content->length;
-      }
-    }
-
-    blen = STRING;
-    buf = safe_malloc (blen);
-
-    /* search the file "fp" */
-    while (lng > 0)
-    {
-      if (pat->op == MUTT_HEADER)
-      {
-       if (*(buf = mutt_read_rfc822_line (fp, buf, &blen)) == '\0')
-         break;
-      }
-      else if (fgets (buf, blen - 1, fp) == NULL)
-       break; /* don't loop forever */
-      if (patmatch (pat, buf) == 0)
-      {
-       match = 1;
-       break;
-      }
-      lng -= mutt_strlen (buf);
-    }
-
-    FREE (&buf);
-
-    mx_close_message (ctx, &msg);
-
-    if (option (OPTTHOROUGHSRC))
-    {
-      safe_fclose (&fp);
-#ifdef USE_FMEMOPEN
-      if (tempsize)
-        FREE(&temp);
-#else
-      unlink (tempfile);
-#endif
-    }
-  }
-
-  return match;
-}
-
 static int eat_regexp (pattern_t *pat, BUFFER *s, BUFFER *err)
 {
   BUFFER buf;
@@ -352,153 +103,76 @@ static int eat_regexp (pattern_t *pat, BUFFER *s, BUFFER *err)
   return 0;
 }
 
-#define KILO 1024
-#define MEGA 1048576
-#define HMSG(h) (((h)->msgno) + 1)
-#define CTX_MSGNO(c) (HMSG((c)->hdrs[(c)->v2r[(c)->menu->current]]))
-
-enum
-{
-  RANGE_K_REL,
-  RANGE_K_ABS,
-  RANGE_K_LT,
-  RANGE_K_GT,
-  RANGE_K_BARE,
-  /* add new ones HERE */
-  RANGE_K_INVALID
-};
-
-static int
-scan_range_num (BUFFER *s, regmatch_t pmatch[], int group, int kind)
+/* Ny  years
+   Nm  months
+   Nw  weeks
+   Nd  days */
+static const char *get_offset (struct tm *tm, const char *s, int sign)
 {
-  int num;
-  unsigned char c;
+  char *ps;
+  int offset = strtol (s, &ps, 0);
+  if ((sign < 0 && offset > 0) || (sign > 0 && offset < 0))
+    offset = -offset;
 
-  /* this cast looks dangerous, but is already all over this code
-   * (explicit or not) */
-  num = (int)strtol(&s->dptr[pmatch[group].rm_so], NULL, 0);
-  c = (unsigned char)(s->dptr[pmatch[group].rm_eo - 1]);
-  if (toupper(c) == 'K')
-    num *= KILO;
-  else if (toupper(c) == 'M')
-    num *= MEGA;
-  switch (kind)
+  switch (*ps)
   {
-  case RANGE_K_REL:
-    return num + CTX_MSGNO(Context);
-  case RANGE_K_LT:
-    return num - 1;
-  case RANGE_K_GT:
-    return num + 1;
-  default:
-    return num;
+    case 'y':
+      tm->tm_year += offset;
+      break;
+    case 'm':
+      tm->tm_mon += offset;
+      break;
+    case 'w':
+      tm->tm_mday += 7 * offset;
+      break;
+    case 'd':
+      tm->tm_mday += offset;
+      break;
+    default:
+      return s;
   }
+  mutt_normalize_time (tm);
+  return (ps + 1);
 }
 
-#define RANGE_DOT '.'
-#define RANGE_CIRCUM '^'
-#define RANGE_DOLLAR '$'
-#define RANGE_LT '<'
-#define RANGE_GT '>'
-
-/* range sides: left or right */
-enum
-{
-  RANGE_S_LEFT,
-  RANGE_S_RIGHT
-};
-
-static int
-scan_range_slot (BUFFER *s, regmatch_t pmatch[], int grp,
-                 int side, int kind)
+static const char *getDate (const char *s, struct tm *t, BUFFER *err)
 {
-  unsigned char c;
+  char *p;
+  time_t now = time (NULL);
+  struct tm *tm = localtime (&now);
 
-  /* This means the left or right subpattern was empty, e.g. ",." */
-  if ((pmatch[grp].rm_so == -1) || (pmatch[grp].rm_so == pmatch[grp].rm_eo))
+  t->tm_mday = strtol (s, &p, 10);
+  if (t->tm_mday < 1 || t->tm_mday > 31)
   {
-    if (side == RANGE_S_LEFT)
-      return 1;
-    else if (side == RANGE_S_RIGHT)
-      return Context->msgcount;
+    snprintf (err->data, err->dsize, _("Invalid day of month: %s"), s);
+    return NULL;
   }
-  /* We have something, so determine what */
-  c = (unsigned char)(s->dptr[pmatch[grp].rm_so]);
-  switch (c)
+  if (*p != '/')
   {
-  case RANGE_CIRCUM:
-    return 1;
-  case RANGE_DOLLAR:
-    return Context->msgcount;
-  case RANGE_DOT:
-    return CTX_MSGNO(Context);
-  case RANGE_LT:
-  case RANGE_GT:
-    return scan_range_num(s, pmatch, grp+1, kind);
-  default:
-    /* Only other possibility: a number */
-    return scan_range_num(s, pmatch, grp, kind);
+    /* fill in today's month and year */
+    t->tm_mon = tm->tm_mon;
+    t->tm_year = tm->tm_year;
+    return p;
   }
-}
-
-static void
-order_range (pattern_t *pat)
-{
-  int num;
-
-  if (pat->min <= pat->max)
-    return;
-  num = pat->min;
-  pat->min = pat->max;
-  pat->max = num;
-}
-
-/* Error codes for eat_range_by_regexp */
-enum
-{
-  RANGE_E_OK,
-  RANGE_E_SYNTAX,
-  RANGE_E_CTX,
-};
-
-static int
-report_regerror(int regerr, regex_t *preg, BUFFER *err)
-{
-  size_t ds = err->dsize;
-
-  if (regerror(regerr, preg, err->data, ds) > ds)
-    mutt_debug (2, "warning: buffer too small for regerror\n");
-  /* The return value is fixed, exists only to shorten code at callsite */
-  return RANGE_E_SYNTAX;
-}
-
-static int
-is_context_available(BUFFER *s, regmatch_t pmatch[], int kind, BUFFER *err)
-{
-  char *context_loc;
-  const char *context_req_chars[] =
+  p++;
+  t->tm_mon = strtol (p, &p, 10) - 1;
+  if (t->tm_mon < 0 || t->tm_mon > 11)
   {
-    [RANGE_K_REL] = ".0123456789",
-    [RANGE_K_ABS] = ".",
-    [RANGE_K_LT] = "",
-    [RANGE_K_GT] = "",
-    [RANGE_K_BARE] = ".",
-  };
-
-  /* First decide if we're going to need the context at all.
-   * Relative patterns need it iff they contain a dot or a number.
-   * Absolute patterns only need it if they contain a dot. */
-  context_loc = strpbrk(s->dptr+pmatch[0].rm_so, context_req_chars[kind]);
-  if ((context_loc == NULL) || (context_loc >= &s->dptr[pmatch[0].rm_eo]))
-    return 1;
-
-  /* We need a current message.  Do we actually have one? */
-  if (Context && Context->menu)
-    return 1;
-
-  /* Nope. */
-  strfcpy(err->data, _("No current message"), err->dsize);
-  return 0;
+    snprintf (err->data, err->dsize, _("Invalid month: %s"), p);
+    return NULL;
+  }
+  if (*p != '/')
+  {
+    t->tm_year = tm->tm_year;
+    return p;
+  }
+  p++;
+  t->tm_year = strtol (p, &p, 10);
+  if (t->tm_year < 70) /* year 2000+ */
+    t->tm_year += 100;
+  else if (t->tm_year > 1900)
+    t->tm_year -= 1900;
+  return p;
 }
 
 /* The regexes in a modern format */
@@ -532,6 +206,17 @@ struct range_regexp
   regex_t cooked;               /* compiled form */
 };
 
+enum
+{
+  RANGE_K_REL,
+  RANGE_K_ABS,
+  RANGE_K_LT,
+  RANGE_K_GT,
+  RANGE_K_BARE,
+  /* add new ones HERE */
+  RANGE_K_INVALID
+};
+
 static struct range_regexp range_regexps[] =
 {
   [RANGE_K_REL] = {.raw = RANGE_REL_RX, .lgrp = 1, .rgrp = 3, .ready = 0},
@@ -541,282 +226,35 @@ static struct range_regexp range_regexps[] =
   [RANGE_K_BARE] = {.raw = RANGE_BARE_RX, .lgrp = 1, .rgrp = 1, .ready = 0},
 };
 
-static int
-eat_range_by_regexp (pattern_t *pat, BUFFER *s, int kind, BUFFER *err)
-{
-  int regerr;
-  regmatch_t pmatch[RANGE_RX_GROUPS];
-  struct range_regexp *pspec = &range_regexps[kind];
+#define KILO 1024
+#define MEGA 1048576
+#define HMSG(h) (((h)->msgno) + 1)
+#define CTX_MSGNO(c) (HMSG((c)->hdrs[(c)->v2r[(c)->menu->current]]))
 
-  /* First time through, compile the big regexp */
-  if (!pspec->ready)
-  {
-    regerr = regcomp(&pspec->cooked, pspec->raw, REG_EXTENDED);
-    if (regerr)
-      return report_regerror(regerr, &pspec->cooked, err);
-    pspec->ready = 1;
-  }
+#define MUTT_MAXRANGE -1
 
-  /* Match the pattern buffer against the compiled regexp.
-   * No match means syntax error. */
-  regerr = regexec(&pspec->cooked, s->dptr, RANGE_RX_GROUPS, pmatch, 0);
-  if (regerr)
-    return report_regerror(regerr, &pspec->cooked, err);
+/* constants for parse_date_range() */
+#define MUTT_PDR_NONE  0x0000
+#define MUTT_PDR_MINUS 0x0001
+#define MUTT_PDR_PLUS  0x0002
+#define MUTT_PDR_WINDOW        0x0004
+#define MUTT_PDR_ABSOLUTE      0x0008
+#define MUTT_PDR_DONE  0x0010
+#define MUTT_PDR_ERROR 0x0100
+#define MUTT_PDR_ERRORDONE     (MUTT_PDR_ERROR | MUTT_PDR_DONE)
 
-  if (!is_context_available(s, pmatch, kind, err))
-    return RANGE_E_CTX;
+#define RANGE_DOT '.'
+#define RANGE_CIRCUM '^'
+#define RANGE_DOLLAR '$'
+#define RANGE_LT '<'
+#define RANGE_GT '>'
 
-  /* Snarf the contents of the two sides of the range. */
-  pat->min = scan_range_slot(s, pmatch, pspec->lgrp, RANGE_S_LEFT, kind);
-  pat->max = scan_range_slot(s, pmatch, pspec->rgrp, RANGE_S_RIGHT, kind);
-  mutt_debug (1, "pat->min=%d pat->max=%d\n", pat->min, pat->max);
-
-  /* Special case for a bare 0. */
-  if ((kind == RANGE_K_BARE) && (pat->min == 0) && (pat->max == 0))
-  {
-    if (!Context->menu)
-    {
-      strfcpy(err->data, _("No current message"), err->dsize);
-      return RANGE_E_CTX;
-    }
-    pat->min = pat->max = CTX_MSGNO(Context);
-  }
-
-  /* Since we don't enforce order, we must swap bounds if they're backward */
-  order_range(pat);
-
-  /* Slide pointer past the entire match. */
-  s->dptr += pmatch[0].rm_eo;
-  return RANGE_E_OK;
-}
-
-static int
-eat_message_range (pattern_t *pat, BUFFER *s, BUFFER *err)
-{
-  int skip_quote = 0;
-  int i_kind;
-
-  /* We need a Context for pretty much anything. */
-  if (!Context)
-  {
-    strfcpy(err->data, _("No Context"), err->dsize);
-    return -1;
-  }
-
-  /*
-   * If simple_search is set to "~m %s", the range will have double quotes
-   * around it...
-   */
-  if (*s->dptr == '"')
-  {
-    s->dptr++;
-    skip_quote = 1;
-  }
-
-  for (i_kind = 0; i_kind != RANGE_K_INVALID; ++i_kind)
-  {
-    switch (eat_range_by_regexp(pat, s, i_kind, err))
-    {
-    case RANGE_E_CTX:
-      /* This means it matched syntactically but lacked context.
-       * No point in continuing. */
-      break;
-    case RANGE_E_SYNTAX:
-      /* Try another syntax, then */
-      continue;
-    case RANGE_E_OK:
-      if (skip_quote && (*s->dptr == '"'))
-        s->dptr++;
-      SKIPWS (s->dptr);
-      return 0;
-    }
-  }
-  return -1;
-}
-
-static int eat_range (pattern_t *pat, BUFFER *s, BUFFER *err)
-{
-  char *tmp;
-  int do_exclusive = 0;
-  int skip_quote = 0;
-
-  /*
-   * If simple_search is set to "~m %s", the range will have double quotes
-   * around it...
-   */
-  if (*s->dptr == '"')
-  {
-    s->dptr++;
-    skip_quote = 1;
-  }
-  if (*s->dptr == '<')
-    do_exclusive = 1;
-  if ((*s->dptr != '-') && (*s->dptr != '<'))
-  {
-    /* range minimum */
-    if (*s->dptr == '>')
-    {
-      pat->max = MUTT_MAXRANGE;
-      pat->min = strtol (s->dptr + 1, &tmp, 0) + 1; /* exclusive range */
-    }
-    else
-      pat->min = strtol (s->dptr, &tmp, 0);
-    if (toupper ((unsigned char) *tmp) == 'K') /* is there a prefix? */
-    {
-      pat->min *= 1024;
-      tmp++;
-    }
-    else if (toupper ((unsigned char) *tmp) == 'M')
-    {
-      pat->min *= 1048576;
-      tmp++;
-    }
-    if (*s->dptr == '>')
-    {
-      s->dptr = tmp;
-      return 0;
-    }
-    if (*tmp != '-')
-    {
-      /* exact value */
-      pat->max = pat->min;
-      s->dptr = tmp;
-      return 0;
-    }
-    tmp++;
-  }
-  else
-  {
-    s->dptr++;
-    tmp = s->dptr;
-  }
-
-  if (isdigit ((unsigned char) *tmp))
-  {
-    /* range maximum */
-    pat->max = strtol (tmp, &tmp, 0);
-    if (toupper ((unsigned char) *tmp) == 'K')
-    {
-      pat->max *= 1024;
-      tmp++;
-    }
-    else if (toupper ((unsigned char) *tmp) == 'M')
-    {
-      pat->max *= 1048576;
-      tmp++;
-    }
-    if (do_exclusive)
-      (pat->max)--;
-  }
-  else
-    pat->max = MUTT_MAXRANGE;
-
-  if (skip_quote && *tmp == '"')
-    tmp++;
-
-  SKIPWS (tmp);
-  s->dptr = tmp;
-  return 0;
-}
-
-static const char *getDate (const char *s, struct tm *t, BUFFER *err)
-{
-  char *p;
-  time_t now = time (NULL);
-  struct tm *tm = localtime (&now);
-
-  t->tm_mday = strtol (s, &p, 10);
-  if (t->tm_mday < 1 || t->tm_mday > 31)
-  {
-    snprintf (err->data, err->dsize, _("Invalid day of month: %s"), s);
-    return NULL;
-  }
-  if (*p != '/')
-  {
-    /* fill in today's month and year */
-    t->tm_mon = tm->tm_mon;
-    t->tm_year = tm->tm_year;
-    return p;
-  }
-  p++;
-  t->tm_mon = strtol (p, &p, 10) - 1;
-  if (t->tm_mon < 0 || t->tm_mon > 11)
-  {
-    snprintf (err->data, err->dsize, _("Invalid month: %s"), p);
-    return NULL;
-  }
-  if (*p != '/')
-  {
-    t->tm_year = tm->tm_year;
-    return p;
-  }
-  p++;
-  t->tm_year = strtol (p, &p, 10);
-  if (t->tm_year < 70) /* year 2000+ */
-    t->tm_year += 100;
-  else if (t->tm_year > 1900)
-    t->tm_year -= 1900;
-  return p;
-}
-
-/* Ny  years
-   Nm  months
-   Nw  weeks
-   Nd  days */
-static const char *get_offset (struct tm *tm, const char *s, int sign)
-{
-  char *ps;
-  int offset = strtol (s, &ps, 0);
-  if ((sign < 0 && offset > 0) || (sign > 0 && offset < 0))
-    offset = -offset;
-
-  switch (*ps)
-  {
-    case 'y':
-      tm->tm_year += offset;
-      break;
-    case 'm':
-      tm->tm_mon += offset;
-      break;
-    case 'w':
-      tm->tm_mday += 7 * offset;
-      break;
-    case 'd':
-      tm->tm_mday += offset;
-      break;
-    default:
-      return s;
-  }
-  mutt_normalize_time (tm);
-  return (ps + 1);
-}
-
-static void adjust_date_range (struct tm *min, struct tm *max)
-{
-  if (min->tm_year > max->tm_year
-      || (min->tm_year == max->tm_year && min->tm_mon > max->tm_mon)
-      || (min->tm_year == max->tm_year && min->tm_mon == max->tm_mon
-       && min->tm_mday > max->tm_mday))
-  {
-    int tmp;
-
-    tmp = min->tm_year;
-    min->tm_year = max->tm_year;
-    max->tm_year = tmp;
-
-    tmp = min->tm_mon;
-    min->tm_mon = max->tm_mon;
-    max->tm_mon = tmp;
-
-    tmp = min->tm_mday;
-    min->tm_mday = max->tm_mday;
-    max->tm_mday = tmp;
-
-    min->tm_hour = min->tm_min = min->tm_sec = 0;
-    max->tm_hour = 23;
-    max->tm_min = max->tm_sec = 59;
-  }
-}
+/* range sides: left or right */
+enum
+{
+  RANGE_S_LEFT,
+  RANGE_S_RIGHT
+};
 
 static const char * parse_date_range (const char* pc, struct tm *min,
     struct tm *max, int haveMin, struct tm *baseMin, BUFFER *err)
@@ -899,18 +337,45 @@ static const char * parse_date_range (const char* pc, struct tm *min,
   return ((flag & MUTT_PDR_ERROR) ? NULL : pc);
 }
 
-static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
+static void adjust_date_range (struct tm *min, struct tm *max)
 {
-  BUFFER buffer;
-  struct tm min, max;
-  char *pexpr;
-
-  mutt_buffer_init (&buffer);
-  pexpr = s->dptr;
-  if (mutt_extract_token (&buffer, s, MUTT_TOKEN_COMMENT | MUTT_TOKEN_PATTERN) != 0
-      || !buffer.data)
+  if (min->tm_year > max->tm_year
+      || (min->tm_year == max->tm_year && min->tm_mon > max->tm_mon)
+      || (min->tm_year == max->tm_year && min->tm_mon == max->tm_mon
+       && min->tm_mday > max->tm_mday))
   {
-    snprintf (err->data, err->dsize, _("Error in expression: %s"), pexpr);
+    int tmp;
+
+    tmp = min->tm_year;
+    min->tm_year = max->tm_year;
+    max->tm_year = tmp;
+
+    tmp = min->tm_mon;
+    min->tm_mon = max->tm_mon;
+    max->tm_mon = tmp;
+
+    tmp = min->tm_mday;
+    min->tm_mday = max->tm_mday;
+    max->tm_mday = tmp;
+
+    min->tm_hour = min->tm_min = min->tm_sec = 0;
+    max->tm_hour = 23;
+    max->tm_min = max->tm_sec = 59;
+  }
+}
+
+static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
+{
+  BUFFER buffer;
+  struct tm min, max;
+  char *pexpr;
+
+  mutt_buffer_init (&buffer);
+  pexpr = s->dptr;
+  if (mutt_extract_token (&buffer, s, MUTT_TOKEN_COMMENT | MUTT_TOKEN_PATTERN) != 0
+      || !buffer.data)
+  {
+    snprintf (err->data, err->dsize, _("Error in expression: %s"), pexpr);
     return (-1);
   }
   if (!*buffer.data)
@@ -1037,15 +502,544 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
   return 0;
 }
 
-static int patmatch (const pattern_t* pat, const char* buf)
+static int eat_range (pattern_t *pat, BUFFER *s, BUFFER *err)
 {
-  if (pat->stringmatch)
-    return pat->ign_case ? !strcasestr (buf, pat->p.str) :
-                          !strstr (buf, pat->p.str);
-  else if (pat->groupmatch)
-    return !mutt_group_match (pat->p.g, buf);
+  char *tmp;
+  int do_exclusive = 0;
+  int skip_quote = 0;
+
+  /*
+   * If simple_search is set to "~m %s", the range will have double quotes
+   * around it...
+   */
+  if (*s->dptr == '"')
+  {
+    s->dptr++;
+    skip_quote = 1;
+  }
+  if (*s->dptr == '<')
+    do_exclusive = 1;
+  if ((*s->dptr != '-') && (*s->dptr != '<'))
+  {
+    /* range minimum */
+    if (*s->dptr == '>')
+    {
+      pat->max = MUTT_MAXRANGE;
+      pat->min = strtol (s->dptr + 1, &tmp, 0) + 1; /* exclusive range */
+    }
+    else
+      pat->min = strtol (s->dptr, &tmp, 0);
+    if (toupper ((unsigned char) *tmp) == 'K') /* is there a prefix? */
+    {
+      pat->min *= 1024;
+      tmp++;
+    }
+    else if (toupper ((unsigned char) *tmp) == 'M')
+    {
+      pat->min *= 1048576;
+      tmp++;
+    }
+    if (*s->dptr == '>')
+    {
+      s->dptr = tmp;
+      return 0;
+    }
+    if (*tmp != '-')
+    {
+      /* exact value */
+      pat->max = pat->min;
+      s->dptr = tmp;
+      return 0;
+    }
+    tmp++;
+  }
   else
-    return regexec (pat->p.rx, buf, 0, NULL, 0);
+  {
+    s->dptr++;
+    tmp = s->dptr;
+  }
+
+  if (isdigit ((unsigned char) *tmp))
+  {
+    /* range maximum */
+    pat->max = strtol (tmp, &tmp, 0);
+    if (toupper ((unsigned char) *tmp) == 'K')
+    {
+      pat->max *= 1024;
+      tmp++;
+    }
+    else if (toupper ((unsigned char) *tmp) == 'M')
+    {
+      pat->max *= 1048576;
+      tmp++;
+    }
+    if (do_exclusive)
+      (pat->max)--;
+  }
+  else
+    pat->max = MUTT_MAXRANGE;
+
+  if (skip_quote && *tmp == '"')
+    tmp++;
+
+  SKIPWS (tmp);
+  s->dptr = tmp;
+  return 0;
+}
+
+static int
+report_regerror(int regerr, regex_t *preg, BUFFER *err)
+{
+  size_t ds = err->dsize;
+
+  if (regerror(regerr, preg, err->data, ds) > ds)
+    mutt_debug (2, "warning: buffer too small for regerror\n");
+  /* The return value is fixed, exists only to shorten code at callsite */
+  return RANGE_E_SYNTAX;
+}
+
+static int
+is_context_available(BUFFER *s, regmatch_t pmatch[], int kind, BUFFER *err)
+{
+  char *context_loc;
+  const char *context_req_chars[] =
+  {
+    [RANGE_K_REL] = ".0123456789",
+    [RANGE_K_ABS] = ".",
+    [RANGE_K_LT] = "",
+    [RANGE_K_GT] = "",
+    [RANGE_K_BARE] = ".",
+  };
+
+  /* First decide if we're going to need the context at all.
+   * Relative patterns need it iff they contain a dot or a number.
+   * Absolute patterns only need it if they contain a dot. */
+  context_loc = strpbrk(s->dptr+pmatch[0].rm_so, context_req_chars[kind]);
+  if ((context_loc == NULL) || (context_loc >= &s->dptr[pmatch[0].rm_eo]))
+    return 1;
+
+  /* We need a current message.  Do we actually have one? */
+  if (Context && Context->menu)
+    return 1;
+
+  /* Nope. */
+  strfcpy(err->data, _("No current message"), err->dsize);
+  return 0;
+}
+
+static int
+scan_range_num (BUFFER *s, regmatch_t pmatch[], int group, int kind)
+{
+  int num;
+  unsigned char c;
+
+  /* this cast looks dangerous, but is already all over this code
+   * (explicit or not) */
+  num = (int)strtol(&s->dptr[pmatch[group].rm_so], NULL, 0);
+  c = (unsigned char)(s->dptr[pmatch[group].rm_eo - 1]);
+  if (toupper(c) == 'K')
+    num *= KILO;
+  else if (toupper(c) == 'M')
+    num *= MEGA;
+  switch (kind)
+  {
+  case RANGE_K_REL:
+    return num + CTX_MSGNO(Context);
+  case RANGE_K_LT:
+    return num - 1;
+  case RANGE_K_GT:
+    return num + 1;
+  default:
+    return num;
+  }
+}
+
+static int
+scan_range_slot (BUFFER *s, regmatch_t pmatch[], int grp,
+                 int side, int kind)
+{
+  unsigned char c;
+
+  /* This means the left or right subpattern was empty, e.g. ",." */
+  if ((pmatch[grp].rm_so == -1) || (pmatch[grp].rm_so == pmatch[grp].rm_eo))
+  {
+    if (side == RANGE_S_LEFT)
+      return 1;
+    else if (side == RANGE_S_RIGHT)
+      return Context->msgcount;
+  }
+  /* We have something, so determine what */
+  c = (unsigned char)(s->dptr[pmatch[grp].rm_so]);
+  switch (c)
+  {
+  case RANGE_CIRCUM:
+    return 1;
+  case RANGE_DOLLAR:
+    return Context->msgcount;
+  case RANGE_DOT:
+    return CTX_MSGNO(Context);
+  case RANGE_LT:
+  case RANGE_GT:
+    return scan_range_num(s, pmatch, grp+1, kind);
+  default:
+    /* Only other possibility: a number */
+    return scan_range_num(s, pmatch, grp, kind);
+  }
+}
+
+static void
+order_range (pattern_t *pat)
+{
+  int num;
+
+  if (pat->min <= pat->max)
+    return;
+  num = pat->min;
+  pat->min = pat->max;
+  pat->max = num;
+}
+
+static int
+eat_range_by_regexp (pattern_t *pat, BUFFER *s, int kind, BUFFER *err)
+{
+  int regerr;
+  regmatch_t pmatch[RANGE_RX_GROUPS];
+  struct range_regexp *pspec = &range_regexps[kind];
+
+  /* First time through, compile the big regexp */
+  if (!pspec->ready)
+  {
+    regerr = regcomp(&pspec->cooked, pspec->raw, REG_EXTENDED);
+    if (regerr)
+      return report_regerror(regerr, &pspec->cooked, err);
+    pspec->ready = 1;
+  }
+
+  /* Match the pattern buffer against the compiled regexp.
+   * No match means syntax error. */
+  regerr = regexec(&pspec->cooked, s->dptr, RANGE_RX_GROUPS, pmatch, 0);
+  if (regerr)
+    return report_regerror(regerr, &pspec->cooked, err);
+
+  if (!is_context_available(s, pmatch, kind, err))
+    return RANGE_E_CTX;
+
+  /* Snarf the contents of the two sides of the range. */
+  pat->min = scan_range_slot(s, pmatch, pspec->lgrp, RANGE_S_LEFT, kind);
+  pat->max = scan_range_slot(s, pmatch, pspec->rgrp, RANGE_S_RIGHT, kind);
+  mutt_debug (1, "pat->min=%d pat->max=%d\n", pat->min, pat->max);
+
+  /* Special case for a bare 0. */
+  if ((kind == RANGE_K_BARE) && (pat->min == 0) && (pat->max == 0))
+  {
+    if (!Context->menu)
+    {
+      strfcpy(err->data, _("No current message"), err->dsize);
+      return RANGE_E_CTX;
+    }
+    pat->min = pat->max = CTX_MSGNO(Context);
+  }
+
+  /* Since we don't enforce order, we must swap bounds if they're backward */
+  order_range(pat);
+
+  /* Slide pointer past the entire match. */
+  s->dptr += pmatch[0].rm_eo;
+  return RANGE_E_OK;
+}
+
+static int
+eat_message_range (pattern_t *pat, BUFFER *s, BUFFER *err)
+{
+  int skip_quote = 0;
+  int i_kind;
+
+  /* We need a Context for pretty much anything. */
+  if (!Context)
+  {
+    strfcpy(err->data, _("No Context"), err->dsize);
+    return -1;
+  }
+
+  /*
+   * If simple_search is set to "~m %s", the range will have double quotes
+   * around it...
+   */
+  if (*s->dptr == '"')
+  {
+    s->dptr++;
+    skip_quote = 1;
+  }
+
+  for (i_kind = 0; i_kind != RANGE_K_INVALID; ++i_kind)
+  {
+    switch (eat_range_by_regexp(pat, s, i_kind, err))
+    {
+    case RANGE_E_CTX:
+      /* This means it matched syntactically but lacked context.
+       * No point in continuing. */
+      break;
+    case RANGE_E_SYNTAX:
+      /* Try another syntax, then */
+      continue;
+    case RANGE_E_OK:
+      if (skip_quote && (*s->dptr == '"'))
+        s->dptr++;
+      SKIPWS (s->dptr);
+      return 0;
+    }
+  }
+  return -1;
+}
+
+static const struct pattern_flags
+{
+  int tag;     /* character used to represent this op */
+  int op;      /* operation to perform */
+  int class;
+  int (*eat_arg) (pattern_t *, BUFFER *, BUFFER *);
+}
+Flags[] =
+{
+  { 'A', MUTT_ALL,                     0,              NULL },
+  { 'b', MUTT_BODY,            MUTT_FULL_MSG,  eat_regexp },
+  { 'B', MUTT_WHOLE_MSG,               MUTT_FULL_MSG,  eat_regexp },
+  { 'c', MUTT_CC,                      0,              eat_regexp },
+  { 'C', MUTT_RECIPIENT,               0,              eat_regexp },
+  { 'd', MUTT_DATE,            0,              eat_date },
+  { 'D', MUTT_DELETED,         0,              NULL },
+  { 'e', MUTT_SENDER,          0,              eat_regexp },
+  { 'E', MUTT_EXPIRED,         0,              NULL },
+  { 'f', MUTT_FROM,            0,              eat_regexp },
+  { 'F', MUTT_FLAG,            0,              NULL },
+  { 'g', MUTT_CRYPT_SIGN,              0,              NULL },
+  { 'G', MUTT_CRYPT_ENCRYPT,   0,              NULL },
+  { 'h', MUTT_HEADER,          MUTT_FULL_MSG,  eat_regexp },
+  { 'H', MUTT_HORMEL,          0,              eat_regexp },
+  { 'i', MUTT_ID,                      0,              eat_regexp },
+  { 'k', MUTT_PGP_KEY,         0,              NULL },
+  { 'l', MUTT_LIST,            0,              NULL },
+  { 'L', MUTT_ADDRESS,         0,              eat_regexp },
+  { 'm', MUTT_MESSAGE,         0,              eat_message_range },
+  { 'n', MUTT_SCORE,           0,              eat_range },
+  { 'N', MUTT_NEW,                     0,              NULL },
+  { 'O', MUTT_OLD,                     0,              NULL },
+  { 'p', MUTT_PERSONAL_RECIP,  0,              NULL },
+  { 'P', MUTT_PERSONAL_FROM,   0,              NULL },
+  { 'Q', MUTT_REPLIED,         0,              NULL },
+  { 'r', MUTT_DATE_RECEIVED,   0,              eat_date },
+  { 'R', MUTT_READ,            0,              NULL },
+  { 's', MUTT_SUBJECT,         0,              eat_regexp },
+  { 'S', MUTT_SUPERSEDED,              0,              NULL },
+  { 't', MUTT_TO,                      0,              eat_regexp },
+  { 'T', MUTT_TAG,                     0,              NULL },
+  { 'u', MUTT_SUBSCRIBED_LIST, 0,              NULL },
+  { 'U', MUTT_UNREAD,          0,              NULL },
+  { 'v', MUTT_COLLAPSED,               0,              NULL },
+  { 'V', MUTT_CRYPT_VERIFIED,  0,              NULL },
+#ifdef USE_NNTP
+  { 'w', MUTT_NEWSGROUPS,              0,              eat_regexp },
+#endif
+  { 'x', MUTT_REFERENCE,               0,              eat_regexp },
+  { 'X', MUTT_MIMEATTACH,              0,              eat_range },
+  { 'y', MUTT_XLABEL,          0,              eat_regexp },
+#ifdef USE_NOTMUCH
+  { 'Y', MUTT_NOTMUCH_LABEL,   0,              eat_regexp },
+#endif
+  { 'z', MUTT_SIZE,            0,              eat_range },
+  { '=', MUTT_DUPLICATED,              0,              NULL },
+  { '$', MUTT_UNREFERENCED,    0,              NULL },
+  { 0,   0,                    0,              NULL }
+};
+
+static pattern_t *SearchPattern = NULL; /* current search pattern */
+static char LastSearch[STRING] = { 0 };        /* last pattern searched for */
+static char LastSearchExpn[LONG_STRING] = { 0 }; /* expanded version of
+                                                   LastSearch */
+
+
+/* if no uppercase letters are given, do a case-insensitive search */
+int mutt_which_case (const char *s)
+{
+  wchar_t w;
+  mbstate_t mb;
+  size_t l;
+
+  memset (&mb, 0, sizeof (mb));
+
+  for (; (l = mbrtowc (&w, s, MB_CUR_MAX, &mb)) != 0; s += l)
+  {
+    if (l == (size_t) -2)
+      continue; /* shift sequences */
+    if (l == (size_t) -1)
+      return 0; /* error; assume case-sensitive */
+    if (iswalpha ((wint_t) w) && iswupper ((wint_t) w))
+      return 0; /* case-sensitive */
+  }
+
+  return REG_ICASE; /* case-insensitive */
+}
+
+static int patmatch (const pattern_t* pat, const char* buf)
+{
+  if (pat->stringmatch)
+    return pat->ign_case ? !strcasestr (buf, pat->p.str) :
+                          !strstr (buf, pat->p.str);
+  else if (pat->groupmatch)
+    return !mutt_group_match (pat->p.g, buf);
+  else
+    return regexec (pat->p.rx, buf, 0, NULL, 0);
+}
+
+static int
+msg_search (CONTEXT *ctx, pattern_t* pat, int msgno)
+{
+  MESSAGE *msg = NULL;
+  STATE s;
+  FILE *fp = NULL;
+  long lng = 0;
+  int match = 0;
+  HEADER *h = ctx->hdrs[msgno];
+  char *buf;
+  size_t blen;
+#ifdef USE_FMEMOPEN
+  char *temp;
+  size_t tempsize;
+#else
+  char tempfile[_POSIX_PATH_MAX];
+  struct stat st;
+#endif
+
+  if ((msg = mx_open_message (ctx, msgno)) != NULL)
+  {
+    if (option (OPTTHOROUGHSRC))
+    {
+      /* decode the header / body */
+      memset (&s, 0, sizeof (s));
+      s.fpin = msg->fp;
+      s.flags = MUTT_CHARCONV;
+#ifdef USE_FMEMOPEN
+      s.fpout = open_memstream (&temp, &tempsize);
+      if (!s.fpout) {
+       mutt_perror (_("Error opening memstream"));
+       return 0;
+      }
+#else
+      mutt_mktemp (tempfile, sizeof (tempfile));
+      if ((s.fpout = safe_fopen (tempfile, "w+")) == NULL)
+      {
+       mutt_perror (tempfile);
+       return (0);
+      }
+#endif
+
+      if (pat->op != MUTT_BODY)
+       mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
+
+      if (pat->op != MUTT_HEADER)
+      {
+       mutt_parse_mime_message (ctx, h);
+
+       if (WithCrypto && (h->security & ENCRYPT)
+            && !crypt_valid_passphrase(h->security))
+       {
+         mx_close_message (ctx, &msg);
+         if (s.fpout)
+         {
+           safe_fclose (&s.fpout);
+#ifdef USE_FMEMOPEN
+            FREE(&temp);
+#else
+           unlink (tempfile);
+#endif
+         }
+         return (0);
+       }
+
+       fseeko (msg->fp, h->offset, 0);
+       mutt_body_handler (h->content, &s);
+      }
+
+#ifdef USE_FMEMOPEN
+      fclose (s.fpout);
+      lng = tempsize;
+
+      if (tempsize) {
+        fp = fmemopen (temp, tempsize, "r");
+        if (!fp) {
+          mutt_perror (_("Error re-opening memstream"));
+          return 0;
+        }
+      } else { /* fmemopen cannot handle empty buffers */
+        fp = safe_fopen ("/dev/null", "r");
+        if (!fp) {
+          mutt_perror (_("Error opening /dev/null"));
+          return 0;
+        }
+      }
+#else
+      fp = s.fpout;
+      fflush (fp);
+      fseek (fp, 0, 0);
+      fstat (fileno (fp), &st);
+      lng = (long) st.st_size;
+#endif
+    }
+    else
+    {
+      /* raw header / body */
+      fp = msg->fp;
+      if (pat->op != MUTT_BODY)
+      {
+       fseeko (fp, h->offset, 0);
+       lng = h->content->offset - h->offset;
+      }
+      if (pat->op != MUTT_HEADER)
+      {
+       if (pat->op == MUTT_BODY)
+         fseeko (fp, h->content->offset, 0);
+       lng += h->content->length;
+      }
+    }
+
+    blen = STRING;
+    buf = safe_malloc (blen);
+
+    /* search the file "fp" */
+    while (lng > 0)
+    {
+      if (pat->op == MUTT_HEADER)
+      {
+       if (*(buf = mutt_read_rfc822_line (fp, buf, &blen)) == '\0')
+         break;
+      }
+      else if (fgets (buf, blen - 1, fp) == NULL)
+       break; /* don't loop forever */
+      if (patmatch (pat, buf) == 0)
+      {
+       match = 1;
+       break;
+      }
+      lng -= mutt_strlen (buf);
+    }
+
+    FREE (&buf);
+
+    mx_close_message (ctx, &msg);
+
+    if (option (OPTTHOROUGHSRC))
+    {
+      safe_fclose (&fp);
+#ifdef USE_FMEMOPEN
+      if (tempsize)
+        FREE(&temp);
+#else
+      unlink (tempfile);
+#endif
+    }
+  }
+
+  return match;
 }
 
 static const struct pattern_flags *lookup_tag (char tag)
index 22cd6517ae695c246dfaa19b96675db4a07b7d6c..7161cf60579ff75c11b5bdf0c755cc7a8c368458 100644 (file)
@@ -69,109 +69,144 @@ extern int optind;
 static short dump_signatures = 0;
 static short dump_fingerprints = 0;
 
+static char gnupg_trustletter (int t)
+{
+  switch (t)
+  {
+    case 1: return 'n';
+    case 2: return 'm';
+    case 3: return 'f';
+  }
+  return 'q';
+}
 
-static void pgpring_find_candidates (char *ringfile, const char *hints[], int nhints);
-static void pgpring_dump_keyblock (pgp_key_t p);
+static void print_userid (const char *id)
+{
+  for (; id && *id; id++)
+  {
+    if (*id >= ' ' && *id <= 'z' && *id != ':')
+      putchar (*id);
+    else
+      printf ("\\x%02x", (*id) & 0xff);
+  }
+}
 
-int main (int argc, char * const argv[])
+static void print_fingerprint (pgp_key_t p)
 {
-  int c;
+  if (!p->fingerprint)
+    return;
 
-  short version = 2;
-  short secring = 0;
+  printf ("fpr:::::::::%s:\n", p->fingerprint);
+} /* print_fingerprint() */
 
-  const char *_kring = NULL;
-  char *env_pgppath, *env_home;
+static void pgpring_dump_signatures (pgp_sig_t *sig)
+{
+  for (; sig; sig = sig->next)
+  {
+    if (sig->sigtype == 0x10 || sig->sigtype == 0x11 ||
+       sig->sigtype == 0x12 || sig->sigtype == 0x13)
+      printf ("sig::::%08lX%08lX::::::%X:\n",
+             sig->sid1, sig->sid2, sig->sigtype);
+    else if (sig->sigtype == 0x20)
+      printf ("rev::::%08lX%08lX::::::%X:\n",
+             sig->sid1, sig->sid2, sig->sigtype);
+  }
+}
 
-  char pgppath[_POSIX_PATH_MAX];
-  char kring[_POSIX_PATH_MAX];
+static void pgpring_dump_keyblock (pgp_key_t p)
+{
+  pgp_uid_t *uid;
+  short first;
+  struct tm *tp;
+  time_t t;
 
-  while ((c = getopt (argc, argv, "f25sk:S")) != EOF)
+  for (; p; p = p->next)
   {
-    switch (c)
+    first = 1;
+
+    if (p->flags & KEYFLAG_SECRET)
     {
-      case 'S':
-      {
-       dump_signatures = 1;
-       break;
-      }
+      if (p->flags & KEYFLAG_SUBKEY)
+       printf ("ssb:");
+      else
+       printf ("sec:");
+    }
+    else
+    {
+      if (p->flags & KEYFLAG_SUBKEY)
+       printf ("sub:");
+      else
+       printf ("pub:");
+    }
 
-      case 'f':
-      {
-       dump_fingerprints = 1;
-       break;
-      }
+    if (p->flags & KEYFLAG_REVOKED)
+      putchar ('r');
+    if (p->flags & KEYFLAG_EXPIRED)
+      putchar ('e');
+    if (p->flags & KEYFLAG_DISABLED)
+      putchar ('d');
 
-      case 'k':
+    for (uid = p->address; uid; uid = uid->next, first = 0)
+    {
+      if (!first)
       {
-       _kring = optarg;
-       break;
+       printf ("uid:%c::::::::", gnupg_trustletter (uid->trust));
+       print_userid (uid->addr);
+       printf (":\n");
       }
-
-      case '2': case '5':
+      else
       {
-       version = c - '0';
-       break;
-      }
+       if (p->flags & KEYFLAG_SECRET)
+         putchar ('u');
+       else
+         putchar (gnupg_trustletter (uid->trust));
 
-      case 's':
-      {
-       secring = 1;
-       break;
+       t = p->gen_time;
+       tp = gmtime (&t);
+
+       printf (":%d:%d:%s:%04d-%02d-%02d::::", p->keylen, p->numalg, p->keyid,
+               1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday);
+
+       print_userid (uid->addr);
+       printf ("::");
+
+       if(pgp_canencrypt(p->numalg))
+         putchar ('e');
+       if(pgp_cansign(p->numalg))
+         putchar ('s');
+       if (p->flags & KEYFLAG_DISABLED)
+         putchar ('D');
+       printf (":\n");
+
+       if (dump_fingerprints)
+          print_fingerprint (p);
       }
 
-      default:
+      if (dump_signatures)
       {
-       fprintf (stderr, "usage: %s [-k <key ring> | [-2 | -5] [ -s] [-S] [-f]] [hints]\n",
-                argv[0]);
-       exit (1);
+       if (first) pgpring_dump_signatures (p->sigs);
+       pgpring_dump_signatures (uid->sigs);
       }
     }
   }
-
-  if (_kring)
-    strfcpy (kring, _kring, sizeof (kring));
-  else
-  {
-    if ((env_pgppath = getenv ("PGPPATH")))
-      strfcpy (pgppath, env_pgppath, sizeof (pgppath));
-    else if ((env_home = getenv ("HOME")))
-      snprintf (pgppath, sizeof (pgppath), "%s/.pgp", env_home);
-    else
-    {
-      fprintf (stderr, "%s: Can't determine your PGPPATH.\n", argv[0]);
-      exit (1);
-    }
-
-    if (secring)
-      snprintf (kring, sizeof (kring), "%s/secring.%s", pgppath, version == 2 ? "pgp" : "skr");
-    else
-      snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath, version == 2 ? "pgp" : "pkr");
-  }
-
-  pgpring_find_candidates (kring, (const char**) argv + optind, argc - optind);
-
-  return 0;
 }
 
-static char *binary_fingerprint_to_string (unsigned char *buff, size_t length)
+static int pgpring_string_matches_hint (const char *s, const char *hints[], int nhints)
 {
   int i;
-  char *fingerprint, *pf;
 
-  pf = fingerprint = safe_malloc ((length * 2) + 1);
+  if (!hints || !nhints)
+    return 1;
 
-  for (i = 0; i < length; i++)
+  for (i = 0; i < nhints; i++)
   {
-    sprintf (pf, "%02X", buff[i]);
-    pf += 2;
+    if (mutt_stristr (s, hints[i]) != NULL)
+      return 1;
   }
-  *pf = 0;
 
-  return fingerprint;
+  return 0;
 }
 
-
 /* The actual key ring parser */
 static void pgp_make_pgp2_fingerprint (unsigned char *buff,
                                        unsigned char *digest)
@@ -198,6 +233,23 @@ static void pgp_make_pgp2_fingerprint (unsigned char *buff,
   md5_finish_ctx (&ctx, digest);
 } /* pgp_make_pgp2_fingerprint() */
 
+static char *binary_fingerprint_to_string (unsigned char *buff, size_t length)
+{
+  int i;
+  char *fingerprint, *pf;
+
+  pf = fingerprint = safe_malloc ((length * 2) + 1);
+
+  for (i = 0; i < length; i++)
+  {
+    sprintf (pf, "%02X", buff[i]);
+    pf += 2;
+  }
+  *pf = 0;
+
+  return fingerprint;
+}
+
 static pgp_key_t pgp_parse_pgp2_key (unsigned char *buff, size_t l)
 {
   pgp_key_t p;
@@ -312,7 +364,6 @@ static void skip_bignum (unsigned char *buff, size_t l, size_t j,
     *toff = j;
 }
 
-
 static pgp_key_t pgp_parse_pgp3_key (unsigned char *buff, size_t l)
 {
   pgp_key_t p;
@@ -568,7 +619,6 @@ static int pgp_parse_pgp3_sig (unsigned char *buff, size_t l,
 
 }
 
-
 static int pgp_parse_sig (unsigned char *buff, size_t l,
                           pgp_key_t p, pgp_sig_t *sig)
 {
@@ -730,22 +780,6 @@ static pgp_key_t pgp_parse_keyblock (FILE * fp)
   return root;
 }
 
-static int pgpring_string_matches_hint (const char *s, const char *hints[], int nhints)
-{
-  int i;
-
-  if (!hints || !nhints)
-    return 1;
-
-  for (i = 0; i < nhints; i++)
-  {
-    if (mutt_stristr (s, hints[i]) != NULL)
-      return 1;
-  }
-
-  return 0;
-}
-
 /*
  * Go through the key ring file and look for keys with
  * matching IDs.
@@ -825,127 +859,86 @@ static void pgpring_find_candidates (char *ringfile, const char *hints[], int nh
 
 }
 
-static void print_userid (const char *id)
-{
-  for (; id && *id; id++)
-  {
-    if (*id >= ' ' && *id <= 'z' && *id != ':')
-      putchar (*id);
-    else
-      printf ("\\x%02x", (*id) & 0xff);
-  }
-}
-
-static void print_fingerprint (pgp_key_t p)
-{
-  if (!p->fingerprint)
-    return;
-
-  printf ("fpr:::::::::%s:\n", p->fingerprint);
-} /* print_fingerprint() */
-
-
-static void pgpring_dump_signatures (pgp_sig_t *sig)
+int main (int argc, char * const argv[])
 {
-  for (; sig; sig = sig->next)
-  {
-    if (sig->sigtype == 0x10 || sig->sigtype == 0x11 ||
-       sig->sigtype == 0x12 || sig->sigtype == 0x13)
-      printf ("sig::::%08lX%08lX::::::%X:\n",
-             sig->sid1, sig->sid2, sig->sigtype);
-    else if (sig->sigtype == 0x20)
-      printf ("rev::::%08lX%08lX::::::%X:\n",
-             sig->sid1, sig->sid2, sig->sigtype);
-  }
-}
+  int c;
 
+  short version = 2;
+  short secring = 0;
 
-static char gnupg_trustletter (int t)
-{
-  switch (t)
-  {
-    case 1: return 'n';
-    case 2: return 'm';
-    case 3: return 'f';
-  }
-  return 'q';
-}
+  const char *_kring = NULL;
+  char *env_pgppath, *env_home;
 
-static void pgpring_dump_keyblock (pgp_key_t p)
-{
-  pgp_uid_t *uid;
-  short first;
-  struct tm *tp;
-  time_t t;
+  char pgppath[_POSIX_PATH_MAX];
+  char kring[_POSIX_PATH_MAX];
 
-  for (; p; p = p->next)
+  while ((c = getopt (argc, argv, "f25sk:S")) != EOF)
   {
-    first = 1;
-
-    if (p->flags & KEYFLAG_SECRET)
-    {
-      if (p->flags & KEYFLAG_SUBKEY)
-       printf ("ssb:");
-      else
-       printf ("sec:");
-    }
-    else
-    {
-      if (p->flags & KEYFLAG_SUBKEY)
-       printf ("sub:");
-      else
-       printf ("pub:");
-    }
-
-    if (p->flags & KEYFLAG_REVOKED)
-      putchar ('r');
-    if (p->flags & KEYFLAG_EXPIRED)
-      putchar ('e');
-    if (p->flags & KEYFLAG_DISABLED)
-      putchar ('d');
-
-    for (uid = p->address; uid; uid = uid->next, first = 0)
+    switch (c)
     {
-      if (!first)
+      case 'S':
       {
-       printf ("uid:%c::::::::", gnupg_trustletter (uid->trust));
-       print_userid (uid->addr);
-       printf (":\n");
+       dump_signatures = 1;
+       break;
       }
-      else
-      {
-       if (p->flags & KEYFLAG_SECRET)
-         putchar ('u');
-       else
-         putchar (gnupg_trustletter (uid->trust));
-
-       t = p->gen_time;
-       tp = gmtime (&t);
 
-       printf (":%d:%d:%s:%04d-%02d-%02d::::", p->keylen, p->numalg, p->keyid,
-               1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday);
+      case 'f':
+      {
+       dump_fingerprints = 1;
+       break;
+      }
 
-       print_userid (uid->addr);
-       printf ("::");
+      case 'k':
+      {
+       _kring = optarg;
+       break;
+      }
 
-       if(pgp_canencrypt(p->numalg))
-         putchar ('e');
-       if(pgp_cansign(p->numalg))
-         putchar ('s');
-       if (p->flags & KEYFLAG_DISABLED)
-         putchar ('D');
-       printf (":\n");
+      case '2': case '5':
+      {
+       version = c - '0';
+       break;
+      }
 
-       if (dump_fingerprints)
-          print_fingerprint (p);
+      case 's':
+      {
+       secring = 1;
+       break;
       }
 
-      if (dump_signatures)
+      default:
       {
-       if (first) pgpring_dump_signatures (p->sigs);
-       pgpring_dump_signatures (uid->sigs);
+       fprintf (stderr, "usage: %s [-k <key ring> | [-2 | -5] [ -s] [-S] [-f]] [hints]\n",
+                argv[0]);
+       exit (1);
       }
     }
   }
+
+  if (_kring)
+    strfcpy (kring, _kring, sizeof (kring));
+  else
+  {
+    if ((env_pgppath = getenv ("PGPPATH")))
+      strfcpy (pgppath, env_pgppath, sizeof (pgppath));
+    else if ((env_home = getenv ("HOME")))
+      snprintf (pgppath, sizeof (pgppath), "%s/.pgp", env_home);
+    else
+    {
+      fprintf (stderr, "%s: Can't determine your PGPPATH.\n", argv[0]);
+      exit (1);
+    }
+
+    if (secring)
+      snprintf (kring, sizeof (kring), "%s/secring.%s", pgppath, version == 2 ? "pgp" : "skr");
+    else
+      snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath, version == 2 ? "pgp" : "pkr");
+  }
+
+  pgpring_find_candidates (kring, (const char**) argv + optind, argc - optind);
+
+  return 0;
 }
 
+
+
diff --git a/query.c b/query.c
index 06a64c427544a784861a31203175293d06692e22..c7f9152740b152002afdb365dc2e615e93fbd9ab 100644 (file)
--- a/query.c
+++ b/query.c
@@ -53,8 +53,6 @@ static const struct mapping_t QueryHelp[] = {
   { NULL,        0 }
 };
 
-static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf);
-
 static ADDRESS *result_to_addr (QUERY *r)
 {
   static ADDRESS *tmp;
@@ -254,58 +252,6 @@ static int query_tag (MUTTMENU *menu, int n, int m)
   return cur->tagged - ot;
 }
 
-int mutt_query_complete (char *buf, size_t buflen)
-{
-  QUERY *results = NULL;
-  ADDRESS *tmpa;
-
-  if (!QueryCmd)
-  {
-    mutt_error (_("Query command not defined."));
-    return 0;
-  }
-
-  results = run_query (buf, 1);
-  if (results)
-  {
-    /* only one response? */
-    if (results->next == NULL)
-    {
-      tmpa = result_to_addr (results);
-      mutt_addrlist_to_local (tmpa);
-      buf[0] = '\0';
-      rfc822_write_address (buf, buflen, tmpa, 0);
-      rfc822_free_address (&tmpa);
-      free_query (&results);
-      mutt_clear_error ();
-      return (0);
-    }
-    /* multiple results, choose from query menu */
-    query_menu (buf, buflen, results, 1);
-  }
-  return (0);
-}
-
-void mutt_query_menu (char *buf, size_t buflen)
-{
-  if (!QueryCmd)
-  {
-    mutt_error (_("Query command not defined."));
-    return;
-  }
-
-  if (buf == NULL)
-  {
-    char buffer[STRING] = "";
-
-    query_menu (buffer, sizeof (buffer), NULL, 0);
-  }
-  else
-  {
-    query_menu (buf, buflen, NULL, 1);
-  }
-}
-
 static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf)
 {
   MUTTMENU *menu;
@@ -539,3 +485,55 @@ static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf)
 
   mutt_menuDestroy (&menu);
 }
+int mutt_query_complete (char *buf, size_t buflen)
+{
+  QUERY *results = NULL;
+  ADDRESS *tmpa;
+
+  if (!QueryCmd)
+  {
+    mutt_error (_("Query command not defined."));
+    return 0;
+  }
+
+  results = run_query (buf, 1);
+  if (results)
+  {
+    /* only one response? */
+    if (results->next == NULL)
+    {
+      tmpa = result_to_addr (results);
+      mutt_addrlist_to_local (tmpa);
+      buf[0] = '\0';
+      rfc822_write_address (buf, buflen, tmpa, 0);
+      rfc822_free_address (&tmpa);
+      free_query (&results);
+      mutt_clear_error ();
+      return (0);
+    }
+    /* multiple results, choose from query menu */
+    query_menu (buf, buflen, results, 1);
+  }
+  return (0);
+}
+
+void mutt_query_menu (char *buf, size_t buflen)
+{
+  if (!QueryCmd)
+  {
+    mutt_error (_("Query command not defined."));
+    return;
+  }
+
+  if (buf == NULL)
+  {
+    char buffer[STRING] = "";
+
+    query_menu (buffer, sizeof (buffer), NULL, 0);
+  }
+  else
+  {
+    query_menu (buf, buflen, NULL, 1);
+  }
+}
+
index 317079dd3a319e84bafefaa88d1171ecd7dd82a8..eeba63685d8011aae0d0e538cd562f62f3fa3e12 100644 (file)
@@ -49,20 +49,6 @@ struct coord
   short r, c;
 };
 
-static REMAILER **mix_type2_list (size_t *l);
-static REMAILER *mix_new_remailer (void);
-static const char *mix_format_caps (REMAILER *r);
-static int mix_chain_add (MIXCHAIN *chain, const char *s, REMAILER **type2_list);
-static int mix_get_caps (const char *capstr);
-static void mix_add_entry (REMAILER ***, REMAILER *, size_t *, size_t *);
-static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num);
-static void mix_free_remailer (REMAILER **r);
-static void mix_free_type2_list (REMAILER ***ttlp);
-static void mix_redraw_ce (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int i, short selected);
-static void mix_redraw_chain (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int cur);
-static void mix_redraw_head (MIXCHAIN *);
-static void mix_screen_coordinates (REMAILER **type2_list, struct coord **, MIXCHAIN *, int);
-
 static int mix_get_caps (const char *capstr)
 {
   int caps = 0;
index 9812b21493c04a38160798928d756fea58064b8a..69c3eb0a0824296c62ee878b295bc9707c9ecf17 100644 (file)
--- a/rfc2231.c
+++ b/rfc2231.c
@@ -51,13 +51,6 @@ struct rfc2231_parameter
        *next;
 };
 
-static char *rfc2231_get_charset (char *, char *, size_t);
-static struct rfc2231_parameter *rfc2231_new_parameter (void);
-static void rfc2231_decode_one (char *, char *);
-static void rfc2231_free_parameter (struct rfc2231_parameter **);
-static void rfc2231_join_continuations (PARAMETER **, struct rfc2231_parameter *);
-static void rfc2231_list_insert (struct rfc2231_parameter **, struct rfc2231_parameter *);
-
 static void purge_empty_parameters (PARAMETER **headp)
 {
   PARAMETER *p, *q, **last;
@@ -77,117 +70,6 @@ static void purge_empty_parameters (PARAMETER **headp)
 }
 
 
-void rfc2231_decode_parameters (PARAMETER **headp)
-{
-  PARAMETER *head = NULL;
-  PARAMETER **last;
-  PARAMETER *p, *q;
-
-  struct rfc2231_parameter *conthead = NULL;
-  struct rfc2231_parameter *conttmp;
-
-  char *s, *t;
-  char charset[STRING];
-
-  int encoded;
-  int index;
-  short dirty = 0;     /* set to 1 when we may have created
-                        * empty parameters.
-                        */
-
-  if (!headp) return;
-
-  purge_empty_parameters (headp);
-
-  for (last = &head, p = *headp; p; p = q)
-  {
-    q = p->next;
-
-    if (!(s = strchr (p->attribute, '*')))
-    {
-
-      /*
-       * Using RFC 2047 encoding in MIME parameters is explicitly
-       * forbidden by that document.  Nevertheless, it's being
-       * generated by some software, including certain Lotus Notes to
-       * Internet Gateways.  So we actually decode it.
-       */
-
-      if (option (OPTRFC2047PARAMS) && p->value && strstr (p->value, "=?"))
-       rfc2047_decode (&p->value);
-      else if (AssumedCharset && *AssumedCharset)
-        convert_nonmime_string (&p->value);
-
-      *last = p;
-      last = &p->next;
-      p->next = NULL;
-    }
-    else if (*(s + 1) == '\0')
-    {
-      *s = '\0';
-
-      s = rfc2231_get_charset (p->value, charset, sizeof (charset));
-      rfc2231_decode_one (p->value, s);
-      mutt_convert_string (&p->value, charset, Charset, MUTT_ICONV_HOOK_FROM);
-      mutt_filter_unprintable (&p->value);
-
-      *last = p;
-      last = &p->next;
-      p->next = NULL;
-
-      dirty = 1;
-    }
-    else
-    {
-      *s = '\0'; s++; /* let s point to the first character of index. */
-      for (t = s; *t && isdigit ((unsigned char) *t); t++)
-       ;
-      encoded = (*t == '*');
-      *t = '\0';
-
-      index = atoi (s);
-
-      conttmp = rfc2231_new_parameter ();
-      conttmp->attribute = p->attribute;
-      conttmp->value = p->value;
-      conttmp->encoded = encoded;
-      conttmp->index = index;
-
-      p->attribute = NULL;
-      p->value = NULL;
-      FREE (&p);
-
-      rfc2231_list_insert (&conthead, conttmp);
-    }
-  }
-
-  if (conthead)
-  {
-    rfc2231_join_continuations (last, conthead);
-    dirty = 1;
-  }
-
-  *headp = head;
-
-  if (dirty)
-    purge_empty_parameters (headp);
-}
-
-static struct rfc2231_parameter *rfc2231_new_parameter (void)
-{
-  return safe_calloc (sizeof (struct rfc2231_parameter), 1);
-}
-
-static void rfc2231_free_parameter (struct rfc2231_parameter **p)
-{
-  if (*p)
-  {
-    FREE (&(*p)->attribute);
-    FREE (&(*p)->value);
-    FREE (p);          /* __FREE_CHECKED__ */
-  }
-}
-
 static char *rfc2231_get_charset (char *value, char *charset, size_t chslen)
 {
   char *t, *u;
@@ -227,6 +109,11 @@ static void rfc2231_decode_one (char *dest, char *src)
   *d = '\0';
 }
 
+static struct rfc2231_parameter *rfc2231_new_parameter (void)
+{
+  return safe_calloc (sizeof (struct rfc2231_parameter), 1);
+}
+
 /* insert parameter into an ordered list.
  *
  * Primary sorting key: attribute
@@ -254,6 +141,16 @@ static void rfc2231_list_insert (struct rfc2231_parameter **list,
   *last = par;
 }
 
+static void rfc2231_free_parameter (struct rfc2231_parameter **p)
+{
+  if (*p)
+  {
+    FREE (&(*p)->attribute);
+    FREE (&(*p)->value);
+    FREE (p);          /* __FREE_CHECKED__ */
+  }
+}
+
 /* process continuation parameters */
 
 static void rfc2231_join_continuations (PARAMETER **head,
@@ -309,6 +206,102 @@ static void rfc2231_join_continuations (PARAMETER **head,
   }
 }
 
+void rfc2231_decode_parameters (PARAMETER **headp)
+{
+  PARAMETER *head = NULL;
+  PARAMETER **last;
+  PARAMETER *p, *q;
+
+  struct rfc2231_parameter *conthead = NULL;
+  struct rfc2231_parameter *conttmp;
+
+  char *s, *t;
+  char charset[STRING];
+
+  int encoded;
+  int index;
+  short dirty = 0;     /* set to 1 when we may have created
+                        * empty parameters.
+                        */
+
+  if (!headp) return;
+
+  purge_empty_parameters (headp);
+
+  for (last = &head, p = *headp; p; p = q)
+  {
+    q = p->next;
+
+    if (!(s = strchr (p->attribute, '*')))
+    {
+
+      /*
+       * Using RFC 2047 encoding in MIME parameters is explicitly
+       * forbidden by that document.  Nevertheless, it's being
+       * generated by some software, including certain Lotus Notes to
+       * Internet Gateways.  So we actually decode it.
+       */
+
+      if (option (OPTRFC2047PARAMS) && p->value && strstr (p->value, "=?"))
+       rfc2047_decode (&p->value);
+      else if (AssumedCharset && *AssumedCharset)
+        convert_nonmime_string (&p->value);
+
+      *last = p;
+      last = &p->next;
+      p->next = NULL;
+    }
+    else if (*(s + 1) == '\0')
+    {
+      *s = '\0';
+
+      s = rfc2231_get_charset (p->value, charset, sizeof (charset));
+      rfc2231_decode_one (p->value, s);
+      mutt_convert_string (&p->value, charset, Charset, MUTT_ICONV_HOOK_FROM);
+      mutt_filter_unprintable (&p->value);
+
+      *last = p;
+      last = &p->next;
+      p->next = NULL;
+
+      dirty = 1;
+    }
+    else
+    {
+      *s = '\0'; s++; /* let s point to the first character of index. */
+      for (t = s; *t && isdigit ((unsigned char) *t); t++)
+       ;
+      encoded = (*t == '*');
+      *t = '\0';
+
+      index = atoi (s);
+
+      conttmp = rfc2231_new_parameter ();
+      conttmp->attribute = p->attribute;
+      conttmp->value = p->value;
+      conttmp->encoded = encoded;
+      conttmp->index = index;
+
+      p->attribute = NULL;
+      p->value = NULL;
+      FREE (&p);
+
+      rfc2231_list_insert (&conthead, conttmp);
+    }
+  }
+
+  if (conthead)
+  {
+    rfc2231_join_continuations (last, conthead);
+    dirty = 1;
+  }
+
+  *headp = head;
+
+  if (dirty)
+    purge_empty_parameters (headp);
+}
+
 int rfc2231_encode_string (char **pd)
 {
   int ext = 0, encode = 0;
index d348163873092689919a668f826f16f21f5583be..0e1fd1c64925bef63c3836dd9f6341fd75c2d420 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -67,8 +67,6 @@ extern char RFC822Specials[];
 
 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
 
-static void transform_to_7bit (BODY *a, FILE *fpin);
-
 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
 {
   int c, linelen = 0;
@@ -1034,6 +1032,60 @@ int mutt_lookup_mime_type (BODY *att, const char *path)
   return (type);
 }
 
+static void transform_to_7bit (BODY *a, FILE *fpin)
+{
+  char buff[_POSIX_PATH_MAX];
+  STATE s;
+  struct stat sb;
+
+  memset (&s, 0, sizeof (s));
+  for (; a; a = a->next)
+  {
+    if (a->type == TYPEMULTIPART)
+    {
+      if (a->encoding != ENC7BIT)
+       a->encoding = ENC7BIT;
+
+      transform_to_7bit (a->parts, fpin);
+    }
+    else if (mutt_is_message_type(a->type, a->subtype))
+    {
+      mutt_message_to_7bit (a, fpin);
+    }
+    else
+    {
+      a->noconv = 1;
+      a->force_charset = 1;
+
+      mutt_mktemp (buff, sizeof (buff));
+      if ((s.fpout = safe_fopen (buff, "w")) == NULL)
+      {
+       mutt_perror ("fopen");
+       return;
+      }
+      s.fpin = fpin;
+      mutt_decode_attachment (a, &s);
+      safe_fclose (&s.fpout);
+      FREE (&a->d_filename);
+      a->d_filename = a->filename;
+      a->filename = safe_strdup (buff);
+      a->unlink = 1;
+      if (stat (a->filename, &sb) == -1)
+      {
+       mutt_perror ("stat");
+       return;
+      }
+      a->length = sb.st_size;
+
+      mutt_update_encoding (a);
+      if (a->encoding == ENC8BIT)
+       a->encoding = ENCQUOTEDPRINTABLE;
+      else if(a->encoding == ENCBINARY)
+       a->encoding = ENCBASE64;
+    }
+  }
+}
+
 void mutt_message_to_7bit (BODY *a, FILE *fp)
 {
   char temp[_POSIX_PATH_MAX];
@@ -1107,60 +1159,6 @@ void mutt_message_to_7bit (BODY *a, FILE *fp)
   a->hdr->content = NULL;
 }
 
-static void transform_to_7bit (BODY *a, FILE *fpin)
-{
-  char buff[_POSIX_PATH_MAX];
-  STATE s;
-  struct stat sb;
-
-  memset (&s, 0, sizeof (s));
-  for (; a; a = a->next)
-  {
-    if (a->type == TYPEMULTIPART)
-    {
-      if (a->encoding != ENC7BIT)
-       a->encoding = ENC7BIT;
-
-      transform_to_7bit (a->parts, fpin);
-    }
-    else if (mutt_is_message_type(a->type, a->subtype))
-    {
-      mutt_message_to_7bit (a, fpin);
-    }
-    else
-    {
-      a->noconv = 1;
-      a->force_charset = 1;
-
-      mutt_mktemp (buff, sizeof (buff));
-      if ((s.fpout = safe_fopen (buff, "w")) == NULL)
-      {
-       mutt_perror ("fopen");
-       return;
-      }
-      s.fpin = fpin;
-      mutt_decode_attachment (a, &s);
-      safe_fclose (&s.fpout);
-      FREE (&a->d_filename);
-      a->d_filename = a->filename;
-      a->filename = safe_strdup (buff);
-      a->unlink = 1;
-      if (stat (a->filename, &sb) == -1)
-      {
-       mutt_perror ("stat");
-       return;
-      }
-      a->length = sb.st_size;
-
-      mutt_update_encoding (a);
-      if (a->encoding == ENC8BIT)
-       a->encoding = ENCQUOTEDPRINTABLE;
-      else if(a->encoding == ENCBINARY)
-       a->encoding = ENCBASE64;
-    }
-  }
-}
-
 /* determine which Content-Transfer-Encoding to use */
 static void mutt_set_encoding (BODY *b, CONTENT *info)
 {
index ed98e740d3d8e67a203db21602de50b14fe4348a..f06e10307525ae1d3371bd1f28333e51ae67871c 100644 (file)
--- a/sidebar.c
+++ b/sidebar.c
@@ -55,8 +55,6 @@ static int OpnIndex = -1;    /* Current (open) mailbox */
 static int HilIndex = -1;    /* Highlighted mailbox */
 static int BotIndex = -1;    /* Last mailbox visible in sidebar */
 
-static int select_next (void);
-
 /* The source of the sidebar divider character. */
 enum div_type
 {
@@ -428,6 +426,172 @@ static void sort_entries (void)
     unsort_entries ();
 }
 
+/**
+ * select_next - Selects the next unhidden mailbox
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int select_next (void)
+{
+  int entry = HilIndex;
+
+  if (!EntryCount || HilIndex < 0)
+    return 0;
+
+  do
+  {
+    entry++;
+    if (entry == EntryCount)
+      return 0;
+  } while (Entries[entry]->is_hidden);
+
+  HilIndex = entry;
+  return 1;
+}
+
+/**
+ * select_next_new - Selects the next new mailbox
+ *
+ * Search down the list of mail folders for one containing new mail.
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int select_next_new (void)
+{
+  int entry = HilIndex;
+
+  if (!EntryCount || HilIndex < 0)
+    return 0;
+
+  do
+  {
+    entry++;
+    if (entry == EntryCount)
+    {
+      if (option (OPTSIDEBARNEXTNEWWRAP))
+        entry = 0;
+      else
+        return 0;
+    }
+    if (entry == HilIndex)
+      return 0;
+  } while (!Entries[entry]->buffy->new &&
+           !Entries[entry]->buffy->msg_unread);
+
+  HilIndex = entry;
+  return 1;
+}
+
+/**
+ * select_prev - Selects the previous unhidden mailbox
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int select_prev (void)
+{
+  int entry = HilIndex;
+
+  if (!EntryCount || HilIndex < 0)
+    return 0;
+
+  do
+  {
+    entry--;
+    if (entry < 0)
+      return 0;
+  } while (Entries[entry]->is_hidden);
+
+  HilIndex = entry;
+  return 1;
+}
+
+/**
+ * select_prev_new - Selects the previous new mailbox
+ *
+ * Search up the list of mail folders for one containing new mail.
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int select_prev_new (void)
+{
+  int entry = HilIndex;
+
+  if (!EntryCount || HilIndex < 0)
+    return 0;
+
+  do
+  {
+    entry--;
+    if (entry < 0)
+    {
+      if (option (OPTSIDEBARNEXTNEWWRAP))
+        entry = EntryCount - 1;
+      else
+        return 0;
+    }
+    if (entry == HilIndex)
+      return 0;
+  } while (!Entries[entry]->buffy->new &&
+           !Entries[entry]->buffy->msg_unread);
+
+  HilIndex = entry;
+  return 1;
+}
+
+/**
+ * select_page_down - Selects the first entry in the next page of mailboxes
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int select_page_down (void)
+{
+  int orig_hil_index = HilIndex;
+
+  if (!EntryCount || BotIndex < 0)
+    return 0;
+
+  HilIndex = BotIndex;
+  select_next ();
+  /* If the rest of the entries are hidden, go up to the last unhidden one */
+  if (Entries[HilIndex]->is_hidden)
+    select_prev ();
+
+  return (orig_hil_index != HilIndex);
+}
+
+/**
+ * select_page_up - Selects the last entry in the previous page of mailboxes
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int select_page_up (void)
+{
+  int orig_hil_index = HilIndex;
+
+  if (!EntryCount || TopIndex < 0)
+    return 0;
+
+  HilIndex = TopIndex;
+  select_prev ();
+  /* If the rest of the entries are hidden, go down to the last unhidden one */
+  if (Entries[HilIndex]->is_hidden)
+    select_next ();
+
+  return (orig_hil_index != HilIndex);
+}
+
 /**
  * prepare_sidebar - Prepare the list of SBENTRYs for the sidebar display
  * @page_size:  The number of lines on a page
@@ -840,172 +1004,6 @@ void mutt_sb_draw (void)
   move (y, x);
 }
 
-/**
- * select_next - Selects the next unhidden mailbox
- *
- * Returns:
- *      1: Success
- *      0: Failure
- */
-static int select_next (void)
-{
-  int entry = HilIndex;
-
-  if (!EntryCount || HilIndex < 0)
-    return 0;
-
-  do
-  {
-    entry++;
-    if (entry == EntryCount)
-      return 0;
-  } while (Entries[entry]->is_hidden);
-
-  HilIndex = entry;
-  return 1;
-}
-
-/**
- * select_next_new - Selects the next new mailbox
- *
- * Search down the list of mail folders for one containing new mail.
- *
- * Returns:
- *      1: Success
- *      0: Failure
- */
-static int select_next_new (void)
-{
-  int entry = HilIndex;
-
-  if (!EntryCount || HilIndex < 0)
-    return 0;
-
-  do
-  {
-    entry++;
-    if (entry == EntryCount)
-    {
-      if (option (OPTSIDEBARNEXTNEWWRAP))
-        entry = 0;
-      else
-        return 0;
-    }
-    if (entry == HilIndex)
-      return 0;
-  } while (!Entries[entry]->buffy->new &&
-           !Entries[entry]->buffy->msg_unread);
-
-  HilIndex = entry;
-  return 1;
-}
-
-/**
- * select_prev - Selects the previous unhidden mailbox
- *
- * Returns:
- *      1: Success
- *      0: Failure
- */
-static int select_prev (void)
-{
-  int entry = HilIndex;
-
-  if (!EntryCount || HilIndex < 0)
-    return 0;
-
-  do
-  {
-    entry--;
-    if (entry < 0)
-      return 0;
-  } while (Entries[entry]->is_hidden);
-
-  HilIndex = entry;
-  return 1;
-}
-
-/**
- * select_prev_new - Selects the previous new mailbox
- *
- * Search up the list of mail folders for one containing new mail.
- *
- * Returns:
- *      1: Success
- *      0: Failure
- */
-static int select_prev_new (void)
-{
-  int entry = HilIndex;
-
-  if (!EntryCount || HilIndex < 0)
-    return 0;
-
-  do
-  {
-    entry--;
-    if (entry < 0)
-    {
-      if (option (OPTSIDEBARNEXTNEWWRAP))
-        entry = EntryCount - 1;
-      else
-        return 0;
-    }
-    if (entry == HilIndex)
-      return 0;
-  } while (!Entries[entry]->buffy->new &&
-           !Entries[entry]->buffy->msg_unread);
-
-  HilIndex = entry;
-  return 1;
-}
-
-/**
- * select_page_down - Selects the first entry in the next page of mailboxes
- *
- * Returns:
- *      1: Success
- *      0: Failure
- */
-static int select_page_down (void)
-{
-  int orig_hil_index = HilIndex;
-
-  if (!EntryCount || BotIndex < 0)
-    return 0;
-
-  HilIndex = BotIndex;
-  select_next ();
-  /* If the rest of the entries are hidden, go up to the last unhidden one */
-  if (Entries[HilIndex]->is_hidden)
-    select_prev ();
-
-  return (orig_hil_index != HilIndex);
-}
-
-/**
- * select_page_up - Selects the last entry in the previous page of mailboxes
- *
- * Returns:
- *      1: Success
- *      0: Failure
- */
-static int select_page_up (void)
-{
-  int orig_hil_index = HilIndex;
-
-  if (!EntryCount || TopIndex < 0)
-    return 0;
-
-  HilIndex = TopIndex;
-  select_prev ();
-  /* If the rest of the entries are hidden, go down to the last unhidden one */
-  if (Entries[HilIndex]->is_hidden)
-    select_next ();
-
-  return (orig_hil_index != HilIndex);
-}
-
 /**
  * mutt_sb_change_mailbox - Change the selected mailbox
  * @op: Operation code
diff --git a/smtp.c b/smtp.c
index 83123b8b268cb4a3492ba26d063033966bf3dcc3..6da74ccf9db9ca1b3243d3bf34ef9c9c15413c25 100644 (file)
--- a/smtp.c
+++ b/smtp.c
@@ -66,16 +66,6 @@ enum {
   CAPMAX
 };
 
-#ifdef USE_SASL
-static int smtp_auth (CONNECTION* conn);
-static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms);
-#else
-static int smtp_auth_plain (CONNECTION* conn);
-#endif
-
-static int smtp_fill_account (ACCOUNT* account);
-static int smtp_open (CONNECTION* conn);
-
 static int Esmtp = 0;
 static char* AuthMechs = NULL;
 static unsigned char Capabilities[(CAPMAX + 7)/ 8];
@@ -275,95 +265,6 @@ static int addresses_use_unicode(const ADDRESS* a)
 }
 
 
-int
-mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc,
-                const ADDRESS* bcc, const char *msgfile, int eightbit)
-{
-  CONNECTION *conn;
-  ACCOUNT account;
-  const char* envfrom;
-  char buf[1024];
-  int ret = -1;
-
-  /* it might be better to synthesize an envelope from from user and host
-   * but this condition is most likely arrived at accidentally */
-  if (EnvFrom)
-    envfrom = EnvFrom->mailbox;
-  else if (from)
-    envfrom = from->mailbox;
-  else
-  {
-    mutt_error (_("No from address given"));
-    return -1;
-  }
-
-  if (smtp_fill_account (&account) < 0)
-    return ret;
-
-  if (!(conn = mutt_conn_find (NULL, &account)))
-    return -1;
-
-  Esmtp = eightbit;
-
-  do
-  {
-    /* send our greeting */
-    if (( ret = smtp_open (conn)))
-      break;
-    FREE (&AuthMechs);
-
-    /* send the sender's address */
-    ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom);
-    if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME))
-    {
-      safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15);
-      ret += 14;
-    }
-    if (DsnReturn && mutt_bit_isset (Capabilities, DSN))
-      ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn);
-    if (mutt_bit_isset (Capabilities, SMTPUTF8) &&
-       (address_uses_unicode(envfrom) ||
-        addresses_use_unicode(to) ||
-        addresses_use_unicode(cc) ||
-        addresses_use_unicode(bcc)))
-      ret += snprintf (buf + ret, sizeof (buf) - ret, " SMTPUTF8");
-    safe_strncat (buf, sizeof (buf), "\r\n", 3);
-    if (mutt_socket_write (conn, buf) == -1)
-    {
-      ret = smtp_err_write;
-      break;
-    }
-    if ((ret = smtp_get_resp (conn)))
-      break;
-
-    /* send the recipient list */
-    if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc))
-        || (ret = smtp_rcpt_to (conn, bcc)))
-      break;
-
-    /* send the message data */
-    if ((ret = smtp_data (conn, msgfile)))
-      break;
-
-    mutt_socket_write (conn, "QUIT\r\n");
-
-    ret = 0;
-  }
-  while (0);
-
-  if (conn)
-    mutt_socket_close (conn);
-
-  if (ret == smtp_err_read)
-    mutt_error (_("SMTP session failed: read error"));
-  else if (ret == smtp_err_write)
-    mutt_error (_("SMTP session failed: write error"));
-  else if (ret == smtp_err_code)
-    mutt_error (_("Invalid server response"));
-
-  return ret;
-}
-
 static int smtp_fill_account (ACCOUNT* account)
 {
   static unsigned short SmtpPort = 0;
@@ -444,123 +345,7 @@ static int smtp_helo (CONNECTION* conn)
   return smtp_get_resp (conn);
 }
 
-static int smtp_open (CONNECTION* conn)
-{
-  int rc;
-
-  if (mutt_socket_open (conn))
-    return -1;
-
-  /* get greeting string */
-  if ((rc = smtp_get_resp (conn)))
-    return rc;
-
-  if ((rc = smtp_helo (conn)))
-    return rc;
-
-#ifdef USE_SSL
-  if (conn->ssf)
-    rc = MUTT_NO;
-  else if (option (OPTSSLFORCETLS))
-    rc = MUTT_YES;
-  else if (mutt_bit_isset (Capabilities, STARTTLS) &&
-           (rc = query_quadoption (OPT_SSLSTARTTLS,
-                                   _("Secure connection with TLS?"))) == MUTT_ABORT)
-    return rc;
-
-  if (rc == MUTT_YES)
-  {
-    if (mutt_socket_write (conn, "STARTTLS\r\n") < 0)
-      return smtp_err_write;
-    if ((rc = smtp_get_resp (conn)))
-      return rc;
-
-    if (mutt_ssl_starttls (conn))
-    {
-      mutt_error (_("Could not negotiate TLS connection"));
-      mutt_sleep (1);
-      return -1;
-    }
-
-    /* re-EHLO to get authentication mechanisms */
-    if ((rc = smtp_helo (conn)))
-      return rc;
-  }
-#endif
-
-  if (conn->account.flags & MUTT_ACCT_USER)
-  {
-    if (!mutt_bit_isset (Capabilities, AUTH))
-    {
-      mutt_error (_("SMTP server does not support authentication"));
-      mutt_sleep (1);
-      return -1;
-    }
-
-#ifdef USE_SASL
-    return smtp_auth (conn);
-#else
-    return smtp_auth_plain (conn);
-#endif /* USE_SASL */
-  }
-
-  return 0;
-}
-
 #ifdef USE_SASL
-static int smtp_auth (CONNECTION* conn)
-{
-  int r = SMTP_AUTH_UNAVAIL;
-
-  if (SmtpAuthenticators && *SmtpAuthenticators)
-  {
-    char* methods = safe_strdup (SmtpAuthenticators);
-    char* method;
-    char* delim;
-
-    for (method = methods; method; method = delim)
-    {
-      delim = strchr (method, ':');
-      if (delim)
-       *delim++ = '\0';
-      if (! method[0])
-       continue;
-
-      mutt_debug (2, "smtp_authenticate: Trying method %s\n", method);
-
-      r = smtp_auth_sasl (conn, method);
-
-      if (r == SMTP_AUTH_FAIL && delim)
-      {
-        mutt_error (_("%s authentication failed, trying next method"), method);
-        mutt_sleep (1);
-      }
-      else if (r != SMTP_AUTH_UNAVAIL)
-        break;
-    }
-
-    FREE (&methods);
-  }
-  else
-    r = smtp_auth_sasl (conn, AuthMechs);
-
-  if (r != SMTP_AUTH_SUCCESS)
-    mutt_account_unsetpass (&conn->account);
-
-  if (r == SMTP_AUTH_FAIL)
-  {
-    mutt_error (_("SASL authentication failed"));
-    mutt_sleep (1);
-  }
-  else if (r == SMTP_AUTH_UNAVAIL)
-  {
-    mutt_error (_("No authenticators available"));
-    mutt_sleep (1);
-  }
-
-  return r == SMTP_AUTH_SUCCESS ? 0 : -1;
-}
-
 static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist)
 {
   sasl_conn_t* saslconn;
@@ -662,6 +447,60 @@ fail:
   FREE (&buf);
   return SMTP_AUTH_FAIL;
 }
+
+static int smtp_auth (CONNECTION* conn)
+{
+  int r = SMTP_AUTH_UNAVAIL;
+
+  if (SmtpAuthenticators && *SmtpAuthenticators)
+  {
+    char* methods = safe_strdup (SmtpAuthenticators);
+    char* method;
+    char* delim;
+
+    for (method = methods; method; method = delim)
+    {
+      delim = strchr (method, ':');
+      if (delim)
+       *delim++ = '\0';
+      if (! method[0])
+       continue;
+
+      mutt_debug (2, "smtp_authenticate: Trying method %s\n", method);
+
+      r = smtp_auth_sasl (conn, method);
+
+      if (r == SMTP_AUTH_FAIL && delim)
+      {
+        mutt_error (_("%s authentication failed, trying next method"), method);
+        mutt_sleep (1);
+      }
+      else if (r != SMTP_AUTH_UNAVAIL)
+        break;
+    }
+
+    FREE (&methods);
+  }
+  else
+    r = smtp_auth_sasl (conn, AuthMechs);
+
+  if (r != SMTP_AUTH_SUCCESS)
+    mutt_account_unsetpass (&conn->account);
+
+  if (r == SMTP_AUTH_FAIL)
+  {
+    mutt_error (_("SASL authentication failed"));
+    mutt_sleep (1);
+  }
+  else if (r == SMTP_AUTH_UNAVAIL)
+  {
+    mutt_error (_("No authenticators available"));
+    mutt_sleep (1);
+  }
+
+  return r == SMTP_AUTH_SUCCESS ? 0 : -1;
+}
+
 #else /* USE_SASL */
 
 static int smtp_auth_plain(CONNECTION* conn)
@@ -720,3 +559,155 @@ error:
   return -1;
 }
 #endif /* USE_SASL */
+static int smtp_open (CONNECTION* conn)
+{
+  int rc;
+
+  if (mutt_socket_open (conn))
+    return -1;
+
+  /* get greeting string */
+  if ((rc = smtp_get_resp (conn)))
+    return rc;
+
+  if ((rc = smtp_helo (conn)))
+    return rc;
+
+#ifdef USE_SSL
+  if (conn->ssf)
+    rc = MUTT_NO;
+  else if (option (OPTSSLFORCETLS))
+    rc = MUTT_YES;
+  else if (mutt_bit_isset (Capabilities, STARTTLS) &&
+           (rc = query_quadoption (OPT_SSLSTARTTLS,
+                                   _("Secure connection with TLS?"))) == MUTT_ABORT)
+    return rc;
+
+  if (rc == MUTT_YES)
+  {
+    if (mutt_socket_write (conn, "STARTTLS\r\n") < 0)
+      return smtp_err_write;
+    if ((rc = smtp_get_resp (conn)))
+      return rc;
+
+    if (mutt_ssl_starttls (conn))
+    {
+      mutt_error (_("Could not negotiate TLS connection"));
+      mutt_sleep (1);
+      return -1;
+    }
+
+    /* re-EHLO to get authentication mechanisms */
+    if ((rc = smtp_helo (conn)))
+      return rc;
+  }
+#endif
+
+  if (conn->account.flags & MUTT_ACCT_USER)
+  {
+    if (!mutt_bit_isset (Capabilities, AUTH))
+    {
+      mutt_error (_("SMTP server does not support authentication"));
+      mutt_sleep (1);
+      return -1;
+    }
+
+#ifdef USE_SASL
+    return smtp_auth (conn);
+#else
+    return smtp_auth_plain (conn);
+#endif /* USE_SASL */
+  }
+
+  return 0;
+}
+
+int
+mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc,
+                const ADDRESS* bcc, const char *msgfile, int eightbit)
+{
+  CONNECTION *conn;
+  ACCOUNT account;
+  const char* envfrom;
+  char buf[1024];
+  int ret = -1;
+
+  /* it might be better to synthesize an envelope from from user and host
+   * but this condition is most likely arrived at accidentally */
+  if (EnvFrom)
+    envfrom = EnvFrom->mailbox;
+  else if (from)
+    envfrom = from->mailbox;
+  else
+  {
+    mutt_error (_("No from address given"));
+    return -1;
+  }
+
+  if (smtp_fill_account (&account) < 0)
+    return ret;
+
+  if (!(conn = mutt_conn_find (NULL, &account)))
+    return -1;
+
+  Esmtp = eightbit;
+
+  do
+  {
+    /* send our greeting */
+    if (( ret = smtp_open (conn)))
+      break;
+    FREE (&AuthMechs);
+
+    /* send the sender's address */
+    ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom);
+    if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME))
+    {
+      safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15);
+      ret += 14;
+    }
+    if (DsnReturn && mutt_bit_isset (Capabilities, DSN))
+      ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn);
+    if (mutt_bit_isset (Capabilities, SMTPUTF8) &&
+       (address_uses_unicode(envfrom) ||
+        addresses_use_unicode(to) ||
+        addresses_use_unicode(cc) ||
+        addresses_use_unicode(bcc)))
+      ret += snprintf (buf + ret, sizeof (buf) - ret, " SMTPUTF8");
+    safe_strncat (buf, sizeof (buf), "\r\n", 3);
+    if (mutt_socket_write (conn, buf) == -1)
+    {
+      ret = smtp_err_write;
+      break;
+    }
+    if ((ret = smtp_get_resp (conn)))
+      break;
+
+    /* send the recipient list */
+    if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc))
+        || (ret = smtp_rcpt_to (conn, bcc)))
+      break;
+
+    /* send the message data */
+    if ((ret = smtp_data (conn, msgfile)))
+      break;
+
+    mutt_socket_write (conn, "QUIT\r\n");
+
+    ret = 0;
+  }
+  while (0);
+
+  if (conn)
+    mutt_socket_close (conn);
+
+  if (ret == smtp_err_read)
+    mutt_error (_("SMTP session failed: read error"));
+  else if (ret == smtp_err_write)
+    mutt_error (_("SMTP session failed: write error"));
+  else if (ret == smtp_err_code)
+    mutt_error (_("Invalid server response"));
+
+  return ret;
+}
+
index c9693e13d8a0915fb75d18acb7909d288d42cb1d..4e67a88e10fd53d9be61e1ea6ed77fd71e67ec76 100644 (file)
--- a/thread.c
+++ b/thread.c
@@ -24,8 +24,6 @@
 #include <string.h>
 #include <ctype.h>
 
-static HASH *mutt_make_subj_hash (CONTEXT *ctx);
-
 #define VISIBLE(hdr, ctx) (hdr->virtual >= 0 || (hdr->collapsed && (!ctx->pattern || hdr->limited)))
 
 /* determine whether a is a descendant of b */
@@ -486,6 +484,24 @@ static void insert_message (THREAD **new, THREAD *newparent, THREAD *cur)
   *new = cur;
 }
 
+static HASH *mutt_make_subj_hash (CONTEXT *ctx)
+{
+  int i;
+  HEADER *hdr;
+  HASH *hash;
+
+  hash = hash_create (ctx->msgcount * 2, MUTT_HASH_ALLOW_DUPS);
+
+  for (i = 0; i < ctx->msgcount; i++)
+  {
+    hdr = ctx->hdrs[i];
+    if (hdr->env->real_subj)
+      hash_insert (hash, hdr->env->real_subj, hdr);
+  }
+
+  return hash;
+}
+
 /* thread by subject things that didn't get threaded by message-id */
 static void pseudo_threads (CONTEXT *ctx)
 {
@@ -1346,24 +1362,6 @@ HASH *mutt_make_id_hash (CONTEXT *ctx)
   return hash;
 }
 
-static HASH *mutt_make_subj_hash (CONTEXT *ctx)
-{
-  int i;
-  HEADER *hdr;
-  HASH *hash;
-
-  hash = hash_create (ctx->msgcount * 2, MUTT_HASH_ALLOW_DUPS);
-
-  for (i = 0; i < ctx->msgcount; i++)
-  {
-    hdr = ctx->hdrs[i];
-    if (hdr->env->real_subj)
-      hash_insert (hash, hdr->env->real_subj, hdr);
-  }
-
-  return hash;
-}
-
 static void clean_references (THREAD *brk, THREAD *cur)
 {
   THREAD *p;