getmx() now uses our array class
[citadel.git] / citadel / netconfig.c
index 3c20541c0f7fd51714b0a750e96ae78506ddd030..73ab22f4f67f32ad3ff9b3a27cef433feaee0cb5 100644 (file)
@@ -1,21 +1,22 @@
 /*
- * This module handles shared rooms, inter-Citadel mail, and outbound
- * mailing list processing.
+ * This module handles loading, saving, and parsing of room network configurations.
  *
- * Copyright (c) 2000-2012 by the citadel.org team
+ * Copyright (c) 2000-2021 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 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.
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
 
 #ifdef HAVE_SYSCALL_H
 # include <syscall.h>
 #  include <sys/syscall.h>
 # endif
 #endif
+#include <dirent.h>
+#include <assert.h>
 
 #include <libcitadel.h>
 
-#include "include/ctdl_module.h"
-HashList *CfgTypeHash = NULL;
+#include "ctdl_module.h"
+#include "serv_extensions.h"
+#include "config.h"
 
 
-
-void RegisterRoomCfgType(const char* Name, long len, RoomNetCfg eCfg, CfgLineParser p, int uniq, CfgLineSerializer s, CfgLineDeAllocator d)
-{
-       CfgLineType *pCfg;
-
-       pCfg = (CfgLineType*) malloc(sizeof(CfgLineType));
-       pCfg->Parser = p;
-       pCfg->Serializer = s;
-       pCfg->C = eCfg;
-       pCfg->Str.Key = Name;
-       pCfg->Str.len = len;
-       pCfg->IsSingleLine = uniq;
-
-       if (CfgTypeHash == NULL)
-               CfgTypeHash = NewHash(1, NULL);
-       Put(CfgTypeHash, Name, len, pCfg, NULL);
+/*
+ * Create a config key for a room's netconfig entry
+ */
+void netcfg_keyname(char *keybuf, long roomnum) {
+       if (!keybuf) return;
+       sprintf(keybuf, "c_netconfig_%010ld", roomnum);
 }
 
 
-const CfgLineType *GetCfgTypeByStr(const char *Key, long len)
-{
-       void *pv;
-       
-       if (GetHash(CfgTypeHash, Key, len, &pv) && (pv != NULL))
-       {
-               return (const CfgLineType *) pv;
-       }
-       else
-       {
-               return NULL;
+/*
+ * Given a room number and a textual netconfig, convert to base64 and write to the configdb
+ */
+void SaveRoomNetConfigFile(long roomnum, const char *raw_netconfig) {
+       char keyname[25];
+       char *enc;
+       int enc_len;
+       int len;
+
+       len = strlen(raw_netconfig);
+       netcfg_keyname(keyname, roomnum);
+       enc = malloc(len * 2);
+
+       if (enc) {
+               enc_len = CtdlEncodeBase64(enc, raw_netconfig, len, 0);
+               if ((enc_len > 1) && (enc[enc_len-2] == 13)) enc[enc_len-2] = 0;
+               if ((enc_len > 0) && (enc[enc_len-1] == 10)) enc[enc_len-1] = 0;
+               enc[enc_len] = 0;
+               syslog(LOG_DEBUG, "netconfig: writing key '%s' (length=%d)", keyname, enc_len);
+               CtdlSetConfigStr(keyname, enc);
+               free(enc);
        }
 }
 
-const CfgLineType *GetCfgTypeByEnum(RoomNetCfg eCfg, HashPos *It)
-{
-       const char *Key;
-       long len;
-       void *pv;
-       CfgLineType *pCfg;
 
-       RewindHashPos(CfgTypeHash, It, 1);
-       while (GetNextHashPos(CfgTypeHash, It, &len, &Key, &pv) && (pv != NULL))
-       {
-               pCfg = (CfgLineType*) pv;
-               if (pCfg->C == eCfg)
-                       return pCfg;
-       }
-       return NULL;
+/*
+ * Given a room number, attempt to load the netconfig configdb entry for that room.
+ * If it returns NULL, there is no netconfig.
+ * Otherwise the caller owns the returned memory and is responsible for freeing it.
+ */
+char *LoadRoomNetConfigFile(long roomnum) {
+       char keyname[25];
+       char *encoded_netconfig = NULL;
+       char *decoded_netconfig = NULL;
+
+       netcfg_keyname(keyname, roomnum);
+       encoded_netconfig = CtdlGetConfigStr(keyname);
+       if (!encoded_netconfig) return NULL;
+
+       decoded_netconfig = malloc(strlen(encoded_netconfig));  // yeah, way bigger than it needs to be, but safe
+       CtdlDecodeBase64(decoded_netconfig, encoded_netconfig, strlen(encoded_netconfig));
+       return decoded_netconfig;
 }
-void ParseGeneric(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *OneRNCFG)
-{
-       RoomNetCfgLine *nptr;
 
-       nptr = (RoomNetCfgLine *)
-               malloc(sizeof(RoomNetCfgLine));
-       nptr->next = OneRNCFG->NetConfigs[ThisOne->C];
-       nptr->Value = NewStrBufPlain(LinePos, StrLength(Line) - ( LinePos - ChrPtr(Line)) );
-       OneRNCFG->NetConfigs[ThisOne->C] = nptr;
-}
 
-void SerializeGeneric(const CfgLineType *ThisOne, StrBuf *OutputBuffer, OneRoomNetCfg *OneRNCFG, RoomNetCfgLine *data)
-{
-       StrBufAppendBufPlain(OutputBuffer, CKEY(ThisOne->Str), 0);
-       StrBufAppendBuf(OutputBuffer, data->Value, 0);
-       StrBufAppendBufPlain(OutputBuffer, HKEY("\n"), 0);
-}
+/*-----------------------------------------------------------------------------*
+ *              Per room network configs : exchange with client                *
+ *-----------------------------------------------------------------------------*/
 
-void DeleteGenericCfgLine(const CfgLineType *ThisOne, RoomNetCfgLine **data)
-{
-       FreeStrBuf(&(*data)->Value);
-       free(*data);
-       *data = NULL;
-}
-int read_spoolcontrol_file(OneRoomNetCfg **pOneRNCFG, char *filename)
-{
-       int fd;
-       const char *ErrStr = NULL;
-       const char *Pos;
-       const CfgLineType *pCfg;
-       StrBuf *Line;
-       StrBuf *InStr;
-       OneRoomNetCfg *OneRNCFG;
-
-       fd = open(filename, O_NONBLOCK|O_RDONLY);
-       if (fd == -1) {
-               *pOneRNCFG = NULL;
-               return 0;
+void cmd_gnet(char *argbuf) {
+       if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) {
+               /* users can edit the netconfigs for their own mailbox rooms */
        }
-       OneRNCFG = malloc(sizeof(OneRoomNetCfg));
-       memset(OneRNCFG, 0, sizeof(OneRoomNetCfg));
-       *pOneRNCFG = OneRNCFG;
-
-       while (StrBufTCP_read_line(Line, &fd, 0, &ErrStr) >= 0) {
-               if (StrLength(Line) == 0)
-                       continue;
-               Pos = NULL;
-               InStr = NewStrBufPlain(NULL, StrLength(Line));
-               StrBufExtract_NextToken(InStr, Line, &Pos, '|');
-
-               pCfg = GetCfgTypeByStr(SKEY(InStr));
-               if (pCfg != NULL)
-               {
-                       pCfg->Parser(pCfg, Line, Pos, OneRNCFG);
-               }
-               else
-               {
-                       if (OneRNCFG->misc == NULL)
-                       {
-                               OneRNCFG->misc = NewStrBufDup(Line);
-                       }
-                       else
-                       {
-                               if(StrLength(OneRNCFG->misc) > 0)
-                                       StrBufAppendBufPlain(OneRNCFG->misc, HKEY("\n"), 0);
-                               StrBufAppendBuf(OneRNCFG->misc, Line, 0);
-                       }
+       else if (CtdlAccessCheck(ac_room_aide)) return;
+       
+       cprintf("%d Network settings for room #%ld <%s>\n", LISTING_FOLLOWS, CC->room.QRnumber, CC->room.QRname);
+
+       char *c = LoadRoomNetConfigFile(CC->room.QRnumber);
+       if (c) {
+               int len = strlen(c);
+               client_write(c, len);                   // Can't use cprintf() here, it has a limit of 1024 bytes
+               if (c[len] != '\n') {
+                       client_write(HKEY("\n"));
                }
+               free(c);
        }
-       if (fd > 0)
-               close(fd);
-       FreeStrBuf(&InStr);
-       FreeStrBuf(&Line);
-       return 1;
+       cprintf("000\n");
 }
 
-int save_spoolcontrol_file(OneRoomNetCfg *OneRNCFG, char *filename)
-{
-       RoomNetCfg eCfg;
-       StrBuf *Cfg;
-       char tempfilename[PATH_MAX];
-       int TmpFD;
-       long len;
-       time_t unixtime;
-       struct timeval tv;
-       long reltid; /* if we don't have SYS_gettid, use "random" value */
-       StrBuf *OutBuffer;
+
+void cmd_snet(char *argbuf) {
+       StrBuf *Line = NULL;
+       StrBuf *TheConfig = NULL;
        int rc;
-       HashPos *CfgIt;
 
-       len = strlen(filename);
-       memcpy(tempfilename, filename, len + 1);
+       unbuffer_output();
+       Line = NewStrBuf();
+       TheConfig = NewStrBuf();
+       cprintf("%d send new netconfig now\n", SEND_LISTING);
 
-#if defined(HAVE_SYONERNCFGALL_H) && defined (SYS_gettid)
-       reltid = syOneRNCFGall(SYS_gettid);
-#endif
-       gettimeofday(&tv, NULL);
-       /* Promote to time_t; types differ on some OSes (like darwin) */
-       unixtime = tv.tv_sec;
-
-       sprintf(tempfilename + len, ".%ld-%ld", reltid, unixtime);
-       errno = 0;
-       TmpFD = open(tempfilename, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
-       Cfg = NewStrBuf();
-       if ((TmpFD < 0) || (errno != 0)) {
-               syslog(LOG_CRIT, "ERROR: cannot open %s: %s\n",
-                       filename, strerror(errno));
-               unlink(tempfilename);
-               return 0;
-       }
-       else {
-               CfgIt = GetNewHashPos(CfgTypeHash, 1);
-               fchown(TmpFD, config.c_ctdluid, 0);
-               for (eCfg = subpending; eCfg < maxRoomNetCfg; eCfg ++)
-               {
-                       const CfgLineType *pCfg;
-                       pCfg = GetCfgTypeByEnum(eCfg, CfgIt);
-                       if (pCfg->IsSingleLine)
-                       {
-                               pCfg->Serializer(pCfg, OutBuffer, OneRNCFG, NULL);
-                       }
-                       else
-                       {
-                               RoomNetCfgLine *pName = OneRNCFG->NetConfigs[pCfg->C];
-                               while (pName != NULL)
-                               {
-                                       pCfg->Serializer(pCfg, OutBuffer, OneRNCFG, pName);
-                                       pName = pName->next;
-                               }
-                               
-                               
-                       }
+       while (rc = CtdlClientGetLine(Line), (rc >= 0)) {
+               if ((rc == 3) && (strcmp(ChrPtr(Line), "000") == 0))
+                       break;
 
-               }
-               DeleteHashPos(&CfgIt);
+               StrBufAppendBuf(TheConfig, Line, 0);
+               StrBufAppendBufPlain(TheConfig, HKEY("\n"), 0);
+       }
+       FreeStrBuf(&Line);
 
+       begin_critical_section(S_NETCONFIGS);
+       SaveRoomNetConfigFile(CC->room.QRnumber, ChrPtr(TheConfig));
+       end_critical_section(S_NETCONFIGS);
+       FreeStrBuf(&TheConfig);
+}
 
-               if (OneRNCFG->misc != NULL) {
-                       StrBufAppendBuf(OutBuffer, OneRNCFG->misc, 0);
-               }
 
-               rc = write(TmpFD, ChrPtr(OutBuffer), StrLength(OutBuffer));
-               if ((rc >=0 ) && (rc == StrLength(Cfg))) 
-               {
-                       close(TmpFD);
-                       rename(tempfilename, filename);
-                       rc = 1;
-               }
-               else {
-                       syslog(LOG_EMERG, 
-                                     "unable to write %s; [%s]; not enough space on the disk?\n", 
-                                     tempfilename, 
-                                     strerror(errno));
-                       close(TmpFD);
-                       unlink(tempfilename);
-                       rc = 0;
+/*
+ * Convert any legacy configuration files in the "netconfigs" directory
+ */
+void convert_legacy_netcfg_files(void)
+{
+       DIR *dh = NULL;
+       struct dirent *dit = NULL;
+       char filename[PATH_MAX];
+       long roomnum;
+       FILE *fp;
+       long len;
+       char *v;
+
+       dh = opendir(ctdl_netcfg_dir);
+       if (!dh) return;
+
+       syslog(LOG_INFO, "netconfig: legacy netconfig files exist - converting them!");
+
+       while (dit = readdir(dh), dit != NULL) {        // yes, we use the non-reentrant version; we're not in threaded mode yet
+               roomnum = atol(dit->d_name);
+               if (roomnum > 0) {
+                       snprintf(filename, sizeof filename, "%s/%ld", ctdl_netcfg_dir, roomnum);
+                       fp = fopen(filename, "r");
+                       if (fp) {
+                               fseek(fp, 0L, SEEK_END);
+                               len = ftell(fp);
+                               if (len > 0) {
+                                       v = malloc(len);
+                                       if (v) {
+                                               rewind(fp);
+                                               if (fread(v, len, 1, fp)) {
+                                                       SaveRoomNetConfigFile(roomnum, v);
+                                                       unlink(filename);
+                                               }
+                                               free(v);
+                                       }
+                               }
+                               else {
+                                       unlink(filename);       // zero length netconfig, just delete it
+                               }
+                               fclose(fp);
+                       }
                }
-               FreeStrBuf(&OutBuffer);
-               
        }
-       return rc;
-}
 
+       closedir(dh);
+       rmdir(ctdl_netcfg_dir);
+}
 
 
-void free_spoolcontrol_struct(OneRoomNetCfg **pOneRNCFG)
+/*
+ * Module entry point
+ */
+CTDL_MODULE_INIT(netconfig)
 {
-       RoomNetCfg eCfg;
-       HashPos *CfgIt;
-       OneRoomNetCfg *OneRNCFG;
-
-       OneRNCFG = *pOneRNCFG;
-       CfgIt = GetNewHashPos(CfgTypeHash, 1);
-       for (eCfg = subpending; eCfg < maxRoomNetCfg; eCfg ++)
+       if (!threading)
        {
-               const CfgLineType *pCfg;
-               RoomNetCfgLine *pNext, *pName;
-               
-               pCfg = GetCfgTypeByEnum(eCfg, CfgIt);
-               pName= OneRNCFG->NetConfigs[pCfg->C];
-               while (pName != NULL)
-               {
-                       pNext = pName->next;
-                       pCfg->DeAllocator(pCfg, &pName);
-                       pName = pNext;
-               }
+               convert_legacy_netcfg_files();
+               CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
+               CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config");
        }
-       DeleteHashPos(&CfgIt);
-
-       FreeStrBuf(&OneRNCFG->Sender);
-       FreeStrBuf(&OneRNCFG->RoomInfo);
-       FreeStrBuf(&OneRNCFG->misc);
-       free(OneRNCFG);
-       *pOneRNCFG=NULL;
+       return "netconfig";
 }
-