Commit 9f1f8277 authored by Thomas Roessler's avatar Thomas Roessler

Turn alternates, lists, and subscribe into regular expression lists.

parent 98b964dd
......@@ -529,25 +529,49 @@ int mutt_addr_is_user (ADDRESS *addr)
{
/* NULL address is assumed to be the user. */
if (!addr)
{
dprint (5, (debugfile, "mail_addr_is_user: yes, NULL address\n"));
return 1;
}
if (!addr->mailbox)
{
dprint (5, (debugfile, "mail_addr_is_user: no, no mailbox\n"));
return 0;
}
if (ascii_strcasecmp (addr->mailbox, Username) == 0)
{
dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s\n", addr->mailbox, Username));
return 1;
}
if (string_is_address(addr->mailbox, Username, Hostname))
{
dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname));
return 1;
}
if (string_is_address(addr->mailbox, Username, mutt_fqdn(0)))
{
dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (0)));
return 1;
}
if (string_is_address(addr->mailbox, Username, mutt_fqdn(1)))
{
dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (1)));
return 1;
}
if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
{
dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox));
return 1;
}
if (Alternates.pattern &&
regexec (Alternates.rx, addr->mailbox, 0, NULL, 0) == 0)
if (mutt_match_rx_list (addr->mailbox, Alternates))
{
dprint (5, (debugfile, "mail_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox));
return 1;
}
dprint (5, (debugfile, "mail_addr_is_user: no, all failed.\n"));
return 0;
}
......@@ -1167,8 +1167,8 @@ unignore posted-to:
<sect1>Mailing lists<label id="lists">
<p>
Usage: <tt/&lsqb;un&rsqb;lists/ <em/address/ &lsqb; <em/address/ ... &rsqb;<newline>
Usage: <tt/&lsqb;un&rsqb;subscribe/ <em/address/ &lsqb; <em/address/ ... &rsqb;
Usage: <tt/&lsqb;un&rsqb;lists/ <em/regexp/ &lsqb; <em/regexp/ ... &rsqb;<newline>
Usage: <tt/&lsqb;un&rsqb;subscribe/ <em/regexp/ &lsqb; <em/regexp/ ... &rsqb;
Mutt has a few nice features for <ref id="using_lists" name="handling
mailing lists">. In order to take advantage of them, you must
......@@ -1184,10 +1184,16 @@ receiving personal CCs of list messages. Also note that the generation
of the Mail-Followup-To header is controlled by the <ref id="followup_to"
name="&dollar;followup_to"> configuration variable.
More precisely, Mutt maintains lists of known and subscribed mailing
lists. Every subscribed mailing list is known. To mark
a mailing list as known, use the ``lists'' command. To mark it as
subscribed, use ``subscribe''.
More precisely, Mutt maintains lists of patterns for the addresses
of known and subscribed mailing lists. Every subscribed mailing
list is known. To mark a mailing list as known, use the ``lists''
command. To mark it as subscribed, use ``subscribe''.
You can use regular expressions with both commands. To mark all
messages sent to a specific bug report's address on mutt's bug
tracking system as list mail, for instance, you could say
``subscribe [0-9]*@bugs.guug.de''. Often, it's sufficient to just
give a portion of the list's e-mail address.
Specify as much of the address as you need to to remove ambiguity. For
example, if you've subscribed to the Mutt mailing list, you will receive mail
......@@ -1207,7 +1213,6 @@ tokens.
To remove a mailing list from the list of subscribed mailing lists,
but keep it on the list of known mailing lists, use ``unsubscribe''.
<sect1>Using Multiple spool mailboxes<label id="mbox-hook">
<p>
Usage: <tt/mbox-hook/ &lsqb;!&rsqb;<em/pattern/ <em/mailbox/
......@@ -1741,8 +1746,8 @@ messages:
~n [MIN]-[MAX] messages with a score in the range MIN to MAX *)
~N new messages
~O old messages
~p message is addressed to you (consults $alternates)
~P message is from you (consults $alternates)
~p message is addressed to you (consults alternates)
~P message is from you (consults alternates)
~Q messages which have been replied to
~R read messages
~r [MIN]-[MAX] messages with ``date-received'' in a Date range
......
......@@ -76,6 +76,17 @@ like sh and bash: Prepend the name of the environment by a dollar
all aliases when \(lq\fB*\fP\(rq is used as an argument.
.PP
.nf
\fBalternates\fP \fIregexp\fP [ \fB,\fP \fIregexp\fP [ ... ]]
\fBunalternates\fP [\fB * \fP | \fIregexp\fP [ \fB,\fP \fIregexp\fP [ ... ]] ]
.fi
.IP
\fBalternates\fP is used to inform mutt about alternate addresses
where you receive mail; you can use regular expressions to specify
alternate addresses. This affects mutt's idea about messages
from you, and messages addressed to you. \fBunalternates\fP removes
a regular expression from the list of known alternates.
.PP
.nf
\fBalternative_order\fP \fItype\fP[\fB/\fP\fIsubtype\fP] [ ... ]
\fBunalternative_order\fP [\fB * \fP | \fItype\fP/\fIsubtype\fP] [...]
.fi
......@@ -213,18 +224,15 @@ The \fBunignore\fP command permits you to define exceptions from
the above mentioned list of ignored headers.
.PP
.nf
\fBlists\fP \fIaddress\fP [ \fIaddress\fP ... ]
\fBunlists\fP \fIaddress\fP [ \fIaddress\fP ... ]
\fBsubscribe\fP \fIaddress\fP [ \fIaddress\fP ... ]
\fBunsubscribe\fP \fIaddress\fP [ \fIaddress\fP ... ]
\fBlists\fP \fIregexp\fP [ \fIregexp\fP ... ]
\fBunlists\fP \fIregexp\fP [ \fIregexp\fP ... ]
\fBsubscribe\fP \fIregexp\fP [ \fIregexp\fP ... ]
\fBunsubscribe\fP \fIregexp\fP [ \fIregexp\fP ... ]
.fi
.IP
Mutt maintains two lists of mailing list addresses, a list of
Mutt maintains two lists of mailing list address patterns, a list of
subscribed mailing lists, and a list of known mailing lists. All
subscribed mailing lists are known. A mail address matches a
mailing list if it begins with the given address. For example, the
lists pattern \(lqmutt-\(rq will match [email protected] and
[email protected]
subscribed mailing lists are known. Patterns use regular expressions.
.IP
The \fBlists\fP command adds a mailing list address to the list of
known mailing lists. The \fBunlists\fP command removes a mailing
......
......@@ -121,8 +121,10 @@ WHERE LIST *HeaderOrderList INITVAL(0);
WHERE LIST *Ignore INITVAL(0);
WHERE LIST *MimeLookupList INITVAL(0);
WHERE LIST *UnIgnore INITVAL(0);
WHERE LIST *MailLists INITVAL(0);
WHERE LIST *SubscribedLists INITVAL(0);
WHERE RX_LIST *Alternates INITVAL(0);
WHERE RX_LIST *MailLists INITVAL(0);
WHERE RX_LIST *SubscribedLists INITVAL(0);
/* bit vector for boolean variables */
#ifdef MAIN_C
......
......@@ -28,31 +28,14 @@
#include <string.h>
#include <locale.h>
static int _mutt_is_mail_list (ADDRESS *addr, LIST *p)
{
char *s;
if (addr->mailbox)
{
for (;p; p = p->next)
{
if (mutt_strncasecmp (addr->mailbox, p->data, mutt_strlen (p->data)) == 0)
return 1;
else if (*p->data == '@' && (s = strchr (addr->mailbox, '@')))
if (mutt_strncasecmp (s, p->data, mutt_strlen (p->data)) == 0)
return 1;
}
}
return 0;
}
int mutt_is_mail_list (ADDRESS *addr)
{
return _mutt_is_mail_list (addr, MailLists);
return mutt_match_rx_list (addr->mailbox, MailLists);
}
int mutt_is_subscribed_list (ADDRESS *addr)
{
return _mutt_is_mail_list (addr, SubscribedLists);
return mutt_match_rx_list (addr->mailbox, SubscribedLists);
}
/* Search for a mailing list in the list of addresses pointed to by adr.
......
......@@ -320,6 +320,52 @@ static void add_to_list (LIST **list, const char *str)
}
}
static int add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
{
RX_LIST *t, *last = NULL;
REGEXP *rx;
if (!s || !*s)
return 0;
if (!(rx = mutt_compile_regexp (s, flags)))
{
snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
return -1;
}
/* check to make sure the item is not already on this list */
for (last = *list; last; last = last->next)
{
if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
{
/* already on the list, so just ignore it */
last = NULL;
break;
}
if (!last->next)
break;
}
if (!*list || last)
{
t = mutt_new_rx_list();
t->rx = rx;
if (last)
{
last->next = t;
last = last->next;
}
else
*list = last = t;
}
else /* duplicate */
mutt_free_regexp (&rx);
return 0;
}
static void remove_from_list (LIST **l, const char *str)
{
LIST *p, *last = NULL;
......@@ -350,6 +396,36 @@ static void remove_from_list (LIST **l, const char *str)
}
}
static void remove_from_rx_list (RX_LIST **l, const char *str)
{
RX_LIST *p, *last = NULL;
if (mutt_strcmp ("*", str) == 0)
mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
else
{
p = *l;
last = NULL;
while (p)
{
if (ascii_strcasecmp (str, p->rx->pattern) == 0)
{
mutt_free_regexp (&p->rx);
if (last)
last->next = p->next;
else
(*l) = p->next;
FREE (&p);
}
else
{
last = p;
p = p->next;
}
}
}
}
static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
do
......@@ -392,6 +468,42 @@ static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
return 0;
}
static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
{
do
{
mutt_extract_token (buf, s, 0);
if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
return -1;
}
while (MoreArgs (s));
return 0;
}
static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
return _parse_rx_list (buf, s, data, err, REG_ICASE);
}
static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
do
{
mutt_extract_token (buf, s, 0);
if (mutt_strcmp (buf->data, "*") == 0)
{
mutt_free_rx_list ((RX_LIST **) data);
break;
}
remove_from_rx_list ((RX_LIST **) data, buf->data);
}
while (MoreArgs (s));
return 0;
}
static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
do
......@@ -418,8 +530,8 @@ static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *er
do
{
mutt_extract_token (buf, s, 0);
remove_from_list (&MailLists, buf->data);
remove_from_list (&SubscribedLists, buf->data);
remove_from_rx_list (&MailLists, buf->data);
remove_from_rx_list (&SubscribedLists, buf->data);
}
while (MoreArgs (s));
......@@ -431,8 +543,10 @@ static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *
do
{
mutt_extract_token (buf, s, 0);
add_to_list (&MailLists, buf->data);
add_to_list (&SubscribedLists, buf->data);
if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
return -1;
if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
return -1;
}
while (MoreArgs (s));
......
......@@ -157,13 +157,6 @@ struct option_t MuttVars[] = {
** message could include a line like "[-- PGP output follows ..." and
** give it the same color as your attachment color.
*/
{ "alternates", DT_RX, R_BOTH, UL &Alternates, 0 },
/*
** .pp
** A regexp that allows you to specify \fIalternate\fP addresses where
** you receive mail. This affects Mutt's idea about messages from you
** and addressed to you.
*/
{ "arrow_cursor", DT_BOOL, R_BOTH, OPTARROWCURSOR, 0 },
/*
** .pp
......@@ -402,7 +395,7 @@ struct option_t MuttVars[] = {
** variable at the time the hook is declared. The default value matches
** if the message is either from a user matching the regular expression
** given, or if it is from you (if the from address matches
** ``$$alternates'') and is to or cc'ed to a user matching the given
** ``alternates'') and is to or cc'ed to a user matching the given
** regular expression.
*/
{ "delete", DT_QUAD, R_NONE, OPT_DELETE, M_ASKYES },
......@@ -1034,8 +1027,8 @@ struct option_t MuttVars[] = {
{ "metoo", DT_BOOL, R_NONE, OPTMETOO, 0 },
/*
** .pp
** If unset, Mutt will remove your address (see the ``$$alternates''
** variable) from the list of recipients when replying to a message.
** If unset, Mutt will remove your address (see the ``alternates''
** command) from the list of recipients when replying to a message.
*/
{ "menu_scroll", DT_BOOL, R_NONE, OPTMENUSCROLL, 0 },
/*
......@@ -2734,7 +2727,10 @@ const struct mapping_t SortKeyMethods[] = {
/* functions used to parse commands in a rc file */
static int parse_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_rx_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unlist (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_rx_unlist (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unlists (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_alias (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unalias (BUFFER *, BUFFER *, unsigned long, BUFFER *);
......@@ -2754,6 +2750,8 @@ struct command_t
};
struct command_t Commands[] = {
{ "alternates", parse_rx_list, UL &Alternates },
{ "unalternates", parse_rx_unlist, UL &Alternates },
#ifdef USE_SOCKET
{ "account-hook", mutt_parse_hook, M_ACCOUNTHOOK },
#endif
......@@ -2775,7 +2773,7 @@ struct command_t Commands[] = {
{ "iconv-hook", mutt_parse_hook, M_ICONVHOOK },
#endif
{ "ignore", parse_ignore, 0 },
{ "lists", parse_list, UL &MailLists },
{ "lists", parse_rx_list, UL &MailLists },
{ "macro", mutt_parse_macro, 0 },
{ "mailboxes", mutt_parse_mailboxes, M_MAILBOXES },
{ "unmailboxes", mutt_parse_mailboxes, M_UNMAILBOXES },
......@@ -2808,6 +2806,6 @@ struct command_t Commands[] = {
{ "unmy_hdr", parse_unmy_hdr, 0 },
{ "unscore", mutt_parse_unscore, 0 },
{ "unset", parse_set, M_SET_UNSET },
{ "unsubscribe", parse_unlist, UL &SubscribedLists },
{ "unsubscribe", parse_rx_unlist, UL &SubscribedLists },
{ NULL }
};
......@@ -75,6 +75,8 @@
#define INITVAL(x)
#endif
#include "mutt_regex.h"
/* flags for mutt_copy_header() */
#define CH_UPDATE 1 /* update the status and x-status fields? */
#define CH_WEED (1<<1) /* weed the headers? */
......@@ -504,8 +506,16 @@ typedef struct list_t
struct list_t *next;
} LIST;
typedef struct rx_list_t
{
REGEXP *rx;
struct rx_list_t *next;
} RX_LIST;
#define mutt_new_list() safe_calloc (1, sizeof (LIST))
#define mutt_new_rx_list() safe_calloc (1, sizeof (RX_LIST))
void mutt_free_list (LIST **);
void mutt_free_rx_list (RX_LIST **);
int mutt_matches_ignore (const char *, LIST *);
/* add an element to a list */
......@@ -720,7 +730,6 @@ typedef struct thread
HEADER *sort_key;
} THREAD;
#include "mutt_regex.h"
/* flag to mutt_pattern_comp() */
#define M_FULL_MSG 1 /* enable body and header matching */
......
......@@ -46,7 +46,6 @@ typedef struct
int not; /* do not match */
} REGEXP;
WHERE REGEXP Alternates;
WHERE REGEXP Mask;
WHERE REGEXP QuoteRegexp;
WHERE REGEXP ReplyRegexp;
......
......@@ -1346,3 +1346,51 @@ const char *mutt_make_version (void)
return vstring;
}
REGEXP *mutt_compile_regexp (const char *s, int flags)
{
REGEXP *pp = safe_calloc (sizeof (REGEXP), 1);
pp->pattern = safe_strdup (s);
pp->rx = safe_calloc (sizeof (regex_t), 1);
if (REGCOMP (pp->rx, NONULL(s), flags) != 0)
mutt_free_regexp (&pp);
return pp;
}
void mutt_free_regexp (REGEXP **pp)
{
FREE (&(*pp)->pattern);
regfree ((*pp)->rx);
FREE (&(*pp)->rx);
FREE (pp);
}
void mutt_free_rx_list (RX_LIST **list)
{
RX_LIST *p;
if (!list) return;
while (*list)
{
p = *list;
*list = (*list)->next;
mutt_free_regexp (&p->rx);
FREE (&p);
}
}
int mutt_match_rx_list (const char *s, RX_LIST *l)
{
if (!s) return 0;
for (; l; l = l->next)
{
if (regexec (l->rx->rx, s, (size_t) 0, (regmatch_t *) 0, (int) 0) == 0)
{
dprint (5, (debugfile, "mutt_match_rx_list: %s matches %s\n", s, l->rx->pattern));
return 1;
}
}
return 0;
}
......@@ -136,6 +136,8 @@ const char *mutt_make_version (void);
const char *mutt_fqdn(short);
REGEXP *mutt_compile_regexp (const char *, int);
void mutt_account_hook (const char* url);
void mutt_add_to_reference_headers (ENVELOPE *env, ENVELOPE *curenv, LIST ***pp, LIST ***qq);
void mutt_adv_mktemp (char *, size_t);
......@@ -181,6 +183,7 @@ void mutt_free_enter_state (ENTER_STATE **);
void mutt_free_envelope (ENVELOPE **);
void mutt_free_header (HEADER **);
void mutt_free_parameter (PARAMETER **);
void mutt_free_regexp (REGEXP **);
void mutt_generate_header (char *, size_t, HEADER *, int);
void mutt_help (int);
void mutt_draw_tree (CONTEXT *);
......@@ -287,6 +290,7 @@ int mutt_is_subscribed_list (ADDRESS *);
int mutt_is_text_part (BODY *);
int mutt_is_valid_mailbox (const char *);
int mutt_lookup_mime_type (BODY *, const char *);
int mutt_match_rx_list (const char *, RX_LIST *);
int mutt_messages_in_thread (CONTEXT *, HEADER *, int);
int mutt_multi_choice (char *prompt, char *letters);
int mutt_needs_mailcap (BODY *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment