2 * Copyright (C) 1996-2000,2002,2007 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "mutt_curses.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
39 int mutt_is_mail_list (ADDRESS *addr)
41 if (!mutt_match_rx_list (addr->mailbox, UnMailLists))
42 return mutt_match_rx_list (addr->mailbox, MailLists);
46 int mutt_is_subscribed_list (ADDRESS *addr)
48 if (!mutt_match_rx_list (addr->mailbox, UnMailLists)
49 && !mutt_match_rx_list (addr->mailbox, UnSubscribedLists))
50 return mutt_match_rx_list (addr->mailbox, SubscribedLists);
54 /* Search for a mailing list in the list of addresses pointed to by adr.
55 * If one is found, print pfx and the name of the list into buf, then
56 * return 1. Otherwise, simply return 0.
59 check_for_mailing_list (ADDRESS *adr, char *pfx, char *buf, int buflen)
61 for (; adr; adr = adr->next)
63 if (mutt_is_subscribed_list (adr))
65 if (pfx && buf && buflen)
66 snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
73 /* Search for a mailing list in the list of addresses pointed to by adr.
74 * If one is found, print the address of the list into buf, then return 1.
75 * Otherwise, simply return 0.
78 check_for_mailing_list_addr (ADDRESS *adr, char *buf, int buflen)
80 for (; adr; adr = adr->next)
82 if (mutt_is_subscribed_list (adr))
85 snprintf (buf, buflen, "%s", adr->mailbox);
93 static int first_mailing_list (char *buf, size_t buflen, ADDRESS *a)
95 for (; a; a = a->next)
97 if (mutt_is_subscribed_list (a))
99 mutt_save_path (buf, buflen, a);
106 static void make_from (ENVELOPE *hdr, char *buf, size_t len, int do_lists)
110 me = mutt_addr_is_user (hdr->from);
114 if (check_for_mailing_list (hdr->to, "To ", buf, len))
116 if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
121 snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
122 else if (me && hdr->cc)
123 snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
124 else if (me && hdr->bcc)
125 snprintf (buf, len, "Bcc %s", mutt_get_name (hdr->bcc));
127 strfcpy (buf, mutt_get_name (hdr->from), len);
132 static void make_from_addr (ENVELOPE *hdr, char *buf, size_t len, int do_lists)
136 me = mutt_addr_is_user (hdr->from);
140 if (check_for_mailing_list_addr (hdr->to, buf, len))
142 if (check_for_mailing_list_addr (hdr->cc, buf, len))
147 snprintf (buf, len, "%s", hdr->to->mailbox);
148 else if (me && hdr->cc)
149 snprintf (buf, len, "%s", hdr->cc->mailbox);
151 strfcpy (buf, hdr->from->mailbox, len);
156 static int user_in_addr (ADDRESS *a)
158 for (; a; a = a->next)
159 if (mutt_addr_is_user (a))
165 * 0: user is not in list
166 * 1: user is unique recipient
167 * 2: user is in the TO list
168 * 3: user is in the CC list
169 * 4: user is originator
170 * 5: sent to a subscribed mailinglist
172 int mutt_user_is_recipient (HEADER *h)
174 ENVELOPE *env = h->env;
180 if (mutt_addr_is_user (env->from))
182 else if (user_in_addr (env->to))
184 if (env->to->next || env->cc)
185 h->recipient = 2; /* non-unique recipient */
187 h->recipient = 1; /* unique recipient */
189 else if (user_in_addr (env->cc))
191 else if (check_for_mailing_list (env->to, NULL, NULL, 0))
193 else if (check_for_mailing_list (env->cc, NULL, NULL, 0))
202 /* %a = address of author
203 * %A = reply-to address (if present; otherwise: address of author
204 * %b = filename of the originating folder
205 * %B = the list to which the letter was sent
206 * %c = size of message in bytes
207 * %C = current message number
208 * %d = date and time of message using $date_format and sender's timezone
209 * %D = date and time of message using $date_format and local timezone
210 * %e = current message number in thread
211 * %E = number of messages in current thread
212 * %f = entire from line
213 * %F = like %n, unless from self
215 * %l = number of lines in the message
216 * %L = like %F, except `lists' are displayed first
217 * %m = number of messages in the mailbox
218 * %n = name of author
220 * %O = like %L, except using address instead of name
221 * %P = progress indicator for builtin pager
223 * %S = short message status (e.g., N/O/D/!/r/-)
224 * %t = `to:' field (recipients)
226 * %u = user (login) name of author
227 * %v = first name of author, unless from self
228 * %X = number of MIME attachments
229 * %y = `x-label:' field (if present)
230 * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
231 * %Z = status flags */
234 hdr_format_str (char *dest,
240 const char *ifstring,
241 const char *elsestring,
245 struct hdr_format_info *hfi = (struct hdr_format_info *) data;
248 char fmt[SHORT_STRING], buf2[LONG_STRING], ch, *p;
250 int optional = (flags & M_FORMAT_OPTIONAL);
251 int threads = ((Sort & SORT_MASK) == SORT_THREADS);
252 int is_index = (flags & M_FORMAT_INDEX);
253 #define THREAD_NEW (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 1)
254 #define THREAD_OLD (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 2)
264 if(hdr->env->reply_to && hdr->env->reply_to->mailbox)
266 mutt_format_s (dest, destlen, prefix, mutt_addr_for_display (hdr->env->reply_to));
269 /* fall through if 'A' returns nothing */
272 if(hdr->env->from && hdr->env->from->mailbox)
274 mutt_format_s (dest, destlen, prefix, mutt_addr_for_display (hdr->env->from));
281 if (!first_mailing_list (dest, destlen, hdr->env->to) &&
282 !first_mailing_list (dest, destlen, hdr->env->cc))
286 strfcpy (buf2, dest, sizeof(buf2));
287 mutt_format_s (dest, destlen, prefix, buf2);
290 /* fall through if 'B' returns nothing */
295 if ((p = strrchr (ctx->path, '/')))
296 strfcpy (dest, p + 1, destlen);
298 strfcpy (dest, ctx->path, destlen);
301 strfcpy(dest, "(null)", destlen);
302 strfcpy (buf2, dest, sizeof(buf2));
303 mutt_format_s (dest, destlen, prefix, buf2);
307 mutt_pretty_size (buf2, sizeof (buf2), (long) hdr->content->length);
308 mutt_format_s (dest, destlen, prefix, buf2);
312 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
313 snprintf (dest, destlen, fmt, hdr->msgno + 1);
323 /* preprocess $date_format to handle %Z */
331 cp = (op == 'd' || op == 'D') ? (NONULL (DateFmt)) : src;
341 while (len > 0 && (((op == 'd' || op == 'D') && *cp) ||
342 (op == '{' && *cp != '}') ||
343 (op == '[' && *cp != ']') ||
344 (op == '(' && *cp != ')') ||
345 (op == '<' && *cp != '>')))
350 if ((*cp == 'Z' || *cp == 'z') && (op == 'd' || op == '{'))
354 sprintf (p, "%c%02u%02u", hdr->zoccident ? '-' : '+',
355 hdr->zhours, hdr->zminutes);
360 break; /* not enough space left */
371 break; /* not enough space */
383 if (do_locales && Locale)
384 setlocale (LC_TIME, Locale);
386 if (op == '[' || op == 'D')
387 tm = localtime (&hdr->date_sent);
389 tm = localtime (&hdr->received);
397 /* restore sender's time zone */
400 T -= (hdr->zhours * 3600 + hdr->zminutes * 60);
402 T += (hdr->zhours * 3600 + hdr->zminutes * 60);
406 strftime (buf2, sizeof (buf2), dest, tm);
409 setlocale (LC_TIME, "C");
411 mutt_format_s (dest, destlen, prefix, buf2);
412 if (len > 0 && op != 'd' && op != 'D') /* Skip ending op */
418 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
419 snprintf (dest, destlen, fmt, mutt_messages_in_thread(ctx, hdr, 1));
425 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
426 snprintf (dest, destlen, fmt, mutt_messages_in_thread(ctx, hdr, 0));
428 else if (mutt_messages_in_thread(ctx, hdr, 0) <= 1)
434 rfc822_write_address (buf2, sizeof (buf2), hdr->env->from, 1);
435 mutt_format_s (dest, destlen, prefix, buf2);
441 make_from (hdr->env, buf2, sizeof (buf2), 0);
442 mutt_format_s (dest, destlen, prefix, buf2);
444 else if (mutt_addr_is_user (hdr->env->from))
449 /* (Hormel) spam score */
451 optional = hdr->env->spam ? 1 : 0;
454 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
456 mutt_format_s (dest, destlen, prefix, "");
461 mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
467 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
468 snprintf (dest, destlen, fmt, (int) hdr->lines);
470 else if (hdr->lines <= 0)
477 make_from (hdr->env, buf2, sizeof (buf2), 1);
478 mutt_format_s (dest, destlen, prefix, buf2);
480 else if (!check_for_mailing_list (hdr->env->to, NULL, NULL, 0) &&
481 !check_for_mailing_list (hdr->env->cc, NULL, NULL, 0))
490 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
491 snprintf (dest, destlen, fmt, ctx->msgcount);
494 strfcpy(dest, "(null)", destlen);
498 mutt_format_s (dest, destlen, prefix, mutt_get_name (hdr->env->from));
504 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
505 snprintf (dest, destlen, fmt, hdr->score);
517 make_from_addr (hdr->env, buf2, sizeof (buf2), 1);
518 if (!option (OPTSAVEADDRESS) && (p = strpbrk (buf2, "%@")))
520 mutt_format_s (dest, destlen, prefix, buf2);
522 else if (!check_for_mailing_list_addr (hdr->env->to, NULL, 0) &&
523 !check_for_mailing_list_addr (hdr->env->cc, NULL, 0))
530 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
533 if (threads && is_index && hdr->collapsed && hdr->num_hidden > 1)
534 snprintf (dest, destlen, fmt, hdr->num_hidden);
535 else if (is_index && threads)
536 mutt_format_s (dest, destlen, prefix, " ");
542 if (!(threads && is_index && hdr->collapsed && hdr->num_hidden > 1))
548 strfcpy(dest, NONULL(hfi->pager_progress), destlen);
553 if (flags & M_FORMAT_TREE && !hdr->collapsed)
555 if (flags & M_FORMAT_FORCESUBJ)
557 mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject));
558 snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
559 mutt_format_s_tree (dest, destlen, prefix, buf2);
562 mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
565 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject));
571 else if (hdr->attach_del)
573 else if (hdr->tagged)
575 else if (hdr->flagged)
577 else if (hdr->replied)
579 else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
586 /* FOO - this is probably unsafe, but we are not likely to have such
587 a short string passed into this routine */
594 if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
595 !check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2)))
598 snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
599 else if (hdr->env->cc)
600 snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
602 mutt_format_s (dest, destlen, prefix, buf2);
606 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
607 snprintf (dest, destlen, fmt,
608 (Tochars && ((i = mutt_user_is_recipient (hdr))) < mutt_strlen (Tochars)) ? Tochars[i] : ' ');
612 if (hdr->env->from && hdr->env->from->mailbox)
614 strfcpy (buf2, mutt_addr_for_display (hdr->env->from), sizeof (buf2));
615 if ((p = strpbrk (buf2, "%@")))
620 mutt_format_s (dest, destlen, prefix, buf2);
624 if (mutt_addr_is_user (hdr->env->from))
627 mutt_format_s (buf2, sizeof (buf2), prefix, mutt_get_name (hdr->env->to));
628 else if (hdr->env->cc)
629 mutt_format_s (buf2, sizeof (buf2), prefix, mutt_get_name (hdr->env->cc));
634 mutt_format_s (buf2, sizeof (buf2), prefix, mutt_get_name (hdr->env->from));
635 if ((p = strpbrk (buf2, " %@")))
637 mutt_format_s (dest, destlen, prefix, buf2);
644 if (WithCrypto && hdr->security & GOODSIGN)
646 else if (WithCrypto && hdr->security & ENCRYPT)
648 else if (WithCrypto && hdr->security & SIGN)
650 else if ((WithCrypto & APPLICATION_PGP) && hdr->security & PGPKEY)
653 snprintf (buf2, sizeof (buf2),
654 "%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' :
655 ((hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
656 ? (hdr->replied ? 'r' : ' ') : (hdr->old ? 'O' : 'N')))),
657 hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
659 (hdr->flagged ? '!' :
660 (Tochars && ((i = mutt_user_is_recipient (hdr)) < mutt_strlen (Tochars)) ? Tochars[i] : ' ')));
661 mutt_format_s (dest, destlen, prefix, buf2);
666 int count = mutt_count_body_parts (ctx, hdr);
668 /* The recursion allows messages without depth to return 0. */
670 optional = count != 0;
672 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
673 snprintf (dest, destlen, fmt, count);
679 optional = hdr->env->x_label ? 1 : 0;
681 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
685 if (hdr->env->x_label)
687 i = 1; /* reduce reuse recycle */
689 if (flags & M_FORMAT_TREE
690 && (hdr->thread->prev && hdr->thread->prev->message
691 && hdr->thread->prev->message->env->x_label))
692 htmp = hdr->thread->prev->message;
693 else if (flags & M_FORMAT_TREE
694 && (hdr->thread->parent && hdr->thread->parent->message
695 && hdr->thread->parent->message->env->x_label))
696 htmp = hdr->thread->parent->message;
697 if (htmp && mutt_strcasecmp (hdr->env->x_label,
698 htmp->env->x_label) == 0)
708 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
710 mutt_format_s (dest, destlen, prefix, "");
715 snprintf (dest, destlen, "%%%s%c", prefix, op);
720 mutt_FormatString (dest, destlen, col, ifstring, hdr_format_str, (unsigned long) hfi, flags);
721 else if (flags & M_FORMAT_OPTIONAL)
722 mutt_FormatString (dest, destlen, col, elsestring, hdr_format_str, (unsigned long) hfi, flags);
730 _mutt_make_string (char *dest, size_t destlen, const char *s, CONTEXT *ctx, HEADER *hdr, format_flag flags)
732 struct hdr_format_info hfi;
736 hfi.pager_progress = 0;
738 mutt_FormatString (dest, destlen, 0, s, hdr_format_str, (unsigned long) &hfi, flags);
742 mutt_make_string_info (char *dst, size_t dstlen, const char *s, struct hdr_format_info *hfi, format_flag flags)
744 mutt_FormatString (dst, dstlen, 0, s, hdr_format_str, (unsigned long) hfi, flags);