3 * Configuration screens that are part of the text mode client.
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
22 # include <sys/time.h>
34 #include "citadel_ipc.h"
35 #include "citadel_decls.h"
36 #include "tuiconfig.h"
46 /* work around solaris include files */
52 extern char tempdir[];
53 extern char *axdefs[8];
54 extern long highest_msg_read;
55 extern long maxmsgnum;
56 extern unsigned room_flags;
57 extern int screenwidth;
61 * General system configuration command
63 void do_system_configuration(CtdlIPC *ipc)
66 #define NUM_CONFIGS 60
69 char sc[NUM_CONFIGS][256];
71 struct ExpirePolicy *site_expirepolicy = NULL;
72 struct ExpirePolicy *mbx_expirepolicy = NULL;
75 int r; /* IPC response code */
76 int server_configs = 0;
78 /* Clear out the config buffers */
79 memset(&sc[0][0], 0, sizeof(sc));
81 /* Fetch the current config */
82 r = CtdlIPCGetSystemConfig(ipc, &resp, buf);
84 server_configs = num_tokens(resp, '\n');
85 for (a=0; a<server_configs; ++a) {
86 if (a < NUM_CONFIGS) {
87 extract_token(&sc[a][0], resp, a, '\n', sizeof sc[a]);
93 /* Fetch the expire policy (this will silently fail on old servers,
94 * resulting in "default" policy)
96 r = CtdlIPCGetMessageExpirationPolicy(ipc, 2, &site_expirepolicy, buf);
97 r = CtdlIPCGetMessageExpirationPolicy(ipc, 3, &mbx_expirepolicy, buf);
99 /* Identification parameters */
101 strprompt("Node name", &sc[0][0], 15);
102 strprompt("Fully qualified domain name", &sc[1][0], 63);
103 strprompt("Human readable node name", &sc[2][0], 20);
104 strprompt("Telephone number", &sc[3][0], 15);
105 strprompt("Geographic location of this system", &sc[12][0], 31);
106 strprompt("Name of system administrator", &sc[13][0], 25);
107 strprompt("Paginator prompt", &sc[10][0], 79);
109 /* Security parameters */
111 snprintf(sc[7], sizeof sc[7], "%d", (boolprompt(
112 "Require registration for new users",
114 snprintf(sc[29], sizeof sc[29], "%d", (boolprompt(
115 "Disable self-service user account creation",
117 strprompt("Initial access level for new users", &sc[6][0], 1);
118 strprompt("Access level required to create rooms", &sc[19][0], 1);
119 snprintf(sc[4], sizeof sc[4], "%d", (boolprompt(
120 "Automatically give room aide privs to a user who creates a private room",
123 snprintf(sc[8], sizeof sc[8], "%d", (boolprompt(
124 "Automatically move problem user messages to twit room",
127 strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
128 snprintf(sc[11], sizeof sc[11], "%d", (boolprompt(
129 "Restrict Internet mail to only those with that privilege",
131 snprintf(sc[26], sizeof sc[26], "%d", (boolprompt(
132 "Allow Aides to Zap (forget) rooms",
135 if (!IsEmptyStr(&sc[18][0])) logpages = 1;
137 logpages = boolprompt("Log all pages", logpages);
139 strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
145 /* Commented out because this setting isn't really appropriate to
146 * change while the server is running.
148 * snprintf(sc[52], sizeof sc[52], "%d", (boolprompt(
149 * "Use system authentication",
150 * atoi(&sc[52][0]))));
155 strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
156 strprompt("Maximum concurrent sessions", &sc[14][0], 4);
157 strprompt("Maximum message length", &sc[20][0], 20);
158 strprompt("Minimum number of worker threads", &sc[21][0], 3);
159 strprompt("Maximum number of worker threads", &sc[22][0], 3);
160 snprintf(sc[43], sizeof sc[43], "%d", (boolprompt(
161 "Automatically delete committed database logs",
164 strprompt("Server IP address (0.0.0.0 for 'any')", &sc[37][0], 15);
165 strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
166 strprompt("POP3S server port (-1 to disable)", &sc[40][0], 5);
167 strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
168 strprompt("IMAPS server port (-1 to disable)", &sc[39][0], 5);
169 strprompt("SMTP MTA server port (-1 to disable)", &sc[24][0], 5);
170 strprompt("SMTP MSA server port (-1 to disable)", &sc[38][0], 5);
171 strprompt("SMTPS server port (-1 to disable)", &sc[41][0], 5);
172 strprompt("Postfix TCP Dictionary Port server port (-1 to disable)", &sc[50][0], 5);
173 strprompt("ManageSieve server port (-1 to disable)", &sc[51][0], 5);
174 /* This logic flips the question around, because it's one of those
175 * situations where 0=yes and 1=no
179 a = boolprompt("Correct forged From: lines during authenticated SMTP",
182 snprintf(sc[25], sizeof sc[25], "%d", a);
183 snprintf(sc[45], sizeof sc[45], "%d", (boolprompt(
184 "Allow unauthenticated SMTP clients to spoof my domains",
186 snprintf(sc[57], sizeof sc[57], "%d", (boolprompt(
187 "Perform RBL checks at greeting instead of after RCPT",
189 snprintf(sc[44], sizeof sc[44], "%d", (boolprompt(
190 "Instantly expunge deleted IMAP messages",
194 if (ipc->ServInfo.supports_ldap) {
195 a = strlen(&sc[32][0]);
196 a = (a ? 1 : 0); /* Set only to 1 or 0 */
197 a = boolprompt("Connect this Citadel to an external LDAP directory", a);
199 strprompt("Host name of LDAP server",
201 strprompt("Port number of LDAP service",
203 strprompt("Base DN", &sc[34][0], 255);
204 strprompt("Bind DN", &sc[35][0], 255);
205 strprompt("Password for bind DN", &sc[36][0], 255);
208 strcpy(&sc[32][0], "");
212 /* Expiry settings */
213 strprompt("Default user purge time (days)", &sc[16][0], 5);
214 strprompt("Default room purge time (days)", &sc[17][0], 5);
216 /* Angels and demons dancing in my head... */
218 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_mode);
219 strprompt("System default message expire policy (? for list)",
223 "1. Never automatically expire messages\n"
224 "2. Expire by message count\n"
225 "3. Expire by message age\n");
227 } while ((buf[0] < '1') || (buf[0] > '3'));
228 site_expirepolicy->expire_mode = buf[0] - '0';
230 /* ...lunatics and monsters underneath my bed */
231 if (site_expirepolicy->expire_mode == 2) {
232 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
233 strprompt("Keep how many messages online?", buf, 10);
234 site_expirepolicy->expire_value = atol(buf);
236 if (site_expirepolicy->expire_mode == 3) {
237 snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
238 strprompt("Keep messages for how many days?", buf, 10);
239 site_expirepolicy->expire_value = atol(buf);
242 /* Media messiahs preying on my fears... */
244 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_mode);
245 strprompt("Mailbox default message expire policy (? for list)",
249 "0. Go with the system default\n"
250 "1. Never automatically expire messages\n"
251 "2. Expire by message count\n"
252 "3. Expire by message age\n");
254 } while ((buf[0] < '0') || (buf[0] > '3'));
255 mbx_expirepolicy->expire_mode = buf[0] - '0';
257 /* ...Pop culture prophets playing in my ears */
258 if (mbx_expirepolicy->expire_mode == 2) {
259 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
260 strprompt("Keep how many messages online?", buf, 10);
261 mbx_expirepolicy->expire_value = atol(buf);
263 if (mbx_expirepolicy->expire_mode == 3) {
264 snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
265 strprompt("Keep messages for how many days?", buf, 10);
266 mbx_expirepolicy->expire_value = atol(buf);
269 strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
270 strprompt("Hour to run purges (0-23)", &sc[31][0], 2);
271 snprintf(sc[42], sizeof sc[42], "%d", (boolprompt(
272 "Enable full text search index (warning: resource intensive)",
275 snprintf(sc[46], sizeof sc[46], "%d", (boolprompt(
276 "Perform journaling of email messages",
278 snprintf(sc[47], sizeof sc[47], "%d", (boolprompt(
279 "Perform journaling of non-email messages",
281 if ( (atoi(&sc[46][0])) || (atoi(&sc[47][0])) ) {
282 strprompt("Email destination of journalized messages",
286 /* Funambol push stuff */
287 int yes_funambol = 0;
288 if (strlen(sc[53]) > 0) yes_funambol = 1;
289 yes_funambol = boolprompt("Connect to an external Funambol sync server", yes_funambol);
291 strprompt("Funambol server (blank to disable)", &sc[53][0], 63);
292 strprompt("Funambol server port", &sc[54][0], 5);
293 strprompt("Funambol sync source", &sc[55][0], 63);
294 strprompt("Funambol authentication details (user:pass in Base64)", &sc[56][0],63);
303 /* Master user account */
305 if (strlen(sc[58]) > 0) yes_muacct = 1;
306 yes_muacct = boolprompt("Enable a 'master user' account", yes_muacct);
308 strprompt("Master user name", &sc[58][0], 31);
309 strprompt("Master user password", &sc[59][0], -31);
312 strcpy(&sc[58][0], "");
313 strcpy(&sc[59][0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
317 scr_printf("Save this configuration? ");
320 for (a = 0; a < NUM_CONFIGS; a++)
321 r += 1 + strlen(sc[a]);
322 resp = (char *)calloc(1, r);
324 err_printf("Can't save config - out of memory!\n");
327 for (a = 0; a < NUM_CONFIGS; a++) {
331 r = CtdlIPCSetSystemConfig(ipc, resp, buf);
333 err_printf("%s\n", buf);
337 r = CtdlIPCSetMessageExpirationPolicy(ipc, 2, site_expirepolicy, buf);
339 err_printf("%s\n", buf);
342 r = CtdlIPCSetMessageExpirationPolicy(ipc, 3, mbx_expirepolicy, buf);
344 err_printf("%s\n", buf);
348 if (site_expirepolicy) free(site_expirepolicy);
349 if (mbx_expirepolicy) free(mbx_expirepolicy);
354 * support function for do_internet_configuration()
356 void get_inet_rec_type(CtdlIPC *ipc, char *buf) {
359 keyopt(" <1> localhost (Alias for this computer)\n");
360 keyopt(" <2> gateway domain (Domain for all Citadel systems)\n");
361 keyopt(" <3> smart-host (Forward all outbound mail to this host)\n");
362 keyopt(" <4> directory (Consult the Global Address Book)\n");
363 keyopt(" <5> SpamAssassin (Address of SpamAssassin server)\n");
364 keyopt(" <6> RBL (domain suffix of spam hunting RBL)\n");
365 keyopt(" <7> masq domains (Domains as which users are allowed to masquerade)\n");
366 sel = intprompt("Which one", 1, 1, 6);
368 case 1: strcpy(buf, "localhost");
370 case 2: strcpy(buf, "gatewaydomain");
372 case 3: strcpy(buf, "smarthost");
374 case 4: strcpy(buf, "directory");
376 case 5: strcpy(buf, "spamassassin");
378 case 6: strcpy(buf, "rbl");
380 case 7: strcpy(buf, "masqdomain");
387 * Internet mail configuration
389 void do_internet_configuration(CtdlIPC *ipc)
402 r = CtdlIPCGetSystemConfigByType(ipc, INTERNETCFG, &resp, buf);
404 while (!IsEmptyStr(resp)) {
405 extract_token(buf, resp, 0, '\n', sizeof buf);
406 remove_token(resp, 0, '\n');
408 if (num_recs == 1) recs = malloc(sizeof(char *));
409 else recs = realloc(recs, (sizeof(char *)) * num_recs);
410 recs[num_recs-1] = malloc(strlen(buf) + 1);
411 strcpy(recs[num_recs-1], buf);
414 if (resp) free(resp);
419 scr_printf("### Host or domain Record type \n");
421 scr_printf("--- -------------------------------------------------- --------------------\n");
422 for (i=0; i<num_recs; ++i) {
424 scr_printf("%3d ", i+1);
425 extract_token(buf, recs[i], 0, '|', sizeof buf);
427 scr_printf("%-50s ", buf);
428 extract_token(buf, recs[i], 1, '|', sizeof buf);
429 color(BRIGHT_MAGENTA);
430 scr_printf("%-20s\n", buf);
434 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
437 newprompt("Enter host name: ",
440 if (!IsEmptyStr(buf)) {
443 recs = malloc(sizeof(char *));
444 else recs = realloc(recs,
445 (sizeof(char *)) * num_recs);
447 get_inet_rec_type(ipc,
449 recs[num_recs-1] = strdup(buf);
454 i = intprompt("Delete which one",
458 for (j=i; j<num_recs; ++j)
464 for (i = 0; i < num_recs; i++)
465 r += 1 + strlen(recs[i]);
466 resp = (char *)calloc(1, r);
468 err_printf("Can't save config - out of memory!\n");
471 if (num_recs) for (i = 0; i < num_recs; i++) {
472 strcat(resp, recs[i]);
475 r = CtdlIPCSetSystemConfigByType(ipc, INTERNETCFG, resp, buf);
477 err_printf("%s\n", buf);
479 scr_printf("Wrote %d records.\n", num_recs);
485 quitting = !modified || boolprompt(
486 "Quit without saving", 0);
494 for (i=0; i<num_recs; ++i) free(recs[i]);
502 * Edit network configuration for room sharing, mailing lists, etc.
504 void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment)
506 char filename[PATH_MAX];
507 char changefile[PATH_MAX];
517 char *listing = NULL;
520 if (IsEmptyStr(editor_paths[0])) {
521 scr_printf("You must have an external editor configured in"
522 " order to use this function.\n");
526 CtdlMakeTempFileName(filename, sizeof filename);
527 CtdlMakeTempFileName(changefile, sizeof changefile);
529 tempfp = fopen(filename, "w");
530 if (tempfp == NULL) {
531 err_printf("Cannot open %s: %s\n", filename, strerror(errno));
535 fprintf(tempfp, "# Configuration for room: %s\n", room_name);
536 fprintf(tempfp, "# %s\n", comment);
537 fprintf(tempfp, "# Specify one per line.\n"
540 r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
542 while(listing && !IsEmptyStr(listing)) {
543 extract_token(buf, listing, 0, '\n', sizeof buf);
544 remove_token(listing, 0, '\n');
545 extract_token(instr, buf, 0, '|', sizeof instr);
546 if (!strcasecmp(instr, entrytype)) {
547 tokens = num_tokens(buf, '|');
548 for (i=1; i<tokens; ++i) {
549 extract_token(addr, buf, i, '|', sizeof addr);
550 fprintf(tempfp, "%s", addr);
551 if (i < (tokens-1)) {
552 fprintf(tempfp, "|");
555 fprintf(tempfp, "\n");
565 e_ex_code = 1; /* start with a failed exit code */
567 stty_ctdl(SB_RESTORE);
569 cksum = file_checksum(filename);
570 if (editor_pid == 0) {
571 chmod(filename, 0600);
572 putenv("WINDOW_TITLE=Network configuration");
573 execlp(editor_paths[0], editor_paths[0], filename, NULL);
576 if (editor_pid > 0) {
579 b = ka_wait(&e_ex_code);
580 } while ((b != editor_pid) && (b >= 0));
586 if (file_checksum(filename) == cksum) {
587 err_printf("*** No changes to save.\n");
591 if (e_ex_code == 0) { /* Save changes */
592 changefp = fopen(changefile, "w");
594 /* Load all netconfig entries that are *not* of the type we are editing */
595 r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
597 while(listing && !IsEmptyStr(listing)) {
598 extract_token(buf, listing, 0, '\n', sizeof buf);
599 remove_token(listing, 0, '\n');
600 extract_token(instr, buf, 0, '|', sizeof instr);
601 if (strcasecmp(instr, entrytype)) {
602 fprintf(changefp, "%s\n", buf);
611 /* ...and merge that with the data we just edited */
612 tempfp = fopen(filename, "r");
613 while (fgets(buf, sizeof buf, tempfp) != NULL) {
614 for (i=0; i<strlen(buf); ++i) {
615 if (buf[i] == '#') buf[i] = 0;
618 if (!IsEmptyStr(buf)) {
619 fprintf(changefp, "%s|%s\n", entrytype, buf);
625 /* now write it to the server... */
626 changefp = fopen(changefile, "r");
627 if (changefp != NULL) {
628 listing = load_message_from_file(changefp);
630 r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
638 unlink(filename); /* Delete the temporary files */
644 * IGnet node configuration
646 void do_ignet_configuration(CtdlIPC *ipc) {
655 char *listing = NULL;
658 r = CtdlIPCGetSystemConfigByType(ipc, IGNETCFG, &listing, buf);
659 if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
660 extract_token(buf, listing, 0, '\n', sizeof buf);
661 remove_token(listing, 0, '\n');
664 if (num_recs == 1) recs = malloc(sizeof(char *));
665 else recs = realloc(recs, (sizeof(char *)) * num_recs);
666 recs[num_recs-1] = malloc(SIZ);
667 strcpy(recs[num_recs-1], buf);
669 if (listing) free(listing);
682 "------------------ "
683 "-------------------------------- "
685 for (i=0; i<num_recs; ++i) {
687 scr_printf("%3d ", i+1);
688 extract_token(buf, recs[i], 0, '|', sizeof buf);
690 scr_printf("%-16s ", buf);
691 extract_token(buf, recs[i], 1, '|', sizeof buf);
692 color(BRIGHT_MAGENTA);
693 scr_printf("%-18s ", buf);
694 extract_token(buf, recs[i], 2, '|', sizeof buf);
696 scr_printf("%-32s ", buf);
697 extract_token(buf, recs[i], 3, '|', sizeof buf);
698 color(BRIGHT_MAGENTA);
699 scr_printf("%-3s\n", buf);
704 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
709 recs = malloc(sizeof(char *));
710 else recs = realloc(recs,
711 (sizeof(char *)) * num_recs);
712 newprompt("Enter node name : ", buf, 16);
714 newprompt("Enter shared secret: ",
715 &buf[strlen(buf)], 18);
717 newprompt("Enter host or IP : ",
718 &buf[strlen(buf)], 32);
720 strprompt("Enter port number : ",
721 &buf[strlen(buf)-3], 5);
722 recs[num_recs-1] = strdup(buf);
726 i = intprompt("Delete which one",
730 for (j=i; j<num_recs; ++j)
736 for (i = 0; i < num_recs; ++i)
737 r += 1 + strlen(recs[i]);
738 listing = (char*) calloc(1, r);
740 err_printf("Can't save config - out of memory!\n");
743 if (num_recs) for (i = 0; i < num_recs; ++i) {
744 strcat(listing, recs[i]);
745 strcat(listing, "\n");
747 r = CtdlIPCSetSystemConfigByType(ipc, IGNETCFG, listing, buf);
749 scr_printf("%s\n", buf);
751 scr_printf("Wrote %d records.\n", num_recs);
757 quitting = !modified || boolprompt(
758 "Quit without saving", 0);
766 for (i=0; i<num_recs; ++i) free(recs[i]);
773 * Filter list configuration
775 void do_filterlist_configuration(CtdlIPC *ipc)
785 char *listing = NULL;
788 r = CtdlIPCGetSystemConfigByType(ipc, FILTERLIST, &listing, buf);
789 if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
790 extract_token(buf, listing, 0, '\n', sizeof buf);
791 remove_token(listing, 0, '\n');
794 if (num_recs == 1) recs = malloc(sizeof(char *));
795 else recs = realloc(recs, (sizeof(char *)) * num_recs);
796 recs[num_recs-1] = malloc(SIZ);
797 strcpy(recs[num_recs-1], buf);
799 if (listing) free(listing);
811 "---------------------------- "
812 "---------------------------- "
815 for (i=0; i<num_recs; ++i) {
817 scr_printf("%3d ", i+1);
818 extract_token(buf, recs[i], 0, '|', sizeof buf);
820 scr_printf("%-28s ", buf);
821 extract_token(buf, recs[i], 1, '|', sizeof buf);
822 color(BRIGHT_MAGENTA);
823 scr_printf("%-28s ", buf);
824 extract_token(buf, recs[i], 2, '|', sizeof buf);
826 scr_printf("%-16s\n", buf);
827 extract_token(buf, recs[i], 3, '|', sizeof buf);
831 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
836 recs = malloc(sizeof(char *));
837 else recs = realloc(recs,
838 (sizeof(char *)) * num_recs);
839 newprompt("Enter user name: ", buf, 28);
841 newprompt("Enter room name: ",
842 &buf[strlen(buf)], 28);
844 newprompt("Enter node name: ",
845 &buf[strlen(buf)], 16);
847 recs[num_recs-1] = strdup(buf);
851 i = intprompt("Delete which one",
855 for (j=i; j<num_recs; ++j)
861 for (i = 0; i < num_recs; ++i)
862 r += 1 + strlen(recs[i]);
863 listing = (char*) calloc(1, r);
865 err_printf("Can't save config - out of memory!\n");
868 if (num_recs) for (i = 0; i < num_recs; ++i) {
869 strcat(listing, recs[i]);
870 strcat(listing, "\n");
872 r = CtdlIPCSetSystemConfigByType(ipc, FILTERLIST, listing, buf);
874 scr_printf("%s\n", buf);
876 scr_printf("Wrote %d records.\n", num_recs);
882 quitting = !modified || boolprompt(
883 "Quit without saving", 0);
891 for (i=0; i<num_recs; ++i) free(recs[i]);
900 * POP3 aggregation client configuration
902 void do_pop3client_configuration(CtdlIPC *ipc)
912 char *listing = NULL;
913 char *other_listing = NULL;
917 r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
919 while(listing && !IsEmptyStr(listing)) {
920 extract_token(buf, listing, 0, '\n', sizeof buf);
921 remove_token(listing, 0, '\n');
922 extract_token(instr, buf, 0, '|', sizeof instr);
923 if (!strcasecmp(instr, "pop3client")) {
926 if (num_recs == 1) recs = malloc(sizeof(char *));
927 else recs = realloc(recs, (sizeof(char *)) * num_recs);
928 recs[num_recs-1] = malloc(SIZ);
929 strcpy(recs[num_recs-1], buf);
949 "---------------------------- "
950 "---------------------------- "
953 for (i=0; i<num_recs; ++i) {
955 scr_printf("%3d ", i+1);
956 extract_token(buf, recs[i], 1, '|', sizeof buf);
958 scr_printf("%-28s ", buf);
959 extract_token(buf, recs[i], 2, '|', sizeof buf);
960 color(BRIGHT_MAGENTA);
961 scr_printf("%-28s ", buf);
962 extract_token(buf, recs[i], 3, '|', sizeof buf);
964 scr_printf("%-16s\n", buf);
965 extract_token(buf, recs[i], 4, '|', sizeof buf);
969 ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
974 recs = malloc(sizeof(char *));
975 else recs = realloc(recs,
976 (sizeof(char *)) * num_recs);
977 strcpy(buf, "pop3client|");
978 newprompt("Enter host name: ",
979 &buf[strlen(buf)], 28);
981 newprompt("Enter user name: ",
982 &buf[strlen(buf)], 28);
984 newprompt("Enter password : ",
985 &buf[strlen(buf)], 16);
987 recs[num_recs-1] = strdup(buf);
991 i = intprompt("Delete which one",
995 for (j=i; j<num_recs; ++j)
1001 for (i = 0; i < num_recs; ++i) {
1002 r += 1 + strlen(recs[i]);
1004 listing = (char*) calloc(1, r);
1006 err_printf("Can't save config - out of memory!\n");
1009 if (num_recs) for (i = 0; i < num_recs; ++i) {
1010 strcat(listing, recs[i]);
1011 strcat(listing, "\n");
1014 /* Retrieve all the *other* records for merging */
1015 r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
1017 for (i=0; i<num_tokens(other_listing, '\n'); ++i) {
1018 extract_token(buf, other_listing, i, '\n', sizeof buf);
1019 if (strncasecmp(buf, "pop3client|", 11)) {
1020 listing = realloc(listing, strlen(listing) +
1022 strcat(listing, buf);
1023 strcat(listing, "\n");
1027 free(other_listing);
1028 r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
1033 scr_printf("%s\n", buf);
1035 scr_printf("Wrote %d records.\n", num_recs);
1041 quitting = !modified || boolprompt(
1042 "Quit without saving", 0);
1047 } while (!quitting);
1050 for (i=0; i<num_recs; ++i) free(recs[i]);