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
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)
#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;
/*
* 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,
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,
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
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) {
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);
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;
}
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"
+ "<"), 0);
+ StrBufAppendBuf(cf_req, Line->Value[0], 0);
+
+ StrBufAppendBufPlain(cf_req, HKEY( "> 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"
- "<%s> 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;
}
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"
+ "<"), 0);
+ StrBufAppendBuf(cf_req, Line->Value[0], 0);
+
+ StrBufAppendBufPlain(cf_req, HKEY("> 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"
- "<%s> 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;
}
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);
}
/*
* 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]);
+ }
}
+++ /dev/null
-/*
- * 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);
*
*/
-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;
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);
+++ /dev/null
-/*
- * 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";
-}
{
if (!threading)
{
+/*
+ currently unsupported.
CtdlRegisterNetprocHook(filter_the_idiots);
+*/
}
/* return our module name for the log */
#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) {
/* 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
/*
* 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,
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?
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);
}
+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,
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?
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);
}
-/*
- * 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;
if (delete_after_send) {
CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, "");
}
- FreeStrBuf(&Buf);
}
#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
*/
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);
}
/* 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) {
/* 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
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
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",
}
closedir(dp);
+ free(d);
}
/*
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);
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... */
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",
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) {
"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;
}
spooloutfilename, strerror(errno)
);
close(infd);
- network_talking_to(SKEY(NextHop), NTT_REMOVE);
+ CtdlNetworkTalkingTo(SKEY(NextHop), NTT_REMOVE);
continue;
}
FDIOBufferDelete(&FDIO);
close(infd);
close(outfd);
- network_talking_to(SKEY(NextHop), NTT_REMOVE);
+ CtdlNetworkTalkingTo(SKEY(NextHop), NTT_REMOVE);
}
}
closedir(dp);
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;
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);
{
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);
}
#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;
typedef struct __roomlists {
RoomProcList *rplist;
- HashList *RoomsInterestedIn;
}roomlists;
/*
* When we do network processing, it's accomplished in two passes; one to
*/
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;
-}
-
-
/*
-
-
-
-
-
-
-
/*
* Send the *entire* contents of the current room to one specific network node,
* ignoring anything we know about which messages have already undergone
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);
}
}
-
-
-/*
- * 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)
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;
}
/*
* 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)
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;
);
}
- /*
- * 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)) {
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) &&
/* 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. */
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);
-}
* 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';
}
}
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';
}
}
/*
* Module entry point
*/
-void SetNTTDebugEnabled(const int n)
-{
- NTTDebugEnabled = n;
-}
+
void SetNetQDebugEnabled(const int n)
{
NetQDebugEnabled = n;
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");
"CC[%d]" FORMAT, \
CCC->cs_pid)
-typedef struct namelist namelist;
-
-struct namelist {
- namelist *next;
- char name[SIZ];
-};
void free_netfilter_list(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
-};
+++ /dev/null
-/*
- * 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";
-}
--- /dev/null
+/*
+ * 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";
+}
/*
* 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))
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;
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);
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);
/*
* 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))
}
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);
}
/*
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);
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);
CtdlRegisterEVCleanupHook(rss_cleanup);
CtdlRegisterDebugFlagHook(HKEY("rssclient"), LogDebugEnableRSSClient, &RSSClientDebugEnabled);
}
+ else
+ {
+ CtdlFillSystemContext(&rss_CC, "rssclient");
+ }
return "rssclient";
}
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 */
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
*/
void quickie_message(const char *from,
const char *fromaddr,
- char *to,
+ const char *to,
char *room,
const char *text,
int format_type,
void flood_protect_quickie_message(const char *from,
const char *fromaddr,
- char *to,
+ const char *to,
char *room,
const char *text,
int format_type,
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
}
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);
void quickie_message(const char *from,
const char *fromaddr,
- char *to,
+ const char *to,
char *room,
const char *text,
int format_type,
void flood_protect_quickie_message(const char *from,
const char *fromaddr,
- char *to,
+ const char *to,
char *room,
const char *text,
int format_type,
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 *);
--- /dev/null
+/*
+ * 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";
+}
--- /dev/null
+/*
+ * 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";
+}
/*
* 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;
/*
* 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;
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);
+ }
}
}
}
CTDL_MODULE_INIT(msgbase);
CTDL_MODULE_INIT(room_ops);
CTDL_MODULE_INIT(user_ops);
+ CTDL_MODULE_INIT(netconfig);
EOF
for i in serv_*.c
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
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.
{
wcsession *sptr, *ss;
wcsession *sessions_to_kill = NULL;
+ time_t the_time;
/*
* Lock the session list, moving any candidates for euthanasia into
*/
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;
}
/*
* 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);
}
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)); */
d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
if (d == NULL) {
+ closedir(filedir);
return 0;
}
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
#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] == '~')
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);
}
}
{
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
#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;
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);
}
}
LoadTemplateDir(SubDirectory, big, SubKey);
break;
- case DT_LNK: /* TODO: check whether its a file or a directory */
+ case DT_LNK:
case DT_REG: