* Network run frequency is now a site-definable setting
[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[29][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 < 29) {
665                                 strcpy(&sc[a][0], buf);
666                         }
667                         ++a;
668                 }
669         }
670         /* Fetch the expire policy (this will silently fail on old servers,
671          * resulting in "default" policy)
672          */
673         serv_puts("GPEX site");
674         serv_gets(buf);
675         if (buf[0] == '2') {
676                 expire_mode = extract_int(&buf[4], 0);
677                 expire_value = extract_int(&buf[4], 1);
678         }
679
680
681         /* Identification parameters */
682
683         strprompt("Node name", &sc[0][0], 15);
684         strprompt("Fully qualified domain name", &sc[1][0], 63);
685         strprompt("Human readable node name", &sc[2][0], 20);
686         strprompt("Modem dialup number", &sc[3][0], 15);
687         strprompt("Geographic location of this system", &sc[12][0], 31);
688         strprompt("Name of system administrator", &sc[13][0], 25);
689         strprompt("Paginator prompt", &sc[10][0], 79);
690
691         /* this prompt is commented out until we finish the moderation system
692         strprompt("Default moderation filter for new users", &sc[25][0], 4);
693         */
694
695         /* Security parameters */
696
697         sprintf(&sc[7][0], "%d", (boolprompt(
698                                     "Require registration for new users",
699                                                     atoi(&sc[7][0]))));
700         strprompt("Initial access level for new users", &sc[6][0], 1);
701         strprompt("Access level required to create rooms", &sc[19][0], 1);
702         sprintf(&sc[4][0], "%d", (boolprompt(
703                                                     "Automatically give room aide privs to a user who creates a private room",
704                                                     atoi(&sc[4][0]))));
705
706         sprintf(&sc[8][0], "%d", (boolprompt(
707                  "Automatically move problem user messages to twit room",
708                                                     atoi(&sc[8][0]))));
709
710         strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
711         sprintf(&sc[11][0], "%d", (boolprompt(
712               "Restrict Internet mail to only those with that privilege",
713                                                      atoi(&sc[11][0]))));
714         sprintf(&sc[26][0], "%d", (boolprompt(
715               "Allow Aides to Zap (forget) rooms",
716                                                      atoi(&sc[26][0]))));
717
718         if (strlen(&sc[18][0]) > 0) logpages = 1;
719         else logpages = 0;
720         logpages = boolprompt("Log all pages", logpages);
721         if (logpages) {
722                 strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
723         }
724         else {
725                 sc[18][0] = 0;
726         }
727
728
729         /* Server tuning */
730
731         strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
732         strprompt("Maximum concurrent sessions", &sc[14][0], 4);
733         strprompt("Maximum message length", &sc[20][0], 20);
734         strprompt("Minimum number of worker threads", &sc[21][0], 3);
735         strprompt("Maximum number of worker threads", &sc[22][0], 3);
736
737         /* no longer applicable ... deprecated
738         strprompt("Server-to-server networking password", &sc[15][0], 19);
739         */
740
741         strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
742         strprompt("SMTP server port (-1 to disable)", &sc[24][0], 5);
743         strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
744         strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
745
746         /* Expiry settings */
747         strprompt("Default user purge time (days)", &sc[16][0], 5);
748         strprompt("Default room purge time (days)", &sc[17][0], 5);
749
750         /* Angels and demons dancing in my head... */
751         do {
752                 sprintf(buf, "%d", expire_mode);
753                 strprompt("System default message expire policy (? for list)",
754                           buf, 1);
755                 if (buf[0] == '?') {
756                         printf("\n");
757                         printf("1. Never automatically expire messages\n");
758                         printf("2. Expire by message count\n");
759                         printf("3. Expire by message age\n");
760                 }
761         } while ((buf[0] < 49) || (buf[0] > 51));
762         expire_mode = buf[0] - 48;
763
764         /* ...lunatics and monsters underneath my bed */
765         if (expire_mode == 2) {
766                 sprintf(buf, "%d", expire_value);
767                 strprompt("Keep how many messages online?", buf, 10);
768                 expire_value = atol(buf);
769         }
770         if (expire_mode == 3) {
771                 sprintf(buf, "%d", expire_value);
772                 strprompt("Keep messages for how many days?", buf, 10);
773                 expire_value = atol(buf);
774         }
775         /* Save it */
776         printf("Save this configuration? ");
777         if (yesno()) {
778                 serv_puts("CONF set");
779                 serv_gets(buf);
780                 if (buf[0] == '4') {
781                         for (a = 0; a < 29; ++a)
782                                 serv_puts(&sc[a][0]);
783                         serv_puts("000");
784                 }
785                 snprintf(buf, sizeof buf, "SPEX site|%d|%d",
786                          expire_mode, expire_value);
787                 serv_puts(buf);
788                 serv_gets(buf);
789         }
790 }
791
792
793 /*
794  * support function for do_internet_configuration()
795  */
796 void get_inet_rec_type(char *buf) {
797         int sel;
798
799         keyopt(" <1> localhost       (Alias for this computer)\n");
800         keyopt(" <2> gateway domain  (Domain for all Citadel systems)\n");
801         keyopt(" <3> smart-host      (Forward all outbound mail to this host)\n");
802         sel = intprompt("Which one", 1, 1, 3);
803         switch(sel) {
804                 case 1: strcpy(buf, "localhost");
805                         return;
806                 case 2: strcpy(buf, "gatewaydomain");
807                         return;
808                 case 3: strcpy(buf, "smarthost");
809                         return;
810         }
811 }
812
813
814 /*
815  * Internet mail configuration
816  */
817 void do_internet_configuration(void) {
818         char buf[SIZ];
819         int num_recs = 0;
820         char **recs = NULL;
821         char ch;
822         int badkey;
823         int i, j;
824         int quitting = 0;
825         
826
827         sprintf(buf, "CONF getsys|%s", INTERNETCFG);
828         serv_puts(buf);
829         serv_gets(buf);
830         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
831                 ++num_recs;
832                 if (num_recs == 1) recs = malloc(sizeof(char *));
833                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
834                 recs[num_recs-1] = malloc(SIZ);
835                 strcpy(recs[num_recs-1], buf);
836         }
837
838         do {
839                 printf("\n");
840                 color(BRIGHT_WHITE);
841                 printf("### ");
842                 printf("                   Host or domain                  ");
843                 printf("   Record type      \n");
844                 color(DIM_WHITE);
845                 printf("--- ");
846                 printf("-------------------------------------------------- ");
847                 printf("--------------------\n");
848                 for (i=0; i<num_recs; ++i) {
849                 color(DIM_WHITE);
850                 printf("%3d ", i+1);
851                 extract(buf, recs[i], 0);
852                 color(BRIGHT_CYAN);
853                 printf("%-50s ", buf);
854                 extract(buf, recs[i], 1);
855                 color(BRIGHT_MAGENTA);
856                 printf("%-20s\n", buf);
857                 color(DIM_WHITE);
858                 }
859
860                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
861                 switch(ch) {
862                         case 'a':
863                                 ++num_recs;
864                                 if (num_recs == 1)
865                                         recs = malloc(sizeof(char *));
866                                 else recs = realloc(recs,
867                                         (sizeof(char *)) * num_recs);
868                                 newprompt("Enter host name: ",
869                                         buf, 50);
870                                 strcat(buf, "|");
871                                 get_inet_rec_type(&buf[strlen(buf)]);
872                                 recs[num_recs-1] = strdup(buf);
873                                 break;
874                         case 'd':
875                                 i = intprompt("Delete which one",
876                                         1, 1, num_recs) - 1;
877                                 free(recs[i]);
878                                 --num_recs;
879                                 for (j=i; j<num_recs; ++j)
880                                         recs[j] = recs[j+1];
881                                 break;
882                         case 's':
883                                 sprintf(buf, "CONF putsys|%s",
884                                         INTERNETCFG);
885                                 serv_puts(buf);
886                                 serv_gets(buf);
887                                 if (buf[0] == '4') {
888                                         for (i=0; i<num_recs; ++i) {
889                                                 serv_puts(recs[i]);
890                                         }
891                                         serv_puts("000");
892                                 }
893                                 else {
894                                         printf("%s\n", &buf[4]);
895                                 }
896                                 quitting = 1;
897                                 break;
898                         case 'q':
899                                 quitting = boolprompt(
900                                         "Quit without saving", 0);
901                                 break;
902                         default:
903                                 badkey = 1;
904                 }
905         } while (quitting == 0);
906
907         if (recs != NULL) {
908                 for (i=0; i<num_recs; ++i) free(recs[i]);
909                 free(recs);
910         }
911 }
912
913
914
915 /*
916  * Edit network configuration for room sharing, mailing lists, etc.
917  */
918 void network_config_management(char *entrytype, char *comment) {
919         char filename[PATH_MAX];
920         char changefile[PATH_MAX];
921         int e_ex_code;
922         pid_t editor_pid;
923         int cksum;
924         int b, i;
925         char buf[SIZ];
926         char instr[SIZ];
927         char addr[SIZ];
928         FILE *tempfp;
929         FILE *changefp;
930
931         if (strlen(editor_path) == 0) {
932                 printf("You must have an external editor configured in order"
933                         " to use this function.\n");
934                 return;
935         }
936
937         snprintf(filename, sizeof filename, "%s.listedit", tmpnam(NULL));
938         snprintf(changefile, sizeof changefile, "%s.listedit", tmpnam(NULL));
939
940         tempfp = fopen(filename, "w");
941         if (tempfp == NULL) {
942                 printf("Cannot open %s: %s\n", filename, strerror(errno));
943                 return;
944         }
945
946         fprintf(tempfp, "# Configuration for room: %s\n", room_name);
947         fprintf(tempfp, "# %s\n", comment);
948         fprintf(tempfp, "# Specify one per line.\n"
949                         "\n\n");
950
951         serv_puts("GNET");
952         serv_gets(buf);
953         if (buf[0] == '1') {
954                 while(serv_gets(buf), strcmp(buf, "000")) {
955                         extract(instr, buf, 0);
956                         if (!strcasecmp(instr, entrytype)) {
957                                 extract(addr, buf, 1);
958                                 fprintf(tempfp, "%s\n", addr);
959                         }
960                 }
961         }
962         fclose(tempfp);
963
964         e_ex_code = 1;  /* start with a failed exit code */
965         editor_pid = fork();
966         cksum = file_checksum(filename);
967         if (editor_pid == 0) {
968                 chmod(filename, 0600);
969                 sttybbs(SB_RESTORE);
970                 execlp(editor_path, editor_path, filename, NULL);
971                 exit(1);
972         }
973         if (editor_pid > 0) {
974                 do {
975                         e_ex_code = 0;
976                         b = ka_wait(&e_ex_code);
977                 } while ((b != editor_pid) && (b >= 0));
978         editor_pid = (-1);
979         sttybbs(0);
980         }
981
982         if (file_checksum(filename) == cksum) {
983                 printf("*** Not saving changes.\n");
984                 e_ex_code = 1;
985         }
986
987         if (e_ex_code == 0) {           /* Save changes */
988                 changefp = fopen(changefile, "w");
989                 serv_puts("GNET");
990                 serv_gets(buf);
991                 if (buf[0] == '1') {
992                         while(serv_gets(buf), strcmp(buf, "000")) {
993                                 extract(instr, buf, 0);
994                                 if (strcasecmp(instr, entrytype)) {
995                                         fprintf(changefp, "%s\n", buf);
996                                 }
997                         }
998                 }
999                 tempfp = fopen(filename, "r");
1000                 while (fgets(buf, sizeof buf, tempfp) != NULL) {
1001                         for (i=0; i<strlen(buf); ++i) {
1002                                 if (buf[i] == '#') buf[i] = 0;
1003                         }
1004                         striplt(buf);
1005                         if (strlen(buf) > 0) {
1006                                 fprintf(changefp, "%s|%s\n", entrytype, buf);
1007                         }
1008                 }
1009                 fclose(tempfp);
1010                 fclose(changefp);
1011
1012                 /* now write it to the server... */
1013                 serv_puts("SNET");
1014                 serv_gets(buf);
1015                 if (buf[0] == '4') {
1016                         changefp = fopen(changefile, "r");
1017                         if (changefp != NULL) {
1018                                 while (fgets(buf, sizeof buf,
1019                                        changefp) != NULL) {
1020                                         buf[strlen(buf) - 1] = 0;
1021                                         serv_puts(buf);
1022                                 }
1023                                 fclose(changefp);
1024                         }
1025                         serv_puts("000");
1026                 }
1027         }
1028
1029         unlink(filename);               /* Delete the temporary files */
1030         unlink(changefile);
1031 }
1032
1033
1034 /*
1035  * IGnet node configuration
1036  */
1037 void do_ignet_configuration(void) {
1038         char buf[SIZ];
1039         int num_recs = 0;
1040         char **recs = NULL;
1041         char ch;
1042         int badkey;
1043         int i, j;
1044         int quitting = 0;
1045         
1046
1047         sprintf(buf, "CONF getsys|%s", IGNETCFG);
1048         serv_puts(buf);
1049         serv_gets(buf);
1050         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
1051                 ++num_recs;
1052                 if (num_recs == 1) recs = malloc(sizeof(char *));
1053                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
1054                 recs[num_recs-1] = malloc(SIZ);
1055                 strcpy(recs[num_recs-1], buf);
1056         }
1057
1058         do {
1059                 printf("\n");
1060                 color(BRIGHT_WHITE);
1061                 printf( "### "
1062                         "   Node          "
1063                         "  Secret         "
1064                         "          Host or IP             "
1065                         "Port#\n");
1066                 color(DIM_WHITE);
1067                 printf( "--- "
1068                         "---------------- "
1069                         "---------------- "
1070                         "-------------------------------- "
1071                         "-----\n");
1072                 for (i=0; i<num_recs; ++i) {
1073                 color(DIM_WHITE);
1074                 printf("%3d ", i+1);
1075                 extract(buf, recs[i], 0);
1076                 color(BRIGHT_CYAN);
1077                 printf("%-16s ", buf);
1078                 extract(buf, recs[i], 1);
1079                 color(BRIGHT_MAGENTA);
1080                 printf("%-16s ", buf);
1081                 extract(buf, recs[i], 2);
1082                 color(BRIGHT_CYAN);
1083                 printf("%-32s ", buf);
1084                 extract(buf, recs[i], 3);
1085                 color(BRIGHT_MAGENTA);
1086                 printf("%-3s\n", buf);
1087                 color(DIM_WHITE);
1088                 }
1089
1090                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
1091                 switch(ch) {
1092                         case 'a':
1093                                 ++num_recs;
1094                                 if (num_recs == 1)
1095                                         recs = malloc(sizeof(char *));
1096                                 else recs = realloc(recs,
1097                                         (sizeof(char *)) * num_recs);
1098                                 newprompt("Enter node name    : ", buf, 16);
1099                                 strcat(buf, "|");
1100                                 newprompt("Enter shared secret: ",
1101                                         &buf[strlen(buf)], 16);
1102                                 strcat(buf, "|");
1103                                 newprompt("Enter host or IP   : ",
1104                                         &buf[strlen(buf)], 32);
1105                                 strcat(buf, "|504");
1106                                 strprompt("Enter port number  : ",
1107                                         &buf[strlen(buf)-3], 5);
1108                                 recs[num_recs-1] = strdup(buf);
1109                                 break;
1110                         case 'd':
1111                                 i = intprompt("Delete which one",
1112                                         1, 1, num_recs) - 1;
1113                                 free(recs[i]);
1114                                 --num_recs;
1115                                 for (j=i; j<num_recs; ++j)
1116                                         recs[j] = recs[j+1];
1117                                 break;
1118                         case 's':
1119                                 sprintf(buf, "CONF putsys|%s", IGNETCFG);
1120                                 serv_puts(buf);
1121                                 serv_gets(buf);
1122                                 if (buf[0] == '4') {
1123                                         for (i=0; i<num_recs; ++i) {
1124                                                 serv_puts(recs[i]);
1125                                         }
1126                                         serv_puts("000");
1127                                 }
1128                                 else {
1129                                         printf("%s\n", &buf[4]);
1130                                 }
1131                                 quitting = 1;
1132                                 break;
1133                         case 'q':
1134                                 quitting = boolprompt(
1135                                         "Quit without saving", 0);
1136                                 break;
1137                         default:
1138                                 badkey = 1;
1139                 }
1140         } while (quitting == 0);
1141
1142         if (recs != NULL) {
1143                 for (i=0; i<num_recs; ++i) free(recs[i]);
1144                 free(recs);
1145         }
1146 }
1147
1148