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/mem.h>
26 #include <lib-lib/ascii.h>
27 #include <lib-lib/str.h>
28 #include <lib-lib/macros.h>
29 #include <lib-lib/file.h>
30 #include <lib-lib/mapping.h>
31 #include <lib-lib/rx.h>
33 #include <lib-ui/curses.h>
34 #include <lib-ui/menu.h>
37 #include "recvattach.h"
41 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
49 static REMAILER **mix_type2_list (size_t * l);
50 static REMAILER *mix_new_remailer (void);
51 static const char *mix_format_caps (REMAILER * r);
52 static int mix_chain_add (MIXCHAIN * chain, const char *s,
53 REMAILER ** type2_list);
54 static int mix_get_caps (const char *capstr);
55 static void mix_add_entry (REMAILER ***, REMAILER *, size_t *, size_t *);
56 static void mix_entry (char *b, size_t blen, MUTTMENU * menu, int num);
57 static void mix_free_remailer (REMAILER ** r);
58 static void mix_free_type2_list (REMAILER *** ttlp);
59 static void mix_redraw_ce (REMAILER ** type2_list, struct coord *coords,
60 MIXCHAIN * chain, int i, short selected);
61 static void mix_redraw_chain (REMAILER ** type2_list, struct coord *coords,
62 MIXCHAIN * chain, int cur);
63 static void mix_redraw_head (MIXCHAIN *);
64 static void mix_screen_coordinates (REMAILER ** type2_list, struct coord **,
67 static int mix_get_caps (const char *capstr)
74 caps |= MIX_CAP_COMPRESS;
78 caps |= MIX_CAP_MIDDLEMAN;
85 caps |= MIX_CAP_NEWSMAIL;
89 caps |= MIX_CAP_NEWSPOST;
103 static void mix_add_entry (REMAILER *** type2_list, REMAILER * entry,
104 size_t * slots, size_t * used)
106 if (*used == *slots) {
108 p_realloc(type2_list, *slots);
111 (*type2_list)[(*used)++] = entry;
116 static REMAILER *mix_new_remailer (void)
118 return p_new(REMAILER, 1);
121 static void mix_free_remailer (REMAILER ** r)
123 p_delete(&(*r)->shortname);
124 p_delete(&(*r)->addr);
125 p_delete(&(*r)->ver);
130 /* parse the type2.list as given by mixmaster -T */
132 static REMAILER **mix_type2_list (size_t * l)
138 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
139 char line[HUGE_STRING];
142 REMAILER **type2_list = NULL, *p;
143 size_t slots = 0, used = 0;
148 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
151 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
154 mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1,
160 /* first, generate the "random" remailer */
162 p = mix_new_remailer ();
163 p->shortname = m_strdup("<random>");
164 mix_add_entry (&type2_list, p, &slots, &used);
166 while (fgets (line, sizeof (line), fp)) {
167 p = mix_new_remailer ();
169 if (!(t = strtok (line, " \t\n")))
172 p->shortname = m_strdup(t);
174 if (!(t = strtok (NULL, " \t\n")))
177 p->addr = m_strdup(t);
179 if (!(t = strtok (NULL, " \t\n")))
182 if (!(t = strtok (NULL, " \t\n")))
185 p->ver = m_strdup(t);
187 if (!(t = strtok (NULL, " \t\n")))
190 p->caps = mix_get_caps (t);
192 mix_add_entry (&type2_list, p, &slots, &used);
196 mix_free_remailer (&p);
201 mix_add_entry (&type2_list, NULL, &slots, &used);
202 mutt_wait_filter (mm_pid);
209 static void mix_free_type2_list (REMAILER *** ttlp)
212 REMAILER **type2_list = *ttlp;
214 for (i = 0; type2_list[i]; i++)
215 mix_free_remailer (&type2_list[i]);
217 p_delete(type2_list);
221 #define MIX_HOFFSET 2
222 #define MIX_VOFFSET (LINES - 6)
223 #define MIX_MAXROW (LINES - 3)
226 static void mix_screen_coordinates (REMAILER ** type2_list,
227 struct coord **coordsp,
228 MIXCHAIN * chain, int i)
231 struct coord *coords;
236 p_realloc(coordsp, chain->cl);
242 coords[i - 1].c + m_strlen(type2_list[chain->ch[i - 1]]->shortname) + 2;
251 for (; i < chain->cl; i++) {
253 c += m_strlen(type2_list[chain->ch[i]]->shortname) + 2;
256 oc = c = MIX_HOFFSET;
267 static void mix_redraw_ce (REMAILER ** type2_list,
268 struct coord *coords,
269 MIXCHAIN * chain, int i, short selected)
271 if (!coords || !chain)
274 if (coords[i].r < MIX_MAXROW) {
277 SETCOLOR (MT_COLOR_INDICATOR);
279 SETCOLOR (MT_COLOR_NORMAL);
281 mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
282 SETCOLOR (MT_COLOR_NORMAL);
284 if (i + 1 < chain->cl)
289 static void mix_redraw_chain (REMAILER ** type2_list,
290 struct coord *coords, MIXCHAIN * chain, int cur)
294 SETCOLOR (MT_COLOR_NORMAL);
295 BKGDSET (MT_COLOR_NORMAL);
297 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) {
302 for (i = 0; i < chain->cl; i++)
303 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
306 static void mix_redraw_head (MIXCHAIN * chain)
308 SETCOLOR (MT_COLOR_STATUS);
309 mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]",
310 chain ? chain->cl : 0);
312 BKGDSET (MT_COLOR_STATUS);
315 BKGDSET (MT_COLOR_NORMAL);
316 SETCOLOR (MT_COLOR_NORMAL);
319 static const char *mix_format_caps (REMAILER * r)
321 static char capbuff[10];
324 if (r->caps & MIX_CAP_COMPRESS)
329 if (r->caps & MIX_CAP_MIDDLEMAN)
334 if (r->caps & MIX_CAP_NEWSPOST) {
343 if (r->caps & MIX_CAP_NEWSMAIL) {
358 * Format an entry for the remailer menu.
367 static const char *mix_entry_fmt (char *dest,
372 const char *ifstring,
373 const char *elsestring,
374 unsigned long data, format_flag flags)
377 REMAILER *remailer = (REMAILER *) data;
378 int optional = (flags & M_FORMAT_OPTIONAL);
383 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
384 snprintf (dest, destlen, fmt, remailer->num);
389 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
390 snprintf (dest, destlen, fmt, mix_format_caps (remailer));
395 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
396 snprintf (dest, destlen, fmt, NONULL (remailer->shortname));
398 else if (!remailer->shortname)
403 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
404 snprintf (dest, destlen, fmt, NONULL (remailer->addr));
406 else if (!remailer->addr)
415 mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
416 else if (flags & M_FORMAT_OPTIONAL)
417 mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
423 static void mix_entry (char *b, size_t blen, MUTTMENU * menu, int num)
425 REMAILER **type2_list = (REMAILER **) menu->data;
426 int w=(COLS-SW)>blen?blen:(COLS-SW);
428 mutt_FormatString (b, w, NONULL (MixEntryFormat), mix_entry_fmt,
429 (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
432 static int mix_chain_add (MIXCHAIN * chain, const char *s,
433 REMAILER ** type2_list)
437 if (chain->cl >= MAXMIXES)
440 if (!m_strcmp(s, "0") || !ascii_strcasecmp (s, "<random>")) {
441 chain->ch[chain->cl++] = 0;
445 for (i = 0; type2_list[i]; i++) {
446 if (!ascii_strcasecmp (s, type2_list[i]->shortname)) {
447 chain->ch[chain->cl++] = i;
452 /* replace unknown remailers by <random> */
455 chain->ch[chain->cl++] = 0;
460 static struct mapping_t RemailerHelp[] = {
461 {N_("Append"), OP_MIX_APPEND},
462 {N_("Insert"), OP_MIX_INSERT},
463 {N_("Delete"), OP_MIX_DELETE},
464 {N_("Abort"), OP_EXIT},
465 {N_("OK"), OP_MIX_USE},
470 void mix_make_chain (LIST ** chainp, int *redraw)
474 int c_cur = 0, c_old = 0;
478 REMAILER **type2_list = NULL;
481 struct coord *coords = NULL;
484 char helpstr[SHORT_STRING];
491 if (!(type2_list = mix_type2_list (&ttll))) {
492 mutt_error _("Can't get mixmaster's type2.list!");
497 *redraw = REDRAW_FULL;
499 chain = p_new(MIXCHAIN, 1);
500 for (p = *chainp; p; p = p->next)
501 mix_chain_add (chain, (char *) p->data, type2_list);
503 mutt_free_list (chainp);
506 for (i = 0; i < chain->cl; i++) {
507 if (chain->ch[i] >= ttll)
511 mix_screen_coordinates (type2_list, &coords, chain, 0);
513 menu = mutt_new_menu ();
514 menu->menu = MENU_MIX;
516 menu->make_entry = mix_entry;
518 menu->title = _("Select a remailer chain.");
519 menu->data = type2_list;
521 mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
523 m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
526 if (menu->pagelen != m_len) {
527 menu->pagelen = m_len;
528 menu->redraw = REDRAW_FULL;
532 mix_redraw_head (chain);
533 mix_redraw_chain (type2_list, coords, chain, c_cur);
536 else if (c_cur != c_old) {
537 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
538 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
543 switch ((op = mutt_menuLoop (menu))) {
546 menu_redraw_status (menu);
547 mix_redraw_head (chain);
548 mix_screen_coordinates (type2_list, &coords, chain, 0);
549 mix_redraw_chain (type2_list, coords, chain, c_cur);
550 menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1;
565 chain->ch[0] = menu->current;
566 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
570 if (chain->cl && chain->ch[chain->cl - 1] &&
571 (type2_list[chain->ch[chain->cl - 1]]->caps & MIX_CAP_MIDDLEMAN))
574 ("Error: %s can't be used as the final remailer of a chain."),
575 type2_list[chain->ch[chain->cl - 1]]->shortname);
583 case OP_GENERIC_SELECT_ENTRY:
586 if (chain->cl < MAXMIXES && c_cur < chain->cl)
592 if (chain->cl < MAXMIXES) {
594 for (i = chain->cl - 1; i > c_cur; i--)
595 chain->ch[i] = chain->ch[i - 1];
597 chain->ch[c_cur] = menu->current;
598 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
602 mutt_error (_("Mixmaster chains are limited to %d elements."),
613 for (i = c_cur; i < chain->cl; i++)
614 chain->ch[i] = chain->ch[i + 1];
616 if (c_cur == chain->cl && c_cur)
619 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
623 mutt_error _("The remailer chain is already empty.");
628 case OP_MIX_CHAIN_PREV:
633 mutt_error _("You already have the first chain element selected.");
638 case OP_MIX_CHAIN_NEXT:
640 if (chain->cl && c_cur < chain->cl - 1)
643 mutt_error _("You already have the last chain element selected.");
650 mutt_menuDestroy (&menu);
652 /* construct the remailer list */
655 for (i = 0; i < chain->cl; i++) {
656 if ((j = chain->ch[i]))
657 t = type2_list[j]->shortname;
661 *chainp = mutt_add_list (*chainp, t);
665 mix_free_type2_list (&type2_list);
670 /* some safety checks before piping the message to mixmaster */
672 int mix_check_message (HEADER * msg)
675 short need_hostname = 0;
678 if (msg->env->cc || msg->env->bcc) {
679 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
684 /* When using mixmaster, we MUST qualify any addresses since
685 * the message will be delivered through remote systems.
687 * use_domain won't be respected at this point, hidden_host will.
690 for (p = msg->env->to; p; p = p->next) {
691 if (!p->group && strchr (p->mailbox, '@') == NULL) {
699 if (!(fqdn = mutt_fqdn (1))) {
702 ("Please set the hostname variable to a proper value when using mixmaster!");
706 /* Cc and Bcc are empty at this point. */
707 rfc822_qualify (msg->env->to, fqdn);
708 rfc822_qualify (msg->env->reply_to, fqdn);
709 rfc822_qualify (msg->env->mail_followup_to, fqdn);
715 int mix_send_message (LIST * chain, const char *tempfile)
717 char cmd[HUGE_STRING];
718 char tmp[HUGE_STRING];
719 char cd_quoted[STRING];
722 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
724 for (i = 0; chain; chain = chain->next, i = 1) {
725 m_strcpy(tmp, sizeof(tmp), cmd);
726 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
727 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
730 if (!option (OPTNOCURSES))
733 if ((i = mutt_system (cmd))) {
734 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
735 if (!option (OPTNOCURSES)) {
736 mutt_any_key_to_continue (NULL);
737 mutt_error _("Error sending message.");