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