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