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