mutt stable branch with some hacks
at jcs 788 lines 16 kB view raw
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