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
31 #include "mutt_curses.h"
32 #include "mutt_menu.h"
33 #include "mutt_regex.h"
42 #include <sys/types.h>
52 static REMAILER **mix_type2_list (size_t * l);
53 static REMAILER *mix_new_remailer (void);
54 static const char *mix_format_caps (REMAILER * r);
55 static int mix_chain_add (MIXCHAIN * chain, const char *s,
56 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,
63 MIXCHAIN * chain, int i, short selected);
64 static void mix_redraw_chain (REMAILER ** type2_list, struct coord *coords,
65 MIXCHAIN * chain, int cur);
66 static void mix_redraw_head (MIXCHAIN *);
67 static void mix_screen_coordinates (REMAILER ** type2_list, struct coord **,
70 static int mix_get_caps (const char *capstr)
77 caps |= MIX_CAP_COMPRESS;
81 caps |= MIX_CAP_MIDDLEMAN;
88 caps |= MIX_CAP_NEWSMAIL;
92 caps |= MIX_CAP_NEWSPOST;
106 static void mix_add_entry (REMAILER *** type2_list, REMAILER * entry,
107 size_t * slots, size_t * used)
109 if (*used == *slots) {
111 safe_realloc (type2_list, sizeof (REMAILER *) * (*slots));
114 (*type2_list)[(*used)++] = entry;
119 static REMAILER *mix_new_remailer (void)
121 return safe_calloc (1, sizeof (REMAILER));
124 static void mix_free_remailer (REMAILER ** r)
126 FREE (&(*r)->shortname);
133 /* parse the type2.list as given by mixmaster -T */
135 static REMAILER **mix_type2_list (size_t * l)
141 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
142 char line[HUGE_STRING];
145 REMAILER **type2_list = NULL, *p;
146 size_t slots = 0, used = 0;
151 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
154 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
157 mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1,
163 /* first, generate the "random" remailer */
165 p = mix_new_remailer ();
166 p->shortname = safe_strdup ("<random>");
167 mix_add_entry (&type2_list, p, &slots, &used);
169 while (fgets (line, sizeof (line), fp)) {
170 p = mix_new_remailer ();
172 if (!(t = strtok (line, " \t\n")))
175 p->shortname = safe_strdup (t);
177 if (!(t = strtok (NULL, " \t\n")))
180 p->addr = safe_strdup (t);
182 if (!(t = strtok (NULL, " \t\n")))
185 if (!(t = strtok (NULL, " \t\n")))
188 p->ver = safe_strdup (t);
190 if (!(t = strtok (NULL, " \t\n")))
193 p->caps = mix_get_caps (t);
195 mix_add_entry (&type2_list, p, &slots, &used);
199 mix_free_remailer (&p);
204 mix_add_entry (&type2_list, NULL, &slots, &used);
205 mutt_wait_filter (mm_pid);
212 static void mix_free_type2_list (REMAILER *** ttlp)
215 REMAILER **type2_list = *ttlp;
217 for (i = 0; type2_list[i]; i++)
218 mix_free_remailer (&type2_list[i]);
224 #define MIX_HOFFSET 2
225 #define MIX_VOFFSET (LINES - 6)
226 #define MIX_MAXROW (LINES - 3)
229 static void mix_screen_coordinates (REMAILER ** type2_list,
230 struct coord **coordsp,
231 MIXCHAIN * chain, int i)
234 struct coord *coords;
239 safe_realloc (coordsp, sizeof (struct coord) * chain->cl);
245 coords[i - 1].c + strlen (type2_list[chain->ch[i - 1]]->shortname) + 2;
254 for (; i < chain->cl; i++) {
256 c += strlen (type2_list[chain->ch[i]]->shortname) + 2;
259 oc = c = MIX_HOFFSET;
270 static void mix_redraw_ce (REMAILER ** type2_list,
271 struct coord *coords,
272 MIXCHAIN * chain, int i, short selected)
274 if (!coords || !chain)
277 if (coords[i].r < MIX_MAXROW) {
280 SETCOLOR (MT_COLOR_INDICATOR);
282 SETCOLOR (MT_COLOR_NORMAL);
284 mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
285 SETCOLOR (MT_COLOR_NORMAL);
287 if (i + 1 < chain->cl)
292 static void mix_redraw_chain (REMAILER ** type2_list,
293 struct coord *coords, MIXCHAIN * chain, int cur)
297 SETCOLOR (MT_COLOR_NORMAL);
298 BKGDSET (MT_COLOR_NORMAL);
300 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) {
305 for (i = 0; i < chain->cl; i++)
306 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
309 static void mix_redraw_head (MIXCHAIN * chain)
311 SETCOLOR (MT_COLOR_STATUS);
312 mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]",
313 chain ? chain->cl : 0);
315 BKGDSET (MT_COLOR_STATUS);
318 BKGDSET (MT_COLOR_NORMAL);
319 SETCOLOR (MT_COLOR_NORMAL);
322 static const char *mix_format_caps (REMAILER * r)
324 static char capbuff[10];
327 if (r->caps & MIX_CAP_COMPRESS)
332 if (r->caps & MIX_CAP_MIDDLEMAN)
337 if (r->caps & MIX_CAP_NEWSPOST) {
346 if (r->caps & MIX_CAP_NEWSMAIL) {
361 * Format an entry for the remailer menu.
370 static const char *mix_entry_fmt (char *dest,
375 const char *ifstring,
376 const char *elsestring,
377 unsigned long data, format_flag flags)
380 REMAILER *remailer = (REMAILER *) data;
381 int optional = (flags & M_FORMAT_OPTIONAL);
386 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
387 snprintf (dest, destlen, fmt, remailer->num);
392 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
393 snprintf (dest, destlen, fmt, mix_format_caps (remailer));
398 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
399 snprintf (dest, destlen, fmt, NONULL (remailer->shortname));
401 else if (!remailer->shortname)
406 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
407 snprintf (dest, destlen, fmt, NONULL (remailer->addr));
409 else if (!remailer->addr)
418 mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
419 else if (flags & M_FORMAT_OPTIONAL)
420 mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
426 static void mix_entry (char *b, size_t blen, MUTTMENU * menu, int num)
428 REMAILER **type2_list = (REMAILER **) menu->data;
430 mutt_FormatString (b, blen, NONULL (MixEntryFormat), mix_entry_fmt,
431 (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
434 static int mix_chain_add (MIXCHAIN * chain, const char *s,
435 REMAILER ** type2_list)
439 if (chain->cl >= MAXMIXES)
442 if (!mutt_strcmp (s, "0") || !ascii_strcasecmp (s, "<random>")) {
443 chain->ch[chain->cl++] = 0;
447 for (i = 0; type2_list[i]; i++) {
448 if (!ascii_strcasecmp (s, type2_list[i]->shortname)) {
449 chain->ch[chain->cl++] = i;
454 /* replace unknown remailers by <random> */
457 chain->ch[chain->cl++] = 0;
462 static struct mapping_t RemailerHelp[] = {
463 {N_("Append"), OP_MIX_APPEND},
464 {N_("Insert"), OP_MIX_INSERT},
465 {N_("Delete"), OP_MIX_DELETE},
466 {N_("Abort"), OP_EXIT},
467 {N_("OK"), OP_MIX_USE},
472 void mix_make_chain (LIST ** chainp, int *redraw)
476 int c_cur = 0, c_old = 0;
480 REMAILER **type2_list = NULL;
483 struct coord *coords = NULL;
486 char helpstr[SHORT_STRING];
493 if (!(type2_list = mix_type2_list (&ttll))) {
494 mutt_error _("Can't get mixmaster's type2.list!");
499 *redraw = REDRAW_FULL;
501 chain = safe_calloc (sizeof (MIXCHAIN), 1);
502 for (p = *chainp; p; p = p->next)
503 mix_chain_add (chain, (char *) p->data, type2_list);
505 mutt_free_list (chainp);
508 for (i = 0; i < chain->cl; i++) {
509 if (chain->ch[i] >= ttll)
513 mix_screen_coordinates (type2_list, &coords, chain, 0);
515 menu = mutt_new_menu ();
516 menu->menu = MENU_MIX;
518 menu->make_entry = mix_entry;
520 menu->title = _("Select a remailer chain.");
521 menu->data = type2_list;
523 mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
525 m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
528 if (menu->pagelen != m_len) {
529 menu->pagelen = m_len;
530 menu->redraw = REDRAW_FULL;
534 mix_redraw_head (chain);
535 mix_redraw_chain (type2_list, coords, chain, c_cur);
538 else if (c_cur != c_old) {
539 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
540 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
545 switch ((op = mutt_menuLoop (menu))) {
548 menu_redraw_status (menu);
549 mix_redraw_head (chain);
550 mix_screen_coordinates (type2_list, &coords, chain, 0);
551 mix_redraw_chain (type2_list, coords, chain, c_cur);
552 menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1;
567 chain->ch[0] = menu->current;
568 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
572 if (chain->cl && chain->ch[chain->cl - 1] &&
573 (type2_list[chain->ch[chain->cl - 1]]->caps & MIX_CAP_MIDDLEMAN))
576 ("Error: %s can't be used as the final remailer of a chain."),
577 type2_list[chain->ch[chain->cl - 1]]->shortname);
585 case OP_GENERIC_SELECT_ENTRY:
588 if (chain->cl < MAXMIXES && c_cur < chain->cl)
594 if (chain->cl < MAXMIXES) {
596 for (i = chain->cl - 1; i > c_cur; i--)
597 chain->ch[i] = chain->ch[i - 1];
599 chain->ch[c_cur] = menu->current;
600 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
604 mutt_error (_("Mixmaster chains are limited to %d elements."),
615 for (i = c_cur; i < chain->cl; i++)
616 chain->ch[i] = chain->ch[i + 1];
618 if (c_cur == chain->cl && c_cur)
621 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
625 mutt_error _("The remailer chain is already empty.");
630 case OP_MIX_CHAIN_PREV:
635 mutt_error _("You already have the first chain element selected.");
640 case OP_MIX_CHAIN_NEXT:
642 if (chain->cl && c_cur < chain->cl - 1)
645 mutt_error _("You already have the last chain element selected.");
652 mutt_menuDestroy (&menu);
654 /* construct the remailer list */
657 for (i = 0; i < chain->cl; i++) {
658 if ((j = chain->ch[i]))
659 t = type2_list[j]->shortname;
663 *chainp = mutt_add_list (*chainp, t);
667 mix_free_type2_list (&type2_list);
672 /* some safety checks before piping the message to mixmaster */
674 int mix_check_message (HEADER * msg)
677 short need_hostname = 0;
680 if (msg->env->cc || msg->env->bcc) {
681 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
686 /* When using mixmaster, we MUST qualify any addresses since
687 * the message will be delivered through remote systems.
689 * use_domain won't be respected at this point, hidden_host will.
692 for (p = msg->env->to; p; p = p->next) {
693 if (!p->group && strchr (p->mailbox, '@') == NULL) {
701 if (!(fqdn = mutt_fqdn (1))) {
704 ("Please set the hostname variable to a proper value when using mixmaster!");
708 /* Cc and Bcc are empty at this point. */
709 rfc822_qualify (msg->env->to, fqdn);
710 rfc822_qualify (msg->env->reply_to, fqdn);
711 rfc822_qualify (msg->env->mail_followup_to, fqdn);
717 int mix_send_message (LIST * chain, const char *tempfile)
719 char cmd[HUGE_STRING];
720 char tmp[HUGE_STRING];
721 char cd_quoted[STRING];
724 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
726 for (i = 0; chain; chain = chain->next, i = 1) {
727 strfcpy (tmp, cmd, sizeof (tmp));
728 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
729 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
732 if (!option (OPTNOCURSES))
735 if ((i = mutt_system (cmd))) {
736 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
737 if (!option (OPTNOCURSES)) {
738 mutt_any_key_to_continue (NULL);
739 mutt_error _("Error sending message.");