- sc = (struct SpoolControl *)userdata;
-
- /*
- * Process mailing list recipients
- */
- instr_len = SIZ;
- 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) {
-
- /* Prepend "[List name]" to the subject */
- if (msg->cm_fields['U'] == NULL) {
- msg->cm_fields['U'] = strdup("(no subject)");
- }
- snprintf(buf, sizeof buf, "[%s] %s", CC->room.QRname, msg->cm_fields['U']);
- free(msg->cm_fields['U']);
- msg->cm_fields['U'] = strdup(buf);
-
- /* Set the recipient of the list message to the
- * email address of the room itself.
- * FIXME ... I want to be able to pick any address
- */
- 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);
- CtdlFreeMessage(msg);
- }
- }
-
- /*
- * 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']);
- }
- fprintf(sc->digestfp, "\n");
- if (msg->cm_fields['U'] != NULL) {
- fprintf(sc->digestfp, "Subject: %s\n", msg->cm_fields['U']);
- }
-
- CC->redirect_buffer = malloc(SIZ);
- CC->redirect_len = 0;
- CC->redirect_alloc = SIZ;
-
- safestrncpy(CC->preferred_formats, "text/plain", sizeof CC->preferred_formats);
- CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_NONE, 0, 0);
-
- striplt(CC->redirect_buffer);
- fprintf(sc->digestfp, "\n%s\n", CC->redirect_buffer);
-
- free(CC->redirect_buffer);
- CC->redirect_buffer = NULL;
- CC->redirect_len = 0;
- CC->redirect_alloc = 0;
-
- sc->num_msgs_spooled += 1;
- free(msg);
- }
- }
-
- /*
- * Process client-side list participations for this room
- */
- instr_len = SIZ;
- 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;
- }
- if (is_valid_node(NULL, NULL, msg->cm_fields['N']) == 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 allocate
- */
- 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);
- CtdlSubmitMsg(msg, valid, "");
- free_recipients(valid);
- }
-
- }
- CtdlFreeMessage(msg);
- }
- }
-
- /*
- * Process IGnet push shares
- */
- 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");
- }
- 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 */
- if (sc->ignet_push_shares != NULL)
- for (mptr = sc->ignet_push_shares; mptr != NULL;
- mptr = mptr->next) {
-
- send = 1;
-
- /* Check for valid node name */
- if (is_valid_node(NULL, NULL, mptr->remote_nodename) != 0) {
- lprintf(CTDL_ERR, "Invalid node <%s>\n",
- mptr->remote_nodename);
- send = 0;
- }
-
- /* Check for split horizon */
- lprintf(CTDL_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);
- if (!strcasecmp(buf, 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']);
- }
- 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 the spool file */
- snprintf(filename, sizeof filename,"%s/%s",
- ctdl_netout_dir,
- mptr->remote_nodename);
- lprintf(CTDL_DEBUG, "Appending to %s\n", filename);
- fp = fopen(filename, "ab");
- if (fp != NULL) {
- fwrite(sermsg.ser,
- sermsg.len, 1, fp);
- fclose(fp);
- }
- else {
- lprintf(CTDL_ERR, "%s: %s\n", filename, strerror(errno));
- }
-
- /* free the serialized version */
- free(sermsg.ser);
- }
-
- }
- }
- CtdlFreeMessage(msg);
- }
-
- /* update lastsent */
- sc->lastsent = msgnum;
-
- /* Delete this message if delete-after-send is set */
- if (delete_after_send) {
- CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, "");
- }
-
-}
-
-
-/*
- * Batch up and send all outbound traffic from the current room
- */
-void network_spoolout_room(char *room_to_spool) {
- char filename[SIZ];
- char buf[SIZ];
- char instr[SIZ];
- char nodename[256];
- char roomname[ROOMNAMELEN];
- char nexthop[256];
- FILE *fp;
- struct SpoolControl sc;
- struct namelist *nptr = NULL;
- struct maplist *mptr = NULL;
- size_t miscsize = 0;
- size_t linesize = 0;
- int skipthisline = 0;
- int i;
-
- /*
- * 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 (getroom(&CC->room, room_to_spool) != 0) {
- lprintf(CTDL_CRIT, "ERROR: cannot load <%s>\n", room_to_spool);
- return;
- }
-
- memset(&sc, 0, sizeof(struct SpoolControl));
- 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 */
- fp = fopen(filename, "r");
- if (fp == NULL) {
- end_critical_section(S_NETCONFIGS);
- return;
- }
-
- lprintf(CTDL_INFO, "Networking started for <%s>\n", CC->room.QRname);
-
- while (fgets(buf, sizeof buf, fp) != NULL) {
- buf[strlen(buf)-1] = 0;
-
- extract_token(instr, buf, 0, '|', sizeof instr);
- if (!strcasecmp(instr, "lastsent")) {
- sc.lastsent = extract_long(buf, 1);
- }
- else if (!strcasecmp(instr, "listrecp")) {
- nptr = (struct namelist *)
- malloc(sizeof(struct namelist));
- nptr->next = sc.listrecps;
- extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
- sc.listrecps = nptr;
- }
- else if (!strcasecmp(instr, "participate")) {
- nptr = (struct namelist *)
- malloc(sizeof(struct namelist));
- nptr->next = sc.participates;
- extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
- sc.participates = nptr;
- }
- else if (!strcasecmp(instr, "digestrecp")) {
- nptr = (struct namelist *)
- malloc(sizeof(struct namelist));
- nptr->next = sc.digestrecps;
- extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
- sc.digestrecps = nptr;
- }
- else if (!strcasecmp(instr, "ignet_push_share")) {
- /* by checking each node's validity, we automatically
- * purge nodes which do not exist from room network
- * configurations at this time.
- */
- extract_token(nodename, buf, 1, '|', sizeof nodename);
- extract_token(roomname, buf, 2, '|', sizeof roomname);
- strcpy(nexthop, "xxx");
- if (is_valid_node(nexthop, NULL, nodename) == 0) {
- if (IsEmptyStr(nexthop)) {
- mptr = (struct maplist *)
- malloc(sizeof(struct 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, "subpending|", 11)) {
- if (time(NULL) - extract_long(buf, 4) > EXP) {
- skipthisline = 1;
- }
- }
- if (!strncasecmp(buf, "unsubpending|", 13)) {
- if (time(NULL) - extract_long(buf, 3) > EXP) {
- skipthisline = 1;
- }
- }
-
- 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;
- }
- }
-
-
- }
- fclose(fp);
-
- /* 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");
- }
-
- /* Do something useful */
- 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);
- 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
- );
- network_deliver_digest(&sc); /* deliver and close */
- }
-
- /* Now rewrite the config file */
- fp = fopen(filename, "w");
- if (fp == NULL) {
- lprintf(CTDL_CRIT, "ERROR: cannot open %s: %s\n",
- filename, strerror(errno));
- }
- else {
- fprintf(fp, "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) {
- fprintf(fp, "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) {
- fprintf(fp, "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) {
- fprintf(fp, "participate|%s\n", sc.participates->name);
- nptr = sc.participates->next;
- free(sc.participates);
- sc.participates = nptr;
- }
- while (sc.ignet_push_shares != NULL) {
- /* by checking each node's validity, we automatically
- * purge nodes which do not exist from room network
- * configurations at this time.
- */
- if (is_valid_node(NULL, NULL, sc.ignet_push_shares->remote_nodename) == 0) {
- }
- fprintf(fp, "ignet_push_share|%s",
- sc.ignet_push_shares->remote_nodename);
- if (!IsEmptyStr(sc.ignet_push_shares->remote_roomname)) {
- fprintf(fp, "|%s", sc.ignet_push_shares->remote_roomname);
- }
- fprintf(fp, "\n");
- mptr = sc.ignet_push_shares->next;
- free(sc.ignet_push_shares);
- sc.ignet_push_shares = mptr;
- }
- if (sc.misc != NULL) {
- fwrite(sc.misc, strlen(sc.misc), 1, fp);
- }
- free(sc.misc);
-
- fclose(fp);
- }
- end_critical_section(S_NETCONFIGS);
-}
-
-
-
-/*
- * Send the *entire* contents of the current room to one specific network node,
- * ignoring anything we know about which messages have already undergone
- * network processing. This can be used to bring a new node into sync.
- */
-int network_sync_to(char *target_node) {
- struct 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[256];
- 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) {
- buf[strlen(buf)-1] = 0;
- extract_token(sc_type, buf, 0, '|', sizeof sc_type);
- extract_token(sc_node, buf, 1, '|', sizeof sc_node);
- extract_token(sc_room, buf, 2, '|', sizeof sc_room);
- if ( (!strcasecmp(sc_type, "ignet_push_share"))
- && (!strcasecmp(sc_node, target_node)) ) {
- found_node = 1;
-
- /* Concise syntax because we don't need a full linked-list */
- memset(&sc, 0, sizeof(struct SpoolControl));
- sc.ignet_push_shares = (struct maplist *)
- malloc(sizeof(struct 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) return(-1);
-
- /* 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);
-
- lprintf(CTDL_NOTICE, "Synchronized %d messages to <%s>\n",
- num_spooled, target_node);
- return(num_spooled);
-}
-
-
-/*
- * Implements the NSYN command
- */
-void cmd_nsyn(char *argbuf) {
- int num_spooled;
- char target_node[256];
-
- if (CtdlAccessCheck(ac_aide)) return;
-
- extract_token(target_node, argbuf, 0, '|', sizeof target_node);
- num_spooled = network_sync_to(target_node);
- if (num_spooled >= 0) {
- cprintf("%d Spooled %d messages.\n", CIT_OK, num_spooled);
- }
- else {
- cprintf("%d No such room/node share exists.\n",
- ERROR + ROOM_NOT_FOUND);
- }
-}
-
-
-
-/*
- * Batch up and send all outbound traffic from the current room
- */
-void network_queue_room(struct ctdlroom *qrbuf, void *data) {
- struct RoomProcList *ptr;
-
- ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
- if (ptr == NULL) return;
-
- safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
- begin_critical_section(S_RPLIST);
- ptr->next = rplist;
- rplist = ptr;
- end_critical_section(S_RPLIST);
-}
-
-void destroy_network_queue_room(void)
-{
- struct RoomProcList *cur, *p;
- struct NetMap *nmcur, *nmp;
-
- cur = rplist;
- begin_critical_section(S_RPLIST);
- while (cur != NULL)
- {
- p = cur->next;
- free (cur);
- cur = p;
- }
- rplist = NULL;
- end_critical_section(S_RPLIST);
-
- nmcur = the_netmap;
- while (nmcur != NULL)
- {
- nmp = nmcur->next;
- free (nmcur);
- nmcur = nmp;
- }
- the_netmap = NULL;
- if (working_ignetcfg != NULL)
- free (working_ignetcfg);
- working_ignetcfg = NULL;
-}
-
-
-/*
- * Learn topology from path fields
- */
-void network_learn_topology(char *node, char *path) {
- char nexthop[256];
- struct NetMap *nmptr;
-
- strcpy(nexthop, "");
-
- if (num_tokens(path, '!') < 3) return;
- for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
- if (!strcasecmp(nmptr->nodename, node)) {
- extract_token(nmptr->nexthop, path, 0, '!', sizeof nmptr->nexthop);
- nmptr->lastcontact = time(NULL);
- ++netmap_changed;
- return;
- }
- }
-
- /* If we got here then it's not in the map, so add it. */
- nmptr = (struct NetMap *) malloc(sizeof (struct NetMap));
- strcpy(nmptr->nodename, node);
- nmptr->lastcontact = time(NULL);
- extract_token(nmptr->nexthop, path, 0, '!', sizeof nmptr->nexthop);
- nmptr->next = the_netmap;
- the_netmap = nmptr;
- ++netmap_changed;
-}
-
-
-
-
-/*
- * Bounce a message back to the sender
- */
-void network_bounce(struct CtdlMessage *msg, char *reason) {
- char *oldpath = NULL;
- char buf[SIZ];
- char bouncesource[SIZ];
- char recipient[SIZ];
- struct recptypes *valid = NULL;
- char force_room[ROOMNAMELEN];
- static int serialnum = 0;
- size_t size;
-
- lprintf(CTDL_DEBUG, "entering network_bounce()\n");
-
- if (msg == NULL) return;
-
- snprintf(bouncesource, sizeof bouncesource, "%s@%s", BOUNCESOURCE, config.c_nodename);
-
- /*
- * Give it a fresh message ID
- */
- if (msg->cm_fields['I'] != NULL) {
- free(msg->cm_fields['I']);
- }
- snprintf(buf, sizeof buf, "%ld.%04lx.%04x@%s",
- (long)time(NULL), (long)getpid(), ++serialnum, config.c_fqdn);
- msg->cm_fields['I'] = strdup(buf);
-
- /*
- * FIXME ... right now we're just sending a bounce; we really want to
- * include the text of the bounced message.
- */
- if (msg->cm_fields['M'] != NULL) {
- free(msg->cm_fields['M']);
- }
- msg->cm_fields['M'] = strdup(reason);
- msg->cm_format_type = 0;
-
- /*
- * Turn the message around
- */
- if (msg->cm_fields['R'] == NULL) {
- free(msg->cm_fields['R']);
- }
-
- if (msg->cm_fields['D'] == NULL) {
- free(msg->cm_fields['D']);
- }
-
- snprintf(recipient, sizeof recipient, "%s@%s",
- msg->cm_fields['A'], msg->cm_fields['N']);
-
- if (msg->cm_fields['A'] == NULL) {
- free(msg->cm_fields['A']);
- }
-
- if (msg->cm_fields['N'] == NULL) {
- free(msg->cm_fields['N']);
- }
-
- if (msg->cm_fields['U'] == NULL) {
- free(msg->cm_fields['U']);
- }
-
- msg->cm_fields['A'] = strdup(BOUNCESOURCE);
- msg->cm_fields['N'] = strdup(config.c_nodename);
- msg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");