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