/* * Copyright (C) 1999-2000 Thomas Roessler * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* * Mixmaster support for Mutt */ #if HAVE_CONFIG_H # include "config.h" #endif #include "mutt.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mutt_regex.h" #include "mapping.h" #include "remailer.h" #include #include #include #include #include #include #ifdef MIXMASTER struct coord { short r, c; }; static REMAILER **mix_type2_list (size_t *l); static REMAILER *mix_new_remailer (void); static const char *mix_format_caps (REMAILER *r); static int mix_chain_add (MIXCHAIN *chain, const char *s, REMAILER **type2_list); static int mix_get_caps (const char *capstr); static void mix_add_entry (REMAILER ***, REMAILER *, size_t *, size_t *); static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num); static void mix_free_remailer (REMAILER **r); static void mix_free_type2_list (REMAILER ***ttlp); static void mix_redraw_ce (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int i, short selected); static void mix_redraw_chain (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int cur); static void mix_redraw_head (MIXCHAIN *); static void mix_screen_coordinates (REMAILER **type2_list, struct coord **, MIXCHAIN *, int); static int mix_get_caps (const char *capstr) { int caps = 0; while (*capstr) { switch (*capstr) { case 'C': caps |= MIX_CAP_COMPRESS; break; case 'M': caps |= MIX_CAP_MIDDLEMAN; break; case 'N': { switch (*++capstr) { case 'm': caps |= MIX_CAP_NEWSMAIL; break; case 'p': caps |= MIX_CAP_NEWSPOST; break; } } } if (*capstr) capstr++; } return caps; } static void mix_add_entry (REMAILER ***type2_list, REMAILER *entry, size_t *slots, size_t *used) { if (*used == *slots) { *slots += 5; safe_realloc (type2_list, sizeof (REMAILER *) * (*slots)); } (*type2_list)[(*used)++] = entry; if (entry) entry->num = *used; } static REMAILER *mix_new_remailer (void) { return safe_calloc (1, sizeof (REMAILER)); } static void mix_free_remailer (REMAILER **r) { FREE (&(*r)->shortname); FREE (&(*r)->addr); FREE (&(*r)->ver); FREE (r); /* __FREE_CHECKED__ */ } /* parse the type2.list as given by mixmaster -T */ static REMAILER **mix_type2_list (size_t *l) { FILE *fp; pid_t mm_pid; int devnull; char cmd[HUGE_STRING + _POSIX_PATH_MAX]; char line[HUGE_STRING]; char *t; REMAILER **type2_list = NULL, *p; size_t slots = 0, used = 0; if (!l) return NULL; if ((devnull = open ("/dev/null", O_RDWR)) == -1) return NULL; snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster); if ((mm_pid = mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1, devnull)) == -1) { close (devnull); return NULL; } /* first, generate the "random" remailer */ p = mix_new_remailer (); p->shortname = safe_strdup (""); mix_add_entry (&type2_list, p, &slots, &used); while (fgets (line, sizeof (line), fp)) { p = mix_new_remailer (); if (!(t = strtok (line, " \t\n"))) goto problem; p->shortname = safe_strdup (t); if (!(t = strtok (NULL, " \t\n"))) goto problem; p->addr = safe_strdup (t); if (!(t = strtok (NULL, " \t\n"))) goto problem; if (!(t = strtok (NULL, " \t\n"))) goto problem; p->ver = safe_strdup (t); if (!(t = strtok (NULL, " \t\n"))) goto problem; p->caps = mix_get_caps (t); mix_add_entry (&type2_list, p, &slots, &used); continue; problem: mix_free_remailer (&p); } *l = used; mix_add_entry (&type2_list, NULL, &slots, &used); mutt_wait_filter (mm_pid); close (devnull); return type2_list; } static void mix_free_type2_list (REMAILER ***ttlp) { int i; REMAILER **type2_list = *ttlp; for (i = 0; type2_list[i]; i++) mix_free_remailer (&type2_list[i]); FREE (type2_list); /* __FREE_CHECKED__ */ } #define MIX_HOFFSET 2 #define MIX_VOFFSET (LINES - 6) #define MIX_MAXROW (LINES - 3) static void mix_screen_coordinates (REMAILER **type2_list, struct coord **coordsp, MIXCHAIN *chain, int i) { short c, r, oc; struct coord *coords; if (!chain->cl) return; safe_realloc (coordsp, sizeof (struct coord) * chain->cl); coords = *coordsp; if (i) { c = coords[i-1].c + strlen (type2_list[chain->ch[i-1]]->shortname) + 2; r = coords[i-1].r; } else { r = MIX_VOFFSET; c = MIX_HOFFSET; } for (; i < chain->cl; i++) { oc = c; c += strlen (type2_list[chain->ch[i]]->shortname) + 2; if (c >= COLS) { oc = c = MIX_HOFFSET; r++; } coords[i].c = oc; coords[i].r = r; } } static void mix_redraw_ce (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int i, short selected) { if (!coords || !chain) return; if (coords[i].r < MIX_MAXROW) { if (selected) SETCOLOR (MT_COLOR_INDICATOR); else SETCOLOR (MT_COLOR_NORMAL); mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname); SETCOLOR (MT_COLOR_NORMAL); if (i + 1 < chain->cl) addstr (", "); } } static void mix_redraw_chain (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int cur) { int i; SETCOLOR (MT_COLOR_NORMAL); BKGDSET (MT_COLOR_NORMAL); for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) { move (i, 0); clrtoeol (); } for (i = 0; i < chain->cl; i++) mix_redraw_ce (type2_list, coords, chain, i, i == cur); } static void mix_redraw_head (MIXCHAIN *chain) { SETCOLOR (MT_COLOR_STATUS); mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]", chain ? chain->cl : 0); BKGDSET (MT_COLOR_STATUS); clrtoeol (); BKGDSET (MT_COLOR_NORMAL); SETCOLOR (MT_COLOR_NORMAL); } static const char *mix_format_caps (REMAILER *r) { static char capbuff[10]; char *t = capbuff; if (r->caps & MIX_CAP_COMPRESS) *t++ = 'C'; else *t++ = ' '; if (r->caps & MIX_CAP_MIDDLEMAN) *t++ = 'M'; else *t++ = ' '; if (r->caps & MIX_CAP_NEWSPOST) { *t++ = 'N'; *t++ = 'p'; } else { *t++ = ' '; *t++ = ' '; } if (r->caps & MIX_CAP_NEWSMAIL) { *t++ = 'N'; *t++ = 'm'; } else { *t++ = ' '; *t++ = ' '; } *t = '\0'; return capbuff; } /* * Format an entry for the remailer menu. * * %n number * %c capabilities * %s short name * %a address * */ static const char *mix_entry_fmt (char *dest, size_t destlen, size_t col, char op, const char *src, const char *prefix, const char *ifstring, const char *elsestring, unsigned long data, format_flag flags) { char fmt[16]; REMAILER *remailer = (REMAILER *) data; int optional = (flags & M_FORMAT_OPTIONAL); switch (op) { case 'n': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%sd", prefix); snprintf (dest, destlen, fmt, remailer->num); } break; case 'c': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, mix_format_caps(remailer)); } break; case 's': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL(remailer->shortname)); } else if (!remailer->shortname) optional = 0; break; case 'a': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL(remailer->addr)); } else if (!remailer->addr) optional = 0; break; default: *dest = '\0'; } if (optional) mutt_FormatString (dest, destlen, col, ifstring, mutt_attach_fmt, data, 0); else if (flags & M_FORMAT_OPTIONAL) mutt_FormatString (dest, destlen, col, elsestring, mutt_attach_fmt, data, 0); return (src); } static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num) { REMAILER **type2_list = (REMAILER **) menu->data; mutt_FormatString (b, blen, 0, NONULL (MixEntryFormat), mix_entry_fmt, (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR); } static int mix_chain_add (MIXCHAIN *chain, const char *s, REMAILER **type2_list) { int i; if (chain->cl >= MAXMIXES) return -1; if (!mutt_strcmp (s, "0") || !ascii_strcasecmp (s, "")) { chain->ch[chain->cl++] = 0; return 0; } for (i = 0; type2_list[i]; i++) { if (!ascii_strcasecmp (s, type2_list[i]->shortname)) { chain->ch[chain->cl++] = i; return 0; } } /* replace unknown remailers by */ if (!type2_list[i]) chain->ch[chain->cl++] = 0; return 0; } static struct mapping_t RemailerHelp[] = { { N_("Append"), OP_MIX_APPEND }, { N_("Insert"), OP_MIX_INSERT }, { N_("Delete"), OP_MIX_DELETE }, { N_("Abort"), OP_EXIT }, { N_("OK"), OP_MIX_USE }, { NULL } }; void mix_make_chain (LIST **chainp, int *redraw) { LIST *p; MIXCHAIN *chain; int c_cur = 0, c_old = 0; int m_len; short c_redraw = 1; REMAILER **type2_list = NULL; size_t ttll = 0; struct coord *coords = NULL; MUTTMENU *menu; char helpstr[LONG_STRING]; short loop = 1; int op; int i, j; char *t; if (!(type2_list = mix_type2_list (&ttll))) { mutt_error _("Can't get mixmaster's type2.list!"); return; } *redraw = REDRAW_FULL; chain = safe_calloc (sizeof (MIXCHAIN), 1); for (p = *chainp; p; p = p->next) mix_chain_add (chain, (char *) p->data, type2_list); mutt_free_list (chainp); /* safety check */ for (i = 0; i < chain->cl; i++) { if (chain->ch[i] >= ttll) chain->ch[i] = 0; } mix_screen_coordinates (type2_list, &coords, chain, 0); menu = mutt_new_menu (); menu->menu = MENU_MIX; menu->max = ttll; menu->make_entry = mix_entry; menu->tag = NULL; menu->title = _("Select a remailer chain."); menu->data = type2_list; menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp); m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1; while (loop) { if (menu->pagelen != m_len) { menu->pagelen = m_len; menu->redraw = REDRAW_FULL; } if (c_redraw) { mix_redraw_head (chain); mix_redraw_chain (type2_list, coords, chain, c_cur); c_redraw = 0; } else if (c_cur != c_old) { mix_redraw_ce (type2_list, coords, chain, c_old, 0); mix_redraw_ce (type2_list, coords, chain, c_cur, 1); } c_old = c_cur; switch ((op = mutt_menuLoop (menu))) { case OP_REDRAW: { menu_redraw_status (menu); mix_redraw_head (chain); mix_screen_coordinates (type2_list, &coords, chain, 0); mix_redraw_chain (type2_list, coords, chain, c_cur); menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1; break; } case OP_EXIT: { chain->cl = 0; loop = 0; break; } case OP_MIX_USE: { if (!chain->cl) { chain->cl++; chain->ch[0] = menu->current; mix_screen_coordinates (type2_list, &coords, chain, c_cur); c_redraw = 1; } if (chain->cl && chain->ch[chain->cl - 1] && (type2_list[chain->ch[chain->cl-1]]->caps & MIX_CAP_MIDDLEMAN)) { mutt_error ( _("Error: %s can't be used as the final remailer of a chain."), type2_list[chain->ch[chain->cl - 1]]->shortname); } else { loop = 0; } break; } case OP_GENERIC_SELECT_ENTRY: case OP_MIX_APPEND: { if (chain->cl < MAXMIXES && c_cur < chain->cl) c_cur++; } /* fallthrough */ case OP_MIX_INSERT: { if (chain->cl < MAXMIXES) { chain->cl++; for (i = chain->cl - 1; i > c_cur; i--) chain->ch[i] = chain->ch[i-1]; chain->ch[c_cur] = menu->current; mix_screen_coordinates (type2_list, &coords, chain, c_cur); c_redraw = 1; } else mutt_error ( _("Mixmaster chains are limited to %d elements."), MAXMIXES); break; } case OP_MIX_DELETE: { if (chain->cl) { chain->cl--; for (i = c_cur; i < chain->cl; i++) chain->ch[i] = chain->ch[i+1]; if (c_cur == chain->cl && c_cur) c_cur--; mix_screen_coordinates (type2_list, &coords, chain, c_cur); c_redraw = 1; } else { mutt_error _("The remailer chain is already empty."); } break; } case OP_MIX_CHAIN_PREV: { if (c_cur) c_cur--; else mutt_error _("You already have the first chain element selected."); break; } case OP_MIX_CHAIN_NEXT: { if (chain->cl && c_cur < chain->cl - 1) c_cur++; else mutt_error _("You already have the last chain element selected."); break; } } } mutt_menuDestroy (&menu); /* construct the remailer list */ if (chain->cl) { for (i = 0; i < chain->cl; i++) { if ((j = chain->ch[i])) t = type2_list[j]->shortname; else t = "*"; *chainp = mutt_add_list (*chainp, t); } } mix_free_type2_list (&type2_list); FREE (&coords); FREE (&chain); } /* some safety checks before piping the message to mixmaster */ int mix_check_message (HEADER *msg) { const char *fqdn; short need_hostname = 0; ADDRESS *p; if (msg->env->cc || msg->env->bcc) { mutt_error _("Mixmaster doesn't accept Cc or Bcc headers."); return -1; } /* When using mixmaster, we MUST qualify any addresses since * the message will be delivered through remote systems. * * use_domain won't be respected at this point, hidden_host will. */ for (p = msg->env->to; p; p = p->next) { if (!p->group && strchr (p->mailbox, '@') == NULL) { need_hostname = 1; break; } } if (need_hostname) { if (!(fqdn = mutt_fqdn (1))) { mutt_error _("Please set the hostname variable to a proper value when using mixmaster!"); return (-1); } /* Cc and Bcc are empty at this point. */ rfc822_qualify (msg->env->to, fqdn); rfc822_qualify (msg->env->reply_to, fqdn); rfc822_qualify (msg->env->mail_followup_to, fqdn); } return 0; } int mix_send_message (LIST *chain, const char *tempfile) { char cmd[HUGE_STRING]; char tmp[HUGE_STRING]; char cd_quoted[STRING]; int i; snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster); for (i = 0; chain; chain = chain->next, i = 1) { strfcpy (tmp, cmd, sizeof (tmp)); mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data); snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted); } if (!option (OPTNOCURSES)) mutt_endwin (NULL); if ((i = mutt_system (cmd))) { fprintf (stderr, _("Error sending message, child exited %d.\n"), i); if (!option (OPTNOCURSES)) { mutt_any_key_to_continue (NULL); mutt_error _("Error sending message."); } } unlink (tempfile); return i; } #endif