9cacb68b05e8567703003f361e38d1a019d9d76a
[citadel.git] / citadel / tuiconfig.c
1 /* $Id:  $
2  *
3  * Configuration screens that are part of the text mode client.
4  *
5  */
6
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <limits.h>
16
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
19 # include <time.h>
20 #else
21 # if HAVE_SYS_TIME_H
22 #  include <sys/time.h>
23 # else
24 #  include <time.h>
25 # endif
26 #endif
27
28 #include <signal.h>
29 #include <pwd.h>
30 #include <errno.h>
31 #include <stdarg.h>
32 #include <libcitadel.h>
33 #include "sysdep.h"
34 #include "citadel.h"
35 #include "citadel_ipc.h"
36 #include "citadel_decls.h"
37 #include "tuiconfig.h"
38 #include "messages.h"
39 #include "routines.h"
40 #include "commands.h"
41 #ifndef HAVE_SNPRINTF
42 #include "snprintf.h"
43 #endif
44 #include "screen.h"
45
46 /* work around solaris include files */
47 #ifdef reg
48 #undef reg
49 #endif
50
51 extern char temp[];
52 extern char tempdir[];
53 extern char *axdefs[8];
54 extern long highest_msg_read;
55 extern long maxmsgnum;
56 extern unsigned room_flags;
57 extern int screenwidth;
58
59
60 /* 
61  * General system configuration command
62  */
63 void do_system_configuration(CtdlIPC *ipc)
64 {
65
66 #define NUM_CONFIGS 61
67
68         char buf[256];
69         char sc[NUM_CONFIGS][256];
70         char *resp = NULL;
71         struct ExpirePolicy *site_expirepolicy = NULL;
72         struct ExpirePolicy *mbx_expirepolicy = NULL;
73         int a;
74         int logpages = 0;
75         int r;                  /* IPC response code */
76         int server_configs = 0;
77
78         /* Clear out the config buffers */
79         memset(&sc[0][0], 0, sizeof(sc));
80
81         /* Fetch the current config */
82         r = CtdlIPCGetSystemConfig(ipc, &resp, buf);
83         if (r / 100 == 1) {
84                 server_configs = num_tokens(resp, '\n');
85                 for (a=0; a<server_configs; ++a) {
86                         if (a < NUM_CONFIGS) {
87                                 extract_token(&sc[a][0], resp, a, '\n', sizeof sc[a]);
88                         }
89                 }
90         }
91         if (resp) free(resp);
92         resp = NULL;
93         /* Fetch the expire policy (this will silently fail on old servers,
94          * resulting in "default" policy)
95          */
96         r = CtdlIPCGetMessageExpirationPolicy(ipc, 2, &site_expirepolicy, buf);
97         r = CtdlIPCGetMessageExpirationPolicy(ipc, 3, &mbx_expirepolicy, buf);
98
99         /* Identification parameters */
100
101         strprompt("Node name", &sc[0][0], 15);
102         strprompt("Fully qualified domain name", &sc[1][0], 63);
103         strprompt("Human readable node name", &sc[2][0], 20);
104         strprompt("Telephone number", &sc[3][0], 15);
105         strprompt("Geographic location of this system", &sc[12][0], 31);
106         strprompt("Name of system administrator", &sc[13][0], 25);
107         strprompt("Paginator prompt", &sc[10][0], 79);
108
109         /* Security parameters */
110
111         snprintf(sc[7], sizeof sc[7], "%d", (boolprompt(
112                 "Require registration for new users",
113                 atoi(&sc[7][0]))));
114         snprintf(sc[29], sizeof sc[29], "%d", (boolprompt(
115                 "Disable self-service user account creation",
116                 atoi(&sc[29][0]))));
117         strprompt("Initial access level for new users", &sc[6][0], 1);
118         strprompt("Access level required to create rooms", &sc[19][0], 1);
119         snprintf(sc[4], sizeof sc[4], "%d", (boolprompt(
120                 "Automatically give room aide privs to a user who creates a private room",
121                 atoi(&sc[4][0]))));
122
123         snprintf(sc[8], sizeof sc[8], "%d", (boolprompt(
124                 "Automatically move problem user messages to twit room",
125                 atoi(&sc[8][0]))));
126
127         strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
128         snprintf(sc[11], sizeof sc[11], "%d", (boolprompt(
129                 "Restrict Internet mail to only those with that privilege",
130                 atoi(&sc[11][0]))));
131         snprintf(sc[26], sizeof sc[26], "%d", (boolprompt(
132                 "Allow Aides to Zap (forget) rooms",
133                 atoi(&sc[26][0]))));
134
135         if (!IsEmptyStr(&sc[18][0])) logpages = 1;
136         else logpages = 0;
137         logpages = boolprompt("Log all pages", logpages);
138         if (logpages) {
139                 strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
140         }
141         else {
142                 sc[18][0] = 0;
143         }
144
145         /* Commented out because this setting isn't really appropriate to
146          * change while the server is running.
147          *
148          * snprintf(sc[52], sizeof sc[52], "%d", (boolprompt(
149          *      "Use system authentication",
150          *      atoi(&sc[52][0]))));
151          */
152
153         /* Server tuning */
154
155         strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
156         strprompt("Maximum concurrent sessions", &sc[14][0], 4);
157         strprompt("Maximum message length", &sc[20][0], 20);
158         strprompt("Minimum number of worker threads", &sc[21][0], 3);
159         strprompt("Maximum number of worker threads", &sc[22][0], 3);
160         snprintf(sc[43], sizeof sc[43], "%d", (boolprompt(
161                 "Automatically delete committed database logs",
162                 atoi(&sc[43][0]))));
163
164         strprompt("Server IP address (0.0.0.0 for 'any')", &sc[37][0], 15);
165         strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
166         strprompt("POP3S server port (-1 to disable)", &sc[40][0], 5);
167         strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
168         strprompt("IMAPS server port (-1 to disable)", &sc[39][0], 5);
169         strprompt("SMTP MTA server port (-1 to disable)", &sc[24][0], 5);
170         strprompt("SMTP MSA server port (-1 to disable)", &sc[38][0], 5);
171         strprompt("SMTPS server port (-1 to disable)", &sc[41][0], 5);
172         strprompt("Postfix TCP Dictionary Port server port (-1 to disable)", &sc[50][0], 5);
173         strprompt("ManageSieve server port (-1 to disable)", &sc[51][0], 5);
174         /* This logic flips the question around, because it's one of those
175          * situations where 0=yes and 1=no
176          */
177         a = atoi(sc[25]);
178         a = (a ? 0 : 1);
179         a = boolprompt("Correct forged From: lines during authenticated SMTP",
180                 a);
181         a = (a ? 0 : 1);
182         snprintf(sc[25], sizeof sc[25], "%d", a);
183         snprintf(sc[45], sizeof sc[45], "%d", (boolprompt(
184                 "Allow unauthenticated SMTP clients to spoof my domains",
185                 atoi(&sc[45][0]))));
186         snprintf(sc[57], sizeof sc[57], "%d", (boolprompt(
187                 "Perform RBL checks at greeting instead of after RCPT",
188                 atoi(&sc[57][0]))));
189         snprintf(sc[44], sizeof sc[44], "%d", (boolprompt(
190                 "Instantly expunge deleted IMAP messages",
191                 atoi(&sc[44][0]))));
192
193         /* LDAP settings */
194         if (ipc->ServInfo.supports_ldap) {
195                 a = strlen(&sc[32][0]);
196                 a = (a ? 1 : 0);        /* Set only to 1 or 0 */
197                 a = boolprompt("Connect this Citadel to an external LDAP directory", a);
198                 if (a) {
199                         strprompt("Host name of LDAP server",
200                                 &sc[32][0], 127);
201                         strprompt("Port number of LDAP service",
202                                 &sc[33][0], 5);
203                         strprompt("Base DN", &sc[34][0], 255);
204                         strprompt("Bind DN", &sc[35][0], 255);
205                         strprompt("Password for bind DN", &sc[36][0], 255);
206                 }
207                 else {
208                         strcpy(&sc[32][0], "");
209                 }
210         }
211
212         /* Expiry settings */
213         strprompt("Default user purge time (days)", &sc[16][0], 5);
214         strprompt("Default room purge time (days)", &sc[17][0], 5);
215
216         /* Angels and demons dancing in my head... */
217         do {
218                 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_mode);
219                 strprompt("System default message expire policy (? for list)",
220                           buf, 1);
221                 if (buf[0] == '?') {
222                         scr_printf("\n"
223                                 "1. Never automatically expire messages\n"
224                                 "2. Expire by message count\n"
225                                 "3. Expire by message age\n");
226                 }
227         } while ((buf[0] < '1') || (buf[0] > '3'));
228         site_expirepolicy->expire_mode = buf[0] - '0';
229
230         /* ...lunatics and monsters underneath my bed */
231         if (site_expirepolicy->expire_mode == 2) {
232                 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
233                 strprompt("Keep how many messages online?", buf, 10);
234                 site_expirepolicy->expire_value = atol(buf);
235         }
236         if (site_expirepolicy->expire_mode == 3) {
237                 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
238                 strprompt("Keep messages for how many days?", buf, 10);
239                 site_expirepolicy->expire_value = atol(buf);
240         }
241
242         /* Media messiahs preying on my fears... */
243         do {
244                 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_mode);
245                 strprompt("Mailbox default message expire policy (? for list)",
246                           buf, 1);
247                 if (buf[0] == '?') {
248                         scr_printf("\n"
249                                 "0. Go with the system default\n"
250                                 "1. Never automatically expire messages\n"
251                                 "2. Expire by message count\n"
252                                 "3. Expire by message age\n");
253                 }
254         } while ((buf[0] < '0') || (buf[0] > '3'));
255         mbx_expirepolicy->expire_mode = buf[0] - '0';
256
257         /* ...Pop culture prophets playing in my ears */
258         if (mbx_expirepolicy->expire_mode == 2) {
259                 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
260                 strprompt("Keep how many messages online?", buf, 10);
261                 mbx_expirepolicy->expire_value = atol(buf);
262         }
263         if (mbx_expirepolicy->expire_mode == 3) {
264                 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
265                 strprompt("Keep messages for how many days?", buf, 10);
266                 mbx_expirepolicy->expire_value = atol(buf);
267         }
268
269         strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
270         strprompt("Hour to run purges (0-23)", &sc[31][0], 2);
271         snprintf(sc[42], sizeof sc[42], "%d", (boolprompt(
272                 "Enable full text search index (warning: resource intensive)",
273                 atoi(&sc[42][0]))));
274
275         snprintf(sc[46], sizeof sc[46], "%d", (boolprompt(
276                 "Perform journaling of email messages",
277                 atoi(&sc[46][0]))));
278         snprintf(sc[47], sizeof sc[47], "%d", (boolprompt(
279                 "Perform journaling of non-email messages",
280                 atoi(&sc[47][0]))));
281         if ( (atoi(&sc[46][0])) || (atoi(&sc[47][0])) ) {
282                 strprompt("Email destination of journalized messages",
283                         &sc[48][0], 127);
284         }
285
286         /* Funambol push stuff */
287         int yes_funambol = 0;
288         if (strlen(sc[53]) > 0) yes_funambol = 1;
289         yes_funambol = boolprompt("Connect to an external Funambol sync server", yes_funambol);
290         if (yes_funambol) {
291                 strprompt("Funambol server (blank to disable)", &sc[53][0], 63);
292                 strprompt("Funambol server port", &sc[54][0], 5);
293                 strprompt("Funambol sync source", &sc[55][0], 63);
294                 strprompt("Funambol authentication details (user:pass in Base64)", &sc[56][0],63);
295         }
296         else {
297                 sc[53][0] = 0;
298                 sc[54][0] = 0;
299                 sc[55][0] = 0;
300                 sc[56][0] = 0;
301         }
302
303         /* External pager stuff */
304         int yes_pager = 0;
305         if (strlen(sc[60]) > 0) yes_pager = 1;
306         yes_pager = boolprompt("Configure an external pager tool", yes_pager);
307         if (yes_pager) {
308                 strprompt("External pager tool", &sc[60][0], 255);
309         }
310         else {
311                 sc[60][0] = 0;
312         }
313
314         /* Master user account */
315         int yes_muacct = 0;
316         if (strlen(sc[58]) > 0) yes_muacct = 1;
317         yes_muacct = boolprompt("Enable a 'master user' account", yes_muacct);
318         if (yes_muacct) {
319                 strprompt("Master user name", &sc[58][0], 31);
320                 strprompt("Master user password", &sc[59][0], -31);
321         }
322         else {
323                 strcpy(&sc[58][0], "");
324                 strcpy(&sc[59][0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
325         }
326
327         /* Save it */
328         scr_printf("Save this configuration? ");
329         if (yesno()) {
330                 r = 1;
331                 for (a = 0; a < NUM_CONFIGS; a++)
332                         r += 1 + strlen(sc[a]);
333                 resp = (char *)calloc(1, r);
334                 if (!resp) {
335                         err_printf("Can't save config - out of memory!\n");
336                         logoff(ipc, 1);
337                 }
338                 for (a = 0; a < NUM_CONFIGS; a++) {
339                         strcat(resp, sc[a]);
340                         strcat(resp, "\n");
341                 }
342                 r = CtdlIPCSetSystemConfig(ipc, resp, buf);
343                 if (r / 100 != 4) {
344                         err_printf("%s\n", buf);
345                 }
346                 free(resp);
347
348                 r = CtdlIPCSetMessageExpirationPolicy(ipc, 2, site_expirepolicy, buf);
349                 if (r / 100 != 2) {
350                         err_printf("%s\n", buf);
351                 }
352
353                 r = CtdlIPCSetMessageExpirationPolicy(ipc, 3, mbx_expirepolicy, buf);
354                 if (r / 100 != 2) {
355                         err_printf("%s\n", buf);
356                 }
357
358         }
359     if (site_expirepolicy) free(site_expirepolicy);
360     if (mbx_expirepolicy) free(mbx_expirepolicy);
361 }
362
363
364 /*
365  * support function for do_internet_configuration()
366  */
367 void get_inet_rec_type(CtdlIPC *ipc, char *buf) {
368         int sel;
369
370         keyopt(" <1> localhost      (Alias for this computer)\n");
371         keyopt(" <2> gateway domain (Domain for all Citadel systems)\n");
372         keyopt(" <3> smart-host     (Forward all outbound mail to this host)\n");
373         keyopt(" <4> directory      (Consult the Global Address Book)\n");
374         keyopt(" <5> SpamAssassin   (Address of SpamAssassin server)\n");
375         keyopt(" <6> RBL            (domain suffix of spam hunting RBL)\n");
376         keyopt(" <7> masq domains   (Domains as which users are allowed to masquerade)\n");
377         sel = intprompt("Which one", 1, 1, 6);
378         switch(sel) {
379                 case 1: strcpy(buf, "localhost");
380                         return;
381                 case 2: strcpy(buf, "gatewaydomain");
382                         return;
383                 case 3: strcpy(buf, "smarthost");
384                         return;
385                 case 4: strcpy(buf, "directory");
386                         return;
387                 case 5: strcpy(buf, "spamassassin");
388                         return;
389                 case 6: strcpy(buf, "rbl");
390                         return;
391                 case 7: strcpy(buf, "masqdomain");
392                         return;
393         }
394 }
395
396
397 /*
398  * Internet mail configuration
399  */
400 void do_internet_configuration(CtdlIPC *ipc)
401 {
402         char buf[256];
403         char *resp = NULL;
404         int num_recs = 0;
405         char **recs = NULL;
406         char ch;
407         int badkey;
408         int i, j;
409         int quitting = 0;
410         int modified = 0;
411         int r;
412         
413         r = CtdlIPCGetSystemConfigByType(ipc, INTERNETCFG, &resp, buf);
414         if (r / 100 == 1) {
415                 while (!IsEmptyStr(resp)) {
416                         extract_token(buf, resp, 0, '\n', sizeof buf);
417                         remove_token(resp, 0, '\n');
418                         ++num_recs;
419                         if (num_recs == 1) recs = malloc(sizeof(char *));
420                         else recs = realloc(recs, (sizeof(char *)) * num_recs);
421                         recs[num_recs-1] = malloc(strlen(buf) + 1);
422                         strcpy(recs[num_recs-1], buf);
423                 }
424         }
425         if (resp) free(resp);
426
427         do {
428                 scr_printf("\n");
429                 color(BRIGHT_WHITE);
430                 scr_printf("###                    Host or domain                     Record type      \n");
431                 color(DIM_WHITE);
432                 scr_printf("--- -------------------------------------------------- --------------------\n");
433                 for (i=0; i<num_recs; ++i) {
434                 color(DIM_WHITE);
435                 scr_printf("%3d ", i+1);
436                 extract_token(buf, recs[i], 0, '|', sizeof buf);
437                 color(BRIGHT_CYAN);
438                 scr_printf("%-50s ", buf);
439                 extract_token(buf, recs[i], 1, '|', sizeof buf);
440                 color(BRIGHT_MAGENTA);
441                 scr_printf("%-20s\n", buf);
442                 color(DIM_WHITE);
443                 }
444
445                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
446                 switch(ch) {
447                         case 'a':
448                                 newprompt("Enter host name: ",
449                                         buf, 50);
450                                 striplt(buf);
451                                 if (!IsEmptyStr(buf)) {
452                                         ++num_recs;
453                                         if (num_recs == 1)
454                                                 recs = malloc(sizeof(char *));
455                                         else recs = realloc(recs,
456                                                 (sizeof(char *)) * num_recs);
457                                         strcat(buf, "|");
458                                         get_inet_rec_type(ipc,
459                                                         &buf[strlen(buf)]);
460                                         recs[num_recs-1] = strdup(buf);
461                                 }
462                                 modified = 1;
463                                 break;
464                         case 'd':
465                                 i = intprompt("Delete which one",
466                                         1, 1, num_recs) - 1;
467                                 free(recs[i]);
468                                 --num_recs;
469                                 for (j=i; j<num_recs; ++j)
470                                         recs[j] = recs[j+1];
471                                 modified = 1;
472                                 break;
473                         case 's':
474                                 r = 1;
475                                 for (i = 0; i < num_recs; i++)
476                                         r += 1 + strlen(recs[i]);
477                                 resp = (char *)calloc(1, r);
478                                 if (!resp) {
479                                         err_printf("Can't save config - out of memory!\n");
480                                         logoff(ipc, 1);
481                                 }
482                                 if (num_recs) for (i = 0; i < num_recs; i++) {
483                                         strcat(resp, recs[i]);
484                                         strcat(resp, "\n");
485                                 }
486                                 r = CtdlIPCSetSystemConfigByType(ipc, INTERNETCFG, resp, buf);
487                                 if (r / 100 != 4) {
488                                         err_printf("%s\n", buf);
489                                 } else {
490                                         scr_printf("Wrote %d records.\n", num_recs);
491                                         modified = 0;
492                                 }
493                 free(resp);
494                                 break;
495                         case 'q':
496                                 quitting = !modified || boolprompt(
497                                         "Quit without saving", 0);
498                                 break;
499                         default:
500                                 badkey = 1;
501                 }
502         } while (!quitting);
503
504         if (recs != NULL) {
505                 for (i=0; i<num_recs; ++i) free(recs[i]);
506                 free(recs);
507         }
508 }
509
510
511
512 /*
513  * Edit network configuration for room sharing, mailing lists, etc.
514  */
515 void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment)
516 {
517         char filename[PATH_MAX];
518         char changefile[PATH_MAX];
519         int e_ex_code;
520         pid_t editor_pid;
521         int cksum;
522         int b, i, tokens;
523         char buf[1024];
524         char instr[1024];
525         char addr[1024];
526         FILE *tempfp;
527         FILE *changefp;
528         char *listing = NULL;
529         int r;
530
531         if (IsEmptyStr(editor_paths[0])) {
532                 scr_printf("You must have an external editor configured in"
533                         " order to use this function.\n");
534                 return;
535         }
536
537         CtdlMakeTempFileName(filename, sizeof filename);
538         CtdlMakeTempFileName(changefile, sizeof changefile);
539
540         tempfp = fopen(filename, "w");
541         if (tempfp == NULL) {
542                 err_printf("Cannot open %s: %s\n", filename, strerror(errno));
543                 return;
544         }
545
546         fprintf(tempfp, "# Configuration for room: %s\n", room_name);
547         fprintf(tempfp, "# %s\n", comment);
548         fprintf(tempfp, "# Specify one per line.\n"
549                         "\n\n");
550
551         r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
552         if (r / 100 == 1) {
553                 while(listing && !IsEmptyStr(listing)) {
554                         extract_token(buf, listing, 0, '\n', sizeof buf);
555                         remove_token(listing, 0, '\n');
556                         extract_token(instr, buf, 0, '|', sizeof instr);
557                         if (!strcasecmp(instr, entrytype)) {
558                                 tokens = num_tokens(buf, '|');
559                                 for (i=1; i<tokens; ++i) {
560                                         extract_token(addr, buf, i, '|', sizeof addr);
561                                         fprintf(tempfp, "%s", addr);
562                                         if (i < (tokens-1)) {
563                                                 fprintf(tempfp, "|");
564                                         }
565                                 }
566                                 fprintf(tempfp, "\n");
567                         }
568                 }
569         }
570         if (listing) {
571                 free(listing);
572                 listing = NULL;
573         }
574         fclose(tempfp);
575
576         e_ex_code = 1;  /* start with a failed exit code */
577         screen_reset();
578         stty_ctdl(SB_RESTORE);
579         editor_pid = fork();
580         cksum = file_checksum(filename);
581         if (editor_pid == 0) {
582                 chmod(filename, 0600);
583                 putenv("WINDOW_TITLE=Network configuration");
584                 execlp(editor_paths[0], editor_paths[0], filename, NULL);
585                 exit(1);
586         }
587         if (editor_pid > 0) {
588                 do {
589                         e_ex_code = 0;
590                         b = ka_wait(&e_ex_code);
591                 } while ((b != editor_pid) && (b >= 0));
592         editor_pid = (-1);
593         stty_ctdl(0);
594         screen_set();
595         }
596
597         if (file_checksum(filename) == cksum) {
598                 err_printf("*** No changes to save.\n");
599                 e_ex_code = 1;
600         }
601
602         if (e_ex_code == 0) {           /* Save changes */
603                 changefp = fopen(changefile, "w");
604
605                 /* Load all netconfig entries that are *not* of the type we are editing */
606                 r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
607                 if (r / 100 == 1) {
608                         while(listing && !IsEmptyStr(listing)) {
609                                 extract_token(buf, listing, 0, '\n', sizeof buf);
610                                 remove_token(listing, 0, '\n');
611                                 extract_token(instr, buf, 0, '|', sizeof instr);
612                                 if (strcasecmp(instr, entrytype)) {
613                                         fprintf(changefp, "%s\n", buf);
614                                 }
615                         }
616                 }
617                 if (listing) {
618                         free(listing);
619                         listing = NULL;
620                 }
621
622                 /* ...and merge that with the data we just edited */
623                 tempfp = fopen(filename, "r");
624                 while (fgets(buf, sizeof buf, tempfp) != NULL) {
625                         for (i=0; i<strlen(buf); ++i) {
626                                 if (buf[i] == '#') buf[i] = 0;
627                         }
628                         striplt(buf);
629                         if (!IsEmptyStr(buf)) {
630                                 fprintf(changefp, "%s|%s\n", entrytype, buf);
631                         }
632                 }
633                 fclose(tempfp);
634                 fclose(changefp);
635
636                 /* now write it to the server... */
637                 changefp = fopen(changefile, "r");
638                 if (changefp != NULL) {
639                         listing = load_message_from_file(changefp);
640                         if (listing) {
641                                 r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
642                                 free(listing);
643                                 listing = NULL;
644                         }
645                         fclose(changefp);
646                 }
647         }
648
649         unlink(filename);               /* Delete the temporary files */
650         unlink(changefile);
651 }
652
653
654 /*
655  * IGnet node configuration
656  */
657 void do_ignet_configuration(CtdlIPC *ipc) {
658         char buf[SIZ];
659         int num_recs = 0;
660         char **recs = NULL;
661         char ch;
662         int badkey;
663         int i, j;
664         int quitting = 0;
665         int modified = 0;
666         char *listing = NULL;
667         int r;
668
669         r = CtdlIPCGetSystemConfigByType(ipc, IGNETCFG, &listing, buf);
670         if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
671                 extract_token(buf, listing, 0, '\n', sizeof buf);
672                 remove_token(listing, 0, '\n');
673
674                 ++num_recs;
675                 if (num_recs == 1) recs = malloc(sizeof(char *));
676                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
677                 recs[num_recs-1] = malloc(SIZ);
678                 strcpy(recs[num_recs-1], buf);
679         }
680         if (listing) free(listing);
681
682         do {
683                 scr_printf("\n");
684                 color(BRIGHT_WHITE);
685                 scr_printf(     "### "
686                         "   Node          "
687                         "  Secret           "
688                         "          Host or IP             "
689                         "Port#\n");
690                 color(DIM_WHITE);
691                 scr_printf(     "--- "
692                         "---------------- "
693                         "------------------ "
694                         "-------------------------------- "
695                         "-----\n");
696                 for (i=0; i<num_recs; ++i) {
697                 color(DIM_WHITE);
698                 scr_printf("%3d ", i+1);
699                 extract_token(buf, recs[i], 0, '|', sizeof buf);
700                 color(BRIGHT_CYAN);
701                 scr_printf("%-16s ", buf);
702                 extract_token(buf, recs[i], 1, '|', sizeof buf);
703                 color(BRIGHT_MAGENTA);
704                 scr_printf("%-18s ", buf);
705                 extract_token(buf, recs[i], 2, '|', sizeof buf);
706                 color(BRIGHT_CYAN);
707                 scr_printf("%-32s ", buf);
708                 extract_token(buf, recs[i], 3, '|', sizeof buf);
709                 color(BRIGHT_MAGENTA);
710                 scr_printf("%-3s\n", buf);
711                 color(DIM_WHITE);
712                 }
713                 scr_printf("\n");
714
715                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
716                 switch(ch) {
717                         case 'a':
718                                 ++num_recs;
719                                 if (num_recs == 1)
720                                         recs = malloc(sizeof(char *));
721                                 else recs = realloc(recs,
722                                         (sizeof(char *)) * num_recs);
723                                 newprompt("Enter node name    : ", buf, 16);
724                                 strcat(buf, "|");
725                                 newprompt("Enter shared secret: ",
726                                         &buf[strlen(buf)], 18);
727                                 strcat(buf, "|");
728                                 newprompt("Enter host or IP   : ",
729                                         &buf[strlen(buf)], 32);
730                                 strcat(buf, "|504");
731                                 strprompt("Enter port number  : ",
732                                         &buf[strlen(buf)-3], 5);
733                                 recs[num_recs-1] = strdup(buf);
734                                 modified = 1;
735                                 break;
736                         case 'd':
737                                 i = intprompt("Delete which one",
738                                         1, 1, num_recs) - 1;
739                                 free(recs[i]);
740                                 --num_recs;
741                                 for (j=i; j<num_recs; ++j)
742                                         recs[j] = recs[j+1];
743                                 modified = 1;
744                                 break;
745                         case 's':
746                                 r = 1;
747                                 for (i = 0; i < num_recs; ++i)
748                                         r += 1 + strlen(recs[i]);
749                                 listing = (char*) calloc(1, r);
750                                 if (!listing) {
751                                         err_printf("Can't save config - out of memory!\n");
752                                         logoff(ipc, 1);
753                                 }
754                                 if (num_recs) for (i = 0; i < num_recs; ++i) {
755                                         strcat(listing, recs[i]);
756                                         strcat(listing, "\n");
757                                 }
758                                 r = CtdlIPCSetSystemConfigByType(ipc, IGNETCFG, listing, buf);
759                                 if (r / 100 != 4) {
760                                         scr_printf("%s\n", buf);
761                                 } else {
762                                         scr_printf("Wrote %d records.\n", num_recs);
763                                         modified = 0;
764                                 }
765                 free(listing);
766                                 break;
767                         case 'q':
768                                 quitting = !modified || boolprompt(
769                                         "Quit without saving", 0);
770                                 break;
771                         default:
772                                 badkey = 1;
773                 }
774         } while (!quitting);
775
776         if (recs != NULL) {
777                 for (i=0; i<num_recs; ++i) free(recs[i]);
778                 free(recs);
779         }
780 }
781
782
783 /*
784  * Filter list configuration
785  */
786 void do_filterlist_configuration(CtdlIPC *ipc)
787 {
788         char buf[SIZ];
789         int num_recs = 0;
790         char **recs = NULL;
791         char ch;
792         int badkey;
793         int i, j;
794         int quitting = 0;
795         int modified = 0;
796         char *listing = NULL;
797         int r;
798
799         r = CtdlIPCGetSystemConfigByType(ipc, FILTERLIST, &listing, buf);
800         if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
801                 extract_token(buf, listing, 0, '\n', sizeof buf);
802                 remove_token(listing, 0, '\n');
803
804                 ++num_recs;
805                 if (num_recs == 1) recs = malloc(sizeof(char *));
806                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
807                 recs[num_recs-1] = malloc(SIZ);
808                 strcpy(recs[num_recs-1], buf);
809         }
810         if (listing) free(listing);
811
812         do {
813                 scr_printf("\n");
814                 color(BRIGHT_WHITE);
815                 scr_printf(     "### "
816                         "         User name           "
817                         "         Room name           "
818                         "    Node name    "
819                         "\n");
820                 color(DIM_WHITE);
821                 scr_printf(     "--- "
822                         "---------------------------- "
823                         "---------------------------- "
824                         "---------------- "
825                         "\n");
826                 for (i=0; i<num_recs; ++i) {
827                 color(DIM_WHITE);
828                 scr_printf("%3d ", i+1);
829                 extract_token(buf, recs[i], 0, '|', sizeof buf);
830                 color(BRIGHT_CYAN);
831                 scr_printf("%-28s ", buf);
832                 extract_token(buf, recs[i], 1, '|', sizeof buf);
833                 color(BRIGHT_MAGENTA);
834                 scr_printf("%-28s ", buf);
835                 extract_token(buf, recs[i], 2, '|', sizeof buf);
836                 color(BRIGHT_CYAN);
837                 scr_printf("%-16s\n", buf);
838                 extract_token(buf, recs[i], 3, '|', sizeof buf);
839                 color(DIM_WHITE);
840                 }
841
842                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
843                 switch(ch) {
844                         case 'a':
845                                 ++num_recs;
846                                 if (num_recs == 1)
847                                         recs = malloc(sizeof(char *));
848                                 else recs = realloc(recs,
849                                         (sizeof(char *)) * num_recs);
850                                 newprompt("Enter user name: ", buf, 28);
851                                 strcat(buf, "|");
852                                 newprompt("Enter room name: ",
853                                         &buf[strlen(buf)], 28);
854                                 strcat(buf, "|");
855                                 newprompt("Enter node name: ",
856                                         &buf[strlen(buf)], 16);
857                                 strcat(buf, "|");
858                                 recs[num_recs-1] = strdup(buf);
859                                 modified = 1;
860                                 break;
861                         case 'd':
862                                 i = intprompt("Delete which one",
863                                         1, 1, num_recs) - 1;
864                                 free(recs[i]);
865                                 --num_recs;
866                                 for (j=i; j<num_recs; ++j)
867                                         recs[j] = recs[j+1];
868                                 modified = 1;
869                                 break;
870                         case 's':
871                                 r = 1;
872                                 for (i = 0; i < num_recs; ++i)
873                                         r += 1 + strlen(recs[i]);
874                                 listing = (char*) calloc(1, r);
875                                 if (!listing) {
876                                         err_printf("Can't save config - out of memory!\n");
877                                         logoff(ipc, 1);
878                                 }
879                                 if (num_recs) for (i = 0; i < num_recs; ++i) {
880                                         strcat(listing, recs[i]);
881                                         strcat(listing, "\n");
882                                 }
883                                 r = CtdlIPCSetSystemConfigByType(ipc, FILTERLIST, listing, buf);
884                                 if (r / 100 != 4) {
885                                         scr_printf("%s\n", buf);
886                                 } else {
887                                         scr_printf("Wrote %d records.\n", num_recs);
888                                         modified = 0;
889                                 }
890                 free(listing);
891                                 break;
892                         case 'q':
893                                 quitting = !modified || boolprompt(
894                                         "Quit without saving", 0);
895                                 break;
896                         default:
897                                 badkey = 1;
898                 }
899         } while (!quitting);
900
901         if (recs != NULL) {
902                 for (i=0; i<num_recs; ++i) free(recs[i]);
903                 free(recs);
904         }
905 }
906
907
908
909
910 /*
911  * POP3 aggregation client configuration
912  */
913 void do_pop3client_configuration(CtdlIPC *ipc)
914 {
915         char buf[SIZ];
916         int num_recs = 0;
917         char **recs = NULL;
918         char ch;
919         int badkey;
920         int i, j;
921         int quitting = 0;
922         int modified = 0;
923         char *listing = NULL;
924         char *other_listing = NULL;
925         int r;
926         char instr[SIZ];
927
928         r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
929         if (r / 100 == 1) {
930                 while(listing && !IsEmptyStr(listing)) {
931                         extract_token(buf, listing, 0, '\n', sizeof buf);
932                         remove_token(listing, 0, '\n');
933                         extract_token(instr, buf, 0, '|', sizeof instr);
934                         if (!strcasecmp(instr, "pop3client")) {
935
936                                 ++num_recs;
937                                 if (num_recs == 1) recs = malloc(sizeof(char *));
938                                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
939                                 recs[num_recs-1] = malloc(SIZ);
940                                 strcpy(recs[num_recs-1], buf);
941
942                         }
943                 }
944         }
945         if (listing) {
946                 free(listing);
947                 listing = NULL;
948         }
949
950         do {
951                 scr_printf("\n");
952                 color(BRIGHT_WHITE);
953                 scr_printf(     "### "
954                         "      Remote POP3 host       "
955                         "         User name           "
956                         "Keep on server? "
957                         "\n");
958                 color(DIM_WHITE);
959                 scr_printf(     "--- "
960                         "---------------------------- "
961                         "---------------------------- "
962                         "--------------- "
963                         "\n");
964                 for (i=0; i<num_recs; ++i) {
965                 color(DIM_WHITE);
966                 scr_printf("%3d ", i+1);
967
968                 extract_token(buf, recs[i], 1, '|', sizeof buf);
969                 color(BRIGHT_CYAN);
970                 scr_printf("%-28s ", buf);
971
972                 extract_token(buf, recs[i], 2, '|', sizeof buf);
973                 color(BRIGHT_MAGENTA);
974                 scr_printf("%-28s ", buf);
975
976                 color(BRIGHT_CYAN);
977                 scr_printf("%-15s\n", (extract_int(recs[i], 4) ? "Yes" : "No") );
978                 color(DIM_WHITE);
979                 }
980
981                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
982                 switch(ch) {
983                         case 'a':
984                                 ++num_recs;
985                                 if (num_recs == 1) {
986                                         recs = malloc(sizeof(char *));
987                                 }
988                                 else {
989                                         recs = realloc(recs, (sizeof(char *)) * num_recs);
990                                 }
991                                 strcpy(buf, "pop3client|");
992                                 newprompt("Enter host name: ", &buf[strlen(buf)], 28);
993                                 strcat(buf, "|");
994                                 newprompt("Enter user name: ", &buf[strlen(buf)], 28);
995                                 strcat(buf, "|");
996                                 newprompt("Enter password : ", &buf[strlen(buf)], 16);
997                                 strcat(buf, "|");
998                                 scr_printf("Keep messages on server instead of deleting them? ");
999                                 sprintf(&buf[strlen(buf)], "%d", yesno());
1000                                 strcat(buf, "|");
1001                                 recs[num_recs-1] = strdup(buf);
1002                                 modified = 1;
1003                                 break;
1004                         case 'd':
1005                                 i = intprompt("Delete which one",
1006                                         1, 1, num_recs) - 1;
1007                                 free(recs[i]);
1008                                 --num_recs;
1009                                 for (j=i; j<num_recs; ++j)
1010                                         recs[j] = recs[j+1];
1011                                 modified = 1;
1012                                 break;
1013                         case 's':
1014                                 r = 1;
1015                                 for (i = 0; i < num_recs; ++i) {
1016                                         r += 1 + strlen(recs[i]);
1017                                 }
1018                                 listing = (char*) calloc(1, r);
1019                                 if (!listing) {
1020                                         err_printf("Can't save config - out of memory!\n");
1021                                         logoff(ipc, 1);
1022                                 }
1023                                 if (num_recs) for (i = 0; i < num_recs; ++i) {
1024                                         strcat(listing, recs[i]);
1025                                         strcat(listing, "\n");
1026                                 }
1027
1028                                 /* Retrieve all the *other* records for merging */
1029                                 r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
1030                                 if (r / 100 == 1) {
1031                                         for (i=0; i<num_tokens(other_listing, '\n'); ++i) {
1032                                                 extract_token(buf, other_listing, i, '\n', sizeof buf);
1033                                                 if (strncasecmp(buf, "pop3client|", 11)) {
1034                                                         listing = realloc(listing, strlen(listing) +
1035                                                                 strlen(buf) + 10);
1036                                                         strcat(listing, buf);
1037                                                         strcat(listing, "\n");
1038                                                 }
1039                                         }
1040                                 }
1041                                 free(other_listing);
1042                                 r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
1043                                 free(listing);
1044                                 listing = NULL;
1045
1046                                 if (r / 100 != 4) {
1047                                         scr_printf("%s\n", buf);
1048                                 } else {
1049                                         scr_printf("Wrote %d records.\n", num_recs);
1050                                         modified = 0;
1051                                 }
1052                                 quitting = 1;
1053                                 break;
1054                         case 'q':
1055                                 quitting = !modified || boolprompt(
1056                                         "Quit without saving", 0);
1057                                 break;
1058                         default:
1059                                 badkey = 1;
1060                 }
1061         } while (!quitting);
1062
1063         if (recs != NULL) {
1064                 for (i=0; i<num_recs; ++i) free(recs[i]);
1065                 free(recs);
1066         }
1067 }
1068
1069
1070
1071
1072
1073
1074 /*
1075  * RSS feed retrieval client configuration
1076  */
1077 void do_rssclient_configuration(CtdlIPC *ipc)
1078 {
1079         char buf[SIZ];
1080         int num_recs = 0;
1081         char **recs = NULL;
1082         char ch;
1083         int badkey;
1084         int i, j;
1085         int quitting = 0;
1086         int modified = 0;
1087         char *listing = NULL;
1088         char *other_listing = NULL;
1089         int r;
1090         char instr[SIZ];
1091
1092         r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
1093         if (r / 100 == 1) {
1094                 while(listing && !IsEmptyStr(listing)) {
1095                         extract_token(buf, listing, 0, '\n', sizeof buf);
1096                         remove_token(listing, 0, '\n');
1097                         extract_token(instr, buf, 0, '|', sizeof instr);
1098                         if (!strcasecmp(instr, "rssclient")) {
1099
1100                                 ++num_recs;
1101                                 if (num_recs == 1) recs = malloc(sizeof(char *));
1102                                 else recs = realloc(recs, (sizeof(char *)) * num_recs);
1103                                 recs[num_recs-1] = malloc(SIZ);
1104                                 strcpy(recs[num_recs-1], buf);
1105
1106                         }
1107                 }
1108         }
1109         if (listing) {
1110                 free(listing);
1111                 listing = NULL;
1112         }
1113
1114         do {
1115                 scr_printf("\n");
1116                 color(BRIGHT_WHITE);
1117                 scr_printf("### Feed URL\n");
1118                 color(DIM_WHITE);
1119                 scr_printf("--- "
1120                         "---------------------------------------------------------------------------"
1121                         "\n");
1122                 
1123                 for (i=0; i<num_recs; ++i) {
1124                 color(DIM_WHITE);
1125                 scr_printf("%3d ", i+1);
1126
1127                 extract_token(buf, recs[i], 1, '|', sizeof buf);
1128                 color(BRIGHT_CYAN);
1129                 scr_printf("%-75s\n", buf);
1130
1131                 color(DIM_WHITE);
1132                 }
1133
1134                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
1135                 switch(ch) {
1136                         case 'a':
1137                                 ++num_recs;
1138                                 if (num_recs == 1) {
1139                                         recs = malloc(sizeof(char *));
1140                                 }
1141                                 else {
1142                                         recs = realloc(recs, (sizeof(char *)) * num_recs);
1143                                 }
1144                                 strcpy(buf, "rssclient|");
1145                                 newprompt("Enter feed URL: ", &buf[strlen(buf)], 75);
1146                                 strcat(buf, "|");
1147                                 recs[num_recs-1] = strdup(buf);
1148                                 modified = 1;
1149                                 break;
1150                         case 'd':
1151                                 i = intprompt("Delete which one", 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                                 modified = 1;
1157                                 break;
1158                         case 's':
1159                                 r = 1;
1160                                 for (i = 0; i < num_recs; ++i) {
1161                                         r += 1 + strlen(recs[i]);
1162                                 }
1163                                 listing = (char*) calloc(1, r);
1164                                 if (!listing) {
1165                                         err_printf("Can't save config - out of memory!\n");
1166                                         logoff(ipc, 1);
1167                                 }
1168                                 if (num_recs) for (i = 0; i < num_recs; ++i) {
1169                                         strcat(listing, recs[i]);
1170                                         strcat(listing, "\n");
1171                                 }
1172
1173                                 /* Retrieve all the *other* records for merging */
1174                                 r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
1175                                 if (r / 100 == 1) {
1176                                         for (i=0; i<num_tokens(other_listing, '\n'); ++i) {
1177                                                 extract_token(buf, other_listing, i, '\n', sizeof buf);
1178                                                 if (strncasecmp(buf, "rssclient|", 10)) {
1179                                                         listing = realloc(listing, strlen(listing) +
1180                                                                 strlen(buf) + 10);
1181                                                         strcat(listing, buf);
1182                                                         strcat(listing, "\n");
1183                                                 }
1184                                         }
1185                                 }
1186                                 free(other_listing);
1187                                 r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
1188                                 free(listing);
1189                                 listing = NULL;
1190
1191                                 if (r / 100 != 4) {
1192                                         scr_printf("%s\n", buf);
1193                                 } else {
1194                                         scr_printf("Wrote %d records.\n", num_recs);
1195                                         modified = 0;
1196                                 }
1197                                 quitting = 1;
1198                                 break;
1199                         case 'q':
1200                                 quitting = !modified || boolprompt(
1201                                         "Quit without saving", 0);
1202                                 break;
1203                         default:
1204                                 badkey = 1;
1205                 }
1206         } while (!quitting);
1207
1208         if (recs != NULL) {
1209                 for (i=0; i<num_recs; ++i) free(recs[i]);
1210                 free(recs);
1211         }
1212 }
1213
1214