48e009750acb22fad6966044455ae5bd103cc8a1
[citadel.git] / textclient / tuiconfig.c
1 /*
2  * Configuration screens that are part of the text mode client.
3  *
4  * Copyright (c) 1987-2018 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 "textclient.h"
16
17 extern char temp[];
18 extern char tempdir[];
19 extern char *axdefs[8];
20 extern long highest_msg_read;
21 extern long maxmsgnum;
22 extern unsigned room_flags;
23 extern int screenwidth;
24 char editor_path[PATH_MAX];
25
26
27 /* 
28  * General system configuration command
29  */
30 void do_system_configuration(CtdlIPC * ipc)
31 {
32
33         /* NUM_CONFIGS is now defined in citadel.h */
34
35         char buf[256];
36         char sc[NUM_CONFIGS][256];
37         char *resp = NULL;
38         struct ExpirePolicy *site_expirepolicy = NULL;
39         struct ExpirePolicy *mbx_expirepolicy = NULL;
40         int a;
41         int logpages = 0;
42         int r;                  /* IPC response code */
43         int server_configs = 0;
44
45         /* Clear out the config buffers */
46         memset(&sc[0][0], 0, sizeof(sc));
47
48         /* Fetch the current config */
49         r = CtdlIPCGetSystemConfig(ipc, &resp, buf);
50         if (r / 100 == 1) {
51                 server_configs = num_tokens(resp, '\n');
52                 for (a = 0; a < server_configs; ++a) {
53                         if (a < NUM_CONFIGS) {
54                                 extract_token(&sc[a][0], resp, a, '\n', sizeof sc[a]);
55                         }
56                 }
57         }
58         if (resp)
59                 free(resp);
60         resp = NULL;
61         /* Fetch the expire policy (this will silently fail on old servers,
62          * resulting in "default" policy)
63          */
64         r = CtdlIPCGetMessageExpirationPolicy(ipc, 2, &site_expirepolicy, buf);
65         r = CtdlIPCGetMessageExpirationPolicy(ipc, 3, &mbx_expirepolicy, buf);
66
67         /* Identification parameters */
68
69         strprompt("Node name", &sc[0][0], 15);
70         strprompt("Fully qualified domain name", &sc[1][0], 63);
71         strprompt("Human readable node name", &sc[2][0], 20);
72         strprompt("Telephone number", &sc[3][0], 15);
73         strprompt("Geographic location of this system", &sc[12][0], 31);
74         strprompt("Name of system administrator", &sc[13][0], 25);
75         strprompt("Paginator prompt", &sc[10][0], 79);
76
77         /* Security parameters */
78
79         snprintf(sc[7], sizeof sc[7], "%d", (boolprompt("Require registration for new users", atoi(&sc[7][0]))));
80         snprintf(sc[29], sizeof sc[29], "%d", (boolprompt("Disable self-service user account creation", atoi(&sc[29][0]))));
81         strprompt("Initial access level for new users", &sc[6][0], 1);
82         strprompt("Access level required to create rooms", &sc[19][0], 1);
83         snprintf(sc[67], sizeof sc[67], "%d", (boolprompt("Allow anonymous guest logins", atoi(&sc[67][0]))));
84         snprintf(sc[4], sizeof sc[4], "%d", (boolprompt("Automatically give room admin privs to a user who creates a private room",
85                                                         atoi(&sc[4][0]))));
86
87         snprintf(sc[8], sizeof sc[8], "%d", (boolprompt("Automatically move problem user messages to twit room", atoi(&sc[8][0]))));
88
89         strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
90         snprintf(sc[11], sizeof sc[11], "%d", (boolprompt("Restrict Internet mail to only those with that privilege",
91                                                           atoi(&sc[11][0]))));
92         snprintf(sc[26], sizeof sc[26], "%d", (boolprompt("Allow admins to Zap (forget) rooms", atoi(&sc[26][0]))));
93
94         if (!IsEmptyStr(&sc[18][0])) {
95                 logpages = 1;
96         } else {
97                 logpages = 0;
98         }
99         logpages = boolprompt("Log all instant messages", logpages);
100         if (logpages) {
101                 strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
102         } else {
103                 sc[18][0] = 0;
104         }
105
106         /* Commented out because this setting isn't really appropriate to
107          * change while the server is running.
108          *
109          * snprintf(sc[52], sizeof sc[52], "%d", (boolprompt(
110          *      "Use system authentication",
111          *      atoi(&sc[52][0]))));
112          */
113
114         /* Server tuning */
115
116         strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
117         strprompt("Maximum concurrent sessions", &sc[14][0], 4);
118         strprompt("Maximum message length", &sc[20][0], 20);
119         strprompt("Minimum number of worker threads", &sc[21][0], 3);
120         strprompt("Maximum number of worker threads", &sc[22][0], 3);
121         snprintf(sc[43], sizeof sc[43], "%d", (boolprompt("Automatically delete committed database logs", atoi(&sc[43][0]))));
122
123         strprompt("Server IP address (* for 'any')", &sc[37][0], 15);
124         strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
125         strprompt("POP3S server port (-1 to disable)", &sc[40][0], 5);
126         strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
127         strprompt("IMAPS server port (-1 to disable)", &sc[39][0], 5);
128         strprompt("SMTP MTA server port (-1 to disable)", &sc[24][0], 5);
129         strprompt("SMTP MSA server port (-1 to disable)", &sc[38][0], 5);
130         strprompt("SMTPS server port (-1 to disable)", &sc[41][0], 5);
131         strprompt("NNTP server port (-1 to disable)", &sc[70][0], 5);
132         strprompt("NNTPS server port (-1 to disable)", &sc[71][0], 5);
133         strprompt("Postfix TCP Dictionary Port server port (-1 to disable)", &sc[50][0], 5);
134         strprompt("ManageSieve server port (-1 to disable)", &sc[51][0], 5);
135
136         strprompt("XMPP (Jabber) client to server port (-1 to disable)", &sc[62][0], 5);
137         /* No prompt because we don't implement this service yet, it's just a placeholder */
138         /* strprompt("XMPP (Jabber) server to server port (-1 to disable)", &sc[63][0], 5); */
139
140         /* This logic flips the question around, because it's one of those
141          * situations where 0=yes and 1=no
142          */
143         a = atoi(sc[25]);
144         a = (a ? 0 : 1);
145         a = boolprompt("Correct forged From: lines during authenticated SMTP", a);
146         a = (a ? 0 : 1);
147         snprintf(sc[25], sizeof sc[25], "%d", a);
148
149         snprintf(sc[66], sizeof sc[66], "%d", (boolprompt("Flag messages as spam instead of rejecting", atoi(&sc[66][0]))));
150
151         /* This logic flips the question around, because it's one of those
152          * situations where 0=yes and 1=no
153          */
154         a = atoi(sc[61]);
155         a = (a ? 0 : 1);
156         a = boolprompt("Force IMAP posts in public rooms to be from the user who submitted them", a);
157         a = (a ? 0 : 1);
158         snprintf(sc[61], sizeof sc[61], "%d", a);
159
160         snprintf(sc[45], sizeof sc[45], "%d", (boolprompt("Allow unauthenticated SMTP clients to spoof my domains",
161                                                           atoi(&sc[45][0]))));
162         snprintf(sc[57], sizeof sc[57], "%d", (boolprompt("Perform RBL checks at greeting instead of after RCPT",
163                                                           atoi(&sc[57][0]))));
164
165         /* LDAP settings */
166         if (ipc->ServInfo.supports_ldap) {
167                 a = strlen(&sc[32][0]);
168                 a = (a ? 1 : 0);        /* Set only to 1 or 0 */
169                 a = boolprompt("Do you want to configure LDAP authentication?", a);
170                 if (a) {
171                         strprompt("Host name of LDAP server", &sc[32][0], 127);
172                         strprompt("Port number of LDAP service", &sc[33][0], 5);
173                         strprompt("Base DN", &sc[34][0], 255);
174                         strprompt("Bind DN (or blank for anonymous bind)", &sc[35][0], 255);
175                         strprompt("Password for bind DN (or blank for anonymous bind)", &sc[36][0], 255);
176                 } else {
177                         strcpy(&sc[32][0], "");
178                 }
179         }
180
181         /* Expiry settings */
182         strprompt("Default user purge time (days)", &sc[16][0], 5);
183         strprompt("Default room purge time (days)", &sc[17][0], 5);
184
185         /* Angels and demons dancing in my head... */
186         do {
187                 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_mode);
188                 strprompt("System default message expire policy (? for list)", buf, 1);
189                 if (buf[0] == '?') {
190                         scr_printf("\n"
191                                    "1. Never automatically expire messages\n"
192                                    "2. Expire by message count\n" "3. Expire by message age\n");
193                 }
194         } while ((buf[0] < '1') || (buf[0] > '3'));
195         site_expirepolicy->expire_mode = buf[0] - '0';
196
197         /* ...lunatics and monsters underneath my bed */
198         if (site_expirepolicy->expire_mode == 2) {
199                 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
200                 strprompt("Keep how many messages online?", buf, 10);
201                 site_expirepolicy->expire_value = atol(buf);
202         }
203         if (site_expirepolicy->expire_mode == 3) {
204                 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
205                 strprompt("Keep messages for how many days?", buf, 10);
206                 site_expirepolicy->expire_value = atol(buf);
207         }
208
209         /* Media messiahs preying on my fears... */
210         do {
211                 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_mode);
212                 strprompt("Mailbox default message expire policy (? for list)", buf, 1);
213                 if (buf[0] == '?') {
214                         scr_printf("\n"
215                                    "0. Go with the system default\n"
216                                    "1. Never automatically expire messages\n"
217                                    "2. Expire by message count\n" "3. Expire by message age\n");
218                 }
219         } while ((buf[0] < '0') || (buf[0] > '3'));
220         mbx_expirepolicy->expire_mode = buf[0] - '0';
221
222         /* ...Pop culture prophets playing in my ears */
223         if (mbx_expirepolicy->expire_mode == 2) {
224                 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
225                 strprompt("Keep how many messages online?", buf, 10);
226                 mbx_expirepolicy->expire_value = atol(buf);
227         }
228         if (mbx_expirepolicy->expire_mode == 3) {
229                 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
230                 strprompt("Keep messages for how many days?", buf, 10);
231                 mbx_expirepolicy->expire_value = atol(buf);
232         }
233
234         strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
235         strprompt("Default frequency to run POP3 collection (in seconds)", &sc[64][0], 5);
236         strprompt("Fastest frequency to run POP3 collection (in seconds)", &sc[65][0], 5);
237         strprompt("Hour to run purges (0-23)", &sc[31][0], 2);
238         snprintf(sc[42], sizeof sc[42], "%d", (boolprompt("Enable full text search index (warning: resource intensive)",
239                                                           atoi(&sc[42][0]))));
240
241         snprintf(sc[46], sizeof sc[46], "%d", (boolprompt("Perform journaling of email messages", atoi(&sc[46][0]))));
242         snprintf(sc[47], sizeof sc[47], "%d", (boolprompt("Perform journaling of non-email messages", atoi(&sc[47][0]))));
243         if ((atoi(&sc[46][0])) || (atoi(&sc[47][0]))) {
244                 strprompt("Email destination of journalized messages", &sc[48][0], 127);
245         }
246
247         /* No more Funambol */
248         sc[53][0] = 0;
249         sc[54][0] = 0;
250         sc[55][0] = 0;
251         sc[56][0] = 0;
252
253         /* External pager stuff */
254         int yes_pager = 0;
255         if (strlen(sc[60]) > 0)
256                 yes_pager = 1;
257         yes_pager = boolprompt("Configure an external pager tool", yes_pager);
258         if (yes_pager) {
259                 strprompt("External pager tool", &sc[60][0], 255);
260         } else {
261                 sc[60][0] = 0;
262         }
263
264         /* Master user account */
265         int yes_muacct = 0;
266         if (strlen(sc[58]) > 0)
267                 yes_muacct = 1;
268         yes_muacct = boolprompt("Enable a 'master user' account", yes_muacct);
269         if (yes_muacct) {
270                 strprompt("Master user name", &sc[58][0], 31);
271                 strprompt("Master user password", &sc[59][0], -31);
272         } else {
273                 strcpy(&sc[58][0], "");
274                 strcpy(&sc[59][0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
275         }
276
277         /* Save it */
278         scr_printf("Save this configuration? ");
279         if (yesno()) {
280                 r = 1;
281                 for (a = 0; a < NUM_CONFIGS; a++) {
282                         r += 1 + strlen(sc[a]);
283                 }
284                 resp = (char *) calloc(1, r);
285                 if (!resp) {
286                         scr_printf("Can't save config - out of memory!\n");
287                         logoff(ipc, 1);
288                 }
289                 for (a = 0; a < NUM_CONFIGS; a++) {
290                         strcat(resp, sc[a]);
291                         strcat(resp, "\n");
292                 }
293                 r = CtdlIPCSetSystemConfig(ipc, resp, buf);
294                 if (r / 100 != 4) {
295                         scr_printf("%s\n", buf);
296                 }
297                 free(resp);
298
299                 r = CtdlIPCSetMessageExpirationPolicy(ipc, 2, site_expirepolicy, buf);
300                 if (r / 100 != 2) {
301                         scr_printf("%s\n", buf);
302                 }
303
304                 r = CtdlIPCSetMessageExpirationPolicy(ipc, 3, mbx_expirepolicy, buf);
305                 if (r / 100 != 2) {
306                         scr_printf("%s\n", buf);
307                 }
308
309         }
310         if (site_expirepolicy)
311                 free(site_expirepolicy);
312         if (mbx_expirepolicy)
313                 free(mbx_expirepolicy);
314 }
315
316
317 /*
318  * support function for do_internet_configuration()
319  */
320 void get_inet_rec_type(CtdlIPC * ipc, char *buf)
321 {
322         int sel;
323
324         keyopt(" <1> localhost      (Alias for this computer)\n");
325         keyopt(" <2> smart host     (Forward all outbound mail to this host)\n");
326         keyopt(" <3> fallback host  (Send mail to this host only if direct delivery fails)\n");
327         keyopt(" <4> SpamAssassin   (Address of SpamAssassin server)\n");
328         keyopt(" <5> RBL            (domain suffix of spam hunting RBL)\n");
329         keyopt(" <6> masq domains   (Domains as which users are allowed to masquerade)\n");
330         keyopt(" <7> ClamAV         (Address of ClamAV clamd server)\n");
331         sel = intprompt("Which one", 1, 1, 8);
332         switch (sel) {
333         case 1:
334                 strcpy(buf, "localhost");
335                 return;
336         case 2:
337                 strcpy(buf, "smarthost");
338                 return;
339         case 3:
340                 strcpy(buf, "fallbackhost");
341                 return;
342         case 4:
343                 strcpy(buf, "spamassassin");
344                 return;
345         case 5:
346                 strcpy(buf, "rbl");
347                 return;
348         case 6:
349                 strcpy(buf, "masqdomain");
350                 return;
351         case 7:
352                 strcpy(buf, "clamav");
353                 return;
354         }
355 }
356
357
358 /*
359  * Internet mail configuration
360  */
361 void do_internet_configuration(CtdlIPC * ipc)
362 {
363         char buf[256];
364         char *resp = NULL;
365         int num_recs = 0;
366         char **recs = NULL;
367         char ch;
368         int i, j;
369         int quitting = 0;
370         int modified = 0;
371         int r;
372
373         r = CtdlIPCGetSystemConfigByType(ipc, INTERNETCFG, &resp, buf);
374         if (r / 100 == 1) {
375                 while (!IsEmptyStr(resp)) {
376                         extract_token(buf, resp, 0, '\n', sizeof buf);
377                         remove_token(resp, 0, '\n');
378
379                         // VILE SLEAZY HACK: replace obsolete "directory" domains with "localhost"
380                         char *d = strstr(buf, "|directory");
381                         if (d != NULL) {
382                                 strcpy(d, "|localhost");
383                         }
384
385                         ++num_recs;
386                         if (num_recs == 1)
387                                 recs = malloc(sizeof(char *));
388                         else
389                                 recs = realloc(recs, (sizeof(char *)) * num_recs);
390                         recs[num_recs - 1] = malloc(strlen(buf) + 1);
391                         strcpy(recs[num_recs - 1], buf);
392                 }
393         }
394         if (resp)
395                 free(resp);
396
397         do {
398                 scr_printf("\n");
399                 color(BRIGHT_WHITE);
400                 scr_printf("###                    Host or domain                     Record type      \n");
401                 color(DIM_WHITE);
402                 scr_printf("--- -------------------------------------------------- --------------------\n");
403                 for (i = 0; i < num_recs; ++i) {
404                         color(DIM_WHITE);
405                         scr_printf("%3d ", i + 1);
406                         extract_token(buf, recs[i], 0, '|', sizeof buf);
407                         color(BRIGHT_CYAN);
408                         scr_printf("%-50s ", buf);
409                         extract_token(buf, recs[i], 1, '|', sizeof buf);
410                         color(BRIGHT_MAGENTA);
411                         scr_printf("%-20s\n", buf);
412                         color(DIM_WHITE);
413                 }
414
415                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
416                 switch (ch) {
417                 case 'a':
418                         newprompt("Enter host name: ", buf, 50);
419                         striplt(buf);
420                         if (!IsEmptyStr(buf)) {
421                                 ++num_recs;
422                                 if (num_recs == 1) {
423                                         recs = malloc(sizeof(char *));
424                                 } else {
425                                         recs = realloc(recs, (sizeof(char *)) * num_recs);
426                                 }
427                                 strcat(buf, "|");
428                                 get_inet_rec_type(ipc, &buf[strlen(buf)]);
429                                 recs[num_recs - 1] = strdup(buf);
430                         }
431                         modified = 1;
432                         break;
433                 case 'd':
434                         i = intprompt("Delete which one", 1, 1, num_recs) - 1;
435                         free(recs[i]);
436                         --num_recs;
437                         for (j = i; j < num_recs; ++j) {
438                                 recs[j] = recs[j + 1];
439                         }
440                         modified = 1;
441                         break;
442                 case 's':
443                         r = 1;
444                         for (i = 0; i < num_recs; i++)
445                                 r += 1 + strlen(recs[i]);
446                         resp = (char *) calloc(1, r);
447                         if (!resp) {
448                                 scr_printf("Can't save config - out of memory!\n");
449                                 logoff(ipc, 1);
450                         }
451                         if (num_recs)
452                                 for (i = 0; i < num_recs; i++) {
453                                         strcat(resp, recs[i]);
454                                         strcat(resp, "\n");
455                                 }
456                         r = CtdlIPCSetSystemConfigByType(ipc, INTERNETCFG, resp, buf);
457                         if (r / 100 != 4) {
458                                 scr_printf("%s\n", buf);
459                         } else {
460                                 scr_printf("Wrote %d records.\n", num_recs);
461                                 modified = 0;
462                         }
463                         free(resp);
464                         break;
465                 case 'q':
466                         quitting = !modified || boolprompt("Quit without saving", 0);
467                         break;
468                 default:
469                         break;
470                 }
471         } while (!quitting);
472
473         if (recs != NULL) {
474                 for (i = 0; i < num_recs; ++i)
475                         free(recs[i]);
476                 free(recs);
477         }
478 }
479
480
481
482 /*
483  * Edit network configuration for room sharing, mailing lists, etc.
484  */
485 void network_config_management(CtdlIPC * ipc, char *entrytype, char *comment)
486 {
487         char filename[PATH_MAX];
488         char changefile[PATH_MAX];
489         int e_ex_code;
490         pid_t editor_pid;
491         int cksum;
492         int b, i, tokens;
493         char buf[1024];
494         char instr[1024];
495         char addr[1024];
496         FILE *tempfp;
497         FILE *changefp;
498         char *listing = NULL;
499         int r;
500
501         if (IsEmptyStr(editor_path)) {
502                 scr_printf("You must have an external editor configured in order to use this function.\n");
503                 return;
504         }
505
506         CtdlMakeTempFileName(filename, sizeof filename);
507         CtdlMakeTempFileName(changefile, sizeof changefile);
508
509         tempfp = fopen(filename, "w");
510         if (tempfp == NULL) {
511                 scr_printf("Cannot open %s: %s\n", filename, strerror(errno));
512                 return;
513         }
514
515         fprintf(tempfp, "# Configuration for room: %s\n", room_name);
516         fprintf(tempfp, "# %s\n", comment);
517         fprintf(tempfp, "# Specify one per line.\n" "\n\n");
518
519         r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
520         if (r / 100 == 1) {
521                 while (listing && !IsEmptyStr(listing)) {
522                         extract_token(buf, listing, 0, '\n', sizeof buf);
523                         remove_token(listing, 0, '\n');
524                         extract_token(instr, buf, 0, '|', sizeof instr);
525                         if (!strcasecmp(instr, entrytype)) {
526                                 tokens = num_tokens(buf, '|');
527                                 for (i = 1; i < tokens; ++i) {
528                                         extract_token(addr, buf, i, '|', sizeof addr);
529                                         fprintf(tempfp, "%s", addr);
530                                         if (i < (tokens - 1)) {
531                                                 fprintf(tempfp, "|");
532                                         }
533                                 }
534                                 fprintf(tempfp, "\n");
535                         }
536                 }
537         }
538         if (listing) {
539                 free(listing);
540                 listing = NULL;
541         }
542         fclose(tempfp);
543
544         e_ex_code = 1;          /* start with a failed exit code */
545         stty_ctdl(SB_RESTORE);
546         editor_pid = fork();
547         cksum = file_checksum(filename);
548         if (editor_pid == 0) {
549                 chmod(filename, 0600);
550                 putenv("WINDOW_TITLE=Network configuration");
551                 execlp(editor_path, editor_path, filename, NULL);
552                 exit(1);
553         }
554         if (editor_pid > 0) {
555                 do {
556                         e_ex_code = 0;
557                         b = ka_wait(&e_ex_code);
558                 } while ((b != editor_pid) && (b >= 0));
559                 editor_pid = (-1);
560                 stty_ctdl(0);
561         }
562
563         if (file_checksum(filename) == cksum) {
564                 scr_printf("*** No changes to save.\n");
565                 e_ex_code = 1;
566         }
567
568         if (e_ex_code == 0) {   /* Save changes */
569                 changefp = fopen(changefile, "w");
570
571                 /* Load all netconfig entries that are *not* of the type we are editing */
572                 r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
573                 if (r / 100 == 1) {
574                         while (listing && !IsEmptyStr(listing)) {
575                                 extract_token(buf, listing, 0, '\n', sizeof buf);
576                                 remove_token(listing, 0, '\n');
577                                 extract_token(instr, buf, 0, '|', sizeof instr);
578                                 if (strcasecmp(instr, entrytype)) {
579                                         fprintf(changefp, "%s\n", buf);
580                                 }
581                         }
582                 }
583                 if (listing) {
584                         free(listing);
585                         listing = NULL;
586                 }
587
588                 /* ...and merge that with the data we just edited */
589                 tempfp = fopen(filename, "r");
590                 while (fgets(buf, sizeof buf, tempfp) != NULL) {
591                         for (i = 0; i < strlen(buf); ++i) {
592                                 if (buf[i] == '#')
593                                         buf[i] = 0;
594                         }
595                         striplt(buf);
596                         if (!IsEmptyStr(buf)) {
597                                 fprintf(changefp, "%s|%s\n", entrytype, buf);
598                         }
599                 }
600                 fclose(tempfp);
601                 fclose(changefp);
602
603                 /* now write it to the server... */
604                 changefp = fopen(changefile, "r");
605                 if (changefp != NULL) {
606                         listing = load_message_from_file(changefp);
607                         if (listing) {
608                                 r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
609                                 free(listing);
610                                 listing = NULL;
611                         }
612                         fclose(changefp);
613                 }
614         }
615
616         unlink(filename);       /* Delete the temporary files */
617         unlink(changefile);
618 }
619
620
621 /*
622  * POP3 aggregation client configuration
623  */
624 void do_pop3client_configuration(CtdlIPC * ipc)
625 {
626         char buf[SIZ];
627         int num_recs = 0;
628         char **recs = NULL;
629         char ch;
630         int i, j;
631         int quitting = 0;
632         int modified = 0;
633         char *listing = NULL;
634         char *other_listing = NULL;
635         int r;
636         char instr[SIZ];
637
638         r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
639         if (r / 100 == 1) {
640                 while (listing && !IsEmptyStr(listing)) {
641                         extract_token(buf, listing, 0, '\n', sizeof buf);
642                         remove_token(listing, 0, '\n');
643                         extract_token(instr, buf, 0, '|', sizeof instr);
644                         if (!strcasecmp(instr, "pop3client")) {
645
646                                 ++num_recs;
647                                 if (num_recs == 1)
648                                         recs = malloc(sizeof(char *));
649                                 else
650                                         recs = realloc(recs, (sizeof(char *)) * num_recs);
651                                 recs[num_recs - 1] = malloc(SIZ);
652                                 strcpy(recs[num_recs - 1], buf);
653
654                         }
655                 }
656         }
657         if (listing) {
658                 free(listing);
659                 listing = NULL;
660         }
661
662         do {
663                 scr_printf("\n");
664                 color(BRIGHT_WHITE);
665                 scr_printf("### " "      Remote POP3 host       " "         User name           " "Keep on server? " "\n");
666                 color(DIM_WHITE);
667                 scr_printf("--- " "---------------------------- " "---------------------------- " "--------------- " "\n");
668                 for (i = 0; i < num_recs; ++i) {
669                         color(DIM_WHITE);
670                         scr_printf("%3d ", i + 1);
671
672                         extract_token(buf, recs[i], 1, '|', sizeof buf);
673                         color(BRIGHT_CYAN);
674                         scr_printf("%-28s ", buf);
675
676                         extract_token(buf, recs[i], 2, '|', sizeof buf);
677                         color(BRIGHT_MAGENTA);
678                         scr_printf("%-28s ", buf);
679
680                         color(BRIGHT_CYAN);
681                         scr_printf("%-15s\n", (extract_int(recs[i], 4) ? "Yes" : "No"));
682                         color(DIM_WHITE);
683                 }
684
685                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
686                 switch (ch) {
687                 case 'a':
688                         ++num_recs;
689                         if (num_recs == 1) {
690                                 recs = malloc(sizeof(char *));
691                         } else {
692                                 recs = realloc(recs, (sizeof(char *)) * num_recs);
693                         }
694                         strcpy(buf, "pop3client|");
695                         newprompt("Enter host name: ", &buf[strlen(buf)], 28);
696                         strcat(buf, "|");
697                         newprompt("Enter user name: ", &buf[strlen(buf)], 28);
698                         strcat(buf, "|");
699                         newprompt("Enter password : ", &buf[strlen(buf)], 16);
700                         strcat(buf, "|");
701                         scr_printf("Keep messages on server instead of deleting them? ");
702                         sprintf(&buf[strlen(buf)], "%d", yesno());
703                         strcat(buf, "|");
704                         recs[num_recs - 1] = strdup(buf);
705                         modified = 1;
706                         break;
707                 case 'd':
708                         i = intprompt("Delete which one", 1, 1, num_recs) - 1;
709                         free(recs[i]);
710                         --num_recs;
711                         for (j = i; j < num_recs; ++j)
712                                 recs[j] = recs[j + 1];
713                         modified = 1;
714                         break;
715                 case 's':
716                         r = 1;
717                         for (i = 0; i < num_recs; ++i) {
718                                 r += 1 + strlen(recs[i]);
719                         }
720                         listing = (char *) calloc(1, r);
721                         if (!listing) {
722                                 scr_printf("Can't save config - out of memory!\n");
723                                 logoff(ipc, 1);
724                         }
725                         if (num_recs)
726                                 for (i = 0; i < num_recs; ++i) {
727                                         strcat(listing, recs[i]);
728                                         strcat(listing, "\n");
729                                 }
730
731                         /* Retrieve all the *other* records for merging */
732                         r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
733                         if (r / 100 == 1) {
734                                 for (i = 0; i < num_tokens(other_listing, '\n'); ++i) {
735                                         extract_token(buf, other_listing, i, '\n', sizeof buf);
736                                         if (strncasecmp(buf, "pop3client|", 11)) {
737                                                 listing = realloc(listing, strlen(listing) + strlen(buf) + 10);
738                                                 strcat(listing, buf);
739                                                 strcat(listing, "\n");
740                                         }
741                                 }
742                         }
743                         free(other_listing);
744                         r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
745                         free(listing);
746                         listing = NULL;
747
748                         if (r / 100 != 4) {
749                                 scr_printf("%s\n", buf);
750                         } else {
751                                 scr_printf("Wrote %d records.\n", num_recs);
752                                 modified = 0;
753                         }
754                         quitting = 1;
755                         break;
756                 case 'q':
757                         quitting = !modified || boolprompt("Quit without saving", 0);
758                         break;
759                 default:
760                         break;
761                 }
762         } while (!quitting);
763
764         if (recs != NULL) {
765                 for (i = 0; i < num_recs; ++i)
766                         free(recs[i]);
767                 free(recs);
768         }
769 }
770
771
772
773
774
775
776 /*
777  * RSS feed retrieval client configuration
778  */
779 void do_rssclient_configuration(CtdlIPC * ipc)
780 {
781         char buf[SIZ];
782         int num_recs = 0;
783         char **recs = NULL;
784         char ch;
785         int i, j;
786         int quitting = 0;
787         int modified = 0;
788         char *listing = NULL;
789         char *other_listing = NULL;
790         int r;
791         char instr[SIZ];
792
793         r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
794         if (r / 100 == 1) {
795                 while (listing && !IsEmptyStr(listing)) {
796                         extract_token(buf, listing, 0, '\n', sizeof buf);
797                         remove_token(listing, 0, '\n');
798                         extract_token(instr, buf, 0, '|', sizeof instr);
799                         if (!strcasecmp(instr, "rssclient")) {
800
801                                 ++num_recs;
802                                 if (num_recs == 1)
803                                         recs = malloc(sizeof(char *));
804                                 else
805                                         recs = realloc(recs, (sizeof(char *)) * num_recs);
806                                 recs[num_recs - 1] = malloc(SIZ);
807                                 strcpy(recs[num_recs - 1], buf);
808
809                         }
810                 }
811         }
812         if (listing) {
813                 free(listing);
814                 listing = NULL;
815         }
816
817         do {
818                 scr_printf("\n");
819                 color(BRIGHT_WHITE);
820                 scr_printf("### Feed URL\n");
821                 color(DIM_WHITE);
822                 scr_printf("--- " "---------------------------------------------------------------------------" "\n");
823
824                 for (i = 0; i < num_recs; ++i) {
825                         color(DIM_WHITE);
826                         scr_printf("%3d ", i + 1);
827
828                         extract_token(buf, recs[i], 1, '|', sizeof buf);
829                         color(BRIGHT_CYAN);
830                         scr_printf("%-75s\n", buf);
831
832                         color(DIM_WHITE);
833                 }
834
835                 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
836                 switch (ch) {
837                 case 'a':
838                         ++num_recs;
839                         if (num_recs == 1) {
840                                 recs = malloc(sizeof(char *));
841                         } else {
842                                 recs = realloc(recs, (sizeof(char *)) * num_recs);
843                         }
844                         strcpy(buf, "rssclient|");
845                         newprompt("Enter feed URL: ", &buf[strlen(buf)], 75);
846                         strcat(buf, "|");
847                         recs[num_recs - 1] = strdup(buf);
848                         modified = 1;
849                         break;
850                 case 'd':
851                         i = intprompt("Delete which one", 1, 1, num_recs) - 1;
852                         free(recs[i]);
853                         --num_recs;
854                         for (j = i; j < num_recs; ++j)
855                                 recs[j] = recs[j + 1];
856                         modified = 1;
857                         break;
858                 case 's':
859                         r = 1;
860                         for (i = 0; i < num_recs; ++i) {
861                                 r += 1 + strlen(recs[i]);
862                         }
863                         listing = (char *) calloc(1, r);
864                         if (!listing) {
865                                 scr_printf("Can't save config - out of memory!\n");
866                                 logoff(ipc, 1);
867                         }
868                         if (num_recs)
869                                 for (i = 0; i < num_recs; ++i) {
870                                         strcat(listing, recs[i]);
871                                         strcat(listing, "\n");
872                                 }
873
874                         /* Retrieve all the *other* records for merging */
875                         r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
876                         if (r / 100 == 1) {
877                                 for (i = 0; i < num_tokens(other_listing, '\n'); ++i) {
878                                         extract_token(buf, other_listing, i, '\n', sizeof buf);
879                                         if (strncasecmp(buf, "rssclient|", 10)) {
880                                                 listing = realloc(listing, strlen(listing) + strlen(buf) + 10);
881                                                 strcat(listing, buf);
882                                                 strcat(listing, "\n");
883                                         }
884                                 }
885                         }
886                         free(other_listing);
887                         r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
888                         free(listing);
889                         listing = NULL;
890
891                         if (r / 100 != 4) {
892                                 scr_printf("%s\n", buf);
893                         } else {
894                                 scr_printf("Wrote %d records.\n", num_recs);
895                                 modified = 0;
896                         }
897                         quitting = 1;
898                         break;
899                 case 'q':
900                         quitting = !modified || boolprompt("Quit without saving", 0);
901                         break;
902                 default:
903                         break;
904                 }
905         } while (!quitting);
906
907         if (recs != NULL) {
908                 for (i = 0; i < num_recs; ++i)
909                         free(recs[i]);
910                 free(recs);
911         }
912 }