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