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.
10 #include <lib-lib/lib-lib.h>
12 #include <lib-mime/mime.h>
13 #include <lib-ui/curses.h>
14 #include <lib-mx/mx.h>
22 #include "mutt_idna.h"
24 int mutt_is_mail_list (address_t * addr)
26 if (!rx_list_match (UnMailLists, addr->mailbox))
27 return rx_list_match (MailLists, addr->mailbox);
31 int mutt_is_subscribed_list (address_t * addr)
33 if (!rx_list_match (UnMailLists, addr->mailbox)
34 && !rx_list_match (UnSubscribedLists, addr->mailbox))
35 return rx_list_match (SubscribedLists, addr->mailbox);
39 /* Search for a mailing list in the list of addresses pointed to by adr.
40 * If one is found, print pfx and the name of the list into buf, then
41 * return 1. Otherwise, simply return 0.
44 check_for_mailing_list (address_t * adr, const char *pfx, char *buf, int buflen)
46 for (; adr; adr = adr->next) {
47 if (mutt_is_subscribed_list (adr)) {
48 if (pfx && buf && buflen)
49 snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
56 /* Search for a mailing list in the list of addresses pointed to by adr.
57 * If one is found, print the address of the list into buf, then return 1.
58 * Otherwise, simply return 0.
60 static int check_for_mailing_list_addr (address_t * adr, char *buf, int buflen)
62 for (; adr; adr = adr->next) {
63 if (mutt_is_subscribed_list (adr)) {
65 snprintf (buf, buflen, "%s", adr->mailbox);
73 static int first_mailing_list (char *buf, ssize_t buflen, address_t * a)
75 for (; a; a = a->next) {
76 if (mutt_is_subscribed_list (a)) {
77 mutt_save_path (buf, buflen, a);
84 static void make_from (ENVELOPE * hdr, char *buf, ssize_t len, int do_lists)
88 me = mutt_addr_is_user (hdr->from);
91 if (check_for_mailing_list (hdr->to, "To ", buf, len))
93 if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
98 snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
99 else if (me && hdr->cc)
100 snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
102 m_strcpy(buf, len, mutt_get_name(hdr->from));
107 static void make_from_addr (ENVELOPE * hdr, char *buf, ssize_t len,
112 me = mutt_addr_is_user (hdr->from);
114 if (do_lists || me) {
115 if (check_for_mailing_list_addr (hdr->to, buf, len))
117 if (check_for_mailing_list_addr (hdr->cc, buf, len))
122 snprintf (buf, len, "%s", hdr->to->mailbox);
123 else if (me && hdr->cc)
124 snprintf (buf, len, "%s", hdr->cc->mailbox);
126 m_strcpy(buf, len, hdr->from->mailbox);
131 static int user_in_addr (address_t * a)
133 for (; a; a = a->next)
134 if (mutt_addr_is_user (a))
140 * 0: user is not in list
141 * 1: user is unique recipient
142 * 2: user is in the TO list
143 * 3: user is in the CC list
144 * 4: user is originator
145 * 5: sent to a subscribed mailinglist
147 int mutt_user_is_recipient (HEADER * h)
149 ENVELOPE *env = h->env;
151 if (!h->recip_valid) {
154 if (mutt_addr_is_user (env->from))
156 else if (user_in_addr (env->to)) {
157 if (env->to->next || env->cc)
158 h->recipient = 2; /* non-unique recipient */
160 h->recipient = 1; /* unique recipient */
162 else if (user_in_addr (env->cc))
164 else if (check_for_mailing_list (env->to, NULL, NULL, 0))
166 else if (check_for_mailing_list (env->cc, NULL, NULL, 0))
175 /* %a = address of author
176 * %A = reply-to address (if present; otherwise: address of author
177 * %b = filename of the originating folder
178 * %B = the list to which the letter was sent
179 * %c = size of message in bytes
180 * %C = current message number
181 * %d = date and time of message using $date_format and sender's timezone
182 * %D = date and time of message using $date_format and local timezone
183 * %e = current message number in thread
184 * %E = number of messages in current thread
185 * %f = entire from line
186 * %F = like %n, unless from self
187 * %g = newsgroup name (if compiled with nntp support)
189 * %I = initials of author
190 * %l = number of lines in the message
191 * %L = like %F, except `lists' are displayed first
192 * %m = number of messages in the mailbox
193 * %n = name of author
195 * %O = like %L, except using address instead of name
197 * %S = short message status (e.g., N/O/D/!/r/-)
198 * %t = `to:' field (recipients)
200 * %u = user (login) name of author
201 * %v = first name of author, unless from self
202 * %W = where user is (organization)
203 * %X = number of MIME attachments
204 * %y = `x-label:' field (if present)
205 * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
206 * %Z = status flags */
208 struct hdr_format_info {
214 hdr_format_str(char *dest, ssize_t destlen,
215 char op, const char *src, const char *prefix,
216 const char *ifstr, const char *elstr,
217 anytype data, format_flag flags)
219 struct hdr_format_info *hfi = data.ptr;
222 char fmt[STRING], buf2[STRING], ch, *p;
223 int do_locales, i, c;
224 int optional = (flags & M_FORMAT_OPTIONAL);
225 int threads = ((Sort & SORT_MASK) == SORT_THREADS);
226 int is_index = (flags & M_FORMAT_INDEX);
228 #define THREAD_NEW (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 1)
229 #define THREAD_OLD (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 2)
238 if (hdr->env->reply_to && hdr->env->reply_to->mailbox) {
239 mutt_format_s (dest, destlen, prefix,
240 mutt_addr_for_display (hdr->env->reply_to));
243 /* fall through if 'A' returns nothing */
246 if (hdr->env->from && hdr->env->from->mailbox) {
247 mutt_format_s (dest, destlen, prefix,
248 mutt_addr_for_display (hdr->env->from));
255 if (!first_mailing_list (dest, destlen, hdr->env->to) &&
256 !first_mailing_list (dest, destlen, hdr->env->cc))
259 m_strcpy(buf2, sizeof(buf2), dest);
260 mutt_format_s (dest, destlen, prefix, buf2);
263 /* fall through if 'B' returns nothing */
267 if ((p = strrchr (ctx->path, '/')))
268 m_strcpy(dest, destlen, p + 1);
270 m_strcpy(dest, destlen, ctx->path);
273 m_strcpy(dest, destlen, "(null)");
274 m_strcpy(buf2, sizeof(buf2), dest);
275 mutt_format_s (dest, destlen, prefix, buf2);
279 mutt_pretty_size (buf2, sizeof (buf2), (long) hdr->content->length);
280 mutt_format_s (dest, destlen, prefix, buf2);
284 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
285 snprintf (dest, destlen, fmt, hdr->msgno + 1);
295 /* preprocess $date_format to handle %Z */
303 cp = (op == 'd' || op == 'D') ? (NONULL (DateFmt)) : src;
312 while (len > 0 && (((op == 'd' || op == 'D') && *cp) ||
313 (op == '{' && *cp != '}') ||
314 (op == '[' && *cp != ']') ||
315 (op == '(' && *cp != ')') ||
316 (op == '<' && *cp != '>'))) {
319 if ((*cp == 'Z' || *cp == 'z') && (op == 'd' || op == '{')) {
321 sprintf (p, "%c%02u%02u", hdr->zoccident ? '-' : '+',
322 hdr->zhours, hdr->zminutes);
327 break; /* not enough space left */
336 break; /* not enough space */
347 if (do_locales && Locale)
348 setlocale (LC_TIME, Locale);
350 if (op == '[' || op == 'D')
351 tm = localtime (&hdr->date_sent);
353 tm = localtime (&hdr->received);
354 else if (op == '<') {
359 /* restore sender's time zone */
362 T -= (hdr->zhours * 3600 + hdr->zminutes * 60);
364 T += (hdr->zhours * 3600 + hdr->zminutes * 60);
368 strftime (buf2, sizeof (buf2), dest, tm);
371 setlocale (LC_TIME, "C");
373 mutt_format_s (dest, destlen, prefix, buf2);
374 if (len > 0 && op != 'd' && op != 'D') /* Skip ending op */
380 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
381 snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 1));
386 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
387 snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 0));
389 else if (mutt_messages_in_thread (ctx, hdr, 0) <= 1)
395 rfc822_addrcat(buf2, sizeof (buf2), hdr->env->from, 1);
396 mutt_format_s (dest, destlen, prefix, buf2);
401 make_from (hdr->env, buf2, sizeof (buf2), 0);
402 mutt_format_s (dest, destlen, prefix, buf2);
404 else if (mutt_addr_is_user (hdr->env->from))
410 mutt_format_s (dest, destlen, prefix,
411 hdr->env->newsgroups ? hdr->env->newsgroups : "");
416 /* (Hormel) spam score */
418 optional = hdr->env->spam ? 1 : 0;
421 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
423 mutt_format_s (dest, destlen, prefix, "");
428 mutt_format_s (dest, destlen, prefix,
429 hdr->env->message_id ? hdr->env->message_id : "<no.id>");
437 for (i = 0; hdr->env->from && hdr->env->from->personal &&
438 hdr->env->from->personal[i] && j < STRING - 1; i++) {
439 if (isalpha ((int) hdr->env->from->personal[i])) {
441 buf2[j++] = hdr->env->from->personal[i];
451 mutt_format_s (dest, destlen, prefix, buf2);
456 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
457 snprintf (dest, destlen, fmt, (int) hdr->lines);
459 else if (hdr->lines <= 0)
465 make_from (hdr->env, buf2, sizeof (buf2), 1);
466 mutt_format_s (dest, destlen, prefix, buf2);
468 else if (!check_for_mailing_list (hdr->env->to, NULL, NULL, 0) &&
469 !check_for_mailing_list (hdr->env->cc, NULL, NULL, 0)) {
476 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
477 snprintf (dest, destlen, fmt, ctx->msgcount);
480 m_strcpy(dest, destlen, "(null)");
484 mutt_format_s (dest, destlen, prefix, mutt_get_name (hdr->env->from));
489 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
490 snprintf (dest, destlen, fmt, hdr->score);
500 make_from_addr (hdr->env, buf2, sizeof (buf2), 1);
501 if (!option (OPTSAVEADDRESS) && (p = strpbrk (buf2, "%@")))
503 mutt_format_s (dest, destlen, prefix, buf2);
505 else if (!check_for_mailing_list_addr (hdr->env->to, NULL, 0) &&
506 !check_for_mailing_list_addr (hdr->env->cc, NULL, 0)) {
512 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
514 if (threads && is_index && hdr->collapsed && hdr->num_hidden > 1)
515 snprintf (dest, destlen, fmt, hdr->num_hidden);
516 else if (is_index && threads)
517 mutt_format_s (dest, destlen, prefix, " ");
522 if (!(threads && is_index && hdr->collapsed && hdr->num_hidden > 1))
529 if (flags & M_FORMAT_TREE && !hdr->collapsed) {
530 if (flags & M_FORMAT_FORCESUBJ) {
531 mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject));
532 snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
533 mutt_format_s_tree (dest, destlen, prefix, buf2);
536 mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
539 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject));
545 else if (hdr->attach_del)
547 else if (hdr->tagged)
549 else if (hdr->flagged)
551 else if (hdr->replied)
553 else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
560 /* FOO - this is probably unsafe, but we are not likely to have such
561 a short string passed into this routine */
568 if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
569 !check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2))) {
571 snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
572 else if (hdr->env->cc)
573 snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
575 mutt_format_s (dest, destlen, prefix, buf2);
579 i = mutt_user_is_recipient(hdr);
580 assert (i >= 0); /* help compiler to see c is initialized */
582 if (Charset_is_utf8) {
583 const char *s = Tochars;
585 snprintf (fmt, sizeof (fmt), "%%%slc", prefix);
587 c = m_ustrgetc(s, &s);
594 snprintf(dest, destlen, fmt, c);
596 snprintf(fmt, sizeof (fmt), "%%%sc", prefix);
597 snprintf(dest, destlen, fmt, i < m_strlen(Tochars) ? Tochars[i] : ' ');
602 if (hdr->env->from && hdr->env->from->mailbox) {
603 m_strcpy(buf2, sizeof(buf2), mutt_addr_for_display(hdr->env->from));
604 if ((p = strpbrk (buf2, "%@")))
609 mutt_format_s (dest, destlen, prefix, buf2);
613 if (mutt_addr_is_user (hdr->env->from)) {
615 mutt_format_s (buf2, sizeof (buf2), prefix,
616 mutt_get_name (hdr->env->to));
617 else if (hdr->env->cc)
618 mutt_format_s (buf2, sizeof (buf2), prefix,
619 mutt_get_name (hdr->env->cc));
624 mutt_format_s (buf2, sizeof (buf2), prefix,
625 mutt_get_name (hdr->env->from));
626 if ((p = strpbrk (buf2, " %@")))
628 mutt_format_s (dest, destlen, prefix, buf2);
633 mutt_format_s (dest, destlen, prefix,
634 hdr->env->organization ? hdr->env->organization : "");
635 else if (!hdr->env->organization)
643 /* The recursion allows messages without depth to return 0. */
645 optional = count != 0;
647 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
648 snprintf (dest, destlen, fmt, count);
656 if (hdr->security & GOODSIGN)
658 else if (hdr->security & ENCRYPT)
660 else if (hdr->security & SIGN)
662 else if (hdr->security & PGPKEY)
671 i = mutt_user_is_recipient(hdr);
672 assert (i >= 0); /* help compiler to see c is initialized */
674 if (Charset_is_utf8) {
675 const char *s = Tochars;
677 snprintf (fmt, sizeof (fmt), "%%%slc", prefix);
679 c = m_ustrgetc(s, &s);
686 c = i < m_strlen(Tochars) ? Tochars[i] : ' ';
689 snprintf(buf2, sizeof (buf2), Charset_is_utf8 ? "%c%c%lc" : "%c%c%c",
693 : ((hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
694 ? (hdr->replied ? 'r' : ' ')
695 : (hdr->old ? 'O' : 'N')))),
696 hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch), c);
697 mutt_format_s (dest, destlen, prefix, buf2);
702 optional = hdr->env->x_label ? 1 : 0;
704 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
708 if (hdr->env->x_label) {
709 i = 1; /* reduce reuse recycle */
711 if (flags & M_FORMAT_TREE
712 && (hdr->thread->prev && hdr->thread->prev->message
713 && hdr->thread->prev->message->env->x_label))
714 htmp = hdr->thread->prev->message;
715 else if (flags & M_FORMAT_TREE
716 && (hdr->thread->parent && hdr->thread->parent->message
717 && hdr->thread->parent->message->env->x_label))
718 htmp = hdr->thread->parent->message;
719 if (htmp && m_strcasecmp(hdr->env->x_label,
720 htmp->env->x_label) == 0)
730 mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
732 mutt_format_s (dest, destlen, prefix, "");
741 if (flags & M_FORMAT_OPTIONAL)
742 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
743 hdr_format_str, data, flags);
751 _mutt_make_string (char *dest, ssize_t destlen, const char *s, CONTEXT * ctx,
752 HEADER * hdr, format_flag flags)
754 struct hdr_format_info hfi;
759 m_strformat(dest, destlen, COLS - SW, s, hdr_format_str, &hfi, flags);