2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
15 #include "mutt_curses.h"
19 #include "mutt_crypt.h"
20 #include "mutt_idna.h"
31 int mutt_is_mail_list (ADDRESS * addr)
33 if (!rx_list_match (UnMailLists, addr->mailbox))
34 return rx_list_match (MailLists, addr->mailbox);
38 int mutt_is_subscribed_list (ADDRESS * addr)
40 if (!rx_list_match (UnMailLists, addr->mailbox)
41 && !rx_list_match (UnSubscribedLists, addr->mailbox))
42 return rx_list_match (SubscribedLists, addr->mailbox);
46 /* Search for a mailing list in the list of addresses pointed to by adr.
47 * If one is found, print pfx and the name of the list into buf, then
48 * return 1. Otherwise, simply return 0.
51 check_for_mailing_list (ADDRESS * adr, char *pfx, char *buf, int buflen)
53 for (; adr; adr = adr->next) {
54 if (mutt_is_subscribed_list (adr)) {
55 if (pfx && buf && buflen)
56 snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
63 /* Search for a mailing list in the list of addresses pointed to by adr.
64 * If one is found, print the address of the list into buf, then return 1.
65 * Otherwise, simply return 0.
67 static int check_for_mailing_list_addr (ADDRESS * adr, char *buf, int buflen)
69 for (; adr; adr = adr->next) {
70 if (mutt_is_subscribed_list (adr)) {
72 snprintf (buf, buflen, "%s", adr->mailbox);
80 static int first_mailing_list (char *buf, size_t buflen, ADDRESS * a)
82 for (; a; a = a->next) {
83 if (mutt_is_subscribed_list (a)) {
84 mutt_save_path (buf, buflen, a);
91 static void make_from (ENVELOPE * hdr, char *buf, size_t len, int do_lists)
95 me = mutt_addr_is_user (hdr->from);
98 if (check_for_mailing_list (hdr->to, "To ", buf, len))
100 if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
105 snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
106 else if (me && hdr->cc)
107 snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
109 strfcpy (buf, mutt_get_name (hdr->from), len);
114 static void make_from_addr (ENVELOPE * hdr, char *buf, size_t len,
119 me = mutt_addr_is_user (hdr->from);
121 if (do_lists || me) {
122 if (check_for_mailing_list_addr (hdr->to, buf, len))
124 if (check_for_mailing_list_addr (hdr->cc, buf, len))
129 snprintf (buf, len, "%s", hdr->to->mailbox);
130 else if (me && hdr->cc)
131 snprintf (buf, len, "%s", hdr->cc->mailbox);
133 strfcpy (buf, hdr->from->mailbox, len);
138 static int user_in_addr (ADDRESS * a)
140 for (; a; a = a->next)
141 if (mutt_addr_is_user (a))
147 * 0: user is not in list
148 * 1: user is unique recipient
149 * 2: user is in the TO list
150 * 3: user is in the CC list
151 * 4: user is originator
152 * 5: sent to a subscribed mailinglist
154 int mutt_user_is_recipient (HEADER * h)
156 ENVELOPE *env = h->env;
158 if (!h->recip_valid) {
161 if (mutt_addr_is_user (env->from))
163 else if (user_in_addr (env->to)) {
164 if (env->to->next || env->cc)
165 h->recipient = 2; /* non-unique recipient */
167 h->recipient = 1; /* unique recipient */
169 else if (user_in_addr (env->cc))
171 else if (check_for_mailing_list (env->to, NULL, NULL, 0))
173 else if (check_for_mailing_list (env->cc, NULL, NULL, 0))
182 /* %a = address of author
183 * %A = reply-to address (if present; otherwise: address of author
184 * %b = filename of the originating folder
185 * %B = the list to which the letter was sent
186 * %c = size of message in bytes
187 * %C = current message number
188 * %d = date and time of message using $date_format and sender's timezone
189 * %D = date and time of message using $date_format and local timezone
190 * %e = current message number in thread
191 * %E = number of messages in current thread
192 * %f = entire from line
193 * %F = like %n, unless from self
194 * %g = newsgroup name (if compiled with nntp support)
196 * %I = initials of author
197 * %l = number of lines in the message
198 * %L = like %F, except `lists' are displayed first
199 * %m = number of messages in the mailbox
200 * %n = name of author
202 * %O = like %L, except using address instead of name
204 * %S = short message status (e.g., N/O/D/!/r/-)
205 * %t = `to:' field (recipients)
207 * %u = user (login) name of author
208 * %v = first name of author, unless from self
209 * %W = where user is (organization)
210 * %y = `x-label:' field (if present)
211 * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
212 * %Z = status flags */
214 struct hdr_format_info {
219 static const char *hdr_format_str (char *dest,
224 const char *ifstring,
225 const char *elsestring,
226 unsigned long data, format_flag flags)
228 struct hdr_format_info *hfi = (struct hdr_format_info *) data;
231 char fmt[SHORT_STRING], buf2[SHORT_STRING], ch, *p;
233 int optional = (flags & M_FORMAT_OPTIONAL);
234 int threads = ((Sort & SORT_MASK) == SORT_THREADS);
235 int is_index = (flags & M_FORMAT_INDEX);
237 #define THREAD_NEW (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 1)
238 #define THREAD_OLD (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 2)
247 if (hdr->env->reply_to && hdr->env->reply_to->mailbox) {
248 mutt_format_s (dest, destlen, prefix,
249 mutt_addr_for_display (hdr->env->reply_to));
252 /* fall through if 'A' returns nothing */
255 if (hdr->env->from && hdr->env->from->mailbox) {
256 mutt_format_s (dest, destlen, prefix,
257 mutt_addr_for_display (hdr->env->from));
264 if (!first_mailing_list (dest, destlen, hdr->env->to) &&
265 !first_mailing_list (dest, destlen, hdr->env->cc))
268 strfcpy (buf2, dest, sizeof (buf2));
269 mutt_format_s (dest, destlen, prefix, buf2);
272 /* fall through if 'B' returns nothing */
276 if ((p = strrchr (ctx->path, '/')))
277 strfcpy (dest, p + 1, destlen);
279 strfcpy (dest, ctx->path, destlen);
282 strfcpy (dest, "(null)", destlen);
283 strfcpy (buf2, dest, sizeof (buf2));
284 mutt_format_s (dest, destlen, prefix, buf2);
288 mutt_pretty_size (buf2, sizeof (buf2), (long) hdr->content->length);
289 mutt_format_s (dest, destlen, prefix, buf2);
293 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
294 snprintf (dest, destlen, fmt, hdr->msgno + 1);
304 /* preprocess $date_format to handle %Z */
312 cp = (op == 'd' || op == 'D') ? (NONULL (DateFmt)) : src;
321 while (len > 0 && (((op == 'd' || op == 'D') && *cp) ||
322 (op == '{' && *cp != '}') ||
323 (op == '[' && *cp != ']') ||
324 (op == '(' && *cp != ')') ||
325 (op == '<' && *cp != '>'))) {
328 if ((*cp == 'Z' || *cp == 'z') && (op == 'd' || op == '{')) {
330 sprintf (p, "%c%02u%02u", hdr->zoccident ? '-' : '+',
331 hdr->zhours, hdr->zminutes);
336 break; /* not enough space left */
345 break; /* not enough space */
356 if (do_locales && Locale)
357 setlocale (LC_TIME, Locale);
359 if (op == '[' || op == 'D')
360 tm = localtime (&hdr->date_sent);
362 tm = localtime (&hdr->received);
363 else if (op == '<') {
368 /* restore sender's time zone */
371 T -= (hdr->zhours * 3600 + hdr->zminutes * 60);
373 T += (hdr->zhours * 3600 + hdr->zminutes * 60);
377 strftime (buf2, sizeof (buf2), dest, tm);
380 setlocale (LC_TIME, "C");
382 mutt_format_s (dest, destlen, prefix, buf2);
383 if (len > 0 && op != 'd' && op != 'D') /* Skip ending op */
389 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
390 snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 1));
395 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
396 snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 0));
398 else if (mutt_messages_in_thread (ctx, hdr, 0) <= 1)
404 rfc822_write_address (buf2, sizeof (buf2), hdr->env->from, 1);
405 mutt_format_s (dest, destlen, prefix, buf2);
410 make_from (hdr->env, buf2, sizeof (buf2), 0);
411 mutt_format_s (dest, destlen, prefix, buf2);
413 else if (mutt_addr_is_user (hdr->env->from))
419 mutt_format_s (dest, destlen, prefix,
420 hdr->env->newsgroups ? hdr->env->newsgroups : "");
425 /* (Hormel) spam score */
427 optional = hdr->env->spam ? 1 : 0;
430 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
432 mutt_format_s (dest, destlen, prefix, "");
437 mutt_format_s (dest, destlen, prefix,
438 hdr->env->message_id ? hdr->env->message_id : "<no.id>");
446 for (i = 0; hdr->env->from && hdr->env->from->personal &&
447 hdr->env->from->personal[i] && j < SHORT_STRING - 1; i++) {
448 if (isalpha ((int) hdr->env->from->personal[i])) {
450 buf2[j++] = hdr->env->from->personal[i];
460 mutt_format_s (dest, destlen, prefix, buf2);
465 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
466 snprintf (dest, destlen, fmt, (int) hdr->lines);
468 else if (hdr->lines <= 0)
474 make_from (hdr->env, buf2, sizeof (buf2), 1);
475 mutt_format_s (dest, destlen, prefix, buf2);
477 else if (!check_for_mailing_list (hdr->env->to, NULL, NULL, 0) &&
478 !check_for_mailing_list (hdr->env->cc, NULL, NULL, 0)) {
485 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
486 snprintf (dest, destlen, fmt, ctx->msgcount);
489 strfcpy (dest, "(null)", destlen);
493 mutt_format_s (dest, destlen, prefix, mutt_get_name (hdr->env->from));
498 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
499 snprintf (dest, destlen, fmt, hdr->score);
509 make_from_addr (hdr->env, buf2, sizeof (buf2), 1);
510 if (!option (OPTSAVEADDRESS) && (p = strpbrk (buf2, "%@")))
512 mutt_format_s (dest, destlen, prefix, buf2);
514 else if (!check_for_mailing_list_addr (hdr->env->to, NULL, 0) &&
515 !check_for_mailing_list_addr (hdr->env->cc, NULL, 0)) {
521 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
523 if (threads && is_index && hdr->collapsed && hdr->num_hidden > 1)
524 snprintf (dest, destlen, fmt, hdr->num_hidden);
525 else if (is_index && threads)
526 mutt_format_s (dest, destlen, prefix, " ");
531 if (!(threads && is_index && hdr->collapsed && hdr->num_hidden > 1))
538 if (flags & M_FORMAT_TREE && !hdr->collapsed) {
539 if (flags & M_FORMAT_FORCESUBJ) {
540 mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject));
541 snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
542 mutt_format_s_tree (dest, destlen, prefix, buf2);
545 mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
548 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject));
554 else if (hdr->attach_del)
556 else if (hdr->tagged)
558 else if (hdr->flagged)
560 else if (hdr->replied)
562 else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
569 /* FOO - this is probably unsafe, but we are not likely to have such
570 a short string passed into this routine */
577 if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
578 !check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2))) {
580 snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
581 else if (hdr->env->cc)
582 snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
584 mutt_format_s (dest, destlen, prefix, buf2);
588 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
589 snprintf (dest, destlen, fmt,
591 && ((i = mutt_user_is_recipient (hdr))) <
592 str_len (Tochars)) ? Tochars[i] : ' ');
596 if (hdr->env->from && hdr->env->from->mailbox) {
597 strfcpy (buf2, mutt_addr_for_display (hdr->env->from), sizeof (buf2));
598 if ((p = strpbrk (buf2, "%@")))
603 mutt_format_s (dest, destlen, prefix, buf2);
607 if (mutt_addr_is_user (hdr->env->from)) {
609 mutt_format_s (buf2, sizeof (buf2), prefix,
610 mutt_get_name (hdr->env->to));
611 else if (hdr->env->cc)
612 mutt_format_s (buf2, sizeof (buf2), prefix,
613 mutt_get_name (hdr->env->cc));
618 mutt_format_s (buf2, sizeof (buf2), prefix,
619 mutt_get_name (hdr->env->from));
620 if ((p = strpbrk (buf2, " %@")))
622 mutt_format_s (dest, destlen, prefix, buf2);
627 mutt_format_s (dest, destlen, prefix,
628 hdr->env->organization ? hdr->env->organization : "");
629 else if (!hdr->env->organization)
637 if (WithCrypto && hdr->security & GOODSIGN)
639 else if (WithCrypto && hdr->security & ENCRYPT)
641 else if (WithCrypto && hdr->security & SIGN)
643 else if ((WithCrypto & APPLICATION_PGP) && hdr->security & PGPKEY)
646 snprintf (buf2, sizeof (buf2),
647 "%c%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' :
650 && ctx->msgnotreadyet !=
653 replied ? 'r' : ' ') : (hdr->
657 hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
658 hdr->tagged ? '*' : (hdr->
659 flagged ? '!' : (Tochars
662 mutt_user_is_recipient
666 (hdr->content && hdr->content->type == TYPEMULTIPART) ?
668 mutt_format_s (dest, destlen, prefix, buf2);
673 optional = hdr->env->x_label ? 1 : 0;
675 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
679 if (hdr->env->x_label) {
680 i = 1; /* reduce reuse recycle */
682 if (flags & M_FORMAT_TREE
683 && (hdr->thread->prev && hdr->thread->prev->message
684 && hdr->thread->prev->message->env->x_label))
685 htmp = hdr->thread->prev->message;
686 else if (flags & M_FORMAT_TREE
687 && (hdr->thread->parent && hdr->thread->parent->message
688 && hdr->thread->parent->message->env->x_label))
689 htmp = hdr->thread->parent->message;
690 if (htmp && str_casecmp (hdr->env->x_label,
691 htmp->env->x_label) == 0)
701 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
703 mutt_format_s (dest, destlen, prefix, "");
708 snprintf (dest, destlen, "%%%s%c", prefix, op);
713 mutt_FormatString (dest, destlen, ifstring, hdr_format_str,
714 (unsigned long) hfi, flags);
715 else if (flags & M_FORMAT_OPTIONAL)
716 mutt_FormatString (dest, destlen, elsestring, hdr_format_str,
717 (unsigned long) hfi, flags);
725 _mutt_make_string (char *dest, size_t destlen, const char *s, CONTEXT * ctx,
726 HEADER * hdr, format_flag flags)
728 struct hdr_format_info hfi;
733 mutt_FormatString (dest, destlen, s, hdr_format_str, (unsigned long) &hfi,