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 BUFFER *cmd = NULL;
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 cmd = mutt_buffer_pool_get ();
154 mutt_buffer_printf (cmd, "%s -T", Mixmaster);
155
156 if ((mm_pid = mutt_create_filter_fd (mutt_b2s (cmd), NULL, &fp, NULL, devnull,
157 -1, devnull)) == -1)
158 {
159 mutt_buffer_pool_release (&cmd);
160 close (devnull);
161 return NULL;
162 }
163
164 mutt_buffer_pool_release (&cmd);
165
166 /* first, generate the "random" remailer */
167
168 p = mix_new_remailer ();
169 p->shortname = safe_strdup ("<random>");
170 mix_add_entry (&type2_list, p, &slots, &used);
171
172 while (fgets (line, sizeof (line), fp))
173 {
174 p = mix_new_remailer ();
175
176 if (!(t = strtok (line, " \t\n")))
177 goto problem;
178
179 p->shortname = safe_strdup (t);
180
181 if (!(t = strtok (NULL, " \t\n")))
182 goto problem;
183
184 p->addr = safe_strdup (t);
185
186 if (!(t = strtok (NULL, " \t\n")))
187 goto problem;
188
189 if (!(t = strtok (NULL, " \t\n")))
190 goto problem;
191
192 p->ver = safe_strdup (t);
193
194 if (!(t = strtok (NULL, " \t\n")))
195 goto problem;
196
197 p->caps = mix_get_caps (t);
198
199 mix_add_entry (&type2_list, p, &slots, &used);
200 continue;
201
202 problem:
203 mix_free_remailer (&p);
204 }
205
206 *l = used;
207
208 mix_add_entry (&type2_list, NULL, &slots, &used);
209 mutt_wait_filter (mm_pid);
210
211 close (devnull);
212
213 return type2_list;
214}
215
216static void mix_free_type2_list (REMAILER ***ttlp)
217{
218 int i;
219 REMAILER **type2_list = *ttlp;
220
221 for (i = 0; type2_list[i]; i++)
222 mix_free_remailer (&type2_list[i]);
223
224 FREE (type2_list); /* __FREE_CHECKED__ */
225}
226
227
228#define MIX_HOFFSET 2
229#define MIX_VOFFSET (MuttIndexWindow->rows - 4)
230#define MIX_MAXROW (MuttIndexWindow->rows - 1)
231
232
233static void mix_screen_coordinates (REMAILER **type2_list,
234 struct coord **coordsp,
235 MIXCHAIN *chain,
236 int i)
237{
238 short c, r, oc;
239 struct coord *coords;
240
241 if (!chain->cl)
242 return;
243
244 safe_realloc (coordsp, sizeof (struct coord) * chain->cl);
245
246 coords = *coordsp;
247
248 if (i)
249 {
250 c = coords[i-1].c + strlen (type2_list[chain->ch[i-1]]->shortname) + 2;
251 r = coords[i-1].r;
252 }
253 else
254 {
255 r = MIX_VOFFSET;
256 c = MIX_HOFFSET;
257 }
258
259
260 for (; i < chain->cl; i++)
261 {
262 oc = c;
263 c += strlen (type2_list[chain->ch[i]]->shortname) + 2;
264
265 if (c >= MuttIndexWindow->cols)
266 {
267 oc = c = MIX_HOFFSET;
268 r++;
269 }
270
271 coords[i].c = oc;
272 coords[i].r = r;
273
274 }
275
276}
277
278static void mix_redraw_ce (REMAILER **type2_list,
279 struct coord *coords,
280 MIXCHAIN *chain,
281 int i,
282 short selected)
283{
284 if (!coords || !chain)
285 return;
286
287 if (coords[i].r < MIX_MAXROW)
288 {
289
290 if (selected)
291 SETCOLOR (MT_COLOR_INDICATOR);
292 else
293 NORMAL_COLOR;
294
295 mutt_window_mvaddstr (MuttIndexWindow, coords[i].r, coords[i].c,
296 type2_list[chain->ch[i]]->shortname);
297 NORMAL_COLOR;
298
299 if (i + 1 < chain->cl)
300 addstr (", ");
301 }
302}
303
304static void mix_redraw_chain (REMAILER **type2_list,
305 struct coord *coords,
306 MIXCHAIN *chain,
307 int cur)
308{
309 int i;
310
311 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++)
312 {
313 mutt_window_move (MuttIndexWindow, i, 0);
314 mutt_window_clrtoeol (MuttIndexWindow);
315 }
316
317 for (i = 0; i < chain->cl; i++)
318 mix_redraw_ce (type2_list, coords, chain, i, i == cur);
319}
320
321static void mix_redraw_head (MIXCHAIN *chain)
322{
323 SETCOLOR (MT_COLOR_STATUS);
324 mutt_window_mvprintw (MuttIndexWindow, MIX_VOFFSET - 1, 0,
325 "-- Remailer chain [Length: %d]", chain ? chain->cl : 0);
326 mutt_window_clrtoeol (MuttIndexWindow);
327 NORMAL_COLOR;
328}
329
330static const char *mix_format_caps (REMAILER *r)
331{
332 static char capbuff[10];
333 char *t = capbuff;
334
335 if (r->caps & MIX_CAP_COMPRESS)
336 *t++ = 'C';
337 else
338 *t++ = ' ';
339
340 if (r->caps & MIX_CAP_MIDDLEMAN)
341 *t++ = 'M';
342 else
343 *t++ = ' ';
344
345 if (r->caps & MIX_CAP_NEWSPOST)
346 {
347 *t++ = 'N';
348 *t++ = 'p';
349 }
350 else
351 {
352 *t++ = ' ';
353 *t++ = ' ';
354 }
355
356 if (r->caps & MIX_CAP_NEWSMAIL)
357 {
358 *t++ = 'N';
359 *t++ = 'm';
360 }
361 else
362 {
363 *t++ = ' ';
364 *t++ = ' ';
365 }
366
367 *t = '\0';
368
369 return capbuff;
370}
371
372/*
373 * Format an entry for the remailer menu.
374 *
375 * %n number
376 * %c capabilities
377 * %s short name
378 * %a address
379 *
380 */
381
382static const char *mix_entry_fmt (char *dest,
383 size_t destlen,
384 size_t col,
385 int cols,
386 char op,
387 const char *src,
388 const char *prefix,
389 const char *ifstring,
390 const char *elsestring,
391 unsigned long data,
392 format_flag flags)
393{
394 char fmt[16];
395 REMAILER *remailer = (REMAILER *) data;
396 int optional = (flags & MUTT_FORMAT_OPTIONAL);
397
398 switch (op)
399 {
400 case 'n':
401 if (!optional)
402 {
403 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
404 snprintf (dest, destlen, fmt, remailer->num);
405 }
406 break;
407 case 'c':
408 if (!optional)
409 {
410 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
411 snprintf (dest, destlen, fmt, mix_format_caps(remailer));
412 }
413 break;
414 case 's':
415 if (!optional)
416 {
417 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
418 snprintf (dest, destlen, fmt, NONULL(remailer->shortname));
419 }
420 else if (!remailer->shortname)
421 optional = 0;
422 break;
423 case 'a':
424 if (!optional)
425 {
426 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
427 snprintf (dest, destlen, fmt, NONULL(remailer->addr));
428 }
429 else if (!remailer->addr)
430 optional = 0;
431 break;
432
433 default:
434 *dest = '\0';
435 }
436
437 if (optional)
438 mutt_FormatString (dest, destlen, col, cols, ifstring, mutt_attach_fmt, data, 0);
439 else if (flags & MUTT_FORMAT_OPTIONAL)
440 mutt_FormatString (dest, destlen, col, cols, elsestring, mutt_attach_fmt, data, 0);
441 return (src);
442}
443
444
445
446static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num)
447{
448 REMAILER **type2_list = (REMAILER **) menu->data;
449 mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (MixEntryFormat), mix_entry_fmt,
450 (unsigned long) type2_list[num], MUTT_FORMAT_ARROWCURSOR);
451}
452
453static int mix_chain_add (MIXCHAIN *chain, const char *s,
454 REMAILER **type2_list)
455{
456 int i;
457
458 if (chain->cl >= MAXMIXES)
459 return -1;
460
461 if (!mutt_strcmp (s, "0") || !ascii_strcasecmp (s, "<random>"))
462 {
463 chain->ch[chain->cl++] = 0;
464 return 0;
465 }
466
467 for (i = 0; type2_list[i]; i++)
468 {
469 if (!ascii_strcasecmp (s, type2_list[i]->shortname))
470 {
471 chain->ch[chain->cl++] = i;
472 return 0;
473 }
474 }
475
476 /* replace unknown remailers by <random> */
477
478 if (!type2_list[i])
479 chain->ch[chain->cl++] = 0;
480
481 return 0;
482}
483
484static const struct mapping_t RemailerHelp[] =
485{
486 { N_("Append"), OP_MIX_APPEND },
487 { N_("Insert"), OP_MIX_INSERT },
488 { N_("Delete"), OP_MIX_DELETE },
489 { N_("Abort"), OP_EXIT },
490 { N_("OK"), OP_MIX_USE },
491 { NULL, 0 }
492};
493
494
495void mix_make_chain (LIST **chainp)
496{
497 LIST *p;
498 MIXCHAIN *chain;
499 int c_cur = 0, c_old = 0;
500 short c_redraw = 1;
501
502 REMAILER **type2_list = NULL;
503 size_t ttll = 0;
504
505 struct coord *coords = NULL;
506
507 MUTTMENU *menu;
508 char helpstr[LONG_STRING];
509 short loop = 1;
510 int op;
511
512 int i, j;
513 char *t;
514
515 if (!(type2_list = mix_type2_list (&ttll)))
516 {
517 mutt_error _("Can't get mixmaster's type2.list!");
518 return;
519 }
520
521 chain = safe_calloc (sizeof (MIXCHAIN), 1);
522 for (p = *chainp; p; p = p->next)
523 mix_chain_add (chain, (char *) p->data, type2_list);
524
525 mutt_free_list (chainp);
526
527 /* safety check */
528 for (i = 0; i < chain->cl; i++)
529 {
530 if (chain->ch[i] >= ttll)
531 chain->ch[i] = 0;
532 }
533
534 mix_screen_coordinates (type2_list, &coords, chain, 0);
535
536 menu = mutt_new_menu (MENU_MIX);
537 menu->max = ttll;
538 menu->make_entry = mix_entry;
539 menu->tag = NULL;
540 menu->title = _("Select a remailer chain.");
541 menu->data = type2_list;
542 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
543 menu->pagelen = MIX_VOFFSET - 1;
544 mutt_push_current_menu (menu);
545
546 while (loop)
547 {
548 if (menu->pagelen != MIX_VOFFSET - 1)
549 {
550 menu->pagelen = MIX_VOFFSET - 1;
551 menu->redraw = REDRAW_FULL;
552 }
553
554 if (c_redraw)
555 {
556 mix_redraw_head (chain);
557 mix_redraw_chain (type2_list, coords, chain, c_cur);
558 c_redraw = 0;
559 }
560 else if (c_cur != c_old)
561 {
562 mix_redraw_ce (type2_list, coords, chain, c_old, 0);
563 mix_redraw_ce (type2_list, coords, chain, c_cur, 1);
564 }
565
566 c_old = c_cur;
567
568 switch ((op = mutt_menuLoop (menu)))
569 {
570 case OP_REDRAW:
571 {
572 menu_redraw_status (menu);
573 mix_redraw_head (chain);
574 mix_screen_coordinates (type2_list, &coords, chain, 0);
575 mix_redraw_chain (type2_list, coords, chain, c_cur);
576 menu->pagelen = MIX_VOFFSET - 1;
577 break;
578 }
579
580 case OP_EXIT:
581 {
582 chain->cl = 0;
583 loop = 0;
584 break;
585 }
586
587 case OP_MIX_USE:
588 {
589 if (!chain->cl)
590 {
591 chain->cl++;
592 chain->ch[0] = menu->current;
593 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
594 c_redraw = 1;
595 }
596
597 if (chain->cl && chain->ch[chain->cl - 1] &&
598 (type2_list[chain->ch[chain->cl-1]]->caps & MIX_CAP_MIDDLEMAN))
599 {
600 mutt_error ( _("Error: %s can't be used as the final remailer of a chain."),
601 type2_list[chain->ch[chain->cl - 1]]->shortname);
602 }
603 else
604 {
605 loop = 0;
606 }
607 break;
608 }
609
610 case OP_GENERIC_SELECT_ENTRY:
611 case OP_MIX_APPEND:
612 {
613 if (chain->cl < MAXMIXES && c_cur < chain->cl)
614 c_cur++;
615 }
616 /* fallthrough */
617 case OP_MIX_INSERT:
618 {
619 if (chain->cl < MAXMIXES)
620 {
621 chain->cl++;
622 for (i = chain->cl - 1; i > c_cur; i--)
623 chain->ch[i] = chain->ch[i-1];
624
625 chain->ch[c_cur] = menu->current;
626 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
627 c_redraw = 1;
628 }
629 else
630 mutt_error ( _("Mixmaster chains are limited to %d elements."),
631 MAXMIXES);
632
633 break;
634 }
635
636 case OP_MIX_DELETE:
637 {
638 if (chain->cl)
639 {
640 chain->cl--;
641
642 for (i = c_cur; i < chain->cl; i++)
643 chain->ch[i] = chain->ch[i+1];
644
645 if (c_cur == chain->cl && c_cur)
646 c_cur--;
647
648 mix_screen_coordinates (type2_list, &coords, chain, c_cur);
649 c_redraw = 1;
650 }
651 else
652 {
653 mutt_error _("The remailer chain is already empty.");
654 }
655 break;
656 }
657
658 case OP_MIX_CHAIN_PREV:
659 {
660 if (c_cur)
661 c_cur--;
662 else
663 mutt_error _("You already have the first chain element selected.");
664
665 break;
666 }
667
668 case OP_MIX_CHAIN_NEXT:
669 {
670 if (chain->cl && c_cur < chain->cl - 1)
671 c_cur++;
672 else
673 mutt_error _("You already have the last chain element selected.");
674
675 break;
676 }
677 }
678 }
679
680 mutt_pop_current_menu (menu);
681 mutt_menuDestroy (&menu);
682
683 /* construct the remailer list */
684
685 if (chain->cl)
686 {
687 for (i = 0; i < chain->cl; i++)
688 {
689 if ((j = chain->ch[i]))
690 t = type2_list[j]->shortname;
691 else
692 t = "*";
693
694 *chainp = mutt_add_list (*chainp, t);
695 }
696 }
697
698 mix_free_type2_list (&type2_list);
699 FREE (&coords);
700 FREE (&chain);
701}
702
703/* some safety checks before piping the message to mixmaster */
704
705int mix_check_message (HEADER *msg)
706{
707 const char *fqdn;
708 short need_hostname = 0;
709 ADDRESS *p;
710
711 if (msg->env->cc || msg->env->bcc)
712 {
713 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
714 return -1;
715 }
716
717 /* When using mixmaster, we MUST qualify any addresses since
718 * the message will be delivered through remote systems.
719 *
720 * use_domain won't be respected at this point, hidden_host will.
721 */
722
723 for (p = msg->env->to; p; p = p->next)
724 {
725 if (!p->group && strchr (p->mailbox, '@') == NULL)
726 {
727 need_hostname = 1;
728 break;
729 }
730 }
731
732 if (need_hostname)
733 {
734
735 if (!(fqdn = mutt_fqdn (1)))
736 {
737 mutt_error _("Please set the hostname variable to a proper value when using mixmaster!");
738 return (-1);
739 }
740
741 /* Cc and Bcc are empty at this point. */
742 rfc822_qualify (msg->env->to, fqdn);
743 rfc822_qualify (msg->env->reply_to, fqdn);
744 rfc822_qualify (msg->env->mail_followup_to, fqdn);
745 }
746
747 return 0;
748}
749
750int mix_send_message (LIST *chain, const char *tempfile)
751{
752 BUFFER *cmd;
753 BUFFER *cd_quoted;
754 int i;
755
756 cmd = mutt_buffer_pool_get ();
757 cd_quoted = mutt_buffer_pool_get ();
758
759 mutt_buffer_printf (cmd, "cat %s | %s -m ", tempfile, Mixmaster);
760
761 for (i = 0; chain; chain = chain->next, i = 1)
762 {
763 mutt_buffer_addstr (cmd, i ? "," : " -l ");
764 mutt_buffer_quote_filename (cd_quoted, (char *) chain->data);
765 mutt_buffer_addstr (cmd, mutt_b2s (cd_quoted));
766 }
767
768 if (!option (OPTNOCURSES))
769 mutt_endwin (NULL);
770
771 if ((i = mutt_system (cmd->data)))
772 {
773 fprintf (stderr, _("Error sending message, child exited %d.\n"), i);
774 if (!option (OPTNOCURSES))
775 {
776 mutt_any_key_to_continue (NULL);
777 mutt_error _("Error sending message.");
778 }
779 }
780
781 mutt_buffer_pool_release (&cmd);
782 mutt_buffer_pool_release (&cd_quoted);
783 unlink (tempfile);
784 return i;
785}
786
787
788#endif