more sprintf
[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 #include "screen.h"
46
47 void interr(int errnum);
48 void strprompt(char *prompt, char *str, int len);
49 void newprompt(char *prompt, char *str, int len);
50 void sttybbs(int cmd);
51 int inkey(void);
52 void serv_write(char *buf, int nbytes);
53 int haschar(char *st, int ch);
54 void progress(long int curr, long int cmax);
55 int yesno(void);
56
57 extern char temp[];
58 extern char tempdir[];
59 extern char *axdefs[7];
60 extern long highest_msg_read;
61 extern long maxmsgnum;
62 extern unsigned room_flags;
63 extern int screenwidth;
64
65
66 int eopen(char *name, int mode)
67 {
68         int ret;
69         ret = open(name, mode);
70         if (ret < 0) {
71                 err_printf("Cannot open file '%s', mode=%d, errno=%d\n",
72                         name, mode, errno);
73                 interr(errno);
74         }
75         return (ret);
76 }
77
78
79 int room_prompt(int qrflags)
80 {                               /* return proper room prompt character */
81         int a;
82         a = '>';
83         if (qrflags & QR_DIRECTORY)
84                 a = ']';
85         if ((a == ']') && (qrflags & QR_NETWORK))
86                 a = '}';
87         if ((a == '>') && (qrflags & QR_NETWORK))
88                 a = ')';
89         return (a);
90 }
91
92 void entregis(void)
93 {                               /* register with name and address */
94
95         char buf[SIZ];
96         char tmpname[SIZ];
97         char tmpaddr[SIZ];
98         char tmpcity[SIZ];
99         char tmpstate[SIZ];
100         char tmpzip[SIZ];
101         char tmpphone[SIZ];
102         char tmpemail[SIZ];
103         char tmpcountry[SIZ];
104         char diruser[SIZ];
105         char dirnode[SIZ];
106         char holdemail[SIZ];
107         int a;
108         int ok = 0;
109
110         strcpy(tmpname, "");
111         strcpy(tmpaddr, "");
112         strcpy(tmpcity, "");
113         strcpy(tmpstate, "");
114         strcpy(tmpzip, "");
115         strcpy(tmpphone, "");
116         strcpy(tmpemail, "");
117         strcpy(tmpcountry, "");
118
119         serv_puts("GREG _SELF_");
120         serv_gets(buf);
121         if (buf[0] == '1') {
122                 a = 0;
123                 while (serv_gets(buf), strcmp(buf, "000")) {
124                         if (a == 2)
125                                 strcpy(tmpname, buf);
126                         if (a == 3)
127                                 strcpy(tmpaddr, buf);
128                         if (a == 4)
129                                 strcpy(tmpcity, buf);
130                         if (a == 5)
131                                 strcpy(tmpstate, buf);
132                         if (a == 6)
133                                 strcpy(tmpzip, buf);
134                         if (a == 7)
135                                 strcpy(tmpphone, buf);
136                         if (a == 9)
137                                 strcpy(tmpemail, buf);
138                         if (a == 10)
139                                 strcpy(tmpcountry, buf);
140                         ++a;
141                 }
142         }
143         strprompt("REAL name", tmpname, 29);
144         strprompt("Address", tmpaddr, 24);
145         strprompt("City/town", tmpcity, 14);
146         strprompt("State/province", tmpstate, 2);
147         strprompt("ZIP/Postal Code", tmpzip, 10);
148         strprompt("Country", tmpcountry, 31);
149         strprompt("Telephone number", tmpphone, 14);
150
151         do {
152                 ok = 1;
153                 strcpy(holdemail, tmpemail);
154                 strprompt("Email address", tmpemail, 31);
155                 snprintf(buf, sizeof buf, "QDIR %s", tmpemail);
156                 serv_puts(buf);
157                 serv_gets(buf);
158                 if (buf[0]=='2') {
159                         extract_token(diruser, &buf[4], 0, '@');
160                         extract_token(dirnode, &buf[4], 1, '@');
161                         striplt(diruser);
162                         striplt(dirnode);
163                         if ((strcasecmp(diruser, fullname))
164                            || (strcasecmp(dirnode, serv_info.serv_nodename))) {
165                                 scr_printf(
166                                         "\nYou can't use %s as your address.\n",
167                                         tmpemail);
168                                 scr_printf(
169                                         "It is already in use by %s @ %s.\n",
170                                         diruser, dirnode);
171                                 ok = 0;
172                                 strcpy(tmpemail, holdemail);
173                         }
174                 }
175         } while (ok == 0);
176
177         /* now send the registration info back to the server */
178         serv_puts("REGI");
179         serv_gets(buf);
180         if (buf[0] != '4') {
181                 scr_printf("%s\n", &buf[4]);
182                 return;
183         }
184         serv_puts(tmpname);
185         serv_puts(tmpaddr);
186         serv_puts(tmpcity);
187         serv_puts(tmpstate);
188         serv_puts(tmpzip);
189         serv_puts(tmpphone);
190         serv_puts(tmpemail);
191         serv_puts(tmpcountry);
192         serv_puts("000");
193         scr_printf("\n");
194 }
195
196 void updatels(void)
197 {                               /* make all messages old in current room */
198         char buf[SIZ];
199
200         if (rc_alt_semantics) {
201                 if (maxmsgnum == highest_msg_read == 0) {
202                         /* err_printf("maxmsgnum == highest_msg_read == 0\n"); */
203                         return;
204                 }
205                 snprintf(buf, sizeof(buf), "SLRP %ld",
206                                 (maxmsgnum > highest_msg_read) ?
207                                  maxmsgnum : highest_msg_read);
208                 serv_puts(buf);
209         } else {
210                 serv_puts("SLRP HIGHEST");
211         }
212         serv_gets(buf);
213         if (buf[0] != '2')
214                 scr_printf("%s\n", &buf[4]);
215 }
216
217 /*
218  * only make messages old in this room that have been read
219  */
220 void updatelsa(void)
221 {
222         char buf[SIZ];
223
224         snprintf(buf, sizeof buf, "SLRP %ld", highest_msg_read);
225         serv_puts(buf);
226         serv_gets(buf);
227         if (buf[0] != '2')
228                 scr_printf("%s\n", &buf[4]);
229 }
230
231
232 /*
233  * This routine completes a client upload
234  */
235 void do_upload(int fd)
236 {
237         char buf[SIZ];
238         char tbuf[4096];
239         long transmitted_bytes, total_bytes;
240         int bytes_to_send;
241         int bytes_expected;
242
243         /* learn the size of the file */
244         total_bytes = lseek(fd, 0L, 2);
245         lseek(fd, 0L, 0);
246
247         transmitted_bytes = 0L;
248         progress(transmitted_bytes, total_bytes);
249         do {
250                 bytes_to_send = read(fd, tbuf, 4096);
251                 if (bytes_to_send > 0) {
252                         snprintf(buf, sizeof buf, "WRIT %d", bytes_to_send);
253                         serv_puts(buf);
254                         serv_gets(buf);
255                         if (buf[0] == '7') {
256                                 bytes_expected = atoi(&buf[4]);
257                                 serv_write(tbuf, bytes_expected);
258                         } else {
259                                 scr_printf("%s\n", &buf[4]);
260                         }
261                 }
262                 transmitted_bytes = transmitted_bytes + (long) bytes_to_send;
263                 progress(transmitted_bytes, total_bytes);
264         } while (bytes_to_send > 0);
265
266         /* close the upload file, locally and at the server */
267         close(fd);
268         serv_puts("UCLS 1");
269         serv_gets(buf);
270         scr_printf("%s\n", &buf[4]);
271 }
272
273
274 /*
275  * client-based uploads (for users with their own clientware)
276  */
277 void cli_upload(void)
278 {
279         char flnm[SIZ];
280         char desc[151];
281         char buf[SIZ];
282         char tbuf[SIZ];
283         int a;
284         int fd;
285
286         if ((room_flags & QR_UPLOAD) == 0) {
287                 scr_printf("*** You cannot upload to this room.\n");
288                 return;
289         }
290         newprompt("File to be uploaded: ", flnm, 55);
291         fd = open(flnm, O_RDONLY);
292         if (fd < 0) {
293                 scr_printf("Cannot open '%s': %s\n", flnm, strerror(errno));
294                 return;
295         }
296         scr_printf("Enter a description of this file:\n");
297         newprompt(": ", desc, 75);
298
299         /* keep generating filenames in hope of finding a unique one */
300         a = 0;
301         do {
302                 if (a == 10)
303                         return; /* fail if tried 10 times */
304                 strcpy(buf, flnm);
305                 while ((strlen(buf) > 0) && (haschar(buf, '/')))
306                         strcpy(buf, &buf[1]);
307                 if (a > 0) {
308                         size_t tmp = strlen(buf);
309                         snprintf(&buf[tmp], sizeof buf - tmp, "%d", a);
310                 }
311                 snprintf(tbuf, sizeof tbuf, "UOPN %s|%s", buf, desc);
312                 serv_puts(tbuf);
313                 serv_gets(buf);
314                 if (buf[0] != '2')
315                         scr_printf("%s\n", &buf[4]);
316                 ++a;
317         } while (buf[0] != '2');
318
319         /* at this point we have an open upload file at the server */
320         do_upload(fd);
321 }
322
323
324 /*
325  * Function used for various image upload commands
326  */
327 void cli_image_upload(char *keyname)
328 {
329         char flnm[SIZ];
330         char buf[SIZ];
331         int fd;
332
333         snprintf(buf, sizeof buf, "UIMG 0|%s", keyname);
334         serv_puts(buf);
335         serv_gets(buf);
336         if (buf[0] != '2') {
337                 scr_printf("%s\n", &buf[4]);
338                 return;
339         }
340         newprompt("Image file to be uploaded: ", flnm, 55);
341         fd = open(flnm, O_RDONLY);
342         if (fd < 0) {
343                 scr_printf("Cannot open '%s': %s\n", flnm, strerror(errno));
344                 return;
345         }
346         snprintf(buf, sizeof buf, "UIMG 1|%s", keyname);
347         serv_puts(buf);
348         serv_gets(buf);
349         if (buf[0] != '2') {
350                 scr_printf("%s\n", &buf[4]);
351                 return;
352         }
353         do_upload(fd);
354 }
355
356
357 /*
358  * protocol-based uploads (Xmodem, Ymodem, Zmodem)
359  */
360 void upload(int c)
361 {                               /* c = upload mode */
362         char flnm[SIZ];
363         char desc[151];
364         char buf[SIZ];
365         char tbuf[4096];
366         int xfer_pid;
367         int a, b;
368         FILE *fp, *lsfp;
369         int fd;
370
371         if ((room_flags & QR_UPLOAD) == 0) {
372                 scr_printf("*** You cannot upload to this room.\n");
373                 return;
374         }
375         /* we don't need a filename when receiving batch y/z modem */
376         if ((c == 2) || (c == 3))
377                 strcpy(flnm, "x");
378         else
379                 newprompt("Enter filename: ", flnm, 15);
380
381         for (a = 0; a < strlen(flnm); ++a)
382                 if ((flnm[a] == '/') || (flnm[a] == '\\') || (flnm[a] == '>')
383                     || (flnm[a] == '?') || (flnm[a] == '*')
384                     || (flnm[a] == ';') || (flnm[a] == '&'))
385                         flnm[a] = '_';
386
387         newprompt("Enter a short description of the file:\n: ", desc, 150);
388
389         /* create a temporary directory... */
390         if (mkdir(tempdir, 0700) != 0) {
391                 scr_printf("*** Could not create temporary directory %s: %s\n",
392                        tempdir, strerror(errno));
393                 return;
394         }
395         /* now do the transfer ... in a separate process */
396         xfer_pid = fork();
397         if (xfer_pid == 0) {
398                 chdir(tempdir);
399                 switch (c) {
400                 case 0:
401                         sttybbs(0);
402                         scr_printf("Receiving %s - press Ctrl-D to end.\n", flnm);
403                         fp = fopen(flnm, "w");
404                         do {
405                                 b = inkey();
406                                 if (b == 13) {
407                                         b = 10;
408                                 }
409                                 if (b != 4) {
410                                         scr_printf("%c", b);
411                                         putc(b, fp);
412                                 }
413                         } while (b != 4);
414                         fclose(fp);
415                         exit(0);
416                 case 1:
417                         screen_reset();
418                         sttybbs(3);
419                         execlp("rx", "rx", flnm, NULL);
420                         exit(1);
421                 case 2:
422                         screen_reset();
423                         sttybbs(3);
424                         execlp("rb", "rb", NULL);
425                         exit(1);
426                 case 3:
427                         screen_reset();
428                         sttybbs(3);
429                         execlp("rz", "rz", NULL);
430                         exit(1);
431                 }
432         } else
433                 do {
434                         b = ka_wait(&a);
435                 } while ((b != xfer_pid) && (b != (-1)));
436         sttybbs(0);
437         screen_set();
438
439         if (a != 0) {
440                 scr_printf("\r*** Transfer unsuccessful.\n");
441                 nukedir(tempdir);
442                 return;
443         }
444         scr_printf("\r*** Transfer successful.  Sending file(s) to server...\n");
445         snprintf(buf, sizeof buf, "cd %s; ls", tempdir);
446         lsfp = popen(buf, "r");
447         if (lsfp != NULL) {
448                 while (fgets(flnm, sizeof flnm, lsfp) != NULL) {
449                         flnm[strlen(flnm) - 1] = 0;
450                         snprintf(buf, sizeof buf, "%s/%s", tempdir, flnm);
451                         fd = open(buf, O_RDONLY);
452                         if (fd >= 0) {
453                                 a = 0;
454                                 do {
455                                         snprintf(buf, sizeof buf, "UOPN %s|%s", flnm, desc);
456                                         if (a > 0) {
457                                                 size_t tmp = strlen(buf);
458                                                 snprintf(&buf[tmp],
459                                                         sizeof buf - tmp,
460                                                         ".%d", a);
461                                         }
462                                         ++a;
463                                         serv_puts(buf);
464                                         serv_gets(buf);
465                                 } while ((buf[0] != '2') && (a < 100));
466                                 if (buf[0] == '2')
467                                         do {
468                                                 a = read(fd, tbuf, 4096);
469                                                 if (a > 0) {
470                                                         snprintf(buf, sizeof buf, "WRIT %d", a);
471                                                         serv_puts(buf);
472                                                         serv_gets(buf);
473                                                         if (buf[0] == '7')
474                                                                 serv_write(tbuf, a);
475                                                 }
476                                         } while (a > 0);
477                                 close(fd);
478                                 serv_puts("UCLS 1");
479                                 serv_gets(buf);
480                                 scr_printf("%s\n", &buf[4]);
481                         }
482                 }
483                 pclose(lsfp);
484         }
485         nukedir(tempdir);
486 }
487
488 /* 
489  * validate a user
490  */
491 void val_user(char *user, int do_validate)
492 {
493         int a;
494         char cmd[SIZ];
495         char buf[SIZ];
496         int ax = 0;
497
498         snprintf(cmd, sizeof cmd, "GREG %s", user);
499         serv_puts(cmd);
500         serv_gets(cmd);
501         if (cmd[0] == '1') {
502                 a = 0;
503                 do {
504                         serv_gets(buf);
505                         ++a;
506                         if (a == 1)
507                                 scr_printf("User #%s - %s  ", buf, &cmd[4]);
508                         if (a == 2)
509                                 scr_printf("PW: %s\n", buf);
510                         if (a == 3)
511                                 scr_printf("%s\n", buf);
512                         if (a == 4)
513                                 scr_printf("%s\n", buf);
514                         if (a == 5)
515                                 scr_printf("%s, ", buf);
516                         if (a == 6)
517                                 scr_printf("%s ", buf);
518                         if (a == 7)
519                                 scr_printf("%s\n", buf);
520                         if (a == 8)
521                                 scr_printf("%s\n", buf);
522                         if (a == 9)
523                                 ax = atoi(buf);
524                         if (a == 10)
525                                 scr_printf("%s\n", buf);
526                         if (a == 11)
527                                 scr_printf("%s\n", buf);
528                 } while (strcmp(buf, "000"));
529                 scr_printf("Current access level: %d (%s)\n", ax, axdefs[ax]);
530         } else {
531                 scr_printf("%-30s\n%s\n", user, &cmd[4]);
532         }
533
534         if (do_validate) {
535                 /* now set the access level */
536                 ax = intprompt("Access level", ax, 0, 6);
537                 snprintf(cmd, sizeof cmd, "VALI %s|%d", user, ax);
538                 serv_puts(cmd);
539                 serv_gets(cmd);
540                 if (cmd[0] != '2')
541                         scr_printf("%s\n", &cmd[4]);
542         }
543         scr_printf("\n");
544 }
545
546
547 void validate(void)
548 {                               /* validate new users */
549         char cmd[SIZ];
550         char buf[SIZ];
551         int finished = 0;
552
553         do {
554                 serv_puts("GNUR");
555                 serv_gets(cmd);
556                 if (cmd[0] != '3')
557                         finished = 1;
558                 if (cmd[0] == '2')
559                         scr_printf("%s\n", &cmd[4]);
560                 if (cmd[0] == '3') {
561                         extract(buf, cmd, 0);
562                         val_user(&buf[4], 1);
563                 }
564         } while (finished == 0);
565 }
566
567 void subshell(void)
568 {
569         int a, b;
570         a = fork();
571         if (a == 0) {
572                 screen_reset();
573                 sttybbs(SB_RESTORE);
574                 signal(SIGINT, SIG_DFL);
575                 signal(SIGQUIT, SIG_DFL);
576                 execlp(getenv("SHELL"), getenv("SHELL"), NULL);
577                 err_printf("Could not open a shell: %s\n", strerror(errno));
578                 exit(errno);
579         }
580         do {
581                 b = ka_wait(NULL);
582         } while ((a != b) && (a != (-1)));
583         sttybbs(0);
584         screen_set();
585 }
586
587 /*
588  * <.A>ide <F>ile <D>elete command
589  */
590 void deletefile(void)
591 {
592         char filename[32];
593         char cmd[SIZ];
594
595         newprompt("Filename: ", filename, 31);
596         if (strlen(filename) == 0)
597                 return;
598         snprintf(cmd, sizeof cmd, "DELF %s", filename);
599         serv_puts(cmd);
600         serv_gets(cmd);
601         err_printf("%s\n", &cmd[4]);
602 }
603
604 /*
605  * <.A>ide <F>ile <S>end command
606  */
607 void netsendfile(void)
608 {
609         char filename[32], destsys[20], cmd[SIZ];
610
611         newprompt("Filename: ", filename, 31);
612         if (strlen(filename) == 0)
613                 return;
614         newprompt("System to send to: ", destsys, 19);
615         snprintf(cmd, sizeof cmd, "NETF %s|%s", filename, destsys);
616         serv_puts(cmd);
617         serv_gets(cmd);
618         err_printf("%s\n", &cmd[4]);
619         return;
620 }
621
622 /*
623  * <.A>ide <F>ile <M>ove command
624  */
625 void movefile(void)
626 {
627         char filename[64];
628         char newroom[ROOMNAMELEN];
629         char cmd[SIZ];
630
631         newprompt("Filename: ", filename, 63);
632         if (strlen(filename) == 0)
633                 return;
634         newprompt("Enter target room: ", newroom, ROOMNAMELEN - 1);
635
636         snprintf(cmd, sizeof cmd, "MOVF %s|%s", filename, newroom);
637         serv_puts(cmd);
638         serv_gets(cmd);
639         err_printf("%s\n", &cmd[4]);
640 }
641
642
643 /* 
644  * list of users who have filled out a bio
645  */
646 void list_bio(void)
647 {
648         char buf[SIZ];
649         int pos = 1;
650
651         serv_puts("LBIO");
652         serv_gets(buf);
653         if (buf[0] != '1') {
654                 pprintf("%s\n", &buf[4]);
655                 return;
656         }
657         while (serv_gets(buf), strcmp(buf, "000")) {
658                 if ((pos + strlen(buf) + 5) > screenwidth) {
659                         pprintf("\n");
660                         pos = 1;
661                 }
662                 pprintf("%s, ", buf);
663                 pos = pos + strlen(buf) + 2;
664         }
665         pprintf("%c%c  \n\n", 8, 8);
666 }
667
668
669 /*
670  * read bio
671  */
672 void read_bio(void)
673 {
674         char who[SIZ];
675         char buf[SIZ];
676
677         do {
678                 newprompt("Read bio for who ('?' for list) : ", who, 25);
679                 pprintf("\n");
680                 if (!strcmp(who, "?"))
681                         list_bio();
682         } while (!strcmp(who, "?"));
683         snprintf(buf, sizeof buf, "RBIO %s", who);
684         serv_puts(buf);
685         serv_gets(buf);
686         if (buf[0] != '1') {
687                 pprintf("%s\n", &buf[4]);
688                 return;
689         }
690         while (serv_gets(buf), strcmp(buf, "000")) {
691                 pprintf("%s\n", buf);
692         }
693 }
694
695
696 /* 
697  * General system configuration command
698  */
699 void do_system_configuration(void)
700 {
701         char buf[SIZ];
702         char sc[30][SIZ];
703         int expire_mode = 0;
704         int expire_value = 0;
705         int a;
706         int logpages = 0;
707
708         /* Clear out the config buffers */
709         memset(&sc[0][0], 0, sizeof(sc));
710
711         /* Fetch the current config */
712         serv_puts("CONF get");
713         serv_gets(buf);
714         if (buf[0] == '1') {
715                 a = 0;
716                 while (serv_gets(buf), strcmp(buf, "000")) {
717                         if (a < 30) {
718                                 strcpy(&sc[a][0], buf);
719                         }
720                         ++a;
721                 }
722         }
723         /* Fetch the expire policy (this will silently fail on old servers,
724          * resulting in "default" policy)
725          */
726         serv_puts("GPEX site");
727         serv_gets(buf);
728         if (buf[0] == '2') {
729                 expire_mode = extract_int(&buf[4], 0);
730                 expire_value = extract_int(&buf[4], 1);
731         }
732
733
734         /* Identification parameters */
735
736         strprompt("Node name", &sc[0][0], 15);
737         strprompt("Fully qualified domain name", &sc[1][0], 63);
738         strprompt("Human readable node name", &sc[2][0], 20);
739         strprompt("Modem dialup number", &sc[3][0], 15);
740         strprompt("Geographic location of this system", &sc[12][0], 31);
741         strprompt("Name of system administrator", &sc[13][0], 25);
742         strprompt("Paginator prompt", &sc[10][0], 79);
743
744         /* this prompt is commented out until we finish the moderation system
745         strprompt("Default moderation filter for new users", &sc[25][0], 4);
746         */
747
748         /* Security parameters */
749
750         snprintf(sc[7], sizeof sc[7], "%d", (boolprompt(
751                                     "Require registration for new users",
752                                                     atoi(&sc[7][0]))));
753         snprintf(sc[29], sizeof sc[29], "%d", (boolprompt(
754               "Disable self-service user account creation",
755                                                      atoi(&sc[29][0]))));
756         strprompt("Initial access level for new users", &sc[6][0], 1);
757         strprompt("Access level required to create rooms", &sc[19][0], 1);
758         snprintf(sc[4], sizeof sc[4], "%d", (boolprompt(
759                                                     "Automatically give room aide privs to a user who creates a private room",
760                                                     atoi(&sc[4][0]))));
761
762         snprintf(sc[8], sizeof sc[8], "%d", (boolprompt(
763                  "Automatically move problem user messages to twit room",
764                                                     atoi(&sc[8][0]))));
765
766         strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
767         snprintf(sc[11], sizeof sc[11], "%d", (boolprompt(
768               "Restrict Internet mail to only those with that privilege",
769                                                      atoi(&sc[11][0]))));
770         snprintf(sc[26], sizeof sc[26], "%d", (boolprompt(
771               "Allow Aides to Zap (forget) rooms",
772                                                      atoi(&sc[26][0]))));
773
774         if (strlen(&sc[18][0]) > 0) logpages = 1;
775         else logpages = 0;
776         logpages = boolprompt("Log all pages", logpages);
777         if (logpages) {
778                 strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
779         }
780         else {
781                 sc[18][0] = 0;
782         }
783
784
785         /* Server tuning */
786
787         strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
788         strprompt("Maximum concurrent sessions", &sc[14][0], 4);
789         strprompt("Maximum message length", &sc[20][0], 20);
790         strprompt("Minimum number of worker threads", &sc[21][0], 3);
791         strprompt("Maximum number of worker threads", &sc[22][0], 3);
792
793         /* no longer applicable ... deprecated
794         strprompt("Server-to-server networking password", &sc[15][0], 19);
795         */
796
797         strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
798         strprompt("SMTP server port (-1 to disable)", &sc[24][0], 5);
799         strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
800         strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
801
802         /* Expiry settings */
803         strprompt("Default user purge time (days)", &sc[16][0], 5);
804         strprompt("Default room purge time (days)", &sc[17][0], 5);
805
806         /* Angels and demons dancing in my head... */
807         do {
808                 snprintf(buf, sizeof buf, "%d", expire_mode);
809                 strprompt("System default message expire policy (? for list)",
810                           buf, 1);
811                 if (buf[0] == '?') {
812                         scr_printf("\n"
813                                 "1. Never automatically expire messages\n"
814                                 "2. Expire by message count\n"
815                                 "3. Expire by message age\n");
816                 }
817         } while ((buf[0] < 49) || (buf[0] > 51));
818         expire_mode = buf[0] - 48;
819
820         /* ...lunatics and monsters underneath my bed */
821         if (expire_mode == 2) {
822                 snprintf(buf, sizeof buf, "%d", expire_value);
823                 strprompt("Keep how many messages online?", buf, 10);
824                 expire_value = atol(buf);
825         }
826         if (expire_mode == 3) {
827                 snprintf(buf, sizeof buf, "%d", expire_value);
828                 strprompt("Keep messages for how many days?", buf, 10);
829                 expire_value = atol(buf);
830         }
831         /* Save it */
832         scr_printf("Save this configuration? ");
833         if (yesno()) {
834                 serv_puts("CONF set");
835                 serv_gets(buf);
836                 if (buf[0] == '4') {
837                         for (a = 0; a < 30; ++a)
838                                 serv_puts(&sc[a][0]);
839                         serv_puts("000");
840                 }
841                 snprintf(buf, sizeof buf, "SPEX site|%d|%d",
842                          expire_mode, expire_value);
843                 serv_puts(buf);
844                 serv_gets(buf);
845         }
846 }
847
848
849 /*
850  * support function for do_internet_configuration()
851  */
852 void get_inet_rec_type(char *buf) {
853         int sel;
854
855         keyopt(" <1> localhost      (Alias for this computer)\n");
856         keyopt(" <2> gateway domain (Domain for all Citadel systems)\n");
857         keyopt(" <3> smart-host     (Forward all outbound mail to this host)\n");
858         keyopt(" <4> directory      (Consult the Global Address Book)\n");
859         sel = intprompt("Which one", 1, 1, 4);
860         switch(sel) {
861                 case 1: strcpy(buf, "localhost");
862                         return;
863                 case 2: strcpy(buf, "gatewaydomain");
864                         return;
865                 case 3: strcpy(buf, "smarthost");
866                         return;
867                 case 4: strcpy(buf, "directory");
868                         return;
869         }
870 }
871
872
873 /*
874  * Internet mail configuration
875  */
876 void do_internet_configuration(void) {
877         char buf[SIZ];
878         int num_recs = 0;
879         char **recs = NULL;
880         char ch;
881         int badkey;
882         int i, j;
883         int quitting = 0;
884         
885
886         snprintf(buf, sizeof buf, "CONF getsys|%s", INTERNETCFG);
887         serv_puts(buf);
888         serv_gets(buf);
889         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
890                 ++num_recs;
891                 if (num_recs == 1) recs = malloc(sizeof(char *));
892                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
893                 recs[num_recs-1] = malloc(SIZ);
894                 strcpy(recs[num_recs-1], buf);
895         }
896
897         do {
898                 scr_printf("\n");
899                 color(BRIGHT_WHITE);
900                 scr_printf("###                    Host or domain                     Record type      \n");
901                 color(DIM_WHITE);
902                 scr_printf("--- -------------------------------------------------- --------------------\n");
903                 for (i=0; i<num_recs; ++i) {
904                 color(DIM_WHITE);
905                 scr_printf("%3d ", i+1);
906                 extract(buf, recs[i], 0);
907                 color(BRIGHT_CYAN);
908                 scr_printf("%-50s ", buf);
909                 extract(buf, recs[i], 1);
910                 color(BRIGHT_MAGENTA);
911                 scr_printf("%-20s\n", buf);
912                 color(DIM_WHITE);
913                 }
914
915                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
916                 switch(ch) {
917                         case 'a':
918                                 ++num_recs;
919                                 if (num_recs == 1)
920                                         recs = malloc(sizeof(char *));
921                                 else recs = realloc(recs,
922                                         (sizeof(char *)) * num_recs);
923                                 newprompt("Enter host name: ",
924                                         buf, 50);
925                                 strcat(buf, "|");
926                                 get_inet_rec_type(&buf[strlen(buf)]);
927                                 recs[num_recs-1] = strdup(buf);
928                                 break;
929                         case 'd':
930                                 i = intprompt("Delete which one",
931                                         1, 1, num_recs) - 1;
932                                 free(recs[i]);
933                                 --num_recs;
934                                 for (j=i; j<num_recs; ++j)
935                                         recs[j] = recs[j+1];
936                                 break;
937                         case 's':
938                                 snprintf(buf, sizeof buf, "CONF putsys|%s",
939                                         INTERNETCFG);
940                                 serv_puts(buf);
941                                 serv_gets(buf);
942                                 if (buf[0] == '4') {
943                                         for (i=0; i<num_recs; ++i) {
944                                                 serv_puts(recs[i]);
945                                         }
946                                         serv_puts("000");
947                                 }
948                                 else {
949                                         scr_printf("%s\n", &buf[4]);
950                                 }
951                                 quitting = 1;
952                                 break;
953                         case 'q':
954                                 quitting = boolprompt(
955                                         "Quit without saving", 0);
956                                 break;
957                         default:
958                                 badkey = 1;
959                 }
960         } while (quitting == 0);
961
962         if (recs != NULL) {
963                 for (i=0; i<num_recs; ++i) free(recs[i]);
964                 free(recs);
965         }
966 }
967
968
969
970 /*
971  * Edit network configuration for room sharing, mailing lists, etc.
972  */
973 void network_config_management(char *entrytype, char *comment) {
974         char filename[PATH_MAX];
975         char changefile[PATH_MAX];
976         int e_ex_code;
977         pid_t editor_pid;
978         int cksum;
979         int b, i;
980         char buf[SIZ];
981         char instr[SIZ];
982         char addr[SIZ];
983         FILE *tempfp;
984         FILE *changefp;
985
986         if (strlen(editor_path) == 0) {
987                 scr_printf("You must have an external editor configured in"
988                         " order to use this function.\n");
989                 return;
990         }
991
992         snprintf(filename, sizeof filename, "%s.listedit", tmpnam(NULL));
993         snprintf(changefile, sizeof changefile, "%s.listedit", tmpnam(NULL));
994
995         tempfp = fopen(filename, "w");
996         if (tempfp == NULL) {
997                 err_printf("Cannot open %s: %s\n", filename, strerror(errno));
998                 return;
999         }
1000
1001         fprintf(tempfp, "# Configuration for room: %s\n", room_name);
1002         fprintf(tempfp, "# %s\n", comment);
1003         fprintf(tempfp, "# Specify one per line.\n"
1004                         "\n\n");
1005
1006         serv_puts("GNET");
1007         serv_gets(buf);
1008         if (buf[0] == '1') {
1009                 while(serv_gets(buf), strcmp(buf, "000")) {
1010                         extract(instr, buf, 0);
1011                         if (!strcasecmp(instr, entrytype)) {
1012                                 extract(addr, buf, 1);
1013                                 fprintf(tempfp, "%s\n", addr);
1014                         }
1015                 }
1016         }
1017         fclose(tempfp);
1018
1019         e_ex_code = 1;  /* start with a failed exit code */
1020         editor_pid = fork();
1021         cksum = file_checksum(filename);
1022         if (editor_pid == 0) {
1023                 chmod(filename, 0600);
1024                 screen_reset();
1025                 sttybbs(SB_RESTORE);
1026                 putenv("WINDOW_TITLE=Network configuration");
1027                 execlp(editor_path, editor_path, filename, NULL);
1028                 exit(1);
1029         }
1030         if (editor_pid > 0) {
1031                 do {
1032                         e_ex_code = 0;
1033                         b = ka_wait(&e_ex_code);
1034                 } while ((b != editor_pid) && (b >= 0));
1035         editor_pid = (-1);
1036         sttybbs(0);
1037         screen_set();
1038         }
1039
1040         if (file_checksum(filename) == cksum) {
1041                 err_printf("*** Not saving changes.\n");
1042                 e_ex_code = 1;
1043         }
1044
1045         if (e_ex_code == 0) {           /* Save changes */
1046                 changefp = fopen(changefile, "w");
1047                 serv_puts("GNET");
1048                 serv_gets(buf);
1049                 if (buf[0] == '1') {
1050                         while(serv_gets(buf), strcmp(buf, "000")) {
1051                                 extract(instr, buf, 0);
1052                                 if (strcasecmp(instr, entrytype)) {
1053                                         fprintf(changefp, "%s\n", buf);
1054                                 }
1055                         }
1056                 }
1057                 tempfp = fopen(filename, "r");
1058                 while (fgets(buf, sizeof buf, tempfp) != NULL) {
1059                         for (i=0; i<strlen(buf); ++i) {
1060                                 if (buf[i] == '#') buf[i] = 0;
1061                         }
1062                         striplt(buf);
1063                         if (strlen(buf) > 0) {
1064                                 fprintf(changefp, "%s|%s\n", entrytype, buf);
1065                         }
1066                 }
1067                 fclose(tempfp);
1068                 fclose(changefp);
1069
1070                 /* now write it to the server... */
1071                 serv_puts("SNET");
1072                 serv_gets(buf);
1073                 if (buf[0] == '4') {
1074                         changefp = fopen(changefile, "r");
1075                         if (changefp != NULL) {
1076                                 while (fgets(buf, sizeof buf,
1077                                        changefp) != NULL) {
1078                                         buf[strlen(buf) - 1] = 0;
1079                                         serv_puts(buf);
1080                                 }
1081                                 fclose(changefp);
1082                         }
1083                         serv_puts("000");
1084                 }
1085         }
1086
1087         unlink(filename);               /* Delete the temporary files */
1088         unlink(changefile);
1089 }
1090
1091
1092 /*
1093  * IGnet node configuration
1094  */
1095 void do_ignet_configuration(void) {
1096         char buf[SIZ];
1097         int num_recs = 0;
1098         char **recs = NULL;
1099         char ch;
1100         int badkey;
1101         int i, j;
1102         int quitting = 0;
1103         
1104
1105         snprintf(buf, sizeof buf, "CONF getsys|%s", IGNETCFG);
1106         serv_puts(buf);
1107         serv_gets(buf);
1108         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
1109                 ++num_recs;
1110                 if (num_recs == 1) recs = malloc(sizeof(char *));
1111                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
1112                 recs[num_recs-1] = malloc(SIZ);
1113                 strcpy(recs[num_recs-1], buf);
1114         }
1115
1116         do {
1117                 scr_printf("\n");
1118                 color(BRIGHT_WHITE);
1119                 scr_printf(     "### "
1120                         "   Node          "
1121                         "  Secret           "
1122                         "          Host or IP             "
1123                         "Port#\n");
1124                 color(DIM_WHITE);
1125                 scr_printf(     "--- "
1126                         "---------------- "
1127                         "------------------ "
1128                         "-------------------------------- "
1129                         "-----\n");
1130                 for (i=0; i<num_recs; ++i) {
1131                 color(DIM_WHITE);
1132                 scr_printf("%3d ", i+1);
1133                 extract(buf, recs[i], 0);
1134                 color(BRIGHT_CYAN);
1135                 scr_printf("%-16s ", buf);
1136                 extract(buf, recs[i], 1);
1137                 color(BRIGHT_MAGENTA);
1138                 scr_printf("%-18s ", buf);
1139                 extract(buf, recs[i], 2);
1140                 color(BRIGHT_CYAN);
1141                 scr_printf("%-32s ", buf);
1142                 extract(buf, recs[i], 3);
1143                 color(BRIGHT_MAGENTA);
1144                 scr_printf("%-3s\n", buf);
1145                 color(DIM_WHITE);
1146                 }
1147
1148                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
1149                 switch(ch) {
1150                         case 'a':
1151                                 ++num_recs;
1152                                 if (num_recs == 1)
1153                                         recs = malloc(sizeof(char *));
1154                                 else recs = realloc(recs,
1155                                         (sizeof(char *)) * num_recs);
1156                                 newprompt("Enter node name    : ", buf, 16);
1157                                 strcat(buf, "|");
1158                                 newprompt("Enter shared secret: ",
1159                                         &buf[strlen(buf)], 18);
1160                                 strcat(buf, "|");
1161                                 newprompt("Enter host or IP   : ",
1162                                         &buf[strlen(buf)], 32);
1163                                 strcat(buf, "|504");
1164                                 strprompt("Enter port number  : ",
1165                                         &buf[strlen(buf)-3], 5);
1166                                 recs[num_recs-1] = strdup(buf);
1167                                 break;
1168                         case 'd':
1169                                 i = intprompt("Delete which one",
1170                                         1, 1, num_recs) - 1;
1171                                 free(recs[i]);
1172                                 --num_recs;
1173                                 for (j=i; j<num_recs; ++j)
1174                                         recs[j] = recs[j+1];
1175                                 break;
1176                         case 's':
1177                                 snprintf(buf, sizeof buf, "CONF putsys|%s", IGNETCFG);
1178                                 serv_puts(buf);
1179                                 serv_gets(buf);
1180                                 if (buf[0] == '4') {
1181                                         for (i=0; i<num_recs; ++i) {
1182                                                 serv_puts(recs[i]);
1183                                         }
1184                                         serv_puts("000");
1185                                 }
1186                                 else {
1187                                         scr_printf("%s\n", &buf[4]);
1188                                 }
1189                                 quitting = 1;
1190                                 break;
1191                         case 'q':
1192                                 quitting = boolprompt(
1193                                         "Quit without saving", 0);
1194                                 break;
1195                         default:
1196                                 badkey = 1;
1197                 }
1198         } while (quitting == 0);
1199
1200         if (recs != NULL) {
1201                 for (i=0; i<num_recs; ++i) free(recs[i]);
1202                 free(recs);
1203         }
1204 }
1205
1206 /*
1207  * Filter list configuration
1208  */
1209 void do_filterlist_configuration(void) {
1210         char buf[SIZ];
1211         int num_recs = 0;
1212         char **recs = NULL;
1213         char ch;
1214         int badkey;
1215         int i, j;
1216         int quitting = 0;
1217         
1218
1219         snprintf(buf, sizeof buf, "CONF getsys|%s", FILTERLIST);
1220         serv_puts(buf);
1221         serv_gets(buf);
1222         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
1223                 ++num_recs;
1224                 if (num_recs == 1) recs = malloc(sizeof(char *));
1225                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
1226                 recs[num_recs-1] = malloc(SIZ);
1227                 strcpy(recs[num_recs-1], buf);
1228         }
1229
1230         do {
1231                 scr_printf("\n");
1232                 color(BRIGHT_WHITE);
1233                 scr_printf(     "### "
1234                         "         User name           "
1235                         "         Room name           "
1236                         "    Node name    "
1237                         "\n");
1238                 color(DIM_WHITE);
1239                 scr_printf(     "--- "
1240                         "---------------------------- "
1241                         "---------------------------- "
1242                         "---------------- "
1243                         "\n");
1244                 for (i=0; i<num_recs; ++i) {
1245                 color(DIM_WHITE);
1246                 scr_printf("%3d ", i+1);
1247                 extract(buf, recs[i], 0);
1248                 color(BRIGHT_CYAN);
1249                 scr_printf("%-28s ", buf);
1250                 extract(buf, recs[i], 1);
1251                 color(BRIGHT_MAGENTA);
1252                 scr_printf("%-28s ", buf);
1253                 extract(buf, recs[i], 2);
1254                 color(BRIGHT_CYAN);
1255                 scr_printf("%-16s\n", buf);
1256                 extract(buf, recs[i], 3);
1257                 color(DIM_WHITE);
1258                 }
1259
1260                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
1261                 switch(ch) {
1262                         case 'a':
1263                                 ++num_recs;
1264                                 if (num_recs == 1)
1265                                         recs = malloc(sizeof(char *));
1266                                 else recs = realloc(recs,
1267                                         (sizeof(char *)) * num_recs);
1268                                 newprompt("Enter user name: ", buf, 28);
1269                                 strcat(buf, "|");
1270                                 newprompt("Enter room name: ",
1271                                         &buf[strlen(buf)], 28);
1272                                 strcat(buf, "|");
1273                                 newprompt("Enter node name: ",
1274                                         &buf[strlen(buf)], 16);
1275                                 strcat(buf, "|");
1276                                 recs[num_recs-1] = strdup(buf);
1277                                 break;
1278                         case 'd':
1279                                 i = intprompt("Delete which one",
1280                                         1, 1, num_recs) - 1;
1281                                 free(recs[i]);
1282                                 --num_recs;
1283                                 for (j=i; j<num_recs; ++j)
1284                                         recs[j] = recs[j+1];
1285                                 break;
1286                         case 's':
1287                                 snprintf(buf, sizeof buf, "CONF putsys|%s", FILTERLIST);
1288                                 serv_puts(buf);
1289                                 serv_gets(buf);
1290                                 if (buf[0] == '4') {
1291                                         for (i=0; i<num_recs; ++i) {
1292                                                 serv_puts(recs[i]);
1293                                         }
1294                                         serv_puts("000");
1295                                 }
1296                                 else {
1297                                         scr_printf("%s\n", &buf[4]);
1298                                 }
1299                                 quitting = 1;
1300                                 break;
1301                         case 'q':
1302                                 quitting = boolprompt(
1303                                         "Quit without saving", 0);
1304                                 break;
1305                         default:
1306                                 badkey = 1;
1307                 }
1308         } while (quitting == 0);
1309
1310         if (recs != NULL) {
1311                 for (i=0; i<num_recs; ++i) free(recs[i]);
1312                 free(recs);
1313         }
1314 }
1315
1316