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