2 * Copyright (C) 1999-2001 Thomas Roessler <roessler@does-not-exist.org>
4 * This program is free software; you can redistribute it
5 * and/or modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later
10 * This program is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 * PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * Mixmaster support for Mutt
31 #include "mutt_curses.h"
32 #include "mutt_menu.h"
33 #include "mutt_regex.h"
42 #include <sys/types.h>
53 static REMAILER **mix_type2_list (size_t *l);
54 static REMAILER *mix_new_remailer (void);
55 static const char *mix_format_caps (REMAILER *r);
56 static int mix_chain_add (MIXCHAIN *chain, const char *s, REMAILER **type2_list);
57 static int mix_get_caps (const char *capstr);
58 static void mix_add_entry (REMAILER ***, REMAILER *, size_t *, size_t *);
59 static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num);
60 static void mix_free_remailer (REMAILER **r);
61 static void mix_free_type2_list (REMAILER ***ttlp);
62 static void mix_redraw_ce (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int i, short selected);
63 static void mix_redraw_chain (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int cur);
64 static void mix_redraw_head (MIXCHAIN *);
65 static void mix_screen_coordinates (REMAILER **type2_list, struct coord **, MIXCHAIN *, int);
67 static int mix_get_caps (const char *capstr)
76 caps |= MIX_CAP_COMPRESS;
80 caps |= MIX_CAP_MIDDLEMAN;
88 caps |= MIX_CAP_NEWSMAIL;
92 caps |= MIX_CAP_NEWSPOST;
99 if (*capstr) capstr++;
105 static void mix_add_entry (REMAILER ***type2_list, REMAILER *entry,
106 size_t *slots, size_t *used)
111 safe_realloc (type2_list, sizeof (REMAILER *) * (*slots));
114 (*type2_list)[(*used)++] = entry;
115 if (entry) entry->num = *used;
118 static REMAILER *mix_new_remailer (void)
120 return safe_calloc (1, sizeof (REMAILER));
123 static void mix_free_remailer (REMAILER **r)
125 FREE (&(*r)->shortname);
129 FREE (r); /* __FREE_CHECKED__ */
132 /* parse the type2.list as given by mixmaster -T */
134 static REMAILER **mix_type2_list (size_t *l)
140 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
141 char line[HUGE_STRING];
144 REMAILER **type2_list = NULL, *p;
145 size_t slots = 0, used = 0;
150 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
153 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
155 if ((mm_pid = mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1, devnull)) == -1)
161 /* first, generate the "random" remailer */
163 p = mix_new_remailer ();
164 p->shortname = safe_strdup ("<random>");
165 mix_add_entry (&type2_list, p, &slots, &used);
167 while (fgets (line, sizeof (line), fp))
169 p = mix_new_remailer ();
171 if (!(t = strtok (line, " \t\n")))
174 p->shortname = safe_strdup (t);
176 if (!(t = strtok (NULL, " \t\n")))
179 p->addr = safe_strdup (t);
181 if (!(t = strtok (NULL, " \t\n")))
184 if (!(t = strtok (NULL, " \t\n")))
187 p->ver = safe_strdup (t);
189 if (!(t = strtok (NULL, " \t\n")))
192 p->caps = mix_get_caps (t);
194 mix_add_entry (&type2_list, p, &slots, &used);
198 mix_free_remailer (&p);
203 mix_add_entry (&type2_list, NULL, &slots, &used);
204 mutt_wait_filter (mm_pid);
211 static void mix_free_type2_list (REMAILER ***ttlp)
214 REMAILER **type2_list = *ttlp;
216 for (i = 0; type2_list[i]; i++)
217 mix_free_remailer (&type2_list[i]);
219 FREE (type2_list); /* __FREE_CHECKED__ */
223 #define MIX_HOFFSET 2
224 #define MIX_VOFFSET (LINES - 6)
225 #define MIX_MAXROW (LINES - 3)
228 static void mix_screen_coordinates (REMAILER **type2_list,
229 struct coord **coordsp,
234 struct coord *coords;
239 safe_realloc (coordsp, sizeof (struct coord) * chain->cl);
245 c = coords[i-1].c + strlen (type2_list[chain->ch[i-1]]->shortname) + 2;
255 for (; i < chain->cl; i++)
258 c += strlen (type2_list[chain->ch[i]]->shortname) + 2;
262 oc = c = MIX_HOFFSET;
273 static void mix_redraw_ce (REMAILER **type2_list,
274 struct coord *coords,
279 if (!coords || !chain)
282 if (coords[i].r < MIX_MAXROW)
286 SETCOLOR (MT_COLOR_INDICATOR);
288 SETCOLOR (MT_COLOR_NORMAL);
290 mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
291 SETCOLOR (MT_COLOR_NORMAL);
293 if (i + 1 < chain->cl)
298 static void mix_redraw_chain (REMAILER **type2_list,
299 struct coord *coords,
305 SETCOLOR (MT_COLOR_NORMAL);
306 BKGDSET (MT_COLOR_NORMAL);
308 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++)
314 for (i = 0; i < chain->cl; i++)
315 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
318 static void mix_redraw_head (MIXCHAIN *chain)
320 SETCOLOR (MT_COLOR_STATUS);
321 mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]", chain ? chain->cl : 0);
323 BKGDSET (MT_COLOR_STATUS);
326 BKGDSET (MT_COLOR_NORMAL);
327 SETCOLOR (MT_COLOR_NORMAL);
330 static const char *mix_format_caps (REMAILER *r)
332 static char capbuff[10];
335 if (r->caps & MIX_CAP_COMPRESS)
340 if (r->caps & MIX_CAP_MIDDLEMAN)
345 if (r->caps & MIX_CAP_NEWSPOST)
356 if (r->caps & MIX_CAP_NEWSMAIL)
373 * Format an entry for the remailer menu.
382 static const char *mix_entry_fmt (char *dest,
388 const char *ifstring,
389 const char *elsestring,
394 REMAILER *remailer = (REMAILER *) data;
395 int optional = (flags & M_FORMAT_OPTIONAL);
402 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
403 snprintf (dest, destlen, fmt, remailer->num);
409 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
410 snprintf (dest, destlen, fmt, mix_format_caps(remailer));
416 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
417 snprintf (dest, destlen, fmt, NONULL(remailer->shortname));
419 else if (!remailer->shortname)
425 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
426 snprintf (dest, destlen, fmt, NONULL(remailer->addr));
428 else if (!remailer->addr)
437 mutt_FormatString (dest, destlen, col, ifstring, mutt_attach_fmt, data, 0);
438 else if (flags & M_FORMAT_OPTIONAL)
439 mutt_FormatString (dest, destlen, col, elsestring, mutt_attach_fmt, data, 0);
445 static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num)
447 REMAILER **type2_list = (REMAILER **) menu->data;
448 mutt_FormatString (b, blen, 0, NONULL (MixEntryFormat), mix_entry_fmt,
449 (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
452 static int mix_chain_add (MIXCHAIN *chain, const char *s,
453 REMAILER **type2_list)
457 if (chain->cl >= MAXMIXES)
460 if (!mutt_strcmp (s, "0") || !ascii_strcasecmp (s, "<random>"))
462 chain->ch[chain->cl++] = 0;
466 for (i = 0; type2_list[i]; i++)
468 if (!ascii_strcasecmp (s, type2_list[i]->shortname))
470 chain->ch[chain->cl++] = i;
475 /* replace unknown remailers by <random> */
478 chain->ch[chain->cl++] = 0;
483 static struct mapping_t RemailerHelp[] =
485 { N_("Append"), OP_MIX_APPEND },
486 { N_("Insert"), OP_MIX_INSERT },
487 { N_("Delete"), OP_MIX_DELETE },
488 { N_("Abort"), OP_EXIT },
489 { N_("OK"), OP_MIX_USE },
494 void mix_make_chain (LIST **chainp, int *redraw)
498 int c_cur = 0, c_old = 0;
502 REMAILER **type2_list = NULL;
505 struct coord *coords = NULL;
508 char helpstr[LONG_STRING];
515 if (!(type2_list = mix_type2_list (&ttll)))
517 mutt_error _("Can't get mixmaster's type2.list!");
521 *redraw = REDRAW_FULL;
523 chain = safe_calloc (sizeof (MIXCHAIN), 1);
524 for (p = *chainp; p; p = p->next)
525 mix_chain_add (chain, (char *) p->data, type2_list);
527 mutt_free_list (chainp);
530 for (i = 0; i < chain->cl; i++)
532 if (chain->ch[i] >= ttll)
536 mix_screen_coordinates (type2_list, &coords, chain, 0);
538 menu = mutt_new_menu (MENU_MIX);
540 menu->make_entry = mix_entry;
542 menu->title = _("Select a remailer chain.");
543 menu->data = type2_list;
544 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
546 m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
550 if (menu->pagelen != m_len)
552 menu->pagelen = m_len;
553 menu->redraw = REDRAW_FULL;
558 mix_redraw_head (chain);
559 mix_redraw_chain (type2_list, coords, chain, c_cur);
562 else if (c_cur != c_old)
564 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
565 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
570 switch ((op = mutt_menuLoop (menu)))
574 menu_redraw_status (menu);
575 mix_redraw_head (chain);
576 mix_screen_coordinates (type2_list, &coords, chain, 0);
577 mix_redraw_chain (type2_list, coords, chain, c_cur);
578 menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1;
594 chain->ch[0] = menu->current;
595 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
599 if (chain->cl && chain->ch[chain->cl - 1] &&
600 (type2_list[chain->ch[chain->cl-1]]->caps & MIX_CAP_MIDDLEMAN))
602 mutt_error ( _("Error: %s can't be used as the final remailer of a chain."),
603 type2_list[chain->ch[chain->cl - 1]]->shortname);
612 case OP_GENERIC_SELECT_ENTRY:
615 if (chain->cl < MAXMIXES && c_cur < chain->cl)
621 if (chain->cl < MAXMIXES)
624 for (i = chain->cl - 1; i > c_cur; i--)
625 chain->ch[i] = chain->ch[i-1];
627 chain->ch[c_cur] = menu->current;
628 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
632 mutt_error ( _("Mixmaster chains are limited to %d elements."),
644 for (i = c_cur; i < chain->cl; i++)
645 chain->ch[i] = chain->ch[i+1];
647 if (c_cur == chain->cl && c_cur)
650 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
655 mutt_error _("The remailer chain is already empty.");
660 case OP_MIX_CHAIN_PREV:
665 mutt_error _("You already have the first chain element selected.");
670 case OP_MIX_CHAIN_NEXT:
672 if (chain->cl && c_cur < chain->cl - 1)
675 mutt_error _("You already have the last chain element selected.");
682 mutt_menuDestroy (&menu);
684 /* construct the remailer list */
688 for (i = 0; i < chain->cl; i++)
690 if ((j = chain->ch[i]))
691 t = type2_list[j]->shortname;
695 *chainp = mutt_add_list (*chainp, t);
699 mix_free_type2_list (&type2_list);
704 /* some safety checks before piping the message to mixmaster */
706 int mix_check_message (HEADER *msg)
709 short need_hostname = 0;
712 if (msg->env->cc || msg->env->bcc)
714 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
718 /* When using mixmaster, we MUST qualify any addresses since
719 * the message will be delivered through remote systems.
721 * use_domain won't be respected at this point, hidden_host will.
724 for (p = msg->env->to; p; p = p->next)
726 if (!p->group && strchr (p->mailbox, '@') == NULL)
736 if (!(fqdn = mutt_fqdn (1)))
738 mutt_error _("Please set the hostname variable to a proper value when using mixmaster!");
742 /* Cc and Bcc are empty at this point. */
743 rfc822_qualify (msg->env->to, fqdn);
744 rfc822_qualify (msg->env->reply_to, fqdn);
745 rfc822_qualify (msg->env->mail_followup_to, fqdn);
751 int mix_send_message (LIST *chain, const char *tempfile)
753 char cmd[HUGE_STRING];
754 char tmp[HUGE_STRING];
755 char cd_quoted[STRING];
758 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
760 for (i = 0; chain; chain = chain->next, i = 1)
762 strfcpy (tmp, cmd, sizeof (tmp));
763 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
764 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
767 if (!option (OPTNOCURSES))
770 if ((i = mutt_system (cmd)))
772 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
773 if (!option (OPTNOCURSES))
775 mutt_any_key_to_continue (NULL);
776 mutt_error _("Error sending message.");