mixmaster

mixmaster 3.0 patched for libressl
git clone git://parazyd.org/mixmaster.git
Log | Files | Refs | README

menu.c (22252B)


      1 /* Mixmaster version 3.0  --  (C) 1999 - 2006 Anonymizer Inc. and others.
      2 
      3    Mixmaster may be redistributed and modified under certain conditions.
      4    This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
      5    ANY KIND, either express or implied. See the file COPYRIGHT for
      6    details.
      7 
      8    Menu-based user interface
      9    $Id: menu.c 934 2006-06-24 13:40:39Z rabbi $ */
     10 
     11 
     12 #include "menu.h"
     13 #include "mix3.h"
     14 #include <string.h>
     15 #include <ctype.h>
     16 #include <stdlib.h>
     17 #include <fcntl.h>
     18 #ifdef POSIX
     19 #include <unistd.h>
     20 #else /* end of POSIX */
     21 #include <io.h>
     22 #endif /* else if not POSIX */
     23 #include <assert.h>
     24 
     25 void menu_folder(char command, char *foldername)
     26 {
     27   mix_init(NULL);
     28   if (foldername)
     29     menu_init();
     30   read_folder(command, foldername, ANON);
     31   menu_exit();
     32 }
     33 
     34 void read_folder(char command, char *foldername, char *nym)
     35 {
     36 #ifdef USE_NCURSES
     37   char path[PATHMAX] = "stdin", path_with_tilde[PATHMAX], l[LINELEN];
     38 #else /* end of USE_NCURSES */
     39   char path[PATHMAX] = "stdin", l[LINELEN];
     40 #endif /* else if not USE_NCURSES */
     41   char *h;
     42   FILE *f;
     43   BUFFER *folder;
     44   BUFFER *line, *field, *content, *name;
     45   BUFFER *index;
     46   BUFFER *mail, *log;
     47   int mailfolder = -1;	/* -1 = unknown, 0 = no mailfolder, 1 = mailfolder */
     48   int num = 0;
     49   long from = -1, subject = -1;
     50   int folder_has_changed;
     51 #ifdef USE_NCURSES
     52   BUFFER *deleted_message;
     53   BUFFER *new_folder;
     54   BUFFER *new_index;
     55   long length;
     56   char sub[LINELEN], str[LINELEN], search[LINELEN] = "";
     57   long p;
     58   int display, range, selected, i, redraw, c, q;
     59 
     60 #endif /* USE_NCURSES */
     61   int ispgp = 0, eof = 0;
     62   folder_has_changed = 0;
     63 
     64   line = buf_new();
     65   field = buf_new();
     66   content = buf_new();
     67   index = buf_new();
     68   mail = buf_new();
     69   name = buf_new();
     70   folder = buf_new();
     71   log = buf_new();
     72 
     73   if (foldername == NULL)
     74     f = stdin;
     75   else {
     76     if (foldername[0] == '~' && (h = getenv("HOME")) != NULL) {
     77       strncpy(path, h, PATHMAX);
     78       strcatn(path, foldername + 1, PATHMAX);
     79     } else
     80       strncpy(path, foldername, PATHMAX);
     81     f = fopen(path, "r");
     82   }
     83   if (f == NULL) {
     84 #ifdef USE_NCURSES
     85     if (foldername)
     86       beep();
     87 #endif /* USE_NCURSES */
     88     mix_status("Can't read %s.\n", path);
     89     goto end;
     90   }
     91   for (;;) {
     92     if (fgets(l, sizeof(l), f) == NULL)
     93       eof = 1;
     94     else if (mailfolder == -1) {
     95       if (strleft(l, "From "))
     96 	mailfolder = 1;
     97       else if (strileft(l, "from:") || strileft(l, "path:")
     98 	  || strileft(l, "xref:") || strileft(l, "return-path"))
     99 	mailfolder = 0;
    100       else
    101 	break;
    102     }
    103     if (eof || (mailfolder && strleft(l, "From ")) ||
    104 	(mailfolder == 0 && from != -1 &&
    105 	 (strileft(l, "path:") ||
    106 	  strileft(l, "xref:") || strileft(l,"return-path")))) {
    107       if (num > 1)
    108 	mix_status("Reading message %d", num);
    109 #ifdef USE_PGP
    110       if (ispgp)
    111 #ifdef NYMSUPPORT
    112 	switch (nym_decrypt(mail, NULL, log)) {
    113 	case 2:
    114 	  from = -1, subject = -1;
    115 	  while (buf_getline(mail, line) == 0) {
    116 	    if (bufileft(line, "from:"))
    117 	      from = folder->length + mail->ptr - line->length - 1;
    118 	    if (bufileft(line, "subject:"))
    119 	      subject = folder->length + mail->ptr - line->length - 1;
    120 	  }
    121 	  folder_has_changed = 1;
    122 	  break;
    123 	case -1:
    124 	  buf_clear(mail);
    125 	  from = -1, subject = -1;
    126 	  continue;
    127 	default:
    128 	  ;
    129 	}
    130 #else
    131 	if (!eof) {
    132 	  buf_clear(mail);
    133 	  from = -1, subject = -1;
    134 	  continue;
    135 	}
    136 #endif /* NYMSUPPORT */
    137 #endif /* USE_PGP */
    138       buf_cat(folder, mail);
    139       buf_clear(mail);
    140       ispgp = 0;
    141       if (num > 0) {
    142 	buf_appendl(index, from);
    143 	buf_appendl(index, subject);
    144       }
    145       if (eof)
    146 	break;
    147 
    148       buf_appendl(index, folder->length);
    149       from = subject = -1;
    150       num++;
    151     }
    152     if (from == -1 && strileft(l, "from:"))
    153       from = folder->length + mail->length;
    154 
    155     if (subject == -1 && strileft(l, "subject:"))
    156       subject = folder->length + mail->length;
    157 
    158     buf_appends(mail, l);
    159     if (strleft(l, begin_pgp))
    160       ispgp = 1;
    161   }
    162 
    163   if (foldername)
    164     fclose(f);
    165   else {
    166     dup2(open("/dev/tty", O_RDWR), fileno(stdin));
    167     menu_init();
    168   }
    169 
    170   mix_status("");
    171   if (folder->length == 0) {
    172 #ifdef USE_NCURSES
    173     clear();
    174     beep();
    175 #endif /* USE_NCURSES */
    176     mix_status("%s is empty.\n", path);
    177     goto end;
    178   }
    179   if (mailfolder == -1) {
    180 #ifdef USE_NCURSES
    181     clear();
    182     beep();
    183 #endif /* USE_NCURSES */
    184     mix_status("%s is not a mail folder.\n", path);
    185     goto end;
    186   }
    187 #ifndef USE_NCURSES
    188   if (command == 0) {
    189     buf_write(folder, stdout);
    190     goto end;
    191   }
    192   if (num > 1) {
    193     mix_status("Folder contains several messages.");
    194     goto end;
    195   }
    196 #endif /* not USE_NCURSES */
    197 
    198   if (num < 2) {
    199     folder->ptr = 0;
    200     mimedecode(folder);
    201 
    202     if (command != 0)
    203       send_message(command, nym, folder);
    204 #ifdef USE_NCURSES
    205     else
    206       read_message(folder, nym);
    207 
    208     clear();
    209 #endif /* USE_NCURSES */
    210     goto end;
    211   }
    212 #ifdef USE_NCURSES
    213   display = selected = 0;
    214   range = LINES - 3;
    215   redraw = 2;
    216 
    217   for (;;) {
    218     if (selected < 0)
    219       selected = 0;
    220     if (selected >= num)
    221       selected = num - 1;
    222 
    223     if (selected < display) {
    224       display = selected - range / 2;
    225       redraw = 2;
    226     }
    227     if (selected >= display + range) {
    228       display = selected - range / 2;
    229       redraw = 2;
    230     }
    231     if (display >= num - 5)
    232       display = num - 5;
    233     if (display < 0)
    234       display = 0;
    235 
    236     if (redraw) {
    237       if (redraw == 2) {
    238 	clear();
    239 	standout();
    240 	mvprintw(0, 0, "Mixmaster %s", VERSION);
    241 	printw("   %.20s reading %.50s", nym, path);
    242 	standend();
    243       }
    244       for (i = display; i < display + range; i++) {
    245 	if (i < num) {
    246 	  index->ptr = 12 * i;
    247 	  p = buf_getl(index);
    248 	  buf_clear(name);
    249 	  folder->ptr = buf_getl(index);
    250 	  if (folder->ptr < 0)
    251 	    folder->ptr = 0;
    252 	  else {
    253 	    buf_getheader(folder, field, line);
    254 	    if (line->length) {
    255 	      decode_header(line);
    256 	      rfc822_name(line, name);
    257 	    }
    258 	  }
    259 	  if (i == selected)
    260 	    standout();
    261 
    262 	  mvaddnstr(i - display + 2, 0, name->data, 18);
    263 
    264 	  sub[0] = '\0';
    265 	  folder->ptr = buf_getl(index);
    266 	  if (folder->ptr < 0)
    267 	    folder->ptr = 0;
    268 	  else {
    269 	    buf_getheader(folder, field, content);
    270 	    if (content->length) {
    271 	      decode_header(content);
    272 	      strncpy(sub, content->data, sizeof(sub));
    273 	    }
    274 	  }
    275 	  if (sub[0] == '\0')
    276 	    strcpy(sub, "(no subject)");
    277 	  mvaddnstr(i - display + 2, 20, sub, COLS - 21);
    278 
    279 	  if (i == selected)
    280 	    standend();
    281 	}
    282       }
    283     }
    284     move(LINES - 1, COLS - 1);
    285     refresh();
    286     redraw = 0;
    287 
    288     c = getch();
    289     switch (c) {
    290     case '\014':
    291       display = selected - range / 2;
    292       redraw = 2;
    293       break;
    294     case 'q':
    295       clear();
    296       goto end;
    297     case '/':
    298       echo();
    299       cl(LINES - 1, 0);
    300       printw("Search: ");
    301       refresh();
    302       wgetnstr(stdscr, str, LINELEN);
    303       if (str[0] != '\0')
    304 	strncpy(search, str, LINELEN);
    305       noecho();
    306       for (i = (selected < num ? selected + 1 : 0); i < num; i++) {
    307 	index->ptr = 12 * i + 4;
    308 	folder->ptr = buf_getl(index);
    309 	if (folder->ptr < 0)
    310 	  folder->ptr = 0;
    311 	else {
    312 	  buf_getheader(folder, field, line);
    313 	  if (line->length) {
    314 	    decode_header(line);
    315 	    if (bufifind(line, search))
    316 	      break;
    317 	  }
    318 	}
    319 	folder->ptr = buf_getl(index);
    320 	if (folder->ptr < 0)
    321 	  folder->ptr = 0;
    322 	else {
    323 	  buf_getheader(folder, field, line);
    324 	  if (line->length) {
    325 	    decode_header(line);
    326 	    if (bufifind(line, search))
    327 	      break;
    328 	  }
    329 	}
    330       }
    331       if (i < num)
    332 	selected = i;
    333       else
    334 	beep();
    335       redraw = 1;
    336       break;
    337     case '\r':			/* read message */
    338     case '\n':
    339     case 'r':			/* reply to message */
    340     case 'g':
    341     case 'f':
    342     case 'm':
    343     case 'p':
    344     case 's':
    345       index->ptr = 12 * selected;
    346       p = buf_getl(index);
    347       if (selected < num - 1) {
    348 	index->ptr = 12 * (selected + 1);
    349 	q = buf_getl(index) - p;
    350       } else
    351 	q = folder->length - p;
    352       buf_clear(mail);
    353       buf_append(mail, folder->data + p, q);
    354       mimedecode(mail);
    355       if(c == 's')
    356 	savemsg(mail);
    357       else{
    358 	if (c == '\r' || c == '\n')
    359 	  read_message(mail, nym);
    360 	else
    361 	  send_message(c, nym, mail);
    362       }
    363       redraw = 2;
    364       break;
    365     case 'd':                   /* delete message */
    366       /* Remove message from folder */
    367       if(num > 0){
    368 	index->ptr = 12 * selected;
    369 	p = buf_getl(index);
    370 	if (selected < num - 1) {
    371 	  index->ptr = 12 * (selected + 1);
    372 	  q = buf_getl(index) - p;
    373 	} else
    374 	  q = folder->length - p;
    375 	deleted_message = buf_new();
    376 	new_folder = buf_new();
    377 	buf_cut_out(folder, deleted_message, new_folder, p, q);
    378 	buf_free(deleted_message);
    379 	buf_free(folder);
    380 	folder = new_folder;
    381 	/* Update index file */
    382 	new_index = buf_new();
    383 	index->ptr = 0;
    384 	if(selected > 0)
    385 	  buf_get(index, new_index, 12 * selected);
    386 	index->ptr = 12 * (selected + 1);
    387 	while((from = buf_getl(index)) != -1){
    388 	  subject = buf_getl(index);
    389 	  length = buf_getl(index);
    390 	  buf_appendl(new_index, from - q);
    391 	  buf_appendl(new_index, subject - q);
    392 	  buf_appendl(new_index, length - q);
    393 	}
    394 	buf_free(index);
    395 	index = new_index;
    396 	/* Done */
    397 	num--;
    398 	folder_has_changed = 1;
    399       }
    400       redraw = 2;
    401       break;
    402     case KEY_UP:
    403       selected--;
    404       redraw = 1;
    405       break;
    406     case KEY_DOWN:
    407     case 'n':			/* nym ???? */
    408       selected++;
    409       redraw = 1;
    410       break;
    411     case KEY_PPAGE:
    412       selected -= range;
    413       redraw = 1;
    414       break;
    415     case KEY_NPAGE:
    416       selected += range;
    417       redraw = 1;
    418       break;
    419     default:
    420       beep();
    421     }
    422   }
    423 #endif /* USE_NCURSES */
    424 
    425 end:
    426 #ifdef USE_NCURSES
    427   /* If folder has changed, ask user about saving new folder. */
    428   if (folder_has_changed && !streq(path, "stdin")) {
    429     mvprintw(LINES - 2, 0, "Buffer has changed. Save [y/n]? ");
    430     c = getch();
    431     cl(LINES - 2, 0);
    432     if ((c == 'y') || (c == 'Y')){
    433       strncpy(path_with_tilde, path, PATHMAX-1);
    434       strcat(path_with_tilde, "~");
    435       rename(path, path_with_tilde); /* Rename folder to folder~ */
    436       f = fopen(path, "w"); /* Write new folder */
    437       if (f == NULL)
    438 	mix_status("Can't write to %s.", path);
    439       else{
    440 	buf_write(folder, f);
    441 	mix_status("Wrote %s.", path);
    442 	fclose(f);
    443       }
    444     }
    445     else{
    446       mix_status("%s was not saved.", path);
    447     }
    448   }
    449 #endif /* USE_NCURSES */
    450   buf_free(folder);
    451   buf_free(line);
    452   buf_free(field);
    453   buf_free(content);
    454   buf_free(index);
    455   buf_free(mail);
    456   buf_free(name);
    457   buf_free(log);
    458 }
    459 
    460 #ifdef USE_NCURSES
    461 static int sortrel(const void *a, const void *b)
    462 {
    463   int na, ra, nb, rb;
    464 
    465   na = *(int *) a;
    466   nb = *(int *) b;
    467 
    468   ra = *((int *) a + 1);
    469   rb = *((int *) b + 1);
    470   return rb - ra;
    471 }
    472 
    473 void menu_main(void)
    474 {
    475   int y, x;
    476   int pool, n;
    477   int c;
    478   int space;
    479   BUFFER *chainlist, *line;
    480   char nym[LINELEN] = ANON;
    481 
    482   chainlist = buf_new();
    483   line = buf_new();
    484 
    485   mix_init(NULL);
    486   menu_init();
    487 
    488 menu_redraw:
    489   clear();
    490   for (;;) {
    491     standout();
    492     mvprintw(0, 0, "Mixmaster %s", VERSION);
    493 #if 0
    494     mvprintw(0, COLS - sizeof(COPYRIGHT), COPYRIGHT);
    495 #endif
    496     for (space = 0; space < (COLS - 10 - sizeof(VERSION)); space++) {
    497         printw(" ");
    498     };
    499     standend();
    500     mix_status(NULL);
    501 
    502 #ifdef NYMSUPPORT
    503     mvprintw(8, 4, "n)ym: %s", nym);
    504 #endif /* NYMSUPPORT */
    505     y = 12, x = 25;
    506     mvprintw(y++, x, "m)ail");
    507     mvprintw(y++, x, "p)ost to Usenet");
    508     mvprintw(y++, x, "r)ead mail (or news article)");
    509     mvprintw(y++, x, "d)ummy message");
    510     mvprintw(y++, x, "s)end messages from pool");
    511     mvprintw(y++, x, "e)dit configuration file");
    512     mvprintw(y++, x, "u)pdate stats");
    513     mvprintw(y++, x, "q)uit");
    514 
    515     pool = pool_read(NULL);
    516     if (pool == 1)
    517       mvprintw(4, 2, "%3d outgoing message in the pool. \n", pool);
    518     else
    519       mvprintw(4, 2, "%3d outgoing messages in the pool.\n", pool);
    520 
    521     move(LINES - 1, COLS - 1);
    522     refresh();
    523     c = getch();
    524     if (c != ERR) {
    525       mix_status("");
    526       switch (c) {
    527       case '\014':
    528 	mix_status("");
    529 	goto menu_redraw;
    530 #ifdef NYMSUPPORT
    531       case 'n':
    532 	menu_nym(nym);
    533 	goto menu_redraw;
    534 #endif /* NYMSUPPORT */
    535       case 'm':
    536       case 'p':
    537 	send_message(c, nym, NULL);
    538 	break;
    539       case 'd':
    540 	mix_status("Creating message...");
    541 	if (mix_encrypt(MSG_NULL, NULL, NULL, 1, chainlist) != 0) {
    542 	  if (chainlist->length > 0)
    543 	    mix_status("%s", chainlist->data);
    544 	  else
    545 	    mix_genericerror();
    546 	  beep();
    547 	} else {
    548 	  for (n = 0; buf_getline(chainlist, line) == 0; n++) ;
    549 	  if (n > 1)
    550 	    mix_status("Done (%d packets).", n);
    551 	  else
    552 	    mix_status("Chain: %s", chainlist->data);
    553 	}
    554 	break;
    555       case 's':
    556 	mix_status("Mailing messages...");
    557 	mix_send();
    558 	mix_status("Done.");
    559 	break;
    560       case 'r':
    561 	{
    562 	  char name[LINELEN];
    563 
    564 	  cl(LINES - 3, 0);
    565 	  if (getenv("MAIL"))
    566 	    printw("File name [%s]: ", getenv("MAIL"));
    567 	  else
    568 	    printw("File name: ");
    569 	  echo();
    570 	  wgetnstr(stdscr, name, LINELEN);
    571 	  noecho();
    572 	  cl(LINES - 3, 0);
    573 	  if (name[0] == '\0') {
    574 	    if (getenv("MAIL"))
    575 	      read_folder(0, getenv("MAIL"), nym);
    576 	  } else
    577 	    read_folder(0, name, nym);
    578 	}
    579 	break;
    580       case 'e':
    581 	do {
    582 	  char path[PATHMAX];
    583 	  mixfile(path, MIXCONF);
    584 	  menu_spawn_editor(path, 0);
    585 	  mix_config();
    586 	} while (0);
    587 	break;
    588       case 'u':
    589 	update_stats();
    590 	break;
    591       case 'q':
    592       case 'Q':
    593 	goto quit;
    594       default:
    595 	beep();
    596       }
    597     }
    598   }
    599 
    600 quit:
    601   menu_exit();
    602   buf_free(chainlist);
    603   buf_free(line);
    604 }
    605 
    606 void read_message(BUFFER *message, char *nym)
    607 {
    608   int l = 0;
    609   int c;
    610   int inhdr = 1, txtbegin;
    611   BUFFER *field, *content, *line, *hdr;
    612   char sub[LINELEN] = "mail";
    613   char thisnym[LINELEN] = "";
    614 
    615   field = buf_new();
    616   content = buf_new();
    617   line = buf_new();
    618   hdr = buf_new();
    619 
    620   if (thisnym[0] == '\0')
    621     strncpy(thisnym, nym, LINELEN);
    622 
    623   if (bufleft(message, "From nymserver ")) {
    624     /* select nym if Nym: pseudo header is in the first line */
    625     buf_getline(message, line);
    626     buf_getheader(message, field, content);
    627     if (bufieq(field, "Nym"))
    628       strncpy(thisnym, content->data, sizeof(thisnym));
    629     buf_rewind(message);
    630   }
    631   while (buf_getheader(message, field, content) == 0) {
    632     if (bufieq(field, "received") || bufleft(field, "From "))
    633       continue;
    634     if (bufieq(field, "subject"))
    635       strncpy(sub, content->data, sizeof(sub));
    636     buf_appendheader(hdr, field, content);
    637   }
    638   if (strlen(sub) > COLS - strlen(VERSION) - strlen(thisnym) - 23)
    639     sub[COLS - strlen(VERSION) - strlen(thisnym) - 24] = '\0';
    640   txtbegin = message->ptr;
    641 
    642 loop:
    643   clear();
    644   standout();
    645   mvprintw(0, 0, "Mixmaster %s", VERSION);
    646   printw("   %.20s reading %.50s\n", thisnym, sub);
    647   standend();
    648   mix_status(NULL);
    649 
    650   while (l < LINES - 2) {
    651     if (inhdr) {
    652       if (buf_getline(hdr, line) == -1)
    653 	buf_clear(line), inhdr = 0;
    654     } else {
    655       if (buf_getline(message, line) == -1) {
    656 	standout();
    657 	mvprintw(LINES - 1, 0, "Command");
    658 	standend();
    659 	refresh();
    660 	for (;;) {
    661 	  c = getch();
    662 	  switch (c) {
    663 	  case 'm':
    664 	  case 'p':
    665 	  case 'r':
    666 	  case 'g':
    667 	  case 'f':
    668 	    send_message(c, thisnym, message);
    669 	    goto end;
    670 	  case 'u':
    671 	    inhdr = 0;
    672 	    message->ptr = txtbegin;
    673 	    l = 0;
    674 	    goto loop;
    675 	  case 'h':
    676 	    inhdr = 1;
    677 	    hdr->ptr = 0;
    678 	    message->ptr = txtbegin;
    679 	    l = 0;
    680 	    goto loop;
    681 	  case 's':
    682 	    savemsg(message);
    683 	    /* fallthru */
    684 	  case 'q':
    685 	  case 'i':
    686 	  case '\n':
    687 	  case '\r':
    688 	    goto end;
    689 	  case '\014':
    690 	    refresh();
    691 	    continue;
    692 	  default:
    693 	    beep();
    694 	    refresh();
    695 	  }
    696 	}
    697       }
    698     }
    699     mvprintw(l + 1, 0, "%s\n", line->data);
    700     l += (line->length - 1) / COLS + 1;
    701   }
    702   standout();
    703   mvprintw(LINES - 1, 0, "MORE");
    704   standend();
    705   refresh();
    706   for (;;) {
    707     c = getch();
    708     switch (c) {
    709     case 'm':
    710     case 'p':
    711     case 'r':
    712     case 'g':
    713     case 'f':
    714       send_message(c, thisnym, message);
    715       goto end;
    716     case 'u':
    717       inhdr = 0;
    718       message->ptr = txtbegin;
    719       l = 0;
    720       goto loop;
    721     case 'h':
    722       inhdr = 1;		/* show full header */
    723       hdr->ptr = 0;
    724       message->ptr = txtbegin;
    725       l = 0;
    726       goto loop;
    727     case 's':
    728       savemsg(message);
    729       /* fallthru */
    730     case 'q':
    731     case 'i':
    732       goto end;
    733     case ' ':
    734     case '\n':
    735     case '\r':
    736       l = 0;
    737       goto loop;
    738     case '\014':
    739       refresh();
    740       continue;
    741     default:
    742       beep();
    743       refresh();
    744     }
    745   }
    746 end:
    747   buf_free(field);
    748   buf_free(content);
    749   buf_free(line);
    750   buf_free(hdr);
    751 }
    752 
    753 int menu_replychain(int *d, int *l, char *mdest, char *pdest, char *psub,
    754 		    char *r)
    755 {
    756   int c;
    757   char line[LINELEN];
    758   char reliability[9];
    759 
    760 redraw:
    761   clear();
    762   standout();
    763   printw("Create a nym reply block:");
    764   standend();
    765   mix_status(NULL);
    766 
    767   mvprintw(3, 0, "Type of reply block:\n");
    768   if (*d == MSG_MAIL)
    769     standout();
    770   printw(" m)ail ");
    771   if (*d == MSG_MAIL)
    772     standend();
    773 
    774   if (*d == MSG_POST)
    775     standout();
    776   printw(" Usenet message p)ool ");
    777   if (*d == MSG_POST)
    778     standend();
    779 
    780   if (*d == MSG_NULL)
    781     standout();
    782   printw(" cover t)raffic ");
    783   if (*d == MSG_NULL)
    784     standend();
    785 
    786   if (*d != MSG_NULL)
    787     mvprintw(6, 0, "d)estination: %s", *d == MSG_MAIL ? mdest : pdest);
    788   if (psub && *d == MSG_POST)
    789     mvprintw(7, 0, "s)ubject: %s", psub);
    790   chain_reliability(r, 1, reliability); /* chaintype 1=ek */
    791   mvprintw(8, 0, "c)hain: %-39s (reliability: %s)", r, reliability);
    792   mvprintw(10, 0, "l)atency: %d hours", *l);
    793   move(LINES - 1, COLS - 1);
    794 
    795   for (;;) {
    796     refresh();
    797     c = getch();
    798     switch (c) {
    799     case 'm':
    800       *d = MSG_MAIL;
    801       goto redraw;
    802     case 'p':
    803       *d = MSG_POST;
    804       goto redraw;
    805     case 't':
    806       *d = MSG_NULL;
    807       goto redraw;
    808     case 'q':
    809       return (-1);
    810     case 'd':
    811       cl(6, 0);
    812       printw("d)estination: ");
    813       echo();
    814       wgetnstr(stdscr, *d == MSG_MAIL ? mdest : pdest, LINELEN);
    815       noecho();
    816       goto redraw;
    817     case 'l':
    818       cl(10, 0);
    819       printw("l)atency: ");
    820       echo();
    821       wgetnstr(stdscr, line, LINELEN);
    822       *l = strtol(line, NULL, 10);
    823       if (*l < 0)
    824 	*l = 0;
    825       noecho();
    826       goto redraw;
    827     case 'c':
    828       menu_chain(r, 1, *d == MSG_POST);
    829       goto redraw;
    830     case '\014':
    831       goto redraw;
    832     case '\n':
    833     case '\r':
    834       return (0);
    835     case 's':
    836       if (*d == MSG_POST) {
    837 	cl(7, 0);
    838 	printw("s)ubject: ");
    839 	echo();
    840 	wgetnstr(stdscr, psub, LINELEN);
    841 	noecho();
    842 	goto redraw;
    843       }
    844     default:
    845       beep();
    846     }
    847   }
    848 }
    849 
    850 void menu_chain(char *chainstr, int chaintype, int post)
    851      /* chaintype 0=mix 1=ek 2=newnym */
    852 {
    853   REMAILER remailer[MAXREM];
    854   int badchains[MAXREM][MAXREM];
    855   int rlist[2 * MAXREM];
    856   char newchain[CHAINMAX];
    857   char info[LINELEN];
    858   int num = 0, i, middlemanlast=0, ok = 1;
    859   int c, x, y;
    860   int nymserv = 0;
    861   int chain[20], chainlen = 0;
    862   char reliability[9];
    863 
    864   if (chaintype == 2)
    865     nymserv = 1, chaintype = 1;
    866   assert(chaintype == 0 || chaintype == 1);
    867 
    868   clear();
    869   standout();
    870   if (nymserv)
    871     printw("Select nym server:\n\n");
    872   else
    873     printw("Select remailer chain:\n\n");
    874   standend();
    875 
    876   if (chaintype == 1)
    877     num = t1_rlist(remailer, badchains);
    878   else
    879     num = mix2_rlist(remailer, badchains);
    880 
    881   if (num < 1) {
    882     mix_status("Can't read remailer list.");
    883     beep();
    884     return;
    885   }
    886   for (i = 0; i < num; i++) {
    887     rlist[2 * i] = i + 1;
    888     rlist[2 * i + 1] = remailer[i + 1].info[chaintype].reliability -
    889       remailer[i + 1].info[chaintype].latency / 3600;
    890     if (remailer[i + 1].info[chaintype].history[0] == '\0')
    891       rlist[2 * i + 1] = -1;
    892     if ((nymserv && !remailer[i + 1].flags.newnym) ||
    893 	((chaintype == 0 && !remailer[i + 1].flags.mix) ||
    894 	 (chaintype == 1 && !nymserv && (!remailer[i + 1].flags.ek
    895 					 || !remailer[i + 1].flags.pgp))))
    896       rlist[2 * i] = 0, rlist[2 * i + 1] = -2;
    897   }
    898   qsort(rlist, num - 1, 2 * sizeof(int), sortrel);
    899 
    900   for (i = 0; i < num; i++)
    901     if (rlist[2 * i + 1] == -2) {
    902       num = i;
    903       break;
    904     }
    905   if (num < 1) {
    906     mix_status("No remailers found!");
    907     return;
    908   }
    909   if (num > 2 * (LINES - 6))
    910     num = 2 * (LINES - 6);
    911   if (num > 2 * 26)
    912     num = 2 * 26;
    913 
    914   for (i = 0; i < num && rlist[2 * i + 1] > -2; i++) {
    915     y = i, x = 0;
    916     if (y >= LINES - 6)
    917       y -= LINES - 6, x += 40;
    918     mvprintw(y + 2, x, "%c", i < 26 ? i + 'a' : i - 26 + 'A');
    919     mvprintw(y + 2, x + 2, "%s", remailer[rlist[2 * i]].name);
    920     mvprintw(y + 2, x + 16, "%s",
    921 	     remailer[rlist[2 * i]].info[chaintype].history);
    922     sprintf(info, "%3.2f",
    923 	    remailer[rlist[2 * i]].info[chaintype].reliability / 100.0);
    924     mvprintw(y + 2, x + 29 + 6 - strlen(info), "%s%%", info);
    925   }
    926   y = num + 3;
    927   if (y > LINES - 4)
    928     y = LINES - 4;
    929   mvprintw(y, 0, "*  select at random");
    930 
    931   for (;;) {
    932     newchain[0] = '\0';
    933     for (i = 0; i < chainlen; i++) {
    934       if (i)
    935 	strcatn(newchain, ",", CHAINMAX);
    936       if (chain[i])
    937 	strcatn(newchain, remailer[chain[i]].name, CHAINMAX);
    938       else
    939 	strcatn(newchain, "*", CHAINMAX);
    940     }
    941     if (chainlen > 0) {
    942       ok = 1;
    943       middlemanlast = remailer[chain[chainlen - 1]].flags.middle;
    944       if (post && !remailer[chain[chainlen - 1]].flags.post && !(chain[chainlen - 1] == 0 /*randhop*/))
    945 	ok = 0;
    946     } else
    947       ok = 1;
    948 
    949     mvprintw(LINES - 4, 40,
    950       middlemanlast ?
    951 	"MIDDLEMAN   " :
    952 	(ok ?
    953 	  "            " :
    954 	  "NO POSTING  "));
    955     cl(LINES - 3, 0);
    956     cl(LINES - 2, 0);
    957     cl(LINES - 1, 0);
    958     if(!nymserv){
    959       chain_reliability(newchain, chaintype, reliability);
    960       mvprintw(LINES - 4, 58, "(reliability: %s)", reliability);
    961     }
    962     mvprintw(LINES - 3, 0, nymserv ? "Nym server: %s" : "Chain: %s",
    963 	     newchain);
    964     refresh();
    965     c = getch();
    966     if (c == '\n' || c == '\r') {
    967     /* beep and sleep in case the user made a mistake */
    968       if (middlemanlast) {
    969 	beep();
    970 	sleep(2);
    971       }
    972       if (ok || middlemanlast)
    973 	break;
    974       else
    975 	beep();
    976     } else if (c == '*') {
    977       if (chainlen > 19 || (nymserv && chainlen > 0))
    978 	beep();
    979       else
    980 	chain[chainlen++] = 0;
    981     } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
    982       if (c >= 'a')
    983 	c -= 'a';
    984       else
    985 	c = c - 'A' + 26;
    986 
    987       if (chainlen > 19 || (nymserv && chainlen > 0) || c >= num)
    988 	beep();
    989       else
    990 	chain[chainlen++] = rlist[2 * c];
    991     } else if (c == killchar())
    992       chainlen = 0;
    993     else if ((c == KEY_BACKSPACE || c == KEY_LEFT || c == erasechar())
    994 	     && chainlen > 0)
    995       --chainlen;
    996     else
    997       beep();
    998   }
    999   if (chainlen)
   1000     strncpy(chainstr, newchain, CHAINMAX);
   1001 }
   1002 
   1003 #endif /* USE_NCURSES */