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.
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 */
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;
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.
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;
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;
}
#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;
-}
#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)
} 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)
{
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,
}
/* 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()
(unsigned long) menu, 0);
}
-
/* return values:
*
* 1 message should be postponed
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;
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)
{
}
-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;
}
/**
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 },
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.
* -1 - we couldn't drop privileges.
*
*/
-
-
static int
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
* BEGIN_PRIVILEGES () won't return if an error occurs.
*
*/
-
static void
BEGIN_PRIVILEGED (void)
{
* END_PRIVILEGED () won't return if an error occurs.
*
*/
-
static void
END_PRIVILEGED (void)
{
}
#ifdef DL_STANDALONE
-
/*
* Usage information.
*
* This function doesn't return.
*
*/
-
static void
usage (const char *av0)
{
exit (DL_EX_ERROR);
}
-
#endif
/*
*
* tlr, Jul 15 1998
*/
-
static int
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.
*
* at least _POSIX_PATH_MAX characters.
*
*/
-
static void
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
* The final path is written to d.
*
*/
-
static int
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.
*
* dotlock_prepare () before using this function!
*
*/
-
-#define HARDMAXATTEMPTS 10
-
static int
dotlock_lock (const char *realpath)
{
(int) getpid ());
snprintf (lockfile, sizeof (lockfile), "%s.lock", realpath);
-
BEGIN_PRIVILEGED ();
unlink (nfslockfile);
{
END_PRIVILEGED ();
-
if (errno != EAGAIN)
{
return DL_EX_ERROR;
}
-
BEGIN_PRIVILEGED ();
}
END_PRIVILEGED ();
-
close (fd);
while (hard_count++ < HARDMAXATTEMPTS)
return DL_EX_OK;
}
-
/*
* Unlock a file.
*
* The same comment as for dotlock_lock () applies here.
*
*/
-
static int
dotlock_unlock (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
+
#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 */
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);
-}
#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",
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;
}
}
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;
}
+
#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;
}
}
+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
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)
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);
}
-/* 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;
-}
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(). */
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;
}
#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");
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)
{
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)
{
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
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)
{
/* Set standard defaults */
for (i = 0; MuttVars[i].option; i++)
{
- mutt_set_default (&MuttVars[i]);
+ set_default (&MuttVars[i]);
mutt_restore_default (&MuttVars[i]);
}
}
#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;
#include "lib.h"
-static int mutt_atol (const char *str, long *dst);
-
static const struct sysexits
{
int v;
}
#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;
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.
#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
{
}
}
+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:
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
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];
#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().
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)
#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;
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.
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)
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;
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
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;
+}
+
+
#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;
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)
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)
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(). */
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];
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
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 ();
+}
+
/* 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)
}
}
+/* 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
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);
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;
}
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.
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;
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)
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;
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)
{
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");
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[] =
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);
-}
#define CERTERR_SIGNERNOTCA 32
#define CERTERR_INSECUREALG 64
+#define CERT_SEP "-----BEGIN"
+
/* deprecated types compatibility */
#ifndef HAVE_GNUTLS_CERTIFICATE_CREDENTIALS_T
}
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;
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;
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);
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))
{
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.
*/
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;
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;
+}
+
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;
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;
+}
+
#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)
{
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
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)
{
#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;
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
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
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,
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;
#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;
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 */
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},
[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)
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)
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)
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)
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;
*toff = j;
}
-
static pgp_key_t pgp_parse_pgp3_key (unsigned char *buff, size_t l)
{
pgp_key_t p;
}
-
static int pgp_parse_sig (unsigned char *buff, size_t l,
pgp_key_t p, pgp_sig_t *sig)
{
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.
}
-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;
}
+
+
{ 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;
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;
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);
+ }
+}
+
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;
*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;
}
-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;
*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
*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,
}
}
+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;
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;
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];
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)
{
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
{
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
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
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];
}
-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;
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;
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)
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;
+}
+
#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 */
*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)
{
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;