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.
19 #include <lib-lib/rx.h>
20 #include <lib-lib/str.h>
22 #include <lib-mime/mime.h>
24 #include <lib-ui/curses.h>
31 #include <lib-crypt/crypt.h>
32 #include "mutt_idna.h"
34 int mutt_is_mail_list (address_t * addr)
36 if (!rx_list_match (UnMailLists, addr->mailbox))
37 return rx_list_match (MailLists, addr->mailbox);
41 int mutt_is_subscribed_list (address_t * addr)
43 if (!rx_list_match (UnMailLists, addr->mailbox)
44 && !rx_list_match (UnSubscribedLists, addr->mailbox))
45 return rx_list_match (SubscribedLists, addr->mailbox);
49 /* Search for a mailing list in the list of addresses pointed to by adr.
50 * If one is found, print pfx and the name of the list into buf, then
51 * return 1. Otherwise, simply return 0.
54 check_for_mailing_list (address_t * adr, const char *pfx, char *buf, int buflen)
56 for (; adr; adr = adr->next) {
57 if (mutt_is_subscribed_list (adr)) {
58 if (pfx && buf && buflen)
59 snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
66 /* Search for a mailing list in the list of addresses pointed to by adr.
67 * If one is found, print the address of the list into buf, then return 1.
68 * Otherwise, simply return 0.
70 static int check_for_mailing_list_addr (address_t * adr, char *buf, int buflen)
72 for (; adr; adr = adr->next) {
73 if (mutt_is_subscribed_list (adr)) {
75 snprintf (buf, buflen, "%s", adr->mailbox);
83 static int first_mailing_list (char *buf, ssize_t buflen, address_t * a)
85 for (; a; a = a->next) {
86 if (mutt_is_subscribed_list (a)) {
87 mutt_save_path (buf, buflen, a);
94 static void make_from (ENVELOPE * hdr, char *buf, ssize_t len, int do_lists)
98 me = mutt_addr_is_user (hdr->from);
100 if (do_lists || me) {
101 if (check_for_mailing_list (hdr->to, "To ", buf, len))
103 if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
108 snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
109 else if (me && hdr->cc)
110 snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
112 m_strcpy(buf, len, mutt_get_name(hdr->from));
117 static void make_from_addr (ENVELOPE * hdr, char *buf, ssize_t len,
122 me = mutt_addr_is_user (hdr->from);
124 if (do_lists || me) {
125 if (check_for_mailing_list_addr (hdr->to, buf, len))
127 if (check_for_mailing_list_addr (hdr->cc, buf, len))
132 snprintf (buf, len, "%s", hdr->to->mailbox);
133 else if (me && hdr->cc)
134 snprintf (buf, len, "%s", hdr->cc->mailbox);
136 m_strcpy(buf, len, hdr->from->mailbox);
141 static int user_in_addr (address_t * a)
143 for (; a; a = a->next)
144 if (mutt_addr_is_user (a))
150 * 0: user is not in list
151 * 1: user is unique recipient
152 * 2: user is in the TO list
153 * 3: user is in the CC list
154 * 4: user is originator
155 * 5: sent to a subscribed mailinglist
157 int mutt_user_is_recipient (HEADER * h)
159 ENVELOPE *env = h->env;
161 if (!h->recip_valid) {
164 if (mutt_addr_is_user (env->from))
166 else if (user_in_addr (env->to)) {
167 if (env->to->next || env->cc)
168 h->recipient = 2; /* non-unique recipient */
170 h->recipient = 1; /* unique recipient */
172 else if (user_in_addr (env->cc))
174 else if (check_for_mailing_list (env->to, NULL, NULL, 0))
176 else if (check_for_mailing_list (env->cc, NULL, NULL, 0))
185 /* %a = address of author
186 * %A = reply-to address (if present; otherwise: address of author
187 * %b = filename of the originating folder
188 * %B = the list to which the letter was sent
189 * %c = size of message in bytes
190 * %C = current message number
191 * %d = date and time of message using $date_format and sender's timezone
192 * %D = date and time of message using $date_format and local timezone
193 * %e = current message number in thread
194 * %E = number of messages in current thread
195 * %f = entire from line
196 * %F = like %n, unless from self
197 * %g = newsgroup name (if compiled with nntp support)
199 * %I = initials of author
200 * %l = number of lines in the message
201 * %L = like %F, except `lists' are displayed first
202 * %m = number of messages in the mailbox
203 * %n = name of author
205 * %O = like %L, except using address instead of name
207 * %S = short message status (e.g., N/O/D/!/r/-)
208 * %t = `to:' field (recipients)
210 * %u = user (login) name of author
211 * %v = first name of author, unless from self
212 * %W = where user is (organization)
213 * %X = number of MIME attachments
214 * %y = `x-label:' field (if present)
215 * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
216 * %Z = status flags */
218 struct hdr_format_info {
223 static const char *hdr_format_str (char *dest,
228 const char *ifstring,
229 const char *elsestring,
230 unsigned long data, format_flag flags)
232 struct hdr_format_info *hfi = (struct hdr_format_info *) data;
235 char fmt[SHORT_STRING], buf2[SHORT_STRING], ch, *p;
237 int optional = (flags & M_FORMAT_OPTIONAL);
238 int threads = ((Sort & SORT_MASK) == SORT_THREADS);
239 int is_index = (flags & M_FORMAT_INDEX);
241 #define THREAD_NEW (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 1)
242 #define THREAD_OLD (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 2)
251 if (hdr->env->reply_to && hdr->env->reply_to->mailbox) {
252 mutt_format_s (dest, destlen, prefix,
253 mutt_addr_for_display (hdr->env->reply_to));
256 /* fall through if 'A' returns nothing */
259 if (hdr->env->from && hdr->env->from->mailbox) {
260 mutt_format_s (dest, destlen, prefix,
261 mutt_addr_for_display (hdr->env->from));
268 if (!first_mailing_list (dest, destlen, hdr->env->to) &&
269 !first_mailing_list (dest, destlen, hdr->env->cc))
272 m_strcpy(buf2, sizeof(buf2), dest);
273 mutt_format_s (dest, destlen, prefix, buf2);
276 /* fall through if 'B' returns nothing */
280 if ((p = strrchr (ctx->path, '/')))
281 m_strcpy(dest, destlen, p + 1);
283 m_strcpy(dest, destlen, ctx->path);
286 m_strcpy(dest, destlen, "(null)");
287 m_strcpy(buf2, sizeof(buf2), dest);
288 mutt_format_s (dest, destlen, prefix, buf2);
292 mutt_pretty_size (buf2, sizeof (buf2), (long) hdr->content->length);
293 mutt_format_s (dest, destlen, prefix, buf2);
297 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
298 snprintf (dest, destlen, fmt, hdr->msgno + 1);
308 /* preprocess $date_format to handle %Z */
316 cp = (op == 'd' || op == 'D') ? (NONULL (DateFmt)) : src;
325 while (len > 0 && (((op == 'd' || op == 'D') && *cp) ||
326 (op == '{' && *cp != '}') ||
327 (op == '[' && *cp != ']') ||
328 (op == '(' && *cp != ')') ||
329 (op == '<' && *cp != '>'))) {
332 if ((*cp == 'Z' || *cp == 'z') && (op == 'd' || op == '{')) {
334 sprintf (p, "%c%02u%02u", hdr->zoccident ? '-' : '+',
335 hdr->zhours, hdr->zminutes);
340 break; /* not enough space left */
349 break; /* not enough space */
360 if (do_locales && Locale)
361 setlocale (LC_TIME, Locale);
363 if (op == '[' || op == 'D')
364 tm = localtime (&hdr->date_sent);
366 tm = localtime (&hdr->received);
367 else if (op == '<') {
372 /* restore sender's time zone */
375 T -= (hdr->zhours * 3600 + hdr->zminutes * 60);
377 T += (hdr->zhours * 3600 + hdr->zminutes * 60);
381 strftime (buf2, sizeof (buf2), dest, tm);
384 setlocale (LC_TIME, "C");
386 mutt_format_s (dest, destlen, prefix, buf2);
387 if (len > 0 && op != 'd' && op != 'D') /* Skip ending op */
393 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
394 snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 1));
399 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
400 snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 0));
402 else if (mutt_messages_in_thread (ctx, hdr, 0) <= 1)
408 rfc822_write_address (buf2, sizeof (buf2), hdr->env->from, 1);
409 mutt_format_s (dest, destlen, prefix, buf2);
414 make_from (hdr->env, buf2, sizeof (buf2), 0);
415 mutt_format_s (dest, destlen, prefix, buf2);
417 else if (mutt_addr_is_user (hdr->env->from))
423 mutt_format_s (dest, destlen, prefix,
424 hdr->env->newsgroups ? hdr->env->newsgroups : "");
429 /* (Hormel) spam score */
431 optional = hdr->env->spam ? 1 : 0;
434 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
436 mutt_format_s (dest, destlen, prefix, "");
441 mutt_format_s (dest, destlen, prefix,
442 hdr->env->message_id ? hdr->env->message_id : "<no.id>");
450 for (i = 0; hdr->env->from && hdr->env->from->personal &&
451 hdr->env->from->personal[i] && j < SHORT_STRING - 1; i++) {
452 if (isalpha ((int) hdr->env->from->personal[i])) {
454 buf2[j++] = hdr->env->from->personal[i];
464 mutt_format_s (dest, destlen, prefix, buf2);
469 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
470 snprintf (dest, destlen, fmt, (int) hdr->lines);
472 else if (hdr->lines <= 0)
478 make_from (hdr->env, buf2, sizeof (buf2), 1);
479 mutt_format_s (dest, destlen, prefix, buf2);
481 else if (!check_for_mailing_list (hdr->env->to, NULL, NULL, 0) &&
482 !check_for_mailing_list (hdr->env->cc, NULL, NULL, 0)) {
489 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
490 snprintf (dest, destlen, fmt, ctx->msgcount);
493 m_strcpy(dest, destlen, "(null)");
497 mutt_format_s (dest, destlen, prefix, mutt_get_name (hdr->env->from));
502 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
503 snprintf (dest, destlen, fmt, hdr->score);
513 make_from_addr (hdr->env, buf2, sizeof (buf2), 1);
514 if (!option (OPTSAVEADDRESS) && (p = strpbrk (buf2, "%@")))
516 mutt_format_s (dest, destlen, prefix, buf2);
518 else if (!check_for_mailing_list_addr (hdr->env->to, NULL, 0) &&
519 !check_for_mailing_list_addr (hdr->env->cc, NULL, 0)) {
525 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
527 if (threads && is_index && hdr->collapsed && hdr->num_hidden > 1)
528 snprintf (dest, destlen, fmt, hdr->num_hidden);
529 else if (is_index && threads)
530 mutt_format_s (dest, destlen, prefix, " ");
535 if (!(threads && is_index && hdr->collapsed && hdr->num_hidden > 1))
542 if (flags & M_FORMAT_TREE && !hdr->collapsed) {
543 if (flags & M_FORMAT_FORCESUBJ) {
544 mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject));
545 snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
546 mutt_format_s_tree (dest, destlen, prefix, buf2);
549 mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
552 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject));
558 else if (hdr->attach_del)
560 else if (hdr->tagged)
562 else if (hdr->flagged)
564 else if (hdr->replied)
566 else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
573 /* FOO - this is probably unsafe, but we are not likely to have such
574 a short string passed into this routine */
581 if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
582 !check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2))) {
584 snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
585 else if (hdr->env->cc)
586 snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
588 mutt_format_s (dest, destlen, prefix, buf2);
592 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
593 snprintf (dest, destlen, fmt,
595 && ((i = mutt_user_is_recipient (hdr))) <
596 m_strlen(Tochars)) ? Tochars[i] : ' ');
600 if (hdr->env->from && hdr->env->from->mailbox) {
601 m_strcpy(buf2, sizeof(buf2), mutt_addr_for_display(hdr->env->from));
602 if ((p = strpbrk (buf2, "%@")))
607 mutt_format_s (dest, destlen, prefix, buf2);
611 if (mutt_addr_is_user (hdr->env->from)) {
613 mutt_format_s (buf2, sizeof (buf2), prefix,
614 mutt_get_name (hdr->env->to));
615 else if (hdr->env->cc)
616 mutt_format_s (buf2, sizeof (buf2), prefix,
617 mutt_get_name (hdr->env->cc));
622 mutt_format_s (buf2, sizeof (buf2), prefix,
623 mutt_get_name (hdr->env->from));
624 if ((p = strpbrk (buf2, " %@")))
626 mutt_format_s (dest, destlen, prefix, buf2);
631 mutt_format_s (dest, destlen, prefix,
632 hdr->env->organization ? hdr->env->organization : "");
633 else if (!hdr->env->organization)
641 if (option (OPTCOUNTATTACH)) {
642 if (!hdr->content->parts)
643 mutt_parse_mime_message(ctx, hdr);
644 count = mutt_count_body_parts(hdr, 0);
647 /* The recursion allows messages without depth to return 0. */
649 optional = count != 0;
651 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
652 snprintf (dest, destlen, fmt, count);
660 if (hdr->security & GOODSIGN)
662 else if (hdr->security & ENCRYPT)
664 else if (hdr->security & SIGN)
666 else if (hdr->security & PGPKEY)
669 snprintf(buf2, sizeof (buf2), "%c%c%c",
673 : ((hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
674 ? (hdr->replied ? 'r' : ' ')
675 : (hdr->old ? 'O' : 'N')))),
676 hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
678 : (hdr->flagged ? '!'
679 : (Tochars && ((i = mutt_user_is_recipient(hdr)) < m_strlen(Tochars))
680 ? Tochars[i] : ' ')));
681 mutt_format_s (dest, destlen, prefix, buf2);
686 optional = hdr->env->x_label ? 1 : 0;
688 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
692 if (hdr->env->x_label) {
693 i = 1; /* reduce reuse recycle */
695 if (flags & M_FORMAT_TREE
696 && (hdr->thread->prev && hdr->thread->prev->message
697 && hdr->thread->prev->message->env->x_label))
698 htmp = hdr->thread->prev->message;
699 else if (flags & M_FORMAT_TREE
700 && (hdr->thread->parent && hdr->thread->parent->message
701 && hdr->thread->parent->message->env->x_label))
702 htmp = hdr->thread->parent->message;
703 if (htmp && m_strcasecmp(hdr->env->x_label,
704 htmp->env->x_label) == 0)
714 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
716 mutt_format_s (dest, destlen, prefix, "");
721 snprintf (dest, destlen, "%%%s%c", prefix, op);
726 mutt_FormatString (dest, destlen, ifstring, hdr_format_str,
727 (unsigned long) hfi, flags);
728 else if (flags & M_FORMAT_OPTIONAL)
729 mutt_FormatString (dest, destlen, elsestring, hdr_format_str,
730 (unsigned long) hfi, flags);
738 _mutt_make_string (char *dest, ssize_t destlen, const char *s, CONTEXT * ctx,
739 HEADER * hdr, format_flag flags)
741 struct hdr_format_info hfi;
746 mutt_FormatString (dest, destlen, s, hdr_format_str, (unsigned long) &hfi,