tmenu.c - mixmaster - mixmaster 3.0 patched for libressl
 (HTM) git clone git://parazyd.org/mixmaster.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
       tmenu.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 */