/* * Copyright notice from original mutt: * Copyright (C) 1999-2000 Thomas Roessler * * This file is part of mutt-ng, see http://www.muttng.org/. * It's licensed under the GNU General Public License, * please see the file GPL in the top level source directory. */ /* * Mixmaster support for Mutt */ #if HAVE_CONFIG_H # include "config.h" #endif #include "mutt.h" #include "recvattach.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mapping.h" #include "remailer.h" #include "lib/mem.h" #include "lib/intl.h" #include "lib/str.h" #include "lib/rx.h" #include #include #include #include #include #include #define SW (option(OPTMBOXPANE)?SidebarWidth:0) #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; mem_realloc (type2_list, sizeof (REMAILER *) * (*slots)); } (*type2_list)[(*used)++] = entry; if (entry) entry->num = *used; } static REMAILER *mix_new_remailer (void) { return mem_calloc (1, sizeof (REMAILER)); } static void mix_free_remailer (REMAILER ** r) { mem_free (&(*r)->shortname); mem_free (&(*r)->addr); mem_free (&(*r)->ver); mem_free (r); } /* 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 = str_dup (""); 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 = str_dup (t); if (!(t = strtok (NULL, " \t\n"))) goto problem; p->addr = str_dup (t); if (!(t = strtok (NULL, " \t\n"))) goto problem; if (!(t = strtok (NULL, " \t\n"))) goto problem; p->ver = str_dup (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]); mem_free (type2_list); } #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; mem_realloc (coordsp, sizeof (struct coord) * chain->cl); coords = *coordsp; if (i) { c = coords[i - 1].c + str_len (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 += str_len (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, 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, ifstring, mutt_attach_fmt, data, 0); else if (flags & M_FORMAT_OPTIONAL) mutt_FormatString (dest, destlen, 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; int w=(COLS-SW)>blen?blen:(COLS-SW); mutt_FormatString (b, w, 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 (!str_cmp (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[SHORT_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 = mem_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); mem_free (&coords); mem_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