X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fserv_network.c;h=668ed2964a30958c0af986b066cb4096820e79eb;hb=c60ee2e375c3b381e36ffebb525f21f7aaf608fd;hp=53872ce3b2e26ee63598a75b54f9a4714de2a097;hpb=590a5256199ce711ff7864a16c3da6929fe2b7f9;p=citadel.git diff --git a/citadel/serv_network.c b/citadel/serv_network.c index 53872ce3b..668ed2964 100644 --- a/citadel/serv_network.c +++ b/citadel/serv_network.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #if TIME_WITH_SYS_TIME @@ -84,8 +85,31 @@ struct RoomProcList *rplist = NULL; * We build a map of network nodes during processing. */ struct NetMap *the_netmap = NULL; +int netmap_changed = 0; +char *working_ignetcfg = NULL; + +/* + * Load or refresh the Citadel network (IGnet) configuration for this node. + */ +void load_working_ignetcfg(void) { + char *cfg; + char *oldcfg; + + cfg = CtdlGetSysConfig(IGNETCFG); + if (cfg == NULL) { + cfg = strdup(""); + } + + oldcfg = working_ignetcfg; + working_ignetcfg = cfg; + if (oldcfg != NULL) { + free(oldcfg); + } +} + + + -char *ignetcfg = NULL; /* * Keep track of what messages to reject @@ -102,13 +126,13 @@ struct FilterList *load_filter_list(void) { /* Use the string tokenizer to grab one line at a time */ for (i=0; ifl_user, buf, 0); + extract_token(nptr->fl_user, buf, 0, '|', sizeof nptr->fl_user); striplt(nptr->fl_user); - extract(nptr->fl_room, buf, 1); + extract_token(nptr->fl_room, buf, 1, '|', sizeof nptr->fl_room); striplt(nptr->fl_room); - extract(nptr->fl_node, buf, 2); + extract_token(nptr->fl_node, buf, 2, '|', sizeof nptr->fl_node); striplt(nptr->fl_node); /* Cowardly refuse to add an any/any/any entry that would @@ -201,16 +225,17 @@ void read_network_map(void) { /* Use the string tokenizer to grab one line at a time */ for (i=0; inodename, buf, 0); + extract_token(nmptr->nodename, buf, 0, '|', sizeof nmptr->nodename); nmptr->lastcontact = extract_long(buf, 1); - extract(nmptr->nexthop, buf, 2); + extract_token(nmptr->nexthop, buf, 2, '|', sizeof nmptr->nexthop); nmptr->next = the_netmap; the_netmap = nmptr; } free(serialized_map); + netmap_changed = 0; } @@ -221,25 +246,28 @@ void write_network_map(void) { char *serialized_map = NULL; struct NetMap *nmptr; - serialized_map = strdup(""); - if (the_netmap != NULL) { - for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) { - serialized_map = realloc(serialized_map, - (strlen(serialized_map)+SIZ) ); - if (strlen(nmptr->nodename) > 0) { - snprintf(&serialized_map[strlen(serialized_map)], - SIZ, - "%s|%ld|%s\n", - nmptr->nodename, - (long)nmptr->lastcontact, - nmptr->nexthop); + if (netmap_changed) { + serialized_map = strdup(""); + + if (the_netmap != NULL) { + for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) { + serialized_map = realloc(serialized_map, + (strlen(serialized_map)+SIZ) ); + if (strlen(nmptr->nodename) > 0) { + snprintf(&serialized_map[strlen(serialized_map)], + SIZ, + "%s|%ld|%s\n", + nmptr->nodename, + (long)nmptr->lastcontact, + nmptr->nexthop); + } } } - } - CtdlPutSysConfig(IGNETMAP, serialized_map); - free(serialized_map); + CtdlPutSysConfig(IGNETMAP, serialized_map); + free(serialized_map); + } /* Now free the list */ while (the_netmap != NULL) { @@ -247,6 +275,7 @@ void write_network_map(void) { free(the_netmap); the_netmap = nmptr; } + netmap_changed = 0; } @@ -271,7 +300,8 @@ int is_valid_node(char *nexthop, char *secret, char *node) { /* * First try the neighbor nodes */ - if (ignetcfg == NULL) { + if (working_ignetcfg == NULL) { + lprintf(CTDL_ERR, "working_ignetcfg is NULL!\n"); if (nexthop != NULL) { strcpy(nexthop, ""); } @@ -284,15 +314,15 @@ int is_valid_node(char *nexthop, char *secret, char *node) { } /* Use the string tokenizer to grab one line at a time */ - for (i=0; iroom, "netconfigs"); @@ -370,7 +402,7 @@ void cmd_snet(char *argbuf) { } cprintf("%d %s\n", SEND_LISTING, tempfilename); - while (client_gets(buf), strcmp(buf, "000")) { + while (client_getln(buf, sizeof buf), strcmp(buf, "000")) { fprintf(fp, "%s\n", buf); } fclose(fp); @@ -392,14 +424,12 @@ void cmd_snet(char *argbuf) { */ void network_spool_msg(long msgnum, void *userdata) { struct SpoolControl *sc; - int err; int i; char *newpath = NULL; - char *instr = NULL; size_t instr_len = SIZ; struct CtdlMessage *msg = NULL; - struct CtdlMessage *imsg; struct namelist *nptr; + struct maplist *mptr; struct ser_ret sermsg; FILE *fp; char filename[SIZ]; @@ -407,74 +437,149 @@ void network_spool_msg(long msgnum, void *userdata) { int bang = 0; int send = 1; int delete_after_send = 0; /* Set to 1 to delete after spooling */ + int ok_to_participate = 0; + struct recptypes *valid; sc = (struct SpoolControl *)userdata; /* * Process mailing list recipients */ + instr_len = SIZ; if (sc->listrecps != NULL) { - - /* First, copy it to the spoolout room */ - err = CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, msgnum, 0); - if (err != 0) return; - - /* - * Figure out how big a buffer we need to allocate - */ - for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) { - instr_len = instr_len + strlen(nptr->name); - } - - /* - * allocate... - */ - lprintf(CTDL_DEBUG, "Generating delivery instructions\n"); - instr = malloc(instr_len); - if (instr == NULL) { - lprintf(CTDL_EMERG, "Cannot allocate %ld bytes for instr...\n", - (long)instr_len); - abort(); - } - snprintf(instr, instr_len, - "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n" - "bounceto|postmaster@%s\n" , - SPOOLMIME, msgnum, (long)time(NULL), config.c_fqdn ); - - /* Generate delivery instructions for each recipient */ - for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) { - size_t tmp = strlen(instr); - snprintf(&instr[tmp], instr_len - tmp, - "remote|%s|0||\n", nptr->name); + /* Fetch the message. We're going to need to modify it + * for each recipient in order to satisfy foreign mailers, 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); + + /* For each recipient... */ + for (nptr = sc->listrecps; 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, NULL, NULL, ""); + free(valid); + + } + CtdlFreeMessage(msg); } - - /* - * Generate a message from the instructions - */ - imsg = malloc(sizeof(struct CtdlMessage)); - memset(imsg, 0, sizeof(struct CtdlMessage)); - imsg->cm_magic = CTDLMESSAGE_MAGIC; - imsg->cm_anon_type = MES_NORMAL; - imsg->cm_format_type = FMT_RFC822; - imsg->cm_fields['A'] = strdup("Citadel"); - imsg->cm_fields['M'] = instr; - - /* Save delivery instructions in spoolout room */ - CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM); - CtdlFreeMessage(imsg); } /* * Process digest recipients */ if ((sc->digestrecps != NULL) && (sc->digestfp != NULL)) { - fprintf(sc->digestfp, " -----------------------------------" - "------------------------------------" - "-------\n"); - CtdlRedirectOutput(sc->digestfp, -1); - CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 0); - CtdlRedirectOutput(NULL, -1); - sc->num_msgs_spooled += 1; + 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, 0L, 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; icm_fields['F']); ++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, NULL, NULL, ""); + free(valid); + } + + } + CtdlFreeMessage(msg); + } } /* @@ -482,7 +587,7 @@ void network_spool_msg(long msgnum, void *userdata) { */ if (sc->ignet_push_shares != NULL) { - msg = CtdlFetchMessage(msgnum); + msg = CtdlFetchMessage(msgnum, 1); if (msg != NULL) { size_t newpath_len; @@ -500,15 +605,6 @@ void network_spool_msg(long msgnum, void *userdata) { free(msg->cm_fields['P']); msg->cm_fields['P'] = newpath; - /* - * 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']); - } - msg->cm_fields['C'] = strdup(CC->room.QRname); - /* * Determine if this message is set to be deleted * after sending out on the network @@ -520,21 +616,16 @@ void network_spool_msg(long msgnum, void *userdata) { } } - /* - * Now serialize it for transmission - */ - serialize_message(&sermsg, msg); - /* Now send it to every node */ - for (nptr = sc->ignet_push_shares; nptr != NULL; - nptr = nptr->next) { + for (mptr = sc->ignet_push_shares; mptr != NULL; + mptr = mptr->next) { send = 1; /* Check for valid node name */ - if (is_valid_node(NULL, NULL, nptr->name) != 0) { + if (is_valid_node(NULL, NULL, mptr->remote_nodename) != 0) { lprintf(CTDL_ERR, "Invalid node <%s>\n", - nptr->name); + mptr->remote_nodename); send = 0; } @@ -543,26 +634,56 @@ void network_spool_msg(long msgnum, void *userdata) { 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, '!'); - if (!strcasecmp(buf, nptr->name)) { + 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 (strlen(mptr->remote_roomname) > 0) { + 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); + + /* write it to the spool file */ snprintf(filename, sizeof filename, - "./network/spoolout/%s", - nptr->name); +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolout/%s", + 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); } } - free(sermsg.ser); CtdlFreeMessage(msg); } } @@ -572,7 +693,7 @@ void network_spool_msg(long msgnum, void *userdata) { /* Delete this message if delete-after-send is set */ if (delete_after_send) { - CtdlDeleteMessages(CC->room.QRname, msgnum, ""); + CtdlDeleteMessages(CC->room.QRname, msgnum, "", 0); } } @@ -607,7 +728,8 @@ void network_deliver_digest(struct SpoolControl *sc) { sprintf(buf, "%ld", time(NULL)); msg->cm_fields['T'] = strdup(buf); msg->cm_fields['A'] = strdup(CC->room.QRname); - msg->cm_fields['U'] = strdup(CC->room.QRname); + snprintf(buf, sizeof buf, "[%s]", CC->room.QRname); + msg->cm_fields['U'] = strdup(buf); sprintf(buf, "room_%s@%s", CC->room.QRname, config.c_fqdn); for (i=0; idigestfp); sc->digestfp = NULL; - msgnum = CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM); + msgnum = CtdlSubmitMsg(msg, NULL, NULL, NULL, SMTP_SPOOLOUT_ROOM); CtdlFreeMessage(msg); /* Now generate the delivery instructions */ @@ -672,7 +794,7 @@ void network_deliver_digest(struct SpoolControl *sc) { imsg->cm_fields['M'] = instr; /* Save delivery instructions in spoolout room */ - CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM); + CtdlSubmitMsg(imsg, NULL, NULL, NULL, SMTP_SPOOLOUT_ROOM); CtdlFreeMessage(imsg); } @@ -684,9 +806,13 @@ 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; @@ -701,7 +827,6 @@ void network_spoolout_room(char *room_to_spool) { assoc_file_name(filename, sizeof filename, &CC->room, "netconfigs"); begin_critical_section(S_NETCONFIGS); - end_critical_section(S_NETCONFIGS); fp = fopen(filename, "r"); if (fp == NULL) { @@ -714,7 +839,7 @@ void network_spoolout_room(char *room_to_spool) { while (fgets(buf, sizeof buf, fp) != NULL) { buf[strlen(buf)-1] = 0; - extract(instr, buf, 0); + extract_token(instr, buf, 0, '|', sizeof instr); if (!strcasecmp(instr, "lastsent")) { sc.lastsent = extract_long(buf, 1); } @@ -722,22 +847,41 @@ void network_spoolout_room(char *room_to_spool) { nptr = (struct namelist *) malloc(sizeof(struct namelist)); nptr->next = sc.listrecps; - extract(nptr->name, buf, 1); + 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(nptr->name, buf, 1); + extract_token(nptr->name, buf, 1, '|', sizeof nptr->name); sc.digestrecps = nptr; } else if (!strcasecmp(instr, "ignet_push_share")) { - nptr = (struct namelist *) - malloc(sizeof(struct namelist)); - nptr->next = sc.ignet_push_shares; - extract(nptr->name, buf, 1); - sc.ignet_push_shares = nptr; + /* 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 (strlen(nexthop) == 0) { + 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 @@ -823,19 +967,29 @@ void network_spoolout_room(char *room_to_spool) { 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->name) == 0) { - fprintf(fp, "ignet_push_share|%s\n", - sc.ignet_push_shares->name); + 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 (strlen(sc.ignet_push_shares->remote_roomname) > 0) { + fprintf(fp, "|%s", sc.ignet_push_shares->remote_roomname); } - nptr = sc.ignet_push_shares->next; + fprintf(fp, "\n"); + mptr = sc.ignet_push_shares->next; free(sc.ignet_push_shares); - sc.ignet_push_shares = nptr; + sc.ignet_push_shares = mptr; } if (sc.misc != NULL) { fwrite(sc.misc, strlen(sc.misc), 1, fp); @@ -857,15 +1011,48 @@ void network_spoolout_room(char *room_to_spool) { 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; - /* Concise syntax because we don't need a full linked-list */ - memset(&sc, 0, sizeof(struct SpoolControl)); - sc.ignet_push_shares = (struct namelist *) - malloc(sizeof(struct namelist)); - sc.ignet_push_shares->next = NULL; - safestrncpy(sc.ignet_push_shares->name, - target_node, - sizeof sc.ignet_push_shares->name); + /* Grab the configuration line we're looking for */ + assoc_file_name(filename, sizeof filename, &CC->room, "netconfigs"); + 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, @@ -874,7 +1061,7 @@ int network_sync_to(char *target_node) { /* Concise cleanup because we know there's only one node in the sc */ free(sc.ignet_push_shares); - lprintf(CTDL_INFO, "Synchronized %d messages to <%s>\n", + lprintf(CTDL_NOTICE, "Synchronized %d messages to <%s>\n", num_spooled, target_node); return(num_spooled); } @@ -885,13 +1072,19 @@ int network_sync_to(char *target_node) { */ void cmd_nsyn(char *argbuf) { int num_spooled; - char target_node[SIZ]; + char target_node[256]; if (CtdlAccessCheck(ac_aide)) return; - extract(target_node, argbuf, 0); + extract_token(target_node, argbuf, 0, '|', sizeof target_node); num_spooled = network_sync_to(target_node); - cprintf("%d Spooled %d messages.\n", CIT_OK, num_spooled); + 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); + } } @@ -915,7 +1108,7 @@ void network_queue_room(struct ctdlroom *qrbuf, void *data) { * Learn topology from path fields */ void network_learn_topology(char *node, char *path) { - char nexthop[SIZ]; + char nexthop[256]; struct NetMap *nmptr; strcpy(nexthop, ""); @@ -923,8 +1116,9 @@ void network_learn_topology(char *node, char *path) { 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, '!'); + extract_token(nmptr->nexthop, path, 0, '!', sizeof nmptr->nexthop); nmptr->lastcontact = time(NULL); + ++netmap_changed; return; } } @@ -933,9 +1127,10 @@ void network_learn_topology(char *node, char *path) { nmptr = (struct NetMap *) malloc(sizeof (struct NetMap)); strcpy(nmptr->nodename, node); nmptr->lastcontact = time(NULL); - extract_token(nmptr->nexthop, path, 0, '!'); + extract_token(nmptr->nexthop, path, 0, '!', sizeof nmptr->nexthop); nmptr->next = the_netmap; the_netmap = nmptr; + ++netmap_changed; } @@ -1002,9 +1197,13 @@ void network_bounce(struct CtdlMessage *msg, char *reason) { 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)"); /* prepend our node to the path */ if (msg->cm_fields['P'] != NULL) { @@ -1034,7 +1233,7 @@ void network_bounce(struct CtdlMessage *msg, char *reason) { if ( (valid == NULL) && (strlen(force_room) == 0) ) { strcpy(force_room, config.c_aideroom); } - CtdlSubmitMsg(msg, valid, force_room); + CtdlSubmitMsg(msg, valid, NULL, NULL, force_room); /* Clean up */ if (valid != NULL) free(valid); @@ -1077,11 +1276,11 @@ void network_process_buffer(char *buffer, long size) { strcpy(target_room, TWITROOM); /* Load the message into memory */ - msg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); - memset(msg, 0, sizeof(struct CtdlMessage)); - msg->cm_magic = CTDLMESSAGE_MAGIC; - msg->cm_anon_type = buffer[1]; - msg->cm_format_type = buffer[2]; + msg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); + memset(msg, 0, sizeof(struct CtdlMessage)); + msg->cm_magic = CTDLMESSAGE_MAGIC; + msg->cm_anon_type = buffer[1]; + msg->cm_format_type = buffer[2]; for (pos = 3; pos < size; ++pos) { field = buffer[pos]; @@ -1120,13 +1319,22 @@ void network_process_buffer(char *buffer, long size) { strcpy(nexthop, msg->cm_fields['D']); } snprintf(filename, sizeof filename, - "./network/spoolout/%s", nexthop); +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolout/%s", nexthop); + 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(sermsg.ser); CtdlFreeMessage(msg); return; @@ -1179,7 +1387,7 @@ void network_process_buffer(char *buffer, long size) { msg = NULL; free(recp); return; - } + } strcpy(target_room, ""); /* no target room if mail */ } @@ -1205,7 +1413,7 @@ void network_process_buffer(char *buffer, long size) { /* save the message into a room */ if (PerformNetprocHooks(msg, target_room) == 0) { msg->cm_flags = CM_SKIP_HOOKS; - CtdlSubmitMsg(msg, recp, target_room); + CtdlSubmitMsg(msg, recp, NULL, NULL, target_room); } CtdlFreeMessage(msg); free(recp); @@ -1284,22 +1492,33 @@ void network_process_file(char *filename) { void network_do_spoolin(void) { DIR *dp; struct dirent *d; - char filename[SIZ]; + char filename[256]; - dp = opendir("./network/spoolin"); + dp = opendir( +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolin"); if (dp == NULL) return; while (d = readdir(dp), d != NULL) { - snprintf(filename, sizeof filename, - "./network/spoolin/%s", d->d_name); - network_process_file(filename); + if ((strcmp(d->d_name, ".")) && (strcmp(d->d_name, ".."))) { + snprintf(filename, sizeof filename, +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolin/%s", d->d_name); + network_process_file(filename); + } } - closedir(dp); } - /* * Delete any files in the outbound queue that were intended * to be sent to nodes which no longer exist. @@ -1307,16 +1526,29 @@ void network_do_spoolin(void) { void network_purge_spoolout(void) { DIR *dp; struct dirent *d; - char filename[SIZ]; - char nexthop[SIZ]; + char filename[256]; + char nexthop[256]; int i; - dp = opendir("./network/spoolout"); + dp = opendir( +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolout"); if (dp == NULL) return; while (d = readdir(dp), d != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; snprintf(filename, sizeof filename, - "./network/spoolout/%s", d->d_name); +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolout/%s", d->d_name); strcpy(nexthop, ""); i = is_valid_node(nexthop, NULL, d->d_name); @@ -1331,7 +1563,6 @@ void network_purge_spoolout(void) { } - /* * receive network spool from the remote system */ @@ -1397,9 +1628,19 @@ void receive_spool(int sock, char *remote_nodename) { unlink(tempfilename); return; } - lprintf(CTDL_DEBUG, "%s\n", buf); - snprintf(buf, sizeof buf, "mv %s ./network/spoolin/%s.%ld", - tempfilename, remote_nodename, (long) getpid()); + if (download_len > 0) + lprintf(CTDL_NOTICE, "Received %ld octets from <%s>", + download_len, remote_nodename); + lprintf(CTDL_DEBUG, "%s", buf); + /* TODO: make move inline. forking is verry expensive. */ + snprintf(buf, sizeof buf, "mv %s " +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolin/%s.%ld", + tempfilename, remote_nodename, (long) getpid()); system(buf); } @@ -1413,7 +1654,7 @@ void transmit_spool(int sock, char *remote_nodename) char buf[SIZ]; char pbuf[4096]; long plen; - long bytes_to_write, thisblock; + long bytes_to_write, thisblock, bytes_written; int fd; char sfname[128]; @@ -1424,17 +1665,22 @@ void transmit_spool(int sock, char *remote_nodename) return; } - snprintf(sfname, sizeof sfname, "./network/spoolout/%s", remote_nodename); + snprintf(sfname, sizeof sfname, +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif /* HAVE_SPOOL_DIR */ + "/network/spoolout/%s", remote_nodename); fd = open(sfname, O_RDONLY); if (fd < 0) { - if (errno == ENOENT) { - lprintf(CTDL_INFO, "Nothing to send.\n"); - } else { + if (errno != ENOENT) { lprintf(CTDL_CRIT, "cannot open upload file locally: %s\n", strerror(errno)); } return; } + bytes_written = 0; while (plen = (long) read(fd, pbuf, IGNET_PACKET_SIZE), plen > 0L) { bytes_to_write = plen; while (bytes_to_write > 0L) { @@ -1454,7 +1700,8 @@ void transmit_spool(int sock, char *remote_nodename) close(fd); return; } - bytes_to_write = bytes_to_write - thisblock; + bytes_to_write -= thisblock; + bytes_written += thisblock; } else { goto ABORTUPL; } @@ -1465,8 +1712,11 @@ ABORTUPL: close(fd); if (sock_puts(sock, "UCLS 1") < 0) return; if (sock_gets(sock, buf) < 0) return; + lprintf(CTDL_NOTICE, "Sent %ld octets to <%s>\n", + bytes_written, remote_nodename); lprintf(CTDL_DEBUG, "<%s\n", buf); if (buf[0] == '2') { + lprintf(CTDL_DEBUG, "Removing <%s>\n", sfname); unlink(sfname); } } @@ -1482,7 +1732,7 @@ void network_poll_node(char *node, char *secret, char *host, char *port) { if (network_talking_to(node, NTT_CHECK)) return; network_talking_to(node, NTT_ADD); - lprintf(CTDL_INFO, "Polling node <%s> at %s:%s\n", node, host, port); + lprintf(CTDL_NOTICE, "Connecting to <%s> at %s:%s\n", node, host, port); sock = sock_connect(host, port, "tcp"); if (sock < 0) { @@ -1523,29 +1773,37 @@ bail: sock_close(sock); */ void network_poll_other_citadel_nodes(int full_poll) { int i; - char linebuf[SIZ]; + char linebuf[256]; char node[SIZ]; - char host[SIZ]; - char port[SIZ]; - char secret[SIZ]; + char host[256]; + char port[256]; + char secret[256]; int poll = 0; - char spoolfile[SIZ]; + char spoolfile[256]; - if (ignetcfg == NULL) return; /* no nodes defined */ + if (working_ignetcfg == NULL) { + lprintf(CTDL_DEBUG, "No nodes defined - not polling\n"); + return; + } /* Use the string tokenizer to grab one line at a time */ - for (i=0; i 0) && (strlen(secret) > 0) && (strlen(host) > 0) && strlen(port) > 0) { poll = full_poll; if (poll == 0) { - sprintf(spoolfile, "./network/spoolout/%s", - node); + snprintf(spoolfile, sizeof spoolfile, +#ifndef HAVE_SPOOL_DIR + "." +#else + SPOOL_DIR +#endif + "/network/spoolout/%s", node); if (access(spoolfile, R_OK) == 0) { poll = 1; } @@ -1561,6 +1819,24 @@ void network_poll_other_citadel_nodes(int full_poll) { +/* + * It's ok if these directories already exist. Just fail silently. + */ +void create_spool_dirs(void) { +#ifndef HAVE_SPOOL_DIR + mkdir("./network", 0700); + mkdir("./network/systems", 0700); + mkdir("./network/spoolin", 0700); + mkdir("./network/spoolout", 0700); +#else + mkdir(SPOOL_DIR "/network", 0700); + mkdir(SPOOL_DIR "/network/systems", 0700); + mkdir(SPOOL_DIR "/network/spoolin", 0700); + mkdir(SPOOL_DIR "/network/spoolout", 0700); +#endif /* HAVE_SPOOL_DIR */ +} + + @@ -1582,6 +1858,8 @@ void network_do_queue(void) { full_processing = 0; } + create_spool_dirs(); + /* * 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 @@ -1592,9 +1870,7 @@ void network_do_queue(void) { doing_queue = 1; /* Load the IGnet Configuration into memory */ - if (ignetcfg == NULL) { - ignetcfg = CtdlGetSysConfig(IGNETCFG); - } + load_working_ignetcfg(); /* * Poll other Citadel nodes. Maybe. If "full_processing" is set @@ -1613,10 +1889,10 @@ void network_do_queue(void) { * Go ahead and run the queue */ if (full_processing) { - lprintf(CTDL_INFO, "network: loading outbound queue\n"); + lprintf(CTDL_DEBUG, "network: loading outbound queue\n"); ForEachRoom(network_queue_room, NULL); - lprintf(CTDL_INFO, "network: running outbound queue\n"); + lprintf(CTDL_DEBUG, "network: running outbound queue\n"); while (rplist != NULL) { network_spoolout_room(rplist->name); ptr = rplist; @@ -1625,7 +1901,7 @@ void network_do_queue(void) { } } - lprintf(CTDL_INFO, "network: processing inbound queue\n"); + lprintf(CTDL_DEBUG, "network: processing inbound queue\n"); network_do_spoolin(); /* Save the network map back to disk */ @@ -1637,7 +1913,7 @@ void network_do_queue(void) { network_purge_spoolout(); - lprintf(CTDL_INFO, "network: queue run completed\n"); + lprintf(CTDL_DEBUG, "network: queue run completed\n"); if (full_processing) { last_run = time(NULL); @@ -1649,94 +1925,68 @@ void network_do_queue(void) { /* * cmd_netp() - authenticate to the server as another Citadel node polling - * for network traffic + * for network traffic */ void cmd_netp(char *cmdbuf) { - char node[SIZ]; - char pass[SIZ]; + char node[256]; + char pass[256]; + int v; - char secret[SIZ]; - char nexthop[SIZ]; + char secret[256]; + char nexthop[256]; + + /* Authenticate */ + extract_token(node, cmdbuf, 0, '|', sizeof node); + extract_token(pass, cmdbuf, 1, '|', sizeof pass); if (doing_queue) { - cprintf("%d spooling - try again in a few minutes\n", ERROR + RESOURCE_BUSY); + lprintf(CTDL_WARNING, "Network node <%s> refused - spooling", node); + cprintf("%d spooling - try again in a few minutes\n", + ERROR + RESOURCE_BUSY); return; } - extract(node, cmdbuf, 0); - extract(pass, cmdbuf, 1); + /* load the IGnet Configuration to check node validity */ + load_working_ignetcfg(); + v = is_valid_node(nexthop, secret, node); - if (is_valid_node(nexthop, secret, node) != 0) { - cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED); + if (v != 0) { + lprintf(CTDL_WARNING, "Unknown node <%s>\n", node); + cprintf("%d authentication failed\n", + ERROR + PASSWORD_REQUIRED); return; } if (strcasecmp(pass, secret)) { + lprintf(CTDL_WARNING, "Bad password for network node <%s>", node); cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED); return; } if (network_talking_to(node, NTT_CHECK)) { + lprintf(CTDL_WARNING, "Duplicate session for network node <%s>", node); cprintf("%d Already talking to %s right now\n", ERROR + RESOURCE_BUSY, node); return; } safestrncpy(CC->net_node, node, sizeof CC->net_node); network_talking_to(node, NTT_ADD); + lprintf(CTDL_NOTICE, "Network node <%s> logged in\n", CC->net_node); cprintf("%d authenticated as network node '%s'\n", CIT_OK, CC->net_node); } - -/* - * This handler detects changes being made to the system's IGnet - * configuration. - */ -int netconfig_aftersave(struct CtdlMessage *msg) { - char *ptr; - int linelen; - - /* If this isn't the configuration room, or if this isn't a MIME - * message, don't bother. - */ - if (strcasecmp(msg->cm_fields['O'], SYSCONFIGROOM)) return(0); - if (msg->cm_format_type != 4) return(0); - - ptr = msg->cm_fields['M']; - while (ptr != NULL) { - - linelen = strcspn(ptr, "\n"); - if (linelen == 0) return(0); /* end of headers */ - - if (!strncasecmp(ptr, "Content-type: ", 14)) { - if (!strncasecmp(&ptr[14], IGNETCFG, - strlen(IGNETCFG))) { - if (ignetcfg != NULL) free(ignetcfg); - ignetcfg = NULL; - } - } - - ptr = strchr((char *)ptr, '\n'); - if (ptr != NULL) ++ptr; - } - - return(0); -} - - - - /* * Module entry point */ char *serv_network_init(void) { + create_spool_dirs(); CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config"); CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config"); CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller"); CtdlRegisterProtoHook(cmd_nsyn, "NSYN", "Synchronize room to node"); CtdlRegisterSessionHook(network_do_queue, EVT_TIMER); - CtdlRegisterMessageHook(netconfig_aftersave, EVT_AFTERSAVE); return "$Id$"; }