silenced a silly little compiler warning
[citadel.git] / citadel / control.c
index d340fcd0539795eb36813ef9f91c2e90b4130bc6..b79689b08e12d68a5c27dc3209fc977fbb8a14f1 100644 (file)
 /*
- * $Id$
- *
  * This module handles states which are global to the entire server.
  *
+ * Copyright (c) 1987-2017 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  */
 
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
 #include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
 #include <sys/file.h>
-#include "citadel.h"
-#include "server.h"
-#include "control.h"
-#include "serv_extensions.h"
-#include "sysdep_decls.h"
-#include "support.h"
+#include <libcitadel.h>
+
+#include "ctdl_module.h"
 #include "config.h"
-#include "msgbase.h"
 #include "citserver.h"
-#include "tools.h"
-#include "room_ops.h"
+#include "user_ops.h"
 
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-
-struct CitControl CitControl;
-extern struct config config;
-FILE *control_fp = NULL;
+long control_highest_user = 0;
 
+/*
+ * This is the legacy "control record" format for the message base.  If found
+ * on disk, its contents will be migrated into the system configuration.  Never
+ * change this.
+ */
+struct legacy_ctrl_format {
+       long MMhighest;                 /* highest message number in file   */
+       unsigned MMflags;               /* Global system flags              */
+       long MMnextuser;                /* highest user number on system    */
+       long MMnextroom;                /* highest room number on system    */
+       int MM_hosted_upgrade_level;    /* Server-hosted upgrade level      */
+       int MM_fulltext_wordbreaker;    /* ID of wordbreaker in use         */
+       long MMfulltext;                /* highest message number indexed   */
+       int MMdbversion;                /* Version of Berkeley DB used on previous server run */
+};
 
 
 /*
- * lock_control  -  acquire a lock on the control record file.
- *                  This keeps multiple citservers from running concurrently.
+ * Callback to get highest room number when rebuilding message base metadata
  */
-void lock_control(void)
+void control_find_highest(struct ctdlroom *qrbuf, void *data)
 {
-       if (flock(fileno(control_fp), (LOCK_EX | LOCK_NB))) {
-               lprintf(CTDL_EMERG, "citserver: unable to lock %s.\n", file_citadel_control);
-               lprintf(CTDL_EMERG, "Is another citserver already running?\n");
-               exit(CTDLEXIT_CONTROL);
+       struct ctdlroom room;
+       struct cdbdata *cdbfr;
+       long *msglist;
+       int num_msgs=0;
+       int c;
+       int room_fixed = 0;
+       int message_fixed = 0;
+       
+       if (qrbuf->QRnumber > CtdlGetConfigLong("MMnextroom"))
+       {
+               CtdlSetConfigLong("MMnextroom", qrbuf->QRnumber);
+               room_fixed = 1;
+       }
+               
+       CtdlGetRoom (&room, qrbuf->QRname);
+       
+       /* Load the message list */
+       cdbfr = cdb_fetch(CDB_MSGLISTS, &room.QRnumber, sizeof(long));
+       if (cdbfr != NULL) {
+               msglist = (long *) cdbfr->ptr;
+               num_msgs = cdbfr->len / sizeof(long);
+       } else {
+               return; /* No messages at all?  No further action. */
+       }
+
+       if (num_msgs>0)
+       {
+               for (c=0; c<num_msgs; c++)
+               {
+                       if (msglist[c] > CtdlGetConfigLong("MMhighest"))
+                       {
+                               CtdlSetConfigLong("MMhighest", msglist[c]);
+                               message_fixed = 1;
+                       }
+               }
+       }
+       cdb_free(cdbfr);
+       if (room_fixed) {
+               syslog(LOG_INFO, "control: fixed room counter");
        }
+       if (message_fixed) {
+               syslog(LOG_INFO, "control: fixed message count");
+       }
+       return;
 }
 
 
 /*
- * get_control  -  read the control record into memory.
+ * Callback to get highest user number.
  */
-void get_control(void)
+void control_find_user (struct ctdluser *EachUser, void *out_data)
 {
-       /* Zero it out.  If the control record on disk is missing or short,
-        * the system functions with all control record fields initialized
-        * to zero.
-        */
-       memset(&CitControl, 0, sizeof(struct CitControl));
-       if (control_fp == NULL) {
-               control_fp = fopen(file_citadel_control, "rb+");
-               if (control_fp != NULL) {
-                       lock_control();
-                       fchown(fileno(control_fp), config.c_ctdluid, -1);
-               }
+       int user_fixed = 0;
+       
+       if (EachUser->usernum > CtdlGetConfigLong("MMnextuser"))
+       {
+               CtdlSetConfigLong("MMnextuser", EachUser->usernum);
+               user_fixed = 1;
        }
-       if (control_fp == NULL) {
-               control_fp = fopen(file_citadel_control, "wb+");
-               if (control_fp != NULL) {
-                       lock_control();
-                       fchown(fileno(control_fp), config.c_ctdluid, -1);
-                       memset(&CitControl, 0, sizeof(struct CitControl));
-                       fwrite(&CitControl, sizeof(struct CitControl),
-                              1, control_fp);
-                       rewind(control_fp);
-               }
-       }
-       if (control_fp == NULL) {
-               lprintf(CTDL_ALERT, "ERROR opening %s: %s\n",
-                               file_citadel_control,
-                               strerror(errno));
-               return;
-       }
-
-       rewind(control_fp);
-       fread(&CitControl, sizeof(struct CitControl), 1, control_fp);
+       if(user_fixed)
+               syslog(LOG_INFO, "control: fixed user count");
 }
 
+
 /*
- * put_control  -  write the control record to disk.
+ * If we have a legacy format control record on disk, import it.
  */
-void put_control(void)
+void migrate_legacy_control_record(void)
 {
+       FILE *fp = NULL;
+       struct legacy_ctrl_format c;
+       memset(&c, 0, sizeof(c));
+
+       fp = fopen(file_citadel_control, "rb+");
+       if (fp != NULL) {
+               syslog(LOG_INFO, "control: legacy format record found -- importing to db");
+               fread(&c, sizeof(struct legacy_ctrl_format), 1, fp);
+               
+               CtdlSetConfigLong(      "MMhighest",                    c.MMhighest);
+               CtdlSetConfigInt(       "MMflags",                      c.MMflags);
+               CtdlSetConfigLong(      "MMnextuser",                   c.MMnextuser);
+               CtdlSetConfigLong(      "MMnextroom",                   c.MMnextroom);
+               CtdlSetConfigInt(       "MM_hosted_upgrade_level",      c.MM_hosted_upgrade_level);
+               CtdlSetConfigInt(       "MM_fulltext_wordbreaker",      c.MM_fulltext_wordbreaker);
+               CtdlSetConfigLong(      "MMfulltext",                   c.MMfulltext);
 
-       if (control_fp != NULL) {
-               rewind(control_fp);
-               fwrite(&CitControl, sizeof(struct CitControl), 1,
-                      control_fp);
-               fflush(control_fp);
+               fclose(fp);
+               if (unlink(file_citadel_control) != 0) {
+                       fprintf(stderr, "Unable to remove legacy control record %s after migrating it.\n", file_citadel_control);
+                       fprintf(stderr, "Exiting to prevent data corruption.\n");
+                       exit(CTDLEXIT_CONFIG);
+               }
        }
 }
 
 
+/*
+ * check_control   -  check the control record has sensible values for message, user and room numbers
+ */
+void check_control(void)
+{
+       syslog(LOG_INFO, "control: sanity checking the recorded highest message, user, and room numbers");
+       CtdlForEachRoom(control_find_highest, NULL);
+       ForEachUser(control_find_user, NULL);
+}
+
+
 /*
  * get_new_message_number()  -  Obtain a new, unique ID to be used for a message.
  */
@@ -126,14 +160,28 @@ long get_new_message_number(void)
 {
        long retval = 0L;
        begin_critical_section(S_CONTROL);
-       get_control();
-       retval = ++CitControl.MMhighest;
-       put_control();
+       retval = CtdlGetConfigLong("MMhighest");
+       ++retval;
+       CtdlSetConfigLong("MMhighest", retval);
        end_critical_section(S_CONTROL);
        return(retval);
 }
 
 
+/*
+ * CtdlGetCurrentMessageNumber()  -  Obtain the current highest message number in the system
+ * This provides a quick way to initialise a variable that might be used to indicate
+ * messages that should not be processed. EG. a new Sieve script will use this
+ * to record determine that messages older than this should not be processed.
+ *
+ * (Why is this function here?  Can't we just go straight to the config variable it fetches?)
+ */
+long CtdlGetCurrentMessageNumber(void)
+{
+       return CtdlGetConfigLong("MMhighest");
+}
+
+
 /*
  * get_new_user_number()  -  Obtain a new, unique ID to be used for a user.
  */
@@ -141,15 +189,14 @@ long get_new_user_number(void)
 {
        long retval = 0L;
        begin_critical_section(S_CONTROL);
-       get_control();
-       retval = ++CitControl.MMnextuser;
-       put_control();
+       retval = CtdlGetConfigLong("MMnextuser");
+       ++retval;
+       CtdlSetConfigLong("MMnextuser", retval);
        end_critical_section(S_CONTROL);
        return(retval);
 }
 
 
-
 /*
  * get_new_room_number()  -  Obtain a new, unique ID to be used for a room.
  */
@@ -157,69 +204,87 @@ long get_new_room_number(void)
 {
        long retval = 0L;
        begin_critical_section(S_CONTROL);
-       get_control();
-       retval = ++CitControl.MMnextroom;
-       put_control();
+       retval = CtdlGetConfigLong("MMnextroom");
+       ++retval;
+       CtdlSetConfigLong("MMnextroom", retval);
        end_critical_section(S_CONTROL);
        return(retval);
 }
 
 
+/*
+ * Helper function for cmd_conf() to handle boolean values
+ */
+int confbool(char *v)
+{
+       if (IsEmptyStr(v)) return(0);
+       if (atoi(v) != 0) return(1);
+       return(0);
+}
+
 
 /* 
  * Get or set global configuration options
+ *
+ * IF YOU ADD OR CHANGE FIELDS HERE, YOU *MUST* DOCUMENT YOUR CHANGES AT:
+ * http://www.citadel.org/doku.php/documentation:appproto:system_config
+ *
  */
 void cmd_conf(char *argbuf)
 {
        char cmd[16];
-       char buf[256];
-       int a;
+       char buf[1024];
+       int a, i;
+       long ii;
        char *confptr;
        char confname[128];
 
        if (CtdlAccessCheck(ac_aide)) return;
 
        extract_token(cmd, argbuf, 0, '|', sizeof cmd);
+
+       // CONF GET - retrieve system configuration in legacy format
+       // This is deprecated; please do not add fields or change their order.
        if (!strcasecmp(cmd, "GET")) {
                cprintf("%d Configuration...\n", LISTING_FOLLOWS);
-               cprintf("%s\n", config.c_nodename);
-               cprintf("%s\n", config.c_fqdn);
-               cprintf("%s\n", config.c_humannode);
-               cprintf("%s\n", config.c_phonenum);
-               cprintf("%d\n", config.c_creataide);
-               cprintf("%d\n", config.c_sleeping);
-               cprintf("%d\n", config.c_initax);
-               cprintf("%d\n", config.c_regiscall);
-               cprintf("%d\n", config.c_twitdetect);
-               cprintf("%s\n", config.c_twitroom);
-               cprintf("%s\n", config.c_moreprompt);
-               cprintf("%d\n", config.c_restrict);
-               cprintf("%s\n", config.c_site_location);
-               cprintf("%s\n", config.c_sysadm);
-               cprintf("%d\n", config.c_maxsessions);
+               cprintf("%s\n",         CtdlGetConfigStr("c_nodename"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_fqdn"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_humannode"));
+               cprintf("xxx\n"); /* placeholder -- field no longer in use */
+               cprintf("%d\n",         CtdlGetConfigInt("c_creataide"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_sleeping"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_initax"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_regiscall"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_twitdetect"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_twitroom"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_moreprompt"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_restrict"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_site_location"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_sysadm"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_maxsessions"));
                cprintf("xxx\n"); /* placeholder -- field no longer in use */
-               cprintf("%d\n", config.c_userpurge);
-               cprintf("%d\n", config.c_roompurge);
-               cprintf("%s\n", config.c_logpages);
-               cprintf("%d\n", config.c_createax);
-               cprintf("%ld\n", config.c_maxmsglen);
-               cprintf("%d\n", config.c_min_workers);
-               cprintf("%d\n", config.c_max_workers);
-               cprintf("%d\n", config.c_pop3_port);
-               cprintf("%d\n", config.c_smtp_port);
-               cprintf("%d\n", config.c_rfc822_strict_from);
-               cprintf("%d\n", config.c_aide_zap);
-               cprintf("%d\n", config.c_imap_port);
-               cprintf("%ld\n", config.c_net_freq);
-               cprintf("%d\n", config.c_disable_newu);
+               cprintf("%d\n",         CtdlGetConfigInt("c_userpurge"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_roompurge"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_logpages"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_createax"));
+               cprintf("%ld\n",        CtdlGetConfigLong("c_maxmsglen"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_min_workers"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_max_workers"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_pop3_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_smtp_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_rfc822_strict_from"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_aide_zap"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_imap_port"));
+               cprintf("%ld\n",        CtdlGetConfigLong("c_net_freq"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_disable_newu"));
                cprintf("1\n"); /* niu */
-               cprintf("%d\n", config.c_purge_hour);
+               cprintf("%d\n",         CtdlGetConfigInt("c_purge_hour"));
 #ifdef HAVE_LDAP
-               cprintf("%s\n", config.c_ldap_host);
-               cprintf("%d\n", config.c_ldap_port);
-               cprintf("%s\n", config.c_ldap_base_dn);
-               cprintf("%s\n", config.c_ldap_bind_dn);
-               cprintf("%s\n", config.c_ldap_bind_pw);
+               cprintf("%s\n",         CtdlGetConfigStr("c_ldap_host"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_ldap_port"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_ldap_base_dn"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_ldap_bind_dn"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_ldap_bind_pw"));
 #else
                cprintf("\n");
                cprintf("0\n");
@@ -227,278 +292,319 @@ void cmd_conf(char *argbuf)
                cprintf("\n");
                cprintf("\n");
 #endif
-               cprintf("%s\n", config.c_ip_addr);
-               cprintf("%d\n", config.c_msa_port);
-               cprintf("%d\n", config.c_imaps_port);
-               cprintf("%d\n", config.c_pop3s_port);
-               cprintf("%d\n", config.c_smtps_port);
-               cprintf("%d\n", config.c_enable_fulltext);
-               cprintf("%d\n", config.c_auto_cull);
-               cprintf("%d\n", config.c_instant_expunge);
-               cprintf("%d\n", config.c_allow_spoofing);
-               cprintf("%d\n", config.c_journal_email);
-               cprintf("%d\n", config.c_journal_pubmsgs);
-               cprintf("%s\n", config.c_journal_dest);
-               cprintf("%s\n", config.c_default_cal_zone);
-               cprintf("%d\n", config.c_pftcpdict_port);
-               cprintf("%d\n", config.c_managesieve_port);
-               cprintf("%d\n", config.c_auth_mode);
-               cprintf("%s\n", config.c_funambol_host);
-               cprintf("%d\n", config.c_funambol_port);
-               cprintf("%s\n", config.c_funambol_source);
-               cprintf("%s\n", config.c_funambol_auth);
+               cprintf("%s\n",         CtdlGetConfigStr("c_ip_addr"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_msa_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_imaps_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_pop3s_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_smtps_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_enable_fulltext"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_auto_cull"));
+               cprintf("1\n");
+               cprintf("%d\n",         CtdlGetConfigInt("c_allow_spoofing"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_journal_email"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_journal_pubmsgs"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_journal_dest"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_default_cal_zone"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_pftcpdict_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_managesieve_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_auth_mode"));
+               cprintf("\n");
+               cprintf("\n");
+               cprintf("\n");
+               cprintf("\n");
+               cprintf("%d\n",         CtdlGetConfigInt("c_rbl_at_greeting"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_master_user"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_master_pass"));
+               cprintf("%s\n",         CtdlGetConfigStr("c_pager_program"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_imap_keep_from"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_xmpp_c2s_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_xmpp_s2s_port"));
+               cprintf("%ld\n",        CtdlGetConfigLong("c_pop3_fetch"));
+               cprintf("%ld\n",        CtdlGetConfigLong("c_pop3_fastest"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_spam_flag_only"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_guest_logins"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_port_number"));
+               cprintf("%d\n",         ctdluid);
+               cprintf("%d\n",         CtdlGetConfigInt("c_nntp_port"));
+               cprintf("%d\n",         CtdlGetConfigInt("c_nntps_port"));
                cprintf("000\n");
        }
 
+       // CONF SET - set system configuration in legacy format
+       // This is deprecated; please do not add fields or change their order.
        else if (!strcasecmp(cmd, "SET")) {
                unbuffer_output();
                cprintf("%d Send configuration...\n", SEND_LISTING);
                a = 0;
-               while (client_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) {
                        switch (a) {
                        case 0:
-                               safestrncpy(config.c_nodename, buf,
-                                           sizeof config.c_nodename);
+                               CtdlSetConfigStr("c_nodename", buf);
                                break;
                        case 1:
-                               safestrncpy(config.c_fqdn, buf,
-                                           sizeof config.c_fqdn);
+                               CtdlSetConfigStr("c_fqdn", buf);
                                break;
                        case 2:
-                               safestrncpy(config.c_humannode, buf,
-                                           sizeof config.c_humannode);
+                               CtdlSetConfigStr("c_humannode", buf);
                                break;
                        case 3:
-                               safestrncpy(config.c_phonenum, buf,
-                                           sizeof config.c_phonenum);
+                               /* placeholder -- field no longer in use */
                                break;
                        case 4:
-                               config.c_creataide = atoi(buf);
+                               CtdlSetConfigInt("c_creataide", confbool(buf));
                                break;
                        case 5:
-                               config.c_sleeping = atoi(buf);
+                               CtdlSetConfigInt("c_sleeping", atoi(buf));
                                break;
                        case 6:
-                               config.c_initax = atoi(buf);
-                               if (config.c_initax < 1)
-                                       config.c_initax = 1;
-                               if (config.c_initax > 6)
-                                       config.c_initax = 6;
+                               i = atoi(buf);
+                               if (i < 1) i = 1;
+                               if (i > 6) i = 6;
+                               CtdlSetConfigInt("c_initax", i);
                                break;
                        case 7:
-                               config.c_regiscall = atoi(buf);
-                               if (config.c_regiscall != 0)
-                                       config.c_regiscall = 1;
+                               CtdlSetConfigInt("c_regiscall", confbool(buf));
                                break;
                        case 8:
-                               config.c_twitdetect = atoi(buf);
-                               if (config.c_twitdetect != 0)
-                                       config.c_twitdetect = 1;
+                               CtdlSetConfigInt("c_twitdetect", confbool(buf));
                                break;
                        case 9:
-                               safestrncpy(config.c_twitroom, buf,
-                                           sizeof config.c_twitroom);
+                               CtdlSetConfigStr("c_twitroom", buf);
                                break;
                        case 10:
-                               safestrncpy(config.c_moreprompt, buf,
-                                           sizeof config.c_moreprompt);
+                               CtdlSetConfigStr("c_moreprompt", buf);
                                break;
                        case 11:
-                               config.c_restrict = atoi(buf);
-                               if (config.c_restrict != 0)
-                                       config.c_restrict = 1;
+                               CtdlSetConfigInt("c_restrict", confbool(buf));
                                break;
                        case 12:
-                               safestrncpy(config.c_site_location, buf,
-                                           sizeof config.c_site_location);
+                               CtdlSetConfigStr("c_site_location", buf);
                                break;
                        case 13:
-                               safestrncpy(config.c_sysadm, buf,
-                                           sizeof config.c_sysadm);
+                               CtdlSetConfigStr("c_sysadm", buf);
                                break;
                        case 14:
-                               config.c_maxsessions = atoi(buf);
-                               if (config.c_maxsessions < 0)
-                                       config.c_maxsessions = 0;
+                               i = atoi(buf);
+                               if (i < 0) i = 0;
+                               CtdlSetConfigInt("c_maxsessions", i);
                                break;
                        case 15:
                                /* placeholder -- field no longer in use */
                                break;
                        case 16:
-                               config.c_userpurge = atoi(buf);
+                               CtdlSetConfigInt("c_userpurge", atoi(buf));
                                break;
                        case 17:
-                               config.c_roompurge = atoi(buf);
+                               CtdlSetConfigInt("c_roompurge", atoi(buf));
                                break;
                        case 18:
-                               safestrncpy(config.c_logpages, buf,
-                                           sizeof config.c_logpages);
+                               CtdlSetConfigStr("c_logpages", buf);
                                break;
                        case 19:
-                               config.c_createax = atoi(buf);
-                               if (config.c_createax < 1)
-                                       config.c_createax = 1;
-                               if (config.c_createax > 6)
-                                       config.c_createax = 6;
+                               i = atoi(buf);
+                               if (i < 1) i = 1;
+                               if (i > 6) i = 6;
+                               CtdlSetConfigInt("c_createax", i);
                                break;
                        case 20:
-                               if (atoi(buf) >= 8192)
-                                       config.c_maxmsglen = atoi(buf);
+                               ii = atol(buf);
+                               if (ii >= 8192) {
+                                       CtdlSetConfigLong("c_maxmsglen", ii);
+                               }
                                break;
                        case 21:
-                               if (atoi(buf) >= 2)
-                                       config.c_min_workers = atoi(buf);
+                               i = atoi(buf);
+                               if (i >= 3) {                                   // minimum value
+                                       CtdlSetConfigInt("c_min_workers", i);
+                               }
+                               break;
                        case 22:
-                               if (atoi(buf) >= config.c_min_workers)
-                                       config.c_max_workers = atoi(buf);
+                               i = atoi(buf);
+                               if (i >= CtdlGetConfigInt("c_min_workers")) {   // max must be >= min
+                                       CtdlSetConfigInt("c_max_workers", i);
+                               }
+                               break;
                        case 23:
-                               config.c_pop3_port = atoi(buf);
+                               CtdlSetConfigInt("c_pop3_port", atoi(buf));
                                break;
                        case 24:
-                               config.c_smtp_port = atoi(buf);
+                               CtdlSetConfigInt("c_smtp_port", atoi(buf));
                                break;
                        case 25:
-                               config.c_rfc822_strict_from = atoi(buf);
+                               CtdlSetConfigInt("c_rfc822_strict_from", atoi(buf));
                                break;
                        case 26:
-                               config.c_aide_zap = atoi(buf);
-                               if (config.c_aide_zap != 0)
-                                       config.c_aide_zap = 1;
+                               CtdlSetConfigInt("c_aide_zap", confbool(buf));
                                break;
                        case 27:
-                               config.c_imap_port = atoi(buf);
+                               CtdlSetConfigInt("c_imap_port", atoi(buf));
                                break;
                        case 28:
-                               config.c_net_freq = atol(buf);
+                               CtdlSetConfigLong("c_net_freq", atol(buf));
                                break;
                        case 29:
-                               config.c_disable_newu = atoi(buf);
-                               if (config.c_disable_newu != 0)
-                                       config.c_disable_newu = 1;
+                               CtdlSetConfigInt("c_disable_newu", confbool(buf));
                                break;
                        case 30:
                                /* niu */
                                break;
                        case 31:
-                               if ((config.c_purge_hour >= 0)
-                                  && (config.c_purge_hour <= 23)) {
-                                       config.c_purge_hour = atoi(buf);
+                               i = atoi(buf);
+                               if ((i >= 0) && (i <= 23)) {
+                                       CtdlSetConfigInt("c_purge_hour", i);
                                }
                                break;
-#ifdef HAVE_LDAP
                        case 32:
-                               safestrncpy(config.c_ldap_host, buf,
-                                           sizeof config.c_ldap_host);
+                               CtdlSetConfigStr("c_ldap_host", buf);
                                break;
                        case 33:
-                               config.c_ldap_port = atoi(buf);
+                               CtdlSetConfigInt("c_ldap_port", atoi(buf));
                                break;
                        case 34:
-                               safestrncpy(config.c_ldap_base_dn, buf,
-                                           sizeof config.c_ldap_base_dn);
+                               CtdlSetConfigStr("c_ldap_base_dn", buf);
                                break;
                        case 35:
-                               safestrncpy(config.c_ldap_bind_dn, buf,
-                                           sizeof config.c_ldap_bind_dn);
+                               CtdlSetConfigStr("c_ldap_bind_dn", buf);
                                break;
                        case 36:
-                               safestrncpy(config.c_ldap_bind_pw, buf,
-                                           sizeof config.c_ldap_bind_pw);
+                               CtdlSetConfigStr("c_ldap_bind_pw", buf);
                                break;
-#endif
                        case 37:
-                               safestrncpy(config.c_ip_addr, buf,
-                                               sizeof config.c_ip_addr);
+                               CtdlSetConfigStr("c_ip_addr", buf);
+                               break;
                        case 38:
-                               config.c_msa_port = atoi(buf);
+                               CtdlSetConfigInt("c_msa_port", atoi(buf));
                                break;
                        case 39:
-                               config.c_imaps_port = atoi(buf);
+                               CtdlSetConfigInt("c_imaps_port", atoi(buf));
                                break;
                        case 40:
-                               config.c_pop3s_port = atoi(buf);
+                               CtdlSetConfigInt("c_pop3s_port", atoi(buf));
                                break;
                        case 41:
-                               config.c_smtps_port = atoi(buf);
+                               CtdlSetConfigInt("c_smtps_port", atoi(buf));
                                break;
                        case 42:
-                               config.c_enable_fulltext = atoi(buf);
+                               CtdlSetConfigInt("c_enable_fulltext", confbool(buf));
                                break;
                        case 43:
-                               config.c_auto_cull = atoi(buf);
+                               CtdlSetConfigInt("c_auto_cull", confbool(buf));
                                break;
                        case 44:
-                               config.c_instant_expunge = atoi(buf);
+                               /* niu */
                                break;
                        case 45:
-                               config.c_allow_spoofing = atoi(buf);
+                               CtdlSetConfigInt("c_allow_spoofing", confbool(buf));
                                break;
                        case 46:
-                               config.c_journal_email = atoi(buf);
+                               CtdlSetConfigInt("c_journal_email", confbool(buf));
                                break;
                        case 47:
-                               config.c_journal_pubmsgs = atoi(buf);
+                               CtdlSetConfigInt("c_journal_pubmsgs", confbool(buf));
                                break;
                        case 48:
-                               safestrncpy(config.c_journal_dest, buf,
-                                               sizeof config.c_journal_dest);
+                               CtdlSetConfigStr("c_journal_dest", buf);
+                               break;
                        case 49:
-                               safestrncpy(config.c_default_cal_zone, buf,
-                                               sizeof config.c_default_cal_zone);
+                               CtdlSetConfigStr("c_default_cal_zone", buf);
                                break;
                        case 50:
-                               config.c_pftcpdict_port = atoi(buf);
+                               CtdlSetConfigInt("c_pftcpdict_port", atoi(buf));
                                break;
                        case 51:
-                               config.c_managesieve_port = atoi(buf);
+                               CtdlSetConfigInt("c_managesieve_port", atoi(buf));
                                break;
                        case 52:
-                               config.c_auth_mode = atoi(buf);
+                               CtdlSetConfigInt("c_auth_mode", atoi(buf));
+                               break;
                        case 53:
-                               safestrncpy(config.c_funambol_host, buf,
-                                       sizeof config.c_funambol_host);
+                               /* niu */
                                break;
                        case 54:
-                               config.c_funambol_port = atoi(buf);
+                               /* niu */
                                break;
                        case 55:
-                               safestrncpy(config.c_funambol_source,
-                                       buf, 
-                                       sizeof config.c_funambol_source);
+                               /* niu */
                                break;
                        case 56:
-                               safestrncpy(config.c_funambol_auth,
-                                       buf,
-                                       sizeof config.c_funambol_auth);
+                               /* niu */
+                               break;
+                       case 57:
+                               CtdlSetConfigInt("c_rbl_at_greeting", confbool(buf));
+                               break;
+                       case 58:
+                               CtdlSetConfigStr("c_master_user", buf);
+                               break;
+                       case 59:
+                               CtdlSetConfigStr("c_master_pass", buf);
+                               break;
+                       case 60:
+                               CtdlSetConfigStr("c_pager_program", buf);
+                               break;
+                       case 61:
+                               CtdlSetConfigInt("c_imap_keep_from", confbool(buf));
+                               break;
+                       case 62:
+                               CtdlSetConfigInt("c_xmpp_c2s_port", atoi(buf));
+                               break;
+                       case 63:
+                               CtdlSetConfigInt("c_xmpp_s2s_port", atoi(buf));
+                               break;
+                       case 64:
+                               CtdlSetConfigLong("c_pop3_fetch", atol(buf));
+                               break;
+                       case 65:
+                               CtdlSetConfigLong("c_pop3_fastest", atol(buf));
+                               break;
+                       case 66:
+                               CtdlSetConfigInt("c_spam_flag_only", confbool(buf));
+                               break;
+                       case 67:
+                               CtdlSetConfigInt("c_guest_logins", confbool(buf));
+                               break;
+                       case 68:
+                               CtdlSetConfigInt("c_port_number", atoi(buf));
+                               break;
+                       case 69:
+                               /* niu */
+                               break;
+                       case 70:
+                               CtdlSetConfigInt("c_nntp_port", atoi(buf));
+                               break;
+                       case 71:
+                               CtdlSetConfigInt("c_nntps_port", atoi(buf));
                                break;
                        }
                        ++a;
                }
-               put_config();
                snprintf(buf, sizeof buf,
-                        "The global system configuration has been edited by %s.\n",
-                        CC->curr_user);
-               aide_message(buf,"Citadel Configuration Manager Message");
+                       "The global system configuration has been edited by %s.\n",
+                        (CC->logged_in ? CC->curr_user : "an administrator")
+               );
+               CtdlAideMessage(buf, "Citadel Configuration Manager Message");
 
-               if (strlen(config.c_logpages) > 0)
-                       create_room(config.c_logpages, 3, "", 0, 1, 1, VIEW_BBS);
+               if (!IsEmptyStr(CtdlGetConfigStr("c_logpages")))
+                       CtdlCreateRoom(CtdlGetConfigStr("c_logpages"), 3, "", 0, 1, 1, VIEW_BBS);
 
                /* If full text indexing has been disabled, invalidate the
                 * index so it doesn't try to use it later.
                 */
-               if (config.c_enable_fulltext == 0) {
-                       CitControl.fulltext_wordbreaker = 0;
-                       put_control();
+               if (CtdlGetConfigInt("c_enable_fulltext") == 0) {
+                       CtdlSetConfigInt("MM_fulltext_wordbreaker", 0);
                }
        }
 
+       // CONF GETSYS - retrieve arbitrary system configuration stanzas stored in the message base
        else if (!strcasecmp(cmd, "GETSYS")) {
                extract_token(confname, argbuf, 1, '|', sizeof confname);
                confptr = CtdlGetSysConfig(confname);
                if (confptr != NULL) {
+                       long len; 
+
+                       len = strlen(confptr);
                        cprintf("%d %s\n", LISTING_FOLLOWS, confname);
-                       client_write(confptr, strlen(confptr));
-                       if (confptr[strlen(confptr) - 1] != 10)
+                       client_write(confptr, len);
+                       if ((len > 0) && (confptr[len - 1] != 10))
                                client_write("\n", 1);
                        cprintf("000\n");
                        free(confptr);
@@ -508,18 +614,195 @@ void cmd_conf(char *argbuf)
                }
        }
 
+       // CONF PUTSYS - store arbitrary system configuration stanzas in the message base
        else if (!strcasecmp(cmd, "PUTSYS")) {
                extract_token(confname, argbuf, 1, '|', sizeof confname);
                unbuffer_output();
                cprintf("%d %s\n", SEND_LISTING, confname);
-               confptr = CtdlReadMessageBody("000",
-                               config.c_maxmsglen, NULL, 0);
+               confptr = CtdlReadMessageBody(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0);
                CtdlPutSysConfig(confname, confptr);
                free(confptr);
        }
 
+       // CONF GETVAL - retrieve configuration variables from the database
+       // CONF LOADVAL - same thing but can handle variables bigger than 1 KB
+       else if ( (!strcasecmp(cmd, "GETVAL")) || (!strcasecmp(cmd, "LOADVAL")) ) {
+               extract_token(confname, argbuf, 1, '|', sizeof confname);
+               char *v = CtdlGetConfigStr(confname);
+               if ( (v) && (!strcasecmp(cmd, "GETVAL")) ) {
+                       cprintf("%d %s|\n", CIT_OK, v);
+               }
+               else if ( (v) && (!strcasecmp(cmd, "LOADVAL")) ) {
+                       cprintf("%d %d\n", BINARY_FOLLOWS, (int)strlen(v));
+                       client_write(v, strlen(v));
+               }
+               else {
+                       cprintf("%d |\n", ERROR);
+               }
+       }
+
+       // CONF PUTVAL - store configuration variables in the database
+       else if (!strcasecmp(cmd, "PUTVAL")) {
+               if (num_tokens(argbuf, '|') < 3) {
+                       cprintf("%d name and value required\n", ERROR);
+               }
+               else {
+                       extract_token(confname, argbuf, 1, '|', sizeof confname);
+                       extract_token(buf, argbuf, 2, '|', sizeof buf);
+                       CtdlSetConfigStr(confname, buf);
+                       cprintf("%d setting '%s' to '%s'\n", CIT_OK, confname, buf);
+               }
+       }
+
+       // CONF STOREVAL - store configuration variables in the database bigger than 1 KB
+       else if (!strcasecmp(cmd, "STOREVAL")) {
+               if (num_tokens(argbuf, '|') < 3) {
+                       cprintf("%d name and length required\n", ERROR);
+               }
+               else {
+                       extract_token(confname, argbuf, 1, '|', sizeof confname);
+                       int bytes = extract_int(argbuf, 2);
+                       char *valbuf = malloc(bytes + 1);
+                       cprintf("%d %d\n", SEND_BINARY, bytes);
+                       client_read(valbuf, bytes);
+                       valbuf[bytes+1] = 0;
+                       CtdlSetConfigStr(confname, valbuf);
+                       free(valbuf);
+               }
+       }
+
+       // CONF LISTVAL - list configuration variables in the database and their values
+       else if (!strcasecmp(cmd, "LISTVAL")) {
+               struct cdbdata *cdbcfg;
+               int keylen = 0;
+               char *key = NULL;
+               char *value = NULL;
+       
+               cprintf("%d all configuration variables\n", LISTING_FOLLOWS);
+               cdb_rewind(CDB_CONFIG);
+               while (cdbcfg = cdb_next_item(CDB_CONFIG), cdbcfg != NULL) {
+                       if (cdbcfg->len < 1020) {
+                               keylen = strlen(cdbcfg->ptr);
+                               key = cdbcfg->ptr;
+                               value = cdbcfg->ptr + keylen + 1;
+                               cprintf("%s|%s\n", key, value);
+                       }
+                       cdb_free(cdbcfg);
+               }
+               cprintf("000\n");
+       }
+
        else {
-               cprintf("%d Illegal option(s) specified.\n",
-                       ERROR + ILLEGAL_VALUE);
+               cprintf("%d Illegal option(s) specified.\n", ERROR + ILLEGAL_VALUE);
+       }
+}
+
+
+typedef struct __ConfType {
+       ConstStr Name;
+       long Type;
+}ConfType;
+
+ConfType CfgNames[] = {
+       { {HKEY("localhost") },    0},
+       { {HKEY("directory") },    0},
+       { {HKEY("smarthost") },    2},
+       { {HKEY("fallbackhost") }, 2},
+       { {HKEY("rbl") },          3},
+       { {HKEY("spamassassin") }, 3},
+       { {HKEY("masqdomain") },   1},
+       { {HKEY("clamav") },       3},
+       { {HKEY("notify") },       3},
+       { {NULL, 0}, 0}
+};
+
+HashList *CfgNameHash = NULL;
+void cmd_gvdn(char *argbuf)
+{
+       const ConfType *pCfg;
+       char *confptr;
+       long min = atol(argbuf);
+       const char *Pos = NULL;
+       const char *PPos = NULL;
+       const char *HKey;
+       long HKLen;
+       StrBuf *Line;
+       StrBuf *Config;
+       StrBuf *Cfg;
+       StrBuf *CfgToken;
+       HashList *List;
+       HashPos *It;
+       void *vptr;
+       
+       List = NewHash(1, NULL);
+       Cfg = NewStrBufPlain(CtdlGetConfigStr("c_fqdn"), -1);
+       Put(List, SKEY(Cfg), Cfg, HFreeStrBuf);
+       Cfg = NULL;
+
+       confptr = CtdlGetSysConfig(INTERNETCFG);
+       Config = NewStrBufPlain(confptr, -1);
+       free(confptr);
+
+       Line = NewStrBufPlain(NULL, StrLength(Config));
+       CfgToken = NewStrBufPlain(NULL, StrLength(Config));
+       while (StrBufSipLine(Line, Config, &Pos))
+       {
+               if (Cfg == NULL)
+                       Cfg = NewStrBufPlain(NULL, StrLength(Line));
+               PPos = NULL;
+               StrBufExtract_NextToken(Cfg, Line, &PPos, '|');
+               StrBufExtract_NextToken(CfgToken, Line, &PPos, '|');
+               if (GetHash(CfgNameHash, SKEY(CfgToken), &vptr) &&
+                   (vptr != NULL))
+               {
+                       pCfg = (ConfType *) vptr;
+                       if (pCfg->Type <= min)
+                       {
+                               Put(List, SKEY(Cfg), Cfg, HFreeStrBuf);
+                               Cfg = NULL;
+                       }
+               }
+       }
+
+       cprintf("%d Valid Domains\n", LISTING_FOLLOWS);
+       It = GetNewHashPos(List, 1);
+       while (GetNextHashPos(List, It, &HKLen, &HKey, &vptr))
+       {
+               cputbuf(vptr);
+               cprintf("\n");
+       }
+       cprintf("000\n");
+
+       DeleteHashPos(&It);
+       DeleteHash(&List);
+       FreeStrBuf(&Cfg);
+       FreeStrBuf(&Line);
+       FreeStrBuf(&CfgToken);
+       FreeStrBuf(&Config);
+}
+
+/*****************************************************************************/
+/*                      MODULE INITIALIZATION STUFF                          */
+/*****************************************************************************/
+
+void control_cleanup(void)
+{
+       DeleteHash(&CfgNameHash);
+}
+CTDL_MODULE_INIT(control)
+{
+       if (!threading) {
+               int i;
+
+               CfgNameHash = NewHash(1, NULL);
+               for (i = 0; CfgNames[i].Name.Key != NULL; i++)
+                       Put(CfgNameHash, CKEY(CfgNames[i].Name), &CfgNames[i], reference_free_handler);
+
+               CtdlRegisterProtoHook(cmd_gvdn, "GVDN", "get valid domain names");
+               CtdlRegisterProtoHook(cmd_conf, "CONF", "get/set system configuration");
+               CtdlRegisterCleanupHook(control_cleanup);
+
        }
+       /* return our id for the Log */
+       return "control";
 }