mutt stable branch with some hacks
at master 779 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 char cmd[HUGE_STRING + _POSIX_PATH_MAX]; 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 snprintf (cmd, sizeof (cmd), "%s -T", Mixmaster); 154 155 if ((mm_pid = mutt_create_filter_fd (cmd, NULL, &fp, NULL, devnull, -1, devnull)) == -1) 156 { 157 close (devnull); 158 return NULL; 159 } 160 161 /* first, generate the "random" remailer */ 162 163 p = mix_new_remailer (); 164 p->shortname = safe_strdup ("<random>"); 165 mix_add_entry (&type2_list, p, &slots, &used); 166 167 while (fgets (line, sizeof (line), fp)) 168 { 169 p = mix_new_remailer (); 170 171 if (!(t = strtok (line, " \t\n"))) 172 goto problem; 173 174 p->shortname = safe_strdup (t); 175 176 if (!(t = strtok (NULL, " \t\n"))) 177 goto problem; 178 179 p->addr = safe_strdup (t); 180 181 if (!(t = strtok (NULL, " \t\n"))) 182 goto problem; 183 184 if (!(t = strtok (NULL, " \t\n"))) 185 goto problem; 186 187 p->ver = safe_strdup (t); 188 189 if (!(t = strtok (NULL, " \t\n"))) 190 goto problem; 191 192 p->caps = mix_get_caps (t); 193 194 mix_add_entry (&type2_list, p, &slots, &used); 195 continue; 196 197 problem: 198 mix_free_remailer (&p); 199 } 200 201 *l = used; 202 203 mix_add_entry (&type2_list, NULL, &slots, &used); 204 mutt_wait_filter (mm_pid); 205 206 close (devnull); 207 208 return type2_list; 209} 210 211static void mix_free_type2_list (REMAILER ***ttlp) 212{ 213 int i; 214 REMAILER **type2_list = *ttlp; 215 216 for (i = 0; type2_list[i]; i++) 217 mix_free_remailer (&type2_list[i]); 218 219 FREE (type2_list); /* __FREE_CHECKED__ */ 220} 221 222 223#define MIX_HOFFSET 2 224#define MIX_VOFFSET (MuttIndexWindow->rows - 4) 225#define MIX_MAXROW (MuttIndexWindow->rows - 1) 226 227 228static void mix_screen_coordinates (REMAILER **type2_list, 229 struct coord **coordsp, 230 MIXCHAIN *chain, 231 int i) 232{ 233 short c, r, oc; 234 struct coord *coords; 235 236 if (!chain->cl) 237 return; 238 239 safe_realloc (coordsp, sizeof (struct coord) * chain->cl); 240 241 coords = *coordsp; 242 243 if (i) 244 { 245 c = coords[i-1].c + strlen (type2_list[chain->ch[i-1]]->shortname) + 2; 246 r = coords[i-1].r; 247 } 248 else 249 { 250 r = MIX_VOFFSET; 251 c = MIX_HOFFSET; 252 } 253 254 255 for (; i < chain->cl; i++) 256 { 257 oc = c; 258 c += strlen (type2_list[chain->ch[i]]->shortname) + 2; 259 260 if (c >= MuttIndexWindow->cols) 261 { 262 oc = c = MIX_HOFFSET; 263 r++; 264 } 265 266 coords[i].c = oc; 267 coords[i].r = r; 268 269 } 270 271} 272 273static void mix_redraw_ce (REMAILER **type2_list, 274 struct coord *coords, 275 MIXCHAIN *chain, 276 int i, 277 short selected) 278{ 279 if (!coords || !chain) 280 return; 281 282 if (coords[i].r < MIX_MAXROW) 283 { 284 285 if (selected) 286 SETCOLOR (MT_COLOR_INDICATOR); 287 else 288 NORMAL_COLOR; 289 290 mutt_window_mvaddstr (MuttIndexWindow, coords[i].r, coords[i].c, 291 type2_list[chain->ch[i]]->shortname); 292 NORMAL_COLOR; 293 294 if (i + 1 < chain->cl) 295 addstr (", "); 296 } 297} 298 299static void mix_redraw_chain (REMAILER **type2_list, 300 struct coord *coords, 301 MIXCHAIN *chain, 302 int cur) 303{ 304 int i; 305 306 for (i = MIX_VOFFSET; i < MIX_MAXROW; i++) 307 { 308 mutt_window_move (MuttIndexWindow, i, 0); 309 mutt_window_clrtoeol (MuttIndexWindow); 310 } 311 312 for (i = 0; i < chain->cl; i++) 313 mix_redraw_ce (type2_list, coords, chain, i, i == cur); 314} 315 316static void mix_redraw_head (MIXCHAIN *chain) 317{ 318 SETCOLOR (MT_COLOR_STATUS); 319 mutt_window_mvprintw (MuttIndexWindow, MIX_VOFFSET - 1, 0, 320 "-- Remailer chain [Length: %d]", chain ? chain->cl : 0); 321 mutt_window_clrtoeol (MuttIndexWindow); 322 NORMAL_COLOR; 323} 324 325static const char *mix_format_caps (REMAILER *r) 326{ 327 static char capbuff[10]; 328 char *t = capbuff; 329 330 if (r->caps & MIX_CAP_COMPRESS) 331 *t++ = 'C'; 332 else 333 *t++ = ' '; 334 335 if (r->caps & MIX_CAP_MIDDLEMAN) 336 *t++ = 'M'; 337 else 338 *t++ = ' '; 339 340 if (r->caps & MIX_CAP_NEWSPOST) 341 { 342 *t++ = 'N'; 343 *t++ = 'p'; 344 } 345 else 346 { 347 *t++ = ' '; 348 *t++ = ' '; 349 } 350 351 if (r->caps & MIX_CAP_NEWSMAIL) 352 { 353 *t++ = 'N'; 354 *t++ = 'm'; 355 } 356 else 357 { 358 *t++ = ' '; 359 *t++ = ' '; 360 } 361 362 *t = '\0'; 363 364 return capbuff; 365} 366 367/* 368 * Format an entry for the remailer menu. 369 * 370 * %n number 371 * %c capabilities 372 * %s short name 373 * %a address 374 * 375 */ 376 377static const char *mix_entry_fmt (char *dest, 378 size_t destlen, 379 size_t col, 380 int cols, 381 char op, 382 const char *src, 383 const char *prefix, 384 const char *ifstring, 385 const char *elsestring, 386 unsigned long data, 387 format_flag flags) 388{ 389 char fmt[16]; 390 REMAILER *remailer = (REMAILER *) data; 391 int optional = (flags & MUTT_FORMAT_OPTIONAL); 392 393 switch (op) 394 { 395 case 'n': 396 if (!optional) 397 { 398 snprintf (fmt, sizeof (fmt), "%%%sd", prefix); 399 snprintf (dest, destlen, fmt, remailer->num); 400 } 401 break; 402 case 'c': 403 if (!optional) 404 { 405 snprintf (fmt, sizeof (fmt), "%%%ss", prefix); 406 snprintf (dest, destlen, fmt, mix_format_caps(remailer)); 407 } 408 break; 409 case 's': 410 if (!optional) 411 { 412 snprintf (fmt, sizeof (fmt), "%%%ss", prefix); 413 snprintf (dest, destlen, fmt, NONULL(remailer->shortname)); 414 } 415 else if (!remailer->shortname) 416 optional = 0; 417 break; 418 case 'a': 419 if (!optional) 420 { 421 snprintf (fmt, sizeof (fmt), "%%%ss", prefix); 422 snprintf (dest, destlen, fmt, NONULL(remailer->addr)); 423 } 424 else if (!remailer->addr) 425 optional = 0; 426 break; 427 428 default: 429 *dest = '\0'; 430 } 431 432 if (optional) 433 mutt_FormatString (dest, destlen, col, cols, ifstring, mutt_attach_fmt, data, 0); 434 else if (flags & MUTT_FORMAT_OPTIONAL) 435 mutt_FormatString (dest, destlen, col, cols, elsestring, mutt_attach_fmt, data, 0); 436 return (src); 437} 438 439 440 441static void mix_entry (char *b, size_t blen, MUTTMENU *menu, int num) 442{ 443 REMAILER **type2_list = (REMAILER **) menu->data; 444 mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (MixEntryFormat), mix_entry_fmt, 445 (unsigned long) type2_list[num], MUTT_FORMAT_ARROWCURSOR); 446} 447 448static int mix_chain_add (MIXCHAIN *chain, const char *s, 449 REMAILER **type2_list) 450{ 451 int i; 452 453 if (chain->cl >= MAXMIXES) 454 return -1; 455 456 if (!mutt_strcmp (s, "0") || !ascii_strcasecmp (s, "<random>")) 457 { 458 chain->ch[chain->cl++] = 0; 459 return 0; 460 } 461 462 for (i = 0; type2_list[i]; i++) 463 { 464 if (!ascii_strcasecmp (s, type2_list[i]->shortname)) 465 { 466 chain->ch[chain->cl++] = i; 467 return 0; 468 } 469 } 470 471 /* replace unknown remailers by <random> */ 472 473 if (!type2_list[i]) 474 chain->ch[chain->cl++] = 0; 475 476 return 0; 477} 478 479static const struct mapping_t RemailerHelp[] = 480{ 481 { N_("Append"), OP_MIX_APPEND }, 482 { N_("Insert"), OP_MIX_INSERT }, 483 { N_("Delete"), OP_MIX_DELETE }, 484 { N_("Abort"), OP_EXIT }, 485 { N_("OK"), OP_MIX_USE }, 486 { NULL, 0 } 487}; 488 489 490void mix_make_chain (LIST **chainp, int *redraw) 491{ 492 LIST *p; 493 MIXCHAIN *chain; 494 int c_cur = 0, c_old = 0; 495 short c_redraw = 1; 496 497 REMAILER **type2_list = NULL; 498 size_t ttll = 0; 499 500 struct coord *coords = NULL; 501 502 MUTTMENU *menu; 503 char helpstr[LONG_STRING]; 504 short loop = 1; 505 int op; 506 507 int i, j; 508 char *t; 509 510 if (!(type2_list = mix_type2_list (&ttll))) 511 { 512 mutt_error _("Can't get mixmaster's type2.list!"); 513 return; 514 } 515 516 *redraw = REDRAW_FULL; 517 518 chain = safe_calloc (sizeof (MIXCHAIN), 1); 519 for (p = *chainp; p; p = p->next) 520 mix_chain_add (chain, (char *) p->data, type2_list); 521 522 mutt_free_list (chainp); 523 524 /* safety check */ 525 for (i = 0; i < chain->cl; i++) 526 { 527 if (chain->ch[i] >= ttll) 528 chain->ch[i] = 0; 529 } 530 531 mix_screen_coordinates (type2_list, &coords, chain, 0); 532 533 menu = mutt_new_menu (MENU_MIX); 534 menu->max = ttll; 535 menu->make_entry = mix_entry; 536 menu->tag = NULL; 537 menu->title = _("Select a remailer chain."); 538 menu->data = type2_list; 539 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MIX, RemailerHelp); 540 menu->pagelen = MIX_VOFFSET - 1; 541 542 while (loop) 543 { 544 if (menu->pagelen != MIX_VOFFSET - 1) 545 { 546 menu->pagelen = MIX_VOFFSET - 1; 547 menu->redraw = REDRAW_FULL; 548 } 549 550 if (c_redraw) 551 { 552 mix_redraw_head (chain); 553 mix_redraw_chain (type2_list, coords, chain, c_cur); 554 c_redraw = 0; 555 } 556 else if (c_cur != c_old) 557 { 558 mix_redraw_ce (type2_list, coords, chain, c_old, 0); 559 mix_redraw_ce (type2_list, coords, chain, c_cur, 1); 560 } 561 562 c_old = c_cur; 563 564 switch ((op = mutt_menuLoop (menu))) 565 { 566 case OP_REDRAW: 567 { 568 menu_redraw_status (menu); 569 mix_redraw_head (chain); 570 mix_screen_coordinates (type2_list, &coords, chain, 0); 571 mix_redraw_chain (type2_list, coords, chain, c_cur); 572 menu->pagelen = MIX_VOFFSET - 1; 573 break; 574 } 575 576 case OP_EXIT: 577 { 578 chain->cl = 0; 579 loop = 0; 580 break; 581 } 582 583 case OP_MIX_USE: 584 { 585 if (!chain->cl) 586 { 587 chain->cl++; 588 chain->ch[0] = menu->current; 589 mix_screen_coordinates (type2_list, &coords, chain, c_cur); 590 c_redraw = 1; 591 } 592 593 if (chain->cl && chain->ch[chain->cl - 1] && 594 (type2_list[chain->ch[chain->cl-1]]->caps & MIX_CAP_MIDDLEMAN)) 595 { 596 mutt_error ( _("Error: %s can't be used as the final remailer of a chain."), 597 type2_list[chain->ch[chain->cl - 1]]->shortname); 598 } 599 else 600 { 601 loop = 0; 602 } 603 break; 604 } 605 606 case OP_GENERIC_SELECT_ENTRY: 607 case OP_MIX_APPEND: 608 { 609 if (chain->cl < MAXMIXES && c_cur < chain->cl) 610 c_cur++; 611 } 612 /* fallthrough */ 613 case OP_MIX_INSERT: 614 { 615 if (chain->cl < MAXMIXES) 616 { 617 chain->cl++; 618 for (i = chain->cl - 1; i > c_cur; i--) 619 chain->ch[i] = chain->ch[i-1]; 620 621 chain->ch[c_cur] = menu->current; 622 mix_screen_coordinates (type2_list, &coords, chain, c_cur); 623 c_redraw = 1; 624 } 625 else 626 mutt_error ( _("Mixmaster chains are limited to %d elements."), 627 MAXMIXES); 628 629 break; 630 } 631 632 case OP_MIX_DELETE: 633 { 634 if (chain->cl) 635 { 636 chain->cl--; 637 638 for (i = c_cur; i < chain->cl; i++) 639 chain->ch[i] = chain->ch[i+1]; 640 641 if (c_cur == chain->cl && c_cur) 642 c_cur--; 643 644 mix_screen_coordinates (type2_list, &coords, chain, c_cur); 645 c_redraw = 1; 646 } 647 else 648 { 649 mutt_error _("The remailer chain is already empty."); 650 } 651 break; 652 } 653 654 case OP_MIX_CHAIN_PREV: 655 { 656 if (c_cur) 657 c_cur--; 658 else 659 mutt_error _("You already have the first chain element selected."); 660 661 break; 662 } 663 664 case OP_MIX_CHAIN_NEXT: 665 { 666 if (chain->cl && c_cur < chain->cl - 1) 667 c_cur++; 668 else 669 mutt_error _("You already have the last chain element selected."); 670 671 break; 672 } 673 } 674 } 675 676 mutt_menuDestroy (&menu); 677 678 /* construct the remailer list */ 679 680 if (chain->cl) 681 { 682 for (i = 0; i < chain->cl; i++) 683 { 684 if ((j = chain->ch[i])) 685 t = type2_list[j]->shortname; 686 else 687 t = "*"; 688 689 *chainp = mutt_add_list (*chainp, t); 690 } 691 } 692 693 mix_free_type2_list (&type2_list); 694 FREE (&coords); 695 FREE (&chain); 696} 697 698/* some safety checks before piping the message to mixmaster */ 699 700int mix_check_message (HEADER *msg) 701{ 702 const char *fqdn; 703 short need_hostname = 0; 704 ADDRESS *p; 705 706 if (msg->env->cc || msg->env->bcc) 707 { 708 mutt_error _("Mixmaster doesn't accept Cc or Bcc headers."); 709 return -1; 710 } 711 712 /* When using mixmaster, we MUST qualify any addresses since 713 * the message will be delivered through remote systems. 714 * 715 * use_domain won't be respected at this point, hidden_host will. 716 */ 717 718 for (p = msg->env->to; p; p = p->next) 719 { 720 if (!p->group && strchr (p->mailbox, '@') == NULL) 721 { 722 need_hostname = 1; 723 break; 724 } 725 } 726 727 if (need_hostname) 728 { 729 730 if (!(fqdn = mutt_fqdn (1))) 731 { 732 mutt_error _("Please set the hostname variable to a proper value when using mixmaster!"); 733 return (-1); 734 } 735 736 /* Cc and Bcc are empty at this point. */ 737 rfc822_qualify (msg->env->to, fqdn); 738 rfc822_qualify (msg->env->reply_to, fqdn); 739 rfc822_qualify (msg->env->mail_followup_to, fqdn); 740 } 741 742 return 0; 743} 744 745int mix_send_message (LIST *chain, const char *tempfile) 746{ 747 char cmd[HUGE_STRING]; 748 char tmp[HUGE_STRING]; 749 char cd_quoted[STRING]; 750 int i; 751 752 snprintf (cmd, sizeof (cmd), "cat %s | %s -m ", tempfile, Mixmaster); 753 754 for (i = 0; chain; chain = chain->next, i = 1) 755 { 756 strfcpy (tmp, cmd, sizeof (tmp)); 757 mutt_quote_filename (cd_quoted, sizeof (cd_quoted), (char *) chain->data); 758 snprintf (cmd, sizeof (cmd), "%s%s%s", tmp, i ? "," : " -l ", cd_quoted); 759 } 760 761 if (!option (OPTNOCURSES)) 762 mutt_endwin (NULL); 763 764 if ((i = mutt_system (cmd))) 765 { 766 fprintf (stderr, _("Error sending message, child exited %d.\n"), i); 767 if (!option (OPTNOCURSES)) 768 { 769 mutt_any_key_to_continue (NULL); 770 mutt_error _("Error sending message."); 771 } 772 } 773 774 unlink (tempfile); 775 return i; 776} 777 778 779#endif