mutt stable branch with some hacks
1/*
2 * Copyright (C) 1999-2001 Thomas Roessler <roessler@does-not-exist.org>
3 *
4 * This program is free software; you can redistribute it
5 * and/or modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later
8 * version.
9 *
10 * This program is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 * PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22/*
23 * Mixmaster support for Mutt
24 */
25
26#if HAVE_CONFIG_H
27# include "config.h"
28#endif
29
30#include "mutt.h"
31#include "mutt_curses.h"
32#include "mutt_menu.h"
33#include "mutt_regex.h"
34#include "mapping.h"
35
36#include "remailer.h"
37
38#include <stdio.h>
39#include <string.h>
40#include <stdlib.h>
41
42#include <sys/types.h>
43#include <sys/file.h>
44#include <fcntl.h>
45
46#ifdef MIXMASTER
47
48struct coord
49{
50 short r, c;
51};
52
53static REMAILER **mix_type2_list (size_t *l);
54static REMAILER *mix_new_remailer (void);
55static const char *mix_format_caps (REMAILER *r);
56static int mix_chain_add (MIXCHAIN *chain, const char *s, REMAILER **type2_list);
57static int mix_get_caps (const char *capstr);
58static void mix_add_entry (REMAILER ***, REMAILER *, size_t *, size_t *);
59static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num);
60static void mix_free_remailer (REMAILER **r);
61static void mix_free_type2_list (REMAILER ***ttlp);
62static void mix_redraw_ce (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int i, short selected);
63static void mix_redraw_chain (REMAILER **type2_list, struct coord *coords, MIXCHAIN *chain, int cur);
64static void mix_redraw_head (MIXCHAIN *);
65static void mix_screen_coordinates (REMAILER **type2_list, struct coord **, MIXCHAIN *, int);
66
67static int mix_get_caps (const char *capstr)
68{
69 int caps = 0;
70
71 while (*capstr)
72 {
73 switch (*capstr)
74 {
75 case 'C':
76 caps |= MIX_CAP_COMPRESS;
77 break;
78
79 case 'M':
80 caps |= MIX_CAP_MIDDLEMAN;
81 break;
82
83 case 'N':
84 {
85 switch (*++capstr)
86 {
87 case 'm':
88 caps |= MIX_CAP_NEWSMAIL;
89 break;
90
91 case 'p':
92 caps |= MIX_CAP_NEWSPOST;
93 break;
94
95 }
96 }
97 }
98
99 if (*capstr) capstr++;
100 }
101
102 return caps;
103}
104
105static void mix_add_entry (REMAILER ***type2_list, REMAILER *entry,
106 size_t *slots, size_t *used)
107{
108 if (*used == *slots)
109 {
110 *slots += 5;
111 safe_realloc (type2_list, sizeof (REMAILER *) * (*slots));
112 }
113
114 (*type2_list)[(*used)++] = entry;
115 if (entry) entry->num = *used;
116}
117
118static REMAILER *mix_new_remailer (void)
119{
120 return safe_calloc (1, sizeof (REMAILER));
121}
122
123static void mix_free_remailer (REMAILER **r)
124{
125 FREE (&(*r)->shortname);
126 FREE (&(*r)->addr);
127 FREE (&(*r)->ver);
128
129 FREE (r); /* __FREE_CHECKED__ */
130}
131
132/* parse the type2.list as given by mixmaster -T */
133
134static REMAILER **mix_type2_list (size_t *l)
135{
136 FILE *fp;
137 pid_t mm_pid;
138 int devnull;
139
140 char cmd[HUGE_STRING + _POSIX_PATH_MAX];
141 char line[HUGE_STRING];
142 char *t;
143
144 REMAILER **type2_list = NULL, *p;
145 size_t slots = 0, used = 0;
146
147 if (!l)
148 return NULL;
149
150 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
151 return NULL;
152
153 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
154
155 if ((mm_pid = mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1, devnull)) == -1)
156 {
157 close (devnull);
158 return NULL;
159 }
160
161 /* first, generate the "random" remailer */
162
163 p = mix_new_remailer ();
164 p->shortname = safe_strdup ("<random>");
165 mix_add_entry (&type2_list, p, &slots, &used);
166
167 while (fgets (line, sizeof (line), fp))
168 {
169 p = mix_new_remailer ();
170
171 if (!(t = strtok (line, " \t\n")))
172 goto problem;
173
174 p->shortname = safe_strdup (t);
175
176 if (!(t = strtok (NULL, " \t\n")))
177 goto problem;
178
179 p->addr = safe_strdup (t);
180
181 if (!(t = strtok (NULL, " \t\n")))
182 goto problem;
183
184 if (!(t = strtok (NULL, " \t\n")))
185 goto problem;
186
187 p->ver = safe_strdup (t);
188
189 if (!(t = strtok (NULL, " \t\n")))
190 goto problem;
191
192 p->caps = mix_get_caps (t);
193
194 mix_add_entry (&type2_list, p, &slots, &used);
195 continue;
196
197 problem:
198 mix_free_remailer (&p);
199 }
200
201 *l = used;
202
203 mix_add_entry (&type2_list, NULL, &slots, &used);
204 mutt_wait_filter (mm_pid);
205
206 close (devnull);
207
208 return type2_list;
209}
210
211static void mix_free_type2_list (REMAILER ***ttlp)
212{
213 int i;
214 REMAILER **type2_list = *ttlp;
215
216 for (i = 0; type2_list[i]; i++)
217 mix_free_remailer (&type2_list[i]);
218
219 FREE (type2_list); /* __FREE_CHECKED__ */
220}
221
222
223#define MIX_HOFFSET 2
224#define MIX_VOFFSET (MuttIndexWindow->rows - 4)
225#define MIX_MAXROW (MuttIndexWindow->rows - 1)
226
227
228static void mix_screen_coordinates (REMAILER **type2_list,
229 struct coord **coordsp,
230 MIXCHAIN *chain,
231 int i)
232{
233 short c, r, oc;
234 struct coord *coords;
235
236 if (!chain->cl)
237 return;
238
239 safe_realloc (coordsp, sizeof (struct coord) * chain->cl);
240
241 coords = *coordsp;
242
243 if (i)
244 {
245 c = coords[i-1].c + strlen (type2_list[chain->ch[i-1]]->shortname) + 2;
246 r = coords[i-1].r;
247 }
248 else
249 {
250 r = MIX_VOFFSET;
251 c = MIX_HOFFSET;
252 }
253
254
255 for (; i < chain->cl; i++)
256 {
257 oc = c;
258 c += strlen (type2_list[chain->ch[i]]->shortname) + 2;
259
260 if (c >= MuttIndexWindow->cols)
261 {
262 oc = c = MIX_HOFFSET;
263 r++;
264 }
265
266 coords[i].c = oc;
267 coords[i].r = r;
268
269 }
270
271}
272
273static void mix_redraw_ce (REMAILER **type2_list,
274 struct coord *coords,
275 MIXCHAIN *chain,
276 int i,
277 short selected)
278{
279 if (!coords || !chain)
280 return;
281
282 if (coords[i].r < MIX_MAXROW)
283 {
284
285 if (selected)
286 SETCOLOR (MT_COLOR_INDICATOR);
287 else
288 NORMAL_COLOR;
289
290 mutt_window_mvaddstr (MuttIndexWindow, coords[i].r, coords[i].c,
291 type2_list[chain->ch[i]]->shortname);
292 NORMAL_COLOR;
293
294 if (i + 1 < chain->cl)
295 addstr (", ");
296 }
297}
298
299static void mix_redraw_chain (REMAILER **type2_list,
300 struct coord *coords,
301 MIXCHAIN *chain,
302 int cur)
303{
304 int i;
305
306 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++)
307 {
308 mutt_window_move (MuttIndexWindow, i, 0);
309 mutt_window_clrtoeol (MuttIndexWindow);
310 }
311
312 for (i = 0; i < chain->cl; i++)
313 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
314}
315
316static void mix_redraw_head (MIXCHAIN *chain)
317{
318 SETCOLOR (MT_COLOR_STATUS);
319 mutt_window_mvprintw (MuttIndexWindow, MIX_VOFFSET - 1, 0,
320 "-- Remailer chain [Length: %d]", chain ? chain->cl : 0);
321 mutt_window_clrtoeol (MuttIndexWindow);
322 NORMAL_COLOR;
323}
324
325static const char *mix_format_caps (REMAILER *r)
326{
327 static char capbuff[10];
328 char *t = capbuff;
329
330 if (r->caps & MIX_CAP_COMPRESS)
331 *t++ = 'C';
332 else
333 *t++ = ' ';
334
335 if (r->caps & MIX_CAP_MIDDLEMAN)
336 *t++ = 'M';
337 else
338 *t++ = ' ';
339
340 if (r->caps & MIX_CAP_NEWSPOST)
341 {
342 *t++ = 'N';
343 *t++ = 'p';
344 }
345 else
346 {
347 *t++ = ' ';
348 *t++ = ' ';
349 }
350
351 if (r->caps & MIX_CAP_NEWSMAIL)
352 {
353 *t++ = 'N';
354 *t++ = 'm';
355 }
356 else
357 {
358 *t++ = ' ';
359 *t++ = ' ';
360 }
361
362 *t = '\0';
363
364 return capbuff;
365}
366
367/*
368 * Format an entry for the remailer menu.
369 *
370 * %n number
371 * %c capabilities
372 * %s short name
373 * %a address
374 *
375 */
376
377static const char *mix_entry_fmt (char *dest,
378 size_t destlen,
379 size_t col,
380 int cols,
381 char op,
382 const char *src,
383 const char *prefix,
384 const char *ifstring,
385 const char *elsestring,
386 unsigned long data,
387 format_flag flags)
388{
389 char fmt[16];
390 REMAILER *remailer = (REMAILER *) data;
391 int optional = (flags & MUTT_FORMAT_OPTIONAL);
392
393 switch (op)
394 {
395 case 'n':
396 if (!optional)
397 {
398 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
399 snprintf (dest, destlen, fmt, remailer->num);
400 }
401 break;
402 case 'c':
403 if (!optional)
404 {
405 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
406 snprintf (dest, destlen, fmt, mix_format_caps(remailer));
407 }
408 break;
409 case 's':
410 if (!optional)
411 {
412 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
413 snprintf (dest, destlen, fmt, NONULL(remailer->shortname));
414 }
415 else if (!remailer->shortname)
416 optional = 0;
417 break;
418 case 'a':
419 if (!optional)
420 {
421 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
422 snprintf (dest, destlen, fmt, NONULL(remailer->addr));
423 }
424 else if (!remailer->addr)
425 optional = 0;
426 break;
427
428 default:
429 *dest = '\0';
430 }
431
432 if (optional)
433 mutt_FormatString (dest, destlen, col, cols, ifstring, mutt_attach_fmt, data, 0);
434 else if (flags & MUTT_FORMAT_OPTIONAL)
435 mutt_FormatString (dest, destlen, col, cols, elsestring, mutt_attach_fmt, data, 0);
436 return (src);
437}
438
439
440
441static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num)
442{
443 REMAILER **type2_list = (REMAILER **) menu->data;
444 mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (MixEntryFormat), mix_entry_fmt,
445 (unsigned long) type2_list[num], MUTT_FORMAT_ARROWCURSOR);
446}
447
448static int mix_chain_add (MIXCHAIN *chain, const char *s,
449 REMAILER **type2_list)
450{
451 int i;
452
453 if (chain->cl >= MAXMIXES)
454 return -1;
455
456 if (!mutt_strcmp (s, "0") || !ascii_strcasecmp (s, "<random>"))
457 {
458 chain->ch[chain->cl++] = 0;
459 return 0;
460 }
461
462 for (i = 0; type2_list[i]; i++)
463 {
464 if (!ascii_strcasecmp (s, type2_list[i]->shortname))
465 {
466 chain->ch[chain->cl++] = i;
467 return 0;
468 }
469 }
470
471 /* replace unknown remailers by <random> */
472
473 if (!type2_list[i])
474 chain->ch[chain->cl++] = 0;
475
476 return 0;
477}
478
479static const struct mapping_t RemailerHelp[] =
480{
481 { N_("Append"), OP_MIX_APPEND },
482 { N_("Insert"), OP_MIX_INSERT },
483 { N_("Delete"), OP_MIX_DELETE },
484 { N_("Abort"), OP_EXIT },
485 { N_("OK"), OP_MIX_USE },
486 { NULL, 0 }
487};
488
489
490void mix_make_chain (LIST **chainp, int *redraw)
491{
492 LIST *p;
493 MIXCHAIN *chain;
494 int c_cur = 0, c_old = 0;
495 short c_redraw = 1;
496
497 REMAILER **type2_list = NULL;
498 size_t ttll = 0;
499
500 struct coord *coords = NULL;
501
502 MUTTMENU *menu;
503 char helpstr[LONG_STRING];
504 short loop = 1;
505 int op;
506
507 int i, j;
508 char *t;
509
510 if (!(type2_list = mix_type2_list (&ttll)))
511 {
512 mutt_error _("Can't get mixmaster's type2.list!");
513 return;
514 }
515
516 *redraw = REDRAW_FULL;
517
518 chain = safe_calloc (sizeof (MIXCHAIN), 1);
519 for (p = *chainp; p; p = p->next)
520 mix_chain_add (chain, (char *) p->data, type2_list);
521
522 mutt_free_list (chainp);
523
524 /* safety check */
525 for (i = 0; i < chain->cl; i++)
526 {
527 if (chain->ch[i] >= ttll)
528 chain->ch[i] = 0;
529 }
530
531 mix_screen_coordinates (type2_list, &coords, chain, 0);
532
533 menu = mutt_new_menu (MENU_MIX);
534 menu->max = ttll;
535 menu->make_entry = mix_entry;
536 menu->tag = NULL;
537 menu->title = _("Select a remailer chain.");
538 menu->data = type2_list;
539 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
540 menu->pagelen = MIX_VOFFSET - 1;
541
542 while (loop)
543 {
544 if (menu->pagelen != MIX_VOFFSET - 1)
545 {
546 menu->pagelen = MIX_VOFFSET - 1;
547 menu->redraw = REDRAW_FULL;
548 }
549
550 if (c_redraw)
551 {
552 mix_redraw_head (chain);
553 mix_redraw_chain (type2_list, coords, chain, c_cur);
554 c_redraw = 0;
555 }
556 else if (c_cur != c_old)
557 {
558 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
559 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
560 }
561
562 c_old = c_cur;
563
564 switch ((op = mutt_menuLoop (menu)))
565 {
566 case OP_REDRAW:
567 {
568 menu_redraw_status (menu);
569 mix_redraw_head (chain);
570 mix_screen_coordinates (type2_list, &coords, chain, 0);
571 mix_redraw_chain (type2_list, coords, chain, c_cur);
572 menu->pagelen = MIX_VOFFSET - 1;
573 break;
574 }
575
576 case OP_EXIT:
577 {
578 chain->cl = 0;
579 loop = 0;
580 break;
581 }
582
583 case OP_MIX_USE:
584 {
585 if (!chain->cl)
586 {
587 chain->cl++;
588 chain->ch[0] = menu->current;
589 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
590 c_redraw = 1;
591 }
592
593 if (chain->cl && chain->ch[chain->cl - 1] &&
594 (type2_list[chain->ch[chain->cl-1]]->caps & MIX_CAP_MIDDLEMAN))
595 {
596 mutt_error ( _("Error: %s can't be used as the final remailer of a chain."),
597 type2_list[chain->ch[chain->cl - 1]]->shortname);
598 }
599 else
600 {
601 loop = 0;
602 }
603 break;
604 }
605
606 case OP_GENERIC_SELECT_ENTRY:
607 case OP_MIX_APPEND:
608 {
609 if (chain->cl < MAXMIXES && c_cur < chain->cl)
610 c_cur++;
611 }
612 /* fallthrough */
613 case OP_MIX_INSERT:
614 {
615 if (chain->cl < MAXMIXES)
616 {
617 chain->cl++;
618 for (i = chain->cl - 1; i > c_cur; i--)
619 chain->ch[i] = chain->ch[i-1];
620
621 chain->ch[c_cur] = menu->current;
622 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
623 c_redraw = 1;
624 }
625 else
626 mutt_error ( _("Mixmaster chains are limited to %d elements."),
627 MAXMIXES);
628
629 break;
630 }
631
632 case OP_MIX_DELETE:
633 {
634 if (chain->cl)
635 {
636 chain->cl--;
637
638 for (i = c_cur; i < chain->cl; i++)
639 chain->ch[i] = chain->ch[i+1];
640
641 if (c_cur == chain->cl && c_cur)
642 c_cur--;
643
644 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
645 c_redraw = 1;
646 }
647 else
648 {
649 mutt_error _("The remailer chain is already empty.");
650 }
651 break;
652 }
653
654 case OP_MIX_CHAIN_PREV:
655 {
656 if (c_cur)
657 c_cur--;
658 else
659 mutt_error _("You already have the first chain element selected.");
660
661 break;
662 }
663
664 case OP_MIX_CHAIN_NEXT:
665 {
666 if (chain->cl && c_cur < chain->cl - 1)
667 c_cur++;
668 else
669 mutt_error _("You already have the last chain element selected.");
670
671 break;
672 }
673 }
674 }
675
676 mutt_menuDestroy (&menu);
677
678 /* construct the remailer list */
679
680 if (chain->cl)
681 {
682 for (i = 0; i < chain->cl; i++)
683 {
684 if ((j = chain->ch[i]))
685 t = type2_list[j]->shortname;
686 else
687 t = "*";
688
689 *chainp = mutt_add_list (*chainp, t);
690 }
691 }
692
693 mix_free_type2_list (&type2_list);
694 FREE (&coords);
695 FREE (&chain);
696}
697
698/* some safety checks before piping the message to mixmaster */
699
700int mix_check_message (HEADER *msg)
701{
702 const char *fqdn;
703 short need_hostname = 0;
704 ADDRESS *p;
705
706 if (msg->env->cc || msg->env->bcc)
707 {
708 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
709 return -1;
710 }
711
712 /* When using mixmaster, we MUST qualify any addresses since
713 * the message will be delivered through remote systems.
714 *
715 * use_domain won't be respected at this point, hidden_host will.
716 */
717
718 for (p = msg->env->to; p; p = p->next)
719 {
720 if (!p->group && strchr (p->mailbox, '@') == NULL)
721 {
722 need_hostname = 1;
723 break;
724 }
725 }
726
727 if (need_hostname)
728 {
729
730 if (!(fqdn = mutt_fqdn (1)))
731 {
732 mutt_error _("Please set the hostname variable to a proper value when using mixmaster!");
733 return (-1);
734 }
735
736 /* Cc and Bcc are empty at this point. */
737 rfc822_qualify (msg->env->to, fqdn);
738 rfc822_qualify (msg->env->reply_to, fqdn);
739 rfc822_qualify (msg->env->mail_followup_to, fqdn);
740 }
741
742 return 0;
743}
744
745int mix_send_message (LIST *chain, const char *tempfile)
746{
747 char cmd[HUGE_STRING];
748 char tmp[HUGE_STRING];
749 char cd_quoted[STRING];
750 int i;
751
752 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
753
754 for (i = 0; chain; chain = chain->next, i = 1)
755 {
756 strfcpy (tmp, cmd, sizeof (tmp));
757 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data);
758 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted);
759 }
760
761 if (!option (OPTNOCURSES))
762 mutt_endwin (NULL);
763
764 if ((i = mutt_system (cmd)))
765 {
766 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
767 if (!option (OPTNOCURSES))
768 {
769 mutt_any_key_to_continue (NULL);
770 mutt_error _("Error sending message.");
771 }
772 }
773
774 unlink (tempfile);
775 return i;
776}
777
778
779#endif