* Changed the comments at the beginning of each file to a consistent format
[citadel.git] / citadel / routines2.c
1 /* $Id$
2  *
3  * More client-side support functions.
4  * Unlike routines.c, some of these DO use global variables.
5  *
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <string.h>
17 #include <time.h>
18 #include <signal.h>
19 #include <pwd.h>
20 #include <setjmp.h>
21 #include <errno.h>
22 #include <stdarg.h>
23 #include "citadel.h"
24 #include "routines2.h"
25 #include "routines.h"
26 #include "commands.h"
27 #include "tools.h"
28 #include "messages.h"
29 #ifndef HAVE_SNPRINTF
30 #include "snprintf.h"
31 #endif
32
33 void interr(int errnum);
34 void strprompt(char *prompt, char *str, int len);
35 void newprompt(char *prompt, char *str, int len);
36 void sttybbs(int cmd);
37 int inkey(void);
38 void serv_write(char *buf, int nbytes);
39 int haschar(char *st, int ch);
40 void progress(long int curr, long int cmax);
41 int yesno(void);
42
43 extern char temp[];
44 extern char tempdir[];
45 extern char *axdefs[7];
46 extern long highest_msg_read;
47 extern long maxmsgnum;
48 extern unsigned room_flags;
49 extern int screenwidth;
50
51
52 int eopen(char *name, int mode)
53 {
54         int ret;
55         ret = open(name, mode);
56         if (ret < 0) {
57                 fprintf(stderr, "Cannot open file '%s', mode=%d, errno=%d\n",
58                         name, mode, errno);
59                 interr(errno);
60         }
61         return (ret);
62 }
63
64
65 int room_prompt(int qrflags)
66 {                               /* return proper room prompt character */
67         int a;
68         a = '>';
69         if (qrflags & QR_DIRECTORY)
70                 a = ']';
71         if ((a == ']') && (qrflags & QR_NETWORK))
72                 a = '}';
73         if ((a == '>') && (qrflags & QR_NETWORK))
74                 a = ')';
75         return (a);
76 }
77
78 void entregis(void)
79 {                               /* register with name and address */
80
81         char buf[256];
82         char tmpname[256];
83         char tmpaddr[256];
84         char tmpcity[256];
85         char tmpstate[256];
86         char tmpzip[256];
87         char tmpphone[256];
88         char tmpemail[256];
89         int a;
90
91         strcpy(tmpname, "");
92         strcpy(tmpaddr, "");
93         strcpy(tmpcity, "");
94         strcpy(tmpstate, "");
95         strcpy(tmpzip, "");
96         strcpy(tmpphone, "");
97         strcpy(tmpemail, "");
98
99         serv_puts("GREG _SELF_");
100         serv_gets(buf);
101         if (buf[0] == '1') {
102                 a = 0;
103                 while (serv_gets(buf), strcmp(buf, "000")) {
104                         if (a == 2)
105                                 strcpy(tmpname, buf);
106                         if (a == 3)
107                                 strcpy(tmpaddr, buf);
108                         if (a == 4)
109                                 strcpy(tmpcity, buf);
110                         if (a == 5)
111                                 strcpy(tmpstate, buf);
112                         if (a == 6)
113                                 strcpy(tmpzip, buf);
114                         if (a == 7)
115                                 strcpy(tmpphone, buf);
116                         if (a == 9)
117                                 strcpy(tmpemail, buf);
118                         ++a;
119                 }
120         }
121         strprompt("REAL name", tmpname, 29);
122         strprompt("Address", tmpaddr, 24);
123         strprompt("City/town", tmpcity, 14);
124         strprompt("State", tmpstate, 2);
125         strprompt("ZIP Code", tmpzip, 10);
126         strprompt("Telephone number", tmpphone, 14);
127         strprompt("Email address", tmpemail, 31);
128
129         /* now send the registration info back to the server */
130         serv_puts("REGI");
131         serv_gets(buf);
132         if (buf[0] != '4') {
133                 printf("%s\n", &buf[4]);
134                 return;
135         }
136         serv_puts(tmpname);
137         serv_puts(tmpaddr);
138         serv_puts(tmpcity);
139         serv_puts(tmpstate);
140         serv_puts(tmpzip);
141         serv_puts(tmpphone);
142         serv_puts(tmpemail);
143         serv_puts("000");
144         printf("\n");
145 }
146
147 void updatels(void)
148 {                               /* make all messages old in current room */
149         char buf[256];
150         serv_puts("SLRP HIGHEST");
151         serv_gets(buf);
152         if (buf[0] != '2')
153                 printf("%s\n", &buf[4]);
154 }
155
156 /*
157  * only make messages old in this room that have been read
158  */
159 void updatelsa(void)
160 {
161         char buf[256];
162         sprintf(buf, "SLRP %ld", highest_msg_read);
163         serv_puts(buf);
164         serv_gets(buf);
165         if (buf[0] != '2')
166                 printf("%s\n", &buf[4]);
167 }
168
169
170 /*
171  * This routine completes a client upload
172  */
173 void do_upload(int fd)
174 {
175         char buf[256];
176         char tbuf[4096];
177         long transmitted_bytes, total_bytes;
178         int bytes_to_send;
179         int bytes_expected;
180
181         /* learn the size of the file */
182         total_bytes = lseek(fd, 0L, 2);
183         lseek(fd, 0L, 0);
184
185         transmitted_bytes = 0L;
186         progress(transmitted_bytes, total_bytes);
187         do {
188                 bytes_to_send = read(fd, tbuf, 4096);
189                 if (bytes_to_send > 0) {
190                         sprintf(buf, "WRIT %d", bytes_to_send);
191                         serv_puts(buf);
192                         serv_gets(buf);
193                         if (buf[0] == '7') {
194                                 bytes_expected = atoi(&buf[4]);
195                                 serv_write(tbuf, bytes_expected);
196                         } else {
197                                 printf("%s\n", &buf[4]);
198                         }
199                 }
200                 transmitted_bytes = transmitted_bytes + (long) bytes_to_send;
201                 progress(transmitted_bytes, total_bytes);
202         } while (bytes_to_send > 0);
203
204         /* close the upload file, locally and at the server */
205         close(fd);
206         serv_puts("UCLS 1");
207         serv_gets(buf);
208         printf("%s\n", &buf[4]);
209 }
210
211
212 /*
213  * client-based uploads (for users with their own clientware)
214  */
215 void cli_upload(void)
216 {
217         char flnm[256];
218         char desc[151];
219         char buf[256];
220         char tbuf[256];
221         int a;
222         int fd;
223
224         if ((room_flags & QR_UPLOAD) == 0) {
225                 printf("*** You cannot upload to this room.\n");
226                 return;
227         }
228         newprompt("File to be uploaded: ", flnm, 55);
229         fd = open(flnm, O_RDONLY);
230         if (fd < 0) {
231                 printf("Cannot open '%s': %s\n", flnm, strerror(errno));
232                 return;
233         }
234         printf("Enter a description of this file:\n");
235         newprompt(": ", desc, 75);
236
237         /* keep generating filenames in hope of finding a unique one */
238         a = 0;
239         do {
240                 if (a == 10)
241                         return; /* fail if tried 10 times */
242                 strcpy(buf, flnm);
243                 while ((strlen(buf) > 0) && (haschar(buf, '/')))
244                         strcpy(buf, &buf[1]);
245                 if (a > 0)
246                         sprintf(&buf[strlen(buf)], "%d", a);
247                 sprintf(tbuf, "UOPN %s|%s", buf, desc);
248                 serv_puts(tbuf);
249                 serv_gets(buf);
250                 if (buf[0] != '2')
251                         printf("%s\n", &buf[4]);
252                 ++a;
253         } while (buf[0] != '2');
254
255         /* at this point we have an open upload file at the server */
256         do_upload(fd);
257 }
258
259
260 /*
261  * Function used for various image upload commands
262  */
263 void cli_image_upload(char *keyname)
264 {
265         char flnm[256];
266         char buf[256];
267         int fd;
268
269         sprintf(buf, "UIMG 0|%s", keyname);
270         serv_puts(buf);
271         serv_gets(buf);
272         if (buf[0] != '2') {
273                 printf("%s\n", &buf[4]);
274                 return;
275         }
276         newprompt("Image file to be uploaded: ", flnm, 55);
277         fd = open(flnm, O_RDONLY);
278         if (fd < 0) {
279                 printf("Cannot open '%s': %s\n", flnm, strerror(errno));
280                 return;
281         }
282         sprintf(buf, "UIMG 1|%s", keyname);
283         serv_puts(buf);
284         serv_gets(buf);
285         if (buf[0] != '2') {
286                 printf("%s\n", &buf[4]);
287                 return;
288         }
289         do_upload(fd);
290 }
291
292
293 /*
294  * protocol-based uploads (Xmodem, Ymodem, Zmodem)
295  */
296 void upload(int c)
297 {                               /* c = upload mode */
298         char flnm[256];
299         char desc[151];
300         char buf[256];
301         char tbuf[4096];
302         int xfer_pid;
303         int a, b;
304         FILE *fp, *lsfp;
305         int fd;
306
307         if ((room_flags & QR_UPLOAD) == 0) {
308                 printf("*** You cannot upload to this room.\n");
309                 return;
310         }
311         /* we don't need a filename when receiving batch y/z modem */
312         if ((c == 2) || (c == 3))
313                 strcpy(flnm, "x");
314         else
315                 newprompt("Enter filename: ", flnm, 15);
316
317         for (a = 0; a < strlen(flnm); ++a)
318                 if ((flnm[a] == '/') || (flnm[a] == '\\') || (flnm[a] == '>')
319                     || (flnm[a] == '?') || (flnm[a] == '*')
320                     || (flnm[a] == ';') || (flnm[a] == '&'))
321                         flnm[a] = '_';
322
323         newprompt("Enter a short description of the file:\n: ", desc, 150);
324
325         /* create a temporary directory... */
326         if (mkdir(tempdir, 0700) != 0) {
327                 printf("*** Could not create temporary directory %s: %s\n",
328                        tempdir, strerror(errno));
329                 return;
330         }
331         /* now do the transfer ... in a separate process */
332         xfer_pid = fork();
333         if (xfer_pid == 0) {
334                 chdir(tempdir);
335                 switch (c) {
336                 case 0:
337                         sttybbs(0);
338                         printf("Receiving %s - press Ctrl-D to end.\n", flnm);
339                         fp = fopen(flnm, "w");
340                         do {
341                                 b = inkey();
342                                 if (b == 13) {
343                                         b = 10;
344                                         printf("\r");
345                                 }
346                                 if (b != 4) {
347                                         printf("%c", b);
348                                         putc(b, fp);
349                                 }
350                         } while (b != 4);
351                         fclose(fp);
352                         exit(0);
353                 case 1:
354                         sttybbs(3);
355                         execlp("rx", "rx", flnm, NULL);
356                         exit(1);
357                 case 2:
358                         sttybbs(3);
359                         execlp("rb", "rb", NULL);
360                         exit(1);
361                 case 3:
362                         sttybbs(3);
363                         execlp("rz", "rz", NULL);
364                         exit(1);
365                 }
366         } else
367                 do {
368                         b = ka_wait(&a);
369                 } while ((b != xfer_pid) && (b != (-1)));
370         sttybbs(0);
371
372         if (a != 0) {
373                 printf("\r*** Transfer unsuccessful.\n");
374                 nukedir(tempdir);
375                 return;
376         }
377         printf("\r*** Transfer successful.  Sending file(s) to server...\n");
378         sprintf(buf, "cd %s; ls", tempdir);
379         lsfp = popen(buf, "r");
380         if (lsfp != NULL) {
381                 while (fgets(flnm, 256, lsfp) != NULL) {
382                         flnm[strlen(flnm) - 1] = 0;
383                         sprintf(buf, "%s/%s", tempdir, flnm);
384                         fd = open(buf, O_RDONLY);
385                         if (fd >= 0) {
386                                 a = 0;
387                                 do {
388                                         sprintf(buf, "UOPN %s|%s", flnm, desc);
389                                         if (a > 0)
390                                                 sprintf(&buf[strlen(buf)],
391                                                         ".%d", a);
392                                         ++a;
393                                         serv_puts(buf);
394                                         serv_gets(buf);
395                                 } while ((buf[0] != '2') && (a < 100));
396                                 if (buf[0] == '2')
397                                         do {
398                                                 a = read(fd, tbuf, 4096);
399                                                 if (a > 0) {
400                                                         sprintf(buf, "WRIT %d", a);
401                                                         serv_puts(buf);
402                                                         serv_gets(buf);
403                                                         if (buf[0] == '7')
404                                                                 serv_write(tbuf, a);
405                                                 }
406                                         } while (a > 0);
407                                 close(fd);
408                                 serv_puts("UCLS 1");
409                                 serv_gets(buf);
410                                 printf("%s\n", &buf[4]);
411                         }
412                 }
413                 pclose(lsfp);
414         }
415         nukedir(tempdir);
416 }
417
418 /* 
419  * validate a user
420  */
421 void val_user(char *user, int do_validate)
422 {
423         int a;
424         char cmd[256];
425         char buf[256];
426         int ax = 0;
427
428         sprintf(cmd, "GREG %s", user);
429         serv_puts(cmd);
430         serv_gets(cmd);
431         if (cmd[0] == '1') {
432                 a = 0;
433                 do {
434                         serv_gets(buf);
435                         ++a;
436                         if (a == 1)
437                                 printf("User #%s - %s  ", buf, &cmd[4]);
438                         if (a == 2)
439                                 printf("PW: %s\n", buf);
440                         if (a == 3)
441                                 printf("%s\n", buf);
442                         if (a == 4)
443                                 printf("%s\n", buf);
444                         if (a == 5)
445                                 printf("%s, ", buf);
446                         if (a == 6)
447                                 printf("%s ", buf);
448                         if (a == 7)
449                                 printf("%s\n", buf);
450                         if (a == 8)
451                                 printf("%s\n", buf);
452                         if (a == 9)
453                                 ax = atoi(buf);
454                         if (a == 10)
455                                 printf("%s\n", buf);
456                 } while (strcmp(buf, "000"));
457                 printf("Current access level: %d (%s)\n", ax, axdefs[ax]);
458         } else {
459                 printf("%-30s\n%s\n", user, &cmd[4]);
460         }
461
462         if (do_validate) {
463                 /* now set the access level */
464                 ax = intprompt("Access level", ax, 0, 6);
465                 sprintf(cmd, "VALI %s|%d", user, ax);
466                 serv_puts(cmd);
467                 serv_gets(cmd);
468                 if (cmd[0] != '2')
469                         printf("%s\n", &cmd[4]);
470         }
471         printf("\n");
472 }
473
474
475 void validate(void)
476 {                               /* validate new users */
477         char cmd[256];
478         char buf[256];
479         int finished = 0;
480
481         do {
482                 serv_puts("GNUR");
483                 serv_gets(cmd);
484                 if (cmd[0] != '3')
485                         finished = 1;
486                 if (cmd[0] == '2')
487                         printf("%s\n", &cmd[4]);
488                 if (cmd[0] == '3') {
489                         extract(buf, cmd, 0);
490                         val_user(&buf[4], 1);
491                 }
492         } while (finished == 0);
493 }
494
495 void subshell(void)
496 {
497         int a, b;
498         a = fork();
499         if (a == 0) {
500                 sttybbs(SB_RESTORE);
501                 signal(SIGINT, SIG_DFL);
502                 signal(SIGQUIT, SIG_DFL);
503                 execlp(getenv("SHELL"), getenv("SHELL"), NULL);
504                 printf("Could not open a shell: %s\n", strerror(errno));
505                 exit(errno);
506         }
507         do {
508                 b = ka_wait(NULL);
509         } while ((a != b) && (a != (-1)));
510         sttybbs(0);
511 }
512
513 /*
514  * <.A>ide <F>ile <D>elete command
515  */
516 void deletefile(void)
517 {
518         char filename[32];
519         char cmd[256];
520
521         newprompt("Filename: ", filename, 31);
522         if (strlen(filename) == 0)
523                 return;
524         sprintf(cmd, "DELF %s", filename);
525         serv_puts(cmd);
526         serv_gets(cmd);
527         printf("%s\n", &cmd[4]);
528 }
529
530 /*
531  * <.A>ide <F>ile <S>end command
532  */
533 void netsendfile(void)
534 {
535         char filename[32], destsys[20], cmd[256];
536
537         newprompt("Filename: ", filename, 31);
538         if (strlen(filename) == 0)
539                 return;
540         newprompt("System to send to: ", destsys, 19);
541         sprintf(cmd, "NETF %s|%s", filename, destsys);
542         serv_puts(cmd);
543         serv_gets(cmd);
544         printf("%s\n", &cmd[4]);
545         return;
546 }
547
548 /*
549  * <.A>ide <F>ile <M>ove command
550  */
551 void movefile(void)
552 {
553         char filename[64];
554         char newroom[ROOMNAMELEN];
555         char cmd[256];
556
557         newprompt("Filename: ", filename, 63);
558         if (strlen(filename) == 0)
559                 return;
560         newprompt("Enter target room: ", newroom, ROOMNAMELEN - 1);
561
562         sprintf(cmd, "MOVF %s|%s", filename, newroom);
563         serv_puts(cmd);
564         serv_gets(cmd);
565         printf("%s\n", &cmd[4]);
566 }
567
568
569 /* 
570  * list of users who have filled out a bio
571  */
572 void list_bio(void)
573 {
574         char buf[256];
575         int pos = 1;
576
577         serv_puts("LBIO");
578         serv_gets(buf);
579         if (buf[0] != '1') {
580                 pprintf("%s\n", &buf[4]);
581                 return;
582         }
583         while (serv_gets(buf), strcmp(buf, "000")) {
584                 if ((pos + strlen(buf) + 5) > screenwidth) {
585                         pprintf("\n");
586                         pos = 1;
587                 }
588                 pprintf("%s, ", buf);
589                 pos = pos + strlen(buf) + 2;
590         }
591         pprintf("%c%c  \n\n", 8, 8);
592 }
593
594
595 /*
596  * read bio
597  */
598 void read_bio(void)
599 {
600         char who[256];
601         char buf[256];
602
603         do {
604                 newprompt("Read bio for who ('?' for list) : ", who, 25);
605                 pprintf("\n");
606                 if (!strcmp(who, "?"))
607                         list_bio();
608         } while (!strcmp(who, "?"));
609         sprintf(buf, "RBIO %s", who);
610         serv_puts(buf);
611         serv_gets(buf);
612         if (buf[0] != '1') {
613                 pprintf("%s\n", &buf[4]);
614                 return;
615         }
616         while (serv_gets(buf), strcmp(buf, "000")) {
617                 pprintf("%s\n", buf);
618         }
619 }
620
621
622 /* 
623  * General system configuration command
624  */
625 void do_system_configuration(void)
626 {
627         char buf[256];
628         char sc[26][256];
629         int expire_mode = 0;
630         int expire_value = 0;
631         int a;
632
633         /* Clear out the config buffers */
634         memset(&sc[0][0], 0, sizeof(sc));
635
636         /* Fetch the current config */
637         serv_puts("CONF get");
638         serv_gets(buf);
639         if (buf[0] == '1') {
640                 a = 0;
641                 while (serv_gets(buf), strcmp(buf, "000")) {
642                         if (a < 26)
643                                 strcpy(&sc[a][0], buf);
644                         ++a;
645                 }
646         }
647         /* Fetch the expire policy (this will silently fail on old servers,
648          * resulting in "default" policy)
649          */
650         serv_puts("GPEX site");
651         serv_gets(buf);
652         if (buf[0] == '2') {
653                 expire_mode = extract_int(&buf[4], 0);
654                 expire_value = extract_int(&buf[4], 1);
655         }
656
657
658         /* Identification parameters */
659
660         strprompt("Node name", &sc[0][0], 15);
661         strprompt("Fully qualified domain name", &sc[1][0], 63);
662         strprompt("Human readable node name", &sc[2][0], 20);
663         strprompt("Modem dialup number", &sc[3][0], 15);
664         strprompt("Geographic location of this system", &sc[12][0], 31);
665         strprompt("Name of system administrator", &sc[13][0], 25);
666         strprompt("Paginator prompt", &sc[10][0], 79);
667         strprompt("Default moderation filter for new users", &sc[25][0], 4);
668
669         /* Security parameters */
670
671         sprintf(&sc[7][0], "%d", (boolprompt(
672                                     "Require registration for new users",
673                                                     atoi(&sc[7][0]))));
674         strprompt("Initial access level for new users", &sc[6][0], 1);
675         strprompt("Access level required to create rooms", &sc[19][0], 1);
676         sprintf(&sc[4][0], "%d", (boolprompt(
677                                                     "Automatically give room aide privs to a user who creates a private room",
678                                                     atoi(&sc[4][0]))));
679
680         sprintf(&sc[8][0], "%d", (boolprompt(
681                  "Automatically move problem user messages to twit room",
682                                                     atoi(&sc[8][0]))));
683
684         strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
685         sprintf(&sc[11][0], "%d", (boolprompt(
686               "Restrict Internet mail to only those with that privilege",
687                                                      atoi(&sc[11][0]))));
688
689         strprompt("Name of room to log pages", &sc[18][0], ROOMNAMELEN);
690
691
692         /* Server tuning */
693
694         strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
695         strprompt("Maximum concurrent sessions", &sc[14][0], 4);
696         strprompt("Maximum message length", &sc[20][0], 20);
697         strprompt("Minimum number of worker threads", &sc[21][0], 3);
698         strprompt("Maximum number of worker threads", &sc[22][0], 3);
699         strprompt("Server-to-server networking password", &sc[15][0], 19);
700         strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
701         strprompt("SMTP server port (-1 to disable)", &sc[24][0], 5);
702
703
704         /* Expiry settings */
705         strprompt("Default user purge time (days)", &sc[16][0], 5);
706         strprompt("Default room purge time (days)", &sc[17][0], 5);
707
708         /* Angels and demons dancing in my head... */
709         do {
710                 sprintf(buf, "%d", expire_mode);
711                 strprompt("System default message expire policy (? for list)",
712                           buf, 1);
713                 if (buf[0] == '?') {
714                         printf("\n");
715                         printf("1. Never automatically expire messages\n");
716                         printf("2. Expire by message count\n");
717                         printf("3. Expire by message age\n");
718                 }
719         } while ((buf[0] < 49) || (buf[0] > 51));
720         expire_mode = buf[0] - 48;
721
722         /* ...lunatics and monsters underneath my bed */
723         if (expire_mode == 2) {
724                 sprintf(buf, "%d", expire_value);
725                 strprompt("Keep how many messages online?", buf, 10);
726                 expire_value = atol(buf);
727         }
728         if (expire_mode == 3) {
729                 sprintf(buf, "%d", expire_value);
730                 strprompt("Keep messages for how many days?", buf, 10);
731                 expire_value = atol(buf);
732         }
733         /* Save it */
734         printf("Save this configuration? ");
735         if (yesno()) {
736                 serv_puts("CONF set");
737                 serv_gets(buf);
738                 if (buf[0] == '4') {
739                         for (a = 0; a < 26; ++a)
740                                 serv_puts(&sc[a][0]);
741                         serv_puts("000");
742                 }
743                 snprintf(buf, sizeof buf, "SPEX site|%d|%d",
744                          expire_mode, expire_value);
745                 serv_puts(buf);
746                 serv_gets(buf);
747         }
748 }
749
750
751 /*
752  * support function for do_internet_configuration()
753  */
754 void get_inet_rec_type(char *buf) {
755         int sel;
756
757         keyopt(" <1> localhost       (Alias for this computer)\n");
758         keyopt(" <2> gateway domain  (Domain for all Citadel systems)\n");
759         keyopt(" <3> smart-host      (Forward all outbound mail to this host)\n");
760         sel = intprompt("Which one", 1, 1, 3);
761         switch(sel) {
762                 case 1: strcpy(buf, "localhost");
763                         return;
764                 case 2: strcpy(buf, "gatewaydomain");
765                         return;
766                 case 3: strcpy(buf, "smarthost");
767                         return;
768         }
769 }
770
771
772 /*
773  * Internet mail configuration
774  */
775 void do_internet_configuration(void) {
776         char buf[256];
777         int num_recs = 0;
778         char **recs = NULL;
779         char ch;
780         int badkey;
781         int i, j;
782         int quitting = 0;
783         
784
785         sprintf(buf, "CONF getsys|%s", INTERNETCFG);
786         serv_puts(buf);
787         serv_gets(buf);
788         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
789                 ++num_recs;
790                 if (num_recs == 1) recs = malloc(sizeof(char *));
791                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
792                 recs[num_recs-1] = malloc(256);
793                 strcpy(recs[num_recs-1], buf);
794         }
795
796         do {
797                 printf("\n");
798                 color(BRIGHT_WHITE);
799                 printf("### ");
800                 printf("                   Host or domain                  ");
801                 printf("   Record type      \n");
802                 color(DIM_WHITE);
803                 printf("--- ");
804                 printf("-------------------------------------------------- ");
805                 printf("--------------------\n");
806                 for (i=0; i<num_recs; ++i) {
807                 color(DIM_WHITE);
808                 printf("%3d ", i+1);
809                 extract(buf, recs[i], 0);
810                 color(BRIGHT_CYAN);
811                 printf("%-50s ", buf);
812                 extract(buf, recs[i], 1);
813                 color(BRIGHT_MAGENTA);
814                 printf("%-20s\n", buf);
815                 color(DIM_WHITE);
816                 }
817
818                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
819                 switch(ch) {
820                         case 'a':
821                                 ++num_recs;
822                                 if (num_recs == 1)
823                                         recs = malloc(sizeof(char *));
824                                 else recs = realloc(recs,
825                                         (sizeof(char *)) * num_recs);
826                                 newprompt("Enter host name: ",
827                                         buf, 50);
828                                 strcat(buf, "|");
829                                 get_inet_rec_type(&buf[strlen(buf)]);
830                                 recs[num_recs-1] = strdup(buf);
831                                 break;
832                         case 'd':
833                                 i = intprompt("Delete which one",
834                                         1, 1, num_recs) - 1;
835                                 free(recs[i]);
836                                 --num_recs;
837                                 for (j=i; j<num_recs; ++j)
838                                         recs[j] = recs[j+1];
839                                 break;
840                         case 's':
841                                 sprintf(buf, "CONF putsys|%s",
842                                         INTERNETCFG);
843                                 serv_puts(buf);
844                                 serv_gets(buf);
845                                 if (buf[0] == '4') {
846                                         for (i=0; i<num_recs; ++i) {
847                                                 serv_puts(recs[i]);
848                                         }
849                                         serv_puts("000");
850                                 }
851                                 else {
852                                         printf("%s\n", &buf[4]);
853                                 }
854                                 quitting = 1;
855                                 break;
856                         case 'q':
857                                 quitting = boolprompt(
858                                         "Quit without saving", 0);
859                                 break;
860                         default:
861                                 badkey = 1;
862                 }
863         } while (quitting == 0);
864
865         if (recs != NULL) {
866                 for (i=0; i<num_recs; ++i) free(recs[i]);
867                 free(recs);
868         }
869 }