Commit 8c5fc80b authored by Thomas Roessler's avatar Thomas Roessler

Advanced threading, v 5.1. From Daniel Eisenbud

<eisenbud@cs.swarthmore.edu>.
parent ea35088a
......@@ -535,8 +535,10 @@ void mutt_enter_command (void)
BUFFER err, token;
char buffer[LONG_STRING], errbuf[SHORT_STRING];
int r;
int old_strictthreads = option (OPTSTRICTTHREADS);
int old_sortre = option (OPTSORTRE);
int old_strictthreads = option (OPTSTRICTTHREADS);
int old_sortre = option (OPTSORTRE);
int old_hidemissing = option (OPTHIDEMISSING);
int old_threadreceived = option (OPTTHREADRECEIVED);
buffer[0] = 0;
if (mutt_get_field (":", buffer, sizeof (buffer), M_COMMAND) != 0 || !buffer[0])
......@@ -557,7 +559,9 @@ void mutt_enter_command (void)
mutt_error ("%s", errbuf);
}
if (option (OPTSTRICTTHREADS) != old_strictthreads ||
option (OPTSORTRE) != old_sortre)
option (OPTSORTRE) != old_sortre ||
option (OPTHIDEMISSING) != old_hidemissing ||
option (OPTTHREADRECEIVED) != old_threadreceived)
set_option (OPTNEEDRESORT);
}
......
......@@ -107,12 +107,12 @@ void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num)
{
format_flag flag = M_FORMAT_MAKEPRINT | M_FORMAT_ARROWCURSOR | M_FORMAT_INDEX;
int edgemsgno, reverse = Sort & SORT_REVERSE;
HEADER *tmp, *h = Context->hdrs[Context->v2r[num]];
HEADER *h = Context->hdrs[Context->v2r[num]];
THREAD *tmp;
if ((Sort & SORT_MASK) == SORT_THREADS && h->tree)
{
flag |= M_FORMAT_TREE; /* display the thread tree */
if (h->display_subject)
flag |= M_FORMAT_FORCESUBJ;
else
......@@ -127,26 +127,32 @@ void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num)
else
edgemsgno = Context->v2r[menu->top];
for (tmp = h->parent; tmp; tmp = tmp->parent)
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->msgno > edgemsgno : tmp->msgno < edgemsgno)
if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
{
flag |= M_FORMAT_FORCESUBJ;
break;
}
else if (tmp->virtual >= 0)
else if (tmp->message->virtual >= 0)
break;
}
if ((flag & M_FORMAT_FORCESUBJ) && h->prev)
if (flag & M_FORMAT_FORCESUBJ)
{
for (tmp = h->prev; tmp; tmp = tmp->prev)
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->msgno > edgemsgno : tmp->msgno < edgemsgno)
if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno)
break;
else if (tmp->virtual >= 0)
else if (tmp->message->virtual >= 0)
{
flag &= ~M_FORMAT_FORCESUBJ;
break;
......@@ -305,21 +311,21 @@ static void update_index (MUTTMENU *menu, CONTEXT *ctx, int check,
/* if the mailbox was reopened, need to rethread from scratch */
mutt_sort_headers (Context, (check == M_REOPENED));
/* uncollapse threads with new mail */
if ((Sort & SORT_MASK) == SORT_THREADS)
{
if (check == M_REOPENED)
{
HEADER *h;
THREAD *h, *j;
h = Context->tree;
Context->collapsed = 0;
while (h)
for (h = Context->tree; h; h = h->next)
{
mutt_uncollapse_thread (Context, h);
h = h->next;
for (j = h; !j->message; j = j->child)
;
mutt_uncollapse_thread (Context, j->message);
}
mutt_set_virtual (Context);
}
......@@ -793,7 +799,8 @@ int mutt_index_menu (void)
else
menu->current = 0;
menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
mutt_linearize_tree (Context, 0);
if (Sort & SORT_THREADS)
mutt_linearize_tree (Context, 0);
}
break;
......@@ -1537,6 +1544,7 @@ int mutt_index_menu (void)
{
HEADER *h, *base;
THREAD *thread, *top;
int final;
if (CURHDR->collapsed)
......@@ -1548,10 +1556,14 @@ int mutt_index_menu (void)
base = Context->hdrs[Context->v2r[final]];
h = Context->tree;
top = Context->tree;
Context->collapsed = !Context->collapsed;
while (h)
while ((thread = top) != NULL)
{
while (!thread->message)
thread = thread->child;
h = thread->message;
if (h->collapsed != Context->collapsed)
{
if (h->collapsed)
......@@ -1559,7 +1571,7 @@ int mutt_index_menu (void)
else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
mutt_collapse_thread (Context, h);
}
h = h->next;
top = top->next;
}
mutt_set_virtual (Context);
......@@ -1573,8 +1585,8 @@ int mutt_index_menu (void)
}
menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
break;
}
break;
/* --------------------------------------------------------------------
* These functions are invoked directly from the internal-pager
......
......@@ -240,10 +240,9 @@ void mutt_tag_set_flag (int flag, int bf)
if (Context->hdrs[Context->v2r[j]]->tagged)
mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], flag, bf);
}
int mutt_thread_set_flag (HEADER *cur, int flag, int bf, int subthread)
int mutt_thread_set_flag (HEADER *hdr, int flag, int bf, int subthread)
{
HEADER *start;
THREAD *start, *cur = hdr->thread;
if ((Sort & SORT_MASK) != SORT_THREADS)
{
......@@ -256,12 +255,17 @@ int mutt_thread_set_flag (HEADER *cur, int flag, int bf, int subthread)
cur = cur->parent;
start = cur;
mutt_set_flag (Context, cur, flag, bf);
if (cur->message)
mutt_set_flag (Context, cur->message, flag, bf);
if ((cur = cur->child) == NULL)
return (0);
FOREVER
{
mutt_set_flag (Context, cur, flag, bf);
if (cur->message)
mutt_set_flag (Context, cur->message, flag, bf);
if (cur->child)
cur = cur->child;
else if (cur->next)
......
......@@ -411,12 +411,12 @@ hdr_format_str (char *dest,
case 'e':
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, mutt_msgno_in_thread(hdr) + 1);
snprintf (dest, destlen, fmt, mutt_messages_in_thread(hdr, 1));
break;
case 'E':
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, mutt_messages_in_thread(hdr));
snprintf (dest, destlen, fmt, mutt_messages_in_thread(hdr, 0));
break;
case 'f':
......@@ -653,11 +653,13 @@ hdr_format_str (char *dest,
i = 1; /* reduce reuse recycle */
htmp = NULL;
if (flags & M_FORMAT_TREE
&& (hdr->prev && hdr->prev->env->x_label))
htmp = hdr->prev;
&& (hdr->thread->prev && hdr->thread->prev->message
&& hdr->thread->prev->message->env->x_label))
htmp = hdr->thread->prev->message;
else if (flags & M_FORMAT_TREE
&& (hdr->parent && hdr->parent->env->x_label))
htmp = hdr->parent;
&& (hdr->thread->parent && hdr->thread->parent->message
&& hdr->thread->parent->message->env->x_label))
htmp = hdr->thread->parent->message;
if (htmp && mutt_strcasecmp (hdr->env->x_label,
htmp->env->x_label) == 0)
i = 0;
......@@ -674,7 +676,7 @@ hdr_format_str (char *dest,
mutt_format_s (dest, destlen, prefix, "");
break;
default:
snprintf (dest, destlen, "%%%s%c", prefix, op);
break;
......
......@@ -53,7 +53,7 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
char tempfile[_POSIX_PATH_MAX];
int msgno;
IMAP_HEADER h;
int rc, mfhrc;
int rc, mfhrc, oldmsgcount;
int fetchlast = 0;
const char *want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE IN-REPLY-TO REPLY-TO LINES X-LABEL";
......@@ -120,6 +120,8 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
memset (&h, 0, sizeof (h));
h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA));
oldmsgcount = ctx->msgcount;
/* this DO loop does two things:
* 1. handles untagged messages, so we can try again on the same msg
* 2. fetches the tagged response at the end of the last message.
......@@ -167,11 +169,14 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
/* content built as a side-effect of mutt_read_rfc822_header */
ctx->hdrs[msgno]->content->length = h.content_length;
mx_update_context (ctx); /* increments ->msgcount */
ctx->msgcount++;
}
while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) ||
((msgno + 1) >= fetchlast)));
if (ctx->msgcount > oldmsgcount)
mx_update_context (ctx, ctx->msgcount - oldmsgcount);
if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK)))
{
imap_free_header_data ((void**) &h.data);
......@@ -343,15 +348,15 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
* mutt_free_envelope gets called, but keep their spots in the hash. This
* confuses threading. Alternatively we could try to merge the new
* envelope into the old one. Also messy and lowlevel. */
if (h->env->message_id)
if (ctx->id_hash && h->env->message_id)
hash_delete (ctx->id_hash, h->env->message_id, h, NULL);
if (h->env->real_subj)
if (ctx->subj_hash && h->env->real_subj)
hash_delete (ctx->subj_hash, h->env->real_subj, h, NULL);
mutt_free_envelope (&h->env);
h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
if (h->env->message_id)
if (ctx->id_hash && h->env->message_id)
hash_insert (ctx->id_hash, h->env->message_id, h, 0);
if (h->env->real_subj)
if (ctx->subj_hash && h->env->real_subj)
hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
/* see above. We want the new status in h->read, so we unset it manually
......
......@@ -640,6 +640,13 @@ struct option_t MuttVars[] = {
** affect the generation of Message-IDs, and it will not lead to the
** cut-off of first-level domains.
*/
{ "hide_missing", DT_BOOL, R_NONE, OPTHIDEMISSING, 1 },
/*
** .pp
** When set, mutt will not indicate the presence of missing messages
** whose ancestors neither have siblings nor are in the current mailbox,
** in the thread tree.
*/
{ "history", DT_NUM, R_NONE, UL &HistSize, 10 },
/*
** .pp
......@@ -2160,6 +2167,12 @@ struct option_t MuttVars[] = {
** .pp
** Note that $$indent_string is ignored when this option is set.
*/
{ "thread_received", DT_BOOL, R_NONE, OPTTHREADRECEIVED, 0 },
/*
** .pp
** When set, mutt uses the date received rather than the date sent
** to thread messages by subject.
*/
{ "thorough_search", DT_BOOL, R_NONE, OPTTHOROUGHSRC, 0 },
/*
** .pp
......
......@@ -82,7 +82,7 @@ int mmdf_parse_mailbox (CONTEXT *ctx)
{
char buf[HUGE_STRING];
char return_path[LONG_STRING];
int count = 0;
int count = 0, oldmsgcount = ctx->msgcount;
int lines;
time_t t, tz;
long loc, tmploc;
......@@ -138,6 +138,7 @@ int mmdf_parse_mailbox (CONTEXT *ctx)
if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
{
/* TODO: memory leak??? */
dprint (1, (debugfile, "mmdf_parse_mailbox: unexpected EOF\n"));
break;
}
......@@ -201,7 +202,7 @@ int mmdf_parse_mailbox (CONTEXT *ctx)
if (!hdr->env->from)
hdr->env->from = rfc822_cpy_adr (hdr->env->return_path);
mx_update_context (ctx);
ctx->msgcount++;
if(ctx->magic == M_KENDRA && feof(ctx->fp))
break;
}
......@@ -213,7 +214,10 @@ int mmdf_parse_mailbox (CONTEXT *ctx)
}
}
return 0;
if (ctx->msgcount > oldmsgcount)
mx_update_context (ctx, ctx->msgcount - oldmsgcount);
return (0);
}
/* Note that this function is also called when new mail is appended to the
......@@ -359,7 +363,7 @@ int mbox_parse_mailbox (CONTEXT *ctx)
}
}
mx_update_context (ctx);
ctx->msgcount++;
if (!curhdr->env->return_path && return_path[0])
curhdr->env->return_path = rfc822_parse_adrlist (curhdr->env->return_path, return_path);
......@@ -392,6 +396,8 @@ int mbox_parse_mailbox (CONTEXT *ctx)
if (!PREV->lines)
PREV->lines = lines ? lines - 1 : 0;
mx_update_context (ctx, count);
}
return (0);
......@@ -1074,8 +1080,11 @@ int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint)
old_msgcount = 0;
/* simulate a close */
hash_destroy (&ctx->id_hash, NULL);
hash_destroy (&ctx->subj_hash, NULL);
if (ctx->id_hash)
hash_destroy (&ctx->id_hash, NULL);
if (ctx->subj_hash)
hash_destroy (&ctx->subj_hash, NULL);
mutt_clear_threads (ctx);
safe_free ((void **) &ctx->v2r);
if (ctx->readonly)
{
......@@ -1100,8 +1109,8 @@ int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint)
ctx->unread = 0;
ctx->flagged = 0;
ctx->changed = 0;
ctx->id_hash = hash_create (1031);
ctx->subj_hash = hash_create (1031);
ctx->id_hash = NULL;
ctx->subj_hash = NULL;
switch (ctx->magic)
{
......
......@@ -100,6 +100,9 @@ static void print_enriched_string (int attr, unsigned char *s, int do_color)
case M_TREE_HIDDEN:
addch ('&');
break;
case M_TREE_MISSING:
addch ('?');
break;
}
s++, n--;
}
......
......@@ -668,6 +668,8 @@ static int maildir_parse_dir(CONTEXT *ctx, struct maildir ***last,
static void maildir_add_to_context(CONTEXT *ctx, struct maildir *md)
{
int oldmsgcount = ctx->msgcount;
while(md)
{
......@@ -692,10 +694,13 @@ static void maildir_add_to_context(CONTEXT *ctx, struct maildir *md)
md->h->content->length + md->h->content->offset - md->h->content->hdr_offset;
md->h = NULL;
mx_update_context(ctx);
ctx->msgcount++;
}
md = md->next;
}
if (ctx->msgcount > oldmsgcount)
mx_update_context (ctx, ctx->msgcount - oldmsgcount);
}
static void maildir_move_to_context(CONTEXT *ctx, struct maildir **md)
......
......@@ -154,7 +154,8 @@ typedef enum
#define M_TREE_RARROW 7
#define M_TREE_STAR 8
#define M_TREE_HIDDEN 9
#define M_TREE_MAX 10
#define M_TREE_MISSING 10
#define M_TREE_MAX 11
#define M_THREAD_COLLAPSE (1<<0)
#define M_THREAD_UNCOLLAPSE (1<<1)
......@@ -329,6 +330,7 @@ enum
OPTHEADER,
OPTHELP,
OPTHIDDENHOST,
OPTHIDEMISSING,
OPTIGNORELISTREPLYTO,
#ifdef USE_IMAP
OPTIMAPLSUB,
......@@ -385,6 +387,7 @@ enum
OPTSUSPEND,
OPTTEXTFLOWED,
OPTTHOROUGHSRC,
OPTTHREADRECEIVED,
OPTTILDE,
OPTUNCOLLAPSEJUMP,
OPTUSE8BITMIME,
......@@ -617,8 +620,6 @@ typedef struct header
unsigned int replied : 1;
unsigned int subject_changed : 1; /* used for threading */
unsigned int display_subject : 1; /* used for threading */
unsigned int fake_thread : 1; /* no ref matched, but subject did */
unsigned int threaded : 1; /* message has been threaded */
unsigned int recip_valid : 1; /* is_recipient is valid */
unsigned int active : 1; /* message is not to be removed */
......@@ -652,13 +653,8 @@ typedef struct header
BODY *content; /* list of MIME parts */
char *path;
/* the following are used for threading support */
struct header *parent;
struct header *child; /* decendants of this message */
struct header *next; /* next message in this thread */
struct header *prev; /* previous message in thread */
struct header *last_sort; /* last message in subthread, for secondary SORT_LAST */
char *tree; /* character string to print thread tree */
struct thread *thread;
#ifdef MIXMASTER
LIST *chain;
......@@ -673,6 +669,17 @@ typedef struct header
#endif
} HEADER;
typedef struct thread
{
unsigned int fake_thread : 1;
struct thread *parent;
struct thread *child;
struct thread *next;
struct thread *prev;
HEADER *message;
HEADER *sort_key;
} THREAD;
#include "mutt_regex.h"
/* flag to mutt_pattern_comp() */
......@@ -705,9 +712,10 @@ typedef struct
char *pattern; /* limit pattern string */
pattern_t *limit_pattern; /* compiled limit pattern */
HEADER **hdrs;
HEADER *tree; /* top of thread tree */
THREAD *tree; /* top of thread tree */
HASH *id_hash; /* hash table by msg id */
HASH *subj_hash; /* hash table by subject */
HASH *thread_hash; /* hash table for threading */
int *v2r; /* mapping from virtual to real msgno */
int hdrmax; /* number of pointers in hdrs */
int msgcount; /* number of messages in the mailbox */
......
......@@ -671,10 +671,6 @@ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx)
*/
set_option (OPTFORCEREFRESH);
/* create hash tables */
ctx->id_hash = hash_create (1031);
ctx->subj_hash = hash_create (1031);
if (!ctx->quiet)
mutt_message (_("Reading %s..."), ctx->path);
......@@ -754,6 +750,7 @@ void mx_fastclose_mailbox (CONTEXT *ctx)
hash_destroy (&ctx->subj_hash, NULL);
if (ctx->id_hash)
hash_destroy (&ctx->id_hash, NULL);
mutt_clear_threads (ctx);
for (i = 0; i < ctx->msgcount; i++)
mutt_free_header (&ctx->hdrs[i]);
safe_free ((void **) &ctx->hdrs);
......@@ -1090,9 +1087,9 @@ void mx_update_tables(CONTEXT *ctx, int committing)
ctx->hdrs[i]->content->offset -
ctx->hdrs[i]->content->hdr_offset);
/* remove message from the hash tables */
if (ctx->hdrs[i]->env->real_subj)
if (ctx->subj_hash && ctx->hdrs[i]->env->real_subj)
hash_delete (ctx->subj_hash, ctx->hdrs[i]->env->real_subj, ctx->hdrs[i], NULL);
if (ctx->hdrs[i]->env->message_id)
if (ctx->id_hash && ctx->hdrs[i]->env->message_id)
hash_delete (ctx->id_hash, ctx->hdrs[i]->env->message_id, ctx->hdrs[i], NULL);
mutt_free_header (&ctx->hdrs[i]);
}
......@@ -1552,60 +1549,69 @@ void mx_alloc_memory (CONTEXT *ctx)
/* this routine is called to update the counts in the context structure for
* the last message header parsed.
*/
void mx_update_context (CONTEXT *ctx)
void mx_update_context (CONTEXT *ctx, int new_messages)
{
HEADER *h = ctx->hdrs[ctx->msgcount];
HEADER *h;
int msgno;
for (msgno = ctx->msgcount - new_messages; msgno < ctx->msgcount; msgno++)
{
h = ctx->hdrs[msgno];
#ifdef HAVE_PGP
/* NOTE: this _must_ be done before the check for mailcap! */
h->pgp = pgp_query (h->content);
/* NOTE: this _must_ be done before the check for mailcap! */
h->pgp = pgp_query (h->content);
#endif /* HAVE_PGP */
if (!ctx->pattern)
{
ctx->v2r[ctx->vcount] = ctx->msgcount;
h->virtual = ctx->vcount++;
}
else
h->virtual = -1;
h->msgno = ctx->msgcount;
ctx->msgcount++;
if (h->env->supersedes)
{
HEADER *h2 = hash_find (ctx->id_hash, h->env->supersedes);
/* safe_free (&h->env->supersedes); should I ? */
if (h2)
if (!ctx->pattern)
{
h2->superseded = 1;
if (option (OPTSCORE))
mutt_score_message (ctx, h2, 1);
ctx->v2r[ctx->vcount] = msgno;
h->virtual = ctx->vcount++;
}
}
else
h->virtual = -1;
h->msgno = msgno;
/* add this message to the hash tables */
if (h->env->message_id)
hash_insert (ctx->id_hash, h->env->message_id, h, 0);
if (h->env->real_subj)
hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
if (h->env->supersedes)
{
HEADER *h2;
if (option (OPTSCORE))
mutt_score_message (ctx, h, 0);
if (h->changed)
ctx->changed = 1;
if (h->flagged)
ctx->flagged++;
if (h->deleted)
ctx->deleted++;
if (!h->read)
{
ctx->unread++;
if (!h->old)
ctx->new++;
if (!ctx->id_hash)
ctx->id_hash = mutt_make_id_hash (ctx);
h2 = hash_find (ctx->id_hash, h->env->supersedes);
/* safe_free (&h->env->supersedes); should I ? */
if (h2)
{
h2->superseded = 1;
if (option (OPTSCORE))
mutt_score_message (ctx, h2, 1);
}
}
/* add this message to the hash tables */
if (ctx->id_hash && h->env->message_id)
hash_insert (ctx->id_hash, h->env->message_id, h, 0);
if (ctx->subj_hash && h->env->real_subj)
hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
if (option (OPTSCORE))
mutt_score_message (ctx, h, 0);
if (h->changed)
ctx->changed = 1;
if (h->flagged)
ctx->flagged++;
if (h->deleted)
ctx->deleted++;
if (!h->read)
{
ctx->unread++;
if (!h->old)
ctx->new++;
}
}
}
......@@ -77,7 +77,7 @@ int mbox_strict_cmp_headers (const HEADER *, const HEADER *);
int mutt_reopen_mailbox (CONTEXT *, int *);
void mx_alloc_memory (CONTEXT *);
void mx_update_context (CONTEXT *);
void mx_update_context (CONTEXT *, int);
void mx_update_tables (CONTEXT *, int);
......
......@@ -90,23 +90,65 @@ static char *read_rfc822_line (FILE *f, char *line, size_t *linelen)
/* not reached */
}
static LIST *mutt_parse_references (char *s)
static LIST *mutt_parse_references (char *s, int in_reply_to)
{
LIST *t, *lst = NULL;
int m, n = 0;
char *o = NULL, *new, *at;
while ((s = strtok (s, " \t")) != NULL)
while ((s = strtok (s, " \t;")) != NULL)
{
/*
* some mail clients add other garbage besides message-ids, so do a quick
* check to make sure this looks like a valid message-id
* some idiotic clients also break their message-ids between lines, deal
* with that too (give up if it's more than two lines, though)
*/
t = NULL;
new = NULL;
if (*s == '<')
{
t = (LIST *)safe_malloc (sizeof (LIST));
t->data = safe_strdup (s);
t->next = lst;
lst = t;
n = strlen (s);
if (s[n-1] != '>')
{
o = s;
s = NULL;
continue;
}
new = safe_strdup (s);
}
else if (o)
{
m = strlen (s);
if (s[m - 1] == '>')
{
new = safe_malloc (sizeof (char) * (n + m + 1));
strcpy (new, o); /* __STRCPY_CHECKED__ */
strcpy (new + n, s); /* __STRCPY_CHECKED__ */
}
}
if (new)
{
/* make sure that this really does look like a message-id.
* it should have exactly one @, and if we're looking at
* an in-reply-to header, make sure that the part before
* the @ has more than eight characters or it's probably
* an email address
*/
if (!(at = strchr (new, '@')) || strchr (at + 1, '@')
|| (in_reply_to && at - new <= 8))
safe_free ((void **) &new);
else
{
t = (LIST *) safe_malloc (sizeof (LIST));
t->data = new;
t->next = lst;
lst = t;
}
}
o = NULL;
s = NULL;
}
......@@ -1009,7 +1051,7 @@ int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short
if (!ascii_strcasecmp (line+1, "n-reply-to"))
{
mutt_free_list (&e->in_reply_to);
e->in_reply_to = mutt_parse_references (p);
e->in_reply_to = mutt_parse_references (p, 1);
matched = 1;
}
break;
......@@ -1058,7 +1100,7 @@ int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short
if (!ascii_strcasecmp (line + 1, "eferences"))