Merge branch 'master' of ssh://git.citadel.org/appl/gitroot/citadel
authorWilfried Goesgens <dothebart@citadel.org>
Tue, 29 Jan 2013 23:24:21 +0000 (00:24 +0100)
committerWilfried Goesgens <dothebart@citadel.org>
Tue, 29 Jan 2013 23:24:21 +0000 (00:24 +0100)
29 files changed:
citadel/Makefile.in
citadel/citserver.h
citadel/include/ctdl_module.h
citadel/modules/bio/serv_bio.c
citadel/modules/listsub/serv_listsub.c
citadel/modules/network/netconfig.h [deleted file]
citadel/modules/network/netspool.h
citadel/modules/network/serv_netconfig.c [deleted file]
citadel/modules/network/serv_netfilter.c
citadel/modules/network/serv_netmail.c
citadel/modules/network/serv_netspool.c
citadel/modules/network/serv_network.c
citadel/modules/network/serv_network.h
citadel/modules/network/serv_networkclient.c [deleted file]
citadel/modules/networkclient/serv_networkclient.c [new file with mode: 0644]
citadel/modules/pop3client/serv_pop3client.c
citadel/modules/rssclient/serv_rssclient.c
citadel/modules/vcard/serv_vcard.c
citadel/msgbase.c
citadel/msgbase.h
citadel/netconfig.c [new file with mode: 0644]
citadel/nttlist.c [new file with mode: 0644]
citadel/room_ops.c
citadel/scripts/mk_module_init.sh
citadel/techdoc/netconfigs.txt
webcit/context_loop.c
webcit/ical_subst.c
webcit/static.c
webcit/subst.c

index 553ea04356c939ab807a3c57e95d41b380333707..d6210e08c3629a0c72d620506ec3e454bc22d04a 100644 (file)
@@ -83,7 +83,7 @@ SOURCES=utils/aidepost.c utils/citmail.c \
        locate_host.c md5.c auth.c msgbase.c parsedate.c \
        room_ops.c euidindex.c server_main.c ldap.c \
        support.c sysdep.c user_ops.c journaling.c threads.c \
-       context.c event_client.c
+       context.c event_client.c netconfig.c nttlist.c
 
 
 include Make_sources
@@ -138,7 +138,7 @@ SERV_OBJS = server_main.o utillib/citadel_dirs.o event_client.o \
        internet_addressing.o journaling.o \
        parsedate.o genstamp.o ecrash.o threads.o context.o \
        clientsocket.o modules_init.o modules_upgrade.o $(SERV_MODULES) \
-       svn_revision.o ldap.o
+       svn_revision.o ldap.o netconfig.o nttlist.o
 
 citserver$(EXEEXT): $(SERV_OBJS)
        $(CC) $(SERV_OBJS) $(LDFLAGS) $(SERVER_LDFLAGS) $(LIBS) $(SERVER_LIBS) $(RESOLV) -o citserver$(EXEEXT)
index b89e4e0f859d86515ec2bf393eb21abe73832cde..59660496cf9e5233f16eac17826ff4527e2f7443 100644 (file)
 
 #include "serv_extensions.h"
 #include "context.h"
+#include "ctdl_module.h"
 
 /* Simple linked list structures ... used in a bunch of different places. */
 typedef struct RoomProcList RoomProcList;
 struct RoomProcList {
         struct RoomProcList *next;
+       OneRoomNetCfg *OneRNCfg;
         char name[ROOMNAMELEN];
         char lcname[ROOMNAMELEN];
        long namelen;
index 12926921509fb8f14ad0351e970a1332506ef411..68577186373401dc521a5fe5d372853c24c537ed 100644 (file)
@@ -226,6 +226,9 @@ long CtdlGetCurrentMessageNumber(void);
 /*
  * Expose various room operation functions from room_ops.c to the modules API
  */
+typedef struct CfgLineType CfgLineType;
+typedef struct RoomNetCfgLine RoomNetCfgLine;
+typedef struct OneRoomNetCfg OneRoomNetCfg;
 
 unsigned CtdlCreateRoom(char *new_room_name,
                        int new_room_type,
@@ -234,14 +237,20 @@ unsigned CtdlCreateRoom(char *new_room_name,
                        int really_create,
                        int avoid_access,
                        int new_room_view);
-int CtdlGetRoom(struct ctdlroom *qrbuf, char *room_name);
+int CtdlGetRoom(struct ctdlroom *qrbuf, const char *room_name);
 int CtdlGetRoomLock(struct ctdlroom *qrbuf, char *room_name);
 int CtdlDoIHavePermissionToDeleteThisRoom(struct ctdlroom *qr);
 void CtdlRoomAccess(struct ctdlroom *roombuf, struct ctdluser *userbuf,
                int *result, int *view);
 void CtdlPutRoomLock(struct ctdlroom *qrbuf);
-void CtdlForEachRoom(void (*CallBack)(struct ctdlroom *EachRoom, void *out_data),
-       void *in_data);
+typedef void (*ForEachRoomCallBack)(struct ctdlroom *EachRoom, void *out_data);
+void CtdlForEachRoom(ForEachRoomCallBack CB, void *in_data);
+typedef void (*ForEachRoomNetCfgCallBack)(struct ctdlroom *EachRoom, void *out_data, OneRoomNetCfg *OneRNCFG);
+void CtdlForEachNetCfgRoom(ForEachRoomNetCfgCallBack CB,
+                          void *in_data,
+                          RoomNetCfg filter);
+void SaveChangedConfigs(void);
+
 void CtdlDeleteRoom(struct ctdlroom *qrbuf);
 int CtdlRenameRoom(char *old_name, char *new_name, int new_floor);
 void CtdlUserGoto (char *where, int display_result, int transiently,
@@ -378,6 +387,90 @@ struct config {
 extern struct config config;
 
 
+typedef void (*CfgLineParser)(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *rncfg);
+typedef void (*CfgLineSerializer)(const CfgLineType *ThisOne, StrBuf *OuptputBuffer, OneRoomNetCfg *rncfg, RoomNetCfgLine *data);
+typedef void (*CfgLineDeAllocator)(const CfgLineType *ThisOne, RoomNetCfgLine **data);
+
+struct CfgLineType {
+       RoomNetCfg C;
+       CfgLineParser Parser;
+       CfgLineSerializer Serializer;
+       CfgLineDeAllocator DeAllocator;
+       ConstStr Str;
+       int IsSingleLine;
+       int nSegments;
+};
+
+struct RoomNetCfgLine {
+       RoomNetCfgLine *next;
+       int nValues;
+       StrBuf **Value;
+};
+
+struct OneRoomNetCfg {
+       long lastsent;
+       long changed;
+       StrBuf *Sender;
+       StrBuf *RoomInfo;
+       RoomNetCfgLine *NetConfigs[maxRoomNetCfg];
+       StrBuf *misc;
+};
+
+
+#define CtdlREGISTERRoomCfgType(a, p, uniq, nSegs, s, d) RegisterRoomCfgType(#a, sizeof(#a) - 1, a, p, uniq, nSegs, s, d);
+void RegisterRoomCfgType(const char* Name, long len, RoomNetCfg eCfg, CfgLineParser p, int uniq, int nSegments, CfgLineSerializer s, CfgLineDeAllocator d);
+void ParseGeneric(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *sc);
+void SerializeGeneric(const CfgLineType *ThisOne, StrBuf *OutputBuffer, OneRoomNetCfg *sc, RoomNetCfgLine *data);
+void DeleteGenericCfgLine(const CfgLineType *ThisOne, RoomNetCfgLine **data);
+RoomNetCfgLine *DuplicateOneGenericCfgLine(const RoomNetCfgLine *data);
+void AddRoomCfgLine(OneRoomNetCfg *OneRNCfg, struct ctdlroom *qrbuf, RoomNetCfg LineType, RoomNetCfgLine *Line);
+
+OneRoomNetCfg* CtdlGetNetCfgForRoom(long QRNumber);
+
+typedef struct _nodeconf {
+       int DeleteMe;
+       StrBuf *NodeName;
+       StrBuf *Secret;
+       StrBuf *Host;
+       StrBuf *Port;
+}CtdlNodeConf;
+
+HashList* CtdlLoadIgNetCfg(void);
+
+
+int CtdlNetconfigCheckRoomaccess(char *errmsgbuf, 
+                                size_t n,
+                                const char* RemoteIdentifier);
+
+
+typedef struct __NetMap {
+       StrBuf *NodeName;
+       time_t lastcontact;
+       StrBuf *NextHop;
+}CtdlNetMap;
+
+HashList* CtdlReadNetworkMap(void);
+StrBuf *CtdlSerializeNetworkMap(HashList *Map);
+void NetworkLearnTopology(char *node, char *path, HashList *the_netmap, int *netmap_changed);
+int CtdlIsValidNode(const StrBuf **nexthop,
+                   const StrBuf **secret,
+                   StrBuf *node,
+                   HashList *IgnetCfg,
+                   HashList *the_netmap);
+
+
+
+
+int CtdlNetworkTalkingTo(const char *nodename, long len, int operation);
+
+/*
+ * Operations that can be performed by network_talking_to()
+ */
+enum {
+        NTT_ADD,
+        NTT_REMOVE,
+        NTT_CHECK
+};
 
 /*
  * Expose API calls from user_ops.c
index 56e0d482488f23c28f8e5d3467cbb49f33de952f..041485d933f0afb5767d73399cd3a34e340b6908 100644 (file)
@@ -92,6 +92,8 @@ void cmd_lbio(char *cmdbuf)
        int dont_resolve_uids;
        size_t d_namelen;
        struct ctdluser usbuf;
+       int d_type = 0;
+
 
        d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 2);
        if (d == NULL) {
@@ -110,22 +112,60 @@ void cmd_lbio(char *cmdbuf)
        while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
               (filedir_entry != NULL))
        {
-#ifdef _DIRENT_HAVE_D_NAMELEN
+#ifdef _DIRENT_HAVE_D_NAMLEN
                d_namelen = filedir_entry->d_namelen;
+
 #else
                d_namelen = strlen(filedir_entry->d_name);
 #endif
-               if (((d_namelen == 1) && (filedir_entry->d_name[0] == '.')) || 
-                   ((d_namelen == 2) && (filedir_entry->d_name[0] == '.') && (filedir_entry->d_name[1] == '.')))
+
+#ifdef _DIRENT_HAVE_D_TYPE
+               d_type = filedir_entry->d_type;
+#else
+
+#ifndef DT_UNKNOWN
+#define DT_UNKNOWN     0
+#define DT_DIR         4
+#define DT_REG         8
+#define DT_LNK         10
+
+#define IFTODT(mode)   (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype)        ((dirtype) << 12)
+#endif
+               d_type = DT_UNKNOWN;
+#endif
+               if ((d_namelen == 1) && 
+                   (filedir_entry->d_name[0] == '.'))
+                       continue;
+
+               if ((d_namelen == 2) && 
+                   (filedir_entry->d_name[0] == '.') &&
+                   (filedir_entry->d_name[1] == '.'))
                        continue;
-                   
-               if (dont_resolve_uids) {
-                       filedir_entry->d_name[d_namelen++] = '\n';
-                       filedir_entry->d_name[d_namelen] = '\0';
-                       client_write(filedir_entry->d_name, d_namelen);
+
+               if (d_type == DT_UNKNOWN) {
+                       struct stat s;
+                       char path[PATH_MAX];
+                       snprintf(path, PATH_MAX, "%s/%s", 
+                                ctdl_bio_dir, filedir_entry->d_name);
+                       if (lstat(path, &s) == 0) {
+                               d_type = IFTODT(s.st_mode);
+                       }
+               }
+               switch (d_type)
+               {
+               case DT_DIR:
+                       break;
+               case DT_LNK:
+               case DT_REG:
+                       if (dont_resolve_uids) {
+                               filedir_entry->d_name[d_namelen++] = '\n';
+                               filedir_entry->d_name[d_namelen] = '\0';
+                               client_write(filedir_entry->d_name, d_namelen);
+                       }
+                       else if (CtdlGetUserByNumber(&usbuf,atol(filedir_entry->d_name))==0)
+                               cprintf("%s\n", usbuf.fullname);
                }
-               else if (CtdlGetUserByNumber(&usbuf,atol(filedir_entry->d_name))==0)
-                       cprintf("%s\n", usbuf.fullname);
        }
        free(d);
        closedir(filedir);
index 8940c952eb7203ab3794659f22cdbe251dc4fc37..a9fd95bfb8761a8706c2fc1af3f644fa55e5f761 100644 (file)
@@ -71,24 +71,49 @@ void listsub_generate_token(char *buf) {
        CtdlEncodeBase64(buf, sourcebuf, strlen(sourcebuf), 0);
 }
 
+const RoomNetCfg ActiveSubscribers[] = {listrecp, digestrecp};
+
+int CountThisSubscriber(OneRoomNetCfg *OneRNCfg, StrBuf *email)
+{
+       RoomNetCfgLine *Line;
+       int found_sub = 0;
+       int i;
+
+       for (i = 0; i < 2; i++)
+       {
+               Line = OneRNCfg->NetConfigs[ActiveSubscribers[i]];
+               while (Line != NULL)
+               {
+                       if (!strcmp(ChrPtr(email),
+                                   ChrPtr(Line->Value[0])))
+                       {
+                               ++found_sub;
+                               break;                                  
+                       }
+                       Line = Line->next;
+               }
+       }
+       return found_sub;
+}
 
 /*
  * Enter a subscription request
  */
-void do_subscribe(char *room, char *email, char *subtype, char *webpage) {
+void do_subscribe(StrBuf **room, StrBuf **email, StrBuf **subtype, StrBuf **webpage) {
        struct ctdlroom qrbuf;
-       FILE *ncfp;
-       char filename[256];
        char token[256];
-       char confirmation_request[2048];
-       char buf[512];
-       char urlroom[ROOMNAMELEN];
-       char scancmd[64];
-       char scanemail[256];
+       char *pcf_req;
+       StrBuf *cf_req;
+       StrBuf *UrlRoom;
        int found_sub = 0;
-
-       if (CtdlGetRoom(&qrbuf, room) != 0) {
-               cprintf("%d There is no list called '%s'\n", ERROR + ROOM_NOT_FOUND, room);
+       const char *RoomMailAddress;
+       OneRoomNetCfg *OneRNCfg;
+       RoomNetCfgLine *Line;
+       const char *EmailSender = NULL;
+       long RoomMailAddressLen;
+
+       if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
+               cprintf("%d There is no list called '%s'\n", ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
                return;
        }
 
@@ -99,137 +124,187 @@ void do_subscribe(char *room, char *email, char *subtype, char *webpage) {
                return;
        }
 
-       listsub_generate_token(token);
-
-       assoc_file_name(filename, sizeof filename, &qrbuf, ctdl_netcfg_dir);
-
        /* 
         * Make sure the requested address isn't already subscribed
         */
        begin_critical_section(S_NETCONFIGS);
-       ncfp = fopen(filename, "r");
-       if (ncfp != NULL) {
-               while (fgets(buf, sizeof buf, ncfp) != NULL) {
-                       buf[strlen(buf)-1] = 0;
-                       extract_token(scancmd, buf, 0, '|', sizeof scancmd);
-                       extract_token(scanemail, buf, 1, '|', sizeof scanemail);
-                       if ((!strcasecmp(scancmd, "listrecp"))
-                          || (!strcasecmp(scancmd, "digestrecp"))) {
-                               if (!strcasecmp(scanemail, email)) {
-                                       ++found_sub;
-                               }
-                       }
+
+       RoomMailAddress = qrbuf.QRname;
+       OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
+       if (OneRNCfg!=NULL) {
+               found_sub = CountThisSubscriber(OneRNCfg, *email);
+               if (StrLength(OneRNCfg->Sender) > 0) {
+                      EmailSender = RoomMailAddress = ChrPtr(OneRNCfg->Sender);
                }
-               fclose(ncfp);
        }
-       end_critical_section(S_NETCONFIGS);
 
        if (found_sub != 0) {
                cprintf("%d '%s' is already subscribed to '%s'.\n",
                        ERROR + ALREADY_EXISTS,
-                       email, qrbuf.QRname);
+                       ChrPtr(*email),
+                       RoomMailAddress);
+
+               end_critical_section(S_NETCONFIGS);
                return;
        }
 
        /*
-        * Now add it to the file
+        * Now add it to the config
         */     
-       begin_critical_section(S_NETCONFIGS);
-       ncfp = fopen(filename, "a");
-       if (ncfp != NULL) {
-               fprintf(ncfp, "subpending|%s|%s|%s|%ld|%s\n",
-                       email,
-                       subtype,
-                       token,
-                       time(NULL),
-                       webpage
-               );
-               fclose(ncfp);
-       }
-       end_critical_section(S_NETCONFIGS);
+       
+       RoomMailAddressLen = strlen(RoomMailAddress);
+       listsub_generate_token(token);
+       Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
+       memset(Line, 0, sizeof(RoomNetCfgLine));
+
+       Line->Value = (StrBuf**) malloc(sizeof(StrBuf*) * 5);
+       
+       Line->Value[0] = NewStrBufDup(*email);
+       Line->Value[1] = *subtype; *subtype = NULL;
+       Line->Value[2] = NewStrBufPlain(token, -1);
+       Line->Value[3] = NewStrBufPlain(NULL, 10);
+       StrBufPrintf(Line->Value[3], "%ld", time(NULL));
+       Line->Value[4] = *webpage; *webpage = NULL;
+       Line->nValues = 5;
+
+       AddRoomCfgLine(OneRNCfg, &qrbuf, subpending, Line);
 
        /* Generate and send the confirmation request */
+       UrlRoom = NewStrBuf();
+       StrBufUrlescAppend(UrlRoom, NULL, qrbuf.QRname);
+
+       cf_req = NewStrBufPlain(NULL, 2048);
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("MIME-Version: 1.0\n"
+                    "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
+                    "\n"
+                    "This is a multipart message in MIME format.\n"
+                    "\n"
+                    "--__ctdlmultipart__\n"
+                    "Content-type: text/plain\n"
+                    "\n"
+                    "Someone (probably you) has submitted a request to subscribe\n"
+                    "<"), 0);
+       StrBufAppendBuf(cf_req, Line->Value[0], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("> to the '"), 0);
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("' mailing list.\n"
+                    "\n"
+                    "Please go here to confirm this request:\n"
+                    "  "), 0);
+       StrBufAppendBuf(cf_req, Line->Value[4], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
+       StrBufAppendBuf(cf_req, UrlRoom, 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
+       StrBufAppendBuf(cf_req, Line->Value[2], 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("&cmd=confirm  \n"
+                    "\n"
+                    "If this request has been submitted in error and you do not\n"
+                    "wish to receive the '"), 0);
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("' mailing list, simply do nothing,\n"
+                    "and you will not receive any further mailings.\n"
+                    "\n"
+                    "--__ctdlmultipart__\n"
+                    "Content-type: text/html\n"
+                    "\n"
+                    "<HTML><BODY>\n"
+                    "Someone (probably you) has submitted a request to subscribe\n"
+                    "&lt;"), 0);
+       StrBufAppendBuf(cf_req, Line->Value[0], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY( "&gt; to the <B>"), 0);
+
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("'</B> mailing list.<BR><BR>\n"
+                    "Please click here to confirm this request:<BR>\n"
+                    "<A HREF=\""), 0);
+       StrBufAppendBuf(cf_req, Line->Value[4], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
+       StrBufAppendBuf(cf_req, UrlRoom, 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
+       StrBufAppendBuf(cf_req, Line->Value[2], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&cmd=confirm\">"), 0);
+       StrBufAppendBuf(cf_req, Line->Value[4], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
+       StrBufAppendBuf(cf_req, UrlRoom, 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
+       StrBufAppendBuf(cf_req, Line->Value[2], 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("&cmd=confirm</A><BR><BR>\n"
+                    "If this request has been submitted in error and you do not\n"
+                    "wish to receive the '"), 0);
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+       
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("' mailing list, simply do nothing,\n"
+                    "and you will not receive any further mailings.\n"
+                    "</BODY></HTML>\n"
+                    "\n"
+                    "--__ctdlmultipart__--\n"), 0);
 
-       urlesc(urlroom, ROOMNAMELEN, qrbuf.QRname);
-
-       snprintf(confirmation_request, sizeof confirmation_request,
-
-               "MIME-Version: 1.0\n"
-               "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
-               "\n"
-               "This is a multipart message in MIME format.\n"
-               "\n"
-               "--__ctdlmultipart__\n"
-               "Content-type: text/plain\n"
-               "\n"
-               "Someone (probably you) has submitted a request to subscribe\n"
-               "<%s> to the '%s' mailing list.\n"
-               "\n"
-               "Please go here to confirm this request:\n"
-               "  %s?room=%s&token=%s&cmd=confirm  \n"
-               "\n"
-               "If this request has been submitted in error and you do not\n"
-               "wish to receive the '%s' mailing list, simply do nothing,\n"
-               "and you will not receive any further mailings.\n"
-               "\n"
-               "--__ctdlmultipart__\n"
-               "Content-type: text/html\n"
-               "\n"
-               "<HTML><BODY>\n"
-               "Someone (probably you) has submitted a request to subscribe\n"
-               "&lt;%s&gt; to the <B>%s</B> mailing list.<BR><BR>\n"
-               "Please click here to confirm this request:<BR>\n"
-               "<A HREF=\"%s?room=%s&token=%s&cmd=confirm\">"
-               "%s?room=%s&token=%s&cmd=confirm</A><BR><BR>\n"
-               "If this request has been submitted in error and you do not\n"
-               "wish to receive the '%s' mailing list, simply do nothing,\n"
-               "and you will not receive any further mailings.\n"
-               "</BODY></HTML>\n"
-               "\n"
-               "--__ctdlmultipart__--\n",
-
-               email, qrbuf.QRname,
-               webpage, urlroom, token,
-               qrbuf.QRname,
-
-               email, qrbuf.QRname,
-               webpage, urlroom, token,
-               webpage, urlroom, token,
-               qrbuf.QRname
-       );
+       end_critical_section(S_NETCONFIGS);
 
+       pcf_req = SmashStrBuf(&cf_req);
        quickie_message(        /* This delivers the message */
                "Citadel",
+               EmailSender,
+               ChrPtr(*email),
                NULL,
-               email,
-               NULL,
-               confirmation_request,
+               pcf_req,
                FMT_RFC822,
                "Please confirm your list subscription"
-       );
-
+               );
+       free(pcf_req);
        cprintf("%d Subscription entered; confirmation request sent\n", CIT_OK);
+
+       FreeStrBuf(&UrlRoom);
 }
 
 
 /*
  * Enter an unsubscription request
  */
-void do_unsubscribe(char *room, char *email, char *webpage) {
+void do_unsubscribe(StrBuf **room, StrBuf **email, StrBuf **webpage) {
        struct ctdlroom qrbuf;
-       FILE *ncfp;
-       char filename[256];
+       const char *EmailSender = NULL;
        char token[256];
-       char buf[512];
-       char confirmation_request[2048];
-       char urlroom[ROOMNAMELEN];
-       char scancmd[256];
-       char scanemail[256];
+       char *pcf_req;
+       StrBuf *cf_req;
+       StrBuf *UrlRoom;
        int found_sub = 0;
+       const char *RoomMailAddress;
+       OneRoomNetCfg *OneRNCfg;
+       RoomNetCfgLine *Line;
+       long RoomMailAddressLen;
 
-       if (CtdlGetRoom(&qrbuf, room) != 0) {
+       if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
                cprintf("%d There is no list called '%s'\n",
-                       ERROR + ROOM_NOT_FOUND, room);
+                       ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
                return;
        }
 
@@ -242,141 +317,186 @@ void do_unsubscribe(char *room, char *email, char *webpage) {
 
        listsub_generate_token(token);
 
-       assoc_file_name(filename, sizeof filename, &qrbuf, ctdl_netcfg_dir);
-
        /* 
         * Make sure there's actually a subscription there to remove
         */
        begin_critical_section(S_NETCONFIGS);
-       ncfp = fopen(filename, "r");
-       if (ncfp != NULL) {
-               while (fgets(buf, sizeof buf, ncfp) != NULL) {
-                       buf[strlen(buf)-1] = 0;
-                       extract_token(scancmd, buf, 0, '|', sizeof scancmd);
-                       extract_token(scanemail, buf, 1, '|', sizeof scanemail);
-                       if ((!strcasecmp(scancmd, "listrecp"))
-                          || (!strcasecmp(scancmd, "digestrecp"))) {
-                               if (!strcasecmp(scanemail, email)) {
-                                       ++found_sub;
-                               }
-                       }
-               }
-               fclose(ncfp);
+       RoomMailAddress = qrbuf.QRname;
+       OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
+       if (OneRNCfg!=NULL) {
+               found_sub = CountThisSubscriber(OneRNCfg, *email);
+               if (StrLength(OneRNCfg->Sender) > 0)
+                       EmailSender = RoomMailAddress = ChrPtr(OneRNCfg->Sender);
        }
-       end_critical_section(S_NETCONFIGS);
 
        if (found_sub == 0) {
                cprintf("%d '%s' is not subscribed to '%s'.\n",
                        ERROR + NO_SUCH_USER,
-                       email, qrbuf.QRname);
+                       ChrPtr(*email),
+                       qrbuf.QRname);
+
+               end_critical_section(S_NETCONFIGS);
                return;
        }
        
        /* 
         * Ok, now enter the unsubscribe-pending entry.
         */
-       begin_critical_section(S_NETCONFIGS);
-       ncfp = fopen(filename, "a");
-       if (ncfp != NULL) {
-               fprintf(ncfp, "unsubpending|%s|%s|%ld|%s\n",
-                       email,
-                       token,
-                       time(NULL),
-                       webpage
-               );
-               fclose(ncfp);
-       }
-       end_critical_section(S_NETCONFIGS);
+       RoomMailAddressLen = strlen(RoomMailAddress);
+       listsub_generate_token(token);
+       Line = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
+       memset(Line, 0, sizeof(RoomNetCfgLine));
+
+       Line->Value = (StrBuf**) malloc(sizeof(StrBuf*) * 4);
+       
+       Line->Value[0] = NewStrBufDup(*email);
+       Line->Value[1] = NewStrBufPlain(token, -1);
+       Line->Value[2] = NewStrBufPlain(NULL, 10);
+       StrBufPrintf(Line->Value[2], "%ld", time(NULL));
+       Line->Value[3] = *webpage; *webpage = NULL;
+       Line->nValues = 4;
+
+       AddRoomCfgLine(OneRNCfg, &qrbuf, unsubpending, Line);
 
        /* Generate and send the confirmation request */
+       UrlRoom = NewStrBuf();
+       StrBufUrlescAppend(UrlRoom, NULL, qrbuf.QRname);
+
+       cf_req = NewStrBufPlain(NULL, 2048);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("MIME-Version: 1.0\n"
+                    "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
+                    "\n"
+                    "This is a multipart message in MIME format.\n"
+                    "\n"
+                    "--__ctdlmultipart__\n"
+                    "Content-type: text/plain\n"
+                    "\n"
+                    "Someone (probably you) has submitted a request to unsubscribe\n"
+                    "<"), 0);
+       StrBufAppendBuf(cf_req, Line->Value[0], 0);
+
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("> from the '"), 0);
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("' mailing list.\n"
+                    "\n"
+                    "Please go here to confirm this request:\n  "), 0);
+       StrBufAppendBuf(cf_req, Line->Value[3], 0);
+       StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
+       StrBufAppendBuf(cf_req, UrlRoom, 0);
+       StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
+       StrBufAppendBuf(cf_req, Line->Value[1], 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("&cmd=confirm  \n"
+                    "\n"
+                    "If this request has been submitted in error and you do not\n"
+                    "wish to unsubscribe from the '"), 0);
+
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("' mailing list, simply do nothing,\n"
+                    "and the request will not be processed.\n"
+                    "\n"
+                    "--__ctdlmultipart__\n"
+                    "Content-type: text/html\n"
+                    "\n"
+                    "<HTML><BODY>\n"
+                    "Someone (probably you) has submitted a request to unsubscribe\n"
+                    "&lt;"), 0);
+       StrBufAppendBuf(cf_req, Line->Value[0], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&gt; from the <B>"), 0);
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("</B> mailing list.<BR><BR>\n"
+                    "Please click here to confirm this request:<BR>\n"
+                    "<A HREF=\""), 0);
+       StrBufAppendBuf(cf_req, Line->Value[3], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
+       StrBufAppendBuf(cf_req, UrlRoom, 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
+       StrBufAppendBuf(cf_req, Line->Value[1], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&cmd=confirm\">"), 0);
+       StrBufAppendBuf(cf_req, Line->Value[3], 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("?room="), 0);
+       StrBufAppendBuf(cf_req, UrlRoom, 0);
+
+       StrBufAppendBufPlain(cf_req, HKEY("&token="), 0);
+       StrBufAppendBuf(cf_req, Line->Value[1], 0);
+
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("&cmd=confirm</A><BR><BR>\n"
+                    "If this request has been submitted in error and you do not\n"
+                    "wish to unsubscribe from the '"), 0);
+       StrBufAppendBufPlain(cf_req, RoomMailAddress, RoomMailAddressLen, 0);
+
+       StrBufAppendBufPlain(
+               cf_req,
+               HKEY("' mailing list, simply do nothing,\n"
+                    "and the request will not be processed.\n"
+                    "</BODY></HTML>\n"
+                    "\n"
+                    "--__ctdlmultipart__--\n"), 0);
 
-       urlesc(urlroom, ROOMNAMELEN, qrbuf.QRname);
-
-       snprintf(confirmation_request, sizeof confirmation_request,
-
-               "MIME-Version: 1.0\n"
-               "Content-Type: multipart/alternative; boundary=\"__ctdlmultipart__\"\n"
-               "\n"
-               "This is a multipart message in MIME format.\n"
-               "\n"
-               "--__ctdlmultipart__\n"
-               "Content-type: text/plain\n"
-               "\n"
-               "Someone (probably you) has submitted a request to unsubscribe\n"
-               "<%s> from the '%s' mailing list.\n"
-               "\n"
-               "Please go here to confirm this request:\n"
-               "  %s?room=%s&token=%s&cmd=confirm  \n"
-               "\n"
-               "If this request has been submitted in error and you do not\n"
-               "wish to unsubscribe from the '%s' mailing list, simply do nothing,\n"
-               "and the request will not be processed.\n"
-               "\n"
-               "--__ctdlmultipart__\n"
-               "Content-type: text/html\n"
-               "\n"
-               "<HTML><BODY>\n"
-               "Someone (probably you) has submitted a request to unsubscribe\n"
-               "&lt;%s&gt; from the <B>%s</B> mailing list.<BR><BR>\n"
-               "Please click here to confirm this request:<BR>\n"
-               "<A HREF=\"%s?room=%s&token=%s&cmd=confirm\">"
-               "%s?room=%s&token=%s&cmd=confirm</A><BR><BR>\n"
-               "If this request has been submitted in error and you do not\n"
-               "wish to unsubscribe from the '%s' mailing list, simply do nothing,\n"
-               "and the request will not be processed.\n"
-               "</BODY></HTML>\n"
-               "\n"
-               "--__ctdlmultipart__--\n",
-
-               email, qrbuf.QRname,
-               webpage, urlroom, token,
-               qrbuf.QRname,
-
-               email, qrbuf.QRname,
-               webpage, urlroom, token,
-               webpage, urlroom, token,
-               qrbuf.QRname
-       );
+       end_critical_section(S_NETCONFIGS);
 
+       pcf_req = SmashStrBuf(&cf_req);
        quickie_message(        /* This delivers the message */
                "Citadel",
+               EmailSender,
+               ChrPtr(*email),
                NULL,
-               email,
-               NULL,
-               confirmation_request,
+               pcf_req,
                FMT_RFC822,
                "Please confirm your unsubscribe request"
        );
 
+       free(pcf_req);
+       FreeStrBuf(&UrlRoom);
        cprintf("%d Unubscription noted; confirmation request sent\n", CIT_OK);
 }
 
 
+const RoomNetCfg ConfirmSubscribers[] = {subpending, unsubpending};
+
 /*
  * Confirm a subscribe/unsubscribe request.
  */
-void do_confirm(char *room, char *token) {
+void do_confirm(StrBuf **room, StrBuf **token) {
        struct ctdlroom qrbuf;
-       FILE *ncfp;
-       char filename[256];
-       char line_token[256];
-       long line_offset;
-       int line_length;
-       char buf[512];
-       char cmd[256];
-       char email[256] = "";
-       char subtype[128];
+       OneRoomNetCfg *OneRNCfg;
+       RoomNetCfgLine *Line;
+       RoomNetCfgLine *ConfirmLine = NULL;
+       RoomNetCfgLine *RemoveLine = NULL;
+       RoomNetCfgLine **PrevLine;
        int success = 0;
-       char address_to_unsubscribe[256] = "";
-       char scancmd[256];
-       char scanemail[256];
-       char *holdbuf = NULL;
-       int linelen = 0;
-       int buflen = 0;
-
-       if (CtdlGetRoom(&qrbuf, room) != 0) {
+       RoomNetCfg ConfirmType;
+       const char *errmsg = "";
+       int i;
+       
+       if (CtdlGetRoom(&qrbuf, ChrPtr(*room)) != 0) {
                cprintf("%d There is no list called '%s'\n",
-                       ERROR + ROOM_NOT_FOUND, room);
+                       ERROR + ROOM_NOT_FOUND, ChrPtr(*room));
                return;
        }
 
@@ -387,125 +507,147 @@ void do_confirm(char *room, char *token) {
                return;
        }
 
+
+       if (StrLength(*token) == 0) {
+               cprintf("%d empty token.\n", ERROR + ILLEGAL_VALUE);
+               return;
+       }
        /*
         * Now start scanning this room's netconfig file for the
         * specified token.
         */
-       assoc_file_name(filename, sizeof filename, &qrbuf, ctdl_netcfg_dir);
        begin_critical_section(S_NETCONFIGS);
-       ncfp = fopen(filename, "r+");
-       if (ncfp != NULL) {
-               while (line_offset = ftell(ncfp),
-                     (fgets(buf, sizeof buf, ncfp) != NULL) ) {
-                       buf[strlen(buf)-1] = 0;
-                       line_length = strlen(buf);
-                       extract_token(cmd, buf, 0, '|', sizeof cmd);
-                       if (!strcasecmp(cmd, "subpending")) {
-                               extract_token(email, buf, 1, '|', sizeof email);
-                               extract_token(subtype, buf, 2, '|', sizeof subtype);
-                               extract_token(line_token, buf, 3, '|', sizeof line_token);
-                               if (!strcasecmp(token, line_token)) {
-                                       if (!strcasecmp(subtype, "digest")) {
-                                               safestrncpy(buf, "digestrecp|", sizeof buf);
-                                       }
-                                       else {
-                                               safestrncpy(buf, "listrecp|", sizeof buf);
-                                       }
-                                       strcat(buf, email);
-                                       strcat(buf, "|");
-                                       /* SLEAZY HACK: pad the line out so
-                                        * it's the same length as the line
-                                        * we're replacing.
-                                        */
-                                       while (strlen(buf) < line_length) {
-                                               strcat(buf, " ");
-                                       }
-                                       fseek(ncfp, line_offset, SEEK_SET);
-                                       fprintf(ncfp, "%s\n", buf);
-                                       ++success;
-                               }
+       OneRNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
+
+       ConfirmType = maxRoomNetCfg;
+       if (OneRNCfg==NULL)
+       {
+               errmsg = "no networking config found";
+       }
+       else for (i = 0; i < 2; i++)
+       {
+               int offset;
+
+               if (ConfirmSubscribers[i] == subpending)
+                       offset = 2;
+               else
+                       offset = 1;
+               PrevLine = &OneRNCfg->NetConfigs[ConfirmSubscribers[i]];
+               Line = *PrevLine;
+               while (Line != NULL)
+               {
+                       if (!strcasecmp(ChrPtr(*token),
+                                       ChrPtr(Line->Value[offset])))
+                       {
+                               ConfirmLine = Line;
+                               *PrevLine = Line->next; /* Remove it from the list */
+                               ConfirmType = ConfirmSubscribers[i];
+                               ConfirmLine->next = NULL;
+
+                               i += 100; 
+                               break;
+
                        }
-                       if (!strcasecmp(cmd, "unsubpending")) {
-                               extract_token(line_token, buf, 2, '|', sizeof line_token);
-                               if (!strcasecmp(token, line_token)) {
-                                       extract_token(address_to_unsubscribe, buf, 1, '|',
-                                               sizeof address_to_unsubscribe);
-                               }
+                       PrevLine = &(*PrevLine)->next;
+                       Line = Line->next;
+               }
+               if (ConfirmType == maxRoomNetCfg)
+               {
+                       errmsg = "No active un/subscribe request found";
+               }
+       }
+
+       if (ConfirmType == subpending)
+       {
+               if (CountThisSubscriber(OneRNCfg, ConfirmLine->Value[0]) == 0)
+               {
+                       if (!strcasecmp(ChrPtr(ConfirmLine->Value[2]), 
+                                       ("digest")))
+                       {
+                               ConfirmType = digestrecp;
                        }
+                       else /* "list" */
+                       {
+                               ConfirmType = listrecp;
+                       }
+
+                       syslog(LOG_NOTICE, 
+                              "Mailing list: %s subscribed to %s with token %s\n", 
+                              ChrPtr(ConfirmLine->Value[0]), 
+                              qrbuf.QRname,
+                              ChrPtr(*token));
+
+                       FreeStrBuf(&ConfirmLine->Value[1]);
+                       FreeStrBuf(&ConfirmLine->Value[2]);
+                       FreeStrBuf(&ConfirmLine->Value[3]);
+                       FreeStrBuf(&ConfirmLine->Value[4]);
+                       ConfirmLine->nValues = 1;
+               
+                       AddRoomCfgLine(OneRNCfg, &qrbuf, ConfirmType, ConfirmLine);
+                       success = 1;
+               }
+               else
+               {
+                       /* whipe duplicate subscribe entry... */
+                       OneRNCfg->changed = 1;
+                       SaveChangedConfigs();
+                       errmsg = "already subscribed";
                }
-               fclose(ncfp);
        }
-       end_critical_section(S_NETCONFIGS);
+       else if (ConfirmType == unsubpending)
+       {
 
-       /*
-        * If "address_to_unsubscribe" contains something, then we have to
-        * make another pass at the file, stripping out lines referring to
-        * that address.
-        */
-       if (!IsEmptyStr(address_to_unsubscribe)) {
-               holdbuf = malloc(SIZ);
-               begin_critical_section(S_NETCONFIGS);
-               ncfp = fopen(filename, "r+");
-               if (ncfp != NULL) {
-                       while (line_offset = ftell(ncfp),
-                             (fgets(buf, sizeof buf, ncfp) != NULL) ) {
-                               buf[strlen(buf)-1]=0;
-                               extract_token(scancmd, buf, 0, '|', sizeof scancmd);
-                               extract_token(scanemail, buf, 1, '|', sizeof scanemail);
-                               if ( (!strcasecmp(scancmd, "listrecp"))
-                                  && (!strcasecmp(scanemail,
-                                               address_to_unsubscribe)) ) {
-                                       ++success;
-                               }
-                               else if ( (!strcasecmp(scancmd, "digestrecp"))
-                                  && (!strcasecmp(scanemail,
-                                               address_to_unsubscribe)) ) {
-                                       ++success;
-                               }
-                               else if ( (!strcasecmp(scancmd, "subpending"))
-                                  && (!strcasecmp(scanemail,
-                                               address_to_unsubscribe)) ) {
-                                       ++success;
-                               }
-                               else if ( (!strcasecmp(scancmd, "unsubpending"))
-                                  && (!strcasecmp(scanemail,
-                                               address_to_unsubscribe)) ) {
-                                       ++success;
-                               }
-                               else {  /* Not relevant, so *keep* it! */
-                                       linelen = strlen(buf);
-                                       holdbuf = realloc(holdbuf,
-                                               (buflen + linelen + 2) );
-                                       strcpy(&holdbuf[buflen], buf);
-                                       buflen += linelen;
-                                       strcpy(&holdbuf[buflen], "\n");
-                                       buflen += 1;
+               for (i = 0; i < 2; i++)
+               {
+                       PrevLine = &OneRNCfg->NetConfigs[ActiveSubscribers[i]];
+                       Line = *PrevLine;
+                       while (Line != NULL)
+                       {
+                               if (!strcasecmp(ChrPtr(ConfirmLine->Value[0]),
+                                               ChrPtr(Line->Value[0])))
+                               {
+                                       success = 1;
+                                       RemoveLine = Line;
+                                       *PrevLine = Line->next; /* Remove it from the list */
+                                       RemoveLine->next = NULL;
+                                       if (RemoveLine != NULL) 
+                                               DeleteGenericCfgLine(NULL/*TODO*/, &RemoveLine);
+                                       Line = *PrevLine;
+                                       continue;
                                }
+                               PrevLine = &(*PrevLine)->next;
+                               Line = Line->next;
                        }
-                       fclose(ncfp);
                }
-               ncfp = fopen(filename, "w");
-               if (ncfp != NULL) {
-                       fwrite(holdbuf, buflen+1, 1, ncfp);
-                       fclose(ncfp);
+
+               if (success) 
+               {
+                       syslog(LOG_NOTICE, 
+                              "Mailing list: %s unsubscribed to %s with token %s\n", 
+                              ChrPtr(ConfirmLine->Value[0]), 
+                              qrbuf.QRname,
+                              ChrPtr(*token));
                }
-               end_critical_section(S_NETCONFIGS);
-               free(holdbuf);
+               else
+               {
+                       errmsg = "no subscriber found for this unsubscription request";
+               }
+               DeleteGenericCfgLine(NULL/*TODO*/, &ConfirmLine);
+               OneRNCfg->changed = 1;
+               SaveChangedConfigs();
        }
 
+       end_critical_section(S_NETCONFIGS);
+       
        /*
         * Did we do anything useful today?
         */
        if (success) {
                cprintf("%d %d operation(s) confirmed.\n", CIT_OK, success);
-               syslog(LOG_NOTICE, 
-                       "Mailing list: %s %ssubscribed to %s with token %s\n", 
-                       email, 
-                       (!IsEmptyStr(address_to_unsubscribe)) ? "un" : "", 
-                       room, 
-                       token);
        }
        else {
+               syslog(LOG_NOTICE, "failed processing (un)subscribe request: %s",
+                      errmsg);
                cprintf("%d Invalid token.\n", ERROR + ILLEGAL_VALUE);
        }
 
@@ -516,44 +658,45 @@ void do_confirm(char *room, char *token) {
 /* 
  * process subscribe/unsubscribe requests and confirmations
  */
-void cmd_subs(char *cmdbuf) {
+void cmd_subs(char *cmdbuf)
+{
+       const char *Pos = NULL;
+       StrBuf *Segments[20];
+       int i=1;
 
-       char opr[256];
-       char room[ROOMNAMELEN];
-       char email[256];
-       char subtype[256];
-       char token[256];
-       char webpage[256];
+       memset(Segments, 0, sizeof(StrBuf*) * 20);
+       Segments[0] = NewStrBufPlain(cmdbuf, -1);
+       while ((Pos != StrBufNOTNULL) && (i < 20))
+       {
+               Segments[i] = NewStrBufPlain(NULL, StrLength(Segments[0]));
+               StrBufExtract_NextToken(Segments[i], Segments[0], &Pos, '|');
+               i++;
+       }
 
-       extract_token(opr, cmdbuf, 0, '|', sizeof opr);
-       if (!strcasecmp(opr, "subscribe")) {
-               extract_token(subtype, cmdbuf, 3, '|', sizeof subtype);
-               if ( (strcasecmp(subtype, "list"))
-                  && (strcasecmp(subtype, "digest")) ) {
+       if (!strcasecmp(ChrPtr(Segments[1]), "subscribe")) {
+               if ( (strcasecmp(ChrPtr(Segments[4]), "list"))
+                  && (strcasecmp(ChrPtr(Segments[4]), "digest")) ) {
                        cprintf("%d Invalid subscription type '%s'\n",
-                               ERROR + ILLEGAL_VALUE, subtype);
+                               ERROR + ILLEGAL_VALUE, ChrPtr(Segments[4]));
                }
                else {
-                       extract_token(room, cmdbuf, 1, '|', sizeof room);
-                       extract_token(email, cmdbuf, 2, '|', sizeof email);
-                       extract_token(webpage, cmdbuf, 4, '|', sizeof webpage);
-                       do_subscribe(room, email, subtype, webpage);
+                       do_subscribe(&Segments[2], &Segments[3], &Segments[4], &Segments[5]);
                }
        }
-       else if (!strcasecmp(opr, "unsubscribe")) {
-               extract_token(room, cmdbuf, 1, '|', sizeof room);
-               extract_token(email, cmdbuf, 2, '|', sizeof email);
-               extract_token(webpage, cmdbuf, 3, '|', sizeof webpage);
-               do_unsubscribe(room, email, webpage);
+       else if (!strcasecmp(ChrPtr(Segments[1]), "unsubscribe")) {
+               do_unsubscribe(&Segments[2], &Segments[3], &Segments[4]);
        }
-       else if (!strcasecmp(opr, "confirm")) {
-               extract_token(room, cmdbuf, 1, '|', sizeof room);
-               extract_token(token, cmdbuf, 2, '|', sizeof token);
-               do_confirm(room, token);
+       else if (!strcasecmp(ChrPtr(Segments[1]), "confirm")) {
+               do_confirm(&Segments[2], &Segments[3]);
        }
        else {
                cprintf("%d Invalid command\n", ERROR + ILLEGAL_VALUE);
        }
+
+       for (; i>=0; i--)
+       {
+               FreeStrBuf(&Segments[i]);
+       }
 }
 
 
diff --git a/citadel/modules/network/netconfig.h b/citadel/modules/network/netconfig.h
deleted file mode 100644 (file)
index 6168f04..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * This module handles shared rooms, inter-Citadel mail, and outbound
- * mailing list processing.
- *
- * Copyright (c) 2000-2012 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 as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * ** NOTE **   A word on the S_NETCONFIGS semaphore:
- * This is a fairly high-level type of critical section.  It ensures that no
- * two threads work on the netconfigs files at the same time.  Since we do
- * so many things inside these, here are the rules:
- *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
- *  2. Do *not* perform any I/O with the client during these sections.
- *
- */
-
-typedef struct _nodeconf {
-       int DeleteMe;
-       StrBuf *NodeName;
-       StrBuf *Secret;
-       StrBuf *Host;
-       StrBuf *Port;
-}NodeConf;
-
-typedef struct __NetMap {
-       StrBuf *NodeName;
-       time_t lastcontact;
-       StrBuf *NextHop;
-}NetMap;
-
-HashList* load_ignetcfg(void);
-
-HashList* read_network_map(void);
-StrBuf *SerializeNetworkMap(HashList *Map);
-void network_learn_topology(char *node, char *path, HashList *the_netmap, int *netmap_changed);
-
-int is_valid_node(const StrBuf **nexthop,
-                 const StrBuf **secret,
-                 StrBuf *node,
-                 HashList *IgnetCfg,
-                 HashList *the_netmap);
index e52829bce1e3b4fd639f412c81585668b9e7a2ae..0fcb1808ba1f9319aef3ffa07212ee2cfb856053 100644 (file)
  *
  */
 
-typedef struct maplist maplist;
-
-struct maplist {
-       struct maplist *next;
-       char remote_nodename[SIZ];
-       char remote_roomname[SIZ];
-};
-
-
 typedef struct SpoolControl SpoolControl;
 
 struct SpoolControl {
-       long lastsent;
-       namelist *listrecps;
-       namelist *digestrecps;
-       namelist *participates;
-       maplist *ignet_push_shares;
-       char *misc;
+       OneRoomNetCfg *RNCfg;
        FILE *digestfp;
        int num_msgs_spooled;
+       long lastsent;
 
        HashList *working_ignetcfg;
        HashList *the_netmap;
@@ -61,4 +48,3 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
 void free_spoolcontrol_struct(SpoolControl **scc);
 int writenfree_spoolcontrol_file(SpoolControl **scc, char *filename);
 int read_spoolcontrol_file(SpoolControl **scc, char *filename);
-int is_recipient(SpoolControl *sc, const char *Name);
diff --git a/citadel/modules/network/serv_netconfig.c b/citadel/modules/network/serv_netconfig.c
deleted file mode 100644 (file)
index 12662f0..0000000
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * This module handles shared rooms, inter-Citadel mail, and outbound
- * mailing list processing.
- *
- * Copyright (c) 2000-2012 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.
- *
- * ** NOTE **   A word on the S_NETCONFIGS semaphore:
- * This is a fairly high-level type of critical section.  It ensures that no
- * two threads work on the netconfigs files at the same time.  Since we do
- * so many things inside these, here are the rules:
- *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
- *  2. Do *not* perform any I/O with the client during these sections.
- *
- */
-
-/*
- * Duration of time (in seconds) after which pending list subscribe/unsubscribe
- * requests that have not been confirmed will be deleted.
- */
-#define EXP    259200  /* three days */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.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
-#ifdef HAVE_SYSCALL_H
-# include <syscall.h>
-#else 
-# if HAVE_SYS_SYSCALL_H
-#  include <sys/syscall.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "server.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "user_ops.h"
-#include "database.h"
-#include "msgbase.h"
-#include "internet_addressing.h"
-#include "serv_network.h"
-#include "clientsocket.h"
-#include "file_ops.h"
-#include "citadel_dirs.h"
-#include "threads.h"
-#include "context.h"
-#include "netconfig.h"
-#include "netspool.h"
-#include "ctdl_module.h"
-
-
-
-void DeleteNodeConf(void *vNode)
-{
-       NodeConf *Node = (NodeConf*) vNode;
-       FreeStrBuf(&Node->NodeName);
-       FreeStrBuf(&Node->Secret);
-       FreeStrBuf(&Node->Host);
-       FreeStrBuf(&Node->Port);
-       free(Node);
-}
-
-NodeConf *NewNode(StrBuf *SerializedNode)
-{
-       const char *Pos = NULL;
-       NodeConf *Node;
-
-       /* we need at least 4 pipes and some other text so its invalid. */
-       if (StrLength(SerializedNode) < 8)
-               return NULL;
-       Node = (NodeConf *) malloc(sizeof(NodeConf));
-
-       Node->DeleteMe = 0;
-
-       Node->NodeName=NewStrBuf();
-       StrBufExtract_NextToken(Node->NodeName, SerializedNode, &Pos, '|');
-
-       Node->Secret=NewStrBuf();
-       StrBufExtract_NextToken(Node->Secret, SerializedNode, &Pos, '|');
-
-       Node->Host=NewStrBuf();
-       StrBufExtract_NextToken(Node->Host, SerializedNode, &Pos, '|');
-
-       Node->Port=NewStrBuf();
-       StrBufExtract_NextToken(Node->Port, SerializedNode, &Pos, '|');
-       return Node;
-}
-
-
-/*
- * Load or refresh the Citadel network (IGnet) configuration for this node.
- */
-HashList* load_ignetcfg(void)
-{
-       const char *LinePos;
-       char       *Cfg;
-       StrBuf     *Buf;
-       StrBuf     *LineBuf;
-       HashList   *Hash;
-       NodeConf   *Node;
-
-       Cfg =  CtdlGetSysConfig(IGNETCFG);
-       if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
-               if (Cfg != NULL)
-                       free(Cfg);
-               return NULL;
-       }
-
-       Hash = NewHash(1, NULL);
-       Buf = NewStrBufPlain(Cfg, -1);
-       free(Cfg);
-       LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
-       LinePos = NULL;
-       do
-       {
-               StrBufSipLine(LineBuf, Buf, &LinePos);
-               if (StrLength(LineBuf) != 0) {
-                       Node = NewNode(LineBuf);
-                       if (Node != NULL) {
-                               Put(Hash, SKEY(Node->NodeName), Node, DeleteNodeConf);
-                       }
-               }
-       } while (LinePos != StrBufNOTNULL);
-       FreeStrBuf(&Buf);
-       FreeStrBuf(&LineBuf);
-       return Hash;
-}
-
-void DeleteNetMap(void *vNetMap)
-{
-       NetMap *TheNetMap = (NetMap*) vNetMap;
-       FreeStrBuf(&TheNetMap->NodeName);
-       FreeStrBuf(&TheNetMap->NextHop);
-       free(TheNetMap);
-}
-
-NetMap *NewNetMap(StrBuf *SerializedNetMap)
-{
-       const char *Pos = NULL;
-       NetMap *NM;
-
-       /* we need at least 3 pipes and some other text so its invalid. */
-       if (StrLength(SerializedNetMap) < 6)
-               return NULL;
-       NM = (NetMap *) malloc(sizeof(NetMap));
-
-       NM->NodeName=NewStrBuf();
-       StrBufExtract_NextToken(NM->NodeName, SerializedNetMap, &Pos, '|');
-
-       NM->lastcontact = StrBufExtractNext_long(SerializedNetMap, &Pos, '|');
-
-       NM->NextHop=NewStrBuf();
-       StrBufExtract_NextToken(NM->NextHop, SerializedNetMap, &Pos, '|');
-
-       return NM;
-}
-
-HashList* read_network_map(void)
-{
-       const char *LinePos;
-       char       *Cfg;
-       StrBuf     *Buf;
-       StrBuf     *LineBuf;
-       HashList   *Hash;
-       NetMap     *TheNetMap;
-
-       Cfg =  CtdlGetSysConfig(IGNETMAP);
-       if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
-               if (Cfg != NULL)
-                       free(Cfg);
-               return NULL;
-       }
-
-       Hash = NewHash(1, NULL);
-       Buf = NewStrBufPlain(Cfg, -1);
-       free(Cfg);
-       LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
-       LinePos = NULL;
-       while (StrBufSipLine(Buf, LineBuf, &LinePos))
-       {
-               TheNetMap = NewNetMap(LineBuf);
-               if (TheNetMap != NULL) { /* TODO: is the NodeName Uniq? */
-                       Put(Hash, SKEY(TheNetMap->NodeName), TheNetMap, DeleteNetMap);
-               }
-       }
-       FreeStrBuf(&Buf);
-       FreeStrBuf(&LineBuf);
-       return Hash;
-}
-
-StrBuf *SerializeNetworkMap(HashList *Map)
-{
-       void *vMap;
-       const char *key;
-       long len;
-       StrBuf *Ret = NewStrBuf();
-       HashPos *Pos = GetNewHashPos(Map, 0);
-
-       while (GetNextHashPos(Map, Pos, &len, &key, &vMap))
-       {
-               NetMap *pMap = (NetMap*) vMap;
-               StrBufAppendBuf(Ret, pMap->NodeName, 0);
-               StrBufAppendBufPlain(Ret, HKEY("|"), 0);
-
-               StrBufAppendPrintf(Ret, "%ld", pMap->lastcontact, 0);
-               StrBufAppendBufPlain(Ret, HKEY("|"), 0);
-
-               StrBufAppendBuf(Ret, pMap->NextHop, 0);
-               StrBufAppendBufPlain(Ret, HKEY("\n"), 0);
-       }
-       DeleteHashPos(&Pos);
-       return Ret;
-}
-
-
-/*
- * Learn topology from path fields
- */
-void network_learn_topology(char *node, char *path, HashList *the_netmap, int *netmap_changed)
-{
-       NetMap *pNM = NULL;
-       void *vptr;
-       char nexthop[256];
-       NetMap *nmptr;
-
-       if (GetHash(the_netmap, node, strlen(node), &vptr) && 
-           (vptr != NULL))/* TODO: is the NodeName Uniq? */
-       {
-               pNM = (NetMap*)vptr;
-               extract_token(nexthop, path, 0, '!', sizeof nexthop);
-               if (!strcmp(nexthop, ChrPtr(pNM->NextHop))) {
-                       pNM->lastcontact = time(NULL);
-                       (*netmap_changed) ++;
-                       return;
-               }
-       }
-
-       /* If we got here then it's not in the map, so add it. */
-       nmptr = (NetMap *) malloc(sizeof (NetMap));
-       nmptr->NodeName = NewStrBufPlain(node, -1);
-       nmptr->lastcontact = time(NULL);
-       nmptr->NextHop = NewStrBuf ();
-       StrBufExtract_tokenFromStr(nmptr->NextHop, path, strlen(path), 0, '!');
-       /* TODO: is the NodeName Uniq? */
-       Put(the_netmap, SKEY(nmptr->NodeName), nmptr, DeleteNetMap);
-       (*netmap_changed) ++;
-}
-
-
-/*
- * Check the network map and determine whether the supplied node name is
- * valid.  If it is not a neighbor node, supply the name of a neighbor node
- * which is the next hop.  If it *is* a neighbor node, we also fill in the
- * shared secret.
- */
-int is_valid_node(const StrBuf **nexthop,
-                 const StrBuf **secret,
-                 StrBuf *node,
-                 HashList *IgnetCfg,
-                 HashList *the_netmap)
-{
-       void *vNetMap;
-       void *vNodeConf;
-       NodeConf *TheNode;
-       NetMap *TheNetMap;
-
-       if (StrLength(node) == 0) {
-               return(-1);
-       }
-
-       /*
-        * First try the neighbor nodes
-        */
-       if (GetCount(IgnetCfg) == 0) {
-               syslog(LOG_INFO, "IgnetCfg is empty!\n");
-               if (nexthop != NULL) {
-                       *nexthop = NULL;
-               }
-               return(-1);
-       }
-
-       /* try to find a neigbour with the name 'node' */
-       if (GetHash(IgnetCfg, SKEY(node), &vNodeConf) && 
-           (vNodeConf != NULL))
-       {
-               TheNode = (NodeConf*)vNodeConf;
-               if (secret != NULL)
-                       *secret = TheNode->Secret;
-               return 0;               /* yup, it's a direct neighbor */
-       }
-
-       /*
-        * If we get to this point we have to see if we know the next hop
-        *//* TODO: is the NodeName Uniq? */
-       if ((GetCount(the_netmap) > 0) &&
-           (GetHash(the_netmap, SKEY(node), &vNetMap)))
-       {
-               TheNetMap = (NetMap*)vNetMap;
-               if (nexthop != NULL)
-                       *nexthop = TheNetMap->NextHop;
-               return(0);
-       }
-
-       /*
-        * If we get to this point, the supplied node name is bogus.
-        */
-       syslog(LOG_ERR, "Invalid node name <%s>\n", ChrPtr(node));
-       return(-1);
-}
-
-
-void cmd_gnet(char *argbuf)
-{
-       char filename[PATH_MAX];
-       char buf[SIZ];
-       FILE *fp;
-
-
-       if (!IsEmptyStr(argbuf))
-       {
-               if (CtdlAccessCheck(ac_aide)) return;
-               if (strcmp(argbuf, FILE_MAILALIAS))
-               {
-                       cprintf("%d No such file or directory\n",
-                               ERROR + INTERNAL_ERROR);
-                       return;
-               }
-               safestrncpy(filename, file_mail_aliases, sizeof(filename));
-               cprintf("%d Settings for <%s>\n",
-                       LISTING_FOLLOWS,
-                       filename);
-       }
-       else
-       {
-               if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) {
-                       /* users can edit the netconfigs for their own mailbox rooms */
-               }
-               else if (CtdlAccessCheck(ac_room_aide)) return;
-               
-               assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
-               cprintf("%d Network settings for room #%ld <%s>\n",
-                       LISTING_FOLLOWS,
-                       CC->room.QRnumber, CC->room.QRname);
-       }
-
-       fp = fopen(filename, "r");
-       if (fp != NULL) {
-               while (fgets(buf, sizeof buf, fp) != NULL) {
-                       buf[strlen(buf)-1] = 0;
-                       cprintf("%s\n", buf);
-               }
-               fclose(fp);
-       }
-
-       cprintf("000\n");
-}
-
-#define nForceAliases 5
-const ConstStr ForceAliases[nForceAliases] = {
-       {HKEY("bbs,")},
-       {HKEY("root,")},
-       {HKEY("Auto,")},
-       {HKEY("postmaster,")},
-       {HKEY("abuse,")}
-};
-
-void cmd_snet(char *argbuf) {
-       char tempfilename[PATH_MAX];
-       char filename[PATH_MAX];
-       int TmpFD;
-       StrBuf *Line;
-       struct stat StatBuf;
-       long len;
-       int rc;
-       int IsMailAlias = 0;
-       int MailAliasesFound[nForceAliases];
-
-       unbuffer_output();
-
-       if (!IsEmptyStr(argbuf))
-       {
-               if (CtdlAccessCheck(ac_aide)) return;
-               if (strcmp(argbuf, FILE_MAILALIAS))
-               {
-                       cprintf("%d No such file or directory\n",
-                               ERROR + INTERNAL_ERROR);
-                       return;
-               }
-               len = safestrncpy(filename, file_mail_aliases, sizeof(filename));
-               memset(MailAliasesFound, 0, sizeof(MailAliasesFound));
-               memcpy(tempfilename, filename, len + 1);
-               IsMailAlias = 1;
-       }
-       else
-       {
-               if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) {
-                       /* users can edit the netconfigs for their own mailbox rooms */
-               }
-               else if (CtdlAccessCheck(ac_room_aide)) return;
-               
-               len = assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
-               memcpy(tempfilename, filename, len + 1);
-       }
-       memset(&StatBuf, 0, sizeof(struct stat));
-       if ((stat(filename, &StatBuf)  == -1) || (StatBuf.st_size == 0))
-               StatBuf.st_size = 80; /* Not there or empty? guess 80 chars line. */
-
-       sprintf(tempfilename + len, ".%d", CC->cs_pid);
-       errno = 0;
-       TmpFD = open(tempfilename, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
-
-       if ((TmpFD > 0) && (errno == 0))
-       {
-               char *tmp = malloc(StatBuf.st_size * 2);
-               memset(tmp, ' ', StatBuf.st_size * 2);
-               rc = write(TmpFD, tmp, StatBuf.st_size * 2);
-               free(tmp);
-               if ((rc <= 0) || (rc != StatBuf.st_size * 2))
-               {
-                       close(TmpFD);
-                       cprintf("%d Unable to allocate the space required for %s: %s\n",
-                               ERROR + INTERNAL_ERROR,
-                               tempfilename,
-                               strerror(errno));
-                       unlink(tempfilename);
-                       return;
-               }       
-               lseek(TmpFD, SEEK_SET, 0);
-       }
-       else {
-               cprintf("%d Unable to allocate the space required for %s: %s\n",
-                       ERROR + INTERNAL_ERROR,
-                       tempfilename,
-                       strerror(errno));
-               unlink(tempfilename);
-               return;
-       }
-       Line = NewStrBuf();
-
-       cprintf("%d %s\n", SEND_LISTING, tempfilename);
-
-       len = 0;
-       while (rc = CtdlClientGetLine(Line), 
-              (rc >= 0))
-       {
-               if ((rc == 3) && (strcmp(ChrPtr(Line), "000") == 0))
-                       break;
-               if (IsMailAlias)
-               {
-                       int i;
-
-                       for (i = 0; i < nForceAliases; i++)
-                       {
-                               if ((!MailAliasesFound[i]) && 
-                                   (strncmp(ForceAliases[i].Key, 
-                                            ChrPtr(Line),
-                                            ForceAliases[i].len) == 0)
-                                       )
-                                   {
-                                           MailAliasesFound[i] = 1;
-                                           break;
-                                   }
-                       }
-               }
-
-               StrBufAppendBufPlain(Line, HKEY("\n"), 0);
-               write(TmpFD, ChrPtr(Line), StrLength(Line));
-               len += StrLength(Line);
-       }
-       FreeStrBuf(&Line);
-       ftruncate(TmpFD, len);
-       close(TmpFD);
-
-       if (IsMailAlias)
-       {
-               int i, state;
-               /*
-                * Sanity check whether all aliases required by the RFCs were set
-                * else bail out.
-                */
-               state = 1;
-               for (i = 0; i < nForceAliases; i++)
-               {
-                       if (!MailAliasesFound[i]) 
-                               state = 0;
-               }
-               if (state == 0)
-               {
-                       cprintf("%d won't do this - you're missing an RFC required alias.\n",
-                               ERROR + INTERNAL_ERROR);
-                       unlink(tempfilename);
-                       return;
-               }
-       }
-
-       /* Now copy the temp file to its permanent location.
-        * (We copy instead of link because they may be on different filesystems)
-        */
-       begin_critical_section(S_NETCONFIGS);
-       rename(tempfilename, filename);
-       end_critical_section(S_NETCONFIGS);
-}
-
-/*
- * cmd_netp() - authenticate to the server as another Citadel node polling
- *           for network traffic
- */
-void cmd_netp(char *cmdbuf)
-{
-       struct CitContext *CCC = CC;
-       HashList *working_ignetcfg;
-       char *node;
-       StrBuf *NodeStr;
-       long nodelen;
-       int v;
-       long lens[2];
-       const char *strs[2];
-
-       const StrBuf *secret = NULL;
-       const StrBuf *nexthop = NULL;
-       char err_buf[SIZ] = "";
-
-       /* Authenticate */
-       node = CCC->curr_user;
-       nodelen = extract_token(CCC->curr_user, cmdbuf, 0, '|', sizeof CCC->curr_user);
-       NodeStr = NewStrBufPlain(node, nodelen);
-       /* load the IGnet Configuration to check node validity */
-       working_ignetcfg = load_ignetcfg();
-       v = is_valid_node(&nexthop, &secret, NodeStr, working_ignetcfg, NULL);
-       if (v != 0) {
-               snprintf(err_buf, sizeof err_buf,
-                       "An unknown Citadel server called \"%s\" attempted to connect from %s [%s].\n",
-                       node, CCC->cs_host, CCC->cs_addr
-               );
-               syslog(LOG_WARNING, "%s", err_buf);
-               cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
-
-               strs[0] = CCC->cs_addr;
-               lens[0] = strlen(CCC->cs_addr);
-               
-               strs[1] = "SRV_UNKNOWN";
-               lens[1] = sizeof("SRV_UNKNOWN" - 1);
-
-               CtdlAideFPMessage(
-                       err_buf,
-                       "IGNet Networking.",
-                       2, strs, (long*) &lens);
-
-               DeleteHash(&working_ignetcfg);
-               FreeStrBuf(&NodeStr);
-               return;
-       }
-
-       extract_token(CCC->user.password, cmdbuf, 1, '|', sizeof CCC->user.password);
-       if (strcasecmp(CCC->user.password, ChrPtr(secret))) {
-               snprintf(err_buf, sizeof err_buf,
-                       "A Citadel server at %s [%s] failed to authenticate as network node \"%s\".\n",
-                       CCC->cs_host, CCC->cs_addr, node
-               );
-               syslog(LOG_WARNING, "%s", err_buf);
-               cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
-
-               strs[0] = CCC->cs_addr;
-               lens[0] = strlen(CCC->cs_addr);
-               
-               strs[1] = "SRV_PW";
-               lens[1] = sizeof("SRV_PW" - 1);
-
-               CtdlAideFPMessage(
-                       err_buf,
-                       "IGNet Networking.",
-                       2, strs, (long*) &lens);
-
-               DeleteHash(&working_ignetcfg);
-               FreeStrBuf(&NodeStr);
-               return;
-       }
-
-       if (network_talking_to(node, nodelen, NTT_CHECK)) {
-               syslog(LOG_WARNING, "Duplicate session for network node <%s>", node);
-               cprintf("%d Already talking to %s right now\n", ERROR + RESOURCE_BUSY, node);
-               DeleteHash(&working_ignetcfg);
-               FreeStrBuf(&NodeStr);
-               return;
-       }
-       nodelen = safestrncpy(CCC->net_node, node, sizeof CCC->net_node);
-       network_talking_to(CCC->net_node, nodelen, NTT_ADD);
-       syslog(LOG_NOTICE, "Network node <%s> logged in from %s [%s]\n",
-               CCC->net_node, CCC->cs_host, CCC->cs_addr
-       );
-       cprintf("%d authenticated as network node '%s'\n", CIT_OK, CCC->net_node);
-       DeleteHash(&working_ignetcfg);
-       FreeStrBuf(&NodeStr);
-}
-
-int netconfig_check_roomaccess(
-       char *errmsgbuf, 
-       size_t n,
-       const char* RemoteIdentifier)
-{
-       SpoolControl *sc;
-       char filename[SIZ];
-       int found;
-
-       if (RemoteIdentifier == NULL)
-       {
-               snprintf(errmsgbuf, n, "Need sender to permit access.");
-               return (ERROR + USERNAME_REQUIRED);
-       }
-
-       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
-       begin_critical_section(S_NETCONFIGS);
-       if (!read_spoolcontrol_file(&sc, filename))
-       {
-               end_critical_section(S_NETCONFIGS);
-               snprintf(errmsgbuf, n,
-                        "This mailing list only accepts posts from subscribers.");
-               return (ERROR + NO_SUCH_USER);
-       }
-       end_critical_section(S_NETCONFIGS);
-       found = is_recipient (sc, RemoteIdentifier);
-       free_spoolcontrol_struct(&sc);
-       if (found) {
-               return (0);
-       }
-       else {
-               snprintf(errmsgbuf, n,
-                        "This mailing list only accepts posts from subscribers.");
-               return (ERROR + NO_SUCH_USER);
-       }
-}
-/*
- * Module entry point
- */
-CTDL_MODULE_INIT(netconfig)
-{
-       if (!threading)
-       {
-               CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
-               CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config");
-               CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller");
-       }
-       return "netconfig";
-}
index 450e803b2bca52655cabf98886b649b82910961d..1473385ae7eba7efe8b2af4fee4b69837b684c58 100644 (file)
@@ -189,7 +189,10 @@ CTDL_MODULE_INIT(netfilter)
 {
        if (!threading)
        {
+/*
+  currently unsupported.
                CtdlRegisterNetprocHook(filter_the_idiots);
+*/
        }
        
        /* return our module name for the log */
index a793c5f600b0151984a576213cb944d43f39bfed..3eab57c5eb538840a996961f6b6bd5dfa870a788 100644 (file)
 #include "citadel_dirs.h"
 #include "threads.h"
 #include "context.h"
-#include "netconfig.h"
+#include "ctdl_module.h"
 #include "netspool.h"
 #include "netmail.h"
-#include "ctdl_module.h"
 
+void network_deliver_list(struct CtdlMessage *msg, SpoolControl *sc, const char *RoomName);
 
 /*
  * Deliver digest messages
  */
-void network_deliver_digest(SpoolControl *sc) {
+void network_deliver_digest(SpoolControl *sc)
+{
        struct CitContext *CCC = CC;
        char buf[SIZ];
        int i;
        struct CtdlMessage *msg = NULL;
        long msglen;
-       char *recps = NULL;
+       StrBuf *recps = NULL;
+       char *precps;
        size_t recps_len = SIZ;
        struct recptypes *valid;
-       namelist *nptr;
+       RoomNetCfgLine *nptr;
        char bounce_to[256];
 
        if (sc->num_msgs_spooled < 1) {
@@ -125,11 +127,11 @@ void network_deliver_digest(SpoolControl *sc) {
        /* Set the 'List-ID' header */
        msg->cm_fields['L'] = malloc(1024);
        snprintf(msg->cm_fields['L'], 1024,
-               "%s <%ld.list-id.%s>",
-               CC->room.QRname,
-               CC->room.QRnumber,
-               config.c_fqdn
-       );
+                "%s <%ld.list-id.%s>",
+                CC->room.QRname,
+                CC->room.QRnumber,
+                config.c_fqdn
+               );
 
        /*
         * Go fetch the contents of the digest
@@ -150,11 +152,11 @@ void network_deliver_digest(SpoolControl *sc) {
        /*
         * Figure out how big a buffer we need to allocate
         */
-       for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
-               recps_len = recps_len + strlen(nptr->name) + 2;
+       for (nptr = sc->RNCfg->NetConfigs[digestrecp]; nptr != NULL; nptr = nptr->next) {
+               recps_len = recps_len + StrLength(nptr->Value[0]) + 2;
        }
 
-       recps = malloc(recps_len);
+       recps = NewStrBufPlain(NULL, recps_len);
 
        if (recps == NULL) {
                QN_syslog(LOG_EMERG,
@@ -163,14 +165,12 @@ void network_deliver_digest(SpoolControl *sc) {
                abort();
        }
 
-       strcpy(recps, "");
-
        /* Each recipient */
-       for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
-               if (nptr != sc->digestrecps) {
-                       strcat(recps, ",");
+       for (nptr = sc->RNCfg->NetConfigs[digestrecp]; nptr != NULL; nptr = nptr->next) {
+               if (nptr != sc->RNCfg->NetConfigs[digestrecp]) {
+                       StrBufAppendBufPlain(recps, HKEY(","), 0);
                }
-               strcat(recps, nptr->name);
+               StrBufAppendBuf(recps, nptr->Value[0], 0);
        }
 
        /* Where do we want bounces and other noise to be heard?
@@ -178,8 +178,9 @@ void network_deliver_digest(SpoolControl *sc) {
        snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", config.c_fqdn);
 
        /* Now submit the message */
-       valid = validate_recipients(recps, NULL, 0);
-       free(recps);
+       precps = SmashStrBuf(&recps);
+       valid = validate_recipients(precps, NULL, 0);
+       free(precps);
        if (valid != NULL) {
                valid->bounce_to = strdup(bounce_to);
                valid->envelope_from = strdup(bounce_to);
@@ -190,31 +191,217 @@ void network_deliver_digest(SpoolControl *sc) {
 }
 
 
+void network_process_digest(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
+{
+
+       struct CtdlMessage *msg = NULL;
+
+       /*
+        * Process digest recipients
+        */
+       if ((sc->RNCfg->NetConfigs[digestrecp] == NULL) || 
+           (sc->digestfp == NULL))
+               return;
+
+       msg = CtdlDuplicateMessage(omsg);
+       if (msg != NULL) {
+               fprintf(sc->digestfp,
+                       " -----------------------------------"
+                       "------------------------------------"
+                       "-------\n");
+               fprintf(sc->digestfp, "From: ");
+               if (msg->cm_fields['A'] != NULL) {
+                       fprintf(sc->digestfp,
+                               "%s ",
+                               msg->cm_fields['A']);
+               }
+               if (msg->cm_fields['F'] != NULL) {
+                       fprintf(sc->digestfp,
+                               "<%s> ",
+                               msg->cm_fields['F']);
+               }
+               else if (msg->cm_fields['N'] != NULL) {
+                       fprintf(sc->digestfp,
+                               "@%s ",
+                               msg->cm_fields['N']);
+               }
+               fprintf(sc->digestfp, "\n");
+               if (msg->cm_fields['U'] != NULL) {
+                       fprintf(sc->digestfp,
+                               "Subject: %s\n",
+                               msg->cm_fields['U']);
+               }
+
+               CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
+
+               safestrncpy(CC->preferred_formats,
+                           "text/plain",
+                           sizeof CC->preferred_formats);
+
+               CtdlOutputPreLoadedMsg(msg,
+                                      MT_CITADEL,
+                                      HEADERS_NONE,
+                                      0, 0, 0);
+
+               StrBufTrim(CC->redirect_buffer);
+               fwrite(HKEY("\n"), 1, sc->digestfp);
+               fwrite(SKEY(CC->redirect_buffer), 1, sc->digestfp);
+               fwrite(HKEY("\n"), 1, sc->digestfp);
+
+               FreeStrBuf(&CC->redirect_buffer);
+
+               sc->num_msgs_spooled += 1;
+               CtdlFreeMessage(msg);
+       }
+}
+
+
+void network_process_list(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
+{
+       int rlen;
+       char *pCh;
+       StrBuf *Subject, *FlatSubject;
+       struct CtdlMessage *msg = NULL;
+       int i;
+
+       /*
+        * Process mailing list recipients
+        */
+       if (sc->RNCfg->NetConfigs[listrecp] == NULL) 
+               return;
+
+       /* create our own copy of the message.
+        *  We're going to need to modify it
+        * in order to insert the [list name] in it, etc.
+        */
+
+       msg = CtdlDuplicateMessage(omsg);
+
+       if (msg->cm_fields['K'] != NULL)
+               free(msg->cm_fields['K']);
+       if (msg->cm_fields['V'] == NULL){
+               /* local message, no enVelope */
+               StrBuf *Buf;
+               Buf = NewStrBuf();
+               StrBufAppendBufPlain(Buf,
+                                    msg->cm_fields['O']
+                                    , -1, 0);
+               StrBufAppendBufPlain(Buf, HKEY("@"), 0);
+               StrBufAppendBufPlain(Buf, config.c_fqdn, -1, 0);
+
+               msg->cm_fields['K'] = SmashStrBuf(&Buf);
+       }
+       else {
+               msg->cm_fields['K'] =
+                       strdup (msg->cm_fields['V']);
+       }
+       /* Set the 'List-ID' header */
+       if (msg->cm_fields['L'] != NULL) {
+               free(msg->cm_fields['L']);
+       }
+       msg->cm_fields['L'] = malloc(1024);
+       snprintf(msg->cm_fields['L'], 1024,
+                "%s <%ld.list-id.%s>",
+                CC->room.QRname,
+                CC->room.QRnumber,
+                config.c_fqdn
+               );
+
+       /* Prepend "[List name]" to the subject */
+       if (msg->cm_fields['U'] == NULL) {
+               Subject = NewStrBufPlain(HKEY("(no subject)"));
+       }
+       else {
+               Subject = NewStrBufPlain(
+                       msg->cm_fields['U'], -1);
+       }
+       FlatSubject = NewStrBufPlain(NULL, StrLength(Subject));
+       StrBuf_RFC822_to_Utf8(FlatSubject, Subject, NULL, NULL);
+
+       rlen = strlen(CC->room.QRname);
+       pCh  = strstr(ChrPtr(FlatSubject), CC->room.QRname);
+       if ((pCh == NULL) ||
+           (*(pCh + rlen) != ']') ||
+           (pCh == ChrPtr(FlatSubject)) ||
+           (*(pCh - 1) != '[')
+               )
+       {
+               StrBuf *tmp;
+               StrBufPlain(Subject, HKEY("["));
+               StrBufAppendBufPlain(Subject,
+                                    CC->room.QRname,
+                                    rlen, 0);
+               StrBufAppendBufPlain(Subject, HKEY("] "), 0);
+               StrBufAppendBuf(Subject, FlatSubject, 0);
+               /* so we can free the right one swap them */
+               tmp = Subject;
+               Subject = FlatSubject;
+               FlatSubject = tmp;
+               StrBufRFC2047encode(&Subject, FlatSubject);
+       }
+
+       if (msg->cm_fields['U'] != NULL)
+               free (msg->cm_fields['U']);
+       msg->cm_fields['U'] = SmashStrBuf(&Subject);
+
+       FreeStrBuf(&FlatSubject);
+
+       /* else we won't modify the buffer, since the
+        * roomname is already here.
+        */
+
+       /* if there is no other recipient, Set the recipient
+        * of the list message to the email address of the
+        * room itself.
+        */
+       if ((msg->cm_fields['R'] == NULL) ||
+           IsEmptyStr(msg->cm_fields['R']))
+       {
+               if (msg->cm_fields['R'] != NULL)
+                       free(msg->cm_fields['R']);
+
+               msg->cm_fields['R'] = malloc(256);
+               snprintf(msg->cm_fields['R'], 256,
+                        "room_%s@%s", CC->room.QRname,
+                        config.c_fqdn);
+               for (i=0; msg->cm_fields['R'][i]; ++i) {
+                       if (isspace(msg->cm_fields['R'][i])) {
+                               msg->cm_fields['R'][i] = '_';
+                       }
+               }
+       }
+
+       /* Handle delivery */
+       network_deliver_list(msg, sc, CC->room.QRname);
+       CtdlFreeMessage(msg);
+}
+
 /*
  * Deliver list messages to everyone on the list ... efficiently
  */
 void network_deliver_list(struct CtdlMessage *msg, SpoolControl *sc, const char *RoomName)
 {
        struct CitContext *CCC = CC;
-       char *recps = NULL;
+       StrBuf *recps = NULL;
+       char *precps = NULL;
        size_t recps_len = SIZ;
        struct recptypes *valid;
-       namelist *nptr;
+       RoomNetCfgLine *nptr;
        char bounce_to[256];
 
        /* Don't do this if there were no recipients! */
-       if (sc->listrecps == NULL) return;
+       if (sc->RNCfg->NetConfigs[listrecp] == NULL) return;
 
        /* Now generate the delivery instructions */
 
        /*
         * Figure out how big a buffer we need to allocate
         */
-       for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
-               recps_len = recps_len + strlen(nptr->name) + 2;
+       for (nptr = sc->RNCfg->NetConfigs[listrecp]; nptr != NULL; nptr = nptr->next) {
+               recps_len = recps_len + StrLength(nptr->Value[0]) + 2;
        }
 
-       recps = malloc(recps_len);
+       recps = NewStrBufPlain(NULL, recps_len);
 
        if (recps == NULL) {
                QN_syslog(LOG_EMERG,
@@ -223,14 +410,12 @@ void network_deliver_list(struct CtdlMessage *msg, SpoolControl *sc, const char
                abort();
        }
 
-       strcpy(recps, "");
-
        /* Each recipient */
-       for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
-               if (nptr != sc->listrecps) {
-                       strcat(recps, ",");
+       for (nptr = sc->RNCfg->NetConfigs[listrecp]; nptr != NULL; nptr = nptr->next) {
+               if (nptr != sc->RNCfg->NetConfigs[listrecp]) {
+                       StrBufAppendBufPlain(recps, HKEY(","), 0);
                }
-               strcat(recps, nptr->name);
+               StrBufAppendBuf(recps, nptr->Value[0], 0);
        }
 
        /* Where do we want bounces and other noise to be heard?
@@ -238,8 +423,9 @@ void network_deliver_list(struct CtdlMessage *msg, SpoolControl *sc, const char
        snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", config.c_fqdn);
 
        /* Now submit the message */
-       valid = validate_recipients(recps, NULL, 0);
-       free(recps);
+       precps = SmashStrBuf(&recps);
+       valid = validate_recipients(precps, NULL, 0);
+       free(precps);
        if (valid != NULL) {
                valid->bounce_to = strdup(bounce_to);
                valid->envelope_from = strdup(bounce_to);
@@ -251,412 +437,264 @@ void network_deliver_list(struct CtdlMessage *msg, SpoolControl *sc, const char
 }
 
 
-/*
- * Spools out one message from the list.
- */
-void network_spool_msg(long msgnum,
-                      void *userdata)
+void network_process_participate(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
 {
-       struct CitContext *CCC = CC;
-       StrBuf *Buf = NULL;
-       SpoolControl *sc;
-       int i;
-       char *newpath = NULL;
        struct CtdlMessage *msg = NULL;
-       namelist *nptr;
-       maplist *mptr;
-       struct ser_ret sermsg;
-       FILE *fp;
-       char filename[PATH_MAX];
-       char buf[SIZ];
-       int bang = 0;
-       int send = 1;
-       int delete_after_send = 0;      /* Set to 1 to delete after spooling */
+       int i;
        int ok_to_participate = 0;
+       StrBuf *Buf = NULL;
+       RoomNetCfgLine *nptr;
        struct recptypes *valid;
 
-       sc = (SpoolControl *)userdata;
-
        /*
-        * Process mailing list recipients
+        * Process client-side list participations for this room
         */
-       if (sc->listrecps != NULL) {
-               /* Fetch the message.  We're going to need to modify it
-                * in order to insert the [list name] in it, etc.
-                */
-               msg = CtdlFetchMessage(msgnum, 1);
-               if (msg != NULL) {
-                       int rlen;
-                       char *pCh;
-                       StrBuf *Subject, *FlatSubject;
-
-                       if (msg->cm_fields['K'] != NULL)
-                               free(msg->cm_fields['K']);
-                       if (msg->cm_fields['V'] == NULL){
-                               /* local message, no enVelope */
-                               StrBuf *Buf;
-                               Buf = NewStrBuf();
-                               StrBufAppendBufPlain(Buf,
-                                                    msg->cm_fields['O']
-                                                    , -1, 0);
-                               StrBufAppendBufPlain(Buf, HKEY("@"), 0);
-                               StrBufAppendBufPlain(Buf, config.c_fqdn, -1, 0);
-
-                               msg->cm_fields['K'] = SmashStrBuf(&Buf);
-                       }
-                       else {
-                               msg->cm_fields['K'] =
-                                       strdup (msg->cm_fields['V']);
-                       }
-                       /* Set the 'List-ID' header */
-                       if (msg->cm_fields['L'] != NULL) {
-                               free(msg->cm_fields['L']);
-                       }
-                       msg->cm_fields['L'] = malloc(1024);
-                       snprintf(msg->cm_fields['L'], 1024,
-                               "%s <%ld.list-id.%s>",
-                               CC->room.QRname,
-                               CC->room.QRnumber,
-                               config.c_fqdn
-                       );
-
-                       /* Prepend "[List name]" to the subject */
-                       if (msg->cm_fields['U'] == NULL) {
-                               Subject = NewStrBufPlain(HKEY("(no subject)"));
-                       }
-                       else {
-                               Subject = NewStrBufPlain(
-                                       msg->cm_fields['U'], -1);
-                       }
-                       FlatSubject = NewStrBufPlain(NULL, StrLength(Subject));
-                       StrBuf_RFC822_to_Utf8(FlatSubject, Subject, NULL, NULL);
-
-                       rlen = strlen(CC->room.QRname);
-                       pCh  = strstr(ChrPtr(FlatSubject), CC->room.QRname);
-                       if ((pCh == NULL) ||
-                           (*(pCh + rlen) != ']') ||
-                           (pCh == ChrPtr(FlatSubject)) ||
-                           (*(pCh - 1) != '[')
-                               )
-                       {
-                               StrBuf *tmp;
-                               StrBufPlain(Subject, HKEY("["));
-                               StrBufAppendBufPlain(Subject,
-                                                    CC->room.QRname,
-                                                    rlen, 0);
-                               StrBufAppendBufPlain(Subject, HKEY("] "), 0);
-                               StrBufAppendBuf(Subject, FlatSubject, 0);
-                                /* so we can free the right one swap them */
-                               tmp = Subject;
-                               Subject = FlatSubject;
-                               FlatSubject = tmp;
-                               StrBufRFC2047encode(&Subject, FlatSubject);
-                       }
-
-                       if (msg->cm_fields['U'] != NULL)
-                               free (msg->cm_fields['U']);
-                       msg->cm_fields['U'] = SmashStrBuf(&Subject);
-
-                       FreeStrBuf(&FlatSubject);
-
-                       /* else we won't modify the buffer, since the
-                        * roomname is already here.
-                        */
+       if (sc->RNCfg->NetConfigs[participate] == NULL)
+               return;
 
-                       /* if there is no other recipient, Set the recipient
-                        * of the list message to the email address of the
-                        * room itself.
-                        */
-                       if ((msg->cm_fields['R'] == NULL) ||
-                           IsEmptyStr(msg->cm_fields['R']))
-                       {
-                               if (msg->cm_fields['R'] != NULL)
-                                       free(msg->cm_fields['R']);
-
-                               msg->cm_fields['R'] = malloc(256);
-                               snprintf(msg->cm_fields['R'], 256,
-                                        "room_%s@%s", CC->room.QRname,
-                                        config.c_fqdn);
-                               for (i=0; msg->cm_fields['R'][i]; ++i) {
-                                       if (isspace(msg->cm_fields['R'][i])) {
-                                               msg->cm_fields['R'][i] = '_';
-                                       }
-                               }
-                       }
+       msg = CtdlDuplicateMessage(omsg);
 
-                       /* Handle delivery */
-                       network_deliver_list(msg, sc, CC->room.QRname);
-                       CtdlFreeMessage(msg);
+       /* Only send messages which originated on our own
+        * Citadel network, otherwise we'll end up sending the
+        * remote mailing list's messages back to it, which
+        * is rude...
+        */
+       ok_to_participate = 0;
+       if (msg->cm_fields['N'] != NULL) {
+               if (!strcasecmp(msg->cm_fields['N'],
+                               config.c_nodename)) {
+                       ok_to_participate = 1;
+               }
+               
+               Buf = NewStrBufPlain(msg->cm_fields['N'], -1);
+               if (CtdlIsValidNode(NULL,
+                                   NULL,
+                                   Buf,
+                                   sc->working_ignetcfg,
+                                   sc->the_netmap) == 0)
+               {
+                       ok_to_participate = 1;
                }
        }
-
-       /*
-        * Process digest recipients
-        */
-       if ((sc->digestrecps != NULL) && (sc->digestfp != NULL)) {
-               msg = CtdlFetchMessage(msgnum, 1);
-               if (msg != NULL) {
-                       fprintf(sc->digestfp,
-                               " -----------------------------------"
-                               "------------------------------------"
-                               "-------\n");
-                       fprintf(sc->digestfp, "From: ");
-                       if (msg->cm_fields['A'] != NULL) {
-                               fprintf(sc->digestfp,
-                                       "%s ",
-                                       msg->cm_fields['A']);
-                       }
-                       if (msg->cm_fields['F'] != NULL) {
-                               fprintf(sc->digestfp,
-                                       "<%s> ",
-                                       msg->cm_fields['F']);
-                       }
-                       else if (msg->cm_fields['N'] != NULL) {
-                               fprintf(sc->digestfp,
-                                       "@%s ",
-                                       msg->cm_fields['N']);
+       if (ok_to_participate) {
+               if (msg->cm_fields['F'] != NULL) {
+                       free(msg->cm_fields['F']);
+               }
+               msg->cm_fields['F'] = malloc(SIZ);
+               /* Replace the Internet email address of the
+                * actual author with the email address of the
+                * room itself, so the remote listserv doesn't
+                * reject us.
+                * FIXME  I want to be able to pick any address
+                */
+               snprintf(msg->cm_fields['F'], SIZ,
+                        "room_%s@%s", CC->room.QRname,
+                        config.c_fqdn);
+               for (i=0; msg->cm_fields['F'][i]; ++i) {
+                       if (isspace(msg->cm_fields['F'][i])) {
+                               msg->cm_fields['F'][i] = '_';
                        }
-                       fprintf(sc->digestfp, "\n");
-                       if (msg->cm_fields['U'] != NULL) {
-                               fprintf(sc->digestfp,
-                                       "Subject: %s\n",
-                                       msg->cm_fields['U']);
+               }
+               
+               /*
+                * Figure out how big a buffer we need to alloc
+                */
+               for (nptr = sc->RNCfg->NetConfigs[participate];
+                    nptr != NULL;
+                    nptr = nptr->next)
+               {
+                       if (msg->cm_fields['R'] != NULL) {
+                               free(msg->cm_fields['R']);
                        }
-
-                       CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
-
-                       safestrncpy(CC->preferred_formats,
-                                   "text/plain",
-                                   sizeof CC->preferred_formats);
-
-                       CtdlOutputPreLoadedMsg(msg,
-                                              MT_CITADEL,
-                                              HEADERS_NONE,
-                                              0, 0, 0);
-
-                       StrBufTrim(CC->redirect_buffer);
-                       fwrite(HKEY("\n"), 1, sc->digestfp);
-                       fwrite(SKEY(CC->redirect_buffer), 1, sc->digestfp);
-                       fwrite(HKEY("\n"), 1, sc->digestfp);
-
-                       FreeStrBuf(&CC->redirect_buffer);
-
-                       sc->num_msgs_spooled += 1;
-                       CtdlFreeMessage(msg);
+                       msg->cm_fields['R'] =
+                               strdup(ChrPtr(nptr->Value[0]));
+                       
+                       valid = validate_recipients(msg->cm_fields['R'],
+                                                   NULL, 0);
+                       
+                       CtdlSubmitMsg(msg, valid, "", 0);
+                       free_recipients(valid);
                }
        }
+       FreeStrBuf(&Buf);
+       CtdlFreeMessage(msg);
+}
 
+void network_process_ignetpush(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
+{
+       struct CtdlMessage *msg = NULL;
+       struct CitContext *CCC = CC;
+       struct ser_ret sermsg;
+       char buf[SIZ];
+       char filename[PATH_MAX];
+       FILE *fp;
+       size_t newpath_len;
+       char *newpath = NULL;
+       RoomNetCfgLine* mptr;
+       StrBuf *Buf = NULL;
+       int i;
+       int bang = 0;
+       int send = 1;
+
+       if (sc->RNCfg->NetConfigs[ignet_push_share] == NULL)
+               return;
        /*
-        * Process client-side list participations for this room
+        * Process IGnet push shares
         */
-       if (sc->participates != NULL) {
-               msg = CtdlFetchMessage(msgnum, 1);
-               if (msg != NULL) {
-
-                       /* Only send messages which originated on our own
-                        * Citadel network, otherwise we'll end up sending the
-                        * remote mailing list's messages back to it, which
-                        * is rude...
-                        */
-                       ok_to_participate = 0;
-                       if (msg->cm_fields['N'] != NULL) {
-                               if (!strcasecmp(msg->cm_fields['N'],
-                                               config.c_nodename)) {
-                                       ok_to_participate = 1;
-                               }
+       msg = CtdlDuplicateMessage(omsg);
 
-                               Buf = NewStrBufPlain(msg->cm_fields['N'], -1);
-                               if (is_valid_node(NULL,
-                                                 NULL,
-                                                 Buf,
-                                                 sc->working_ignetcfg,
-                                                 sc->the_netmap) == 0)
-                               {
-                                       ok_to_participate = 1;
-                               }
-                       }
-                       if (ok_to_participate) {
-                               if (msg->cm_fields['F'] != NULL) {
-                                       free(msg->cm_fields['F']);
-                               }
-                               msg->cm_fields['F'] = malloc(SIZ);
-                               /* Replace the Internet email address of the
-                                * actual author with the email address of the
-                                * room itself, so the remote listserv doesn't
-                                * reject us.
-                                * FIXME  I want to be able to pick any address
-                               */
-                               snprintf(msg->cm_fields['F'], SIZ,
-                                       "room_%s@%s", CC->room.QRname,
-                                       config.c_fqdn);
-                               for (i=0; msg->cm_fields['F'][i]; ++i) {
-                                       if (isspace(msg->cm_fields['F'][i])) {
-                                               msg->cm_fields['F'][i] = '_';
-                                       }
-                               }
-
-                               /*
-                                * Figure out how big a buffer we need to alloc
-                                */
-                               for (nptr = sc->participates;
-                                    nptr != NULL;
-                                    nptr = nptr->next)
-                               {
-                                       if (msg->cm_fields['R'] != NULL) {
-                                               free(msg->cm_fields['R']);
-                                       }
-                                       msg->cm_fields['R'] =
-                                               strdup(nptr->name);
-
-                                       valid = validate_recipients(nptr->name,
-                                                                   NULL, 0);
-
-                                       CtdlSubmitMsg(msg, valid, "", 0);
-                                       free_recipients(valid);
-                               }
-                       }
-                       CtdlFreeMessage(msg);
-               }
+       /* Prepend our node name to the Path field whenever
+        * sending a message to another IGnet node
+        */
+       if (msg->cm_fields['P'] == NULL)
+       {
+               msg->cm_fields['P'] = strdup("username");
        }
-
+       newpath_len = strlen(msg->cm_fields['P']) +
+               strlen(config.c_nodename) + 2;
+       newpath = malloc(newpath_len);
+       snprintf(newpath, newpath_len, "%s!%s",
+                config.c_nodename, msg->cm_fields['P']);
+       free(msg->cm_fields['P']);
+       msg->cm_fields['P'] = newpath;
+       
        /*
-        * Process IGnet push shares
+        * Determine if this message is set to be deleted
+        * after sending out on the network
         */
-       msg = CtdlFetchMessage(msgnum, 1);
-       if (msg != NULL) {
-               size_t newpath_len;
-
-               /* Prepend our node name to the Path field whenever
-                * sending a message to another IGnet node
-                */
-               if (msg->cm_fields['P'] == NULL) {
-                       msg->cm_fields['P'] = strdup("username");
+       if (msg->cm_fields['S'] != NULL) {
+               if (!strcasecmp(msg->cm_fields['S'], "CANCEL")) {
+                       *delete_after_send = 1;
                }
-               newpath_len = strlen(msg->cm_fields['P']) +
-                        strlen(config.c_nodename) + 2;
-               newpath = malloc(newpath_len);
-               snprintf(newpath, newpath_len, "%s!%s",
-                        config.c_nodename, msg->cm_fields['P']);
-               free(msg->cm_fields['P']);
-               msg->cm_fields['P'] = newpath;
+       }
 
-               /*
-                * Determine if this message is set to be deleted
-                * after sending out on the network
-                */
-               if (msg->cm_fields['S'] != NULL) {
-                       if (!strcasecmp(msg->cm_fields['S'], "CANCEL")) {
-                               delete_after_send = 1;
+       /* Now send it to every node */
+       for (mptr = sc->RNCfg->NetConfigs[ignet_push_share];
+            mptr != NULL;
+            mptr = mptr->next)
+       {
+               send = 1;
+               NewStrBufDupAppendFlush(&Buf, mptr->Value[0], NULL, 1);
+                       
+               /* Check for valid node name */
+               if (CtdlIsValidNode(NULL,
+                                   NULL,
+                                   Buf,
+                                   sc->working_ignetcfg,
+                                   sc->the_netmap) != 0)
+               {
+                       QN_syslog(LOG_ERR,
+                                 "Invalid node <%s>\n",
+                                 ChrPtr(mptr->Value[0]));
+                       
+                       send = 0;
+               }
+               
+               /* Check for split horizon */
+               QN_syslog(LOG_DEBUG, "Path is %s\n", msg->cm_fields['P']);
+               bang = num_tokens(msg->cm_fields['P'], '!');
+               if (bang > 1) {
+                       for (i=0; i<(bang-1); ++i) {
+                               extract_token(buf,
+                                             msg->cm_fields['P'],
+                                             i, '!',
+                                             sizeof buf);
+
+                               QN_syslog(LOG_DEBUG, "Compare <%s> to <%s>\n",
+                                         buf, ChrPtr(mptr->Value[0])) ;
+                               if (!strcasecmp(buf, ChrPtr(mptr->Value[0]))) {
+                                       send = 0;
+                                       break;
+                               }
                        }
+                       
+                       QN_syslog(LOG_INFO,
+                                 "%sSending to %s\n",
+                                 (send)?"":"Not ",
+                                 ChrPtr(mptr->Value[0]));
                }
-
-               /* Now send it to every node */
-               if (sc->ignet_push_shares != NULL)
-                 for (mptr = sc->ignet_push_shares; mptr != NULL;
-                   mptr = mptr->next) {
-
-                       send = 1;
-                       if (Buf == NULL)
-                               Buf = NewStrBufPlain(mptr->remote_nodename, -1);
-                       else
-                               StrBufPlain(Buf, mptr->remote_nodename, -1);
-                       /* Check for valid node name */
-                       if (is_valid_node(NULL,
-                                         NULL,
-                                         Buf,
-                                         sc->working_ignetcfg,
-                                         sc->the_netmap) != 0)
-                       {
-                               QN_syslog(LOG_ERR,
-                                         "Invalid node <%s>\n",
-                                         mptr->remote_nodename);
-
-                               send = 0;
+               
+               /* Send the message */
+               if (send == 1)
+               {
+                       /*
+                        * Force the message to appear in the correct
+                        * room on the far end by setting the C field
+                        * correctly
+                        */
+                       if (msg->cm_fields['C'] != NULL) {
+                               free(msg->cm_fields['C']);
                        }
-
-                       /* Check for split horizon */
-                       QN_syslog(LOG_DEBUG, "Path is %s\n", msg->cm_fields['P']);
-                       bang = num_tokens(msg->cm_fields['P'], '!');
-                       if (bang > 1) {
-                               for (i=0; i<(bang-1); ++i) {
-                                       extract_token(buf,
-                                                     msg->cm_fields['P'],
-                                                     i, '!',
-                                                     sizeof buf);
-                                       
-                                       QN_syslog(LOG_DEBUG, "Compare <%s> to <%s>\n",
-                                                 buf, mptr->remote_nodename) ;
-                                       if (!strcasecmp(buf, mptr->remote_nodename)) {
-                                               send = 0;
-                                               break;
-                                       }
-                               }
-
-                               QN_syslog(LOG_INFO,
-                                         "%sSending to %s\n",
-                                         (send)?"":"Not ",
-                                         mptr->remote_nodename);
+                       if (StrLength(mptr->Value[0]) > 0) {
+                               msg->cm_fields['C'] =
+                                       strdup(ChrPtr(mptr->Value[0]));
                        }
-
-                       /* Send the message */
-                       if (send == 1)
-                       {
-                               /*
-                                * Force the message to appear in the correct
-                                * room on the far end by setting the C field
-                                * correctly
-                                */
-                               if (msg->cm_fields['C'] != NULL) {
-                                       free(msg->cm_fields['C']);
-                               }
-                               if (!IsEmptyStr(mptr->remote_roomname)) {
-                                       msg->cm_fields['C'] =
-                                               strdup(mptr->remote_roomname);
+                       else {
+                               msg->cm_fields['C'] =
+                                       strdup(CC->room.QRname);
+                       }
+                       
+                       /* serialize it for transmission */
+                       serialize_message(&sermsg, msg);
+                       if (sermsg.len > 0) {
+                               
+                               /* write it to a spool file */
+                               snprintf(filename,
+                                        sizeof(filename),
+                                        "%s/%s@%lx%x",
+                                        ctdl_netout_dir,
+                                        ChrPtr(mptr->Value[0]),
+                                        time(NULL),
+                                        rand()
+                                       );
+                                       
+                               QN_syslog(LOG_DEBUG,
+                                         "Appending to %s\n",
+                                         filename);
+                               
+                               fp = fopen(filename, "ab");
+                               if (fp != NULL) {
+                                       fwrite(sermsg.ser,
+                                              sermsg.len, 1, fp);
+                                       fclose(fp);
                                }
                                else {
-                                       msg->cm_fields['C'] =
-                                               strdup(CC->room.QRname);
-                               }
-
-                               /* serialize it for transmission */
-                               serialize_message(&sermsg, msg);
-                               if (sermsg.len > 0) {
-
-                                       /* write it to a spool file */
-                                       snprintf(filename,
-                                                sizeof(filename),
-                                                "%s/%s@%lx%x",
-                                                ctdl_netout_dir,
-                                                mptr->remote_nodename,
-                                                time(NULL),
-                                                rand()
-                                       );
-
-                                       QN_syslog(LOG_DEBUG,
-                                                 "Appending to %s\n",
-                                                 filename);
-
-                                       fp = fopen(filename, "ab");
-                                       if (fp != NULL) {
-                                               fwrite(sermsg.ser,
-                                                       sermsg.len, 1, fp);
-                                               fclose(fp);
-                                       }
-                                       else {
-                                               QN_syslog(LOG_ERR,
-                                                         "%s: %s\n",
-                                                         filename,
-                                                         strerror(errno));
-                                       }
-
-                                       /* free the serialized version */
-                                       free(sermsg.ser);
+                                       QN_syslog(LOG_ERR,
+                                                 "%s: %s\n",
+                                                 filename,
+                                                 strerror(errno));
                                }
 
+                               /* free the serialized version */
+                               free(sermsg.ser);
                        }
                }
-               CtdlFreeMessage(msg);
        }
+       FreeStrBuf(&Buf);
+       CtdlFreeMessage(msg);
+}
+
+
+/*
+ * Spools out one message from the list.
+ */
+void network_spool_msg(long msgnum,
+                      void *userdata)
+{
+       struct CtdlMessage *msg = NULL;
+       long delete_after_send = 0;     /* Set to 1 to delete after spooling */
+       SpoolControl *sc;
+
+       sc = (SpoolControl *)userdata;
+
+       msg = CtdlFetchMessage(msgnum, 1);
+
+       network_process_list(sc, msg, &delete_after_send);
+       network_process_digest(sc, msg, &delete_after_send);
+       network_process_participate(sc, msg, &delete_after_send);
+       network_process_ignetpush(sc, msg, &delete_after_send);
+       
+       CtdlFreeMessage(msg);
 
        /* update lastsent */
        sc->lastsent = msgnum;
@@ -665,5 +703,4 @@ void network_spool_msg(long msgnum,
        if (delete_after_send) {
                CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, "");
        }
-       FreeStrBuf(&Buf);
 }
index f333e4bb180a1c5281a13ad8b39847c0f05abe03..c24f31bc55df207377bfb8fcc5bf18f494cc507c 100644 (file)
 #include "citadel_dirs.h"
 #include "threads.h"
 #include "context.h"
-#include "netconfig.h"
-#include "netspool.h"
-#include "netmail.h"
+
 #include "ctdl_module.h"
 
-int read_spoolcontrol_file(SpoolControl **scc, char *filename)
-{
-       FILE *fp;
-       char instr[SIZ];
-       char buf[SIZ];
-       char nodename[256];
-       char roomname[ROOMNAMELEN];
-       size_t miscsize = 0;
-       size_t linesize = 0;
-       int skipthisline = 0;
-       namelist *nptr = NULL;
-       maplist *mptr = NULL;
-       SpoolControl *sc;
-
-       fp = fopen(filename, "r");
-       if (fp == NULL) {
-               return 0;
-       }
-       sc = malloc(sizeof(SpoolControl));
-       memset(sc, 0, sizeof(SpoolControl));
-       *scc = sc;
+#include "netspool.h"
+#include "netmail.h"
 
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               buf[strlen(buf)-1] = 0;
 
-               extract_token(instr, buf, 0, '|', sizeof instr);
-               if (!strcasecmp(instr, strof(lastsent))) {
-                       sc->lastsent = extract_long(buf, 1);
-               }
-               else if (!strcasecmp(instr, strof(listrecp))) {
-                       nptr = (namelist *)
-                               malloc(sizeof(namelist));
-                       nptr->next = sc->listrecps;
-                       extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
-                       sc->listrecps = nptr;
-               }
-               else if (!strcasecmp(instr, strof(participate))) {
-                       nptr = (namelist *)
-                               malloc(sizeof(namelist));
-                       nptr->next = sc->participates;
-                       extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
-                       sc->participates = nptr;
-               }
-               else if (!strcasecmp(instr, strof(digestrecp))) {
-                       nptr = (namelist *)
-                               malloc(sizeof(namelist));
-                       nptr->next = sc->digestrecps;
-                       extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
-                       sc->digestrecps = nptr;
-               }
-               else if (!strcasecmp(instr, strof(ignet_push_share))) {
-                       extract_token(nodename, buf, 1, '|', sizeof nodename);
-                       extract_token(roomname, buf, 2, '|', sizeof roomname);
-                       mptr = (maplist *) malloc(sizeof(maplist));
-                       mptr->next = sc->ignet_push_shares;
-                       strcpy(mptr->remote_nodename, nodename);
-                       strcpy(mptr->remote_roomname, roomname);
-                       sc->ignet_push_shares = mptr;
-               }
-               else {
-                       /* Preserve 'other' lines ... *unless* they happen to
-                        * be subscribe/unsubscribe pendings with expired
-                        * timestamps.
-                        */
-                       skipthisline = 0;
-                       if (!strncasecmp(buf, strof(subpending)"|", 11)) {
-                               if (time(NULL) - extract_long(buf, 4) > EXP) {
-                                       skipthisline = 1;
-                               }
-                       }
-                       if (!strncasecmp(buf, strof(unsubpending)"|", 13)) {
-                               if (time(NULL) - extract_long(buf, 3) > EXP) {
-                                       skipthisline = 1;
-                               }
-                       }
+#ifndef DT_UNKNOWN
+#define DT_UNKNOWN     0
+#define DT_DIR         4
+#define DT_REG         8
+#define DT_LNK         10
 
-                       if (skipthisline == 0) {
-                               linesize = strlen(buf);
-                               sc->misc = realloc(sc->misc,
-                                       (miscsize + linesize + 2) );
-                               sprintf(&sc->misc[miscsize], "%s\n", buf);
-                               miscsize = miscsize + linesize + 1;
-                       }
-               }
+#define IFTODT(mode)   (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype)        ((dirtype) << 12)
+#endif
 
 
-       }
-       fclose(fp);
-       return 1;
+void ParseLastSent(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *OneRNCFG)
+{
+       RoomNetCfgLine *nptr;
+       nptr = (RoomNetCfgLine *)
+               malloc(sizeof(RoomNetCfgLine));
+       memset(nptr, 0, sizeof(RoomNetCfgLine));
+       OneRNCFG->lastsent = extract_long(LinePos, 0);
+       OneRNCFG->NetConfigs[ThisOne->C] = nptr;
 }
 
-void free_spoolcontrol_struct(SpoolControl **scc)
+void ParseRoomAlias(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *rncfg)
 {
-       SpoolControl *sc;
-       namelist *nptr = NULL;
-       maplist *mptr = NULL;
-
-       sc = *scc;
-       while (sc->listrecps != NULL) {
-               nptr = sc->listrecps->next;
-               free(sc->listrecps);
-               sc->listrecps = nptr;
-       }
-       /* Do the same for digestrecps */
-       while (sc->digestrecps != NULL) {
-               nptr = sc->digestrecps->next;
-               free(sc->digestrecps);
-               sc->digestrecps = nptr;
-       }
-       /* Do the same for participates */
-       while (sc->participates != NULL) {
-               nptr = sc->participates->next;
-               free(sc->participates);
-               sc->participates = nptr;
-       }
-       while (sc->ignet_push_shares != NULL) {
-               mptr = sc->ignet_push_shares->next;
-               free(sc->ignet_push_shares);
-               sc->ignet_push_shares = mptr;
-       }
-       free(sc->misc);
-       free(sc);
-       *scc=NULL;
+/*
+       if (rncfg->RNCfg->sender != NULL)
+               continue; / * just one alowed... * /
+       extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
+       rncfg->RNCfg->sender = nptr;
+*/
 }
 
-int writenfree_spoolcontrol_file(SpoolControl **scc, char *filename)
+void ParseSubPendingLine(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *OneRNCFG)
 {
-       char tempfilename[PATH_MAX];
-       int TmpFD;
-       SpoolControl *sc;
-       namelist *nptr = NULL;
-       maplist *mptr = NULL;
-       long len;
-       time_t unixtime;
-       struct timeval tv;
-       long reltid; /* if we don't have SYS_gettid, use "random" value */
-       StrBuf *Cfg;
-       int rc;
-
-       len = strlen(filename);
-       memcpy(tempfilename, filename, len + 1);
-
-
-#if defined(HAVE_SYSCALL_H) && defined (SYS_gettid)
-       reltid = syscall(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);
-       sc = *scc;
-       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));
-               free_spoolcontrol_struct(scc);
-               unlink(tempfilename);
-       }
-       else {
-               fchown(TmpFD, config.c_ctdluid, 0);
-               StrBufAppendPrintf(Cfg, "lastsent|%ld\n", sc->lastsent);
-               
-               /* Write out the listrecps while freeing from memory at the
-                * same time.  Am I clever or what?  :)
-                */
-               while (sc->listrecps != NULL) {
-                   StrBufAppendPrintf(Cfg, "listrecp|%s\n", sc->listrecps->name);
-                       nptr = sc->listrecps->next;
-                       free(sc->listrecps);
-                       sc->listrecps = nptr;
-               }
-               /* Do the same for digestrecps */
-               while (sc->digestrecps != NULL) {
-                       StrBufAppendPrintf(Cfg, "digestrecp|%s\n", sc->digestrecps->name);
-                       nptr = sc->digestrecps->next;
-                       free(sc->digestrecps);
-                       sc->digestrecps = nptr;
-               }
-               /* Do the same for participates */
-               while (sc->participates != NULL) {
-                       StrBufAppendPrintf(Cfg, "participate|%s\n", sc->participates->name);
-                       nptr = sc->participates->next;
-                       free(sc->participates);
-                       sc->participates = nptr;
-               }
-               while (sc->ignet_push_shares != NULL) {
-                       StrBufAppendPrintf(Cfg, "ignet_push_share|%s", sc->ignet_push_shares->remote_nodename);
-                       if (!IsEmptyStr(sc->ignet_push_shares->remote_roomname)) {
-                               StrBufAppendPrintf(Cfg, "|%s", sc->ignet_push_shares->remote_roomname);
-                       }
-                       StrBufAppendPrintf(Cfg, "\n");
-                       mptr = sc->ignet_push_shares->next;
-                       free(sc->ignet_push_shares);
-                       sc->ignet_push_shares = mptr;
-               }
-               if (sc->misc != NULL) {
-                       StrBufAppendBufPlain(Cfg, sc->misc, -1, 0);
-               }
-               free(sc->misc);
+       if (time(NULL) - extract_long(LinePos, 3) > EXP) 
+               return; /* expired subscription... */
 
-               rc = write(TmpFD, ChrPtr(Cfg), StrLength(Cfg));
-               if ((rc >=0 ) && (rc == StrLength(Cfg))) 
-               {
-                       close(TmpFD);
-                       rename(tempfilename, filename);
-               }
-               else {
-                       syslog(LOG_EMERG, 
-                                     "unable to write %s; [%s]; not enough space on the disk?\n", 
-                                     tempfilename, 
-                                     strerror(errno));
-                       close(TmpFD);
-                       unlink(tempfilename);
-               }
-               FreeStrBuf(&Cfg);
-               free(sc);
-               *scc=NULL;
-       }
-       return 1;
+       ParseGeneric(ThisOne, Line, LinePos, OneRNCFG);
 }
-int is_recipient(SpoolControl *sc, const char *Name)
+void ParseUnSubPendingLine(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *OneRNCFG)
 {
-       namelist *nptr;
-       size_t len;
-
-       len = strlen(Name);
-       nptr = sc->listrecps;
-       while (nptr != NULL) {
-               if (strncmp(Name, nptr->name, len)==0)
-                       return 1;
-               nptr = nptr->next;
-       }
-       /* Do the same for digestrecps */
-       nptr = sc->digestrecps;
-       while (nptr != NULL) {
-               if (strncmp(Name, nptr->name, len)==0)
-                       return 1;
-               nptr = nptr->next;
-       }
-       /* Do the same for participates */
-       nptr = sc->participates;
-       while (nptr != NULL) {
-               if (strncmp(Name, nptr->name, len)==0)
-                       return 1;
-               nptr = nptr->next;
-       }
-       return 0;
+       if (time(NULL) - extract_long(LinePos, 2) > EXP)
+               return; /* expired subscription... */
+
+       ParseGeneric(ThisOne, Line, LinePos, OneRNCFG);
 }
 
 
+void SerializeLastSent(const CfgLineType *ThisOne, StrBuf *OutputBuffer, OneRoomNetCfg *RNCfg, RoomNetCfgLine *data)
+{
+       StrBufAppendBufPlain(OutputBuffer, CKEY(ThisOne->Str), 0);
+       StrBufAppendPrintf(OutputBuffer, "|%ld\n", RNCfg->lastsent);
+}
+
+void DeleteLastSent(const CfgLineType *ThisOne, RoomNetCfgLine **data)
+{
+       free(*data);
+       *data = NULL;
+}
+
+
+
+
 /*
  * Batch up and send all outbound traffic from the current room
  */
@@ -341,66 +152,88 @@ void network_spoolout_room(RoomProcList *room_to_spool,
                           HashList *working_ignetcfg,
                           HashList *the_netmap)
 {
+       struct CitContext *CCC = CC;
        char buf[SIZ];
-       char filename[PATH_MAX];
-       SpoolControl *sc;
+       SpoolControl sc;
        int i;
 
+       memset(&sc, 0, sizeof(SpoolControl));
+       sc.RNCfg = room_to_spool->OneRNCfg;
+       sc.lastsent = room_to_spool->OneRNCfg->lastsent;
+       sc.working_ignetcfg = working_ignetcfg;
+       sc.the_netmap = the_netmap;
+
+       if ((sc.RNCfg->NetConfigs[listrecp] == NULL) && 
+           (sc.RNCfg->NetConfigs[digestrecp] == NULL) && 
+           (sc.RNCfg->NetConfigs[participate] == NULL) && 
+           (sc.RNCfg->NetConfigs[ignet_push_share] == NULL))
+       {
+               /* nothing to do for this room... */
+               return;
+       }
+
        /*
         * If the room doesn't exist, don't try to perform its networking tasks.
         * Normally this should never happen, but once in a while maybe a room gets
         * queued for networking and then deleted before it can happen.
         */
-       if (CtdlGetRoom(&CC->room, room_to_spool->name) != 0) {
+       if (CtdlGetRoom(&CCC->room, room_to_spool->name) != 0) {
                syslog(LOG_CRIT, "ERROR: cannot load <%s>\n", room_to_spool->name);
                return;
        }
 
-       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
-       begin_critical_section(S_NETCONFIGS);
-
-       /* Only do net processing for rooms that have netconfigs */
-       if (!read_spoolcontrol_file(&sc, filename))
-       {
-               end_critical_section(S_NETCONFIGS);
-               return;
-       }
-       syslog(LOG_INFO, "Networking started for <%s>\n", CC->room.QRname);
-
-       sc->working_ignetcfg = working_ignetcfg;
-       sc->the_netmap = the_netmap;
+       
+       syslog(LOG_INFO, "Networking started for <%s>\n", CCC->room.QRname);
 
        /* If there are digest recipients, we have to build a digest */
-       if (sc->digestrecps != NULL) {
-               sc->digestfp = tmpfile();
-               fprintf(sc->digestfp, "Content-type: text/plain\n\n");
+       if (sc.RNCfg->NetConfigs[digestrecp] != NULL) {
+               sc.digestfp = tmpfile();
+               fprintf(sc.digestfp, "Content-type: text/plain\n\n");
        }
 
        /* Do something useful */
-       CtdlForEachMessage(MSGS_GT, sc->lastsent, NULL, NULL, NULL,
-               network_spool_msg, sc);
+       CtdlForEachMessage(MSGS_GT, sc.lastsent, NULL, NULL, NULL,
+               network_spool_msg, &sc);
 
        /* If we wrote a digest, deliver it and then close it */
-       snprintf(buf, sizeof buf, "room_%s@%s",
-               CC->room.QRname, config.c_fqdn);
+       if (StrLength(sc.RNCfg->Sender) > 0)
+       {
+               long len;
+               len = StrLength(sc.RNCfg->Sender);
+               if (len + 1 > sizeof(buf))
+                       len = sizeof(buf) - 1;
+               memcpy(buf, ChrPtr(sc.RNCfg->Sender), len);
+               buf[len] = '\0';
+       }
+       else
+       {
+               snprintf(buf, sizeof buf, "room_%s@%s",
+                        CCC->room.QRname, config.c_fqdn);
+       }
+
        for (i=0; buf[i]; ++i) {
                buf[i] = tolower(buf[i]);
                if (isspace(buf[i])) buf[i] = '_';
        }
-       if (sc->digestfp != NULL) {
-               fprintf(sc->digestfp,   " -----------------------------------"
-                                       "------------------------------------"
-                                       "-------\n"
-                                       "You are subscribed to the '%s' "
-                                       "list.\n"
-                                       "To post to the list: %s\n",
-                                       CC->room.QRname, buf
+       if (sc.digestfp != NULL) {
+               fprintf(sc.digestfp,
+                       " -----------------------------------"
+                       "------------------------------------"
+                       "-------\n"
+                       "You are subscribed to the '%s' "
+                       "list.\n"
+                       "To post to the list: %s\n",
+                       CCC->room.QRname, buf
                );
-               network_deliver_digest(sc);     /* deliver and close */
+               network_deliver_digest(&sc);    /* deliver and close */
        }
 
        /* Now rewrite the config file */
-       writenfree_spoolcontrol_file(&sc, filename);
+       if (sc.lastsent != room_to_spool->OneRNCfg->lastsent)
+       {
+               room_to_spool->OneRNCfg->lastsent = sc.lastsent;
+               room_to_spool->OneRNCfg->changed = 1;
+       }
        end_critical_section(S_NETCONFIGS);
 }
 
@@ -458,11 +291,11 @@ void network_process_buffer(char *buffer, long size, HashList *working_ignetcfg,
 
                        /* route the message */
                        Buf = NewStrBufPlain(msg->cm_fields['D'], -1);
-                       if (is_valid_node(&nexthop, 
-                                         NULL, 
-                                         Buf, 
-                                         working_ignetcfg, 
-                                         the_netmap) == 0) 
+                       if (CtdlIsValidNode(&nexthop, 
+                                           NULL, 
+                                           Buf, 
+                                           working_ignetcfg, 
+                                           the_netmap) == 0) 
                        {
                                /* prepend our node to the path */
                                if (msg->cm_fields['P'] != NULL) {
@@ -534,10 +367,10 @@ void network_process_buffer(char *buffer, long size, HashList *working_ignetcfg,
 
        /* Learn network topology from the path */
        if ((msg->cm_fields['N'] != NULL) && (msg->cm_fields['P'] != NULL)) {
-               network_learn_topology(msg->cm_fields['N'], 
-                                      msg->cm_fields['P'], 
-                                      the_netmap, 
-                                      netmap_changed);
+               NetworkLearnTopology(msg->cm_fields['N'], 
+                                    msg->cm_fields['P'], 
+                                    the_netmap, 
+                                    netmap_changed);
        }
 
        /* Is the sending node giving us a very persuasive suggestion about
@@ -690,9 +523,12 @@ void network_do_spoolin(HashList *working_ignetcfg, HashList *the_netmap, int *n
        struct CitContext *CCC = CC;
        DIR *dp;
        struct dirent *d;
+       struct dirent *filedir_entry;
        struct stat statbuf;
        char filename[PATH_MAX];
        static time_t last_spoolin_mtime = 0L;
+       int d_type = 0;
+        int d_namelen;
 
        /*
         * Check the spoolin directory's modification time.  If it hasn't
@@ -712,8 +548,60 @@ void network_do_spoolin(HashList *working_ignetcfg, HashList *the_netmap, int *n
        dp = opendir(ctdl_netin_dir);
        if (dp == NULL) return;
 
-       while (d = readdir(dp), d != NULL) {
-               if ((strcmp(d->d_name, ".")) && (strcmp(d->d_name, ".."))) {
+       d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
+       if (d == NULL) {
+               closedir(dp);
+               return;
+       }
+
+       while ((readdir_r(dp, d, &filedir_entry) == 0) &&
+              (filedir_entry != NULL))
+       {
+#ifdef _DIRENT_HAVE_D_NAMLEN
+               d_namelen = filedir_entry->d_namelen;
+
+#else
+               d_namelen = strlen(filedir_entry->d_name);
+#endif
+
+#ifdef _DIRENT_HAVE_D_TYPE
+               d_type = filedir_entry->d_type;
+#else
+               d_type = DT_UNKNOWN;
+#endif
+               if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
+                       continue; /* Ignore backup files... */
+
+               if ((d_namelen == 1) && 
+                   (filedir_entry->d_name[0] == '.'))
+                       continue;
+
+               if ((d_namelen == 2) && 
+                   (filedir_entry->d_name[0] == '.') &&
+                   (filedir_entry->d_name[1] == '.'))
+                       continue;
+
+               if (d_type == DT_UNKNOWN) {
+                       struct stat s;
+                       char path[PATH_MAX];
+
+                       snprintf(path,
+                                PATH_MAX,
+                                "%s/%s", 
+                                ctdl_netin_dir,
+                                filedir_entry->d_name);
+
+                       if (lstat(path, &s) == 0) {
+                               d_type = IFTODT(s.st_mode);
+                       }
+               }
+
+               switch (d_type)
+               {
+               case DT_DIR:
+                       break;
+               case DT_LNK: /* TODO: check whether its a file or a directory */
+               case DT_REG:
                        snprintf(filename, 
                                sizeof filename,
                                "%s/%s",
@@ -728,6 +616,7 @@ void network_do_spoolin(HashList *working_ignetcfg, HashList *the_netmap, int *n
        }
 
        closedir(dp);
+       free(d);
 }
 
 /*
@@ -751,6 +640,8 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
        int i;
        struct stat statbuf;
        int nFailed = 0;
+       int d_type = 0;
+
 
        /* Step 1: consolidate files in the outbound queue into one file per neighbor node */
        d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
@@ -770,21 +661,21 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
        while ((readdir_r(dp, d, &filedir_entry) == 0) &&
               (filedir_entry != NULL))
        {
-#ifdef _DIRENT_HAVE_D_NAMELEN
+#ifdef _DIRENT_HAVE_D_NAMLEN
                d_namelen = filedir_entry->d_namelen;
-#else
-
-#ifndef DT_UNKNOWN
-#define DT_UNKNOWN     0
-#define DT_DIR         4
-#define DT_REG         8
-#define DT_LNK         10
 
-#define IFTODT(mode)   (((mode) & 0170000) >> 12)
-#define DTTOIF(dirtype)        ((dirtype) << 12)
-#endif
+#else
                d_namelen = strlen(filedir_entry->d_name);
 #endif
+
+#ifdef _DIRENT_HAVE_D_TYPE
+               d_type = filedir_entry->d_type;
+#else
+               d_type = DT_UNKNOWN;
+#endif
+               if (d_type == DT_DIR)
+                       continue;
+
                if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
                        continue; /* Ignore backup files... */
 
@@ -818,7 +709,7 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
                         ChrPtr(NextHop));
 
                QN_syslog(LOG_DEBUG, "Consolidate %s to %s\n", filename, ChrPtr(NextHop));
-               if (network_talking_to(SKEY(NextHop), NTT_CHECK)) {
+               if (CtdlNetworkTalkingTo(SKEY(NextHop), NTT_CHECK)) {
                        nFailed++;
                        QN_syslog(LOG_DEBUG,
                                  "Currently online with %s - skipping for now\n",
@@ -830,7 +721,7 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
                        size_t fsize;
                        int infd, outfd;
                        const char *err = NULL;
-                       network_talking_to(SKEY(NextHop), NTT_ADD);
+                       CtdlNetworkTalkingTo(SKEY(NextHop), NTT_ADD);
 
                        infd = open(filename, O_RDONLY);
                        if (infd == -1) {
@@ -839,7 +730,7 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
                                          "failed to open %s for reading due to %s; skipping.\n",
                                          filename, strerror(errno)
                                        );
-                               network_talking_to(SKEY(NextHop), NTT_REMOVE);
+                               CtdlNetworkTalkingTo(SKEY(NextHop), NTT_REMOVE);
                                continue;                               
                        }
                        
@@ -859,7 +750,7 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
                                          spooloutfilename, strerror(errno)
                                        );
                                close(infd);
-                               network_talking_to(SKEY(NextHop), NTT_REMOVE);
+                               CtdlNetworkTalkingTo(SKEY(NextHop), NTT_REMOVE);
                                continue;
                        }
 
@@ -893,7 +784,7 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
                        FDIOBufferDelete(&FDIO);
                        close(infd);
                        close(outfd);
-                       network_talking_to(SKEY(NextHop), NTT_REMOVE);
+                       CtdlNetworkTalkingTo(SKEY(NextHop), NTT_REMOVE);
                }
        }
        closedir(dp);
@@ -919,22 +810,21 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
        while ((readdir_r(dp, d, &filedir_entry) == 0) &&
               (filedir_entry != NULL))
        {
-#ifdef _DIRENT_HAVE_D_NAMELEN
+#ifdef _DIRENT_HAVE_D_NAMLEN
                d_namelen = filedir_entry->d_namelen;
-               d_type = filedir_entry->d_type;
-#else
 
-#ifndef DT_UNKNOWN
-#define DT_UNKNOWN     0
-#define DT_DIR         4
-#define DT_REG         8
-#define DT_LNK         10
-
-#define IFTODT(mode)   (((mode) & 0170000) >> 12)
-#define DTTOIF(dirtype)        ((dirtype) << 12)
-#endif
+#else
                d_namelen = strlen(filedir_entry->d_name);
 #endif
+
+#ifdef _DIRENT_HAVE_D_TYPE
+               d_type = filedir_entry->d_type;
+#else
+               d_type = DT_UNKNOWN;
+#endif
+               if (d_type == DT_DIR)
+                       continue;
+
                if ((d_namelen == 1) && 
                    (filedir_entry->d_name[0] == '.'))
                        continue;
@@ -959,11 +849,11 @@ void network_consolidate_spoolout(HashList *working_ignetcfg, HashList *the_netm
                        filedir_entry->d_name
                );
 
-               i = is_valid_node(&nexthop,
-                                 NULL,
-                                 NextHop,
-                                 working_ignetcfg,
-                                 the_netmap);
+               i = CtdlIsValidNode(&nexthop,
+                                   NULL,
+                                   NextHop,
+                                   working_ignetcfg,
+                                   the_netmap);
        
                if ( (i != 0) || (StrLength(nexthop) > 0) ) {
                        unlink(filename);
@@ -1006,6 +896,15 @@ CTDL_MODULE_INIT(network_spool)
 {
        if (!threading)
        {
+               CtdlREGISTERRoomCfgType(subpending,       ParseSubPendingLine,   0, 5, SerializeGeneric,  DeleteGenericCfgLine); /// todo: move this to mailinglist manager
+               CtdlREGISTERRoomCfgType(unsubpending,     ParseUnSubPendingLine, 0, 4, SerializeGeneric,  DeleteGenericCfgLine); /// todo: move this to mailinglist manager
+               CtdlREGISTERRoomCfgType(lastsent,         ParseLastSent,         1, 1, SerializeLastSent, DeleteLastSent);
+               CtdlREGISTERRoomCfgType(ignet_push_share, ParseGeneric,          0, 2, SerializeGeneric,  DeleteGenericCfgLine); // [remotenode|remoteroomname (optional)]// todo: move this to the ignet client
+               CtdlREGISTERRoomCfgType(listrecp,         ParseGeneric,          0, 1, SerializeGeneric,  DeleteGenericCfgLine);
+               CtdlREGISTERRoomCfgType(digestrecp,       ParseGeneric,          0, 1, SerializeGeneric,  DeleteGenericCfgLine);
+               CtdlREGISTERRoomCfgType(participate,      ParseGeneric,          0, 1, SerializeGeneric,  DeleteGenericCfgLine);
+               CtdlREGISTERRoomCfgType(roommailalias,    ParseRoomAlias,        0, 1, SerializeGeneric,  DeleteGenericCfgLine);
+
                create_spool_dirs();
 //////todo             CtdlRegisterCleanupHook(destroy_network_queue_room);
        }
index 578a8e17e4e90fa35e843820d761ed152e1a56a3..ebc00e04c1dc6aab4d4f05ba5e7eeeb2474ff0ee 100644 (file)
 #include "citadel_dirs.h"
 #include "threads.h"
 #include "context.h"
-#include "netconfig.h"
+#include "ctdl_module.h"
 #include "netspool.h"
 #include "netmail.h"
-#include "ctdl_module.h"
 
 int NetQDebugEnabled = 0;
 struct CitContext networker_spool_CC;
@@ -89,7 +88,6 @@ extern uint32_t hashlittle( const void *key, size_t length, uint32_t initval);
 
 typedef struct __roomlists {
        RoomProcList *rplist;
-       HashList *RoomsInterestedIn;
 }roomlists;
 /*
  * When we do network processing, it's accomplished in two passes; one to
@@ -98,39 +96,6 @@ typedef struct __roomlists {
  */
 struct RoomProcList *rplist = NULL;
 
-int GetNetworkedRoomNumbers(const char *DirName, HashList *DirList)
-{
-       DIR *filedir = NULL;
-       struct dirent *d;
-       struct dirent *filedir_entry;
-       long RoomNR;
-       long Count = 0;
-               
-       filedir = opendir (DirName);
-       if (filedir == NULL) {
-               return 0;
-       }
-
-       d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
-       if (d == NULL) {
-               return 0;
-       }
-
-       while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
-              (filedir_entry != NULL))
-       {
-               RoomNR = atol(filedir_entry->d_name);
-               if (RoomNR != 0) {
-                       Count++;
-                       Put(DirList, LKEY(RoomNR), &Count, reference_free_handler);
-               }
-       }
-       free(d);
-       closedir(filedir);
-       return Count;
-}
-
-
 
 
 /*
@@ -185,13 +150,6 @@ int network_usetable(struct CtdlMessage *msg)
 
 
 
-
-
-
-
-
-
-
 /*
  * Send the *entire* contents of the current room to one specific network node,
  * ignoring anything we know about which messages have already undergone
@@ -200,68 +158,48 @@ int network_usetable(struct CtdlMessage *msg)
 int network_sync_to(char *target_node, long len)
 {
        struct CitContext *CCC = CC;
+       OneRoomNetCfg OneRNCFG;
+       OneRoomNetCfg *pRNCFG;
+       const RoomNetCfgLine *pCfgLine;
        SpoolControl sc;
        int num_spooled = 0;
-       int found_node = 0;
-       char buf[256];
-       char sc_type[256];
-       char sc_node[256];
-       char sc_room[256];
-       char filename[PATH_MAX];
-       FILE *fp;
 
        /* Grab the configuration line we're looking for */
-       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
        begin_critical_section(S_NETCONFIGS);
-       fp = fopen(filename, "r");
-       if (fp == NULL) {
-               end_critical_section(S_NETCONFIGS);
-               return(-1);
-       }
-       while (fgets(buf, sizeof buf, fp) != NULL)
+       pRNCFG = CtdlGetNetCfgForRoom(CCC->room.QRnumber);
+       if ((pRNCFG == NULL) ||
+           (pRNCFG->NetConfigs[ignet_push_share] == NULL))
        {
-               buf[strlen(buf)-1] = 0;
-
-               extract_token(sc_type, buf, 0, '|', sizeof sc_type);
-               if (strcasecmp(sc_type, "ignet_push_share"))
-                       continue;
+               return -1;
+       }
 
-               extract_token(sc_node, buf, 1, '|', sizeof sc_node);
-               if (strcasecmp(sc_node, target_node))
-                       continue;
+       pCfgLine = pRNCFG->NetConfigs[ignet_push_share];
+       while (pCfgLine != NULL)
+       {
+               if (strcmp(ChrPtr(pCfgLine->Value[0]), target_node))
+                       break;
+               pCfgLine = pCfgLine->next;
+       }
+       if (pCfgLine == NULL)
+       {
+               return -1;
+       }
+       memset(&sc, 0, sizeof(SpoolControl));
+       memset(&OneRNCFG, 0, sizeof(OneRoomNetCfg));
+       sc.RNCfg = &OneRNCFG;
+       sc.RNCfg->NetConfigs[ignet_push_share] = DuplicateOneGenericCfgLine(pCfgLine);
 
-               extract_token(sc_room, buf, 2, '|', sizeof sc_room);
-               found_node = 1;
-                       
-               /* Concise syntax because we don't need a full linked-list */
-               memset(&sc, 0, sizeof(SpoolControl));
-               sc.ignet_push_shares = (maplist *)
-                       malloc(sizeof(maplist));
-               sc.ignet_push_shares->next = NULL;
-               safestrncpy(sc.ignet_push_shares->remote_nodename,
-                           sc_node,
-                           sizeof sc.ignet_push_shares->remote_nodename);
-               safestrncpy(sc.ignet_push_shares->remote_roomname,
-                           sc_room,
-                           sizeof sc.ignet_push_shares->remote_roomname);
-       }
-       fclose(fp);
        end_critical_section(S_NETCONFIGS);
 
-       if (!found_node) {
-               free(sc.ignet_push_shares);
-               return(-1);
-       }
-
-       sc.working_ignetcfg = load_ignetcfg();
-       sc.the_netmap = read_network_map();
+       sc.working_ignetcfg = CtdlLoadIgNetCfg();
+       sc.the_netmap = CtdlReadNetworkMap();
 
        /* Send ALL messages */
        num_spooled = CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
                network_spool_msg, &sc);
 
        /* Concise cleanup because we know there's only one node in the sc */
-       free(sc.ignet_push_shares);
+       DeleteGenericCfgLine(NULL/*TODO*/, &sc.RNCfg->NetConfigs[ignet_push_share]);
 
        DeleteHash(&sc.working_ignetcfg);
        DeleteHash(&sc.the_netmap);
@@ -293,23 +231,13 @@ void cmd_nsyn(char *argbuf) {
        }
 }
 
-
-
-/*
- * Batch up and send all outbound traffic from the current room
- */
-void network_queue_interesting_rooms(struct ctdlroom *qrbuf, void *data) {
+RoomProcList *CreateRoomProcListEntry(struct ctdlroom *qrbuf, OneRoomNetCfg *OneRNCFG)
+{
        int i;
        struct RoomProcList *ptr;
-       long QRNum = qrbuf->QRnumber;
-       void *v;
-       roomlists *RP = (roomlists*) data;
-
-       if (!GetHash(RP->RoomsInterestedIn, LKEY(QRNum), &v))
-               return;
 
        ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
-       if (ptr == NULL) return;
+       if (ptr == NULL) return NULL;
 
        ptr->namelen = strlen(qrbuf->QRname);
        if (ptr->namelen > ROOMNAMELEN)
@@ -326,6 +254,20 @@ void network_queue_interesting_rooms(struct ctdlroom *qrbuf, void *data) {
 
        ptr->lcname[ptr->namelen] = '\0';
        ptr->key = hashlittle(ptr->lcname, ptr->namelen, 9872345);
+       ptr->OneRNCfg = OneRNCFG;
+       return ptr;
+}
+
+/*
+ * Batch up and send all outbound traffic from the current room
+ */
+void network_queue_interesting_rooms(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCfg)
+{
+       struct RoomProcList *ptr;
+       roomlists *RP = (roomlists*) data;
+
+       ptr = CreateRoomProcListEntry(qrbuf, OneRNCfg);
+
        ptr->next = RP->rplist;
        RP->rplist = ptr;
 }
@@ -333,34 +275,27 @@ void network_queue_interesting_rooms(struct ctdlroom *qrbuf, void *data) {
 /*
  * Batch up and send all outbound traffic from the current room
  */
-void network_queue_room(struct ctdlroom *qrbuf, void *data) {
-       int i;
+int network_room_handler (struct ctdlroom *qrbuf)
+{
        struct RoomProcList *ptr;
+       OneRoomNetCfg* RNCfg;
 
        if (qrbuf->QRdefaultview == VIEW_QUEUE)
-               return;
-       ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
-       if (ptr == NULL) return;
-
-       ptr->namelen = strlen(qrbuf->QRname);
-       if (ptr->namelen > ROOMNAMELEN)
-               ptr->namelen = ROOMNAMELEN - 1;
+               return 1;
 
-       memcpy (ptr->name, qrbuf->QRname, ptr->namelen);
-       ptr->name[ptr->namelen] = '\0';
-       ptr->QRNum = qrbuf->QRnumber;
+       RNCfg = CtdlGetNetCfgForRoom(qrbuf->QRnumber);
+       if (RNCfg == NULL)
+               return 1;
 
-       for (i = 0; i < ptr->namelen; i++)
-       {
-               ptr->lcname[i] = tolower(ptr->name[i]);
-       }
-       ptr->lcname[ptr->namelen] = '\0';
-       ptr->key = hashlittle(ptr->lcname, ptr->namelen, 9872345);
+       ptr = CreateRoomProcListEntry(qrbuf, RNCfg);
+       if (ptr == NULL)
+               return 1;
 
        begin_critical_section(S_RPLIST);
        ptr->next = rplist;
        rplist = ptr;
        end_critical_section(S_RPLIST);
+       return 1;
 }
 
 void destroy_network_queue_room(RoomProcList *rplist)
@@ -506,7 +441,6 @@ void network_bounce(struct CtdlMessage *msg, char *reason)
 void network_do_queue(void)
 {
        struct CitContext *CCC = CC;
-       static int doing_queue = 0;
        static time_t last_run = 0L;
        int full_processing = 1;
        HashList *working_ignetcfg;
@@ -525,49 +459,33 @@ void network_do_queue(void)
                );
        }
 
-       /*
-        * This is a simple concurrency check to make sure only one queue run
-        * is done at a time.  We could do this with a mutex, but since we
-        * don't really require extremely fine granularity here, we'll do it
-        * with a static variable instead.
-        */
-       if (doing_queue) {
-               return;
-       }
-       doing_queue = 1;
-
        become_session(&networker_spool_CC);
        begin_critical_section(S_RPLIST);
        RL.rplist = rplist;
        rplist = NULL;
        end_critical_section(S_RPLIST);
-
-       RL.RoomsInterestedIn = NewHash(1, lFlathash);
-       if (full_processing &&
-           (GetNetworkedRoomNumbers(ctdl_netcfg_dir, RL.RoomsInterestedIn)==0))
-       {
-               doing_queue = 0;
-               DeleteHash(&RL.RoomsInterestedIn);
-               if (RL.rplist == NULL)
-                       return;
-       }
+///TODO hm, check whether we have a config at all here?
        /* Load the IGnet Configuration into memory */
-       working_ignetcfg = load_ignetcfg();
+       working_ignetcfg = CtdlLoadIgNetCfg();
 
        /*
         * Load the network map and filter list into memory.
         */
        if (!server_shutting_down)
-               the_netmap = read_network_map();
+               the_netmap = CtdlReadNetworkMap();
+#if 0
+       /* filterlist isn't supported anymore
        if (!server_shutting_down)
                load_network_filter_list();
+       */
+#endif
 
        /* 
         * Go ahead and run the queue
         */
        if (full_processing && !server_shutting_down) {
                QNM_syslog(LOG_DEBUG, "network: loading outbound queue");
-               CtdlForEachRoom(network_queue_interesting_rooms, &RL);
+               CtdlForEachNetCfgRoom(network_queue_interesting_rooms, &RL, maxRoomNetCfg);
        }
 
        if ((RL.rplist != NULL) && (!server_shutting_down)) {
@@ -577,7 +495,7 @@ void network_do_queue(void)
                while (ptr != NULL && !server_shutting_down) {
                        
                        cmp = ptr->next;
-
+                       /* filter duplicates from the list... */
                        while (cmp != NULL) {
                                if ((cmp->namelen > 0) &&
                                    (cmp->key == ptr->key) &&
@@ -610,8 +528,10 @@ void network_do_queue(void)
 
        /* Save the network map back to disk */
        if (netmap_changed) {
-               StrBuf *MapStr = SerializeNetworkMap(the_netmap);
-               CtdlPutSysConfig(IGNETMAP, SmashStrBuf(&MapStr));
+               StrBuf *MapStr = CtdlSerializeNetworkMap(the_netmap);
+               char *pMapStr = SmashStrBuf(&MapStr);
+               CtdlPutSysConfig(IGNETMAP, pMapStr);
+               free(pMapStr);
        }
 
        /* combine singe message files into one spool entry per remote node. */
@@ -628,74 +548,15 @@ void network_do_queue(void)
        if (full_processing) {
                last_run = time(NULL);
        }
-       DeleteHash(&RL.RoomsInterestedIn);
        destroy_network_queue_room(RL.rplist);
-       doing_queue = 0;
-}
-
-
-
+       SaveChangedConfigs();
 
-int network_room_handler (struct ctdlroom *room)
-{
-       network_queue_room(room, NULL);
-       return 0;
 }
 
-int NTTDebugEnabled = 0;
-
-/*
- * network_talking_to()  --  concurrency checker
- */
-static HashList *nttlist = NULL;
-int network_talking_to(const char *nodename, long len, int operation) {
-
-       int retval = 0;
-       HashPos *Pos = NULL;
-       void *vdata;
-
-       begin_critical_section(S_NTTLIST);
 
-       switch(operation) {
 
-               case NTT_ADD:
-                       if (nttlist == NULL) 
-                               nttlist = NewHash(1, NULL);
-                       Put(nttlist, nodename, len, NewStrBufPlain(nodename, len), HFreeStrBuf);
-                       if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: added <%s>\n", nodename);
-                       break;
-               case NTT_REMOVE:
-                       if ((nttlist == NULL) ||
-                           (GetCount(nttlist) == 0))
-                               break;
-                       Pos = GetNewHashPos(nttlist, 1);
-                       if (GetHashPosFromKey (nttlist, nodename, len, Pos))
-                               DeleteEntryFromHash(nttlist, Pos);
-                       DeleteHashPos(&Pos);
-                       if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: removed <%s>\n", nodename);
-
-                       break;
-
-               case NTT_CHECK:
-                       if ((nttlist == NULL) ||
-                           (GetCount(nttlist) == 0))
-                               break;
-                       if (GetHash(nttlist, nodename, len, &vdata))
-                               retval ++;
-                       if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: have [%d] <%s>\n", retval, nodename);
-                       break;
-       }
 
-       end_critical_section(S_NTTLIST);
-       return(retval);
-}
 
-void cleanup_nttlist(void)
-{
-        begin_critical_section(S_NTTLIST);
-       DeleteHash(&nttlist);
-        end_critical_section(S_NTTLIST);
-}
 
 
 
@@ -707,7 +568,7 @@ void network_logout_hook(void)
         * If we were talking to a network node, we're not anymore...
         */
        if (!IsEmptyStr(CCC->net_node)) {
-               network_talking_to(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
+               CtdlNetworkTalkingTo(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
                CCC->net_node[0] = '\0';
        }
 }
@@ -716,7 +577,7 @@ void network_cleanup_function(void)
        struct CitContext *CCC = CC;
 
        if (!IsEmptyStr(CCC->net_node)) {
-               network_talking_to(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
+               CtdlNetworkTalkingTo(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
                CCC->net_node[0] = '\0';
        }
 }
@@ -725,10 +586,7 @@ void network_cleanup_function(void)
 /*
  * Module entry point
  */
-void SetNTTDebugEnabled(const int n)
-{
-       NTTDebugEnabled = n;
-}
+
 void SetNetQDebugEnabled(const int n)
 {
        NetQDebugEnabled = n;
@@ -739,9 +597,7 @@ CTDL_MODULE_INIT(network)
        if (!threading)
        {
                CtdlFillSystemContext(&networker_spool_CC, "CitNetSpool");
-               CtdlRegisterDebugFlagHook(HKEY("networktalkingto"), SetNTTDebugEnabled, &NTTDebugEnabled);
                CtdlRegisterDebugFlagHook(HKEY("networkqueue"), SetNetQDebugEnabled, &NetQDebugEnabled);
-               CtdlRegisterCleanupHook(cleanup_nttlist);
                CtdlRegisterSessionHook(network_cleanup_function, EVT_STOP, PRIO_STOP + 30);
                 CtdlRegisterSessionHook(network_logout_hook, EVT_LOGOUT, PRIO_LOGOUT + 10);
                CtdlRegisterProtoHook(cmd_nsyn, "NSYN", "Synchronize room to node");
index c11a7f891ff02374e85b428453b5bd9401ef94ad..7de793905f76699979d801c97b1f105940af756e 100644 (file)
@@ -30,12 +30,6 @@ extern int NetQDebugEnabled;
                             "CC[%d]" FORMAT, \
                             CCC->cs_pid)
 
-typedef struct namelist namelist;
-
-struct namelist {
-       namelist *next;
-       char name[SIZ];
-};
 
 
 void free_netfilter_list(void);
@@ -48,13 +42,3 @@ void network_queue_room(struct ctdlroom *, void *);
 void network_bounce(struct CtdlMessage *msg, char *reason);
 int network_usetable(struct CtdlMessage *msg);
 
-int network_talking_to(const char *nodename, long len, int operation);
-
-/*
- * Operations that can be performed by network_talking_to()
- */
-enum {
-        NTT_ADD,
-        NTT_REMOVE,
-        NTT_CHECK
-};
diff --git a/citadel/modules/network/serv_networkclient.c b/citadel/modules/network/serv_networkclient.c
deleted file mode 100644 (file)
index 92ae1f7..0000000
+++ /dev/null
@@ -1,1118 +0,0 @@
-/*
- * This module handles shared rooms, inter-Citadel mail, and outbound
- * mailing list processing.
- *
- * Copyright (c) 2000-2012 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.
- *
- * ** NOTE **   A word on the S_NETCONFIGS semaphore:
- * This is a fairly high-level type of critical section.  It ensures that no
- * two threads work on the netconfigs files at the same time.  Since we do
- * so many things inside these, here are the rules:
- *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
- *  2. Do *not* perform any I/O with the client during these sections.
- *
- */
-
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.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
-#ifdef HAVE_SYSCALL_H
-# include <syscall.h>
-#else 
-# if HAVE_SYS_SYSCALL_H
-#  include <sys/syscall.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "server.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "user_ops.h"
-#include "database.h"
-#include "msgbase.h"
-#include "internet_addressing.h"
-#include "serv_network.h"
-#include "clientsocket.h"
-#include "file_ops.h"
-#include "citadel_dirs.h"
-#include "threads.h"
-#include "context.h"
-#include "netconfig.h"
-#include "ctdl_module.h"
-
-struct CitContext networker_client_CC;
-
-#define NODE ChrPtr(((AsyncNetworker*)IO->Data)->node)
-#define N ((AsyncNetworker*)IO->Data)->n
-
-int NetworkClientDebugEnabled = 0;
-
-#define NCDBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (NetworkClientDebugEnabled != 0))
-
-#define EVN_syslog(LEVEL, FORMAT, ...)                                 \
-       NCDBGLOG(LEVEL) syslog(LEVEL,                                   \
-                              "IO[%ld]CC[%d]NW[%s][%ld]" FORMAT,       \
-                              IO->ID, CCID, NODE, N, __VA_ARGS__)
-
-#define EVNM_syslog(LEVEL, FORMAT)                                     \
-       NCDBGLOG(LEVEL) syslog(LEVEL,                                   \
-                              "IO[%ld]CC[%d]NW[%s][%ld]" FORMAT,       \
-                              IO->ID, CCID, NODE, N)
-
-#define EVNCS_syslog(LEVEL, FORMAT, ...) \
-       NCDBGLOG(LEVEL) syslog(LEVEL, "IO[%ld]NW[%s][%ld]" FORMAT,      \
-                              IO->ID, NODE, N, __VA_ARGS__)
-
-#define EVNCSM_syslog(LEVEL, FORMAT) \
-       NCDBGLOG(LEVEL) syslog(LEVEL, "IO[%ld]NW[%s][%ld]" FORMAT,      \
-                              IO->ID, NODE, N)
-
-
-typedef enum _eNWCState {
-       eGreating,
-       eAuth,
-       eNDOP,
-       eREAD,
-       eReadBLOB,
-       eCLOS,
-       eNUOP,
-       eWRIT,
-       eWriteBLOB,
-       eUCLS,
-       eQUIT
-}eNWCState;
-
-typedef enum _eNWCVState {
-       eNWCVSLookup,
-       eNWCVSConnecting,
-       eNWCVSConnFail,
-       eNWCVSGreating,
-       eNWCVSAuth,
-       eNWCVSAuthFailNTT,
-       eNWCVSAuthFail,
-       eNWCVSNDOP,
-       eNWCVSNDOPDone,
-       eNWCVSNUOP,
-       eNWCVSNUOPDone,
-       eNWCVSFail
-}eNWCVState;
-
-ConstStr NWCStateStr[] = {
-       {HKEY("Looking up Host")},
-       {HKEY("Connecting host")},
-       {HKEY("Failed to connect")},
-       {HKEY("Rread Greeting")},
-       {HKEY("Authenticating")},
-       {HKEY("Auth failed by NTT")},
-       {HKEY("Auth failed")},
-       {HKEY("Downloading")},
-       {HKEY("Downloading Success")},
-       {HKEY("Uploading Spoolfile")},
-       {HKEY("Uploading done")},
-       {HKEY("failed")}
-};
-
-void SetNWCState(AsyncIO *IO, eNWCVState State)
-{
-       CitContext* CCC = IO->CitContext;
-       memcpy(CCC->cs_clientname, NWCStateStr[State].Key, NWCStateStr[State].len + 1);
-}
-
-typedef struct _async_networker {
-        AsyncIO IO;
-       DNSQueryParts HostLookup;
-       eNWCState State;
-       long n;
-        StrBuf *SpoolFileName;
-        StrBuf *tempFileName;
-       StrBuf *node;
-       StrBuf *host;
-       StrBuf *port;
-       StrBuf *secret;
-       StrBuf          *Url;
-} AsyncNetworker;
-
-typedef eNextState(*NWClientHandler)(AsyncNetworker* NW);
-eNextState nwc_get_one_host_ip(AsyncIO *IO);
-
-eNextState nwc_connect_ip(AsyncIO *IO);
-
-eNextState NWC_SendQUIT(AsyncNetworker *NW);
-eNextState NWC_DispatchWriteDone(AsyncIO *IO);
-
-void DeleteNetworker(void *vptr)
-{
-       AsyncNetworker *NW = (AsyncNetworker *)vptr;
-        FreeStrBuf(&NW->SpoolFileName);
-        FreeStrBuf(&NW->tempFileName);
-       FreeStrBuf(&NW->node);
-       FreeStrBuf(&NW->host);
-       FreeStrBuf(&NW->port);
-       FreeStrBuf(&NW->secret);
-       FreeStrBuf(&NW->Url);
-       FreeStrBuf(&NW->IO.ErrMsg);
-       FreeAsyncIOContents(&NW->IO);
-       if (NW->HostLookup.VParsedDNSReply != NULL) {
-               NW->HostLookup.DNSReplyFree(NW->HostLookup.VParsedDNSReply);
-               NW->HostLookup.VParsedDNSReply = NULL;
-       }
-       free(NW);
-}
-
-#define NWC_DBG_SEND() EVN_syslog(LOG_DEBUG, ": > %s", ChrPtr(NW->IO.SendBuf.Buf))
-#define NWC_DBG_READ() EVN_syslog(LOG_DEBUG, ": < %s\n", ChrPtr(NW->IO.IOBuf))
-#define NWC_OK (strncasecmp(ChrPtr(NW->IO.IOBuf), "+OK", 3) == 0)
-
-eNextState SendFailureMessage(AsyncIO *IO)
-{
-       AsyncNetworker *NW = IO->Data;
-       long lens[2];
-       const char *strs[2];
-
-       strs[0] = ChrPtr(NW->node);
-       lens[0] = StrLength(NW->node);
-       
-       strs[1] = ChrPtr(NW->IO.ErrMsg);
-       lens[1] = StrLength(NW->IO.ErrMsg);
-       CtdlAideFPMessage(
-               ChrPtr(NW->IO.ErrMsg),
-               "Networker error",
-               2, strs, (long*) &lens);
-       
-       return eAbort;
-}
-
-eNextState FinalizeNetworker(AsyncIO *IO)
-{
-       AsyncNetworker *NW = (AsyncNetworker *)IO->Data;
-
-       network_talking_to(SKEY(NW->node), NTT_REMOVE);
-
-       DeleteNetworker(IO->Data);
-       return eAbort;
-}
-
-eNextState NWC_ReadGreeting(AsyncNetworker *NW)
-{
-       char connected_to[SIZ];
-       AsyncIO *IO = &NW->IO;
-       SetNWCState(IO, eNWCVSGreating);
-       NWC_DBG_READ();
-       /* Read the server greeting */
-       /* Check that the remote is who we think it is and warn the Aide if not */
-       extract_token (connected_to, ChrPtr(NW->IO.IOBuf), 1, ' ', sizeof connected_to);
-       if (strcmp(connected_to, ChrPtr(NW->node)) != 0)
-       {
-               if (NW->IO.ErrMsg == NULL)
-                       NW->IO.ErrMsg = NewStrBuf();
-               StrBufPrintf(NW->IO.ErrMsg,
-                            "Connected to node \"%s\" but I was expecting to connect to node \"%s\".",
-                            connected_to, ChrPtr(NW->node));
-               EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
-               StopClientWatchers(IO, 1);
-               return QueueDBOperation(IO, SendFailureMessage);
-       }
-       return eSendReply;
-}
-
-eNextState NWC_SendAuth(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       SetNWCState(IO, eNWCVSAuth);
-       /* We're talking to the correct node.  Now identify ourselves. */
-       StrBufPrintf(NW->IO.SendBuf.Buf, "NETP %s|%s\n", 
-                    config.c_nodename, 
-                    ChrPtr(NW->secret));
-       NWC_DBG_SEND();
-       return eSendReply;
-}
-
-eNextState NWC_ReadAuthReply(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-       if (ChrPtr(NW->IO.IOBuf)[0] == '2')
-       {
-               return eSendReply;
-       }
-       else
-       {
-               int Error = atol(ChrPtr(NW->IO.IOBuf));
-               if (NW->IO.ErrMsg == NULL)
-                       NW->IO.ErrMsg = NewStrBuf();
-               StrBufPrintf(NW->IO.ErrMsg,
-                            "Connected to node \"%s\" but my secret wasn't accurate.\nReason was:%s\n",
-                            ChrPtr(NW->node), ChrPtr(NW->IO.IOBuf) + 4);
-               if (Error == 552) {
-                       SetNWCState(IO, eNWCVSAuthFailNTT);
-                       EVN_syslog(LOG_INFO,
-                                  "Already talking to %s; skipping this time.\n",
-                                  ChrPtr(NW->node));
-                       
-               }
-               else {
-                       SetNWCState(IO, eNWCVSAuthFailNTT);
-                       EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
-                       StopClientWatchers(IO, 1);
-                       return QueueDBOperation(IO, SendFailureMessage);
-               }
-               return eAbort;
-       }
-}
-
-eNextState NWC_SendNDOP(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       SetNWCState(IO, eNWCVSNDOP);
-       NW->tempFileName = NewStrBuf();
-       NW->SpoolFileName = NewStrBuf();
-       StrBufPrintf(NW->SpoolFileName,
-                    "%s/%s.%lx%x",
-                    ctdl_netin_dir,
-                    ChrPtr(NW->node),
-                    time(NULL),// TODO: get time from libev
-                    rand());
-       StrBufStripSlashes(NW->SpoolFileName, 1);
-       StrBufPrintf(NW->tempFileName, 
-                    "%s/%s.%lx%x",
-                    ctdl_nettmp_dir,
-                    ChrPtr(NW->node),
-                    time(NULL),// TODO: get time from libev
-                    rand());
-       StrBufStripSlashes(NW->tempFileName, 1);
-       /* We're talking to the correct node.  Now identify ourselves. */
-       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NDOP\n"));
-       NWC_DBG_SEND();
-       return eSendReply;
-}
-
-eNextState NWC_ReadNDOPReply(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       int TotalSendSize;
-       NWC_DBG_READ();
-       if (ChrPtr(NW->IO.IOBuf)[0] == '2')
-       {
-
-               NW->IO.IOB.TotalSentAlready = 0;
-               TotalSendSize = atol (ChrPtr(NW->IO.IOBuf) + 4);
-               EVN_syslog(LOG_DEBUG, "Expecting to transfer %d bytes\n", TotalSendSize);
-               if (TotalSendSize <= 0) {
-                       NW->State = eNUOP - 1;
-               }
-               else {
-                       int fd;
-                       fd = open(ChrPtr(NW->tempFileName), 
-                                 O_EXCL|O_CREAT|O_NONBLOCK|O_WRONLY, 
-                                 S_IRUSR|S_IWUSR);
-                       if (fd < 0)
-                       {
-                               SetNWCState(IO, eNWCVSFail);
-                               EVN_syslog(LOG_CRIT,
-                                      "cannot open %s: %s\n", 
-                                      ChrPtr(NW->tempFileName), 
-                                      strerror(errno));
-
-                               NW->State = eQUIT - 1;
-                               return eAbort;
-                       }
-                       FDIOBufferInit(&NW->IO.IOB, &NW->IO.RecvBuf, fd, TotalSendSize);
-               }
-               return eSendReply;
-       }
-       else
-       {
-               SetNWCState(IO, eNWCVSFail);
-               return eAbort;
-       }
-}
-
-eNextState NWC_SendREAD(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       eNextState rc;
-
-       if (NW->IO.IOB.TotalSentAlready < NW->IO.IOB.TotalSendSize)
-       {
-               /*
-                * If shutting down we can exit here and unlink the temp file.
-                * this shouldn't loose us any messages.
-                */
-               if (server_shutting_down)
-               {
-                       FDIOBufferDelete(&NW->IO.IOB);
-                       unlink(ChrPtr(NW->tempFileName));
-                       FDIOBufferDelete(&IO->IOB);
-                       SetNWCState(IO, eNWCVSFail);
-                       return eAbort;
-               }
-               StrBufPrintf(NW->IO.SendBuf.Buf, "READ %ld|%ld\n",
-                            NW->IO.IOB.TotalSentAlready,
-                            NW->IO.IOB.TotalSendSize);
-/*
-                            ((NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready > IGNET_PACKET_SIZE)
-                             ? IGNET_PACKET_SIZE : 
-                             (NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready))
-                       );
-*/
-               NWC_DBG_SEND();
-               return eSendReply;
-       }
-       else 
-       {
-               NW->State = eCLOS;
-               rc = NWC_DispatchWriteDone(&NW->IO);
-               NWC_DBG_SEND();
-
-               return rc;
-       }
-}
-
-eNextState NWC_ReadREADState(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-       if (ChrPtr(NW->IO.IOBuf)[0] == '6')
-       {
-               NW->IO.IOB.ChunkSendRemain = 
-                       NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
-               return eReadFile;
-       }
-       FDIOBufferDelete(&IO->IOB);
-       return eAbort;
-}
-eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW);
-eNextState NWC_ReadREADBlob(AsyncNetworker *NW)
-{
-       eNextState rc;
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-       if (NW->IO.IOB.TotalSendSize == NW->IO.IOB.TotalSentAlready)
-       {
-               NW->State ++;
-
-               FDIOBufferDelete(&NW->IO.IOB);
-
-               if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
-                       EVN_syslog(LOG_ALERT, 
-                              "Could not link %s to %s: %s\n",
-                              ChrPtr(NW->tempFileName), 
-                              ChrPtr(NW->SpoolFileName), 
-                              strerror(errno));
-               }
-       
-               unlink(ChrPtr(NW->tempFileName));
-               rc = NWC_DispatchWriteDone(&NW->IO);
-               NW->State --;
-               return rc;
-       }
-       else {
-               NW->State --;
-               NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
-               return eSendReply; //NWC_DispatchWriteDone(&NW->IO);
-       }
-}
-
-eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW)
-{
-       eNextState rc;
-       AsyncIO *IO = &NW->IO;
-/* we don't have any data to debug print here. */
-       if (NW->IO.IOB.TotalSentAlready >= NW->IO.IOB.TotalSendSize)
-       {
-               NW->State ++;
-
-               FDIOBufferDelete(&NW->IO.IOB);
-               if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
-                       EVN_syslog(LOG_ALERT, 
-                              "Could not link %s to %s: %s\n",
-                              ChrPtr(NW->tempFileName), 
-                              ChrPtr(NW->SpoolFileName), 
-                              strerror(errno));
-               }
-       
-               unlink(ChrPtr(NW->tempFileName));
-               rc = NWC_DispatchWriteDone(&NW->IO);
-               NW->State --;
-               return rc;
-       }
-       else {
-               NW->State --;
-               NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
-               return NWC_DispatchWriteDone(&NW->IO);
-       }
-}
-eNextState NWC_SendCLOS(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       SetNWCState(IO, eNWCVSNDOPDone);
-       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("CLOS\n"));
-       NWC_DBG_SEND();
-       return eSendReply;
-}
-
-eNextState NWC_ReadCLOSReply(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-       FDIOBufferDelete(&IO->IOB);
-       if (ChrPtr(NW->IO.IOBuf)[0] != '2')
-               return eTerminateConnection;
-       return eSendReply;
-}
-
-
-eNextState NWC_SendNUOP(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       eNextState rc;
-       long TotalSendSize;
-       struct stat statbuf;
-       int fd;
-
-       SetNWCState(IO, eNWCVSNUOP);
-       StrBufPrintf(NW->SpoolFileName,
-                    "%s/%s",
-                    ctdl_netout_dir,
-                    ChrPtr(NW->node));
-       StrBufStripSlashes(NW->SpoolFileName, 1);
-
-       fd = open(ChrPtr(NW->SpoolFileName), O_EXCL|O_NONBLOCK|O_RDONLY);
-       if (fd < 0) {
-               if (errno != ENOENT) {
-                       EVN_syslog(LOG_CRIT,
-                              "cannot open %s: %s\n", 
-                              ChrPtr(NW->SpoolFileName), 
-                              strerror(errno));
-               }
-               NW->State = eQUIT;
-               rc = NWC_SendQUIT(NW);
-               NWC_DBG_SEND();
-               return rc;
-       }
-
-       if (fstat(fd, &statbuf) == -1) {
-               EVN_syslog(LOG_CRIT, "FSTAT FAILED %s [%s]--\n", 
-                          ChrPtr(NW->SpoolFileName), 
-                          strerror(errno));
-               if (fd > 0) close(fd);
-               return eAbort;
-       }
-       TotalSendSize = statbuf.st_size;
-       if (TotalSendSize == 0) {
-               EVNM_syslog(LOG_DEBUG,
-                      "Nothing to send.\n");
-               NW->State = eQUIT;
-               rc = NWC_SendQUIT(NW);
-               NWC_DBG_SEND();
-               if (fd > 0) close(fd);
-               return rc;
-       }
-       FDIOBufferInit(&NW->IO.IOB, &NW->IO.SendBuf, fd, TotalSendSize);
-
-       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NUOP\n"));
-       NWC_DBG_SEND();
-       return eSendReply;
-
-}
-eNextState NWC_ReadNUOPReply(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-       if (ChrPtr(NW->IO.IOBuf)[0] != '2') {
-               FDIOBufferDelete(&IO->IOB);
-               return eAbort;
-       }
-       return eSendReply;
-}
-
-eNextState NWC_SendWRIT(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       StrBufPrintf(NW->IO.SendBuf.Buf, "WRIT %ld\n", 
-                    NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready);
-       NWC_DBG_SEND();
-       return eSendReply;
-}
-eNextState NWC_ReadWRITReply(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-       if (ChrPtr(NW->IO.IOBuf)[0] != '7')
-       {
-               FDIOBufferDelete(&IO->IOB);
-               return eAbort;
-       }
-
-       NW->IO.IOB.ChunkSendRemain = 
-               NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
-       return eSendFile;
-}
-
-eNextState NWC_SendBlobDone(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       eNextState rc;
-       if (NW->IO.IOB.TotalSentAlready >= IO->IOB.TotalSendSize)
-       {
-               NW->State ++;
-
-               FDIOBufferDelete(&IO->IOB);
-               rc =  NWC_DispatchWriteDone(IO);
-               NW->State --;
-               return rc;
-       }
-       else {
-               NW->State --;
-               IO->IOB.ChunkSendRemain = IO->IOB.ChunkSize;
-               rc = NWC_DispatchWriteDone(IO);
-               NW->State --;
-               return rc;
-       }
-}
-
-eNextState NWC_SendUCLS(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("UCLS 1\n"));
-       NWC_DBG_SEND();
-       return eSendReply;
-
-}
-eNextState NWC_ReadUCLS(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-
-       EVN_syslog(LOG_NOTICE, "Sent %ld octets to <%s>\n", NW->IO.IOB.ChunkSize, ChrPtr(NW->node));
-       if (ChrPtr(NW->IO.IOBuf)[0] == '2') {
-               EVN_syslog(LOG_DEBUG, "Removing <%s>\n", ChrPtr(NW->SpoolFileName));
-               unlink(ChrPtr(NW->SpoolFileName));
-       }
-       FDIOBufferDelete(&IO->IOB);
-       SetNWCState(IO, eNWCVSNUOPDone);
-       return eSendReply;
-}
-
-eNextState NWC_SendQUIT(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("QUIT\n"));
-
-       NWC_DBG_SEND();
-       return eSendReply;
-}
-
-eNextState NWC_ReadQUIT(AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       NWC_DBG_READ();
-
-       return eAbort;
-}
-
-
-NWClientHandler NWC_ReadHandlers[] = {
-       NWC_ReadGreeting,
-       NWC_ReadAuthReply,
-       NWC_ReadNDOPReply,
-       NWC_ReadREADState,
-       NWC_ReadREADBlob,
-       NWC_ReadCLOSReply,
-       NWC_ReadNUOPReply,
-       NWC_ReadWRITReply,
-       NWC_SendBlobDone,
-       NWC_ReadUCLS,
-       NWC_ReadQUIT};
-
-long NWC_ConnTimeout = 100;
-
-const long NWC_SendTimeouts[] = {
-       100,
-       100,
-       100,
-       100,
-       100,
-       100,
-       100,
-       100
-};
-const ConstStr NWC[] = {
-       {HKEY("Connection broken during ")},
-       {HKEY("Connection broken during ")},
-       {HKEY("Connection broken during ")},
-       {HKEY("Connection broken during ")},
-       {HKEY("Connection broken during ")},
-       {HKEY("Connection broken during ")},
-       {HKEY("Connection broken during ")},
-       {HKEY("Connection broken during ")}
-};
-
-NWClientHandler NWC_SendHandlers[] = {
-       NULL,
-       NWC_SendAuth,
-       NWC_SendNDOP,
-       NWC_SendREAD,
-       NWC_ReadREADBlobDone,
-       NWC_SendCLOS,
-       NWC_SendNUOP,
-       NWC_SendWRIT,
-       NWC_SendBlobDone,
-       NWC_SendUCLS,
-       NWC_SendQUIT
-};
-
-const long NWC_ReadTimeouts[] = {
-       100,
-       100,
-       100,
-       100,
-       100,
-       100,
-       100,
-       100,
-       100,
-       100
-};
-
-
-
-
-eNextState nwc_get_one_host_ip_done(AsyncIO *IO)
-{
-       AsyncNetworker *NW = IO->Data;
-       struct hostent *hostent;
-
-       QueryCbDone(IO);
-
-       hostent = NW->HostLookup.VParsedDNSReply;
-       if ((NW->HostLookup.DNSStatus == ARES_SUCCESS) && 
-           (hostent != NULL) ) {
-               memset(&NW->IO.ConnectMe->Addr, 0, sizeof(struct in6_addr));
-               if (NW->IO.ConnectMe->IPv6) {
-                       memcpy(&NW->IO.ConnectMe->Addr.sin6_addr.s6_addr, 
-                              &hostent->h_addr_list[0],
-                              sizeof(struct in6_addr));
-                       
-                       NW->IO.ConnectMe->Addr.sin6_family = hostent->h_addrtype;
-                       NW->IO.ConnectMe->Addr.sin6_port   = htons(atol(ChrPtr(NW->port)));//// TODO use the one from the URL.
-               }
-               else {
-                       struct sockaddr_in *addr = (struct sockaddr_in*) &NW->IO.ConnectMe->Addr;
-                       /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
-//                     addr->sin_addr.s_addr = htonl((uint32_t)&hostent->h_addr_list[0]);
-                       memcpy(&addr->sin_addr.s_addr, 
-                              hostent->h_addr_list[0], 
-                              sizeof(uint32_t));
-                       
-                       addr->sin_family = hostent->h_addrtype;
-                       addr->sin_port   = htons(504);/// default citadel port
-                       
-               }
-               return nwc_connect_ip(IO);
-       }
-       else
-               return eAbort;
-}
-
-
-eNextState nwc_get_one_host_ip(AsyncIO *IO)
-{
-       AsyncNetworker *NW = IO->Data;
-       /* 
-        * here we start with the lookup of one host.
-        */ 
-
-       EVN_syslog(LOG_DEBUG, "NWC: %s\n", __FUNCTION__);
-
-       EVN_syslog(LOG_DEBUG, 
-                  "NWC client[%ld]: looking up %s-Record %s : %d ...\n", 
-                  NW->n, 
-                  (NW->IO.ConnectMe->IPv6)? "aaaa": "a",
-                  NW->IO.ConnectMe->Host, 
-                  NW->IO.ConnectMe->Port);
-
-       QueueQuery((NW->IO.ConnectMe->IPv6)? ns_t_aaaa : ns_t_a, 
-                  NW->IO.ConnectMe->Host, 
-                  &NW->IO, 
-                  &NW->HostLookup, 
-                  nwc_get_one_host_ip_done);
-       IO->NextState = eReadDNSReply;
-       return IO->NextState;
-}
-/**
- * @brief lineread Handler; understands when to read more POP3 lines, and when this is a one-lined reply.
- */
-eReadState NWC_ReadServerStatus(AsyncIO *IO)
-{
-//     AsyncNetworker *NW = IO->Data;
-       eReadState Finished = eBufferNotEmpty; 
-
-       switch (IO->NextState) {
-       case eSendDNSQuery:
-       case eReadDNSReply:
-       case eDBQuery:
-       case eConnect:
-       case eTerminateConnection:
-       case eAbort:
-               Finished = eReadFail;
-               break;
-       case eSendReply: 
-       case eSendMore:
-       case eReadMore:
-       case eReadMessage: 
-               Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
-               break;
-       case eReadFile:
-       case eSendFile:
-       case eReadPayload:
-               break;
-       }
-       return Finished;
-}
-
-
-
-eNextState NWC_FailNetworkConnection(AsyncIO *IO)
-{
-       SetNWCState(IO, eNWCVSConnFail);
-       StopClientWatchers(IO, 1);
-       return QueueDBOperation(IO, SendFailureMessage);
-}
-
-void NWC_SetTimeout(eNextState NextTCPState, AsyncNetworker *NW)
-{
-       AsyncIO *IO = &NW->IO;
-       double Timeout = 0.0;
-
-       EVN_syslog(LOG_DEBUG, "%s - %d\n", __FUNCTION__, NextTCPState);
-
-       switch (NextTCPState) {
-       case eSendReply:
-       case eSendMore:
-               break;
-       case eReadFile:
-       case eReadMessage:
-               Timeout = NWC_ReadTimeouts[NW->State];
-               break;
-       case eReadPayload:
-               Timeout = 100000;
-               /* TODO!!! */
-               break;
-       case eSendDNSQuery:
-       case eReadDNSReply:
-       case eConnect:
-       case eSendFile:
-//TODO
-       case eTerminateConnection:
-       case eDBQuery:
-       case eAbort:
-       case eReadMore://// TODO
-               return;
-       }
-       if (Timeout > 0) {
-               EVN_syslog(LOG_DEBUG, 
-                          "%s - %d %f\n",
-                          __FUNCTION__,
-                          NextTCPState,
-                          Timeout);
-               SetNextTimeout(&NW->IO, Timeout*100);
-       }
-}
-
-
-eNextState NWC_DispatchReadDone(AsyncIO *IO)
-{
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       AsyncNetworker *NW = IO->Data;
-       eNextState rc;
-
-       rc = NWC_ReadHandlers[NW->State](NW);
-       if (rc != eReadMore)
-               NW->State++;
-       NWC_SetTimeout(rc, NW);
-       return rc;
-}
-eNextState NWC_DispatchWriteDone(AsyncIO *IO)
-{
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       AsyncNetworker *NW = IO->Data;
-       eNextState rc;
-
-       rc = NWC_SendHandlers[NW->State](NW);
-       NWC_SetTimeout(rc, NW);
-       return rc;
-}
-
-/*****************************************************************************/
-/*                     Networker CLIENT ERROR CATCHERS                       */
-/*****************************************************************************/
-eNextState NWC_Terminate(AsyncIO *IO)
-{
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       FinalizeNetworker(IO);
-       return eAbort;
-}
-
-eNextState NWC_TerminateDB(AsyncIO *IO)
-{
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       FinalizeNetworker(IO);
-       return eAbort;
-}
-
-eNextState NWC_Timeout(AsyncIO *IO)
-{
-       AsyncNetworker *NW = IO->Data;
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-
-       if (NW->IO.ErrMsg == NULL)
-               NW->IO.ErrMsg = NewStrBuf();
-       StrBufPrintf(NW->IO.ErrMsg, "Timeout while talking to %s \r\n", ChrPtr(NW->host));
-       return NWC_FailNetworkConnection(IO);
-}
-eNextState NWC_ConnFail(AsyncIO *IO)
-{
-       AsyncNetworker *NW = IO->Data;
-
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       if (NW->IO.ErrMsg == NULL)
-               NW->IO.ErrMsg = NewStrBuf();
-       StrBufPrintf(NW->IO.ErrMsg, "failed to connect %s \r\n", ChrPtr(NW->host));
-
-       return NWC_FailNetworkConnection(IO);
-}
-eNextState NWC_DNSFail(AsyncIO *IO)
-{
-       AsyncNetworker *NW = IO->Data;
-
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       if (NW->IO.ErrMsg == NULL)
-               NW->IO.ErrMsg = NewStrBuf();
-       StrBufPrintf(NW->IO.ErrMsg, "failed to look up %s \r\n", ChrPtr(NW->host));
-
-       return NWC_FailNetworkConnection(IO);
-}
-eNextState NWC_Shutdown(AsyncIO *IO)
-{
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-
-       FinalizeNetworker(IO);
-       return eAbort;
-}
-
-
-eNextState nwc_connect_ip(AsyncIO *IO)
-{
-       AsyncNetworker *NW = IO->Data;
-
-       SetNWCState(&NW->IO, eNWCVSConnecting);
-       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       EVN_syslog(LOG_NOTICE, "Connecting to <%s> at %s:%s\n", 
-                  ChrPtr(NW->node), 
-                  ChrPtr(NW->host),
-                  ChrPtr(NW->port));
-       
-       return EvConnectSock(IO,
-                            NWC_ConnTimeout,
-                            NWC_ReadTimeouts[0],
-                            1);
-}
-
-static int NetworkerCount = 0;
-void RunNetworker(AsyncNetworker *NW)
-{
-       NW->n = NetworkerCount++;
-       network_talking_to(SKEY(NW->node), NTT_ADD);
-       syslog(LOG_DEBUG, "NW[%s][%ld]: polling\n", ChrPtr(NW->node), NW->n);
-       ParseURL(&NW->IO.ConnectMe, NW->Url, 504);
-
-       InitIOStruct(&NW->IO,
-                    NW,
-                    eReadMessage,
-                    NWC_ReadServerStatus,
-                    NWC_DNSFail,
-                    NWC_DispatchWriteDone,
-                    NWC_DispatchReadDone,
-                    NWC_Terminate,
-                    NWC_TerminateDB,
-                    NWC_ConnFail,
-                    NWC_Timeout,
-                    NWC_Shutdown);
-
-       safestrncpy(((CitContext *)NW->IO.CitContext)->cs_host, 
-                   ChrPtr(NW->host),
-                   sizeof(((CitContext *)NW->IO.CitContext)->cs_host)); 
-
-       if (NW->IO.ConnectMe->IsIP) {
-               SetNWCState(&NW->IO, eNWCVSLookup);
-               QueueEventContext(&NW->IO,
-                                 nwc_connect_ip);
-       }
-       else { /* uneducated admin has chosen to add DNS to the equation... */
-               SetNWCState(&NW->IO, eNWCVSConnecting);
-               QueueEventContext(&NW->IO,
-                                 nwc_get_one_host_ip);
-       }
-}
-
-/*
- * Poll other Citadel nodes and transfer inbound/outbound network data.
- * Set "full" to nonzero to force a poll of every node, or to zero to poll
- * only nodes to which we have data to send.
- */
-void network_poll_other_citadel_nodes(int full_poll, HashList *ignetcfg)
-{
-       const char *key;
-       long len;
-       HashPos *Pos;
-       void *vCfg;
-       AsyncNetworker *NW;
-       StrBuf *SpoolFileName;
-       
-       int poll = 0;
-       
-       if (GetCount(ignetcfg) ==0) {
-               syslog(LOG_DEBUG, "network: no neighbor nodes are configured - not polling.\n");
-               return;
-       }
-       become_session(&networker_client_CC);
-
-       SpoolFileName = NewStrBufPlain(ctdl_netout_dir, -1);
-
-       Pos = GetNewHashPos(ignetcfg, 0);
-
-       while (GetNextHashPos(ignetcfg, Pos, &len, &key, &vCfg))
-       {
-               /* Use the string tokenizer to grab one line at a time */
-               if(server_shutting_down)
-                       return;/* TODO free stuff*/
-               NodeConf *pNode = (NodeConf*) vCfg;
-               poll = 0;
-               NW = (AsyncNetworker*)malloc(sizeof(AsyncNetworker));
-               memset(NW, 0, sizeof(AsyncNetworker));
-               
-               NW->node = NewStrBufDup(pNode->NodeName);
-               NW->host = NewStrBufDup(pNode->Host);
-               NW->port = NewStrBufDup(pNode->Port);
-               NW->secret = NewStrBufDup(pNode->Secret);
-               
-               if ( (StrLength(NW->node) != 0) && 
-                    (StrLength(NW->secret) != 0) &&
-                    (StrLength(NW->host) != 0) &&
-                    (StrLength(NW->port) != 0))
-               {
-                       poll = full_poll;
-                       if (poll == 0)
-                       {
-                               StrBufAppendBufPlain(SpoolFileName, HKEY("/"), 0);
-                               StrBufAppendBuf(SpoolFileName, NW->node, 0);
-                               StrBufStripSlashes(SpoolFileName, 1);
-                               
-                               if (access(ChrPtr(SpoolFileName), R_OK) == 0) {
-                                       poll = 1;
-                               }
-                       }
-               }
-               if (poll && 
-                   (StrLength(NW->host) > 0) && 
-                   strcmp("0.0.0.0", ChrPtr(NW->host)))
-               {
-                       NW->Url = NewStrBuf();
-                       StrBufPrintf(NW->Url, "citadel://%s@%s:%s", 
-                                    ChrPtr(NW->secret),
-                                    ChrPtr(NW->host),
-                                    ChrPtr(NW->port));
-                       if (!network_talking_to(SKEY(NW->node), NTT_CHECK))
-                       {
-                               RunNetworker(NW);
-                               continue;
-                       }
-               }
-               DeleteNetworker(NW);
-       }
-       FreeStrBuf(&SpoolFileName);
-       DeleteHashPos(&Pos);
-}
-
-
-void network_do_clientqueue(void)
-{
-       HashList *working_ignetcfg;
-       int full_processing = 1;
-       static time_t last_run = 0L;
-
-       /*
-        * Run the full set of processing tasks no more frequently
-        * than once every n seconds
-        */
-       if ( (time(NULL) - last_run) < config.c_net_freq ) {
-               full_processing = 0;
-               syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
-                       config.c_net_freq - (time(NULL)- last_run)
-               );
-       }
-
-       working_ignetcfg = load_ignetcfg();
-       /*
-        * Poll other Citadel nodes.  Maybe.  If "full_processing" is set
-        * then we poll everyone.  Otherwise we only poll nodes we have stuff
-        * to send to.
-        */
-       network_poll_other_citadel_nodes(full_processing, working_ignetcfg);
-       DeleteHash(&working_ignetcfg);
-}
-
-void LogDebugEnableNetworkClient(const int n)
-{
-       NetworkClientDebugEnabled = n;
-}
-/*
- * Module entry point
- */
-CTDL_MODULE_INIT(network_client)
-{
-       if (!threading)
-       {
-               CtdlFillSystemContext(&networker_client_CC, "CitNetworker");
-               
-               CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER, PRIO_SEND + 10);
-               CtdlRegisterDebugFlagHook(HKEY("networkclient"), LogDebugEnableNetworkClient, &NetworkClientDebugEnabled);
-
-       }
-       return "networkclient";
-}
diff --git a/citadel/modules/networkclient/serv_networkclient.c b/citadel/modules/networkclient/serv_networkclient.c
new file mode 100644 (file)
index 0000000..4c1afa8
--- /dev/null
@@ -0,0 +1,1116 @@
+/*
+ * This module handles shared rooms, inter-Citadel mail, and outbound
+ * mailing list processing.
+ *
+ * Copyright (c) 2000-2012 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.
+ *
+ * ** NOTE **   A word on the S_NETCONFIGS semaphore:
+ * This is a fairly high-level type of critical section.  It ensures that no
+ * two threads work on the netconfigs files at the same time.  Since we do
+ * so many things inside these, here are the rules:
+ *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
+ *  2. Do *not* perform any I/O with the client during these sections.
+ *
+ */
+
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.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
+#ifdef HAVE_SYSCALL_H
+# include <syscall.h>
+#else 
+# if HAVE_SYS_SYSCALL_H
+#  include <sys/syscall.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "server.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "user_ops.h"
+#include "database.h"
+#include "msgbase.h"
+#include "internet_addressing.h"
+#include "clientsocket.h"
+#include "file_ops.h"
+#include "citadel_dirs.h"
+#include "threads.h"
+#include "context.h"
+#include "ctdl_module.h"
+
+struct CitContext networker_client_CC;
+
+#define NODE ChrPtr(((AsyncNetworker*)IO->Data)->node)
+#define N ((AsyncNetworker*)IO->Data)->n
+
+int NetworkClientDebugEnabled = 0;
+
+#define NCDBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (NetworkClientDebugEnabled != 0))
+
+#define EVN_syslog(LEVEL, FORMAT, ...)                                 \
+       NCDBGLOG(LEVEL) syslog(LEVEL,                                   \
+                              "IO[%ld]CC[%d]NW[%s][%ld]" FORMAT,       \
+                              IO->ID, CCID, NODE, N, __VA_ARGS__)
+
+#define EVNM_syslog(LEVEL, FORMAT)                                     \
+       NCDBGLOG(LEVEL) syslog(LEVEL,                                   \
+                              "IO[%ld]CC[%d]NW[%s][%ld]" FORMAT,       \
+                              IO->ID, CCID, NODE, N)
+
+#define EVNCS_syslog(LEVEL, FORMAT, ...) \
+       NCDBGLOG(LEVEL) syslog(LEVEL, "IO[%ld]NW[%s][%ld]" FORMAT,      \
+                              IO->ID, NODE, N, __VA_ARGS__)
+
+#define EVNCSM_syslog(LEVEL, FORMAT) \
+       NCDBGLOG(LEVEL) syslog(LEVEL, "IO[%ld]NW[%s][%ld]" FORMAT,      \
+                              IO->ID, NODE, N)
+
+
+typedef enum _eNWCState {
+       eGreating,
+       eAuth,
+       eNDOP,
+       eREAD,
+       eReadBLOB,
+       eCLOS,
+       eNUOP,
+       eWRIT,
+       eWriteBLOB,
+       eUCLS,
+       eQUIT
+}eNWCState;
+
+typedef enum _eNWCVState {
+       eNWCVSLookup,
+       eNWCVSConnecting,
+       eNWCVSConnFail,
+       eNWCVSGreating,
+       eNWCVSAuth,
+       eNWCVSAuthFailNTT,
+       eNWCVSAuthFail,
+       eNWCVSNDOP,
+       eNWCVSNDOPDone,
+       eNWCVSNUOP,
+       eNWCVSNUOPDone,
+       eNWCVSFail
+}eNWCVState;
+
+ConstStr NWCStateStr[] = {
+       {HKEY("Looking up Host")},
+       {HKEY("Connecting host")},
+       {HKEY("Failed to connect")},
+       {HKEY("Rread Greeting")},
+       {HKEY("Authenticating")},
+       {HKEY("Auth failed by NTT")},
+       {HKEY("Auth failed")},
+       {HKEY("Downloading")},
+       {HKEY("Downloading Success")},
+       {HKEY("Uploading Spoolfile")},
+       {HKEY("Uploading done")},
+       {HKEY("failed")}
+};
+
+void SetNWCState(AsyncIO *IO, eNWCVState State)
+{
+       CitContext* CCC = IO->CitContext;
+       memcpy(CCC->cs_clientname, NWCStateStr[State].Key, NWCStateStr[State].len + 1);
+}
+
+typedef struct _async_networker {
+        AsyncIO IO;
+       DNSQueryParts HostLookup;
+       eNWCState State;
+       long n;
+        StrBuf *SpoolFileName;
+        StrBuf *tempFileName;
+       StrBuf *node;
+       StrBuf *host;
+       StrBuf *port;
+       StrBuf *secret;
+       StrBuf          *Url;
+} AsyncNetworker;
+
+typedef eNextState(*NWClientHandler)(AsyncNetworker* NW);
+eNextState nwc_get_one_host_ip(AsyncIO *IO);
+
+eNextState nwc_connect_ip(AsyncIO *IO);
+
+eNextState NWC_SendQUIT(AsyncNetworker *NW);
+eNextState NWC_DispatchWriteDone(AsyncIO *IO);
+
+void DeleteNetworker(void *vptr)
+{
+       AsyncNetworker *NW = (AsyncNetworker *)vptr;
+        FreeStrBuf(&NW->SpoolFileName);
+        FreeStrBuf(&NW->tempFileName);
+       FreeStrBuf(&NW->node);
+       FreeStrBuf(&NW->host);
+       FreeStrBuf(&NW->port);
+       FreeStrBuf(&NW->secret);
+       FreeStrBuf(&NW->Url);
+       FreeStrBuf(&NW->IO.ErrMsg);
+       FreeAsyncIOContents(&NW->IO);
+       if (NW->HostLookup.VParsedDNSReply != NULL) {
+               NW->HostLookup.DNSReplyFree(NW->HostLookup.VParsedDNSReply);
+               NW->HostLookup.VParsedDNSReply = NULL;
+       }
+       free(NW);
+}
+
+#define NWC_DBG_SEND() EVN_syslog(LOG_DEBUG, ": > %s", ChrPtr(NW->IO.SendBuf.Buf))
+#define NWC_DBG_READ() EVN_syslog(LOG_DEBUG, ": < %s\n", ChrPtr(NW->IO.IOBuf))
+#define NWC_OK (strncasecmp(ChrPtr(NW->IO.IOBuf), "+OK", 3) == 0)
+
+eNextState SendFailureMessage(AsyncIO *IO)
+{
+       AsyncNetworker *NW = IO->Data;
+       long lens[2];
+       const char *strs[2];
+
+       strs[0] = ChrPtr(NW->node);
+       lens[0] = StrLength(NW->node);
+       
+       strs[1] = ChrPtr(NW->IO.ErrMsg);
+       lens[1] = StrLength(NW->IO.ErrMsg);
+       CtdlAideFPMessage(
+               ChrPtr(NW->IO.ErrMsg),
+               "Networker error",
+               2, strs, (long*) &lens);
+       
+       return eAbort;
+}
+
+eNextState FinalizeNetworker(AsyncIO *IO)
+{
+       AsyncNetworker *NW = (AsyncNetworker *)IO->Data;
+
+       CtdlNetworkTalkingTo(SKEY(NW->node), NTT_REMOVE);
+
+       DeleteNetworker(IO->Data);
+       return eAbort;
+}
+
+eNextState NWC_ReadGreeting(AsyncNetworker *NW)
+{
+       char connected_to[SIZ];
+       AsyncIO *IO = &NW->IO;
+       SetNWCState(IO, eNWCVSGreating);
+       NWC_DBG_READ();
+       /* Read the server greeting */
+       /* Check that the remote is who we think it is and warn the Aide if not */
+       extract_token (connected_to, ChrPtr(NW->IO.IOBuf), 1, ' ', sizeof connected_to);
+       if (strcmp(connected_to, ChrPtr(NW->node)) != 0)
+       {
+               if (NW->IO.ErrMsg == NULL)
+                       NW->IO.ErrMsg = NewStrBuf();
+               StrBufPrintf(NW->IO.ErrMsg,
+                            "Connected to node \"%s\" but I was expecting to connect to node \"%s\".",
+                            connected_to, ChrPtr(NW->node));
+               EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
+               StopClientWatchers(IO, 1);
+               return QueueDBOperation(IO, SendFailureMessage);
+       }
+       return eSendReply;
+}
+
+eNextState NWC_SendAuth(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       SetNWCState(IO, eNWCVSAuth);
+       /* We're talking to the correct node.  Now identify ourselves. */
+       StrBufPrintf(NW->IO.SendBuf.Buf, "NETP %s|%s\n", 
+                    config.c_nodename, 
+                    ChrPtr(NW->secret));
+       NWC_DBG_SEND();
+       return eSendReply;
+}
+
+eNextState NWC_ReadAuthReply(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+       if (ChrPtr(NW->IO.IOBuf)[0] == '2')
+       {
+               return eSendReply;
+       }
+       else
+       {
+               int Error = atol(ChrPtr(NW->IO.IOBuf));
+               if (NW->IO.ErrMsg == NULL)
+                       NW->IO.ErrMsg = NewStrBuf();
+               StrBufPrintf(NW->IO.ErrMsg,
+                            "Connected to node \"%s\" but my secret wasn't accurate.\nReason was:%s\n",
+                            ChrPtr(NW->node), ChrPtr(NW->IO.IOBuf) + 4);
+               if (Error == 552) {
+                       SetNWCState(IO, eNWCVSAuthFailNTT);
+                       EVN_syslog(LOG_INFO,
+                                  "Already talking to %s; skipping this time.\n",
+                                  ChrPtr(NW->node));
+                       
+               }
+               else {
+                       SetNWCState(IO, eNWCVSAuthFailNTT);
+                       EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
+                       StopClientWatchers(IO, 1);
+                       return QueueDBOperation(IO, SendFailureMessage);
+               }
+               return eAbort;
+       }
+}
+
+eNextState NWC_SendNDOP(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       SetNWCState(IO, eNWCVSNDOP);
+       NW->tempFileName = NewStrBuf();
+       NW->SpoolFileName = NewStrBuf();
+       StrBufPrintf(NW->SpoolFileName,
+                    "%s/%s.%lx%x",
+                    ctdl_netin_dir,
+                    ChrPtr(NW->node),
+                    time(NULL),// TODO: get time from libev
+                    rand());
+       StrBufStripSlashes(NW->SpoolFileName, 1);
+       StrBufPrintf(NW->tempFileName, 
+                    "%s/%s.%lx%x",
+                    ctdl_nettmp_dir,
+                    ChrPtr(NW->node),
+                    time(NULL),// TODO: get time from libev
+                    rand());
+       StrBufStripSlashes(NW->tempFileName, 1);
+       /* We're talking to the correct node.  Now identify ourselves. */
+       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NDOP\n"));
+       NWC_DBG_SEND();
+       return eSendReply;
+}
+
+eNextState NWC_ReadNDOPReply(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       int TotalSendSize;
+       NWC_DBG_READ();
+       if (ChrPtr(NW->IO.IOBuf)[0] == '2')
+       {
+
+               NW->IO.IOB.TotalSentAlready = 0;
+               TotalSendSize = atol (ChrPtr(NW->IO.IOBuf) + 4);
+               EVN_syslog(LOG_DEBUG, "Expecting to transfer %d bytes\n", TotalSendSize);
+               if (TotalSendSize <= 0) {
+                       NW->State = eNUOP - 1;
+               }
+               else {
+                       int fd;
+                       fd = open(ChrPtr(NW->tempFileName), 
+                                 O_EXCL|O_CREAT|O_NONBLOCK|O_WRONLY, 
+                                 S_IRUSR|S_IWUSR);
+                       if (fd < 0)
+                       {
+                               SetNWCState(IO, eNWCVSFail);
+                               EVN_syslog(LOG_CRIT,
+                                      "cannot open %s: %s\n", 
+                                      ChrPtr(NW->tempFileName), 
+                                      strerror(errno));
+
+                               NW->State = eQUIT - 1;
+                               return eAbort;
+                       }
+                       FDIOBufferInit(&NW->IO.IOB, &NW->IO.RecvBuf, fd, TotalSendSize);
+               }
+               return eSendReply;
+       }
+       else
+       {
+               SetNWCState(IO, eNWCVSFail);
+               return eAbort;
+       }
+}
+
+eNextState NWC_SendREAD(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       eNextState rc;
+
+       if (NW->IO.IOB.TotalSentAlready < NW->IO.IOB.TotalSendSize)
+       {
+               /*
+                * If shutting down we can exit here and unlink the temp file.
+                * this shouldn't loose us any messages.
+                */
+               if (server_shutting_down)
+               {
+                       FDIOBufferDelete(&NW->IO.IOB);
+                       unlink(ChrPtr(NW->tempFileName));
+                       FDIOBufferDelete(&IO->IOB);
+                       SetNWCState(IO, eNWCVSFail);
+                       return eAbort;
+               }
+               StrBufPrintf(NW->IO.SendBuf.Buf, "READ %ld|%ld\n",
+                            NW->IO.IOB.TotalSentAlready,
+                            NW->IO.IOB.TotalSendSize);
+/*
+                            ((NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready > IGNET_PACKET_SIZE)
+                             ? IGNET_PACKET_SIZE : 
+                             (NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready))
+                       );
+*/
+               NWC_DBG_SEND();
+               return eSendReply;
+       }
+       else 
+       {
+               NW->State = eCLOS;
+               rc = NWC_DispatchWriteDone(&NW->IO);
+               NWC_DBG_SEND();
+
+               return rc;
+       }
+}
+
+eNextState NWC_ReadREADState(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+       if (ChrPtr(NW->IO.IOBuf)[0] == '6')
+       {
+               NW->IO.IOB.ChunkSendRemain = 
+                       NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
+               return eReadFile;
+       }
+       FDIOBufferDelete(&IO->IOB);
+       return eAbort;
+}
+eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW);
+eNextState NWC_ReadREADBlob(AsyncNetworker *NW)
+{
+       eNextState rc;
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+       if (NW->IO.IOB.TotalSendSize == NW->IO.IOB.TotalSentAlready)
+       {
+               NW->State ++;
+
+               FDIOBufferDelete(&NW->IO.IOB);
+
+               if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
+                       EVN_syslog(LOG_ALERT, 
+                              "Could not link %s to %s: %s\n",
+                              ChrPtr(NW->tempFileName), 
+                              ChrPtr(NW->SpoolFileName), 
+                              strerror(errno));
+               }
+       
+               unlink(ChrPtr(NW->tempFileName));
+               rc = NWC_DispatchWriteDone(&NW->IO);
+               NW->State --;
+               return rc;
+       }
+       else {
+               NW->State --;
+               NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
+               return eSendReply; //NWC_DispatchWriteDone(&NW->IO);
+       }
+}
+
+eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW)
+{
+       eNextState rc;
+       AsyncIO *IO = &NW->IO;
+/* we don't have any data to debug print here. */
+       if (NW->IO.IOB.TotalSentAlready >= NW->IO.IOB.TotalSendSize)
+       {
+               NW->State ++;
+
+               FDIOBufferDelete(&NW->IO.IOB);
+               if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
+                       EVN_syslog(LOG_ALERT, 
+                              "Could not link %s to %s: %s\n",
+                              ChrPtr(NW->tempFileName), 
+                              ChrPtr(NW->SpoolFileName), 
+                              strerror(errno));
+               }
+       
+               unlink(ChrPtr(NW->tempFileName));
+               rc = NWC_DispatchWriteDone(&NW->IO);
+               NW->State --;
+               return rc;
+       }
+       else {
+               NW->State --;
+               NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
+               return NWC_DispatchWriteDone(&NW->IO);
+       }
+}
+eNextState NWC_SendCLOS(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       SetNWCState(IO, eNWCVSNDOPDone);
+       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("CLOS\n"));
+       NWC_DBG_SEND();
+       return eSendReply;
+}
+
+eNextState NWC_ReadCLOSReply(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+       FDIOBufferDelete(&IO->IOB);
+       if (ChrPtr(NW->IO.IOBuf)[0] != '2')
+               return eTerminateConnection;
+       return eSendReply;
+}
+
+
+eNextState NWC_SendNUOP(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       eNextState rc;
+       long TotalSendSize;
+       struct stat statbuf;
+       int fd;
+
+       SetNWCState(IO, eNWCVSNUOP);
+       StrBufPrintf(NW->SpoolFileName,
+                    "%s/%s",
+                    ctdl_netout_dir,
+                    ChrPtr(NW->node));
+       StrBufStripSlashes(NW->SpoolFileName, 1);
+
+       fd = open(ChrPtr(NW->SpoolFileName), O_EXCL|O_NONBLOCK|O_RDONLY);
+       if (fd < 0) {
+               if (errno != ENOENT) {
+                       EVN_syslog(LOG_CRIT,
+                              "cannot open %s: %s\n", 
+                              ChrPtr(NW->SpoolFileName), 
+                              strerror(errno));
+               }
+               NW->State = eQUIT;
+               rc = NWC_SendQUIT(NW);
+               NWC_DBG_SEND();
+               return rc;
+       }
+
+       if (fstat(fd, &statbuf) == -1) {
+               EVN_syslog(LOG_CRIT, "FSTAT FAILED %s [%s]--\n", 
+                          ChrPtr(NW->SpoolFileName), 
+                          strerror(errno));
+               if (fd > 0) close(fd);
+               return eAbort;
+       }
+       TotalSendSize = statbuf.st_size;
+       if (TotalSendSize == 0) {
+               EVNM_syslog(LOG_DEBUG,
+                      "Nothing to send.\n");
+               NW->State = eQUIT;
+               rc = NWC_SendQUIT(NW);
+               NWC_DBG_SEND();
+               if (fd > 0) close(fd);
+               return rc;
+       }
+       FDIOBufferInit(&NW->IO.IOB, &NW->IO.SendBuf, fd, TotalSendSize);
+
+       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NUOP\n"));
+       NWC_DBG_SEND();
+       return eSendReply;
+
+}
+eNextState NWC_ReadNUOPReply(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+       if (ChrPtr(NW->IO.IOBuf)[0] != '2') {
+               FDIOBufferDelete(&IO->IOB);
+               return eAbort;
+       }
+       return eSendReply;
+}
+
+eNextState NWC_SendWRIT(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       StrBufPrintf(NW->IO.SendBuf.Buf, "WRIT %ld\n", 
+                    NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready);
+       NWC_DBG_SEND();
+       return eSendReply;
+}
+eNextState NWC_ReadWRITReply(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+       if (ChrPtr(NW->IO.IOBuf)[0] != '7')
+       {
+               FDIOBufferDelete(&IO->IOB);
+               return eAbort;
+       }
+
+       NW->IO.IOB.ChunkSendRemain = 
+               NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
+       return eSendFile;
+}
+
+eNextState NWC_SendBlobDone(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       eNextState rc;
+       if (NW->IO.IOB.TotalSentAlready >= IO->IOB.TotalSendSize)
+       {
+               NW->State ++;
+
+               FDIOBufferDelete(&IO->IOB);
+               rc =  NWC_DispatchWriteDone(IO);
+               NW->State --;
+               return rc;
+       }
+       else {
+               NW->State --;
+               IO->IOB.ChunkSendRemain = IO->IOB.ChunkSize;
+               rc = NWC_DispatchWriteDone(IO);
+               NW->State --;
+               return rc;
+       }
+}
+
+eNextState NWC_SendUCLS(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("UCLS 1\n"));
+       NWC_DBG_SEND();
+       return eSendReply;
+
+}
+eNextState NWC_ReadUCLS(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+
+       EVN_syslog(LOG_NOTICE, "Sent %ld octets to <%s>\n", NW->IO.IOB.ChunkSize, ChrPtr(NW->node));
+       if (ChrPtr(NW->IO.IOBuf)[0] == '2') {
+               EVN_syslog(LOG_DEBUG, "Removing <%s>\n", ChrPtr(NW->SpoolFileName));
+               unlink(ChrPtr(NW->SpoolFileName));
+       }
+       FDIOBufferDelete(&IO->IOB);
+       SetNWCState(IO, eNWCVSNUOPDone);
+       return eSendReply;
+}
+
+eNextState NWC_SendQUIT(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       StrBufPlain(NW->IO.SendBuf.Buf, HKEY("QUIT\n"));
+
+       NWC_DBG_SEND();
+       return eSendReply;
+}
+
+eNextState NWC_ReadQUIT(AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       NWC_DBG_READ();
+
+       return eAbort;
+}
+
+
+NWClientHandler NWC_ReadHandlers[] = {
+       NWC_ReadGreeting,
+       NWC_ReadAuthReply,
+       NWC_ReadNDOPReply,
+       NWC_ReadREADState,
+       NWC_ReadREADBlob,
+       NWC_ReadCLOSReply,
+       NWC_ReadNUOPReply,
+       NWC_ReadWRITReply,
+       NWC_SendBlobDone,
+       NWC_ReadUCLS,
+       NWC_ReadQUIT};
+
+long NWC_ConnTimeout = 100;
+
+const long NWC_SendTimeouts[] = {
+       100,
+       100,
+       100,
+       100,
+       100,
+       100,
+       100,
+       100
+};
+const ConstStr NWC[] = {
+       {HKEY("Connection broken during ")},
+       {HKEY("Connection broken during ")},
+       {HKEY("Connection broken during ")},
+       {HKEY("Connection broken during ")},
+       {HKEY("Connection broken during ")},
+       {HKEY("Connection broken during ")},
+       {HKEY("Connection broken during ")},
+       {HKEY("Connection broken during ")}
+};
+
+NWClientHandler NWC_SendHandlers[] = {
+       NULL,
+       NWC_SendAuth,
+       NWC_SendNDOP,
+       NWC_SendREAD,
+       NWC_ReadREADBlobDone,
+       NWC_SendCLOS,
+       NWC_SendNUOP,
+       NWC_SendWRIT,
+       NWC_SendBlobDone,
+       NWC_SendUCLS,
+       NWC_SendQUIT
+};
+
+const long NWC_ReadTimeouts[] = {
+       100,
+       100,
+       100,
+       100,
+       100,
+       100,
+       100,
+       100,
+       100,
+       100
+};
+
+
+
+
+eNextState nwc_get_one_host_ip_done(AsyncIO *IO)
+{
+       AsyncNetworker *NW = IO->Data;
+       struct hostent *hostent;
+
+       QueryCbDone(IO);
+
+       hostent = NW->HostLookup.VParsedDNSReply;
+       if ((NW->HostLookup.DNSStatus == ARES_SUCCESS) && 
+           (hostent != NULL) ) {
+               memset(&NW->IO.ConnectMe->Addr, 0, sizeof(struct in6_addr));
+               if (NW->IO.ConnectMe->IPv6) {
+                       memcpy(&NW->IO.ConnectMe->Addr.sin6_addr.s6_addr, 
+                              &hostent->h_addr_list[0],
+                              sizeof(struct in6_addr));
+                       
+                       NW->IO.ConnectMe->Addr.sin6_family = hostent->h_addrtype;
+                       NW->IO.ConnectMe->Addr.sin6_port   = htons(atol(ChrPtr(NW->port)));//// TODO use the one from the URL.
+               }
+               else {
+                       struct sockaddr_in *addr = (struct sockaddr_in*) &NW->IO.ConnectMe->Addr;
+                       /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
+//                     addr->sin_addr.s_addr = htonl((uint32_t)&hostent->h_addr_list[0]);
+                       memcpy(&addr->sin_addr.s_addr, 
+                              hostent->h_addr_list[0], 
+                              sizeof(uint32_t));
+                       
+                       addr->sin_family = hostent->h_addrtype;
+                       addr->sin_port   = htons(504);/// default citadel port
+                       
+               }
+               return nwc_connect_ip(IO);
+       }
+       else
+               return eAbort;
+}
+
+
+eNextState nwc_get_one_host_ip(AsyncIO *IO)
+{
+       AsyncNetworker *NW = IO->Data;
+       /* 
+        * here we start with the lookup of one host.
+        */ 
+
+       EVN_syslog(LOG_DEBUG, "NWC: %s\n", __FUNCTION__);
+
+       EVN_syslog(LOG_DEBUG, 
+                  "NWC client[%ld]: looking up %s-Record %s : %d ...\n", 
+                  NW->n, 
+                  (NW->IO.ConnectMe->IPv6)? "aaaa": "a",
+                  NW->IO.ConnectMe->Host, 
+                  NW->IO.ConnectMe->Port);
+
+       QueueQuery((NW->IO.ConnectMe->IPv6)? ns_t_aaaa : ns_t_a, 
+                  NW->IO.ConnectMe->Host, 
+                  &NW->IO, 
+                  &NW->HostLookup, 
+                  nwc_get_one_host_ip_done);
+       IO->NextState = eReadDNSReply;
+       return IO->NextState;
+}
+/**
+ * @brief lineread Handler; understands when to read more POP3 lines, and when this is a one-lined reply.
+ */
+eReadState NWC_ReadServerStatus(AsyncIO *IO)
+{
+//     AsyncNetworker *NW = IO->Data;
+       eReadState Finished = eBufferNotEmpty; 
+
+       switch (IO->NextState) {
+       case eSendDNSQuery:
+       case eReadDNSReply:
+       case eDBQuery:
+       case eConnect:
+       case eTerminateConnection:
+       case eAbort:
+               Finished = eReadFail;
+               break;
+       case eSendReply: 
+       case eSendMore:
+       case eReadMore:
+       case eReadMessage: 
+               Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
+               break;
+       case eReadFile:
+       case eSendFile:
+       case eReadPayload:
+               break;
+       }
+       return Finished;
+}
+
+
+
+eNextState NWC_FailNetworkConnection(AsyncIO *IO)
+{
+       SetNWCState(IO, eNWCVSConnFail);
+       StopClientWatchers(IO, 1);
+       return QueueDBOperation(IO, SendFailureMessage);
+}
+
+void NWC_SetTimeout(eNextState NextTCPState, AsyncNetworker *NW)
+{
+       AsyncIO *IO = &NW->IO;
+       double Timeout = 0.0;
+
+       EVN_syslog(LOG_DEBUG, "%s - %d\n", __FUNCTION__, NextTCPState);
+
+       switch (NextTCPState) {
+       case eSendReply:
+       case eSendMore:
+               break;
+       case eReadFile:
+       case eReadMessage:
+               Timeout = NWC_ReadTimeouts[NW->State];
+               break;
+       case eReadPayload:
+               Timeout = 100000;
+               /* TODO!!! */
+               break;
+       case eSendDNSQuery:
+       case eReadDNSReply:
+       case eConnect:
+       case eSendFile:
+//TODO
+       case eTerminateConnection:
+       case eDBQuery:
+       case eAbort:
+       case eReadMore://// TODO
+               return;
+       }
+       if (Timeout > 0) {
+               EVN_syslog(LOG_DEBUG, 
+                          "%s - %d %f\n",
+                          __FUNCTION__,
+                          NextTCPState,
+                          Timeout);
+               SetNextTimeout(&NW->IO, Timeout*100);
+       }
+}
+
+
+eNextState NWC_DispatchReadDone(AsyncIO *IO)
+{
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+       AsyncNetworker *NW = IO->Data;
+       eNextState rc;
+
+       rc = NWC_ReadHandlers[NW->State](NW);
+       if (rc != eReadMore)
+               NW->State++;
+       NWC_SetTimeout(rc, NW);
+       return rc;
+}
+eNextState NWC_DispatchWriteDone(AsyncIO *IO)
+{
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+       AsyncNetworker *NW = IO->Data;
+       eNextState rc;
+
+       rc = NWC_SendHandlers[NW->State](NW);
+       NWC_SetTimeout(rc, NW);
+       return rc;
+}
+
+/*****************************************************************************/
+/*                     Networker CLIENT ERROR CATCHERS                       */
+/*****************************************************************************/
+eNextState NWC_Terminate(AsyncIO *IO)
+{
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+       FinalizeNetworker(IO);
+       return eAbort;
+}
+
+eNextState NWC_TerminateDB(AsyncIO *IO)
+{
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+       FinalizeNetworker(IO);
+       return eAbort;
+}
+
+eNextState NWC_Timeout(AsyncIO *IO)
+{
+       AsyncNetworker *NW = IO->Data;
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+
+       if (NW->IO.ErrMsg == NULL)
+               NW->IO.ErrMsg = NewStrBuf();
+       StrBufPrintf(NW->IO.ErrMsg, "Timeout while talking to %s \r\n", ChrPtr(NW->host));
+       return NWC_FailNetworkConnection(IO);
+}
+eNextState NWC_ConnFail(AsyncIO *IO)
+{
+       AsyncNetworker *NW = IO->Data;
+
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+       if (NW->IO.ErrMsg == NULL)
+               NW->IO.ErrMsg = NewStrBuf();
+       StrBufPrintf(NW->IO.ErrMsg, "failed to connect %s \r\n", ChrPtr(NW->host));
+
+       return NWC_FailNetworkConnection(IO);
+}
+eNextState NWC_DNSFail(AsyncIO *IO)
+{
+       AsyncNetworker *NW = IO->Data;
+
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+       if (NW->IO.ErrMsg == NULL)
+               NW->IO.ErrMsg = NewStrBuf();
+       StrBufPrintf(NW->IO.ErrMsg, "failed to look up %s \r\n", ChrPtr(NW->host));
+
+       return NWC_FailNetworkConnection(IO);
+}
+eNextState NWC_Shutdown(AsyncIO *IO)
+{
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+
+       FinalizeNetworker(IO);
+       return eAbort;
+}
+
+
+eNextState nwc_connect_ip(AsyncIO *IO)
+{
+       AsyncNetworker *NW = IO->Data;
+
+       SetNWCState(&NW->IO, eNWCVSConnecting);
+       EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+       EVN_syslog(LOG_NOTICE, "Connecting to <%s> at %s:%s\n", 
+                  ChrPtr(NW->node), 
+                  ChrPtr(NW->host),
+                  ChrPtr(NW->port));
+       
+       return EvConnectSock(IO,
+                            NWC_ConnTimeout,
+                            NWC_ReadTimeouts[0],
+                            1);
+}
+
+static int NetworkerCount = 0;
+void RunNetworker(AsyncNetworker *NW)
+{
+       NW->n = NetworkerCount++;
+       CtdlNetworkTalkingTo(SKEY(NW->node), NTT_ADD);
+       syslog(LOG_DEBUG, "NW[%s][%ld]: polling\n", ChrPtr(NW->node), NW->n);
+       ParseURL(&NW->IO.ConnectMe, NW->Url, 504);
+
+       InitIOStruct(&NW->IO,
+                    NW,
+                    eReadMessage,
+                    NWC_ReadServerStatus,
+                    NWC_DNSFail,
+                    NWC_DispatchWriteDone,
+                    NWC_DispatchReadDone,
+                    NWC_Terminate,
+                    NWC_TerminateDB,
+                    NWC_ConnFail,
+                    NWC_Timeout,
+                    NWC_Shutdown);
+
+       safestrncpy(((CitContext *)NW->IO.CitContext)->cs_host, 
+                   ChrPtr(NW->host),
+                   sizeof(((CitContext *)NW->IO.CitContext)->cs_host)); 
+
+       if (NW->IO.ConnectMe->IsIP) {
+               SetNWCState(&NW->IO, eNWCVSLookup);
+               QueueEventContext(&NW->IO,
+                                 nwc_connect_ip);
+       }
+       else { /* uneducated admin has chosen to add DNS to the equation... */
+               SetNWCState(&NW->IO, eNWCVSConnecting);
+               QueueEventContext(&NW->IO,
+                                 nwc_get_one_host_ip);
+       }
+}
+
+/*
+ * Poll other Citadel nodes and transfer inbound/outbound network data.
+ * Set "full" to nonzero to force a poll of every node, or to zero to poll
+ * only nodes to which we have data to send.
+ */
+void network_poll_other_citadel_nodes(int full_poll, HashList *ignetcfg)
+{
+       const char *key;
+       long len;
+       HashPos *Pos;
+       void *vCfg;
+       AsyncNetworker *NW;
+       StrBuf *SpoolFileName;
+       
+       int poll = 0;
+       
+       if (GetCount(ignetcfg) ==0) {
+               syslog(LOG_DEBUG, "network: no neighbor nodes are configured - not polling.\n");
+               return;
+       }
+       become_session(&networker_client_CC);
+
+       SpoolFileName = NewStrBufPlain(ctdl_netout_dir, -1);
+
+       Pos = GetNewHashPos(ignetcfg, 0);
+
+       while (GetNextHashPos(ignetcfg, Pos, &len, &key, &vCfg))
+       {
+               /* Use the string tokenizer to grab one line at a time */
+               if(server_shutting_down)
+                       return;/* TODO free stuff*/
+               CtdlNodeConf *pNode = (CtdlNodeConf*) vCfg;
+               poll = 0;
+               NW = (AsyncNetworker*)malloc(sizeof(AsyncNetworker));
+               memset(NW, 0, sizeof(AsyncNetworker));
+               
+               NW->node = NewStrBufDup(pNode->NodeName);
+               NW->host = NewStrBufDup(pNode->Host);
+               NW->port = NewStrBufDup(pNode->Port);
+               NW->secret = NewStrBufDup(pNode->Secret);
+               
+               if ( (StrLength(NW->node) != 0) && 
+                    (StrLength(NW->secret) != 0) &&
+                    (StrLength(NW->host) != 0) &&
+                    (StrLength(NW->port) != 0))
+               {
+                       poll = full_poll;
+                       if (poll == 0)
+                       {
+                               StrBufAppendBufPlain(SpoolFileName, HKEY("/"), 0);
+                               StrBufAppendBuf(SpoolFileName, NW->node, 0);
+                               StrBufStripSlashes(SpoolFileName, 1);
+                               
+                               if (access(ChrPtr(SpoolFileName), R_OK) == 0) {
+                                       poll = 1;
+                               }
+                       }
+               }
+               if (poll && 
+                   (StrLength(NW->host) > 0) && 
+                   strcmp("0.0.0.0", ChrPtr(NW->host)))
+               {
+                       NW->Url = NewStrBuf();
+                       StrBufPrintf(NW->Url, "citadel://%s@%s:%s", 
+                                    ChrPtr(NW->secret),
+                                    ChrPtr(NW->host),
+                                    ChrPtr(NW->port));
+                       if (!CtdlNetworkTalkingTo(SKEY(NW->node), NTT_CHECK))
+                       {
+                               RunNetworker(NW);
+                               continue;
+                       }
+               }
+               DeleteNetworker(NW);
+       }
+       FreeStrBuf(&SpoolFileName);
+       DeleteHashPos(&Pos);
+}
+
+
+void network_do_clientqueue(void)
+{
+       HashList *working_ignetcfg;
+       int full_processing = 1;
+       static time_t last_run = 0L;
+
+       /*
+        * Run the full set of processing tasks no more frequently
+        * than once every n seconds
+        */
+       if ( (time(NULL) - last_run) < config.c_net_freq ) {
+               full_processing = 0;
+               syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
+                       config.c_net_freq - (time(NULL)- last_run)
+               );
+       }
+
+       working_ignetcfg = CtdlLoadIgNetCfg();
+       /*
+        * Poll other Citadel nodes.  Maybe.  If "full_processing" is set
+        * then we poll everyone.  Otherwise we only poll nodes we have stuff
+        * to send to.
+        */
+       network_poll_other_citadel_nodes(full_processing, working_ignetcfg);
+       DeleteHash(&working_ignetcfg);
+}
+
+void LogDebugEnableNetworkClient(const int n)
+{
+       NetworkClientDebugEnabled = n;
+}
+/*
+ * Module entry point
+ */
+CTDL_MODULE_INIT(network_client)
+{
+       if (!threading)
+       {
+               CtdlFillSystemContext(&networker_client_CC, "CitNetworker");
+               
+               CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER, PRIO_SEND + 10);
+               CtdlRegisterDebugFlagHook(HKEY("networkclient"), LogDebugEnableNetworkClient, &NetworkClientDebugEnabled);
+
+       }
+       return "networkclient";
+}
index 433309f38895ed0d9e6877f510ad40848e985d3c..25c835c7561022bd276c5e8093b879101a1ce8d6 100644 (file)
@@ -983,22 +983,10 @@ int pop3_do_fetching(pop3aggr *cpptr)
 /*
  * Scan a room's netconfig to determine whether it requires POP3 aggregation
  */
-void pop3client_scan_room(struct ctdlroom *qrbuf, void *data)
+void pop3client_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCFG)
 {
-       StrBuf *CfgData;
-       StrBuf *CfgType;
-       StrBuf *Line;
-
-       struct stat statbuf;
-       char filename[PATH_MAX];
-       int  fd;
-       int Done;
+       const RoomNetCfgLine *pLine;
        void *vptr;
-       const char *CfgPtr, *lPtr;
-       const char *Err;
-
-//     pop3_room_counter *Count = NULL;
-//     pop3aggr *cpptr;
 
        pthread_mutex_lock(&POP3QueueMutex);
        if (GetHash(POP3QueueRooms, LKEY(qrbuf->QRnumber), &vptr))
@@ -1014,167 +1002,87 @@ void pop3client_scan_room(struct ctdlroom *qrbuf, void *data)
 
        if (server_shutting_down) return;
 
-       assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
+       pLine = OneRNCFG->NetConfigs[pop3client];
 
-       if (server_shutting_down)
-               return;
+       while (pLine != NULL)
+       {
+               pop3aggr *cptr;
 
-       /* Only do net processing for rooms that have netconfigs */
-       fd = open(filename, 0);
-       if (fd <= 0) {
-               return;
-       }
-       if (server_shutting_down)
-               return;
-       if (fstat(fd, &statbuf) == -1) {
-               EVP3CQ_syslog(LOG_INFO,
-                             "ERROR: could not stat configfile '%s' - %s",
-                             filename,
-                             strerror(errno));
-               return;
-       }
-       if (server_shutting_down)
-               return;
-       CfgData = NewStrBufPlain(NULL, statbuf.st_size + 1);
-       if (StrBufReadBLOB(CfgData, &fd, 1, statbuf.st_size, &Err) < 0) {
-               close(fd);
-               FreeStrBuf(&CfgData);
-               EVP3CQ_syslog(LOG_INFO,
-                             "ERROR: reading config '%s' - %s",
-                             filename, strerror(errno));
-               return;
-       }
-       close(fd);
-       if (server_shutting_down)
-               return;
+               cptr = (pop3aggr *) malloc(sizeof(pop3aggr));
+               memset(cptr, 0, sizeof(pop3aggr));
+               ///TODO do we need this? cptr->roomlist_parts=1;
+               cptr->RoomName = NewStrBufPlain(qrbuf->QRname, -1);
+               cptr->pop3user = NewStrBufDup(pLine->Value[1]);
+               cptr->pop3pass = NewStrBufDup(pLine->Value[2]);
+               cptr->Url = NewStrBuf();
+               cptr->Host = NewStrBufDup(pLine->Value[0]);
 
-       CfgPtr = NULL;
-       CfgType = NewStrBuf();
-       Line = NewStrBufPlain(NULL, StrLength(CfgData));
-       Done = 0;
+               cptr->keep = atol(ChrPtr(pLine->Value[3]));
+               cptr->interval = atol(ChrPtr(pLine->Value[4]));
 
-       while (!Done)
-       {
-               Done = StrBufSipLine(Line, CfgData, &CfgPtr) == 0;
-               if (StrLength(Line) > 0)
-               {
-                       lPtr = NULL;
-                       StrBufExtract_NextToken(CfgType, Line, &lPtr, '|');
-                       if (!strcasecmp("pop3client", ChrPtr(CfgType)))
-                       {
-                               pop3aggr *cptr;
-/*
-                               if (Count == NULL)
-                               {
-                               Count = malloc(sizeof(pop3_room_counter));
-                                       Count->count = 0;
-                               }
-                               Count->count ++;
-*/
-                               cptr = (pop3aggr *) malloc(sizeof(pop3aggr));
-                               memset(cptr, 0, sizeof(pop3aggr));
-                               ///TODO do we need this? cptr->roomlist_parts=1;
-                               cptr->RoomName =
-                                       NewStrBufPlain(qrbuf->QRname, -1);
-                               cptr->pop3user =
-                                       NewStrBufPlain(NULL, StrLength(Line));
-                               cptr->pop3pass =
-                                       NewStrBufPlain(NULL, StrLength(Line));
-                               cptr->Url = NewStrBuf();
-                               cptr->Host =
-                                       NewStrBufPlain(NULL, StrLength(Line));
-
-                               StrBufExtract_NextToken(cptr->Host, Line, &lPtr, '|');
-                               StrBufExtract_NextToken(cptr->pop3user,
-                                                       Line,
-                                                       &lPtr,
-                                                       '|');
-
-                               StrBufExtract_NextToken(cptr->pop3pass,
-                                                       Line,
-                                                       &lPtr,
-                                                       '|');
-
-                               cptr->keep = StrBufExtractNext_long(Line,
-                                                                   &lPtr,
-                                                                   '|');
-
-                               cptr->interval = StrBufExtractNext_long(Line,
-                                                                       &lPtr,
-                                                                       '|');
-
-                               StrBufAppendBufPlain(cptr->Url, HKEY("pop3://"), 0);
-                               StrBufUrlescUPAppend(cptr->Url, cptr->pop3user, NULL);
-                               StrBufAppendBufPlain(cptr->Url, HKEY(":"), 0);
-                               StrBufUrlescUPAppend(cptr->Url, cptr->pop3pass, NULL);
-                               StrBufAppendBufPlain(cptr->Url, HKEY("@"), 0);
-                               StrBufAppendBuf(cptr->Url, cptr->Host, 0);
-                               StrBufAppendBufPlain(cptr->Url, HKEY("/"), 0);
-                               StrBufUrlescAppend(cptr->Url, cptr->RoomName, NULL);
-
-                               ParseURL(&cptr->IO.ConnectMe, cptr->Url, 110);
+               StrBufAppendBufPlain(cptr->Url, HKEY("pop3://"), 0);
+               StrBufUrlescUPAppend(cptr->Url, cptr->pop3user, NULL);
+               StrBufAppendBufPlain(cptr->Url, HKEY(":"), 0);
+               StrBufUrlescUPAppend(cptr->Url, cptr->pop3pass, NULL);
+               StrBufAppendBufPlain(cptr->Url, HKEY("@"), 0);
+               StrBufAppendBuf(cptr->Url, cptr->Host, 0);
+               StrBufAppendBufPlain(cptr->Url, HKEY("/"), 0);
+               StrBufUrlescAppend(cptr->Url, cptr->RoomName, NULL);
+
+               ParseURL(&cptr->IO.ConnectMe, cptr->Url, 110);
 
 
 #if 0
 /* todo: we need to reunite the url to be shure. */
 
-                               pthread_mutex_lock(&POP3ueueMutex);
-                               GetHash(POP3FetchUrls, SKEY(ptr->Url), &vptr);
-                               use_this_cptr = (pop3aggr *)vptr;
+               pthread_mutex_lock(&POP3ueueMutex);
+               GetHash(POP3FetchUrls, SKEY(ptr->Url), &vptr);
+               use_this_cptr = (pop3aggr *)vptr;
 
-                               if (use_this_rncptr != NULL)
-                               {
-                                       /* mustn't attach to an active session */
-                                       if (use_this_cptr->RefCount > 0)
-                                       {
-                                               DeletePOP3Cfg(cptr);
+               if (use_this_rncptr != NULL)
+               {
+                       /* mustn't attach to an active session */
+                       if (use_this_cptr->RefCount > 0)
+                       {
+                               DeletePOP3Cfg(cptr);
 ///                                            Count->count--;
-                                       }
-                                       else
-                                       {
-                                               long *QRnumber;
-                                               StrBufAppendBufPlain(
-                                                       use_this_cptr->rooms,
-                                                       qrbuf->QRname,
-                                                       -1, 0);
-                                               if (use_this_cptr->roomlist_parts == 1)
-                                               {
-                                                       use_this_cptr->OtherQRnumbers
-                                                               = NewHash(1, lFlathash);
-                                               }
-                                               QRnumber = (long*)malloc(sizeof(long));
-                                               *QRnumber = qrbuf->QRnumber;
-                                               Put(use_this_cptr->OtherQRnumbers,
-                                                   LKEY(qrbuf->QRnumber),
-                                                   QRnumber,
-                                                   NULL);
-
-                                               use_this_cptr->roomlist_parts++;
-                                       }
-                                       pthread_mutex_unlock(&POP3QueueMutex);
-                                       continue;
+                       }
+                       else
+                       {
+                               long *QRnumber;
+                               StrBufAppendBufPlain(
+                                       use_this_cptr->rooms,
+                                       qrbuf->QRname,
+                                       -1, 0);
+                               if (use_this_cptr->roomlist_parts == 1)
+                               {
+                                       use_this_cptr->OtherQRnumbers
+                                               = NewHash(1, lFlathash);
                                }
-                               pthread_mutex_unlock(&RSSQueueMutex);
-#endif
-                               cptr->n = Pop3ClientID++;
-                               pthread_mutex_lock(&POP3QueueMutex);
-                               Put(POP3FetchUrls,
-                                   SKEY(cptr->Url),
-                                   cptr,
-                                   DeletePOP3Aggregator);
-
-                               pthread_mutex_unlock(&POP3QueueMutex);
-
+                               QRnumber = (long*)malloc(sizeof(long));
+                               *QRnumber = qrbuf->QRnumber;
+                               Put(use_this_cptr->OtherQRnumbers,
+                                   LKEY(qrbuf->QRnumber),
+                                   QRnumber,
+                                   NULL);
+
+                               use_this_cptr->roomlist_parts++;
                        }
-
+                       pthread_mutex_unlock(&POP3QueueMutex);
+                       continue;
                }
+               pthread_mutex_unlock(&RSSQueueMutex);
+#endif
+               cptr->n = Pop3ClientID++;
+               pthread_mutex_lock(&POP3QueueMutex);
+               Put(POP3FetchUrls,
+                   SKEY(cptr->Url),
+                   cptr,
+                   DeletePOP3Aggregator);
 
-               ///fclose(fp);
+               pthread_mutex_unlock(&POP3QueueMutex);
 
        }
-       FreeStrBuf(&Line);
-       FreeStrBuf(&CfgType);
-       FreeStrBuf(&CfgData);
 }
 
 static int doing_pop3client = 0;
@@ -1212,7 +1120,7 @@ void pop3client_scan(void) {
        doing_pop3client = 1;
 
        EVP3CQM_syslog(LOG_DEBUG, "pop3client started");
-       CtdlForEachRoom(pop3client_scan_room, NULL);
+       CtdlForEachNetCfgRoom(pop3client_scan_room, NULL, pop3client);
 
        pthread_mutex_lock(&POP3QueueMutex);
        it = GetNewHashPos(POP3FetchUrls, 0);
@@ -1263,6 +1171,7 @@ CTDL_MODULE_INIT(pop3client)
        if (!threading)
        {
                CtdlFillSystemContext(&pop3_client_CC, "POP3aggr");
+               CtdlREGISTERRoomCfgType(pop3client, ParseGeneric, 0, 5, SerializeGeneric, DeleteGenericCfgLine);
                pthread_mutex_init(&POP3QueueMutex, NULL);
                POP3QueueRooms = NewHash(1, lFlathash);
                POP3FetchUrls = NewHash(1, NULL);
index 82e206792bb77ea32d404e609769be0911341929..c2d95d34a88de0e68b0510524129fcfccafa16ba 100644 (file)
@@ -556,21 +556,13 @@ int rss_do_fetching(rss_aggregator *RSSAggr)
 /*
  * Scan a room's netconfig to determine whether it is requesting any RSS feeds
  */
-void rssclient_scan_room(struct ctdlroom *qrbuf, void *data)
+void rssclient_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCFG)
 {
-       StrBuf *CfgData=NULL;
-       StrBuf *CfgType;
-       StrBuf *Line;
+       const RoomNetCfgLine *pLine;
        rss_room_counter *Count = NULL;
-       struct stat statbuf;
-       char filename[PATH_MAX];
-       int fd;
-       int Done;
        rss_aggregator *RSSAggr = NULL;
        rss_aggregator *use_this_RSSAggr = NULL;
        void *vptr;
-       const char *CfgPtr, *lPtr;
-       const char *Err;
 
        pthread_mutex_lock(&RSSQueueMutex);
        if (GetHash(RSSQueueRooms, LKEY(qrbuf->QRnumber), &vptr))
@@ -584,143 +576,76 @@ void rssclient_scan_room(struct ctdlroom *qrbuf, void *data)
        }
        pthread_mutex_unlock(&RSSQueueMutex);
 
-       assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
+       if (server_shutting_down) return;
 
-       if (server_shutting_down)
-               return;
-
-       /* Only do net processing for rooms that have netconfigs */
-       fd = open(filename, 0);
-       if (fd <= 0) {
-               /* syslog(LOG_DEBUG,
-                  "rssclient: %s no config.\n",
-                  qrbuf->QRname); */
-               return;
-       }
-
-       if (server_shutting_down)
-               return;
-
-       if (fstat(fd, &statbuf) == -1) {
-               EVRSSQ_syslog(LOG_DEBUG,
-                             "ERROR: could not stat configfile '%s' - %s\n",
-                             filename,
-                             strerror(errno));
-               return;
-       }
+       pLine = OneRNCFG->NetConfigs[pop3client];
 
-       if (server_shutting_down)
-               return;
+       while (pLine != NULL)
+       {
+               if (Count == NULL)
+               {
+                       Count = malloc(
+                               sizeof(rss_room_counter));
+                       Count->count = 0;
+               }
+               Count->count ++;
+               RSSAggr = (rss_aggregator *) malloc(
+                       sizeof(rss_aggregator));
 
-       CfgData = NewStrBufPlain(NULL, statbuf.st_size + 1);
+               memset (RSSAggr, 0, sizeof(rss_aggregator));
+               RSSAggr->QRnumber = qrbuf->QRnumber;
+               RSSAggr->roomlist_parts = 1;
+               RSSAggr->Url = NewStrBufDup(pLine->Value[1]);
 
-       if (StrBufReadBLOB(CfgData, &fd, 1, statbuf.st_size, &Err) < 0) {
-               close(fd);
-               FreeStrBuf(&CfgData);
-               EVRSSQ_syslog(LOG_ERR, "ERROR: reading config '%s' - %s<br>\n",
-                             filename, strerror(errno));
-               return;
-       }
-       close(fd);
-       if (server_shutting_down)
-               return;
+               pthread_mutex_lock(&RSSQueueMutex);
+               GetHash(RSSFetchUrls,
+                       SKEY(RSSAggr->Url),
+                       &vptr);
 
-       CfgPtr = NULL;
-       CfgType = NewStrBuf();
-       Line = NewStrBufPlain(NULL, StrLength(CfgData));
-       Done = 0;
-       while (!Done)
-       {
-               Done = StrBufSipLine(Line, CfgData, &CfgPtr) == 0;
-               if (StrLength(Line) > 0)
+               use_this_RSSAggr = (rss_aggregator *)vptr;
+               if (use_this_RSSAggr != NULL)
                {
-                       lPtr = NULL;
-                       StrBufExtract_NextToken(CfgType, Line, &lPtr, '|');
-                       if (!strcasecmp("rssclient", ChrPtr(CfgType)))
+                       long *QRnumber;
+                       StrBufAppendBufPlain(
+                               use_this_RSSAggr->rooms,
+                               qrbuf->QRname,
+                               -1, 0);
+                       if (use_this_RSSAggr->roomlist_parts==1)
                        {
-                               if (Count == NULL)
-                               {
-                                       Count = malloc(
-                                               sizeof(rss_room_counter));
-                                       Count->count = 0;
-                               }
-                               Count->count ++;
-                               RSSAggr = (rss_aggregator *) malloc(
-                                       sizeof(rss_aggregator));
-
-                               memset (RSSAggr, 0, sizeof(rss_aggregator));
-                               RSSAggr->QRnumber = qrbuf->QRnumber;
-                               RSSAggr->roomlist_parts = 1;
-                               RSSAggr->Url = NewStrBuf();
-
-                               StrBufExtract_NextToken(RSSAggr->Url,
-                                                       Line,
-                                                       &lPtr,
-                                                       '|');
-
-                               pthread_mutex_lock(&RSSQueueMutex);
-                               GetHash(RSSFetchUrls,
-                                       SKEY(RSSAggr->Url),
-                                       &vptr);
-
-                               use_this_RSSAggr = (rss_aggregator *)vptr;
-                               if (use_this_RSSAggr != NULL)
-                               {
-                                       long *QRnumber;
-                                       StrBufAppendBufPlain(
-                                               use_this_RSSAggr->rooms,
-                                               qrbuf->QRname,
-                                               -1, 0);
-                                       if (use_this_RSSAggr->roomlist_parts==1)
-                                       {
-                                               use_this_RSSAggr->OtherQRnumbers
-                                                       = NewHash(1, lFlathash);
-                                       }
-                                       QRnumber = (long*)malloc(sizeof(long));
-                                       *QRnumber = qrbuf->QRnumber;
-                                       Put(use_this_RSSAggr->OtherQRnumbers,
-                                           LKEY(qrbuf->QRnumber),
-                                           QRnumber,
-                                           NULL);
-                                       use_this_RSSAggr->roomlist_parts++;
-
-                                       pthread_mutex_unlock(&RSSQueueMutex);
-
-                                       FreeStrBuf(&RSSAggr->Url);
-                                       free(RSSAggr);
-                                       RSSAggr = NULL;
-                                       continue;
-                               }
-                               pthread_mutex_unlock(&RSSQueueMutex);
-
-                               RSSAggr->ItemType = RSS_UNSET;
-
-                               RSSAggr->rooms = NewStrBufPlain(
-                                       qrbuf->QRname, -1);
-
-                               pthread_mutex_lock(&RSSQueueMutex);
-
-                               Put(RSSFetchUrls,
-                                   SKEY(RSSAggr->Url),
-                                   RSSAggr,
-                                   DeleteRssCfg);
-
-                               pthread_mutex_unlock(&RSSQueueMutex);
+                               use_this_RSSAggr->OtherQRnumbers
+                                       = NewHash(1, lFlathash);
                        }
+                       QRnumber = (long*)malloc(sizeof(long));
+                       *QRnumber = qrbuf->QRnumber;
+                       Put(use_this_RSSAggr->OtherQRnumbers,
+                           LKEY(qrbuf->QRnumber),
+                           QRnumber,
+                           NULL);
+                       use_this_RSSAggr->roomlist_parts++;
+
+                       pthread_mutex_unlock(&RSSQueueMutex);
+
+                       FreeStrBuf(&RSSAggr->Url);
+                       free(RSSAggr);
+                       RSSAggr = NULL;
+                       continue;
                }
-       }
-       if (Count != NULL)
-       {
-               Count->QRnumber = qrbuf->QRnumber;
+               pthread_mutex_unlock(&RSSQueueMutex);
+
+               RSSAggr->ItemType = RSS_UNSET;
+
+               RSSAggr->rooms = NewStrBufPlain(
+                       qrbuf->QRname, -1);
+
                pthread_mutex_lock(&RSSQueueMutex);
-               EVRSSQ_syslog(LOG_DEBUG, "client: [%ld] %s now starting.\n",
-                             qrbuf->QRnumber, qrbuf->QRname);
-               Put(RSSQueueRooms, LKEY(qrbuf->QRnumber), Count, NULL);
+
+               Put(RSSFetchUrls,
+                   SKEY(RSSAggr->Url),
+                   RSSAggr,
+                   DeleteRssCfg);
+
                pthread_mutex_unlock(&RSSQueueMutex);
        }
-       FreeStrBuf(&CfgData);
-       FreeStrBuf(&CfgType);
-       FreeStrBuf(&Line);
 }
 
 /*
@@ -764,7 +689,7 @@ void rssclient_scan(void) {
 
        become_session(&rss_CC);
        EVRSSQM_syslog(LOG_DEBUG, "rssclient started\n");
-       CtdlForEachRoom(rssclient_scan_room, NULL);
+       CtdlForEachNetCfgRoom(rssclient_scan_room, NULL, rssclient);
 
        pthread_mutex_lock(&RSSQueueMutex);
 
@@ -797,9 +722,9 @@ void LogDebugEnableRSSClient(const int n)
 
 CTDL_MODULE_INIT(rssclient)
 {
-       if (threading)
+       if (!threading)
        {
-               CtdlFillSystemContext(&rss_CC, "rssclient");
+               CtdlREGISTERRoomCfgType(rssclient, ParseGeneric, 0, 1, SerializeGeneric, DeleteGenericCfgLine); /// todo: implement rss specific parser
                pthread_mutex_init(&RSSQueueMutex, NULL);
                RSSQueueRooms = NewHash(1, lFlathash);
                RSSFetchUrls = NewHash(1, NULL);
@@ -808,5 +733,9 @@ CTDL_MODULE_INIT(rssclient)
                CtdlRegisterEVCleanupHook(rss_cleanup);
                CtdlRegisterDebugFlagHook(HKEY("rssclient"), LogDebugEnableRSSClient, &RSSClientDebugEnabled);
        }
+       else
+       {
+               CtdlFillSystemContext(&rss_CC, "rssclient");
+       }
        return "rssclient";
 }
index edcbd4cce6bcade7bd3b79d1174188163e120e4a..0952d6d3b931ce65ef0b414728ab3368e786780e 100644 (file)
@@ -1492,6 +1492,10 @@ CTDL_MODULE_INIT(vcard)
                        if (rv == -1)
                                syslog(LOG_EMERG, "Failed to adjust ownership of: %s [%s]", 
                                       filename, strerror(errno));
+                       rv = chmod(filename, 0600);
+                       if (rv == -1)
+                               syslog(LOG_EMERG, "Failed to adjust ownership of: %s [%s]", 
+                                      filename, strerror(errno));
                }
 
                /* for postfix tcpdict */
index dc407afdc369ff88aad6d06684dd39da9cb03b53..0b8e8ba9194912f4f63c707949b1f8d6f00e3dbf 100644 (file)
@@ -1312,6 +1312,49 @@ void CtdlFreeMessage(struct CtdlMessage *msg)
        free(msg);
 }
 
+int DupCMField(int i, struct CtdlMessage *OrgMsg, struct CtdlMessage *NewMsg)
+{
+       long len;
+       len = strlen(OrgMsg->cm_fields[i]);
+       NewMsg->cm_fields[i] = malloc(len + 1);
+       if (NewMsg->cm_fields[i] == NULL)
+               return 0;
+       memcpy(NewMsg->cm_fields[i], OrgMsg->cm_fields[i], len);
+       NewMsg->cm_fields[i][len] = '\0';
+       return 1;
+}
+
+struct CtdlMessage * CtdlDuplicateMessage(struct CtdlMessage *OrgMsg)
+{
+       int i;
+       struct CtdlMessage *NewMsg;
+
+       if (is_valid_message(OrgMsg) == 0) 
+               return NULL;
+       NewMsg = (struct CtdlMessage *)malloc(sizeof(struct CtdlMessage));
+       if (NewMsg == NULL)
+               return NULL;
+
+       memcpy(NewMsg, OrgMsg, sizeof(struct CtdlMessage));
+
+       memset(&NewMsg->cm_fields, 0, sizeof(char*) * 256);
+       
+       for (i = 0; i < 256; ++i)
+       {
+               if (OrgMsg->cm_fields[i] != NULL)
+               {
+                       if (!DupCMField(i, OrgMsg, NewMsg))
+                       {
+                               CtdlFreeMessage(NewMsg);
+                               return NULL;
+                       }
+               }
+       }
+
+       return NewMsg;
+}
+
+
 
 /*
  * Pre callback function for multipart/alternative
@@ -3499,7 +3542,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
  */
 void quickie_message(const char *from,
                     const char *fromaddr,
-                    char *to,
+                    const char *to,
                     char *room,
                     const char *text, 
                     int format_type,
@@ -3546,7 +3589,7 @@ void quickie_message(const char *from,
 
 void flood_protect_quickie_message(const char *from,
                                   const char *fromaddr,
-                                  char *to,
+                                  const char *to,
                                   char *room,
                                   const char *text, 
                                   int format_type,
@@ -4011,11 +4054,6 @@ struct CtdlMessage *CtdlMakeMessage(
        return(msg);
 }
 
-extern int netconfig_check_roomaccess(
-       char *errmsgbuf, 
-       size_t n,
-       const char* RemoteIdentifier); /* TODO: find a smarter way */
-
 /*
  * Check to see whether we have permission to post a message in the current
  * room.  Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
@@ -4050,7 +4088,7 @@ int CtdlDoIHavePermissionToPostInThisRoom(
                }
                if ((PostPublic!=POST_LMTP) &&(CC->room.QRflags2 & QR2_SMTP_PUBLIC) == 0) {
 
-                       return netconfig_check_roomaccess(errmsgbuf, n, RemoteIdentifier);
+                       return CtdlNetconfigCheckRoomaccess(errmsgbuf, n, RemoteIdentifier);
                }
                return (0);
 
index 888b62725719547e3b1d597a52cbe02d5e5c27ec..45a7f765cac64776a0122c4cb1ba1e8c4754439d 100644 (file)
@@ -114,7 +114,7 @@ long CtdlSubmitMsg(struct CtdlMessage *, struct recptypes *, const char *, int);
 
 void quickie_message(const char *from,
                     const char *fromaddr,
-                    char *to,
+                    const char *to,
                     char *room,
                     const char *text, 
                     int format_type,
@@ -122,7 +122,7 @@ void quickie_message(const char *from,
 
 void flood_protect_quickie_message(const char *from,
                                   const char *fromaddr,
-                                  char *to,
+                                  const char *to,
                                   char *room,
                                   const char *text, 
                                   int format_type,
@@ -160,6 +160,7 @@ void CtdlWriteObject(char *req_room,                        /* Room to stuff it in */
                        unsigned int flags              /* Internal save flags */
 );
 struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body);
+struct CtdlMessage * CtdlDuplicateMessage(struct CtdlMessage *OrgMsg);
 void CtdlFreeMessage(struct CtdlMessage *msg);
 void CtdlFreeMessageContents(struct CtdlMessage *msg);
 void serialize_message(struct ser_ret *, struct CtdlMessage *);
diff --git a/citadel/netconfig.c b/citadel/netconfig.c
new file mode 100644 (file)
index 0000000..52adbbd
--- /dev/null
@@ -0,0 +1,1185 @@
+/*
+ * This module handles shared rooms, inter-Citadel mail, and outbound
+ * mailing list processing.
+ *
+ * Copyright (c) 2000-2012 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 <stdio.h>
+
+#ifdef HAVE_SYSCALL_H
+# include <syscall.h>
+#else 
+# if HAVE_SYS_SYSCALL_H
+#  include <sys/syscall.h>
+# endif
+#endif
+#include <dirent.h>
+
+#include <libcitadel.h>
+
+#include "include/ctdl_module.h"
+
+
+void vFreeRoomNetworkStruct(void *vOneRoomNetCfg);
+
+HashList *CfgTypeHash = NULL;
+HashList *RoomConfigs = NULL;
+/*-----------------------------------------------------------------------------*
+ *                       Per room network configs                              *
+ *-----------------------------------------------------------------------------*/
+void RegisterRoomCfgType(const char* Name, long len, RoomNetCfg eCfg, CfgLineParser p, int uniq,  int nSegments, CfgLineSerializer s, CfgLineDeAllocator d)
+{
+       CfgLineType *pCfg;
+
+       pCfg = (CfgLineType*) malloc(sizeof(CfgLineType));
+       pCfg->Parser = p;
+       pCfg->Serializer = s;
+       pCfg->DeAllocator = d;
+       pCfg->C = eCfg;
+       pCfg->Str.Key = Name;
+       pCfg->Str.len = len;
+       pCfg->IsSingleLine = uniq;
+       pCfg->nSegments = nSegments;
+       if (CfgTypeHash == NULL)
+               CfgTypeHash = NewHash(1, NULL);
+       Put(CfgTypeHash, Name, len, pCfg, NULL);
+}
+
+
+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;
+       }
+}
+
+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;
+}
+void ParseGeneric(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *OneRNCfg)
+{
+       RoomNetCfgLine *nptr;
+       int i;
+
+       nptr = (RoomNetCfgLine *)
+               malloc(sizeof(RoomNetCfgLine));
+       nptr->next = OneRNCfg->NetConfigs[ThisOne->C];
+       nptr->Value = malloc(sizeof(StrBuf*) * ThisOne->nSegments);
+       nptr->nValues = 0;
+       memset(nptr->Value, 0, sizeof(StrBuf*) * ThisOne->nSegments);
+       if (ThisOne->nSegments == 1)
+       {
+               nptr->Value[0] = NewStrBufPlain(LinePos, StrLength(Line) - ( LinePos - ChrPtr(Line)) );
+               nptr->nValues = 1;
+       }
+       else for (i = 0; i < ThisOne->nSegments; i++)
+       {
+               nptr->nValues++;
+               nptr->Value[i] = NewStrBufPlain(NULL, StrLength(Line) - ( LinePos - ChrPtr(Line)) );
+               StrBufExtract_NextToken(nptr->Value[i], Line, &LinePos, '|');
+       }
+
+       OneRNCfg->NetConfigs[ThisOne->C] = nptr;
+}
+
+void SerializeGeneric(const CfgLineType *ThisOne, StrBuf *OutputBuffer, OneRoomNetCfg *OneRNCfg, RoomNetCfgLine *data)
+{
+       int i;
+
+       StrBufAppendBufPlain(OutputBuffer, CKEY(ThisOne->Str), 0);
+       StrBufAppendBufPlain(OutputBuffer, HKEY("|"), 0);
+       for (i = 0; i < ThisOne->nSegments; i++)
+       {
+               StrBufAppendBuf(OutputBuffer, data->Value[i], 0);
+               if (i + 1 < ThisOne->nSegments)
+                       StrBufAppendBufPlain(OutputBuffer, HKEY("|"), 0);
+       }
+       StrBufAppendBufPlain(OutputBuffer, HKEY("\n"), 0);
+}
+
+void DeleteGenericCfgLine(const CfgLineType *ThisOne, RoomNetCfgLine **data)
+{
+       int i;
+
+       if (*data == NULL)
+               return;
+
+       for (i = 0; i < (*data)->nValues; i++)
+       {
+               FreeStrBuf(&(*data)->Value[i]);
+       }
+       free ((*data)->Value);
+       free(*data);
+       *data = NULL;
+}
+RoomNetCfgLine *DuplicateOneGenericCfgLine(const RoomNetCfgLine *data)
+{
+       RoomNetCfgLine *NewData;
+
+       NewData = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
+       int i;
+       NewData->Value = (StrBuf **)malloc(sizeof(StrBuf*) * data->nValues);
+
+       for (i = 0; i < data->nValues; i++)
+       {
+               NewData->Value[i] = NewStrBufDup(data->Value[i]);
+       }
+       return NewData;
+}
+int ReadRoomNetConfigFile(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;
+       }
+       if (*pOneRNCfg != NULL)
+               OneRNCfg = *pOneRNCfg;
+       else
+               OneRNCfg = malloc(sizeof(OneRoomNetCfg));
+       memset(OneRNCfg, 0, sizeof(OneRoomNetCfg));
+       *pOneRNCfg = OneRNCfg;
+       Line = NewStrBuf();
+       InStr = NewStrBuf();
+
+       while (StrBufTCP_read_line(Line, &fd, 0, &ErrStr) >= 0) {
+               if (StrLength(Line) == 0)
+                       continue;
+               Pos = NULL;
+               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);
+                       }
+               }
+       }
+       if (fd > 0)
+               close(fd);
+       FreeStrBuf(&InStr);
+       FreeStrBuf(&Line);
+       return 1;
+}
+
+int SaveRoomNetConfigFile(OneRoomNetCfg *OneRNCfg, char *filename)
+{
+       RoomNetCfg eCfg;
+       StrBuf *Cfg = NULL;
+       StrBuf *OutBuffer = NULL;
+       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 */
+       int rc;
+       HashPos *CfgIt;
+
+       len = strlen(filename);
+       memcpy(tempfilename, filename, len + 1);
+
+#if defined(HAVE_SYSCALL_H) && defined (SYS_gettid)
+       reltid = syscall(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);
+               FreeStrBuf(&Cfg);
+               return 0;
+       }
+       else {
+               OutBuffer = NewStrBuf();
+               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;
+                               }
+                               
+                               
+                       }
+
+               }
+               DeleteHashPos(&CfgIt);
+
+
+               if (OneRNCfg->misc != NULL) {
+                       StrBufAppendBuf(OutBuffer, OneRNCfg->misc, 0);
+               }
+
+               rc = write(TmpFD, ChrPtr(OutBuffer), StrLength(OutBuffer));
+               if ((rc >=0 ) && (rc == StrLength(OutBuffer))) 
+               {
+                       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;
+               }
+               FreeStrBuf(&OutBuffer);
+               
+       }
+       FreeStrBuf(&Cfg);
+       return rc;
+}
+
+void SaveModifiedRooms(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCfg)
+{
+       char filename[PATH_MAX];
+
+       if (OneRNCfg->changed)
+       {
+               assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
+               SaveRoomNetConfigFile(OneRNCfg, filename);
+               OneRNCfg->changed = 0;
+       }
+}
+void SaveChangedConfigs(void)
+{
+       CtdlForEachNetCfgRoom(SaveModifiedRooms,
+                             NULL, 
+                             maxRoomNetCfg);
+}
+
+
+void AddRoomCfgLine(OneRoomNetCfg *OneRNCfg, struct ctdlroom *qrbuf, RoomNetCfg LineType, RoomNetCfgLine *Line)
+{
+       int new = 0;
+       RoomNetCfgLine **pLine;
+       char filename[PATH_MAX];
+
+       if (OneRNCfg == NULL)
+       {
+               new = 1;
+               OneRNCfg = (OneRoomNetCfg*) malloc(sizeof(OneRoomNetCfg));
+               memset(OneRNCfg, 0, sizeof(OneRoomNetCfg));
+       }
+       pLine = &OneRNCfg->NetConfigs[LineType];
+
+       while(*pLine != NULL) pLine = &((*pLine)->next);
+       *pLine = Line;
+
+       assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
+       SaveRoomNetConfigFile(OneRNCfg, filename);
+       OneRNCfg->changed = 0;
+       if (new)
+       {
+               Put(RoomConfigs, LKEY(qrbuf->QRnumber), OneRNCfg, vFreeRoomNetworkStruct);
+       }
+}
+
+void FreeRoomNetworkStructContent(OneRoomNetCfg *OneRNCfg)
+{
+       RoomNetCfg eCfg;
+       HashPos *CfgIt;
+
+       CfgIt = GetNewHashPos(CfgTypeHash, 1);
+       for (eCfg = subpending; eCfg < maxRoomNetCfg; eCfg ++)
+       {
+               const CfgLineType *pCfg;
+               RoomNetCfgLine *pNext, *pName;
+               
+               pCfg = GetCfgTypeByEnum(eCfg, CfgIt);
+               pName= OneRNCfg->NetConfigs[eCfg];
+               while (pName != NULL)
+               {
+                       pNext = pName->next;
+                       if (pCfg != NULL)
+                       {
+                               pCfg->DeAllocator(pCfg, &pName);
+                       }
+                       else
+                       {
+                               DeleteGenericCfgLine(NULL, &pName);
+                       }
+                       pName = pNext;
+               }
+       }
+       DeleteHashPos(&CfgIt);
+
+       FreeStrBuf(&OneRNCfg->Sender);
+       FreeStrBuf(&OneRNCfg->RoomInfo);
+       FreeStrBuf(&OneRNCfg->misc);
+}
+void vFreeRoomNetworkStruct(void *vOneRoomNetCfg)
+{
+       OneRoomNetCfg *OneRNCfg;
+       OneRNCfg = (OneRoomNetCfg*)vOneRoomNetCfg;
+       FreeRoomNetworkStructContent(OneRNCfg);
+       free(OneRNCfg);
+}
+void FreeRoomNetworkStruct(OneRoomNetCfg **pOneRNCfg)
+{
+       vFreeRoomNetworkStruct(*pOneRNCfg);
+       *pOneRNCfg=NULL;
+}
+
+OneRoomNetCfg* CtdlGetNetCfgForRoom(long QRNumber)
+{
+       void *pv;
+       GetHash(RoomConfigs, LKEY(QRNumber), &pv);
+       return (OneRoomNetCfg*)pv;
+}
+
+
+void LoadAllNetConfigs(void)
+{
+       DIR *filedir = NULL;
+       struct dirent *d;
+       struct dirent *filedir_entry;
+       int d_type = 0;
+        int d_namelen;
+       long RoomNumber;
+       OneRoomNetCfg *OneRNCfg;
+       int IsNumOnly;
+       const char *pch;
+       char path[PATH_MAX];
+
+       RoomConfigs = NewHash(1, NULL);
+       filedir = opendir (ctdl_netcfg_dir);
+       if (filedir == NULL) {
+               return ; /// todo: panic!
+       }
+
+       d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
+       if (d == NULL) {
+               closedir(filedir);
+               return ;
+       }
+
+       while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
+              (filedir_entry != NULL))
+       {
+#ifdef _DIRENT_HAVE_D_NAMLEN
+               d_namelen = filedir_entry->d_namelen;
+#else
+               d_namelen = strlen(filedir_entry->d_name);
+#endif
+
+#ifdef _DIRENT_HAVE_D_TYPE
+               d_type = filedir_entry->d_type;
+#else
+
+#ifndef DT_UNKNOWN
+#define DT_UNKNOWN     0
+#define DT_DIR         4
+#define DT_REG         8
+#define DT_LNK         10
+
+#define IFTODT(mode)   (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype)        ((dirtype) << 12)
+#endif
+               d_type = DT_UNKNOWN;
+#endif
+               if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
+                       continue; /* Ignore backup files... */
+
+               if ((d_namelen == 1) && 
+                   (filedir_entry->d_name[0] == '.'))
+                       continue;
+
+               if ((d_namelen == 2) && 
+                   (filedir_entry->d_name[0] == '.') &&
+                   (filedir_entry->d_name[1] == '.'))
+                       continue;
+
+               snprintf(path, PATH_MAX, "%s/%s", 
+                        ctdl_netcfg_dir, filedir_entry->d_name);
+
+               if (d_type == DT_UNKNOWN) {
+                       struct stat s;
+                       if (lstat(path, &s) == 0) {
+                               d_type = IFTODT(s.st_mode);
+                       }
+               }
+
+               switch (d_type)
+               {
+               case DT_DIR:
+                       break;
+               case DT_LNK: /* TODO: check whether its a file or a directory */
+               case DT_REG:
+                       IsNumOnly = 1;
+                       pch = filedir_entry->d_name;
+                       while (*pch != '\0')
+                       {
+                               if (!isdigit(*pch))
+                               {
+                                       IsNumOnly = 0;
+                               }
+                               pch ++;
+                       }
+                       if (IsNumOnly)
+                       {
+                               OneRNCfg = NULL;
+                               RoomNumber = atol(filedir_entry->d_name);
+                               ReadRoomNetConfigFile(&OneRNCfg, path);
+
+                               if (OneRNCfg != NULL)
+                                       Put(RoomConfigs, LKEY(RoomNumber), OneRNCfg, vFreeRoomNetworkStruct);
+                               /* syslog(9, "[%s | %s]\n", ChrPtr(OneWebName), ChrPtr(FileName)); */
+                       }
+                       break;
+               default:
+                       break;
+               }
+
+
+       }
+       free(d);
+       closedir(filedir);
+}
+
+
+/*-----------------------------------------------------------------------------*
+ *              Per room network configs : exchange with client                *
+ *-----------------------------------------------------------------------------*/
+void cmd_gnet(char *argbuf)
+{
+       char filename[PATH_MAX];
+       char buf[SIZ];
+       FILE *fp;
+
+
+       if (!IsEmptyStr(argbuf))
+       {
+               if (CtdlAccessCheck(ac_aide)) return;
+               if (strcmp(argbuf, FILE_MAILALIAS))
+               {
+                       cprintf("%d No such file or directory\n",
+                               ERROR + INTERNAL_ERROR);
+                       return;
+               }
+               safestrncpy(filename, file_mail_aliases, sizeof(filename));
+               cprintf("%d Settings for <%s>\n",
+                       LISTING_FOLLOWS,
+                       filename);
+       }
+       else
+       {
+               if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) {
+                       /* users can edit the netconfigs for their own mailbox rooms */
+               }
+               else if (CtdlAccessCheck(ac_room_aide)) return;
+               
+               assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
+               cprintf("%d Network settings for room #%ld <%s>\n",
+                       LISTING_FOLLOWS,
+                       CC->room.QRnumber, CC->room.QRname);
+       }
+
+       fp = fopen(filename, "r");
+       if (fp != NULL) {
+               while (fgets(buf, sizeof buf, fp) != NULL) {
+                       buf[strlen(buf)-1] = 0;
+                       cprintf("%s\n", buf);
+               }
+               fclose(fp);
+       }
+
+       cprintf("000\n");
+}
+
+#define nForceAliases 5
+const ConstStr ForceAliases[nForceAliases] = {
+       {HKEY("bbs,")},
+       {HKEY("root,")},
+       {HKEY("Auto,")},
+       {HKEY("postmaster,")},
+       {HKEY("abuse,")}
+};
+
+void cmd_snet(char *argbuf)
+{
+       struct CitContext *CCC = CC;
+       char tempfilename[PATH_MAX];
+       char filename[PATH_MAX];
+       int TmpFD;
+       StrBuf *Line;
+       struct stat StatBuf;
+       long len;
+       int rc;
+       int IsMailAlias = 0;
+       int MailAliasesFound[nForceAliases];
+
+       unbuffer_output();
+
+       if (!IsEmptyStr(argbuf))
+       {
+               if (CtdlAccessCheck(ac_aide)) return;
+               if (strcmp(argbuf, FILE_MAILALIAS))
+               {
+                       cprintf("%d No such file or directory\n",
+                               ERROR + INTERNAL_ERROR);
+                       return;
+               }
+               len = safestrncpy(filename, file_mail_aliases, sizeof(filename));
+               memset(MailAliasesFound, 0, sizeof(MailAliasesFound));
+               memcpy(tempfilename, filename, len + 1);
+               IsMailAlias = 1;
+       }
+       else
+       {
+               if ( (CCC->room.QRflags & QR_MAILBOX) && (CCC->user.usernum == atol(CCC->room.QRname)) ) {
+                       /* users can edit the netconfigs for their own mailbox rooms */
+               }
+               else if (CtdlAccessCheck(ac_room_aide)) return;
+               
+               len = assoc_file_name(filename, sizeof filename, &CCC->room, ctdl_netcfg_dir);
+               memcpy(tempfilename, filename, len + 1);
+       }
+       memset(&StatBuf, 0, sizeof(struct stat));
+       if ((stat(filename, &StatBuf)  == -1) || (StatBuf.st_size == 0))
+               StatBuf.st_size = 80; /* Not there or empty? guess 80 chars line. */
+
+       sprintf(tempfilename + len, ".%d", CCC->cs_pid);
+       errno = 0;
+       TmpFD = open(tempfilename, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
+
+       if ((TmpFD > 0) && (errno == 0))
+       {
+               char *tmp = malloc(StatBuf.st_size * 2);
+               memset(tmp, ' ', StatBuf.st_size * 2);
+               rc = write(TmpFD, tmp, StatBuf.st_size * 2);
+               free(tmp);
+               if ((rc <= 0) || (rc != StatBuf.st_size * 2))
+               {
+                       close(TmpFD);
+                       cprintf("%d Unable to allocate the space required for %s: %s\n",
+                               ERROR + INTERNAL_ERROR,
+                               tempfilename,
+                               strerror(errno));
+                       unlink(tempfilename);
+                       return;
+               }       
+               lseek(TmpFD, SEEK_SET, 0);
+       }
+       else {
+               cprintf("%d Unable to allocate the space required for %s: %s\n",
+                       ERROR + INTERNAL_ERROR,
+                       tempfilename,
+                       strerror(errno));
+               unlink(tempfilename);
+               return;
+       }
+       Line = NewStrBuf();
+
+       cprintf("%d %s\n", SEND_LISTING, tempfilename);
+
+       len = 0;
+       while (rc = CtdlClientGetLine(Line), 
+              (rc >= 0))
+       {
+               if ((rc == 3) && (strcmp(ChrPtr(Line), "000") == 0))
+                       break;
+               if (IsMailAlias)
+               {
+                       int i;
+
+                       for (i = 0; i < nForceAliases; i++)
+                       {
+                               if ((!MailAliasesFound[i]) && 
+                                   (strncmp(ForceAliases[i].Key, 
+                                            ChrPtr(Line),
+                                            ForceAliases[i].len) == 0)
+                                       )
+                                   {
+                                           MailAliasesFound[i] = 1;
+                                           break;
+                                   }
+                       }
+               }
+
+               StrBufAppendBufPlain(Line, HKEY("\n"), 0);
+               write(TmpFD, ChrPtr(Line), StrLength(Line));
+               len += StrLength(Line);
+       }
+       FreeStrBuf(&Line);
+       ftruncate(TmpFD, len);
+       close(TmpFD);
+
+       if (IsMailAlias)
+       {
+               int i, state;
+               /*
+                * Sanity check whether all aliases required by the RFCs were set
+                * else bail out.
+                */
+               state = 1;
+               for (i = 0; i < nForceAliases; i++)
+               {
+                       if (!MailAliasesFound[i]) 
+                               state = 0;
+               }
+               if (state == 0)
+               {
+                       cprintf("%d won't do this - you're missing an RFC required alias.\n",
+                               ERROR + INTERNAL_ERROR);
+                       unlink(tempfilename);
+                       return;
+               }
+       }
+
+       /* Now copy the temp file to its permanent location.
+        * (We copy instead of link because they may be on different filesystems)
+        */
+       begin_critical_section(S_NETCONFIGS);
+       rename(tempfilename, filename);
+       if (!IsMailAlias)
+       {
+               OneRoomNetCfg *RNCfg;
+               RNCfg = CtdlGetNetCfgForRoom(CCC->room.QRnumber);
+               if (RNCfg != NULL)
+               {
+                       FreeRoomNetworkStructContent(RNCfg);
+                       ReadRoomNetConfigFile(&RNCfg, filename);
+               }
+               else
+               {
+                       ReadRoomNetConfigFile(&RNCfg, filename);
+                       Put(RoomConfigs, LKEY(CCC->room.QRnumber), RNCfg, vFreeRoomNetworkStruct);
+               }
+       }
+       end_critical_section(S_NETCONFIGS);
+}
+
+
+/*-----------------------------------------------------------------------------*
+ *                       Per node network configs                              *
+ *-----------------------------------------------------------------------------*/
+void DeleteCtdlNodeConf(void *vNode)
+{
+       CtdlNodeConf *Node = (CtdlNodeConf*) vNode;
+       FreeStrBuf(&Node->NodeName);
+       FreeStrBuf(&Node->Secret);
+       FreeStrBuf(&Node->Host);
+       FreeStrBuf(&Node->Port);
+       free(Node);
+}
+
+CtdlNodeConf *NewNode(StrBuf *SerializedNode)
+{
+       const char *Pos = NULL;
+       CtdlNodeConf *Node;
+
+       /* we need at least 4 pipes and some other text so its invalid. */
+       if (StrLength(SerializedNode) < 8)
+               return NULL;
+       Node = (CtdlNodeConf *) malloc(sizeof(CtdlNodeConf));
+
+       Node->DeleteMe = 0;
+
+       Node->NodeName=NewStrBuf();
+       StrBufExtract_NextToken(Node->NodeName, SerializedNode, &Pos, '|');
+
+       Node->Secret=NewStrBuf();
+       StrBufExtract_NextToken(Node->Secret, SerializedNode, &Pos, '|');
+
+       Node->Host=NewStrBuf();
+       StrBufExtract_NextToken(Node->Host, SerializedNode, &Pos, '|');
+
+       Node->Port=NewStrBuf();
+       StrBufExtract_NextToken(Node->Port, SerializedNode, &Pos, '|');
+       return Node;
+}
+
+
+/*
+ * Load or refresh the Citadel network (IGnet) configuration for this node.
+ */
+HashList* CtdlLoadIgNetCfg(void)
+{
+       const char *LinePos;
+       char       *Cfg;
+       StrBuf     *Buf;
+       StrBuf     *LineBuf;
+       HashList   *Hash;
+       CtdlNodeConf   *Node;
+
+       Cfg =  CtdlGetSysConfig(IGNETCFG);
+       if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
+               if (Cfg != NULL)
+                       free(Cfg);
+               return NULL;
+       }
+
+       Hash = NewHash(1, NULL);
+       Buf = NewStrBufPlain(Cfg, -1);
+       free(Cfg);
+       LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
+       LinePos = NULL;
+       do
+       {
+               StrBufSipLine(LineBuf, Buf, &LinePos);
+               if (StrLength(LineBuf) != 0) {
+                       Node = NewNode(LineBuf);
+                       if (Node != NULL) {
+                               Put(Hash, SKEY(Node->NodeName), Node, DeleteCtdlNodeConf);
+                       }
+               }
+       } while (LinePos != StrBufNOTNULL);
+       FreeStrBuf(&Buf);
+       FreeStrBuf(&LineBuf);
+       return Hash;
+}
+
+
+int is_recipient(OneRoomNetCfg *RNCfg, const char *Name)
+{
+       const RoomNetCfg RecipientCfgs[] = {
+               listrecp,
+               digestrecp,
+               participate,
+               maxRoomNetCfg
+       };
+       int i;
+       RoomNetCfgLine *nptr;
+       size_t len;
+       
+       len = strlen(Name);
+       i = 0;
+       while (RecipientCfgs[i] != maxRoomNetCfg)
+       {
+               nptr = RNCfg->NetConfigs[RecipientCfgs[i]];
+               
+               while (nptr != NULL)
+               {
+                       if ((StrLength(nptr->Value[0]) == len) && 
+                           (!strcmp(Name, ChrPtr(nptr->Value[0]))))
+                       {
+                               return 1;
+                       }
+                       nptr = nptr->next;
+               }
+       }
+       return 0;
+}
+
+
+
+int CtdlNetconfigCheckRoomaccess(
+       char *errmsgbuf, 
+       size_t n,
+       const char* RemoteIdentifier)
+{
+       OneRoomNetCfg *RNCfg;
+       char filename[SIZ];
+       int found;
+
+       if (RemoteIdentifier == NULL)
+       {
+               snprintf(errmsgbuf, n, "Need sender to permit access.");
+               return (ERROR + USERNAME_REQUIRED);
+       }
+
+       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
+       begin_critical_section(S_NETCONFIGS);
+       if (!ReadRoomNetConfigFile(&RNCfg, filename))
+       {
+               end_critical_section(S_NETCONFIGS);
+               snprintf(errmsgbuf, n,
+                        "This mailing list only accepts posts from subscribers.");
+               return (ERROR + NO_SUCH_USER);
+       }
+       end_critical_section(S_NETCONFIGS);
+       found = is_recipient (RNCfg, RemoteIdentifier);
+       vFreeRoomNetworkStruct(&RNCfg);
+       if (found) {
+               return (0);
+       }
+       else {
+               snprintf(errmsgbuf, n,
+                        "This mailing list only accepts posts from subscribers.");
+               return (ERROR + NO_SUCH_USER);
+       }
+}
+
+
+
+/*
+ * cmd_netp() - authenticate to the server as another Citadel node polling
+ *           for network traffic
+ */
+void cmd_netp(char *cmdbuf)
+{
+       struct CitContext *CCC = CC;
+       HashList *working_ignetcfg;
+       char *node;
+       StrBuf *NodeStr;
+       long nodelen;
+       int v;
+       long lens[2];
+       const char *strs[2];
+
+       const StrBuf *secret = NULL;
+       const StrBuf *nexthop = NULL;
+       char err_buf[SIZ] = "";
+
+       /* Authenticate */
+       node = CCC->curr_user;
+       nodelen = extract_token(CCC->curr_user, cmdbuf, 0, '|', sizeof CCC->curr_user);
+       NodeStr = NewStrBufPlain(node, nodelen);
+       /* load the IGnet Configuration to check node validity */
+       working_ignetcfg = CtdlLoadIgNetCfg();
+       v = CtdlIsValidNode(&nexthop, &secret, NodeStr, working_ignetcfg, NULL);
+       if (v != 0) {
+               snprintf(err_buf, sizeof err_buf,
+                       "An unknown Citadel server called \"%s\" attempted to connect from %s [%s].\n",
+                       node, CCC->cs_host, CCC->cs_addr
+               );
+               syslog(LOG_WARNING, "%s", err_buf);
+               cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
+
+               strs[0] = CCC->cs_addr;
+               lens[0] = strlen(CCC->cs_addr);
+               
+               strs[1] = "SRV_UNKNOWN";
+               lens[1] = sizeof("SRV_UNKNOWN" - 1);
+
+               CtdlAideFPMessage(
+                       err_buf,
+                       "IGNet Networking.",
+                       2, strs, (long*) &lens);
+
+               DeleteHash(&working_ignetcfg);
+               FreeStrBuf(&NodeStr);
+               return;
+       }
+
+       extract_token(CCC->user.password, cmdbuf, 1, '|', sizeof CCC->user.password);
+       if (strcasecmp(CCC->user.password, ChrPtr(secret))) {
+               snprintf(err_buf, sizeof err_buf,
+                       "A Citadel server at %s [%s] failed to authenticate as network node \"%s\".\n",
+                       CCC->cs_host, CCC->cs_addr, node
+               );
+               syslog(LOG_WARNING, "%s", err_buf);
+               cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
+
+               strs[0] = CCC->cs_addr;
+               lens[0] = strlen(CCC->cs_addr);
+               
+               strs[1] = "SRV_PW";
+               lens[1] = sizeof("SRV_PW" - 1);
+
+               CtdlAideFPMessage(
+                       err_buf,
+                       "IGNet Networking.",
+                       2, strs, (long*) &lens);
+
+               DeleteHash(&working_ignetcfg);
+               FreeStrBuf(&NodeStr);
+               return;
+       }
+
+       if (CtdlNetworkTalkingTo(node, nodelen, NTT_CHECK)) {
+               syslog(LOG_WARNING, "Duplicate session for network node <%s>", node);
+               cprintf("%d Already talking to %s right now\n", ERROR + RESOURCE_BUSY, node);
+               DeleteHash(&working_ignetcfg);
+               FreeStrBuf(&NodeStr);
+               return;
+       }
+       nodelen = safestrncpy(CCC->net_node, node, sizeof CCC->net_node);
+       CtdlNetworkTalkingTo(CCC->net_node, nodelen, NTT_ADD);
+       syslog(LOG_NOTICE, "Network node <%s> logged in from %s [%s]\n",
+               CCC->net_node, CCC->cs_host, CCC->cs_addr
+       );
+       cprintf("%d authenticated as network node '%s'\n", CIT_OK, CCC->net_node);
+       DeleteHash(&working_ignetcfg);
+       FreeStrBuf(&NodeStr);
+}
+
+
+/*-----------------------------------------------------------------------------*
+ *                 Network maps: evaluate other nodes                          *
+ *-----------------------------------------------------------------------------*/
+
+void DeleteNetMap(void *vNetMap)
+{
+       CtdlNetMap *TheNetMap = (CtdlNetMap*) vNetMap;
+       FreeStrBuf(&TheNetMap->NodeName);
+       FreeStrBuf(&TheNetMap->NextHop);
+       free(TheNetMap);
+}
+
+CtdlNetMap *NewNetMap(StrBuf *SerializedNetMap)
+{
+       const char *Pos = NULL;
+       CtdlNetMap *NM;
+
+       /* we need at least 3 pipes and some other text so its invalid. */
+       if (StrLength(SerializedNetMap) < 6)
+               return NULL;
+       NM = (CtdlNetMap *) malloc(sizeof(CtdlNetMap));
+
+       NM->NodeName=NewStrBuf();
+       StrBufExtract_NextToken(NM->NodeName, SerializedNetMap, &Pos, '|');
+
+       NM->lastcontact = StrBufExtractNext_long(SerializedNetMap, &Pos, '|');
+
+       NM->NextHop=NewStrBuf();
+       StrBufExtract_NextToken(NM->NextHop, SerializedNetMap, &Pos, '|');
+
+       return NM;
+}
+
+HashList* CtdlReadNetworkMap(void)
+{
+       const char *LinePos;
+       char       *Cfg;
+       StrBuf     *Buf;
+       StrBuf     *LineBuf;
+       HashList   *Hash;
+       CtdlNetMap     *TheNetMap;
+
+       Hash = NewHash(1, NULL);
+       Cfg =  CtdlGetSysConfig(IGNETMAP);
+       if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
+               if (Cfg != NULL)
+                       free(Cfg);
+               return Hash;
+       }
+
+       Buf = NewStrBufPlain(Cfg, -1);
+       free(Cfg);
+       LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
+       LinePos = NULL;
+       while (StrBufSipLine(Buf, LineBuf, &LinePos))
+       {
+               TheNetMap = NewNetMap(LineBuf);
+               if (TheNetMap != NULL) { /* TODO: is the NodeName Uniq? */
+                       Put(Hash, SKEY(TheNetMap->NodeName), TheNetMap, DeleteNetMap);
+               }
+       }
+       FreeStrBuf(&Buf);
+       FreeStrBuf(&LineBuf);
+       return Hash;
+}
+
+StrBuf *CtdlSerializeNetworkMap(HashList *Map)
+{
+       void *vMap;
+       const char *key;
+       long len;
+       StrBuf *Ret = NewStrBuf();
+       HashPos *Pos = GetNewHashPos(Map, 0);
+
+       while (GetNextHashPos(Map, Pos, &len, &key, &vMap))
+       {
+               CtdlNetMap *pMap = (CtdlNetMap*) vMap;
+               StrBufAppendBuf(Ret, pMap->NodeName, 0);
+               StrBufAppendBufPlain(Ret, HKEY("|"), 0);
+
+               StrBufAppendPrintf(Ret, "%ld", pMap->lastcontact, 0);
+               StrBufAppendBufPlain(Ret, HKEY("|"), 0);
+
+               StrBufAppendBuf(Ret, pMap->NextHop, 0);
+               StrBufAppendBufPlain(Ret, HKEY("\n"), 0);
+       }
+       DeleteHashPos(&Pos);
+       return Ret;
+}
+
+
+/*
+ * Learn topology from path fields
+ */
+void NetworkLearnTopology(char *node, char *path, HashList *the_netmap, int *netmap_changed)
+{
+       CtdlNetMap *pNM = NULL;
+       void *vptr;
+       char nexthop[256];
+       CtdlNetMap *nmptr;
+
+       if (GetHash(the_netmap, node, strlen(node), &vptr) && 
+           (vptr != NULL))/* TODO: is the NodeName Uniq? */
+       {
+               pNM = (CtdlNetMap*)vptr;
+               extract_token(nexthop, path, 0, '!', sizeof nexthop);
+               if (!strcmp(nexthop, ChrPtr(pNM->NextHop))) {
+                       pNM->lastcontact = time(NULL);
+                       (*netmap_changed) ++;
+                       return;
+               }
+       }
+
+       /* If we got here then it's not in the map, so add it. */
+       nmptr = (CtdlNetMap *) malloc(sizeof (CtdlNetMap));
+       nmptr->NodeName = NewStrBufPlain(node, -1);
+       nmptr->lastcontact = time(NULL);
+       nmptr->NextHop = NewStrBuf ();
+       StrBufExtract_tokenFromStr(nmptr->NextHop, path, strlen(path), 0, '!');
+       /* TODO: is the NodeName Uniq? */
+       Put(the_netmap, SKEY(nmptr->NodeName), nmptr, DeleteNetMap);
+       (*netmap_changed) ++;
+}
+
+
+/*
+ * Check the network map and determine whether the supplied node name is
+ * valid.  If it is not a neighbor node, supply the name of a neighbor node
+ * which is the next hop.  If it *is* a neighbor node, we also fill in the
+ * shared secret.
+ */
+int CtdlIsValidNode(const StrBuf **nexthop,
+                   const StrBuf **secret,
+                   StrBuf *node,
+                   HashList *IgnetCfg,
+                   HashList *the_netmap)
+{
+       void *vNetMap;
+       void *vNodeConf;
+       CtdlNodeConf *TheNode;
+       CtdlNetMap *TheNetMap;
+
+       if (StrLength(node) == 0) {
+               return(-1);
+       }
+
+       /*
+        * First try the neighbor nodes
+        */
+       if (GetCount(IgnetCfg) == 0) {
+               syslog(LOG_INFO, "IgnetCfg is empty!\n");
+               if (nexthop != NULL) {
+                       *nexthop = NULL;
+               }
+               return(-1);
+       }
+
+       /* try to find a neigbour with the name 'node' */
+       if (GetHash(IgnetCfg, SKEY(node), &vNodeConf) && 
+           (vNodeConf != NULL))
+       {
+               TheNode = (CtdlNodeConf*)vNodeConf;
+               if (secret != NULL)
+                       *secret = TheNode->Secret;
+               return 0;               /* yup, it's a direct neighbor */
+       }
+
+       /*
+        * If we get to this point we have to see if we know the next hop
+        *//* TODO: is the NodeName Uniq? */
+       if ((GetCount(the_netmap) > 0) &&
+           (GetHash(the_netmap, SKEY(node), &vNetMap)))
+       {
+               TheNetMap = (CtdlNetMap*)vNetMap;
+               if (nexthop != NULL)
+                       *nexthop = TheNetMap->NextHop;
+               return(0);
+       }
+
+       /*
+        * If we get to this point, the supplied node name is bogus.
+        */
+       syslog(LOG_ERR, "Invalid node name <%s>\n", ChrPtr(node));
+       return(-1);
+}
+
+
+void destroy_network_cfgs(void)
+{
+       HashList *pCfgTypeHash = CfgTypeHash;
+       HashList *pRoomConfigs = RoomConfigs;
+
+       CfgTypeHash = NULL;
+       RoomConfigs = NULL;
+       
+       DeleteHash(&pCfgTypeHash);
+       DeleteHash(&pRoomConfigs);
+}
+
+/*
+ * Module entry point
+ */
+CTDL_MODULE_INIT(netconfig)
+{
+       if (!threading)
+       {
+               CtdlRegisterCleanupHook(destroy_network_cfgs);
+               LoadAllNetConfigs ();
+               CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
+               CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config");
+               CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller");
+       }
+       return "netconfig";
+}
diff --git a/citadel/nttlist.c b/citadel/nttlist.c
new file mode 100644 (file)
index 0000000..5831d4a
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This module handles shared rooms, inter-Citadel mail, and outbound
+ * mailing list processing.
+ *
+ * Copyright (c) 2000-2012 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 <stdio.h>
+#include <libcitadel.h>
+#include "ctdl_module.h"
+#include "serv_extensions.h"
+
+/*-----------------------------------------------------------------------------*
+ *                 Network maps: evaluate other nodes                          *
+ *-----------------------------------------------------------------------------*/
+int NTTDebugEnabled = 0;
+
+/*
+ * network_talking_to()  --  concurrency checker
+ */
+static HashList *nttlist = NULL;
+int CtdlNetworkTalkingTo(const char *nodename, long len, int operation)
+{
+
+       int retval = 0;
+       HashPos *Pos = NULL;
+       void *vdata;
+
+       begin_critical_section(S_NTTLIST);
+
+       switch(operation) {
+
+               case NTT_ADD:
+                       if (nttlist == NULL) 
+                               nttlist = NewHash(1, NULL);
+                       Put(nttlist, nodename, len, NewStrBufPlain(nodename, len), HFreeStrBuf);
+                       if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: added <%s>\n", nodename);
+                       break;
+               case NTT_REMOVE:
+                       if ((nttlist == NULL) ||
+                           (GetCount(nttlist) == 0))
+                               break;
+                       Pos = GetNewHashPos(nttlist, 1);
+                       if (GetHashPosFromKey (nttlist, nodename, len, Pos))
+                               DeleteEntryFromHash(nttlist, Pos);
+                       DeleteHashPos(&Pos);
+                       if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: removed <%s>\n", nodename);
+
+                       break;
+
+               case NTT_CHECK:
+                       if ((nttlist == NULL) ||
+                           (GetCount(nttlist) == 0))
+                               break;
+                       if (GetHash(nttlist, nodename, len, &vdata))
+                               retval ++;
+                       if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: have [%d] <%s>\n", retval, nodename);
+                       break;
+       }
+
+       end_critical_section(S_NTTLIST);
+       return(retval);
+}
+
+void cleanup_nttlist(void)
+{
+        begin_critical_section(S_NTTLIST);
+       DeleteHash(&nttlist);
+        end_critical_section(S_NTTLIST);
+}
+
+
+
+/*
+ * Module entry point
+ */
+void SetNTTDebugEnabled(const int n)
+{
+       NTTDebugEnabled = n;
+}
+
+
+
+/*
+ * Module entry point
+ */
+CTDL_MODULE_INIT(nttlist)
+{
+       if (!threading)
+       {
+               CtdlRegisterDebugFlagHook(HKEY("networktalkingto"), SetNTTDebugEnabled, &NTTDebugEnabled);
+
+               CtdlRegisterCleanupHook(cleanup_nttlist);
+       }
+       return "nttlist";
+}
index 1e4209bdc6867bccc3a8b9e10ed9b1d15d2bb99b..998cf8f1cdf232d6cfe5801edaa07a603b2e3278 100644 (file)
@@ -278,12 +278,13 @@ void room_sanity_check(struct ctdlroom *qrbuf)
 /*
  * CtdlGetRoom()  -  retrieve room data from disk
  */
-int CtdlGetRoom(struct ctdlroom *qrbuf, char *room_name)
+int CtdlGetRoom(struct ctdlroom *qrbuf, const char *room_name)
 {
        struct cdbdata *cdbqr;
        char lowercase_name[ROOMNAMELEN];
        char personal_lowercase_name[ROOMNAMELEN];
-       char *dptr, *sptr, *eptr;
+       const char *sptr;
+       char *dptr, *eptr;
 
        dptr = lowercase_name;
        sptr = room_name;
@@ -565,8 +566,7 @@ void lputfloor(struct floor *flbuf, int floor_num)
 /* 
  *  Traverse the room file...
  */
-void CtdlForEachRoom(void (*CallBack) (struct ctdlroom *EachRoom, void *out_data),
-               void *in_data)
+void CtdlForEachRoom(ForEachRoomCallBack CB, void *in_data)
 {
        struct ctdlroom qrbuf;
        struct cdbdata *cdbqr;
@@ -582,7 +582,41 @@ void CtdlForEachRoom(void (*CallBack) (struct ctdlroom *EachRoom, void *out_data
                cdb_free(cdbqr);
                room_sanity_check(&qrbuf);
                if (qrbuf.QRflags & QR_INUSE) {
-                       (*CallBack)(&qrbuf, in_data);
+                       CB(&qrbuf, in_data);
+               }
+       }
+}
+
+/* 
+ *  Traverse the room file...
+ */
+void CtdlForEachNetCfgRoom(ForEachRoomNetCfgCallBack CB,
+                          void *in_data,
+                          RoomNetCfg filter)
+{
+       struct ctdlroom qrbuf;
+       struct cdbdata *cdbqr;
+
+       cdb_rewind(CDB_ROOMS);
+
+       while (cdbqr = cdb_next_item(CDB_ROOMS), cdbqr != NULL) {
+               memset(&qrbuf, 0, sizeof(struct ctdlroom));
+               memcpy(&qrbuf, cdbqr->ptr,
+                      ((cdbqr->len > sizeof(struct ctdlroom)) ?
+                       sizeof(struct ctdlroom) : cdbqr->len)
+               );
+               cdb_free(cdbqr);
+               room_sanity_check(&qrbuf);
+               if (qrbuf.QRflags & QR_INUSE)
+               {
+                       OneRoomNetCfg* RNCfg;
+                       RNCfg = CtdlGetNetCfgForRoom(qrbuf.QRnumber);
+                       if ((RNCfg != NULL) &&
+                           ((filter == maxRoomNetCfg) ||
+                            (RNCfg->NetConfigs[filter] != NULL)))
+                       {
+                               CB(&qrbuf, in_data, RNCfg);
+                       }
                }
        }
 }
index 7939cd7faa92cd306b63eba6867e48eaefe95707..bbf19b783819daeb10807659c2b6babc1806ce1b 100755 (executable)
@@ -171,6 +171,7 @@ void upgrade_modules(void);
        CTDL_MODULE_INIT(msgbase);
        CTDL_MODULE_INIT(room_ops);
        CTDL_MODULE_INIT(user_ops);
+       CTDL_MODULE_INIT(netconfig);
 EOF
 
 for i in serv_*.c
@@ -318,6 +319,11 @@ fi
 
 cd $CUR_DIR
 
+# this one has to be called last, else it will not find all hooks registered.
+cat <<EOF >> $C_FILE
+        pMod = CTDL_INIT_CALL(netconfig);
+        MOD_syslog(LOG_DEBUG, "Loaded module: %s\n", pMod);
+EOF
 /usr/bin/printf "\n\n" >> $C_FILE
 /usr/bin/printf "\tfor (filter = 1; filter != 0; filter = filter << 1)\n" >> $C_FILE
 /usr/bin/printf "\t\tif ((filter & DetailErrorFlags) != 0)\n" >> $C_FILE
index 4725d02e068fb3159cb377447b699779fd2e86b5..f120d48a6078769cd58a4bbec68d9db67151c18d 100644 (file)
@@ -90,6 +90,8 @@ to use this option you are on your own.
  
  
  INSTRUCTION:  pop3client
- SYNTAX:       pop3client|hostname|username|password
+ SYNTAX:       pop3client|hostname|username|password|keep|interval
    Periodically fetch messages from a remote POP3 account and store them in this
 room.  Messages will be deleted from the remote account once successfully stored.
+if Keep is set, messages won't be erased on the remote host.
+Interval can specify a bigger value than the system configs value.
index ff95e825ebe5ad428d604b610d8621ae6a6a327f..6fab115fad22bd156cf7779246561840d184de2b 100644 (file)
@@ -58,6 +58,7 @@ void do_housekeeping(void)
 {
        wcsession *sptr, *ss;
        wcsession *sessions_to_kill = NULL;
+       time_t the_time;
 
        /*
         * Lock the session list, moving any candidates for euthanasia into
@@ -65,9 +66,12 @@ void do_housekeeping(void)
         */
        CtdlLogResult(pthread_mutex_lock(&SessionListMutex));
        for (sptr = SessionList; sptr != NULL; sptr = sptr->next) {
-
+               if (the_time == 0)
+                       the_time = time(NULL);
                /* Kill idle sessions */
-               if ((time(NULL) - (sptr->lastreq)) > (time_t) WEBCIT_TIMEOUT) {
+               if ((sptr->inuse == 0) && 
+                   ((the_time - (sptr->lastreq)) > (time_t) WEBCIT_TIMEOUT))
+               {
                        syslog(3, "Timeout session %d", sptr->wc_session);
                        sptr->killthis = 1;
                }
@@ -560,7 +564,7 @@ void context_loop(ParsedHttpHdrs *Hdr)
        /*
         * If there were no qualifying sessions, then create a new one.
         */
-       if (TheSession == NULL) {
+       if ((TheSession == NULL) || (TheSession->killthis != 0)) {
                TheSession = CreateSession(1, 0, &SessionList, Hdr, &SessionListMutex);
        }
 
index 385e40b66984a928a7a6fc1f6c2e5924b74a53fd..01e795829914d635e260075cc05ae1a05ff0e9e8 100644 (file)
@@ -325,7 +325,8 @@ void render_MIME_ICS_TPL(StrBuf *Target, WCTemplputParams *TP, StrBuf *FoundChar
                      0,
                      SuperTP.Tokens);
        FlushStrBuf(Mime->Data);
-       DoTemplate(HKEY("ical_attachment_display"), Mime->Data, &SubTP);
+///    DoTemplate(HKEY("ical_attachment_display"), Mime->Data, &SubTP);
+       DoTemplate(HKEY("ical_edit"), Mime->Data, &SubTP);
 
        /*/ cal_process_object(Mime->Data, cal, 0, Mime->msgnum, ChrPtr(Mime->PartNum)); */
 
index ab7ce69e0989c2788e94f8ed028efc04f106d06b..9a91e267649c7a21a9b7358b608495a3781dbe06 100644 (file)
@@ -170,6 +170,7 @@ int LoadStaticDir(const char *DirName, HashList *DirList, const char *RelDir)
 
        d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
        if (d == NULL) {
+               closedir(filedir);
                return 0;
        }
 
@@ -181,8 +182,14 @@ int LoadStaticDir(const char *DirName, HashList *DirList, const char *RelDir)
        while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
               (filedir_entry != NULL))
        {
-#ifdef _DIRENT_HAVE_D_NAMELEN
+#ifdef _DIRENT_HAVE_D_NAMLEN
                d_namelen = filedir_entry->d_namelen;
+
+#else
+               d_namelen = strlen(filedir_entry->d_name);
+#endif
+
+#ifdef _DIRENT_HAVE_D_TYPE
                d_type = filedir_entry->d_type;
 #else
 
@@ -195,7 +202,6 @@ int LoadStaticDir(const char *DirName, HashList *DirList, const char *RelDir)
 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
 #define DTTOIF(dirtype)        ((dirtype) << 12)
 #endif
-               d_namelen = strlen(filedir_entry->d_name);
                d_type = DT_UNKNOWN;
 #endif
                if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
@@ -215,7 +221,7 @@ int LoadStaticDir(const char *DirName, HashList *DirList, const char *RelDir)
                        char path[PATH_MAX];
                        snprintf(path, PATH_MAX, "%s/%s", 
                                DirName, filedir_entry->d_name);
-                       if (stat(path, &s) == 0) {
+                       if (lstat(path, &s) == 0) {
                                d_type = IFTODT(s.st_mode);
                        }
                }
index 130b1a12ac61bd7e2811d485d7775c11527f4803..b119ce18ca645bebdaebda59d7b4be67a5f98966 100644 (file)
@@ -1525,8 +1525,14 @@ int LoadTemplateDir(const StrBuf *DirName, HashList *big, const StrBuf *BaseKey)
        {
                char *MinorPtr;
 
-#ifdef _DIRENT_HAVE_D_NAMELEN
+#ifdef _DIRENT_HAVE_D_NAMLEN
                d_namelen = filedir_entry->d_namelen;
+
+#else
+               d_namelen = strlen(filedir_entry->d_name);
+#endif
+
+#ifdef _DIRENT_HAVE_D_TYPE
                d_type = filedir_entry->d_type;
 #else
 
@@ -1539,7 +1545,6 @@ int LoadTemplateDir(const StrBuf *DirName, HashList *big, const StrBuf *BaseKey)
 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
 #define DTTOIF(dirtype)        ((dirtype) << 12)
 #endif
-               d_namelen = strlen(filedir_entry->d_name);
                d_type = DT_UNKNOWN;
 #endif
                d_without_ext = d_namelen;
@@ -1561,7 +1566,7 @@ int LoadTemplateDir(const StrBuf *DirName, HashList *big, const StrBuf *BaseKey)
                        char path[PATH_MAX];
                        snprintf(path, PATH_MAX, "%s/%s", 
                                 ChrPtr(DirName), filedir_entry->d_name);
-                       if (stat(path, &s) == 0) {
+                       if (lstat(path, &s) == 0) {
                                d_type = IFTODT(s.st_mode);
                        }
                }
@@ -1589,7 +1594,7 @@ int LoadTemplateDir(const StrBuf *DirName, HashList *big, const StrBuf *BaseKey)
                        LoadTemplateDir(SubDirectory, big, SubKey);
 
                        break;
-               case DT_LNK: /* TODO: check whether its a file or a directory */
+               case DT_LNK: 
                case DT_REG: