remove a whole lot of #include mutt.h
[apps/madmutt.git] / remailer.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
4  *
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.
8  */
9
10 /*
11  * Mixmaster support for Mutt
12  */
13
14 #include <lib-lib/lib-lib.h>
15
16 #include <lib-sys/unix.h>
17 #include <lib-ui/curses.h>
18 #include <lib-ui/menu.h>
19
20 #include "mutt.h"
21 #include "recvattach.h"
22
23 #include "remailer.h"
24
25 #define SW              (option(OPTMBOXPANE)?SidebarWidth:0)
26
27 #ifdef MIXMASTER
28
29 struct coord {
30   short r, c;
31 };
32
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 **,
49                                     MIXCHAIN *, int);
50
51 static int mix_get_caps (const char *capstr)
52 {
53   int caps = 0;
54
55   while (*capstr) {
56     switch (*capstr) {
57     case 'C':
58       caps |= MIX_CAP_COMPRESS;
59       break;
60
61     case 'M':
62       caps |= MIX_CAP_MIDDLEMAN;
63       break;
64
65     case 'N':
66       {
67         switch (*++capstr) {
68         case 'm':
69           caps |= MIX_CAP_NEWSMAIL;
70           break;
71
72         case 'p':
73           caps |= MIX_CAP_NEWSPOST;
74           break;
75
76         }
77       }
78     }
79
80     if (*capstr)
81       capstr++;
82   }
83
84   return caps;
85 }
86
87 static void mix_add_entry (REMAILER *** type2_list, REMAILER * entry,
88                            ssize_t * slots, ssize_t * used)
89 {
90   if (*used == *slots) {
91     *slots += 5;
92     p_realloc(type2_list, *slots);
93   }
94
95   (*type2_list)[(*used)++] = entry;
96   if (entry)
97     entry->num = *used;
98 }
99
100 static REMAILER *mix_new_remailer (void)
101 {
102   return p_new(REMAILER, 1);
103 }
104
105 static void mix_free_remailer (REMAILER ** r)
106 {
107   p_delete(&(*r)->shortname);
108   p_delete(&(*r)->addr);
109   p_delete(&(*r)->ver);
110
111   p_delete(r);
112 }
113
114 /* parse the type2.list as given by mixmaster -T */
115
116 static REMAILER **mix_type2_list (ssize_t * l)
117 {
118   FILE *fp;
119   pid_t mm_pid;
120   int devnull;
121
122   char cmd[HUGE_STRING + _POSIX_PATH_MAX];
123   char line[HUGE_STRING];
124   char *t;
125
126   REMAILER **type2_list = NULL, *p;
127   ssize_t slots = 0, used = 0;
128
129   if (!l)
130     return NULL;
131
132   if ((devnull = open ("/dev/null", O_RDWR)) == -1)
133     return NULL;
134
135   snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster);
136
137   if ((mm_pid =
138        mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1,
139                               devnull)) == -1) {
140     close (devnull);
141     return NULL;
142   }
143
144   /* first, generate the "random" remailer */
145
146   p = mix_new_remailer ();
147   p->shortname = m_strdup("<random>");
148   mix_add_entry (&type2_list, p, &slots, &used);
149
150   while (fgets (line, sizeof (line), fp)) {
151     p = mix_new_remailer ();
152
153     if (!(t = strtok (line, " \t\n")))
154       goto problem;
155
156     p->shortname = m_strdup(t);
157
158     if (!(t = strtok (NULL, " \t\n")))
159       goto problem;
160
161     p->addr = m_strdup(t);
162
163     if (!(t = strtok (NULL, " \t\n")))
164       goto problem;
165
166     if (!(t = strtok (NULL, " \t\n")))
167       goto problem;
168
169     p->ver = m_strdup(t);
170
171     if (!(t = strtok (NULL, " \t\n")))
172       goto problem;
173
174     p->caps = mix_get_caps (t);
175
176     mix_add_entry (&type2_list, p, &slots, &used);
177     continue;
178
179   problem:
180     mix_free_remailer (&p);
181   }
182
183   *l = used;
184
185   mix_add_entry (&type2_list, NULL, &slots, &used);
186   mutt_wait_filter (mm_pid);
187
188   close (devnull);
189
190   return type2_list;
191 }
192
193 static void mix_free_type2_list (REMAILER *** ttlp)
194 {
195   int i;
196   REMAILER **type2_list = *ttlp;
197
198   for (i = 0; type2_list[i]; i++)
199     mix_free_remailer (&type2_list[i]);
200
201   p_delete(type2_list);
202 }
203
204
205 #define MIX_HOFFSET 2
206 #define MIX_VOFFSET (LINES - 6)
207 #define MIX_MAXROW  (LINES - 3)
208
209
210 static void mix_screen_coordinates (REMAILER ** type2_list,
211                                     struct coord **coordsp,
212                                     MIXCHAIN * chain, int i)
213 {
214   short c, r, oc;
215   struct coord *coords;
216
217   if (!chain->cl)
218     return;
219
220   p_realloc(coordsp, chain->cl);
221
222   coords = *coordsp;
223
224   if (i) {
225     c =
226       coords[i - 1].c + m_strlen(type2_list[chain->ch[i - 1]]->shortname) + 2;
227     r = coords[i - 1].r;
228   }
229   else {
230     r = MIX_VOFFSET;
231     c = MIX_HOFFSET;
232   }
233
234
235   for (; i < chain->cl; i++) {
236     oc = c;
237     c += m_strlen(type2_list[chain->ch[i]]->shortname) + 2;
238
239     if (c >= COLS) {
240       oc = c = MIX_HOFFSET;
241       r++;
242     }
243
244     coords[i].c = oc;
245     coords[i].r = r;
246
247   }
248
249 }
250
251 static void mix_redraw_ce (REMAILER ** type2_list,
252                            struct coord *coords,
253                            MIXCHAIN * chain, int i, short selected)
254 {
255   if (!coords || !chain)
256     return;
257
258   if (coords[i].r < MIX_MAXROW) {
259
260     if (selected)
261       SETCOLOR (MT_COLOR_INDICATOR);
262     else
263       SETCOLOR (MT_COLOR_NORMAL);
264
265     mvaddstr (coords[i].r, coords[i].c, type2_list[chain->ch[i]]->shortname);
266     SETCOLOR (MT_COLOR_NORMAL);
267
268     if (i + 1 < chain->cl)
269       addstr (", ");
270   }
271 }
272
273 static void mix_redraw_chain (REMAILER ** type2_list,
274                               struct coord *coords, MIXCHAIN * chain, int cur)
275 {
276   int i;
277
278   SETCOLOR (MT_COLOR_NORMAL);
279   BKGDSET (MT_COLOR_NORMAL);
280
281   for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) {
282     move (i, 0);
283     clrtoeol ();
284   }
285
286   for (i = 0; i < chain->cl; i++)
287     mix_redraw_ce (type2_list, coords, chain, i, i == cur);
288 }
289
290 static void mix_redraw_head (MIXCHAIN * chain)
291 {
292   SETCOLOR (MT_COLOR_STATUS);
293   mvprintw (MIX_VOFFSET - 1, 0, "-- Remailer chain [Length: %d]",
294             chain ? chain->cl : 0);
295
296   BKGDSET (MT_COLOR_STATUS);
297   clrtoeol ();
298
299   BKGDSET (MT_COLOR_NORMAL);
300   SETCOLOR (MT_COLOR_NORMAL);
301 }
302
303 static const char *mix_format_caps (REMAILER * r)
304 {
305   static char capbuff[10];
306   char *t = capbuff;
307
308   if (r->caps & MIX_CAP_COMPRESS)
309     *t++ = 'C';
310   else
311     *t++ = ' ';
312
313   if (r->caps & MIX_CAP_MIDDLEMAN)
314     *t++ = 'M';
315   else
316     *t++ = ' ';
317
318   if (r->caps & MIX_CAP_NEWSPOST) {
319     *t++ = 'N';
320     *t++ = 'p';
321   }
322   else {
323     *t++ = ' ';
324     *t++ = ' ';
325   }
326
327   if (r->caps & MIX_CAP_NEWSMAIL) {
328     *t++ = 'N';
329     *t++ = 'm';
330   }
331   else {
332     *t++ = ' ';
333     *t++ = ' ';
334   }
335
336   *t = '\0';
337
338   return capbuff;
339 }
340
341 /*
342  * Format an entry for the remailer menu.
343  * 
344  * %n   number
345  * %c   capabilities
346  * %s   short name
347  * %a   address
348  *
349  */
350
351 static const char *mix_entry_fmt (char *dest,
352                                   ssize_t destlen,
353                                   char op,
354                                   const char *src,
355                                   const char *prefix,
356                                   const char *ifstring,
357                                   const char *elsestring,
358                                   unsigned long data, format_flag flags)
359 {
360   char fmt[16];
361   REMAILER *remailer = (REMAILER *) data;
362   int optional = (flags & M_FORMAT_OPTIONAL);
363
364   switch (op) {
365   case 'n':
366     if (!optional) {
367       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
368       snprintf (dest, destlen, fmt, remailer->num);
369     }
370     break;
371   case 'c':
372     if (!optional) {
373       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
374       snprintf (dest, destlen, fmt, mix_format_caps (remailer));
375     }
376     break;
377   case 's':
378     if (!optional) {
379       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
380       snprintf (dest, destlen, fmt, NONULL (remailer->shortname));
381     }
382     else if (!remailer->shortname)
383       optional = 0;
384     break;
385   case 'a':
386     if (!optional) {
387       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
388       snprintf (dest, destlen, fmt, NONULL (remailer->addr));
389     }
390     else if (!remailer->addr)
391       optional = 0;
392     break;
393
394   default:
395     *dest = '\0';
396   }
397
398   if (optional)
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);
402   return (src);
403 }
404
405
406
407 static void mix_entry (char *b, ssize_t blen, MUTTMENU * menu, int num)
408 {
409   REMAILER **type2_list = (REMAILER **) menu->data;
410   int w = (COLS-SW) > blen ? blen : (COLS-SW);
411
412   mutt_FormatString (b, w, NONULL (MixEntryFormat), mix_entry_fmt,
413                      (unsigned long) type2_list[num], M_FORMAT_ARROWCURSOR);
414 }
415
416 static int mix_chain_add (MIXCHAIN * chain, const char *s,
417                           REMAILER ** type2_list)
418 {
419   int i;
420
421   if (chain->cl >= MAXMIXES)
422     return -1;
423
424   if (!m_strcmp(s, "0") || !ascii_strcasecmp (s, "<random>")) {
425     chain->ch[chain->cl++] = 0;
426     return 0;
427   }
428
429   for (i = 0; type2_list[i]; i++) {
430     if (!ascii_strcasecmp (s, type2_list[i]->shortname)) {
431       chain->ch[chain->cl++] = i;
432       return 0;
433     }
434   }
435
436   /* replace unknown remailers by <random> */
437
438   if (!type2_list[i])
439     chain->ch[chain->cl++] = 0;
440
441   return 0;
442 }
443
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},
450   {NULL, OP_NULL}
451 };
452
453
454 void mix_make_chain (string_list_t ** chainp, int *redraw)
455 {
456   string_list_t *p;
457   MIXCHAIN *chain;
458   int c_cur = 0, c_old = 0;
459   int m_len;
460   short c_redraw = 1;
461
462   REMAILER **type2_list = NULL;
463   ssize_t ttll = 0;
464
465   struct coord *coords = NULL;
466
467   MUTTMENU *menu;
468   char helpstr[SHORT_STRING];
469   short loop = 1;
470   int op;
471
472   int i, j;
473   const char *t;
474
475   if (!(type2_list = mix_type2_list (&ttll))) {
476     mutt_error _("Can't get mixmaster's type2.list!");
477
478     return;
479   }
480
481   *redraw = REDRAW_FULL;
482
483   chain = p_new(MIXCHAIN, 1);
484   for (p = *chainp; p; p = p->next)
485     mix_chain_add (chain, (char *) p->data, type2_list);
486
487   string_list_wipe(chainp);
488
489   /* safety check */
490   for (i = 0; i < chain->cl; i++) {
491     if (chain->ch[i] >= ttll)
492       chain->ch[i] = 0;
493   }
494
495   mix_screen_coordinates (type2_list, &coords, chain, 0);
496
497   menu = mutt_new_menu ();
498   menu->menu = MENU_MIX;
499   menu->max = ttll;
500   menu->make_entry = mix_entry;
501   menu->tag = NULL;
502   menu->title = _("Select a remailer chain.");
503   menu->data = type2_list;
504   menu->help =
505     mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp);
506
507   m_len = menu->pagelen = MIX_VOFFSET - menu->offset - 1;
508
509   while (loop) {
510     if (menu->pagelen != m_len) {
511       menu->pagelen = m_len;
512       menu->redraw = REDRAW_FULL;
513     }
514
515     if (c_redraw) {
516       mix_redraw_head (chain);
517       mix_redraw_chain (type2_list, coords, chain, c_cur);
518       c_redraw = 0;
519     }
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);
523     }
524
525     c_old = c_cur;
526
527     switch ((op = mutt_menuLoop (menu))) {
528     case OP_REDRAW:
529       {
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;
535         break;
536       }
537
538     case OP_EXIT:
539       {
540         chain->cl = 0;
541         loop = 0;
542         break;
543       }
544
545     case OP_MIX_USE:
546       {
547         if (!chain->cl) {
548           chain->cl++;
549           chain->ch[0] = menu->current;
550           mix_screen_coordinates (type2_list, &coords, chain, c_cur);
551           c_redraw = 1;
552         }
553
554         if (chain->cl && chain->ch[chain->cl - 1] &&
555             (type2_list[chain->ch[chain->cl - 1]]->caps & MIX_CAP_MIDDLEMAN))
556         {
557           mutt_error (_
558                       ("Error: %s can't be used as the final remailer of a chain."),
559                       type2_list[chain->ch[chain->cl - 1]]->shortname);
560         }
561         else {
562           loop = 0;
563         }
564         break;
565       }
566
567     case OP_GENERIC_SELECT_ENTRY:
568     case OP_MIX_APPEND:
569       {
570         if (chain->cl < MAXMIXES && c_cur < chain->cl)
571           c_cur++;
572       }
573       /* fallthrough */
574     case OP_MIX_INSERT:
575       {
576         if (chain->cl < MAXMIXES) {
577           chain->cl++;
578           for (i = chain->cl - 1; i > c_cur; i--)
579             chain->ch[i] = chain->ch[i - 1];
580
581           chain->ch[c_cur] = menu->current;
582           mix_screen_coordinates (type2_list, &coords, chain, c_cur);
583           c_redraw = 1;
584         }
585         else
586           mutt_error (_("Mixmaster chains are limited to %d elements."),
587                       MAXMIXES);
588
589         break;
590       }
591
592     case OP_MIX_DELETE:
593       {
594         if (chain->cl) {
595           chain->cl--;
596
597           for (i = c_cur; i < chain->cl; i++)
598             chain->ch[i] = chain->ch[i + 1];
599
600           if (c_cur == chain->cl && c_cur)
601             c_cur--;
602
603           mix_screen_coordinates (type2_list, &coords, chain, c_cur);
604           c_redraw = 1;
605         }
606         else {
607           mutt_error _("The remailer chain is already empty.");
608         }
609         break;
610       }
611
612     case OP_MIX_CHAIN_PREV:
613       {
614         if (c_cur)
615           c_cur--;
616         else
617           mutt_error _("You already have the first chain element selected.");
618
619         break;
620       }
621
622     case OP_MIX_CHAIN_NEXT:
623       {
624         if (chain->cl && c_cur < chain->cl - 1)
625           c_cur++;
626         else
627           mutt_error _("You already have the last chain element selected.");
628
629         break;
630       }
631     }
632   }
633
634   mutt_menuDestroy (&menu);
635
636   /* construct the remailer list */
637
638   if (chain->cl) {
639     for (i = 0; i < chain->cl; i++) {
640       if ((j = chain->ch[i]))
641         t = type2_list[j]->shortname;
642       else
643         t = "*";
644
645       *chainp = mutt_add_list (*chainp, t);
646     }
647   }
648
649   mix_free_type2_list (&type2_list);
650   p_delete(&coords);
651   p_delete(&chain);
652 }
653
654 /* some safety checks before piping the message to mixmaster */
655
656 int mix_check_message (HEADER * msg)
657 {
658   const char *fqdn;
659   short need_hostname = 0;
660   address_t *p;
661
662   if (msg->env->cc || msg->env->bcc) {
663     mutt_error _("Mixmaster doesn't accept Cc or Bcc headers.");
664
665     return -1;
666   }
667
668   /* When using mixmaster, we MUST qualify any addresses since
669    * the message will be delivered through remote systems.
670    * 
671    * use_domain won't be respected at this point, hidden_host will.
672    */
673
674   for (p = msg->env->to; p; p = p->next) {
675     if (!p->group && strchr (p->mailbox, '@') == NULL) {
676       need_hostname = 1;
677       break;
678     }
679   }
680
681   if (need_hostname) {
682
683     if (!(fqdn = mutt_fqdn (1))) {
684       mutt_error
685         _
686         ("Please set the hostname variable to a proper value when using mixmaster!");
687       return (-1);
688     }
689
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);
694   }
695
696   return 0;
697 }
698
699 int mix_send_message (string_list_t * chain, const char *tempfile)
700 {
701   char cmd[HUGE_STRING];
702   char tmp[HUGE_STRING];
703   char cd_quoted[STRING];
704   int i;
705
706   snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster);
707
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);
712   }
713
714   if (!option (OPTNOCURSES))
715     mutt_endwin (NULL);
716
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.");
722     }
723   }
724
725   unlink (tempfile);
726   return i;
727 }
728
729
730 #endif