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