Back to index

citadel  8.12
serv_network.c
Go to the documentation of this file.
00001 /*
00002  * This module handles shared rooms, inter-Citadel mail, and outbound
00003  * mailing list processing.
00004  *
00005  * Copyright (c) 2000-2012 by the citadel.org team
00006  *
00007  *  This program is open source software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 3 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  *
00021  * ** NOTE **   A word on the S_NETCONFIGS semaphore:
00022  * This is a fairly high-level type of critical section.  It ensures that no
00023  * two threads work on the netconfigs files at the same time.  Since we do
00024  * so many things inside these, here are the rules:
00025  *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
00026  *  2. Do *not* perform any I/O with the client during these sections.
00027  *
00028  */
00029 
00030 /*
00031  * Duration of time (in seconds) after which pending list subscribe/unsubscribe
00032  * requests that have not been confirmed will be deleted.
00033  */
00034 #define EXP   259200 /* three days */
00035 
00036 #include "sysdep.h"
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 #include <stdio.h>
00040 #include <fcntl.h>
00041 #include <ctype.h>
00042 #include <signal.h>
00043 #include <pwd.h>
00044 #include <errno.h>
00045 #include <sys/stat.h>
00046 #include <sys/types.h>
00047 #include <dirent.h>
00048 #if TIME_WITH_SYS_TIME
00049 # include <sys/time.h>
00050 # include <time.h>
00051 #else
00052 # if HAVE_SYS_TIME_H
00053 #  include <sys/time.h>
00054 # else
00055 #  include <time.h>
00056 # endif
00057 #endif
00058 #ifdef HAVE_SYSCALL_H
00059 # include <syscall.h>
00060 #else 
00061 # if HAVE_SYS_SYSCALL_H
00062 #  include <sys/syscall.h>
00063 # endif
00064 #endif
00065 
00066 #include <sys/wait.h>
00067 #include <string.h>
00068 #include <limits.h>
00069 #include <libcitadel.h>
00070 #include "citadel.h"
00071 #include "server.h"
00072 #include "citserver.h"
00073 #include "support.h"
00074 #include "config.h"
00075 #include "user_ops.h"
00076 #include "database.h"
00077 #include "msgbase.h"
00078 #include "internet_addressing.h"
00079 #include "serv_network.h"
00080 #include "clientsocket.h"
00081 #include "file_ops.h"
00082 #include "citadel_dirs.h"
00083 #include "threads.h"
00084 
00085 #ifndef HAVE_SNPRINTF
00086 #include "snprintf.h"
00087 #endif
00088 
00089 #include "context.h"
00090 #include "netconfig.h"
00091 #include "netspool.h"
00092 #include "netmail.h"
00093 #include "ctdl_module.h"
00094 
00095 int NetQDebugEnabled = 0;
00096 struct CitContext networker_spool_CC;
00097 
00098 /* comes from lookup3.c from libcitadel... */
00099 extern uint32_t hashlittle( const void *key, size_t length, uint32_t initval);
00100 
00101 typedef struct __roomlists {
00102        RoomProcList *rplist;
00103        HashList *RoomsInterestedIn;
00104 }roomlists;
00105 /*
00106  * When we do network processing, it's accomplished in two passes; one to
00107  * gather a list of rooms and one to actually do them.  It's ok that rplist
00108  * is global; we have a mutex that keeps it safe.
00109  */
00110 struct RoomProcList *rplist = NULL;
00111 
00112 int GetNetworkedRoomNumbers(const char *DirName, HashList *DirList)
00113 {
00114        DIR *filedir = NULL;
00115        struct dirent *d;
00116        struct dirent *filedir_entry;
00117        long RoomNR;
00118        long Count = 0;
00119               
00120        filedir = opendir (DirName);
00121        if (filedir == NULL) {
00122               return 0;
00123        }
00124 
00125        d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
00126        if (d == NULL) {
00127               return 0;
00128        }
00129 
00130        while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
00131               (filedir_entry != NULL))
00132        {
00133               RoomNR = atol(filedir_entry->d_name);
00134               if (RoomNR != 0) {
00135                      Count++;
00136                      Put(DirList, LKEY(RoomNR), &Count, reference_free_handler);
00137               }
00138        }
00139        free(d);
00140        closedir(filedir);
00141        return Count;
00142 }
00143 
00144 
00145 
00146 
00147 /*
00148  * Check the use table.  This is a list of messages which have recently
00149  * arrived on the system.  It is maintained and queried to prevent the same
00150  * message from being entered into the database multiple times if it happens
00151  * to arrive multiple times by accident.
00152  */
00153 int network_usetable(struct CtdlMessage *msg)
00154 {
00155        struct CitContext *CCC = CC;
00156        char msgid[SIZ];
00157        struct cdbdata *cdbut;
00158        struct UseTable ut;
00159 
00160        /* Bail out if we can't generate a message ID */
00161        if (msg == NULL) {
00162               return(0);
00163        }
00164        if (msg->cm_fields['I'] == NULL) {
00165               return(0);
00166        }
00167        if (IsEmptyStr(msg->cm_fields['I'])) {
00168               return(0);
00169        }
00170 
00171        /* Generate the message ID */
00172        strcpy(msgid, msg->cm_fields['I']);
00173        if (haschar(msgid, '@') == 0) {
00174               strcat(msgid, "@");
00175               if (msg->cm_fields['N'] != NULL) {
00176                      strcat(msgid, msg->cm_fields['N']);
00177               }
00178               else {
00179                      return(0);
00180               }
00181        }
00182 
00183        cdbut = cdb_fetch(CDB_USETABLE, msgid, strlen(msgid));
00184        if (cdbut != NULL) {
00185               cdb_free(cdbut);
00186               QN_syslog(LOG_DEBUG, "network_usetable() : we already have %s\n", msgid);
00187               return(1);
00188        }
00189 
00190        /* If we got to this point, it's unique: add it. */
00191        strcpy(ut.ut_msgid, msgid);
00192        ut.ut_timestamp = time(NULL);
00193        cdb_store(CDB_USETABLE, msgid, strlen(msgid), &ut, sizeof(struct UseTable) );
00194        return(0);
00195 }
00196 
00197 
00198 
00199 
00200 
00201 
00202 
00203 
00204 
00205 
00206 /*
00207  * Send the *entire* contents of the current room to one specific network node,
00208  * ignoring anything we know about which messages have already undergone
00209  * network processing.  This can be used to bring a new node into sync.
00210  */
00211 int network_sync_to(char *target_node, long len)
00212 {
00213        struct CitContext *CCC = CC;
00214        SpoolControl sc;
00215        int num_spooled = 0;
00216        int found_node = 0;
00217        char buf[256];
00218        char sc_type[256];
00219        char sc_node[256];
00220        char sc_room[256];
00221        char filename[PATH_MAX];
00222        FILE *fp;
00223 
00224        /* Grab the configuration line we're looking for */
00225        assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
00226        begin_critical_section(S_NETCONFIGS);
00227        fp = fopen(filename, "r");
00228        if (fp == NULL) {
00229               end_critical_section(S_NETCONFIGS);
00230               return(-1);
00231        }
00232        while (fgets(buf, sizeof buf, fp) != NULL)
00233        {
00234               buf[strlen(buf)-1] = 0;
00235 
00236               extract_token(sc_type, buf, 0, '|', sizeof sc_type);
00237               if (strcasecmp(sc_type, "ignet_push_share"))
00238                      continue;
00239 
00240               extract_token(sc_node, buf, 1, '|', sizeof sc_node);
00241               if (strcasecmp(sc_node, target_node))
00242                      continue;
00243 
00244               extract_token(sc_room, buf, 2, '|', sizeof sc_room);
00245               found_node = 1;
00246                      
00247               /* Concise syntax because we don't need a full linked-list */
00248               memset(&sc, 0, sizeof(SpoolControl));
00249               sc.ignet_push_shares = (maplist *)
00250                      malloc(sizeof(maplist));
00251               sc.ignet_push_shares->next = NULL;
00252               safestrncpy(sc.ignet_push_shares->remote_nodename,
00253                          sc_node,
00254                          sizeof sc.ignet_push_shares->remote_nodename);
00255               safestrncpy(sc.ignet_push_shares->remote_roomname,
00256                          sc_room,
00257                          sizeof sc.ignet_push_shares->remote_roomname);
00258        }
00259        fclose(fp);
00260        end_critical_section(S_NETCONFIGS);
00261 
00262        if (!found_node) return(-1);
00263 
00264        sc.working_ignetcfg = load_ignetcfg();
00265        sc.the_netmap = read_network_map();
00266 
00267        /* Send ALL messages */
00268        num_spooled = CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
00269               network_spool_msg, &sc);
00270 
00271        /* Concise cleanup because we know there's only one node in the sc */
00272        free(sc.ignet_push_shares);
00273 
00274        DeleteHash(&sc.working_ignetcfg);
00275        DeleteHash(&sc.the_netmap);
00276 
00277        QN_syslog(LOG_NOTICE, "Synchronized %d messages to <%s>\n",
00278                 num_spooled, target_node);
00279        return(num_spooled);
00280 }
00281 
00282 
00283 /*
00284  * Implements the NSYN command
00285  */
00286 void cmd_nsyn(char *argbuf) {
00287        int num_spooled;
00288        long len;
00289        char target_node[256];
00290 
00291        if (CtdlAccessCheck(ac_aide)) return;
00292 
00293        len = extract_token(target_node, argbuf, 0, '|', sizeof target_node);
00294        num_spooled = network_sync_to(target_node, len);
00295        if (num_spooled >= 0) {
00296               cprintf("%d Spooled %d messages.\n", CIT_OK, num_spooled);
00297        }
00298        else {
00299               cprintf("%d No such room/node share exists.\n",
00300                      ERROR + ROOM_NOT_FOUND);
00301        }
00302 }
00303 
00304 
00305 
00306 /*
00307  * Batch up and send all outbound traffic from the current room
00308  */
00309 void network_queue_interesting_rooms(struct ctdlroom *qrbuf, void *data) {
00310        int i;
00311        struct RoomProcList *ptr;
00312        long QRNum = qrbuf->QRnumber;
00313        void *v;
00314        roomlists *RP = (roomlists*) data;
00315 
00316        if (!GetHash(RP->RoomsInterestedIn, LKEY(QRNum), &v))
00317               return;
00318 
00319        ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
00320        if (ptr == NULL) return;
00321 
00322        ptr->namelen = strlen(qrbuf->QRname);
00323        if (ptr->namelen > ROOMNAMELEN)
00324               ptr->namelen = ROOMNAMELEN - 1;
00325 
00326        memcpy (ptr->name, qrbuf->QRname, ptr->namelen);
00327        ptr->name[ptr->namelen] = '\0';
00328        ptr->QRNum = qrbuf->QRnumber;
00329 
00330        for (i = 0; i < ptr->namelen; i++)
00331        {
00332               ptr->lcname[i] = tolower(ptr->name[i]);
00333        }
00334 
00335        ptr->lcname[ptr->namelen] = '\0';
00336        ptr->key = hashlittle(ptr->lcname, ptr->namelen, 9872345);
00337        ptr->next = RP->rplist;
00338        RP->rplist = ptr;
00339 }
00340 
00341 /*
00342  * Batch up and send all outbound traffic from the current room
00343  */
00344 void network_queue_room(struct ctdlroom *qrbuf, void *data) {
00345        int i;
00346        struct RoomProcList *ptr;
00347 
00348        if (qrbuf->QRdefaultview == VIEW_QUEUE)
00349               return;
00350        ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
00351        if (ptr == NULL) return;
00352 
00353        ptr->namelen = strlen(qrbuf->QRname);
00354        if (ptr->namelen > ROOMNAMELEN)
00355               ptr->namelen = ROOMNAMELEN - 1;
00356 
00357        memcpy (ptr->name, qrbuf->QRname, ptr->namelen);
00358        ptr->name[ptr->namelen] = '\0';
00359        ptr->QRNum = qrbuf->QRnumber;
00360 
00361        for (i = 0; i < ptr->namelen; i++)
00362        {
00363               ptr->lcname[i] = tolower(ptr->name[i]);
00364        }
00365        ptr->lcname[ptr->namelen] = '\0';
00366        ptr->key = hashlittle(ptr->lcname, ptr->namelen, 9872345);
00367 
00368        begin_critical_section(S_RPLIST);
00369        ptr->next = rplist;
00370        rplist = ptr;
00371        end_critical_section(S_RPLIST);
00372 }
00373 
00374 void destroy_network_queue_room(RoomProcList *rplist)
00375 {
00376        struct RoomProcList *cur, *p;
00377 
00378        cur = rplist;
00379        while (cur != NULL)
00380        {
00381               p = cur->next;
00382               free (cur);
00383               cur = p;             
00384        }
00385 }
00386 
00387 void destroy_network_queue_room_locked (void)
00388 {
00389        begin_critical_section(S_RPLIST);
00390        destroy_network_queue_room(rplist);
00391        end_critical_section(S_RPLIST);
00392 }
00393 
00394 
00395 
00396 /*
00397  * Bounce a message back to the sender
00398  */
00399 void network_bounce(struct CtdlMessage *msg, char *reason)
00400 {
00401        struct CitContext *CCC = CC;
00402        char *oldpath = NULL;
00403        char buf[SIZ];
00404        char bouncesource[SIZ];
00405        char recipient[SIZ];
00406        struct recptypes *valid = NULL;
00407        char force_room[ROOMNAMELEN];
00408        static int serialnum = 0;
00409        size_t size;
00410 
00411        QNM_syslog(LOG_DEBUG, "entering network_bounce()\n");
00412 
00413        if (msg == NULL) return;
00414 
00415        snprintf(bouncesource, sizeof bouncesource, "%s@%s", BOUNCESOURCE, config.c_nodename);
00416 
00417        /* 
00418         * Give it a fresh message ID
00419         */
00420        if (msg->cm_fields['I'] != NULL) {
00421               free(msg->cm_fields['I']);
00422        }
00423        snprintf(buf, sizeof buf, "%ld.%04lx.%04x@%s",
00424               (long)time(NULL), (long)getpid(), ++serialnum, config.c_fqdn);
00425        msg->cm_fields['I'] = strdup(buf);
00426 
00427        /*
00428         * FIXME ... right now we're just sending a bounce; we really want to
00429         * include the text of the bounced message.
00430         */
00431        if (msg->cm_fields['M'] != NULL) {
00432               free(msg->cm_fields['M']);
00433        }
00434        msg->cm_fields['M'] = strdup(reason);
00435        msg->cm_format_type = 0;
00436 
00437        /*
00438         * Turn the message around
00439         */
00440        if (msg->cm_fields['R'] == NULL) {
00441               free(msg->cm_fields['R']);
00442        }
00443 
00444        if (msg->cm_fields['D'] == NULL) {
00445               free(msg->cm_fields['D']);
00446        }
00447 
00448        snprintf(recipient, sizeof recipient, "%s@%s",
00449               msg->cm_fields['A'], msg->cm_fields['N']);
00450 
00451        if (msg->cm_fields['A'] == NULL) {
00452               free(msg->cm_fields['A']);
00453        }
00454 
00455        if (msg->cm_fields['N'] == NULL) {
00456               free(msg->cm_fields['N']);
00457        }
00458 
00459        if (msg->cm_fields['U'] == NULL) {
00460               free(msg->cm_fields['U']);
00461        }
00462 
00463        msg->cm_fields['A'] = strdup(BOUNCESOURCE);
00464        msg->cm_fields['N'] = strdup(config.c_nodename);
00465        msg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
00466 
00467        /* prepend our node to the path */
00468        if (msg->cm_fields['P'] != NULL) {
00469               oldpath = msg->cm_fields['P'];
00470               msg->cm_fields['P'] = NULL;
00471        }
00472        else {
00473               oldpath = strdup("unknown_user");
00474        }
00475        size = strlen(oldpath) + SIZ;
00476        msg->cm_fields['P'] = malloc(size);
00477        snprintf(msg->cm_fields['P'], size, "%s!%s", config.c_nodename, oldpath);
00478        free(oldpath);
00479 
00480        /* Now submit the message */
00481        valid = validate_recipients(recipient, NULL, 0);
00482        if (valid != NULL) if (valid->num_error != 0) {
00483               free_recipients(valid);
00484               valid = NULL;
00485        }
00486        if ( (valid == NULL) || (!strcasecmp(recipient, bouncesource)) ) {
00487               strcpy(force_room, config.c_aideroom);
00488        }
00489        else {
00490               strcpy(force_room, "");
00491        }
00492        if ( (valid == NULL) && IsEmptyStr(force_room) ) {
00493               strcpy(force_room, config.c_aideroom);
00494        }
00495        CtdlSubmitMsg(msg, valid, force_room, 0);
00496 
00497        /* Clean up */
00498        if (valid != NULL) free_recipients(valid);
00499        CtdlFreeMessage(msg);
00500        QNM_syslog(LOG_DEBUG, "leaving network_bounce()\n");
00501 }
00502 
00503 
00504 
00505 
00506 
00507 
00508 
00509 /*
00510  * network_do_queue()
00511  * 
00512  * Run through the rooms doing various types of network stuff.
00513  */
00514 void network_do_queue(void)
00515 {
00516        struct CitContext *CCC = CC;
00517        static int doing_queue = 0;
00518        static time_t last_run = 0L;
00519        int full_processing = 1;
00520        HashList *working_ignetcfg;
00521        HashList *the_netmap = NULL;
00522        int netmap_changed = 0;
00523        roomlists RL;
00524 
00525        /*
00526         * Run the full set of processing tasks no more frequently
00527         * than once every n seconds
00528         */
00529        if ( (time(NULL) - last_run) < config.c_net_freq ) {
00530               full_processing = 0;
00531               syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
00532                      config.c_net_freq - (time(NULL)- last_run)
00533               );
00534        }
00535 
00536        /*
00537         * This is a simple concurrency check to make sure only one queue run
00538         * is done at a time.  We could do this with a mutex, but since we
00539         * don't really require extremely fine granularity here, we'll do it
00540         * with a static variable instead.
00541         */
00542        if (doing_queue) {
00543               return;
00544        }
00545        doing_queue = 1;
00546 
00547        become_session(&networker_spool_CC);
00548        begin_critical_section(S_RPLIST);
00549        RL.rplist = rplist;
00550        rplist = NULL;
00551        end_critical_section(S_RPLIST);
00552 
00553        RL.RoomsInterestedIn = NewHash(1, lFlathash);
00554        if (full_processing &&
00555            (GetNetworkedRoomNumbers(ctdl_netcfg_dir, RL.RoomsInterestedIn)==0))
00556        {
00557               doing_queue = 0;
00558               DeleteHash(&RL.RoomsInterestedIn);
00559               if (RL.rplist == NULL)
00560                      return;
00561        }
00562        /* Load the IGnet Configuration into memory */
00563        working_ignetcfg = load_ignetcfg();
00564 
00565        /*
00566         * Load the network map and filter list into memory.
00567         */
00568        if (!server_shutting_down)
00569               the_netmap = read_network_map();
00570        if (!server_shutting_down)
00571               load_network_filter_list();
00572 
00573        /* 
00574         * Go ahead and run the queue
00575         */
00576        if (full_processing && !server_shutting_down) {
00577               QNM_syslog(LOG_DEBUG, "network: loading outbound queue");
00578               CtdlForEachRoom(network_queue_interesting_rooms, &RL);
00579        }
00580 
00581        if ((RL.rplist != NULL) && (!server_shutting_down)) {
00582               RoomProcList *ptr, *cmp;
00583               ptr = RL.rplist;
00584               QNM_syslog(LOG_DEBUG, "network: running outbound queue");
00585               while (ptr != NULL && !server_shutting_down) {
00586                      
00587                      cmp = ptr->next;
00588 
00589                      while (cmp != NULL) {
00590                             if ((cmp->namelen > 0) &&
00591                                 (cmp->key == ptr->key) &&
00592                                 (cmp->namelen == ptr->namelen) &&
00593                                 (strcmp(cmp->lcname, ptr->lcname) == 0))
00594                             {
00595                                    cmp->namelen = 0;
00596                             }
00597                             cmp = cmp->next;
00598                      }
00599 
00600                      if (ptr->namelen > 0) {
00601                             network_spoolout_room(ptr, 
00602                                                 working_ignetcfg,
00603                                                 the_netmap);
00604                      }
00605                      ptr = ptr->next;
00606               }
00607        }
00608 
00609        /* If there is anything in the inbound queue, process it */
00610        if (!server_shutting_down) {
00611               network_do_spoolin(working_ignetcfg, 
00612                                the_netmap,
00613                                &netmap_changed);
00614        }
00615 
00616        /* Free the filter list in memory */
00617        free_netfilter_list();
00618 
00619        /* Save the network map back to disk */
00620        if (netmap_changed) {
00621               StrBuf *MapStr = SerializeNetworkMap(the_netmap);
00622               CtdlPutSysConfig(IGNETMAP, SmashStrBuf(&MapStr));
00623        }
00624 
00625        /* combine singe message files into one spool entry per remote node. */
00626        network_consolidate_spoolout(working_ignetcfg, the_netmap);
00627 
00628        /* shut down. */
00629 
00630        DeleteHash(&the_netmap);
00631 
00632        DeleteHash(&working_ignetcfg);
00633 
00634        QNM_syslog(LOG_DEBUG, "network: queue run completed");
00635 
00636        if (full_processing) {
00637               last_run = time(NULL);
00638        }
00639        DeleteHash(&RL.RoomsInterestedIn);
00640        destroy_network_queue_room(RL.rplist);
00641        doing_queue = 0;
00642 }
00643 
00644 
00645 
00646 
00647 int network_room_handler (struct ctdlroom *room)
00648 {
00649        network_queue_room(room, NULL);
00650        return 0;
00651 }
00652 
00653 int NTTDebugEnabled = 0;
00654 
00655 /*
00656  * network_talking_to()  --  concurrency checker
00657  */
00658 static HashList *nttlist = NULL;
00659 int network_talking_to(const char *nodename, long len, int operation) {
00660 
00661        int retval = 0;
00662        HashPos *Pos = NULL;
00663        void *vdata;
00664 
00665        begin_critical_section(S_NTTLIST);
00666 
00667        switch(operation) {
00668 
00669               case NTT_ADD:
00670                      if (nttlist == NULL) 
00671                             nttlist = NewHash(1, NULL);
00672                      Put(nttlist, nodename, len, NewStrBufPlain(nodename, len), HFreeStrBuf);
00673                      if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: added <%s>\n", nodename);
00674                      break;
00675               case NTT_REMOVE:
00676                      if ((nttlist == NULL) ||
00677                          (GetCount(nttlist) == 0))
00678                             break;
00679                      Pos = GetNewHashPos(nttlist, 1);
00680                      if (GetHashPosFromKey (nttlist, nodename, len, Pos))
00681                             DeleteEntryFromHash(nttlist, Pos);
00682                      DeleteHashPos(&Pos);
00683                      if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: removed <%s>\n", nodename);
00684 
00685                      break;
00686 
00687               case NTT_CHECK:
00688                      if ((nttlist == NULL) ||
00689                          (GetCount(nttlist) == 0))
00690                             break;
00691                      if (GetHash(nttlist, nodename, len, &vdata))
00692                             retval ++;
00693                      if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: have [%d] <%s>\n", retval, nodename);
00694                      break;
00695        }
00696 
00697        end_critical_section(S_NTTLIST);
00698        return(retval);
00699 }
00700 
00701 void cleanup_nttlist(void)
00702 {
00703         begin_critical_section(S_NTTLIST);
00704        DeleteHash(&nttlist);
00705         end_critical_section(S_NTTLIST);
00706 }
00707 
00708 
00709 
00710 void network_logout_hook(void)
00711 {
00712        CitContext *CCC = MyContext();
00713 
00714        /*
00715         * If we were talking to a network node, we're not anymore...
00716         */
00717        if (!IsEmptyStr(CCC->net_node)) {
00718               network_talking_to(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
00719               CCC->net_node[0] = '\0';
00720        }
00721 }
00722 void network_cleanup_function(void)
00723 {
00724        struct CitContext *CCC = CC;
00725 
00726        if (!IsEmptyStr(CCC->net_node)) {
00727               network_talking_to(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
00728               CCC->net_node[0] = '\0';
00729        }
00730 }
00731 
00732 
00733 /*
00734  * Module entry point
00735  */
00736 void SetNTTDebugEnabled(const int n)
00737 {
00738        NTTDebugEnabled = n;
00739 }
00740 void SetNetQDebugEnabled(const int n)
00741 {
00742        NetQDebugEnabled = n;
00743 }
00744 
00745 CTDL_MODULE_INIT(network)
00746 {
00747        if (!threading)
00748        {
00749               CtdlFillSystemContext(&networker_spool_CC, "CitNetSpool");
00750               CtdlRegisterDebugFlagHook(HKEY("networktalkingto"), SetNTTDebugEnabled, &NTTDebugEnabled);
00751               CtdlRegisterDebugFlagHook(HKEY("networkqueue"), SetNetQDebugEnabled, &NetQDebugEnabled);
00752               CtdlRegisterCleanupHook(cleanup_nttlist);
00753               CtdlRegisterSessionHook(network_cleanup_function, EVT_STOP, PRIO_STOP + 30);
00754                 CtdlRegisterSessionHook(network_logout_hook, EVT_LOGOUT, PRIO_LOGOUT + 10);
00755               CtdlRegisterProtoHook(cmd_nsyn, "NSYN", "Synchronize room to node");
00756               CtdlRegisterRoomHook(network_room_handler);
00757               CtdlRegisterCleanupHook(destroy_network_queue_room_locked);
00758               CtdlRegisterSessionHook(network_do_queue, EVT_TIMER, PRIO_QUEUE + 10);
00759        }
00760        return "network";
00761 }