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)
33 static REMAILER **mix_type2_list (ssize_t * l);
34 static REMAILER *mix_new_remailer (void);
35 static const char *mix_format_caps (REMAILER * r);
36 static int mix_chain_add (MIXCHAIN * chain, const char *s,
37 REMAILER ** type2_list);
38 static int mix_get_caps (const char *capstr);
39 static void mix_add_entry (REMAILER ***, REMAILER *, ssize_t *, ssize_t *);
40 static void mix_entry (char *b, ssize_t blen, MUTTMENU * menu, int num);
41 static void mix_free_remailer (REMAILER ** r);
42 static void mix_free_type2_list (REMAILER *** ttlp);
43 static void mix_redraw_ce (REMAILER ** type2_list, struct coord *coords,
44 MIXCHAIN * chain, int i, short selected);
45 static void mix_redraw_chain (REMAILER ** type2_list, struct coord *coords,
46 MIXCHAIN * chain, int cur);
47 static void mix_redraw_head (MIXCHAIN *);
48 static void mix_screen_coordinates (REMAILER ** type2_list, struct coord **,
51 static int mix_get_caps (const char *capstr)
58 caps |= MIX_CAP_COMPRESS;
62 caps |= MIX_CAP_MIDDLEMAN;
69 caps |= MIX_CAP_NEWSMAIL;
73 caps |= MIX_CAP_NEWSPOST;
87 static void mix_add_entry (REMAILER *** type2_list, REMAILER * entry,
88 ssize_t * slots, ssize_t * used)
90 if (*used == *slots) {
92 p_realloc(type2_list, *slots);
95 (*type2_list)[(*used)++] = entry;
100 static REMAILER *mix_new_remailer (void)
102 return p_new(REMAILER, 1);
105 static void mix_free_remailer (REMAILER ** r)
107 p_delete(&(*r)->shortname);
108 p_delete(&(*r)->addr);
109 p_delete(&(*r)->ver);
114 /* parse the type2.list as given by mixmaster -T */
116 static REMAILER **mix_type2_list (ssize_t * l)
122 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
123 char line[HUGE_STRING];
126 REMAILER **type2_list = NULL, *p;
127 ssize_t slots = 0, used = 0;
132 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
135 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
138 mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1,
144 /* first, generate the "random" remailer */
146 p = mix_new_remailer ();
147 p->shortname = m_strdup("<random>");
148 mix_add_entry (&type2_list, p, &slots, &used);
150 while (fgets (line, sizeof (line), fp)) {
151 p = mix_new_remailer ();
153 if (!(t = strtok (line, " \t\n")))
156 p->shortname = m_strdup(t);
158 if (!(t = strtok (NULL, " \t\n")))
161 p->addr = m_strdup(t);
163 if (!(t = strtok (NULL, " \t\n")))
166 if (!(t = strtok (NULL, " \t\n")))
169 p->ver = m_strdup(t);
171 if (!(t = strtok (NULL, " \t\n")))
174 p->caps = mix_get_caps (t);
176 mix_add_entry (&type2_list, p, &slots, &used);
180 mix_free_remailer (&p);
185 mix_add_entry (&type2_list, NULL, &slots, &used);
186 mutt_wait_filter (mm_pid);
193 static void mix_free_type2_list (REMAILER *** ttlp)
196 REMAILER **type2_list = *ttlp;
198 for (i = 0; type2_list[i]; i++)
199 mix_free_remailer (&type2_list[i]);
201 p_delete(type2_list);
205 #define MIX_HOFFSET 2
206 #define MIX_VOFFSET (LINES - 6)
207 #define MIX_MAXROW (LINES - 3)
210 static void mix_screen_coordinates (REMAILER ** type2_list,
211 struct coord **coordsp,
212 MIXCHAIN * chain, int i)
215 struct coord *coords;
220 p_realloc(coordsp, chain->cl);
226 coords[i - 1].c + m_strlen(type2_list[chain->ch[i - 1]]->shortname) + 2;
235 for (; i < chain->cl; i++) {
237 c += m_strlen(type2_list[chain->ch[i]]->shortname) + 2;
240 oc = c = MIX_HOFFSET;
251 static void mix_redraw_ce (REMAILER ** type2_list,
252 struct coord *coords,
253 MIXCHAIN * chain, int i, short selected)
255 if (!coords || !chain)
258 if (coords[i].r < MIX_MAXROW) {
261 SETCOLOR (MT_COLOR_INDICATOR);
263 SETCOLOR (MT_COLOR_NORMAL);
265 mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
266 SETCOLOR (MT_COLOR_NORMAL);
268 if (i + 1 < chain->cl)
273 static void mix_redraw_chain (REMAILER ** type2_list,
274 struct coord *coords, MIXCHAIN * chain, int cur)
278 SETCOLOR (MT_COLOR_NORMAL);
279 BKGDSET (MT_COLOR_NORMAL);
281 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) {
286 for (i = 0; i < chain->cl; i++)
287 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
290 static void mix_redraw_head (MIXCHAIN * chain)
292 SETCOLOR (MT_COLOR_STATUS);
293 mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]",
294 chain ? chain->cl : 0);
296 BKGDSET (MT_COLOR_STATUS);
299 BKGDSET (MT_COLOR_NORMAL);
300 SETCOLOR (MT_COLOR_NORMAL);
303 static const char *mix_format_caps (REMAILER * r)
305 static char capbuff[10];
308 if (r->caps & MIX_CAP_COMPRESS)
313 if (r->caps & MIX_CAP_MIDDLEMAN)
318 if (r->caps & MIX_CAP_NEWSPOST) {
327 if (r->caps & MIX_CAP_NEWSMAIL) {
342 * Format an entry for the remailer menu.
351 static const char *mix_entry_fmt (char *dest,
356 const char *ifstring,
357 const char *elsestring,
358 unsigned long data, format_flag flags)
361 REMAILER *remailer = (REMAILER *) data;
362 int optional = (flags & M_FORMAT_OPTIONAL);
367 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
368 snprintf (dest, destlen, fmt, remailer->num);
373 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
374 snprintf (dest, destlen, fmt, mix_format_caps (remailer));
379 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
380 snprintf (dest, destlen, fmt, NONULL (remailer->shortname));
382 else if (!remailer->shortname)
387 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
388 snprintf (dest, destlen, fmt, NONULL (remailer->addr));
390 else if (!remailer->addr)
399 mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
400 else if (flags & M_FORMAT_OPTIONAL)
401 mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
407 static void mix_entry (char *b, ssize_t blen, MUTTMENU * menu, int num)
409 REMAILER **type2_list = (REMAILER **) menu->data;
410 int w = (COLS-SW) > blen ? blen : (COLS-SW);
412 mutt_FormatString (b, w, NONULL (MixEntryFormat), mix_entry_fmt,
413 (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
416 static int mix_chain_add (MIXCHAIN * chain, const char *s,
417 REMAILER ** type2_list)
421 if (chain->cl >= MAXMIXES)
424 if (!m_strcmp(s, "0") || !ascii_strcasecmp (s, "<random>")) {
425 chain->ch[chain->cl++] = 0;
429 for (i = 0; type2_list[i]; i++) {
430 if (!ascii_strcasecmp (s, type2_list[i]->shortname)) {
431 chain->ch[chain->cl++] = i;
436 /* replace unknown remailers by <random> */
439 chain->ch[chain->cl++] = 0;
444 static struct mapping_t RemailerHelp[] = {
445 {N_("Append"), OP_MIX_APPEND},
446 {N_("Insert"), OP_MIX_INSERT},
447 {N_("Delete"), OP_MIX_DELETE},
448 {N_("Abort"), OP_EXIT},
449 {N_("OK"), OP_MIX_USE},
454 void mix_make_chain (string_list_t ** chainp, int *redraw)
458 int c_cur = 0, c_old = 0;
462 REMAILER **type2_list = NULL;
465 struct coord *coords = NULL;
468 char helpstr[SHORT_STRING];
475 if (!(type2_list = mix_type2_list (&ttll))) {
476 mutt_error _("Can't get mixmaster's type2.list!");
481 *redraw = REDRAW_FULL;
483 chain = p_new(MIXCHAIN, 1);
484 for (p = *chainp; p; p = p->next)
485 mix_chain_add (chain, (char *) p->data, type2_list);
487 string_list_wipe(chainp);
490 for (i = 0; i < chain->cl; i++) {
491 if (chain->ch[i] >= ttll)
495 mix_screen_coordinates (type2_list, &coords, chain, 0);
497 menu = mutt_new_menu ();
498 menu->menu = MENU_MIX;
500 menu->make_entry = mix_entry;
502 menu->title = _("Select a remailer chain.");
503 menu->data = type2_list;
505 mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
507 m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
510 if (menu->pagelen != m_len) {
511 menu->pagelen = m_len;
512 menu->redraw = REDRAW_FULL;
516 mix_redraw_head (chain);
517 mix_redraw_chain (type2_list, coords, chain, c_cur);
520 else if (c_cur != c_old) {
521 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
522 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
527 switch ((op = mutt_menuLoop (menu))) {
530 menu_redraw_status (menu);
531 mix_redraw_head (chain);
532 mix_screen_coordinates (type2_list, &coords, chain, 0);
533 mix_redraw_chain (type2_list, coords, chain, c_cur);
534 menu->pagelen = m_len = MIX_VOFFSET - menu->offset - 1;
549 chain->ch[0] = menu->current;
550 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
554 if (chain->cl && chain->ch[chain->cl - 1] &&
555 (type2_list[chain->ch[chain->cl - 1]]->caps & MIX_CAP_MIDDLEMAN))
558 ("Error: %s can't be used as the final remailer of a chain."),
559 type2_list[chain->ch[chain->cl - 1]]->shortname);
567 case OP_GENERIC_SELECT_ENTRY:
570 if (chain->cl < MAXMIXES && c_cur < chain->cl)
576 if (chain->cl < MAXMIXES) {
578 for (i = chain->cl - 1; i > c_cur; i--)
579 chain->ch[i] = chain->ch[i - 1];
581 chain->ch[c_cur] = menu->current;
582 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
586 mutt_error (_("Mixmaster chains are limited to %d elements."),
597 for (i = c_cur; i < chain->cl; i++)
598 chain->ch[i] = chain->ch[i + 1];
600 if (c_cur == chain->cl && c_cur)
603 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
607 mutt_error _("The remailer chain is already empty.");
612 case OP_MIX_CHAIN_PREV:
617 mutt_error _("You already have the first chain element selected.");
622 case OP_MIX_CHAIN_NEXT:
624 if (chain->cl && c_cur < chain->cl - 1)
627 mutt_error _("You already have the last chain element selected.");
634 mutt_menuDestroy (&menu);
636 /* construct the remailer list */
639 for (i = 0; i < chain->cl; i++) {
640 if ((j = chain->ch[i]))
641 t = type2_list[j]->shortname;
645 *chainp = mutt_add_list (*chainp, t);
649 mix_free_type2_list (&type2_list);
654 /* some safety checks before piping the message to mixmaster */
656 int mix_check_message (HEADER * msg)
659 short need_hostname = 0;
662 if (msg->env->cc || msg->env->bcc) {
663 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
668 /* When using mixmaster, we MUST qualify any addresses since
669 * the message will be delivered through remote systems.
671 * use_domain won't be respected at this point, hidden_host will.
674 for (p = msg->env->to; p; p = p->next) {
675 if (!p->group && strchr (p->mailbox, '@') == NULL) {
683 if (!(fqdn = mutt_fqdn (1))) {
686 ("Please set the hostname variable to a proper value when using mixmaster!");
690 /* Cc and Bcc are empty at this point. */
691 rfc822_qualify (msg->env->to, fqdn);
692 rfc822_qualify (msg->env->reply_to, fqdn);
693 rfc822_qualify (msg->env->mail_followup_to, fqdn);
699 int mix_send_message (string_list_t * chain, const char *tempfile)
701 char cmd[HUGE_STRING];
702 char tmp[HUGE_STRING];
703 char cd_quoted[STRING];
706 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
708 for (i = 0; chain; chain = chain->next, i = 1) {
709 m_strcpy(tmp, sizeof(tmp), cmd);
710 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
711 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
714 if (!option (OPTNOCURSES))
717 if ((i = mutt_system (cmd))) {
718 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
719 if (!option (OPTNOCURSES)) {
720 mutt_any_key_to_continue (NULL);
721 mutt_error _("Error sending message.");