ec3179f8c37288f9df580283fc0a382320d1735a
[citadel.git] / citadel / control.c
1 /*
2  * This module handles states which are global to the entire server.
3  *
4  * Copyright (c) 1987-2021 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 <stdio.h>
16 #include <sys/file.h>
17 #include <libcitadel.h>
18
19 #include "ctdl_module.h"
20 #include "config.h"
21 #include "citserver.h"
22 #include "user_ops.h"
23
24 long control_highest_user = 0;
25
26 /*
27  * This is the legacy "control record" format for the message base.  If found
28  * on disk, its contents will be migrated into the system configuration.  Never
29  * change this.
30  */
31 struct legacy_ctrl_format {
32         long MMhighest;                 /* highest message number in file   */
33         unsigned MMflags;               /* Global system flags              */
34         long MMnextuser;                /* highest user number on system    */
35         long MMnextroom;                /* highest room number on system    */
36         int MM_hosted_upgrade_level;    /* Server-hosted upgrade level      */
37         int MM_fulltext_wordbreaker;    /* ID of wordbreaker in use         */
38         long MMfulltext;                /* highest message number indexed   */
39         int MMdbversion;                /* Version of Berkeley DB used on previous server run */
40 };
41
42
43 /*
44  * data that gets passed back and forth between control_find_highest() and its caller
45  */
46 struct cfh {
47         long highest_roomnum_found;
48         long highest_msgnum_found;
49 };
50
51
52 /*
53  * Callback to get highest room number when rebuilding message base metadata
54  *
55  * sanity_diag_mode (can be set by -s flag at startup) may be:
56  * 0 = attempt to fix inconsistencies
57  * 1 = show inconsistencies but don't repair them, exit after complete
58  * 2 = show inconsistencies but don't repair them, continue execution
59  */
60 void control_find_highest(struct ctdlroom *qrbuf, void *data) {
61         struct cfh *cfh = (struct cfh *)data;
62         struct cdbdata *cdbfr;
63         long *msglist;
64         int num_msgs=0;
65         int c;
66
67         if (qrbuf->QRnumber > cfh->highest_roomnum_found) {
68                 cfh->highest_roomnum_found = qrbuf->QRnumber;
69         }
70
71         /* Load the message list */
72         cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf->QRnumber, sizeof(long));
73         if (cdbfr != NULL) {
74                 msglist = (long *) cdbfr->ptr;
75                 num_msgs = cdbfr->len / sizeof(long);
76         }
77         else {
78                 return; /* No messages at all?  No further action. */
79         }
80
81         if (num_msgs > 0) {
82                 for (c=0; c<num_msgs; c++) {
83                         if (msglist[c] > cfh->highest_msgnum_found) {
84                                 cfh->highest_msgnum_found = msglist[c];
85                         }
86                 }
87         }
88
89         cdb_free(cdbfr);
90 }
91
92
93 /*
94  * Callback to get highest user number.
95  */
96 void control_find_user(char *username, void *out_data) {
97         struct ctdluser EachUser;
98
99         if (CtdlGetUser(&EachUser, username) != 0) {
100                 return;
101         }
102
103         if (EachUser.usernum > CtdlGetConfigLong("MMnextuser")) {
104                 syslog(LOG_DEBUG, "control: fixing MMnextuser %ld > %ld , found in %s",
105                         EachUser.usernum, CtdlGetConfigLong("MMnextuser"), EachUser.fullname
106                 );
107                 if (!sanity_diag_mode) {
108                         CtdlSetConfigLong("MMnextuser", EachUser.usernum);
109                 }
110         }
111 }
112
113
114 /*
115  * If we have a legacy format control record on disk, import it.
116  */
117 void migrate_legacy_control_record(void) {
118         FILE *fp = NULL;
119         struct legacy_ctrl_format c;
120         memset(&c, 0, sizeof(c));
121
122         fp = fopen("citadel.control", "rb+");
123         if (fp != NULL) {
124                 syslog(LOG_INFO, "control: legacy format record found -- importing to db");
125                 fread(&c, sizeof(struct legacy_ctrl_format), 1, fp);
126                 
127                 CtdlSetConfigLong(      "MMhighest",                    c.MMhighest);
128                 CtdlSetConfigInt(       "MMflags",                      c.MMflags);
129                 CtdlSetConfigLong(      "MMnextuser",                   c.MMnextuser);
130                 CtdlSetConfigLong(      "MMnextroom",                   c.MMnextroom);
131                 CtdlSetConfigInt(       "MM_hosted_upgrade_level",      c.MM_hosted_upgrade_level);
132                 CtdlSetConfigInt(       "MM_fulltext_wordbreaker",      c.MM_fulltext_wordbreaker);
133                 CtdlSetConfigLong(      "MMfulltext",                   c.MMfulltext);
134
135                 fclose(fp);
136                 if (unlink("citadel.control") != 0) {
137                         fprintf(stderr, "Unable to remove legacy control record after migrating it.\n");
138                         fprintf(stderr, "Exiting to prevent data corruption.\n");
139                         exit(CTDLEXIT_CONFIG);
140                 }
141         }
142 }
143
144
145 /*
146  * check_control   -  check the control record has sensible values for message, user and room numbers
147  */
148 void check_control(void) {
149
150         syslog(LOG_INFO, "control: sanity checking the recorded highest message and room numbers");
151         struct cfh cfh;
152         memset(&cfh, 0, sizeof(struct cfh));
153         CtdlForEachRoom(control_find_highest, &cfh);
154
155         if (cfh.highest_roomnum_found > CtdlGetConfigLong("MMnextroom")) {
156                 syslog(LOG_DEBUG, "control: fixing MMnextroom %ld > %ld", cfh.highest_roomnum_found, CtdlGetConfigLong("MMnextroom"));
157                 if (!sanity_diag_mode) {
158                         CtdlSetConfigLong("MMnextroom", cfh.highest_roomnum_found);
159                 }
160         }
161
162         if (cfh.highest_msgnum_found > CtdlGetConfigLong("MMhighest")) {
163                 syslog(LOG_DEBUG, "control: fixing MMhighest %ld > %ld", cfh.highest_msgnum_found, CtdlGetConfigLong("MMhighest"));
164                 if (!sanity_diag_mode) {
165                         CtdlSetConfigLong("MMhighest", cfh.highest_msgnum_found);
166                 }
167         }
168
169         syslog(LOG_INFO, "control: sanity checking the recorded highest user number");
170         ForEachUser(control_find_user, NULL);
171
172         syslog(LOG_INFO, "control: sanity checks complete");
173
174         if (sanity_diag_mode == 1) {
175                 syslog(LOG_INFO, "control: sanity check diagnostic mode is active - exiting now");
176                 abort();
177         }
178 }
179
180
181 /*
182  * get_new_message_number()  -  Obtain a new, unique ID to be used for a message.
183  */
184 long get_new_message_number(void)
185 {
186         long retval = 0L;
187         begin_critical_section(S_CONTROL);
188         retval = CtdlGetConfigLong("MMhighest");
189         ++retval;
190         CtdlSetConfigLong("MMhighest", retval);
191         end_critical_section(S_CONTROL);
192         return(retval);
193 }
194
195
196 /*
197  * CtdlGetCurrentMessageNumber()  -  Obtain the current highest message number in the system
198  * This provides a quick way to initialise a variable that might be used to indicate
199  * messages that should not be processed.   For example, an inbox rules script will use this
200  * to record determine that messages older than this should not be processed.
201  *
202  * (Why is this function here?  Can't we just go straight to the config variable it fetches?)
203  */
204 long CtdlGetCurrentMessageNumber(void)
205 {
206         return CtdlGetConfigLong("MMhighest");
207 }
208
209
210 /*
211  * get_new_user_number()  -  Obtain a new, unique ID to be used for a user.
212  */
213 long get_new_user_number(void)
214 {
215         long retval = 0L;
216         begin_critical_section(S_CONTROL);
217         retval = CtdlGetConfigLong("MMnextuser");
218         ++retval;
219         CtdlSetConfigLong("MMnextuser", retval);
220         end_critical_section(S_CONTROL);
221         return(retval);
222 }
223
224
225 /*
226  * get_new_room_number()  -  Obtain a new, unique ID to be used for a room.
227  */
228 long get_new_room_number(void)
229 {
230         long retval = 0L;
231         begin_critical_section(S_CONTROL);
232         retval = CtdlGetConfigLong("MMnextroom");
233         ++retval;
234         CtdlSetConfigLong("MMnextroom", retval);
235         end_critical_section(S_CONTROL);
236         return(retval);
237 }
238
239
240 /*
241  * Helper function for cmd_conf() to handle boolean values
242  */
243 int confbool(char *v)
244 {
245         if (IsEmptyStr(v)) return(0);
246         if (atoi(v) != 0) return(1);
247         return(0);
248 }
249
250
251 /* 
252  * Get or set global configuration options
253  */
254 void cmd_conf(char *argbuf)
255 {
256         char cmd[16];
257         char buf[1024];
258         int a, i;
259         long ii;
260         char *confptr;
261         char confname[128];
262
263         if (CtdlAccessCheck(ac_aide)) return;
264
265         extract_token(cmd, argbuf, 0, '|', sizeof cmd);
266
267         // CONF GET - retrieve system configuration in legacy format
268         // This is deprecated; please do not add fields or change their order.
269         if (!strcasecmp(cmd, "GET")) {
270                 cprintf("%d Configuration...\n", LISTING_FOLLOWS);
271                 cprintf("%s\n",         CtdlGetConfigStr("c_nodename"));
272                 cprintf("%s\n",         CtdlGetConfigStr("c_fqdn"));
273                 cprintf("%s\n",         CtdlGetConfigStr("c_humannode"));
274                 cprintf("xxx\n"); /* placeholder -- field no longer in use */
275                 cprintf("%d\n",         CtdlGetConfigInt("c_creataide"));
276                 cprintf("%d\n",         CtdlGetConfigInt("c_sleeping"));
277                 cprintf("%d\n",         CtdlGetConfigInt("c_initax"));
278                 cprintf("%d\n",         CtdlGetConfigInt("c_regiscall"));
279                 cprintf("%d\n",         CtdlGetConfigInt("c_twitdetect"));
280                 cprintf("%s\n",         CtdlGetConfigStr("c_twitroom"));
281                 cprintf("%s\n",         CtdlGetConfigStr("c_moreprompt"));
282                 cprintf("%d\n",         CtdlGetConfigInt("c_restrict"));
283                 cprintf("%s\n",         CtdlGetConfigStr("c_site_location"));
284                 cprintf("%s\n",         CtdlGetConfigStr("c_sysadm"));
285                 cprintf("%d\n",         CtdlGetConfigInt("c_maxsessions"));
286                 cprintf("xxx\n"); /* placeholder -- field no longer in use */
287                 cprintf("%d\n",         CtdlGetConfigInt("c_userpurge"));
288                 cprintf("%d\n",         CtdlGetConfigInt("c_roompurge"));
289                 cprintf("%s\n",         CtdlGetConfigStr("c_logpages"));
290                 cprintf("%d\n",         CtdlGetConfigInt("c_createax"));
291                 cprintf("%ld\n",        CtdlGetConfigLong("c_maxmsglen"));
292                 cprintf("%d\n",         CtdlGetConfigInt("c_min_workers"));
293                 cprintf("%d\n",         CtdlGetConfigInt("c_max_workers"));
294                 cprintf("%d\n",         CtdlGetConfigInt("c_pop3_port"));
295                 cprintf("%d\n",         CtdlGetConfigInt("c_smtp_port"));
296                 cprintf("%d\n",         CtdlGetConfigInt("c_rfc822_strict_from"));
297                 cprintf("%d\n",         CtdlGetConfigInt("c_aide_zap"));
298                 cprintf("%d\n",         CtdlGetConfigInt("c_imap_port"));
299                 cprintf("%ld\n",        CtdlGetConfigLong("c_net_freq"));
300                 cprintf("%d\n",         CtdlGetConfigInt("c_disable_newu"));
301                 cprintf("1\n"); /* niu */
302                 cprintf("%d\n",         CtdlGetConfigInt("c_purge_hour"));
303 #ifdef HAVE_LDAP
304                 cprintf("%s\n",         CtdlGetConfigStr("c_ldap_host"));
305                 cprintf("%d\n",         CtdlGetConfigInt("c_ldap_port"));
306                 cprintf("%s\n",         CtdlGetConfigStr("c_ldap_base_dn"));
307                 cprintf("%s\n",         CtdlGetConfigStr("c_ldap_bind_dn"));
308                 cprintf("%s\n",         CtdlGetConfigStr("c_ldap_bind_pw"));
309 #else
310                 cprintf("\n");
311                 cprintf("0\n");
312                 cprintf("\n");
313                 cprintf("\n");
314                 cprintf("\n");
315 #endif
316                 cprintf("%s\n",         CtdlGetConfigStr("c_ip_addr"));
317                 cprintf("%d\n",         CtdlGetConfigInt("c_msa_port"));
318                 cprintf("%d\n",         CtdlGetConfigInt("c_imaps_port"));
319                 cprintf("%d\n",         CtdlGetConfigInt("c_pop3s_port"));
320                 cprintf("%d\n",         CtdlGetConfigInt("c_smtps_port"));
321                 cprintf("%d\n",         CtdlGetConfigInt("c_enable_fulltext"));
322                 cprintf("%d\n",         CtdlGetConfigInt("c_auto_cull"));
323                 cprintf("1\n");
324                 cprintf("%d\n",         CtdlGetConfigInt("c_allow_spoofing"));
325                 cprintf("%d\n",         CtdlGetConfigInt("c_journal_email"));
326                 cprintf("%d\n",         CtdlGetConfigInt("c_journal_pubmsgs"));
327                 cprintf("%s\n",         CtdlGetConfigStr("c_journal_dest"));
328                 cprintf("%s\n",         CtdlGetConfigStr("c_default_cal_zone"));
329                 cprintf("%d\n",         CtdlGetConfigInt("c_pftcpdict_port"));
330                 cprintf("0\n");
331                 cprintf("%d\n",         CtdlGetConfigInt("c_auth_mode"));
332                 cprintf("\n");
333                 cprintf("\n");
334                 cprintf("\n");
335                 cprintf("\n");
336                 cprintf("%d\n",         CtdlGetConfigInt("c_rbl_at_greeting"));
337                 cprintf("\n");
338                 cprintf("\n");
339                 cprintf("%s\n",         CtdlGetConfigStr("c_pager_program"));
340                 cprintf("%d\n",         CtdlGetConfigInt("c_imap_keep_from"));
341                 cprintf("%d\n",         CtdlGetConfigInt("c_xmpp_c2s_port"));
342                 cprintf("%d\n",         CtdlGetConfigInt("c_xmpp_s2s_port"));
343                 cprintf("%ld\n",        CtdlGetConfigLong("c_pop3_fetch"));
344                 cprintf("%ld\n",        CtdlGetConfigLong("c_pop3_fastest"));
345                 cprintf("%d\n",         CtdlGetConfigInt("c_spam_flag_only"));
346                 cprintf("%d\n",         CtdlGetConfigInt("c_guest_logins"));
347                 cprintf("%d\n",         CtdlGetConfigInt("c_port_number"));
348                 cprintf("%d\n",         ctdluid);
349                 cprintf("%d\n",         CtdlGetConfigInt("c_nntp_port"));
350                 cprintf("%d\n",         CtdlGetConfigInt("c_nntps_port"));
351                 cprintf("000\n");
352         }
353
354         // CONF SET - set system configuration in legacy format
355         // This is deprecated; please do not add fields or change their order.
356         else if (!strcasecmp(cmd, "SET")) {
357                 unbuffer_output();
358                 cprintf("%d Send configuration...\n", SEND_LISTING);
359                 a = 0;
360                 while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) {
361                         switch (a) {
362                         case 0:
363                                 CtdlSetConfigStr("c_nodename", buf);
364                                 break;
365                         case 1:
366                                 CtdlSetConfigStr("c_fqdn", buf);
367                                 break;
368                         case 2:
369                                 CtdlSetConfigStr("c_humannode", buf);
370                                 break;
371                         case 3:
372                                 /* placeholder -- field no longer in use */
373                                 break;
374                         case 4:
375                                 CtdlSetConfigInt("c_creataide", confbool(buf));
376                                 break;
377                         case 5:
378                                 CtdlSetConfigInt("c_sleeping", atoi(buf));
379                                 break;
380                         case 6:
381                                 i = atoi(buf);
382                                 if (i < 1) i = 1;
383                                 if (i > 6) i = 6;
384                                 CtdlSetConfigInt("c_initax", i);
385                                 break;
386                         case 7:
387                                 CtdlSetConfigInt("c_regiscall", confbool(buf));
388                                 break;
389                         case 8:
390                                 CtdlSetConfigInt("c_twitdetect", confbool(buf));
391                                 break;
392                         case 9:
393                                 CtdlSetConfigStr("c_twitroom", buf);
394                                 break;
395                         case 10:
396                                 CtdlSetConfigStr("c_moreprompt", buf);
397                                 break;
398                         case 11:
399                                 CtdlSetConfigInt("c_restrict", confbool(buf));
400                                 break;
401                         case 12:
402                                 CtdlSetConfigStr("c_site_location", buf);
403                                 break;
404                         case 13:
405                                 CtdlSetConfigStr("c_sysadm", buf);
406                                 break;
407                         case 14:
408                                 i = atoi(buf);
409                                 if (i < 0) i = 0;
410                                 CtdlSetConfigInt("c_maxsessions", i);
411                                 break;
412                         case 15:
413                                 /* placeholder -- field no longer in use */
414                                 break;
415                         case 16:
416                                 CtdlSetConfigInt("c_userpurge", atoi(buf));
417                                 break;
418                         case 17:
419                                 CtdlSetConfigInt("c_roompurge", atoi(buf));
420                                 break;
421                         case 18:
422                                 CtdlSetConfigStr("c_logpages", buf);
423                                 break;
424                         case 19:
425                                 i = atoi(buf);
426                                 if (i < 1) i = 1;
427                                 if (i > 6) i = 6;
428                                 CtdlSetConfigInt("c_createax", i);
429                                 break;
430                         case 20:
431                                 ii = atol(buf);
432                                 if (ii >= 8192) {
433                                         CtdlSetConfigLong("c_maxmsglen", ii);
434                                 }
435                                 break;
436                         case 21:
437                                 i = atoi(buf);
438                                 if (i >= 3) {                                   // minimum value
439                                         CtdlSetConfigInt("c_min_workers", i);
440                                 }
441                                 break;
442                         case 22:
443                                 i = atoi(buf);
444                                 if (i >= CtdlGetConfigInt("c_min_workers")) {   // max must be >= min
445                                         CtdlSetConfigInt("c_max_workers", i);
446                                 }
447                                 break;
448                         case 23:
449                                 CtdlSetConfigInt("c_pop3_port", atoi(buf));
450                                 break;
451                         case 24:
452                                 CtdlSetConfigInt("c_smtp_port", atoi(buf));
453                                 break;
454                         case 25:
455                                 CtdlSetConfigInt("c_rfc822_strict_from", atoi(buf));
456                                 break;
457                         case 26:
458                                 CtdlSetConfigInt("c_aide_zap", confbool(buf));
459                                 break;
460                         case 27:
461                                 CtdlSetConfigInt("c_imap_port", atoi(buf));
462                                 break;
463                         case 28:
464                                 CtdlSetConfigLong("c_net_freq", atol(buf));
465                                 break;
466                         case 29:
467                                 CtdlSetConfigInt("c_disable_newu", confbool(buf));
468                                 break;
469                         case 30:
470                                 /* niu */
471                                 break;
472                         case 31:
473                                 i = atoi(buf);
474                                 if ((i >= 0) && (i <= 23)) {
475                                         CtdlSetConfigInt("c_purge_hour", i);
476                                 }
477                                 break;
478                         case 32:
479                                 CtdlSetConfigStr("c_ldap_host", buf);
480                                 break;
481                         case 33:
482                                 CtdlSetConfigInt("c_ldap_port", atoi(buf));
483                                 break;
484                         case 34:
485                                 CtdlSetConfigStr("c_ldap_base_dn", buf);
486                                 break;
487                         case 35:
488                                 CtdlSetConfigStr("c_ldap_bind_dn", buf);
489                                 break;
490                         case 36:
491                                 CtdlSetConfigStr("c_ldap_bind_pw", buf);
492                                 break;
493                         case 37:
494                                 CtdlSetConfigStr("c_ip_addr", buf);
495                                 break;
496                         case 38:
497                                 CtdlSetConfigInt("c_msa_port", atoi(buf));
498                                 break;
499                         case 39:
500                                 CtdlSetConfigInt("c_imaps_port", atoi(buf));
501                                 break;
502                         case 40:
503                                 CtdlSetConfigInt("c_pop3s_port", atoi(buf));
504                                 break;
505                         case 41:
506                                 CtdlSetConfigInt("c_smtps_port", atoi(buf));
507                                 break;
508                         case 42:
509                                 CtdlSetConfigInt("c_enable_fulltext", confbool(buf));
510                                 break;
511                         case 43:
512                                 CtdlSetConfigInt("c_auto_cull", confbool(buf));
513                                 break;
514                         case 44:
515                                 /* niu */
516                                 break;
517                         case 45:
518                                 CtdlSetConfigInt("c_allow_spoofing", confbool(buf));
519                                 break;
520                         case 46:
521                                 CtdlSetConfigInt("c_journal_email", confbool(buf));
522                                 break;
523                         case 47:
524                                 CtdlSetConfigInt("c_journal_pubmsgs", confbool(buf));
525                                 break;
526                         case 48:
527                                 CtdlSetConfigStr("c_journal_dest", buf);
528                                 break;
529                         case 49:
530                                 CtdlSetConfigStr("c_default_cal_zone", buf);
531                                 break;
532                         case 50:
533                                 CtdlSetConfigInt("c_pftcpdict_port", atoi(buf));
534                                 break;
535                         case 51:
536                                 /* niu */
537                                 break;
538                         case 52:
539                                 CtdlSetConfigInt("c_auth_mode", atoi(buf));
540                                 break;
541                         case 53:
542                                 /* niu */
543                                 break;
544                         case 54:
545                                 /* niu */
546                                 break;
547                         case 55:
548                                 /* niu */
549                                 break;
550                         case 56:
551                                 /* niu */
552                                 break;
553                         case 57:
554                                 CtdlSetConfigInt("c_rbl_at_greeting", confbool(buf));
555                                 break;
556                         case 58:
557                                 /* niu */
558                                 break;
559                         case 59:
560                                 /* niu */
561                                 break;
562                         case 60:
563                                 CtdlSetConfigStr("c_pager_program", buf);
564                                 break;
565                         case 61:
566                                 CtdlSetConfigInt("c_imap_keep_from", confbool(buf));
567                                 break;
568                         case 62:
569                                 CtdlSetConfigInt("c_xmpp_c2s_port", atoi(buf));
570                                 break;
571                         case 63:
572                                 CtdlSetConfigInt("c_xmpp_s2s_port", atoi(buf));
573                                 break;
574                         case 64:
575                                 CtdlSetConfigLong("c_pop3_fetch", atol(buf));
576                                 break;
577                         case 65:
578                                 CtdlSetConfigLong("c_pop3_fastest", atol(buf));
579                                 break;
580                         case 66:
581                                 CtdlSetConfigInt("c_spam_flag_only", confbool(buf));
582                                 break;
583                         case 67:
584                                 CtdlSetConfigInt("c_guest_logins", confbool(buf));
585                                 break;
586                         case 68:
587                                 CtdlSetConfigInt("c_port_number", atoi(buf));
588                                 break;
589                         case 69:
590                                 /* niu */
591                                 break;
592                         case 70:
593                                 CtdlSetConfigInt("c_nntp_port", atoi(buf));
594                                 break;
595                         case 71:
596                                 CtdlSetConfigInt("c_nntps_port", atoi(buf));
597                                 break;
598                         }
599                         ++a;
600                 }
601                 snprintf(buf, sizeof buf,
602                         "The global system configuration has been edited by %s.\n",
603                          (CC->logged_in ? CC->curr_user : "an administrator")
604                 );
605                 CtdlAideMessage(buf, "Citadel Configuration Manager Message");
606
607                 if (!IsEmptyStr(CtdlGetConfigStr("c_logpages")))
608                         CtdlCreateRoom(CtdlGetConfigStr("c_logpages"), 3, "", 0, 1, 1, VIEW_BBS);
609
610                 /* If full text indexing has been disabled, invalidate the
611                  * index so it doesn't try to use it later.
612                  */
613                 if (CtdlGetConfigInt("c_enable_fulltext") == 0) {
614                         CtdlSetConfigInt("MM_fulltext_wordbreaker", 0);
615                 }
616         }
617
618         // CONF GETSYS - retrieve arbitrary system configuration stanzas stored in the message base
619         else if (!strcasecmp(cmd, "GETSYS")) {
620                 extract_token(confname, argbuf, 1, '|', sizeof confname);
621                 confptr = CtdlGetSysConfig(confname);
622                 if (confptr != NULL) {
623                         long len; 
624
625                         len = strlen(confptr);
626                         cprintf("%d %s\n", LISTING_FOLLOWS, confname);
627                         client_write(confptr, len);
628                         if ((len > 0) && (confptr[len - 1] != 10))
629                                 client_write("\n", 1);
630                         cprintf("000\n");
631                         free(confptr);
632                 } else {
633                         cprintf("%d No such configuration.\n",
634                                 ERROR + ILLEGAL_VALUE);
635                 }
636         }
637
638         // CONF PUTSYS - store arbitrary system configuration stanzas in the message base
639         else if (!strcasecmp(cmd, "PUTSYS")) {
640                 extract_token(confname, argbuf, 1, '|', sizeof confname);
641                 unbuffer_output();
642                 cprintf("%d %s\n", SEND_LISTING, confname);
643                 confptr = CtdlReadMessageBody(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0);
644                 CtdlPutSysConfig(confname, confptr);
645                 free(confptr);
646         }
647
648         // CONF GETVAL - retrieve configuration variables from the database
649         // CONF LOADVAL - same thing but can handle variables bigger than 1 KB
650         else if ( (!strcasecmp(cmd, "GETVAL")) || (!strcasecmp(cmd, "LOADVAL")) ) {
651                 extract_token(confname, argbuf, 1, '|', sizeof confname);
652                 char *v = CtdlGetConfigStr(confname);
653                 if ( (v) && (!strcasecmp(cmd, "GETVAL")) ) {
654                         cprintf("%d %s|\n", CIT_OK, v);
655                 }
656                 else if ( (v) && (!strcasecmp(cmd, "LOADVAL")) ) {
657                         cprintf("%d %d\n", BINARY_FOLLOWS, (int)strlen(v));
658                         client_write(v, strlen(v));
659                 }
660                 else {
661                         cprintf("%d |\n", ERROR);
662                 }
663         }
664
665         // CONF PUTVAL - store configuration variables in the database
666         else if (!strcasecmp(cmd, "PUTVAL")) {
667                 if (num_tokens(argbuf, '|') < 3) {
668                         cprintf("%d name and value required\n", ERROR);
669                 }
670                 else {
671                         extract_token(confname, argbuf, 1, '|', sizeof confname);
672                         extract_token(buf, argbuf, 2, '|', sizeof buf);
673                         CtdlSetConfigStr(confname, buf);
674                         cprintf("%d setting '%s' to '%s'\n", CIT_OK, confname, buf);
675                 }
676         }
677
678         // CONF STOREVAL - store configuration variables in the database bigger than 1 KB
679         else if (!strcasecmp(cmd, "STOREVAL")) {
680                 if (num_tokens(argbuf, '|') < 3) {
681                         cprintf("%d name and length required\n", ERROR);
682                 }
683                 else {
684                         extract_token(confname, argbuf, 1, '|', sizeof confname);
685                         int bytes = extract_int(argbuf, 2);
686                         char *valbuf = malloc(bytes + 1);
687                         cprintf("%d %d\n", SEND_BINARY, bytes);
688                         client_read(valbuf, bytes);
689                         valbuf[bytes+1] = 0;
690                         CtdlSetConfigStr(confname, valbuf);
691                         free(valbuf);
692                 }
693         }
694
695         // CONF LISTVAL - list configuration variables in the database and their values
696         else if (!strcasecmp(cmd, "LISTVAL")) {
697                 struct cdbdata *cdbcfg;
698                 int keylen = 0;
699                 char *key = NULL;
700                 char *value = NULL;
701         
702                 cprintf("%d all configuration variables\n", LISTING_FOLLOWS);
703                 cdb_rewind(CDB_CONFIG);
704                 while (cdbcfg = cdb_next_item(CDB_CONFIG), cdbcfg != NULL) {
705                         if (cdbcfg->len < 1020) {
706                                 keylen = strlen(cdbcfg->ptr);
707                                 key = cdbcfg->ptr;
708                                 value = cdbcfg->ptr + keylen + 1;
709                                 cprintf("%s|%s\n", key, value);
710                         }
711                         cdb_free(cdbcfg);
712                 }
713                 cprintf("000\n");
714         }
715
716         else {
717                 cprintf("%d Illegal option(s) specified.\n", ERROR + ILLEGAL_VALUE);
718         }
719 }
720
721
722 typedef struct __ConfType {
723         ConstStr Name;
724         long Type;
725 }ConfType;
726
727 ConfType CfgNames[] = {
728         { {HKEY("localhost") },    0},
729         { {HKEY("directory") },    0},
730         { {HKEY("smarthost") },    2},
731         { {HKEY("fallbackhost") }, 2},
732         { {HKEY("rbl") },          3},
733         { {HKEY("spamassassin") }, 3},
734         { {HKEY("masqdomain") },   1},
735         { {HKEY("clamav") },       3},
736         { {HKEY("notify") },       3},
737         { {NULL, 0}, 0}
738 };
739
740 HashList *CfgNameHash = NULL;
741 void cmd_gvdn(char *argbuf)
742 {
743         const ConfType *pCfg;
744         char *confptr;
745         long min = atol(argbuf);
746         const char *Pos = NULL;
747         const char *PPos = NULL;
748         const char *HKey;
749         long HKLen;
750         StrBuf *Line;
751         StrBuf *Config;
752         StrBuf *Cfg;
753         StrBuf *CfgToken;
754         HashList *List;
755         HashPos *It;
756         void *vptr;
757         
758         List = NewHash(1, NULL);
759         Cfg = NewStrBufPlain(CtdlGetConfigStr("c_fqdn"), -1);
760         Put(List, SKEY(Cfg), Cfg, HFreeStrBuf);
761         Cfg = NULL;
762
763         confptr = CtdlGetSysConfig(INTERNETCFG);
764         Config = NewStrBufPlain(confptr, -1);
765         free(confptr);
766
767         Line = NewStrBufPlain(NULL, StrLength(Config));
768         CfgToken = NewStrBufPlain(NULL, StrLength(Config));
769         while (StrBufSipLine(Line, Config, &Pos))
770         {
771                 if (Cfg == NULL)
772                         Cfg = NewStrBufPlain(NULL, StrLength(Line));
773                 PPos = NULL;
774                 StrBufExtract_NextToken(Cfg, Line, &PPos, '|');
775                 StrBufExtract_NextToken(CfgToken, Line, &PPos, '|');
776                 if (GetHash(CfgNameHash, SKEY(CfgToken), &vptr) &&
777                     (vptr != NULL))
778                 {
779                         pCfg = (ConfType *) vptr;
780                         if (pCfg->Type <= min)
781                         {
782                                 Put(List, SKEY(Cfg), Cfg, HFreeStrBuf);
783                                 Cfg = NULL;
784                         }
785                 }
786         }
787
788         cprintf("%d Valid Domains\n", LISTING_FOLLOWS);
789         It = GetNewHashPos(List, 1);
790         while (GetNextHashPos(List, It, &HKLen, &HKey, &vptr))
791         {
792                 cputbuf(vptr);
793                 cprintf("\n");
794         }
795         cprintf("000\n");
796
797         DeleteHashPos(&It);
798         DeleteHash(&List);
799         FreeStrBuf(&Cfg);
800         FreeStrBuf(&Line);
801         FreeStrBuf(&CfgToken);
802         FreeStrBuf(&Config);
803 }
804
805 /*****************************************************************************/
806 /*                      MODULE INITIALIZATION STUFF                          */
807 /*****************************************************************************/
808
809 CTDL_MODULE_INIT(control)
810 {
811         if (!threading) {
812                 int i;
813
814                 CfgNameHash = NewHash(1, NULL);
815                 for (i = 0; CfgNames[i].Name.Key != NULL; i++)
816                         Put(CfgNameHash, CKEY(CfgNames[i].Name), &CfgNames[i], reference_free_handler);
817
818                 CtdlRegisterProtoHook(cmd_gvdn, "GVDN", "get valid domain names");
819                 CtdlRegisterProtoHook(cmd_conf, "CONF", "get/set system configuration");
820         }
821         /* return our id for the Log */
822         return "control";
823 }