/*
* Copyright notice from original mutt:
* Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org>
*
* This file is part of mutt-ng, see http://www.muttng.org/.
* It's licensed under the GNU General Public License,
* please see the file GPL in the top level source directory.
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include "mutt.h"
#include "mutt_curses.h"
#include "sort.h"
#include "thread.h"
#include "charset.h"
#include "mutt_crypt.h"
#include "mutt_idna.h"
#include "mime.h"
#include "lib/str.h"
#include "lib/rx.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
int mutt_is_mail_list (ADDRESS * addr)
{
if (!rx_list_match (UnMailLists, addr->mailbox))
return rx_list_match (MailLists, addr->mailbox);
return 0;
}
int mutt_is_subscribed_list (ADDRESS * addr)
{
if (!rx_list_match (UnMailLists, addr->mailbox)
&& !rx_list_match (UnSubscribedLists, addr->mailbox))
return rx_list_match (SubscribedLists, addr->mailbox);
return 0;
}
/* Search for a mailing list in the list of addresses pointed to by adr.
* If one is found, print pfx and the name of the list into buf, then
* return 1. Otherwise, simply return 0.
*/
static int
check_for_mailing_list (ADDRESS * adr, char *pfx, char *buf, int buflen)
{
for (; adr; adr = adr->next) {
if (mutt_is_subscribed_list (adr)) {
if (pfx && buf && buflen)
snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
return 1;
}
}
return 0;
}
/* Search for a mailing list in the list of addresses pointed to by adr.
* If one is found, print the address of the list into buf, then return 1.
* Otherwise, simply return 0.
*/
static int check_for_mailing_list_addr (ADDRESS * adr, char *buf, int buflen)
{
for (; adr; adr = adr->next) {
if (mutt_is_subscribed_list (adr)) {
if (buf && buflen)
snprintf (buf, buflen, "%s", adr->mailbox);
return 1;
}
}
return 0;
}
static int first_mailing_list (char *buf, size_t buflen, ADDRESS * a)
{
for (; a; a = a->next) {
if (mutt_is_subscribed_list (a)) {
mutt_save_path (buf, buflen, a);
return 1;
}
}
return 0;
}
static void make_from (ENVELOPE * hdr, char *buf, size_t len, int do_lists)
{
int me;
me = mutt_addr_is_user (hdr->from);
if (do_lists || me) {
if (check_for_mailing_list (hdr->to, "To ", buf, len))
return;
if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
return;
}
if (me && hdr->to)
snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
else if (me && hdr->cc)
snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
else if (hdr->from)
strfcpy (buf, mutt_get_name (hdr->from), len);
else
*buf = 0;
}
static void make_from_addr (ENVELOPE * hdr, char *buf, size_t len,
int do_lists)
{
int me;
me = mutt_addr_is_user (hdr->from);
if (do_lists || me) {
if (check_for_mailing_list_addr (hdr->to, buf, len))
return;
if (check_for_mailing_list_addr (hdr->cc, buf, len))
return;
}
if (me && hdr->to)
snprintf (buf, len, "%s", hdr->to->mailbox);
else if (me && hdr->cc)
snprintf (buf, len, "%s", hdr->cc->mailbox);
else if (hdr->from)
strfcpy (buf, hdr->from->mailbox, len);
else
*buf = 0;
}
static int user_in_addr (ADDRESS * a)
{
for (; a; a = a->next)
if (mutt_addr_is_user (a))
return 1;
return 0;
}
/* Return values:
* 0: user is not in list
* 1: user is unique recipient
* 2: user is in the TO list
* 3: user is in the CC list
* 4: user is originator
* 5: sent to a subscribed mailinglist
*/
int mutt_user_is_recipient (HEADER * h)
{
ENVELOPE *env = h->env;
if (!h->recip_valid) {
h->recip_valid = 1;
if (mutt_addr_is_user (env->from))
h->recipient = 4;
else if (user_in_addr (env->to)) {
if (env->to->next || env->cc)
h->recipient = 2; /* non-unique recipient */
else
h->recipient = 1; /* unique recipient */
}
else if (user_in_addr (env->cc))
h->recipient = 3;
else if (check_for_mailing_list (env->to, NULL, NULL, 0))
h->recipient = 5;
else if (check_for_mailing_list (env->cc, NULL, NULL, 0))
h->recipient = 5;
else
h->recipient = 0;
}
return h->recipient;
}
/* %a = address of author
* %A = reply-to address (if present; otherwise: address of author
* %b = filename of the originating folder
* %B = the list to which the letter was sent
* %c = size of message in bytes
* %C = current message number
* %d = date and time of message using $date_format and sender's timezone
* %D = date and time of message using $date_format and local timezone
* %e = current message number in thread
* %E = number of messages in current thread
* %f = entire from line
* %F = like %n, unless from self
* %g = newsgroup name (if compiled with nntp support)
* %i = message-id
* %I = initials of author
* %l = number of lines in the message
* %L = like %F, except `lists' are displayed first
* %m = number of messages in the mailbox
* %n = name of author
* %N = score
* %O = like %L, except using address instead of name
* %s = subject
* %S = short message status (e.g., N/O/D/!/r/-)
* %t = `to:' field (recipients)
* %T = $to_chars
* %u = user (login) name of author
* %v = first name of author, unless from self
* %W = where user is (organization)
* %X = number of MIME attachments
* %y = `x-label:' field (if present)
* %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
* %Z = status flags */
struct hdr_format_info {
CONTEXT *ctx;
HEADER *hdr;
};
static const char *hdr_format_str (char *dest,
size_t destlen,
char op,
const char *src,
const char *prefix,
const char *ifstring,
const char *elsestring,
unsigned long data, format_flag flags)
{
struct hdr_format_info *hfi = (struct hdr_format_info *) data;
HEADER *hdr, *htmp;
CONTEXT *ctx;
char fmt[SHORT_STRING], buf2[SHORT_STRING], ch, *p;
int do_locales, i;
int optional = (flags & M_FORMAT_OPTIONAL);
int threads = ((Sort & SORT_MASK) == SORT_THREADS);
int is_index = (flags & M_FORMAT_INDEX);
#define THREAD_NEW (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 1)
#define THREAD_OLD (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 2)
size_t len;
hdr = hfi->hdr;
ctx = hfi->ctx;
dest[0] = 0;
switch (op) {
case 'A':
if (hdr->env->reply_to && hdr->env->reply_to->mailbox) {
mutt_format_s (dest, destlen, prefix,
mutt_addr_for_display (hdr->env->reply_to));
break;
}
/* fall through if 'A' returns nothing */
case 'a':
if (hdr->env->from && hdr->env->from->mailbox) {
mutt_format_s (dest, destlen, prefix,
mutt_addr_for_display (hdr->env->from));
}
else
dest[0] = '\0';
break;
case 'B':
if (!first_mailing_list (dest, destlen, hdr->env->to) &&
!first_mailing_list (dest, destlen, hdr->env->cc))
dest[0] = 0;
if (dest[0]) {
strfcpy (buf2, dest, sizeof (buf2));
mutt_format_s (dest, destlen, prefix, buf2);
break;
}
/* fall through if 'B' returns nothing */
case 'b':
if (ctx) {
if ((p = strrchr (ctx->path, '/')))
strfcpy (dest, p + 1, destlen);
else
strfcpy (dest, ctx->path, destlen);
}
else
strfcpy (dest, "(null)", destlen);
strfcpy (buf2, dest, sizeof (buf2));
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'c':
mutt_pretty_size (buf2, sizeof (buf2), (long) hdr->content->length);
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'C':
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, hdr->msgno + 1);
break;
case 'd':
case 'D':
case '{':
case '[':
case '(':
case '<':
/* preprocess $date_format to handle %Z */
{
const char *cp;
struct tm *tm;
time_t T;
p = dest;
cp = (op == 'd' || op == 'D') ? (NONULL (DateFmt)) : src;
if (*cp == '!') {
do_locales = 0;
cp++;
}
else
do_locales = 1;
len = destlen - 1;
while (len > 0 && (((op == 'd' || op == 'D') && *cp) ||
(op == '{' && *cp != '}') ||
(op == '[' && *cp != ']') ||
(op == '(' && *cp != ')') ||
(op == '<' && *cp != '>'))) {
if (*cp == '%') {
cp++;
if ((*cp == 'Z' || *cp == 'z') && (op == 'd' || op == '{')) {
if (len >= 5) {
sprintf (p, "%c%02u%02u", hdr->zoccident ? '-' : '+',
hdr->zhours, hdr->zminutes);
p += 5;
len -= 5;
}
else
break; /* not enough space left */
}
else {
if (len >= 2) {
*p++ = '%';
*p++ = *cp;
len -= 2;
}
else
break; /* not enough space */
}
cp++;
}
else {
*p++ = *cp++;
len--;
}
}
*p = 0;
if (do_locales && Locale)
setlocale (LC_TIME, Locale);
if (op == '[' || op == 'D')
tm = localtime (&hdr->date_sent);
else if (op == '(')
tm = localtime (&hdr->received);
else if (op == '<') {
T = time (NULL);
tm = localtime (&T);
}
else {
/* restore sender's time zone */
T = hdr->date_sent;
if (hdr->zoccident)
T -= (hdr->zhours * 3600 + hdr->zminutes * 60);
else
T += (hdr->zhours * 3600 + hdr->zminutes * 60);
tm = gmtime (&T);
}
strftime (buf2, sizeof (buf2), dest, tm);
if (do_locales)
setlocale (LC_TIME, "C");
mutt_format_s (dest, destlen, prefix, buf2);
if (len > 0 && op != 'd' && op != 'D') /* Skip ending op */
src = cp + 1;
}
break;
case 'e':
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 1));
break;
case 'E':
if (!optional) {
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 0));
}
else if (mutt_messages_in_thread (ctx, hdr, 0) <= 1)
optional = 0;
break;
case 'f':
buf2[0] = 0;
rfc822_write_address (buf2, sizeof (buf2), hdr->env->from, 1);
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'F':
if (!optional) {
make_from (hdr->env, buf2, sizeof (buf2), 0);
mutt_format_s (dest, destlen, prefix, buf2);
}
else if (mutt_addr_is_user (hdr->env->from))
optional = 0;
break;
#ifdef USE_NNTP
case 'g':
mutt_format_s (dest, destlen, prefix,
hdr->env->newsgroups ? hdr->env->newsgroups : "");
break;
#endif
case 'H':
/* (Hormel) spam score */
if (optional)
optional = hdr->env->spam ? 1 : 0;
if (hdr->env->spam)
mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
else
mutt_format_s (dest, destlen, prefix, "");
break;
case 'i':
mutt_format_s (dest, destlen, prefix,
hdr->env->message_id ? hdr->env->message_id : "<no.id>");
break;
case 'I':
{
int iflag = FALSE;
int j = 0;
for (i = 0; hdr->env->from && hdr->env->from->personal &&
hdr->env->from->personal[i] && j < SHORT_STRING - 1; i++) {
if (isalpha ((int) hdr->env->from->personal[i])) {
if (!iflag) {
buf2[j++] = hdr->env->from->personal[i];
iflag = TRUE;
}
}
else
iflag = FALSE;
}
buf2[j] = '\0';
}
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'l':
if (!optional) {
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, (int) hdr->lines);
}
else if (hdr->lines <= 0)
optional = 0;
break;
case 'L':
if (!optional) {
make_from (hdr->env, buf2, sizeof (buf2), 1);
mutt_format_s (dest, destlen, prefix, buf2);
}
else if (!check_for_mailing_list (hdr->env->to, NULL, NULL, 0) &&
!check_for_mailing_list (hdr->env->cc, NULL, NULL, 0)) {
optional = 0;
}
break;
case 'm':
if (ctx) {
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, ctx->msgcount);
}
else
strfcpy (dest, "(null)", destlen);
break;
case 'n':
mutt_format_s (dest, destlen, prefix, mutt_get_name (hdr->env->from));
break;
case 'N':
if (!optional) {
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, hdr->score);
}
else {
if (hdr->score == 0)
optional = 0;
}
break;
case 'O':
if (!optional) {
make_from_addr (hdr->env, buf2, sizeof (buf2), 1);
if (!option (OPTSAVEADDRESS) && (p = strpbrk (buf2, "%@")))
*p = 0;
mutt_format_s (dest, destlen, prefix, buf2);
}
else if (!check_for_mailing_list_addr (hdr->env->to, NULL, 0) &&
!check_for_mailing_list_addr (hdr->env->cc, NULL, 0)) {
optional = 0;
}
break;
case 'M':
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
if (!optional) {
if (threads && is_index && hdr->collapsed && hdr->num_hidden > 1)
snprintf (dest, destlen, fmt, hdr->num_hidden);
else if (is_index && threads)
mutt_format_s (dest, destlen, prefix, " ");
else
*dest = '\0';
}
else {
if (!(threads && is_index && hdr->collapsed && hdr->num_hidden > 1))
optional = 0;
}
break;
case 's':
if (flags & M_FORMAT_TREE && !hdr->collapsed) {
if (flags & M_FORMAT_FORCESUBJ) {
mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject));
snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
mutt_format_s_tree (dest, destlen, prefix, buf2);
}
else
mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
}
else
mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject));
break;
case 'S':
if (hdr->deleted)
ch = 'D';
else if (hdr->attach_del)
ch = 'd';
else if (hdr->tagged)
ch = '*';
else if (hdr->flagged)
ch = '!';
else if (hdr->replied)
ch = 'r';
else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
ch = '-';
else if (hdr->old)
ch = 'O';
else
ch = 'N';
/* FOO - this is probably unsafe, but we are not likely to have such
a short string passed into this routine */
*dest = ch;
*(dest + 1) = 0;
break;
case 't':
buf2[0] = 0;
if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
!check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2))) {
if (hdr->env->to)
snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
else if (hdr->env->cc)
snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
}
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'T':
snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
snprintf (dest, destlen, fmt,
(Tochars
&& ((i = mutt_user_is_recipient (hdr))) <
str_len (Tochars)) ? Tochars[i] : ' ');
break;
case 'u':
if (hdr->env->from && hdr->env->from->mailbox) {
strfcpy (buf2, mutt_addr_for_display (hdr->env->from), sizeof (buf2));
if ((p = strpbrk (buf2, "%@")))
*p = 0;
}
else
buf2[0] = 0;
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'v':
if (mutt_addr_is_user (hdr->env->from)) {
if (hdr->env->to)
mutt_format_s (buf2, sizeof (buf2), prefix,
mutt_get_name (hdr->env->to));
else if (hdr->env->cc)
mutt_format_s (buf2, sizeof (buf2), prefix,
mutt_get_name (hdr->env->cc));
else
*buf2 = 0;
}
else
mutt_format_s (buf2, sizeof (buf2), prefix,
mutt_get_name (hdr->env->from));
if ((p = strpbrk (buf2, " %@")))
*p = 0;
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'W':
if (!optional)
mutt_format_s (dest, destlen, prefix,
hdr->env->organization ? hdr->env->organization : "");
else if (!hdr->env->organization)
optional = 0;
break;
case 'X':
{
int count = 0;
if (option (OPTCOUNTATTACH)) {
if (!hdr->content->parts)
mutt_parse_mime_message(ctx, hdr);
count = mutt_count_body_parts(hdr, 0);
}
/* The recursion allows messages without depth to return 0. */
if (optional)
optional = count != 0;
snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
snprintf (dest, destlen, fmt, count);
}
break;
case 'Z':
ch = ' ';
if (WithCrypto && hdr->security & GOODSIGN)
ch = 'S';
else if (WithCrypto && hdr->security & ENCRYPT)
ch = 'P';
else if (WithCrypto && hdr->security & SIGN)
ch = 's';
else if ((WithCrypto & APPLICATION_PGP) && hdr->security & PGPKEY)
ch = 'K';
snprintf (buf2, sizeof (buf2),
"%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' :
((hdr->read
&& (ctx
&& ctx->msgnotreadyet !=
hdr->msgno))
? (hdr->
replied ? 'r' : ' ') : (hdr->
old ?
'O' :
'N')))),
hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
hdr->tagged ? '*' : (hdr->
flagged ? '!' : (Tochars
&&
((i =
mutt_user_is_recipient
(hdr)) <
str_len (Tochars)) ?
Tochars[i] : ' ')));
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'y':
if (optional)
optional = hdr->env->x_label ? 1 : 0;
mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
break;
case 'Y':
if (hdr->env->x_label) {
i = 1; /* reduce reuse recycle */
htmp = NULL;
if (flags & M_FORMAT_TREE
&& (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->thread->parent && hdr->thread->parent->message
&& hdr->thread->parent->message->env->x_label))
htmp = hdr->thread->parent->message;
if (htmp && str_casecmp (hdr->env->x_label,
htmp->env->x_label) == 0)
i = 0;
}
else
i = 0;
if (optional)
optional = i;
if (i)
mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
else
mutt_format_s (dest, destlen, prefix, "");
break;
default:
snprintf (dest, destlen, "%%%s%c", prefix, op);
break;
}
if (optional)
mutt_FormatString (dest, destlen, ifstring, hdr_format_str,
(unsigned long) hfi, flags);
else if (flags & M_FORMAT_OPTIONAL)
mutt_FormatString (dest, destlen, elsestring, hdr_format_str,
(unsigned long) hfi, flags);
return (src);
#undef THREAD_NEW
#undef THREAD_OLD
}
void
_mutt_make_string (char *dest, size_t destlen, const char *s, CONTEXT * ctx,
HEADER * hdr, format_flag flags)
{
struct hdr_format_info hfi;
hfi.hdr = hdr;
hfi.ctx = ctx;
mutt_FormatString (dest, destlen, s, hdr_format_str, (unsigned long) &hfi,
flags);
}
syntax highlighted by Code2HTML, v. 0.9.1