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