2 * Copyright (C) 1999-2000 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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111, USA.
23 * Mixmaster support for Mutt
27 #include "mutt_curses.h"
28 #include "mutt_menu.h"
29 #include "mutt_regex.h"
38 #include <sys/types.h>
49 static REMAILER **mix_type2_list (size_t *l);
50 static REMAILER *mix_new_remailer (void);
51 static const char *mix_format_caps (REMAILER *r);
52 static int mix_chain_add (MIXCHAIN *chain, const char *s, REMAILER **type2_list);
53 static int mix_get_caps (const char *capstr);
54 static void mix_add_entry (REMAILER ***, REMAILER *, size_t *, size_t *);
55 static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num);
56 static void mix_free_remailer (REMAILER **r);
57 static void mix_free_type2_list (REMAILER ***ttlp);
58 static void mix_redraw_ce (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int i, short selected);
59 static void mix_redraw_chain (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int cur);
60 static void mix_redraw_head (MIXCHAIN *);
61 static void mix_screen_coordinates (REMAILER **type2_list, struct coord **, MIXCHAIN *, int);
63 static int mix_get_caps (const char *capstr)
72 caps |= MIX_CAP_COMPRESS;
76 caps |= MIX_CAP_MIDDLEMAN;
84 caps |= MIX_CAP_NEWSMAIL;
88 caps |= MIX_CAP_NEWSPOST;
95 if (*capstr) capstr++;
101 static void mix_add_entry (REMAILER ***type2_list, REMAILER *entry,
102 size_t *slots, size_t *used)
107 safe_realloc (type2_list, sizeof (REMAILER *) * (*slots));
110 (*type2_list)[(*used)++] = entry;
111 if (entry) entry->num = *used;
114 static REMAILER *mix_new_remailer (void)
116 return safe_calloc (1, sizeof (REMAILER));
119 static void mix_free_remailer (REMAILER **r)
121 FREE (&(*r)->shortname);
128 /* parse the type2.list as given by mixmaster -T */
130 static REMAILER **mix_type2_list (size_t *l)
136 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
137 char line[HUGE_STRING];
140 REMAILER **type2_list = NULL, *p;
141 size_t slots = 0, used = 0;
146 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
149 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
151 if ((mm_pid = mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1, devnull)) == -1)
157 /* first, generate the "random" remailer */
159 p = mix_new_remailer ();
160 p->shortname = safe_strdup ("<random>");
161 mix_add_entry (&type2_list, p, &slots, &used);
163 while (fgets (line, sizeof (line), fp))
165 p = mix_new_remailer ();
167 if (!(t = strtok (line, " \t\n")))
170 p->shortname = safe_strdup (t);
172 if (!(t = strtok (NULL, " \t\n")))
175 p->addr = safe_strdup (t);
177 if (!(t = strtok (NULL, " \t\n")))
180 if (!(t = strtok (NULL, " \t\n")))
183 p->ver = safe_strdup (t);
185 if (!(t = strtok (NULL, " \t\n")))
188 p->caps = mix_get_caps (t);
190 mix_add_entry (&type2_list, p, &slots, &used);
194 mix_free_remailer (&p);
199 mix_add_entry (&type2_list, NULL, &slots, &used);
200 mutt_wait_filter (mm_pid);
207 static void mix_free_type2_list (REMAILER ***ttlp)
210 REMAILER **type2_list = *ttlp;
212 for (i = 0; type2_list[i]; i++)
213 mix_free_remailer (&type2_list[i]);
219 #define MIX_HOFFSET 2
220 #define MIX_VOFFSET (LINES - 6)
221 #define MIX_MAXROW (LINES - 3)
224 static void mix_screen_coordinates (REMAILER **type2_list,
225 struct coord **coordsp,
230 struct coord *coords;
235 safe_realloc (coordsp, sizeof (struct coord) * chain->cl);
241 c = coords[i-1].c + strlen (type2_list[chain->ch[i-1]]->shortname) + 2;
251 for (; i < chain->cl; i++)
254 c += strlen (type2_list[chain->ch[i]]->shortname) + 2;
258 oc = c = MIX_HOFFSET;
269 static void mix_redraw_ce (REMAILER **type2_list,
270 struct coord *coords,
275 if (!coords || !chain)
278 if (coords[i].r < MIX_MAXROW)
282 SETCOLOR (MT_COLOR_INDICATOR);
284 SETCOLOR (MT_COLOR_NORMAL);
286 mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
287 SETCOLOR (MT_COLOR_NORMAL);
289 if (i + 1 < chain->cl)
294 static void mix_redraw_chain (REMAILER **type2_list,
295 struct coord *coords,
301 SETCOLOR (MT_COLOR_NORMAL);
302 BKGDSET (MT_COLOR_NORMAL);
304 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++)
310 for (i = 0; i < chain->cl; i++)
311 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
314 static void mix_redraw_head (MIXCHAIN *chain)
316 SETCOLOR (MT_COLOR_STATUS);
317 mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]", chain ? chain->cl : 0);
319 BKGDSET (MT_COLOR_STATUS);
322 BKGDSET (MT_COLOR_NORMAL);
323 SETCOLOR (MT_COLOR_NORMAL);
326 static const char *mix_format_caps (REMAILER *r)
328 static char capbuff[10];
331 if (r->caps & MIX_CAP_COMPRESS)
336 if (r->caps & MIX_CAP_MIDDLEMAN)
341 if (r->caps & MIX_CAP_NEWSPOST)
352 if (r->caps & MIX_CAP_NEWSMAIL)
369 * Format an entry for the remailer menu.
378 static const char *mix_entry_fmt (char *dest,
383 const char *ifstring,
384 const char *elsestring,
389 REMAILER *remailer = (REMAILER *) data;
390 int optional = (flags & M_FORMAT_OPTIONAL);
397 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
398 snprintf (dest, destlen, fmt, remailer->num);
404 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
405 snprintf (dest, destlen, fmt, mix_format_caps(remailer));
411 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
412 snprintf (dest, destlen, fmt, NONULL(remailer->shortname));
414 else if (!remailer->shortname)
420 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
421 snprintf (dest, destlen, fmt, NONULL(remailer->addr));
423 else if (!remailer->addr)
432 mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
433 else if (flags & M_FORMAT_OPTIONAL)
434 mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
440 static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num)
442 REMAILER **type2_list = (REMAILER **) menu->data;
443 mutt_FormatString (b, blen, NONULL (MixEntryFormat), mix_entry_fmt,
444 (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
447 static int mix_chain_add (MIXCHAIN *chain, const char *s,
448 REMAILER **type2_list)
452 if (chain->cl >= MAXMIXES)
455 if (!mutt_strcmp (s, "0") || !ascii_strcasecmp (s, "<random>"))
457 chain->ch[chain->cl++] = 0;
461 for (i = 0; type2_list[i]; i++)
463 if (!ascii_strcasecmp (s, type2_list[i]->shortname))
465 chain->ch[chain->cl++] = i;
470 /* replace unknown remailers by <random> */
473 chain->ch[chain->cl++] = 0;
478 static struct mapping_t RemailerHelp[] =
480 { N_("Append"), OP_MIX_APPEND },
481 { N_("Insert"), OP_MIX_INSERT },
482 { N_("Delete"), OP_MIX_DELETE },
483 { N_("Abort"), OP_EXIT },
484 { N_("OK"), OP_MIX_USE },
489 void mix_make_chain (LIST **chainp, int *redraw)
493 int c_cur = 0, c_old = 0;
497 REMAILER **type2_list = NULL;
500 struct coord *coords = NULL;
503 char helpstr[SHORT_STRING];
510 if (!(type2_list = mix_type2_list (&ttll)))
512 mutt_error _("Can't get mixmaster's type2.list!");
516 *redraw = REDRAW_FULL;
518 chain = safe_calloc (sizeof (MIXCHAIN), 1);
519 for (p = *chainp; p; p = p->next)
520 mix_chain_add (chain, (char *) p->data, type2_list);
522 mutt_free_list (chainp);
525 for (i = 0; i < chain->cl; i++)
527 if (chain->ch[i] >= ttll)
531 mix_screen_coordinates (type2_list, &coords, chain, 0);
533 menu = mutt_new_menu ();
534 menu->menu = MENU_MIX;
536 menu->make_entry = mix_entry;
538 menu->title = _("Select a remailer chain.");
539 menu->data = type2_list;
540 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
542 m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
546 if (menu->pagelen != m_len)
548 menu->pagelen = m_len;
549 menu->redraw = REDRAW_FULL;
554 mix_redraw_head (chain);
555 mix_redraw_chain (type2_list, coords, chain, c_cur);
558 else if (c_cur != c_old)
560 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
561 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
566 switch ((op = mutt_menuLoop (menu)))
570 menu_redraw_status (menu);
571 mix_redraw_head (chain);
572 mix_screen_coordinates (type2_list, &coords, chain, 0);
573 mix_redraw_chain (type2_list, coords, chain, c_cur);
574 menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1;
590 chain->ch[0] = menu->current;
591 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
595 if (chain->cl && chain->ch[chain->cl - 1] &&
596 (type2_list[chain->ch[chain->cl-1]]->caps & MIX_CAP_MIDDLEMAN))
598 mutt_error ( _("Error: %s can't be used as the final remailer of a chain."),
599 type2_list[chain->ch[chain->cl - 1]]->shortname);
608 case OP_GENERIC_SELECT_ENTRY:
611 if (chain->cl < MAXMIXES && c_cur < chain->cl)
617 if (chain->cl < MAXMIXES)
620 for (i = chain->cl - 1; i > c_cur; i--)
621 chain->ch[i] = chain->ch[i-1];
623 chain->ch[c_cur] = menu->current;
624 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
628 mutt_error ( _("Mixmaster chains are limited to %d elements."),
640 for (i = c_cur; i < chain->cl; i++)
641 chain->ch[i] = chain->ch[i+1];
643 if (c_cur == chain->cl && c_cur)
646 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
651 mutt_error _("The remailer chain is already empty.");
656 case OP_MIX_CHAIN_PREV:
661 mutt_error _("You already have the first chain element selected.");
666 case OP_MIX_CHAIN_NEXT:
668 if (chain->cl && c_cur < chain->cl - 1)
671 mutt_error _("You already have the last chain element selected.");
678 mutt_menuDestroy (&menu);
680 /* construct the remailer list */
684 for (i = 0; i < chain->cl; i++)
686 if ((j = chain->ch[i]))
687 t = type2_list[j]->shortname;
691 *chainp = mutt_add_list (*chainp, t);
695 mix_free_type2_list (&type2_list);
700 /* some safety checks before piping the message to mixmaster */
702 int mix_check_message (HEADER *msg)
705 short need_hostname = 0;
708 if (msg->env->cc || msg->env->bcc)
710 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
714 /* When using mixmaster, we MUST qualify any addresses since
715 * the message will be delivered through remote systems.
717 * use_domain won't be respected at this point, hidden_host will.
720 for (p = msg->env->to; p; p = p->next)
722 if (!p->group && strchr (p->mailbox, '@') == NULL)
732 if (!(fqdn = mutt_fqdn (1)))
734 mutt_error _("Please set the hostname variable to a proper value when using mixmaster!");
738 /* Cc and Bcc are empty at this point. */
739 rfc822_qualify (msg->env->to, fqdn);
740 rfc822_qualify (msg->env->reply_to, fqdn);
741 rfc822_qualify (msg->env->mail_followup_to, fqdn);
747 int mix_send_message (LIST *chain, const char *tempfile)
749 char cmd[HUGE_STRING];
750 char tmp[HUGE_STRING];
751 char cd_quoted[STRING];
754 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
756 for (i = 0; chain; chain = chain->next, i = 1)
758 strfcpy (tmp, cmd, sizeof (tmp));
759 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
760 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
763 if (!option (OPTNOCURSES))
766 if ((i = mutt_system (cmd)))
768 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
769 if (!option (OPTNOCURSES))
771 mutt_any_key_to_continue (NULL);
772 mutt_error _("Error sending message.");