2 * Copyright notice from original mutt:
3 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
11 * Mixmaster support for Mutt
14 #include <lib-lib/lib-lib.h>
16 #include <lib-sys/unix.h>
17 #include <lib-ui/curses.h>
18 #include <lib-ui/menu.h>
21 #include "recvattach.h"
25 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
29 #define MIX_CAP_COMPRESS (1 << 0)
30 #define MIX_CAP_MIDDLEMAN (1 << 1)
31 #define MIX_CAP_NEWSPOST (1 << 2)
32 #define MIX_CAP_NEWSMAIL (1 << 3)
34 /* Mixmaster's maximum chain length. Don't change this. */
37 typedef struct remailer_t {
45 DO_INIT(remailer_t, remailer);
46 static void remailer_wipe(remailer_t *remailer) {
47 p_delete(&remailer->shortname);
48 p_delete(&remailer->addr);
49 p_delete(&remailer->ver);
51 DO_NEW(remailer_t, remailer);
52 DO_DELETE(remailer_t, remailer);
54 typedef struct mixchain_t {
63 static int mix_get_caps(const char *capstr)
70 caps |= MIX_CAP_COMPRESS;
74 caps |= MIX_CAP_MIDDLEMAN;
80 caps |= MIX_CAP_NEWSMAIL;
84 caps |= MIX_CAP_NEWSPOST;
97 static void mix_add_entry(remailer_t ***type2_list, remailer_t *entry,
98 ssize_t *slots, ssize_t *used)
100 if (*used == *slots) {
102 p_realloc(type2_list, *slots);
105 (*type2_list)[(*used)++] = entry;
110 /* parse the type2.list as given by mixmaster -T */
111 static remailer_t **mix_type2_list (ssize_t * l)
117 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
118 char line[HUGE_STRING];
121 remailer_t **type2_list = NULL, *p;
122 ssize_t slots = 0, used = 0;
127 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
130 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
133 mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1,
139 /* first, generate the "random" remailer */
142 p->shortname = m_strdup("<random>");
143 mix_add_entry (&type2_list, p, &slots, &used);
145 while (fgets (line, sizeof (line), fp)) {
148 if (!(t = strtok (line, " \t\n")))
151 p->shortname = m_strdup(t);
153 if (!(t = strtok (NULL, " \t\n")))
156 p->addr = m_strdup(t);
158 if (!(t = strtok (NULL, " \t\n")))
161 if (!(t = strtok (NULL, " \t\n")))
164 p->ver = m_strdup(t);
166 if (!(t = strtok (NULL, " \t\n")))
169 p->caps = mix_get_caps (t);
171 mix_add_entry (&type2_list, p, &slots, &used);
180 mix_add_entry (&type2_list, NULL, &slots, &used);
181 mutt_wait_filter (mm_pid);
188 static void mix_free_type2_list (remailer_t *** ttlp)
191 remailer_t **type2_list = *ttlp;
193 for (i = 0; type2_list[i]; i++)
194 remailer_delete(&type2_list[i]);
196 p_delete(type2_list);
200 #define MIX_HOFFSET 2
201 #define MIX_VOFFSET (LINES - 6)
202 #define MIX_MAXROW (LINES - 3)
205 static void mix_screen_coordinates (remailer_t ** type2_list,
206 struct coord **coordsp,
207 mixchain_t * chain, int i)
210 struct coord *coords;
215 p_realloc(coordsp, chain->cl);
221 coords[i - 1].c + m_strlen(type2_list[chain->ch[i - 1]]->shortname) + 2;
230 for (; i < chain->cl; i++) {
232 c += m_strlen(type2_list[chain->ch[i]]->shortname) + 2;
235 oc = c = MIX_HOFFSET;
246 static void mix_redraw_ce (remailer_t ** type2_list,
247 struct coord *coords,
248 mixchain_t * chain, int i, short selected)
250 if (!coords || !chain)
253 if (coords[i].r < MIX_MAXROW) {
256 SETCOLOR (MT_COLOR_INDICATOR);
258 SETCOLOR (MT_COLOR_NORMAL);
260 mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
261 SETCOLOR (MT_COLOR_NORMAL);
263 if (i + 1 < chain->cl)
268 static void mix_redraw_chain (remailer_t ** type2_list,
269 struct coord *coords, mixchain_t * chain, int cur)
273 SETCOLOR (MT_COLOR_NORMAL);
274 BKGDSET (MT_COLOR_NORMAL);
276 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) {
281 for (i = 0; i < chain->cl; i++)
282 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
285 static void mix_redraw_head (mixchain_t * chain)
287 SETCOLOR (MT_COLOR_STATUS);
288 mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]",
289 chain ? chain->cl : 0);
291 BKGDSET (MT_COLOR_STATUS);
294 BKGDSET (MT_COLOR_NORMAL);
295 SETCOLOR (MT_COLOR_NORMAL);
298 static const char *mix_format_caps (remailer_t * r)
300 static char capbuff[10];
303 if (r->caps & MIX_CAP_COMPRESS)
308 if (r->caps & MIX_CAP_MIDDLEMAN)
313 if (r->caps & MIX_CAP_NEWSPOST) {
322 if (r->caps & MIX_CAP_NEWSMAIL) {
337 * Format an entry for the remailer menu.
345 static const char *mix_entry_fmt (char *dest,
350 const char *ifstring,
351 const char *elsestring,
352 unsigned long data, format_flag flags)
355 remailer_t *remailer = (remailer_t *) data;
356 int optional = (flags & M_FORMAT_OPTIONAL);
361 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
362 snprintf (dest, destlen, fmt, remailer->num);
367 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
368 snprintf (dest, destlen, fmt, mix_format_caps (remailer));
373 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
374 snprintf (dest, destlen, fmt, NONULL (remailer->shortname));
376 else if (!remailer->shortname)
381 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
382 snprintf (dest, destlen, fmt, NONULL (remailer->addr));
384 else if (!remailer->addr)
393 mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
394 else if (flags & M_FORMAT_OPTIONAL)
395 mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
401 static void mix_entry (char *b, ssize_t blen, MUTTMENU * menu, int num)
403 remailer_t **type2_list = (remailer_t **) menu->data;
404 int w = (COLS-SW) > blen ? blen : (COLS-SW);
406 mutt_FormatString (b, w, NONULL (MixEntryFormat), mix_entry_fmt,
407 (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
410 static int mix_chain_add (mixchain_t * chain, const char *s,
411 remailer_t ** type2_list)
415 if (chain->cl >= MAXMIXES)
418 if (!m_strcmp(s, "0") || !ascii_strcasecmp (s, "<random>")) {
419 chain->ch[chain->cl++] = 0;
423 for (i = 0; type2_list[i]; i++) {
424 if (!ascii_strcasecmp (s, type2_list[i]->shortname)) {
425 chain->ch[chain->cl++] = i;
430 /* replace unknown remailers by <random> */
433 chain->ch[chain->cl++] = 0;
438 static struct mapping_t RemailerHelp[] = {
439 {N_("Append"), OP_MIX_APPEND},
440 {N_("Insert"), OP_MIX_INSERT},
441 {N_("Delete"), OP_MIX_DELETE},
442 {N_("Abort"), OP_EXIT},
443 {N_("OK"), OP_MIX_USE},
448 void mix_make_chain (string_list_t ** chainp, int *redraw)
452 int c_cur = 0, c_old = 0;
456 remailer_t **type2_list = NULL;
459 struct coord *coords = NULL;
462 char helpstr[SHORT_STRING];
469 if (!(type2_list = mix_type2_list (&ttll))) {
470 mutt_error _("Can't get mixmaster's type2.list!");
475 *redraw = REDRAW_FULL;
477 chain = p_new(mixchain_t, 1);
478 for (p = *chainp; p; p = p->next)
479 mix_chain_add (chain, (char *) p->data, type2_list);
481 string_list_wipe(chainp);
484 for (i = 0; i < chain->cl; i++) {
485 if (chain->ch[i] >= ttll)
489 mix_screen_coordinates (type2_list, &coords, chain, 0);
491 menu = mutt_new_menu ();
492 menu->menu = MENU_MIX;
494 menu->make_entry = mix_entry;
496 menu->title = _("Select a remailer chain.");
497 menu->data = type2_list;
499 mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
501 m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
504 if (menu->pagelen != m_len) {
505 menu->pagelen = m_len;
506 menu->redraw = REDRAW_FULL;
510 mix_redraw_head (chain);
511 mix_redraw_chain (type2_list, coords, chain, c_cur);
514 else if (c_cur != c_old) {
515 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
516 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
521 switch ((op = mutt_menuLoop (menu))) {
524 menu_redraw_status (menu);
525 mix_redraw_head (chain);
526 mix_screen_coordinates (type2_list, &coords, chain, 0);
527 mix_redraw_chain (type2_list, coords, chain, c_cur);
528 menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1;
543 chain->ch[0] = menu->current;
544 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
548 if (chain->cl && chain->ch[chain->cl - 1] &&
549 (type2_list[chain->ch[chain->cl - 1]]->caps & MIX_CAP_MIDDLEMAN))
552 ("Error: %s can't be used as the final remailer of a chain."),
553 type2_list[chain->ch[chain->cl - 1]]->shortname);
561 case OP_GENERIC_SELECT_ENTRY:
564 if (chain->cl < MAXMIXES && c_cur < chain->cl)
570 if (chain->cl < MAXMIXES) {
572 for (i = chain->cl - 1; i > c_cur; i--)
573 chain->ch[i] = chain->ch[i - 1];
575 chain->ch[c_cur] = menu->current;
576 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
580 mutt_error (_("Mixmaster chains are limited to %d elements."),
591 for (i = c_cur; i < chain->cl; i++)
592 chain->ch[i] = chain->ch[i + 1];
594 if (c_cur == chain->cl && c_cur)
597 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
601 mutt_error _("The remailer chain is already empty.");
606 case OP_MIX_CHAIN_PREV:
611 mutt_error _("You already have the first chain element selected.");
616 case OP_MIX_CHAIN_NEXT:
618 if (chain->cl && c_cur < chain->cl - 1)
621 mutt_error _("You already have the last chain element selected.");
628 mutt_menuDestroy (&menu);
630 /* construct the remailer list */
633 for (i = 0; i < chain->cl; i++) {
634 if ((j = chain->ch[i]))
635 t = type2_list[j]->shortname;
639 *chainp = mutt_add_list (*chainp, t);
643 mix_free_type2_list (&type2_list);
648 /* some safety checks before piping the message to mixmaster */
650 int mix_check_message (HEADER * msg)
653 short need_hostname = 0;
656 if (msg->env->cc || msg->env->bcc) {
657 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
662 /* When using mixmaster, we MUST qualify any addresses since
663 * the message will be delivered through remote systems.
665 * use_domain won't be respected at this point, hidden_host will.
668 for (p = msg->env->to; p; p = p->next) {
669 if (!p->group && strchr (p->mailbox, '@') == NULL) {
677 if (!(fqdn = mutt_fqdn (1))) {
680 ("Please set the hostname variable to a proper value when using mixmaster!");
684 /* Cc and Bcc are empty at this point. */
685 rfc822_qualify (msg->env->to, fqdn);
686 rfc822_qualify (msg->env->reply_to, fqdn);
687 rfc822_qualify (msg->env->mail_followup_to, fqdn);
693 int mix_send_message (string_list_t * chain, const char *tempfile)
695 char cmd[HUGE_STRING];
696 char tmp[HUGE_STRING];
697 char cd_quoted[STRING];
700 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
702 for (i = 0; chain; chain = chain->next, i = 1) {
703 m_strcpy(tmp, sizeof(tmp), cmd);
704 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
705 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
708 if (!option (OPTNOCURSES))
711 if ((i = mutt_system (cmd))) {
712 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
713 if (!option (OPTNOCURSES)) {
714 mutt_any_key_to_continue (NULL);
715 mutt_error _("Error sending message.");