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