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