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
21 #include <sys/types.h>
25 #include <lib-lib/lib-lib.h>
27 #include <lib-sys/unix.h>
29 #include <lib-ui/curses.h>
30 #include <lib-ui/menu.h>
33 #include "recvattach.h"
37 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
45 static REMAILER **mix_type2_list (ssize_t * l);
46 static REMAILER *mix_new_remailer (void);
47 static const char *mix_format_caps (REMAILER * r);
48 static int mix_chain_add (MIXCHAIN * chain, const char *s,
49 REMAILER ** type2_list);
50 static int mix_get_caps (const char *capstr);
51 static void mix_add_entry (REMAILER ***, REMAILER *, ssize_t *, ssize_t *);
52 static void mix_entry (char *b, ssize_t blen, MUTTMENU * menu, int num);
53 static void mix_free_remailer (REMAILER ** r);
54 static void mix_free_type2_list (REMAILER *** ttlp);
55 static void mix_redraw_ce (REMAILER ** type2_list, struct coord *coords,
56 MIXCHAIN * chain, int i, short selected);
57 static void mix_redraw_chain (REMAILER ** type2_list, struct coord *coords,
58 MIXCHAIN * chain, int cur);
59 static void mix_redraw_head (MIXCHAIN *);
60 static void mix_screen_coordinates (REMAILER ** type2_list, struct coord **,
63 static int mix_get_caps (const char *capstr)
70 caps |= MIX_CAP_COMPRESS;
74 caps |= MIX_CAP_MIDDLEMAN;
81 caps |= MIX_CAP_NEWSMAIL;
85 caps |= MIX_CAP_NEWSPOST;
99 static void mix_add_entry (REMAILER *** type2_list, REMAILER * entry,
100 ssize_t * slots, ssize_t * used)
102 if (*used == *slots) {
104 p_realloc(type2_list, *slots);
107 (*type2_list)[(*used)++] = entry;
112 static REMAILER *mix_new_remailer (void)
114 return p_new(REMAILER, 1);
117 static void mix_free_remailer (REMAILER ** r)
119 p_delete(&(*r)->shortname);
120 p_delete(&(*r)->addr);
121 p_delete(&(*r)->ver);
126 /* parse the type2.list as given by mixmaster -T */
128 static REMAILER **mix_type2_list (ssize_t * l)
134 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
135 char line[HUGE_STRING];
138 REMAILER **type2_list = NULL, *p;
139 ssize_t slots = 0, used = 0;
144 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
147 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
150 mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1,
156 /* first, generate the "random" remailer */
158 p = mix_new_remailer ();
159 p->shortname = m_strdup("<random>");
160 mix_add_entry (&type2_list, p, &slots, &used);
162 while (fgets (line, sizeof (line), fp)) {
163 p = mix_new_remailer ();
165 if (!(t = strtok (line, " \t\n")))
168 p->shortname = m_strdup(t);
170 if (!(t = strtok (NULL, " \t\n")))
173 p->addr = m_strdup(t);
175 if (!(t = strtok (NULL, " \t\n")))
178 if (!(t = strtok (NULL, " \t\n")))
181 p->ver = m_strdup(t);
183 if (!(t = strtok (NULL, " \t\n")))
186 p->caps = mix_get_caps (t);
188 mix_add_entry (&type2_list, p, &slots, &used);
192 mix_free_remailer (&p);
197 mix_add_entry (&type2_list, NULL, &slots, &used);
198 mutt_wait_filter (mm_pid);
205 static void mix_free_type2_list (REMAILER *** ttlp)
208 REMAILER **type2_list = *ttlp;
210 for (i = 0; type2_list[i]; i++)
211 mix_free_remailer (&type2_list[i]);
213 p_delete(type2_list);
217 #define MIX_HOFFSET 2
218 #define MIX_VOFFSET (LINES - 6)
219 #define MIX_MAXROW (LINES - 3)
222 static void mix_screen_coordinates (REMAILER ** type2_list,
223 struct coord **coordsp,
224 MIXCHAIN * chain, int i)
227 struct coord *coords;
232 p_realloc(coordsp, chain->cl);
238 coords[i - 1].c + m_strlen(type2_list[chain->ch[i - 1]]->shortname) + 2;
247 for (; i < chain->cl; i++) {
249 c += m_strlen(type2_list[chain->ch[i]]->shortname) + 2;
252 oc = c = MIX_HOFFSET;
263 static void mix_redraw_ce (REMAILER ** type2_list,
264 struct coord *coords,
265 MIXCHAIN * chain, int i, short selected)
267 if (!coords || !chain)
270 if (coords[i].r < MIX_MAXROW) {
273 SETCOLOR (MT_COLOR_INDICATOR);
275 SETCOLOR (MT_COLOR_NORMAL);
277 mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
278 SETCOLOR (MT_COLOR_NORMAL);
280 if (i + 1 < chain->cl)
285 static void mix_redraw_chain (REMAILER ** type2_list,
286 struct coord *coords, MIXCHAIN * chain, int cur)
290 SETCOLOR (MT_COLOR_NORMAL);
291 BKGDSET (MT_COLOR_NORMAL);
293 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) {
298 for (i = 0; i < chain->cl; i++)
299 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
302 static void mix_redraw_head (MIXCHAIN * chain)
304 SETCOLOR (MT_COLOR_STATUS);
305 mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]",
306 chain ? chain->cl : 0);
308 BKGDSET (MT_COLOR_STATUS);
311 BKGDSET (MT_COLOR_NORMAL);
312 SETCOLOR (MT_COLOR_NORMAL);
315 static const char *mix_format_caps (REMAILER * r)
317 static char capbuff[10];
320 if (r->caps & MIX_CAP_COMPRESS)
325 if (r->caps & MIX_CAP_MIDDLEMAN)
330 if (r->caps & MIX_CAP_NEWSPOST) {
339 if (r->caps & MIX_CAP_NEWSMAIL) {
354 * Format an entry for the remailer menu.
363 static const char *mix_entry_fmt (char *dest,
368 const char *ifstring,
369 const char *elsestring,
370 unsigned long data, format_flag flags)
373 REMAILER *remailer = (REMAILER *) data;
374 int optional = (flags & M_FORMAT_OPTIONAL);
379 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
380 snprintf (dest, destlen, fmt, remailer->num);
385 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
386 snprintf (dest, destlen, fmt, mix_format_caps (remailer));
391 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
392 snprintf (dest, destlen, fmt, NONULL (remailer->shortname));
394 else if (!remailer->shortname)
399 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
400 snprintf (dest, destlen, fmt, NONULL (remailer->addr));
402 else if (!remailer->addr)
411 mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
412 else if (flags & M_FORMAT_OPTIONAL)
413 mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
419 static void mix_entry (char *b, ssize_t blen, MUTTMENU * menu, int num)
421 REMAILER **type2_list = (REMAILER **) menu->data;
422 int w = (COLS-SW) > blen ? blen : (COLS-SW);
424 mutt_FormatString (b, w, NONULL (MixEntryFormat), mix_entry_fmt,
425 (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
428 static int mix_chain_add (MIXCHAIN * chain, const char *s,
429 REMAILER ** type2_list)
433 if (chain->cl >= MAXMIXES)
436 if (!m_strcmp(s, "0") || !ascii_strcasecmp (s, "<random>")) {
437 chain->ch[chain->cl++] = 0;
441 for (i = 0; type2_list[i]; i++) {
442 if (!ascii_strcasecmp (s, type2_list[i]->shortname)) {
443 chain->ch[chain->cl++] = i;
448 /* replace unknown remailers by <random> */
451 chain->ch[chain->cl++] = 0;
456 static struct mapping_t RemailerHelp[] = {
457 {N_("Append"), OP_MIX_APPEND},
458 {N_("Insert"), OP_MIX_INSERT},
459 {N_("Delete"), OP_MIX_DELETE},
460 {N_("Abort"), OP_EXIT},
461 {N_("OK"), OP_MIX_USE},
466 void mix_make_chain (string_list_t ** chainp, int *redraw)
470 int c_cur = 0, c_old = 0;
474 REMAILER **type2_list = NULL;
477 struct coord *coords = NULL;
480 char helpstr[SHORT_STRING];
487 if (!(type2_list = mix_type2_list (&ttll))) {
488 mutt_error _("Can't get mixmaster's type2.list!");
493 *redraw = REDRAW_FULL;
495 chain = p_new(MIXCHAIN, 1);
496 for (p = *chainp; p; p = p->next)
497 mix_chain_add (chain, (char *) p->data, type2_list);
499 string_list_wipe(chainp);
502 for (i = 0; i < chain->cl; i++) {
503 if (chain->ch[i] >= ttll)
507 mix_screen_coordinates (type2_list, &coords, chain, 0);
509 menu = mutt_new_menu ();
510 menu->menu = MENU_MIX;
512 menu->make_entry = mix_entry;
514 menu->title = _("Select a remailer chain.");
515 menu->data = type2_list;
517 mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
519 m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
522 if (menu->pagelen != m_len) {
523 menu->pagelen = m_len;
524 menu->redraw = REDRAW_FULL;
528 mix_redraw_head (chain);
529 mix_redraw_chain (type2_list, coords, chain, c_cur);
532 else if (c_cur != c_old) {
533 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
534 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
539 switch ((op = mutt_menuLoop (menu))) {
542 menu_redraw_status (menu);
543 mix_redraw_head (chain);
544 mix_screen_coordinates (type2_list, &coords, chain, 0);
545 mix_redraw_chain (type2_list, coords, chain, c_cur);
546 menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1;
561 chain->ch[0] = menu->current;
562 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
566 if (chain->cl && chain->ch[chain->cl - 1] &&
567 (type2_list[chain->ch[chain->cl - 1]]->caps & MIX_CAP_MIDDLEMAN))
570 ("Error: %s can't be used as the final remailer of a chain."),
571 type2_list[chain->ch[chain->cl - 1]]->shortname);
579 case OP_GENERIC_SELECT_ENTRY:
582 if (chain->cl < MAXMIXES && c_cur < chain->cl)
588 if (chain->cl < MAXMIXES) {
590 for (i = chain->cl - 1; i > c_cur; i--)
591 chain->ch[i] = chain->ch[i - 1];
593 chain->ch[c_cur] = menu->current;
594 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
598 mutt_error (_("Mixmaster chains are limited to %d elements."),
609 for (i = c_cur; i < chain->cl; i++)
610 chain->ch[i] = chain->ch[i + 1];
612 if (c_cur == chain->cl && c_cur)
615 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
619 mutt_error _("The remailer chain is already empty.");
624 case OP_MIX_CHAIN_PREV:
629 mutt_error _("You already have the first chain element selected.");
634 case OP_MIX_CHAIN_NEXT:
636 if (chain->cl && c_cur < chain->cl - 1)
639 mutt_error _("You already have the last chain element selected.");
646 mutt_menuDestroy (&menu);
648 /* construct the remailer list */
651 for (i = 0; i < chain->cl; i++) {
652 if ((j = chain->ch[i]))
653 t = type2_list[j]->shortname;
657 *chainp = mutt_add_list (*chainp, t);
661 mix_free_type2_list (&type2_list);
666 /* some safety checks before piping the message to mixmaster */
668 int mix_check_message (HEADER * msg)
671 short need_hostname = 0;
674 if (msg->env->cc || msg->env->bcc) {
675 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
680 /* When using mixmaster, we MUST qualify any addresses since
681 * the message will be delivered through remote systems.
683 * use_domain won't be respected at this point, hidden_host will.
686 for (p = msg->env->to; p; p = p->next) {
687 if (!p->group && strchr (p->mailbox, '@') == NULL) {
695 if (!(fqdn = mutt_fqdn (1))) {
698 ("Please set the hostname variable to a proper value when using mixmaster!");
702 /* Cc and Bcc are empty at this point. */
703 rfc822_qualify (msg->env->to, fqdn);
704 rfc822_qualify (msg->env->reply_to, fqdn);
705 rfc822_qualify (msg->env->mail_followup_to, fqdn);
711 int mix_send_message (string_list_t * chain, const char *tempfile)
713 char cmd[HUGE_STRING];
714 char tmp[HUGE_STRING];
715 char cd_quoted[STRING];
718 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
720 for (i = 0; chain; chain = chain->next, i = 1) {
721 m_strcpy(tmp, sizeof(tmp), cmd);
722 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
723 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
726 if (!option (OPTNOCURSES))
729 if ((i = mutt_system (cmd))) {
730 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
731 if (!option (OPTNOCURSES)) {
732 mutt_any_key_to_continue (NULL);
733 mutt_error _("Error sending message.");