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