1 /**
2 * @file
3 * Mixmaster Remailer Dialog
4 *
5 * @authors
6 * Copyright (C) 1999-2001 Thomas Roessler <roessler@does-not-exist.org>
7 * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
8 *
9 * @copyright
10 * This program is free software: you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 2 of the License, or (at your option) any later
13 * version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * details.
19 *
20 * You should have received a copy of the GNU General Public License along with
21 * this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /**
25 * @page neo_remailer Mixmaster Remailer Dialog
26 *
27 * The Mixmaster Remailer Dialog lets the user edit anonymous remailer chain.
28 *
29 * ## Windows
30 *
31 * | Name | Type | See Also |
32 * | :------------------------ | :-------------- | :--------------------------- |
33 * | Mixmaster Remailer Dialog | WT_DLG_REMAILER | dlg_select_mixmaster_chain() |
34 *
35 * **Parent**
36 * - @ref gui_dialog
37 *
38 * **Children**
39 * - Hosts: @ref menu_window
40 * - Chain Bar: @ref gui_sbar
41 * - Chain: @ref gui_window
42 * - Remailer Bar: @ref gui_sbar
43 *
44 * ## Data
45 * - #Remailer
46 *
47 * The Mixmaster Remailer Dialog stores its data (#Remailer) in Menu::mdata.
48 *
49 * ## Events
50 *
51 * Once constructed, it is controlled by the following events:
52 *
53 * | Event Type | Handler |
54 * | :---------- | :------------------------- |
55 * | #NT_CONFIG | remailer_config_observer() |
56 * | #NT_WINDOW | remailer_window_observer() |
57 *
58 * The Mixmaster Remailer Dialog does not implement MuttWindow::recalc() or MuttWindow::repaint().
59 *
60 * Some other events are handled by the dialog's children.
61 */
62
63 #include "config.h"
64 #include <fcntl.h>
65 #include <stdbool.h>
66 #include <stdio.h>
67 #include <string.h>
68 #include <unistd.h>
69 #include "mutt/lib.h"
70 #include "address/lib.h"
71 #include "config/lib.h"
72 #include "email/lib.h"
73 #include "core/lib.h"
74 #include "gui/lib.h"
75 #include "color/lib.h"
76 #include "menu/lib.h"
77 #include "send/lib.h"
78 #include "format_flags.h"
79 #include "muttlib.h"
80 #include "opcodes.h"
81 #include "options.h"
82 #include "protos.h"
83 #ifdef MIXMASTER
84 #include "remailer.h"
85 #endif
86
87 /**
88 * struct Coord - Screen coordinates
89 */
90 struct Coord
91 {
92 short r; ///< row
93 short c; ///< column
94 };
95
96 /// Help Bar for the Mixmaster dialog
97 static const struct Mapping RemailerHelp[] = {
98 // clang-format off
99 { N_("Append"), OP_MIX_APPEND },
100 { N_("Insert"), OP_MIX_INSERT },
101 { N_("Delete"), OP_MIX_DELETE },
102 { N_("Abort"), OP_EXIT },
103 { N_("OK"), OP_MIX_USE },
104 { NULL, 0 },
105 // clang-format on
106 };
107
108 /**
109 * mix_get_caps - Get Mixmaster Capabilities
110 * @param capstr Capability string to parse
111 * @retval num Capabilities, see #MixCapFlags
112 */
mix_get_caps(const char * capstr)113 static MixCapFlags mix_get_caps(const char *capstr)
114 {
115 MixCapFlags caps = MIX_CAP_NO_FLAGS;
116
117 while (*capstr)
118 {
119 switch (*capstr)
120 {
121 case 'C':
122 caps |= MIX_CAP_COMPRESS;
123 break;
124
125 case 'M':
126 caps |= MIX_CAP_MIDDLEMAN;
127 break;
128
129 case 'N':
130 {
131 switch (*++capstr)
132 {
133 case 'm':
134 caps |= MIX_CAP_NEWSMAIL;
135 break;
136
137 case 'p':
138 caps |= MIX_CAP_NEWSPOST;
139 break;
140 }
141 }
142 }
143
144 if (*capstr)
145 capstr++;
146 }
147
148 return caps;
149 }
150
151 /**
152 * mix_add_entry - Add an entry to the Remailer list
153 * @param[out] type2_list Remailer list to add to
154 * @param[in] entry Remailer to add
155 * @param[out] slots Total number of slots
156 * @param[out] used Number of slots used
157 */
mix_add_entry(struct Remailer *** type2_list,struct Remailer * entry,size_t * slots,size_t * used)158 static void mix_add_entry(struct Remailer ***type2_list, struct Remailer *entry,
159 size_t *slots, size_t *used)
160 {
161 if (*used == *slots)
162 {
163 *slots += 5;
164 mutt_mem_realloc(type2_list, sizeof(struct Remailer *) * (*slots));
165 }
166
167 (*type2_list)[(*used)++] = entry;
168 if (entry)
169 entry->num = *used;
170 }
171
172 /**
173 * remailer_new - Create a new Remailer
174 * @retval ptr Newly allocated Remailer
175 */
remailer_new(void)176 static struct Remailer *remailer_new(void)
177 {
178 return mutt_mem_calloc(1, sizeof(struct Remailer));
179 }
180
181 /**
182 * remailer_free - Free a Remailer
183 * @param[out] ptr Remailer to free
184 */
remailer_free(struct Remailer ** ptr)185 static void remailer_free(struct Remailer **ptr)
186 {
187 if (!ptr || !*ptr)
188 return;
189
190 struct Remailer *r = *ptr;
191
192 FREE(&r->shortname);
193 FREE(&r->addr);
194 FREE(&r->ver);
195 FREE(ptr);
196 }
197
198 /**
199 * mix_type2_list - Parse the type2.list as given by mixmaster -T
200 * @param[out] l Length of list
201 * @retval ptr type2.list
202 */
mix_type2_list(size_t * l)203 static struct Remailer **mix_type2_list(size_t *l)
204 {
205 if (!l)
206 return NULL;
207
208 FILE *fp = NULL;
209 char line[8192];
210 char *t = NULL;
211
212 struct Remailer **type2_list = NULL;
213 struct Remailer *p = NULL;
214 size_t slots = 0, used = 0;
215
216 int fd_null = open("/dev/null", O_RDWR);
217 if (fd_null == -1)
218 return NULL;
219
220 struct Buffer *cmd = mutt_buffer_pool_get();
221 const char *const c_mixmaster = cs_subset_string(NeoMutt->sub, "mixmaster");
222 mutt_buffer_printf(cmd, "%s -T", c_mixmaster);
223
224 pid_t mm_pid =
225 filter_create_fd(mutt_buffer_string(cmd), NULL, &fp, NULL, fd_null, -1, fd_null);
226 if (mm_pid == -1)
227 {
228 mutt_buffer_pool_release(&cmd);
229 close(fd_null);
230 return NULL;
231 }
232
233 mutt_buffer_pool_release(&cmd);
234
235 /* first, generate the "random" remailer */
236
237 p = remailer_new();
238 p->shortname = mutt_str_dup(_("<random>"));
239 mix_add_entry(&type2_list, p, &slots, &used);
240
241 while (fgets(line, sizeof(line), fp))
242 {
243 p = remailer_new();
244
245 t = strtok(line, " \t\n");
246 if (!t)
247 goto problem;
248
249 p->shortname = mutt_str_dup(t);
250
251 t = strtok(NULL, " \t\n");
252 if (!t)
253 goto problem;
254
255 p->addr = mutt_str_dup(t);
256
257 t = strtok(NULL, " \t\n");
258 if (!t)
259 goto problem;
260
261 t = strtok(NULL, " \t\n");
262 if (!t)
263 goto problem;
264
265 p->ver = mutt_str_dup(t);
266
267 t = strtok(NULL, " \t\n");
268 if (!t)
269 goto problem;
270
271 p->caps = mix_get_caps(t);
272
273 mix_add_entry(&type2_list, p, &slots, &used);
274 continue;
275
276 problem:
277 remailer_free(&p);
278 }
279
280 *l = used;
281
282 mix_add_entry(&type2_list, NULL, &slots, &used);
283 filter_wait(mm_pid);
284
285 close(fd_null);
286
287 return type2_list;
288 }
289
290 /**
291 * mix_type2_list_free - Free a Remailer List
292 * @param[out] ttlp Remailer List to free
293 */
mix_type2_list_free(struct Remailer *** ttlp)294 static void mix_type2_list_free(struct Remailer ***ttlp)
295 {
296 struct Remailer **type2_list = *ttlp;
297
298 for (int i = 0; type2_list[i]; i++)
299 remailer_free(&type2_list[i]);
300
301 FREE(ttlp);
302 }
303
304 /**
305 * mix_screen_coordinates - Get the screen coordinates to place a chain
306 * @param[in] win Window
307 * @param[out] type2_list Remailer List
308 * @param[out] coordsp On screen coordinates
309 * @param[in] chain Chain
310 * @param[in] i Index in chain
311 */
mix_screen_coordinates(struct MuttWindow * win,struct Remailer ** type2_list,struct Coord ** coordsp,struct MixChain * chain,int i)312 static void mix_screen_coordinates(struct MuttWindow *win, struct Remailer **type2_list,
313 struct Coord **coordsp, struct MixChain *chain, int i)
314 {
315 const int wrap_indent = 2;
316
317 if (!chain->cl)
318 return;
319
320 short c, r;
321
322 mutt_mem_realloc(coordsp, sizeof(struct Coord) * chain->cl);
323
324 struct Coord *coords = *coordsp;
325
326 if (i == 0)
327 {
328 r = 0;
329 c = 0;
330 }
331 else
332 {
333 c = coords[i - 1].c + strlen(type2_list[chain->ch[i - 1]]->shortname) + 2;
334 r = coords[i - 1].r;
335 }
336
337 for (; i < chain->cl; i++)
338 {
339 short oc = c;
340 c += strlen(type2_list[chain->ch[i]]->shortname) + 2;
341
342 if (c >= win->state.cols)
343 {
344 oc = wrap_indent;
345 c = wrap_indent;
346 r++;
347 }
348
349 coords[i].c = oc;
350 coords[i].r = r;
351 }
352 }
353
354 /**
355 * mix_redraw_ce - Redraw the Remailer chain
356 * @param[in] win Window
357 * @param[out] type2_list Remailer List
358 * @param[in] coords Screen Coordinates
359 * @param[in] chain Chain
360 * @param[in] i Index in chain
361 * @param[in] selected true, if this item is selected
362 */
mix_redraw_ce(struct MuttWindow * win,struct Remailer ** type2_list,struct Coord * coords,struct MixChain * chain,int i,bool selected)363 static void mix_redraw_ce(struct MuttWindow *win, struct Remailer **type2_list,
364 struct Coord *coords, struct MixChain *chain, int i, bool selected)
365 {
366 if (!coords || !chain)
367 return;
368
369 if (coords[i].r < win->state.rows)
370 {
371 if (selected)
372 mutt_curses_set_color_by_id(MT_COLOR_INDICATOR);
373 else
374 mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
375
376 mutt_window_mvaddstr(win, coords[i].c, coords[i].r, type2_list[chain->ch[i]]->shortname);
377 mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
378
379 if (i + 1 < chain->cl)
380 mutt_window_addstr(win, ", ");
381 }
382 }
383
384 /**
385 * mix_redraw_chain - Redraw the chain on screen
386 * @param[in] win Window
387 * @param[out] type2_list Remailer List
388 * @param[in] coords Where to draw the list on screen
389 * @param[in] chain Chain to display
390 * @param[in] cur Chain index of current selection
391 */
mix_redraw_chain(struct MuttWindow * win,struct Remailer ** type2_list,struct Coord * coords,struct MixChain * chain,int cur)392 static void mix_redraw_chain(struct MuttWindow *win, struct Remailer **type2_list,
393 struct Coord *coords, struct MixChain *chain, int cur)
394 {
395 for (int i = 0; i < win->state.rows; i++)
396 {
397 mutt_window_move(win, 0, i);
398 mutt_window_clrtoeol(win);
399 }
400
401 for (int i = 0; i < chain->cl; i++)
402 mix_redraw_ce(win, type2_list, coords, chain, i, i == cur);
403 }
404
405 /**
406 * mix_redraw_head - Redraw the Chain info
407 * @param win Window
408 * @param chain Chain
409 */
mix_redraw_head(struct MuttWindow * win,struct MixChain * chain)410 static void mix_redraw_head(struct MuttWindow *win, struct MixChain *chain)
411 {
412 char buf[1024];
413 snprintf(buf, sizeof(buf), "-- Remailer chain [Length: %ld]", chain ? chain->cl : 0);
414 sbar_set_title(win, buf);
415 }
416
417 /**
418 * mix_format_caps - Turn flags into a MixMaster capability string
419 * @param r Remailer to use
420 * @retval ptr Capability string
421 *
422 * @note The string is a static buffer
423 */
mix_format_caps(struct Remailer * r)424 static const char *mix_format_caps(struct Remailer *r)
425 {
426 static char capbuf[10];
427 char *t = capbuf;
428
429 if (r->caps & MIX_CAP_COMPRESS)
430 *t++ = 'C';
431 else
432 *t++ = ' ';
433
434 if (r->caps & MIX_CAP_MIDDLEMAN)
435 *t++ = 'M';
436 else
437 *t++ = ' ';
438
439 if (r->caps & MIX_CAP_NEWSPOST)
440 {
441 *t++ = 'N';
442 *t++ = 'p';
443 }
444 else
445 {
446 *t++ = ' ';
447 *t++ = ' ';
448 }
449
450 if (r->caps & MIX_CAP_NEWSMAIL)
451 {
452 *t++ = 'N';
453 *t++ = 'm';
454 }
455 else
456 {
457 *t++ = ' ';
458 *t++ = ' ';
459 }
460
461 *t = '\0';
462
463 return capbuf;
464 }
465
466 /**
467 * mix_format_str - Format a string for the remailer menu - Implements ::format_t - @ingroup expando_api
468 *
469 * | Expando | Description
470 * |:--------|:--------------------------------------------------------
471 * | \%a | The remailer's e-mail address
472 * | \%c | Remailer capabilities
473 * | \%n | The running number on the menu
474 * | \%s | The remailer's short name
475 */
mix_format_str(char * buf,size_t buflen,size_t col,int cols,char op,const char * src,const char * prec,const char * if_str,const char * else_str,intptr_t data,MuttFormatFlags flags)476 static const char *mix_format_str(char *buf, size_t buflen, size_t col, int cols,
477 char op, const char *src, const char *prec,
478 const char *if_str, const char *else_str,
479 intptr_t data, MuttFormatFlags flags)
480 {
481 char fmt[128];
482 struct Remailer *remailer = (struct Remailer *) data;
483 bool optional = (flags & MUTT_FORMAT_OPTIONAL);
484
485 switch (op)
486 {
487 case 'a':
488 if (!optional)
489 {
490 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
491 snprintf(buf, buflen, fmt, NONULL(remailer->addr));
492 }
493 else if (!remailer->addr)
494 optional = false;
495 break;
496
497 case 'c':
498 if (optional)
499 break;
500
501 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
502 snprintf(buf, buflen, fmt, mix_format_caps(remailer));
503 break;
504
505 case 'n':
506 if (optional)
507 break;
508
509 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
510 snprintf(buf, buflen, fmt, remailer->num);
511 break;
512
513 case 's':
514 if (!optional)
515 {
516 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
517 snprintf(buf, buflen, fmt, NONULL(remailer->shortname));
518 }
519 else if (!remailer->shortname)
520 optional = false;
521 break;
522
523 default:
524 *buf = '\0';
525 }
526
527 if (optional)
528 {
529 mutt_expando_format(buf, buflen, col, cols, if_str, mix_format_str, data,
530 MUTT_FORMAT_NO_FLAGS);
531 }
532 else if (flags & MUTT_FORMAT_OPTIONAL)
533 {
534 mutt_expando_format(buf, buflen, col, cols, else_str, mix_format_str, data,
535 MUTT_FORMAT_NO_FLAGS);
536 }
537
538 /* We return the format string, unchanged */
539 return src;
540 }
541
542 /**
543 * mix_make_entry - Format a menu item for the mixmaster chain list - Implements Menu::make_entry() - @ingroup menu_make_entry
544 */
mix_make_entry(struct Menu * menu,char * buf,size_t buflen,int num)545 static void mix_make_entry(struct Menu *menu, char *buf, size_t buflen, int num)
546 {
547 struct Remailer **type2_list = menu->mdata;
548 const char *const c_mix_entry_format =
549 cs_subset_string(NeoMutt->sub, "mix_entry_format");
550 mutt_expando_format(buf, buflen, 0, menu->win->state.cols,
551 NONULL(c_mix_entry_format), mix_format_str,
552 (intptr_t) type2_list[num], MUTT_FORMAT_ARROWCURSOR);
553 }
554
555 /**
556 * mix_chain_add - Add a host to the chain
557 * @param[in] chain Chain to add to
558 * @param[in] s Hostname
559 * @param[out] type2_list Remailer List
560 * @retval 0 Success
561 * @retval -1 Error
562 */
mix_chain_add(struct MixChain * chain,const char * s,struct Remailer ** type2_list)563 static int mix_chain_add(struct MixChain *chain, const char *s, struct Remailer **type2_list)
564 {
565 int i;
566
567 if (chain->cl >= MAX_MIXES)
568 return -1;
569
570 if (mutt_str_equal(s, "0") || mutt_istr_equal(s, "<random>"))
571 {
572 chain->ch[chain->cl++] = 0;
573 return 0;
574 }
575
576 for (i = 0; type2_list[i]; i++)
577 {
578 if (mutt_istr_equal(s, type2_list[i]->shortname))
579 {
580 chain->ch[chain->cl++] = i;
581 return 0;
582 }
583 }
584
585 /* replace unknown remailers by <random> */
586
587 if (!type2_list[i])
588 chain->ch[chain->cl++] = 0;
589
590 return 0;
591 }
592
593 /**
594 * remailer_config_observer - Notification that a Config Variable has changed - Implements ::observer_t - @ingroup observer_api
595 */
remailer_config_observer(struct NotifyCallback * nc)596 static int remailer_config_observer(struct NotifyCallback *nc)
597 {
598 if ((nc->event_type != NT_CONFIG) || !nc->global_data || !nc->event_data)
599 return -1;
600
601 struct EventConfig *ev_c = nc->event_data;
602 if (!mutt_str_equal(ev_c->name, "status_on_top"))
603 return 0;
604
605 struct MuttWindow *dlg = nc->global_data;
606 window_status_on_top(dlg, NeoMutt->sub);
607 mutt_debug(LL_DEBUG5, "config done\n");
608 return 0;
609 }
610
611 /**
612 * remailer_window_observer - Notification that a Window has changed - Implements ::observer_t - @ingroup observer_api
613 */
remailer_window_observer(struct NotifyCallback * nc)614 static int remailer_window_observer(struct NotifyCallback *nc)
615 {
616 if ((nc->event_type != NT_WINDOW) || !nc->global_data || !nc->event_data)
617 return -1;
618
619 if (nc->event_subtype != NT_WINDOW_DELETE)
620 return 0;
621
622 struct MuttWindow *dlg = nc->global_data;
623 struct EventWindow *ev_w = nc->event_data;
624 if (ev_w->win != dlg)
625 return 0;
626
627 notify_observer_remove(NeoMutt->notify, remailer_config_observer, dlg);
628 notify_observer_remove(dlg->notify, remailer_window_observer, dlg);
629 mutt_debug(LL_DEBUG5, "window delete done\n");
630
631 return 0;
632 }
633
634 /**
635 * dlg_select_mixmaster_chain - Create a Mixmaster chain
636 * @param chainhead List of chain links
637 *
638 * Ask the user to select Mixmaster hosts to create a chain.
639 */
dlg_select_mixmaster_chain(struct ListHead * chainhead)640 void dlg_select_mixmaster_chain(struct ListHead *chainhead)
641 {
642 int c_cur = 0, c_old = 0;
643 bool c_redraw = true;
644 size_t ttll = 0;
645
646 struct Coord *coords = NULL;
647
648 struct Menu *menu = NULL;
649 bool loop = true;
650
651 char *t = NULL;
652
653 struct Remailer **type2_list = mix_type2_list(&ttll);
654 if (!type2_list)
655 {
656 mutt_error(_("Can't get mixmaster's type2.list"));
657 return;
658 }
659
660 struct MixChain *chain = mutt_mem_calloc(1, sizeof(struct MixChain));
661
662 struct ListNode *p = NULL;
663 STAILQ_FOREACH(p, chainhead, entries)
664 {
665 mix_chain_add(chain, p->data, type2_list);
666 }
667 mutt_list_free(chainhead);
668
669 /* safety check */
670 for (int i = 0; i < chain->cl; i++)
671 {
672 if (chain->ch[i] >= ttll)
673 chain->ch[i] = 0;
674 }
675
676 struct MuttWindow *dlg =
677 mutt_window_new(WT_DLG_REMAILER, MUTT_WIN_ORIENT_VERTICAL, MUTT_WIN_SIZE_MAXIMISE,
678 MUTT_WIN_SIZE_UNLIMITED, MUTT_WIN_SIZE_UNLIMITED);
679 dlg->help_menu = MENU_MIX;
680 dlg->help_data = RemailerHelp;
681
682 struct MuttWindow *win_hosts = menu_new_window(MENU_MIX, NeoMutt->sub);
683 win_hosts->focus = win_hosts;
684
685 struct MuttWindow *win_chain =
686 mutt_window_new(WT_CUSTOM, MUTT_WIN_ORIENT_VERTICAL, MUTT_WIN_SIZE_FIXED,
687 MUTT_WIN_SIZE_UNLIMITED, 4);
688
689 struct MuttWindow *win_cbar = sbar_new();
690 struct MuttWindow *win_rbar = sbar_new();
691
692 const bool c_status_on_top = cs_subset_bool(NeoMutt->sub, "status_on_top");
693 if (c_status_on_top)
694 {
695 mutt_window_add_child(dlg, win_rbar);
696 mutt_window_add_child(dlg, win_hosts);
697 mutt_window_add_child(dlg, win_cbar);
698 mutt_window_add_child(dlg, win_chain);
699 }
700 else
701 {
702 mutt_window_add_child(dlg, win_hosts);
703 mutt_window_add_child(dlg, win_cbar);
704 mutt_window_add_child(dlg, win_chain);
705 mutt_window_add_child(dlg, win_rbar);
706 }
707 sbar_set_title(win_rbar, _("Select a remailer chain"));
708
709 mix_screen_coordinates(dlg, type2_list, &coords, chain, 0);
710
711 menu = win_hosts->wdata;
712 menu->max = ttll;
713 menu->make_entry = mix_make_entry;
714 menu->tag = NULL;
715 menu->mdata = type2_list;
716
717 notify_observer_add(NeoMutt->notify, NT_CONFIG, remailer_config_observer, dlg);
718 notify_observer_add(dlg->notify, NT_WINDOW, remailer_window_observer, dlg);
719 dialog_push(dlg);
720
721 while (loop)
722 {
723 if (c_redraw)
724 {
725 mix_redraw_head(win_cbar, chain);
726 mix_redraw_chain(win_chain, type2_list, coords, chain, c_cur);
727 c_redraw = false;
728 }
729 else if (c_cur != c_old)
730 {
731 mix_redraw_ce(win_chain, type2_list, coords, chain, c_old, false);
732 mix_redraw_ce(win_chain, type2_list, coords, chain, c_cur, true);
733 }
734
735 c_old = c_cur;
736
737 window_redraw(dlg);
738 const int op = menu_loop(menu);
739 switch (op)
740 {
741 case OP_REDRAW:
742 {
743 mix_redraw_head(win_cbar, chain);
744 mix_screen_coordinates(menu->win, type2_list, &coords, chain, 0);
745 mix_redraw_chain(win_chain, type2_list, coords, chain, c_cur);
746 break;
747 }
748
749 case OP_EXIT:
750 {
751 chain->cl = 0;
752 loop = false;
753 break;
754 }
755
756 case OP_MIX_USE:
757 {
758 if (!chain->cl)
759 {
760 chain->cl++;
761 chain->ch[0] = menu_get_index(menu);
762 mix_screen_coordinates(menu->win, type2_list, &coords, chain, c_cur);
763 c_redraw = true;
764 }
765
766 if (chain->cl && chain->ch[chain->cl - 1] &&
767 (type2_list[chain->ch[chain->cl - 1]]->caps & MIX_CAP_MIDDLEMAN))
768 {
769 mutt_error(
770 _("Error: %s can't be used as the final remailer of a chain"),
771 type2_list[chain->ch[chain->cl - 1]]->shortname);
772 }
773 else
774 {
775 loop = false;
776 }
777 break;
778 }
779
780 case OP_GENERIC_SELECT_ENTRY:
781 case OP_MIX_APPEND:
782 {
783 if ((chain->cl < MAX_MIXES) && (c_cur < chain->cl))
784 c_cur++;
785 }
786 /* fallthrough */
787 case OP_MIX_INSERT:
788 {
789 if (chain->cl < MAX_MIXES)
790 {
791 chain->cl++;
792 for (int i = chain->cl - 1; i > c_cur; i--)
793 chain->ch[i] = chain->ch[i - 1];
794
795 chain->ch[c_cur] = menu_get_index(menu);
796 mix_screen_coordinates(menu->win, type2_list, &coords, chain, c_cur);
797 c_redraw = true;
798 }
799 else
800 {
801 /* L10N The '%d' here hard-coded to 19 */
802 mutt_error(_("Mixmaster chains are limited to %d elements"), MAX_MIXES);
803 }
804
805 break;
806 }
807
808 case OP_MIX_DELETE:
809 {
810 if (chain->cl)
811 {
812 chain->cl--;
813
814 for (int i = c_cur; i < chain->cl; i++)
815 chain->ch[i] = chain->ch[i + 1];
816
817 if ((c_cur == chain->cl) && c_cur)
818 c_cur--;
819
820 mix_screen_coordinates(menu->win, type2_list, &coords, chain, c_cur);
821 c_redraw = true;
822 }
823 else
824 {
825 mutt_error(_("The remailer chain is already empty"));
826 }
827 break;
828 }
829
830 case OP_MIX_CHAIN_PREV:
831 {
832 if (c_cur)
833 c_cur--;
834 else
835 mutt_error(_("You already have the first chain element selected"));
836
837 break;
838 }
839
840 case OP_MIX_CHAIN_NEXT:
841 {
842 if (chain->cl && (c_cur < chain->cl - 1))
843 c_cur++;
844 else
845 mutt_error(_("You already have the last chain element selected"));
846
847 break;
848 }
849 }
850 }
851
852 dialog_pop();
853 mutt_window_free(&dlg);
854
855 /* construct the remailer list */
856
857 if (chain->cl)
858 {
859 for (int i = 0; i < chain->cl; i++)
860 {
861 const int j = chain->ch[i];
862 if (j != 0)
863 t = type2_list[j]->shortname;
864 else
865 t = "*";
866
867 mutt_list_insert_tail(chainhead, mutt_str_dup(t));
868 }
869 }
870
871 mix_type2_list_free(&type2_list);
872 FREE(&coords);
873 FREE(&chain);
874 }
875
876 /**
877 * mix_check_message - Safety-check the message before passing it to mixmaster
878 * @param e Email
879 * @retval 0 Success
880 * @retval -1 Error
881 */
mix_check_message(struct Email * e)882 int mix_check_message(struct Email *e)
883 {
884 bool need_hostname = false;
885
886 if (!TAILQ_EMPTY(&e->env->cc) || !TAILQ_EMPTY(&e->env->bcc))
887 {
888 mutt_error(_("Mixmaster doesn't accept Cc or Bcc headers"));
889 return -1;
890 }
891
892 /* When using mixmaster, we MUST qualify any addresses since
893 * the message will be delivered through remote systems.
894 *
895 * use_domain won't be respected at this point, hidden_host will.
896 */
897
898 struct Address *a = NULL;
899 TAILQ_FOREACH(a, &e->env->to, entries)
900 {
901 if (!a->group && !strchr(a->mailbox, '@'))
902 {
903 need_hostname = true;
904 break;
905 }
906 }
907
908 if (need_hostname)
909 {
910 const char *fqdn = mutt_fqdn(true, NeoMutt->sub);
911 if (!fqdn)
912 {
913 mutt_error(_("Please set the hostname variable to a proper value when "
914 "using mixmaster"));
915 return -1;
916 }
917
918 /* Cc and Bcc are empty at this point. */
919 mutt_addrlist_qualify(&e->env->to, fqdn);
920 mutt_addrlist_qualify(&e->env->reply_to, fqdn);
921 mutt_addrlist_qualify(&e->env->mail_followup_to, fqdn);
922 }
923
924 return 0;
925 }
926
927 /**
928 * mix_send_message - Send an email via Mixmaster
929 * @param chain String list of hosts
930 * @param tempfile Temporary file containing email
931 * @retval -1 Error
932 * @retval >=0 Success (Mixmaster's return code)
933 */
mix_send_message(struct ListHead * chain,const char * tempfile)934 int mix_send_message(struct ListHead *chain, const char *tempfile)
935 {
936 int i = 0;
937 struct Buffer *cmd = mutt_buffer_pool_get();
938 struct Buffer *cd_quoted = mutt_buffer_pool_get();
939
940 const char *const c_mixmaster = cs_subset_string(NeoMutt->sub, "mixmaster");
941 mutt_buffer_printf(cmd, "cat %s | %s -m ", tempfile, c_mixmaster);
942
943 struct ListNode *np = NULL;
944 STAILQ_FOREACH(np, chain, entries)
945 {
946 mutt_buffer_addstr(cmd, (i != 0) ? "," : " -l ");
947 mutt_buffer_quote_filename(cd_quoted, (char *) np->data, true);
948 mutt_buffer_addstr(cmd, mutt_buffer_string(cd_quoted));
949 i = 1;
950 }
951
952 mutt_endwin();
953
954 i = mutt_system(cmd->data);
955 if (i != 0)
956 {
957 fprintf(stderr, _("Error sending message, child exited %d\n"), i);
958 if (!OptNoCurses)
959 {
960 mutt_any_key_to_continue(NULL);
961 mutt_error(_("Error sending message"));
962 }
963 }
964
965 mutt_buffer_pool_release(&cmd);
966 mutt_buffer_pool_release(&cd_quoted);
967 unlink(tempfile);
968 return i;
969 }
970