Annotation of sbbs/src/sbbs3/main.cpp, revision 1.1

1.1     ! root        1: /* main.cpp */
        !             2: 
        !             3: /* Synchronet main/telnet server thread and related functions */
        !             4: 
        !             5: /* $Id: main.cpp,v 1.468 2006/12/30 04:36:44 rswindell Exp $ */
        !             6: 
        !             7: /****************************************************************************
        !             8:  * @format.tab-size 4          (Plain Text/Source Code File Header)                    *
        !             9:  * @format.use-tabs true       (see http://www.synchro.net/ptsc_hdr.html)              *
        !            10:  *                                                                                                                                                     *
        !            11:  * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html         *
        !            12:  *                                                                                                                                                     *
        !            13:  * This program is free software; you can redistribute it and/or                       *
        !            14:  * modify it under the terms of the GNU General Public License                         *
        !            15:  * as published by the Free Software Foundation; either version 2                      *
        !            16:  * of the License, or (at your option) any later version.                                      *
        !            17:  * See the GNU General Public License for more details: gpl.txt or                     *
        !            18:  * http://www.fsf.org/copyleft/gpl.html                                                                                *
        !            19:  *                                                                                                                                                     *
        !            20:  * Anonymous FTP access to the most recent released source is available at     *
        !            21:  * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net     *
        !            22:  *                                                                                                                                                     *
        !            23:  * Anonymous CVS access to the development source and modification history     *
        !            24:  * is available at cvs.synchro.net:/cvsroot/sbbs, example:                                     *
        !            25:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs login                       *
        !            26:  *     (just hit return, no password is necessary)                                                     *
        !            27:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src                *
        !            28:  *                                                                                                                                                     *
        !            29:  * For Synchronet coding style and modification guidelines, see                                *
        !            30:  * http://www.synchro.net/source.html                                                                          *
        !            31:  *                                                                                                                                                     *
        !            32:  * You are encouraged to submit any modifications (preferably in Unix diff     *
        !            33:  * format) via e-mail to [email protected]                                                                      *
        !            34:  *                                                                                                                                                     *
        !            35:  * Note: If this box doesn't appear square, then you need to fix your tabs.    *
        !            36:  ****************************************************************************/
        !            37: 
        !            38: #include "sbbs.h"
        !            39: #include "ident.h"
        !            40: #include "telnet.h" 
        !            41: 
        !            42: #ifdef __unix__
        !            43:        #include <sys/un.h>
        !            44:        #ifndef SUN_LEN
        !            45:                #define SUN_LEN(su) \
        !            46:                (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
        !            47:        #endif
        !            48: #endif
        !            49: 
        !            50: //---------------------------------------------------------------------------
        !            51: 
        !            52: #define TELNET_SERVER "Synchronet Telnet Server"
        !            53: #define STATUS_WFC     "Listening"
        !            54: 
        !            55: #define TIMEOUT_THREAD_WAIT            60                      // Seconds (was 15)
        !            56: #define IO_THREAD_BUF_SIZE             20000           // Bytes
        !            57: 
        !            58: // Globals
        !            59: #ifdef _WIN32
        !            60:        HANDLE          exec_mutex=NULL;
        !            61:        HINSTANCE       hK32=NULL;
        !            62: 
        !            63:        #if defined(_DEBUG) && defined(_MSC_VER)
        !            64:                        HANDLE  debug_log=INVALID_HANDLE_VALUE;
        !            65:                   _CrtMemState mem_chkpoint;
        !            66:        #endif // _DEBUG && _MSC_VER
        !            67: 
        !            68: #endif // _WIN32
        !            69: 
        !            70: #ifdef USE_CRYPTLIB
        !            71:        #define SSH_END()       if(ssh) cryptDestroySession(sbbs->ssh_session);
        !            72: #else
        !            73:        #define SSH_END()
        !            74: #endif
        !            75: 
        !            76: time_t uptime=0;
        !            77: DWORD  served=0;
        !            78: 
        !            79: static ulong node_threads_running=0;
        !            80: static ulong thread_count=0;
        !            81:                
        !            82: char   lastuseron[LEN_ALIAS+1];  /* Name of user last online */
        !            83: RingBuf* node_inbuf[MAX_NODES];
        !            84: SOCKET spy_socket[MAX_NODES];
        !            85: #ifdef __unix__
        !            86: SOCKET uspy_socket[MAX_NODES];   /* UNIX domain spy sockets */
        !            87: #endif
        !            88: SOCKET node_socket[MAX_NODES];
        !            89: static SOCKET telnet_socket=INVALID_SOCKET;
        !            90: static SOCKET rlogin_socket=INVALID_SOCKET;
        !            91: #ifdef USE_CRYPTLIB
        !            92: static SOCKET ssh_socket=INVALID_SOCKET;
        !            93: #endif
        !            94: static sbbs_t* sbbs=NULL;
        !            95: static scfg_t  scfg;
        !            96: static char *  text[TOTAL_TEXT];
        !            97: static WORD    first_node;
        !            98: static WORD    last_node;
        !            99: static bool    terminate_server=false;
        !           100: static str_list_t recycle_semfiles;
        !           101: static str_list_t shutdown_semfiles;
        !           102: #ifdef _THREAD_SUID_BROKEN
        !           103: int    thread_suid_broken=TRUE;                        /* NPTL is no longer broken */
        !           104: #endif
        !           105: 
        !           106: extern "C" {
        !           107: 
        !           108: static bbs_startup_t* startup=NULL;
        !           109: 
        !           110: static void status(char* str)
        !           111: {
        !           112:        if(startup!=NULL && startup->status!=NULL)
        !           113:            startup->status(startup->cbdata,str);
        !           114: }
        !           115: 
        !           116: static void update_clients()
        !           117: {
        !           118:        if(startup!=NULL && startup->clients!=NULL)
        !           119:                startup->clients(startup->cbdata,node_threads_running);
        !           120: }
        !           121: 
        !           122: void client_on(SOCKET sock, client_t* client, BOOL update)
        !           123: {
        !           124:        if(startup!=NULL && startup->client_on!=NULL)
        !           125:                startup->client_on(startup->cbdata,TRUE,sock,client,update);
        !           126: }
        !           127: 
        !           128: static void client_off(SOCKET sock)
        !           129: {
        !           130:        if(startup!=NULL && startup->client_on!=NULL)
        !           131:                startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
        !           132: }
        !           133: 
        !           134: static void thread_up(BOOL setuid)
        !           135: {
        !           136:        thread_count++;
        !           137:        if(startup!=NULL && startup->thread_up!=NULL)
        !           138:                startup->thread_up(startup->cbdata,TRUE,setuid);
        !           139: }
        !           140: 
        !           141: static void thread_down()
        !           142: {
        !           143:        if(thread_count>0)
        !           144:                thread_count--;
        !           145:        if(startup!=NULL && startup->thread_up!=NULL)
        !           146:                startup->thread_up(startup->cbdata,FALSE,FALSE);
        !           147: }
        !           148: 
        !           149: int lputs(int level, char* str)
        !           150: {
        !           151:        if(startup==NULL || startup->lputs==NULL || str==NULL)
        !           152:        return(0);
        !           153: 
        !           154:     return(startup->lputs(startup->cbdata,level,str));
        !           155: }
        !           156: 
        !           157: int lprintf(int level, char *fmt, ...)
        !           158: {
        !           159:        va_list argptr;
        !           160:        char sbuf[1024];
        !           161: 
        !           162:     va_start(argptr,fmt);
        !           163:     vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
        !           164:        sbuf[sizeof(sbuf)-1]=0;
        !           165:     va_end(argptr);
        !           166:     return(lputs(level,sbuf));
        !           167: }
        !           168: 
        !           169: int eprintf(int level, char *fmt, ...)
        !           170: {
        !           171:        va_list argptr;
        !           172:        char sbuf[1024];
        !           173: 
        !           174:     if(startup==NULL || startup->event_lputs==NULL)
        !           175:         return(0);
        !           176: 
        !           177:     va_start(argptr,fmt);
        !           178:     vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
        !           179:        sbuf[sizeof(sbuf)-1]=0;
        !           180:     va_end(argptr);
        !           181:        strip_ctrl(sbuf);
        !           182:     return(startup->event_lputs(level,sbuf));
        !           183: }
        !           184: 
        !           185: SOCKET open_socket(int type, const char* protocol)
        !           186: {
        !           187:        SOCKET  sock;
        !           188:        char    error[256];
        !           189: 
        !           190:        sock=socket(AF_INET, type, IPPROTO_IP);
        !           191:        if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
        !           192:                startup->socket_open(startup->cbdata,TRUE);
        !           193:        if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
        !           194:                lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
        !           195: 
        !           196:        return(sock);
        !           197: }
        !           198: 
        !           199: SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen)
        !           200: {
        !           201:        SOCKET  sock;
        !           202: 
        !           203:        sock=accept(s,addr,addrlen);
        !           204:        if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
        !           205:                startup->socket_open(startup->cbdata,TRUE);
        !           206: 
        !           207:        return(sock);
        !           208: }
        !           209: 
        !           210: int close_socket(SOCKET sock)
        !           211: {
        !           212:        int             result;
        !           213: 
        !           214:        if(sock==INVALID_SOCKET || sock==0)
        !           215:                return(0);
        !           216: 
        !           217:        shutdown(sock,SHUT_RDWR);       /* required on Unix */
        !           218:        result=closesocket(sock);
        !           219:        if(startup!=NULL && startup->socket_open!=NULL)
        !           220:                startup->socket_open(startup->cbdata,FALSE);
        !           221:        if(result!=0 && ERROR_VALUE!=ENOTSOCK)
        !           222:                lprintf(LOG_ERR,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
        !           223:        return(result);
        !           224: }
        !           225: 
        !           226: 
        !           227: u_long resolve_ip(char *addr)
        !           228: {
        !           229:        HOSTENT*        host;
        !           230:        char*           p;
        !           231: 
        !           232:        if(*addr==0)
        !           233:                return((u_long)INADDR_NONE);
        !           234: 
        !           235:        for(p=addr;*p;p++)
        !           236:                if(*p!='.' && !isdigit(*p))
        !           237:                        break;
        !           238:        if(!(*p))
        !           239:                return(inet_addr(addr));
        !           240:        if((host=gethostbyname(addr))==NULL) 
        !           241:                return((u_long)INADDR_NONE);
        !           242:        return(*((ulong*)host->h_addr_list[0]));
        !           243: }
        !           244: 
        !           245: } /* extern "C" */
        !           246: 
        !           247: #ifdef _WINSOCKAPI_
        !           248: 
        !           249: WSADATA WSAData;
        !           250: #define SOCKLIB_DESC WSAData.szDescription
        !           251: static BOOL WSAInitialized=FALSE;
        !           252: 
        !           253: static BOOL winsock_startup(void)
        !           254: {
        !           255:        int             status;             /* Status Code */
        !           256: 
        !           257:     if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
        !           258:                lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
        !           259:                WSAInitialized=TRUE;
        !           260:                return(TRUE);
        !           261:        }
        !           262: 
        !           263:     lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
        !           264:        return(FALSE);
        !           265: }
        !           266: 
        !           267: #else /* No WINSOCK */
        !           268: 
        !           269: #define winsock_startup()      (TRUE)
        !           270: #define SOCKLIB_DESC NULL
        !           271: 
        !           272: #endif
        !           273: 
        !           274: DLLEXPORT void DLLCALL sbbs_srand()
        !           275: {
        !           276:        DWORD seed = time(NULL) ^ (DWORD)GetCurrentThreadId();
        !           277: 
        !           278: #if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
        !           279:        int     rf;
        !           280: 
        !           281:        if((rf=open(RANDOM_DEV, O_RDONLY))!=-1) {
        !           282:                read(rf, &seed, sizeof(seed));
        !           283:                close(rf);
        !           284:        }
        !           285: #endif
        !           286: 
        !           287:        srand(seed);
        !           288:        sbbs_random(10);        /* Throw away first number */
        !           289: }
        !           290: 
        !           291: int DLLCALL sbbs_random(int n)
        !           292: {
        !           293:        return(xp_random(n));
        !           294: }
        !           295: 
        !           296: #ifdef JAVASCRIPT
        !           297: 
        !           298: static js_server_props_t js_server_props;
        !           299: 
        !           300: JSBool 
        !           301: DLLCALL js_CreateArrayOfStrings(JSContext* cx, JSObject* parent, const char* name, char* str[],uintN flags)
        !           302: {
        !           303:        JSObject*       array;
        !           304:        JSString*       js_str;
        !           305:        jsval           val;
        !           306:        size_t          i;
        !           307:        jsuint          len=0;
        !           308:                
        !           309:        if(JS_GetProperty(cx,parent,name,&val) && val!=JSVAL_VOID)
        !           310:                array=JSVAL_TO_OBJECT(val);
        !           311:        else
        !           312:                if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)        /* Assertion here, in _heap_alloc_dbg, June-21-2004 */
        !           313:                        return(JS_FALSE);                                                               /* Caused by nntpservice.js? */
        !           314: 
        !           315:        if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
        !           316:                ,NULL,NULL,flags))
        !           317:                return(JS_FALSE);
        !           318: 
        !           319:        if(!JS_GetArrayLength(cx, array, &len))
        !           320:                return(JS_FALSE);
        !           321: 
        !           322:        for(i=0;str[i]!=NULL;i++) {
        !           323:                if((js_str = JS_NewStringCopyZ(cx, str[i]))==NULL)
        !           324:                        break;
        !           325:                val = STRING_TO_JSVAL(js_str);
        !           326:                if(!JS_SetElement(cx, array, len+i, &val))
        !           327:                        break;
        !           328:        }
        !           329: 
        !           330:        return(JS_TRUE);
        !           331: }
        !           332: 
        !           333: /* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
        !           334: 
        !           335: JSBool
        !           336: DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
        !           337: {
        !           338:        JSString* js_str = JS_NewStringCopyZ(cx, str);
        !           339: 
        !           340:        if(js_str==NULL)
        !           341:                return(JS_FALSE);
        !           342: 
        !           343:        if(ver < 10000)         /* auto convert 313 to 31300 */
        !           344:                ver*=100;
        !           345: 
        !           346:        return(JS_DefineProperty(cx,obj,"_description"
        !           347:                        ,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
        !           348:                && JS_DefineProperty(cx,obj,"_ver"
        !           349:                        ,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
        !           350: }
        !           351: 
        !           352: JSBool
        !           353: DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
        !           354: {
        !           355:        JSString* js_str = JS_NewStringCopyZ(cx, str);
        !           356: 
        !           357:        if(js_str==NULL)
        !           358:                return(JS_FALSE);
        !           359: 
        !           360:        return(JS_DefineProperty(cx,obj,"_constructor"
        !           361:                ,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY));
        !           362: }
        !           363: 
        !           364: #ifdef BUILD_JSDOCS
        !           365: 
        !           366: static const char* method_array_name = "_method_list";
        !           367: static const char* propver_array_name = "_property_ver_list";
        !           368: 
        !           369: /*
        !           370:  * from jsatom.c:
        !           371:  * Keep this in sync with jspubtd.h -- an assertion below will insist that
        !           372:  * its length match the JSType enum's JSTYPE_LIMIT limit value.
        !           373:  */
        !           374: static const char *js_type_str[] = {
        !           375:     "void",                    // changed from "undefined"
        !           376:     "object",
        !           377:     "function",
        !           378:     "string",
        !           379:     "number",
        !           380:     "boolean",
        !           381:        "array",
        !           382:        "alias",
        !           383:        "undefined",
        !           384:        "null"
        !           385: };
        !           386: 
        !           387: JSBool
        !           388: DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
        !           389: {
        !           390:        uint            i;
        !           391:        long            ver;
        !           392:        jsval           val;
        !           393:        jsuint          len=0;
        !           394:        JSObject*       array;
        !           395: 
        !           396:        if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
        !           397:                return(JS_FALSE);
        !           398: 
        !           399:        if(!JS_DefineProperty(cx, obj, propver_array_name, OBJECT_TO_JSVAL(array)
        !           400:                ,NULL,NULL,JSPROP_READONLY))
        !           401:                return(JS_FALSE);
        !           402: 
        !           403:        for(i=0;props[i].name;i++) {
        !           404:                if(!JS_DefinePropertyWithTinyId(cx, obj, /* Never reserve any "slots" for properties */
        !           405:                        props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
        !           406:                        return(JS_FALSE);
        !           407:                if(props[i].flags&JSPROP_ENUMERATE) {   /* No need to version invisible props */
        !           408:                        if((ver=props[i].ver) < 10000)          /* auto convert 313 to 31300 */
        !           409:                                ver*=100;
        !           410:                        val = INT_TO_JSVAL(ver);
        !           411:                        if(!JS_SetElement(cx, array, len++, &val))
        !           412:                                return(JS_FALSE);
        !           413:                }
        !           414:        }
        !           415: 
        !           416:        return(JS_TRUE);
        !           417: }
        !           418: 
        !           419: JSBool 
        !           420: DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
        !           421: {
        !           422:        int                     i;
        !           423:        jsuint          len=0;
        !           424:        long            ver;
        !           425:        jsval           val;
        !           426:        JSObject*       method;
        !           427:        JSObject*       method_array;
        !           428:        JSString*       js_str;
        !           429: 
        !           430:        /* Return existing method_list array if it's already been created */
        !           431:        if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID)
        !           432:                method_array=JSVAL_TO_OBJECT(val);
        !           433:        else
        !           434:                if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
        !           435:                        return(JS_FALSE);
        !           436: 
        !           437:        if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
        !           438:                , NULL, NULL, 0))
        !           439:                return(JS_FALSE);
        !           440: 
        !           441:        if(append)
        !           442:                if(!JS_GetArrayLength(cx, method_array, &len))
        !           443:                        return(JS_FALSE);
        !           444: 
        !           445:        for(i=0;funcs[i].name;i++) {
        !           446: 
        !           447:                if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
        !           448:                        return(JS_FALSE);
        !           449: 
        !           450:                if(funcs[i].type==JSTYPE_ALIAS)
        !           451:                        continue;
        !           452: 
        !           453:                method = JS_NewObject(cx, NULL, NULL, method_array);    /* exception here June-7-2003 */
        !           454: 
        !           455:                if(method==NULL)
        !           456:                        return(JS_FALSE);
        !           457: 
        !           458:                if(funcs[i].name!=NULL) {
        !           459:                        if((js_str=JS_NewStringCopyZ(cx,funcs[i].name))==NULL)
        !           460:                                return(JS_FALSE);
        !           461:                        val = STRING_TO_JSVAL(js_str);
        !           462:                        JS_SetProperty(cx, method, "name", &val);
        !           463:                }
        !           464: 
        !           465:                val = INT_TO_JSVAL(funcs[i].nargs);
        !           466:                if(!JS_SetProperty(cx, method, "nargs", &val))
        !           467:                        return(JS_FALSE);
        !           468: 
        !           469:                if((js_str=JS_NewStringCopyZ(cx,js_type_str[funcs[i].type]))==NULL)
        !           470:                        return(JS_FALSE);
        !           471:                val = STRING_TO_JSVAL(js_str);
        !           472:                JS_SetProperty(cx, method, "type", &val);
        !           473: 
        !           474:                if(funcs[i].args!=NULL) {
        !           475:                        if((js_str=JS_NewStringCopyZ(cx,funcs[i].args))==NULL)
        !           476:                                return(JS_FALSE);
        !           477:                        val = STRING_TO_JSVAL(js_str);
        !           478:                        JS_SetProperty(cx, method, "args", &val);
        !           479:                }
        !           480: 
        !           481:                if(funcs[i].desc!=NULL) { 
        !           482:                        if((js_str=JS_NewStringCopyZ(cx,funcs[i].desc))==NULL)
        !           483:                                return(JS_FALSE);
        !           484:                        val = STRING_TO_JSVAL(js_str);
        !           485:                        JS_SetProperty(cx, method, "desc", &val);
        !           486:                }
        !           487: 
        !           488:                if(funcs[i].ver) {
        !           489:                        if((ver=funcs[i].ver) < 10000)          /* auto convert 313 to 31300 */
        !           490:                                ver*=100;
        !           491:                        val = INT_TO_JSVAL(ver);
        !           492:                        JS_SetProperty(cx,method, "ver", &val);
        !           493:                }
        !           494: 
        !           495:                val=OBJECT_TO_JSVAL(method);
        !           496:                if(!JS_SetElement(cx, method_array, len+i, &val))
        !           497:                        return(JS_FALSE);
        !           498:        }
        !           499: 
        !           500:        return(JS_TRUE);
        !           501: }
        !           502: 
        !           503: #else // NON-JSDOCS
        !           504: 
        !           505: JSBool
        !           506: DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
        !           507: {
        !           508:        uint i;
        !           509: 
        !           510:        for(i=0;props[i].name;i++) 
        !           511:                if(!JS_DefinePropertyWithTinyId(cx, obj, 
        !           512:                        props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
        !           513:                        return(JS_FALSE);
        !           514: 
        !           515:        return(JS_TRUE);
        !           516: }
        !           517: 
        !           518: 
        !           519: JSBool 
        !           520: DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
        !           521: {
        !           522:        uint i;
        !           523: 
        !           524:        for(i=0;funcs[i].name;i++)
        !           525:                if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
        !           526:                        return(JS_FALSE);
        !           527:        return(JS_TRUE);
        !           528: }
        !           529: 
        !           530: #endif
        !           531: 
        !           532: /* This is a stream-lined version of JS_DefineConstDoubles */
        !           533: JSBool
        !           534: DLLCALL js_DefineConstIntegers(JSContext* cx, JSObject* obj, jsConstIntSpec* ints, int flags)
        !           535: {
        !           536:        uint    i;
        !           537:        jsval   val;
        !           538: 
        !           539:        for(i=0;ints[i].name;i++) {
        !           540:         if(!JS_NewNumberValue(cx, ints[i].val, &val))
        !           541:                        return(JS_FALSE);
        !           542: 
        !           543:                if(!JS_DefineProperty(cx, obj, ints[i].name, val ,NULL, NULL, flags))
        !           544:                        return(JS_FALSE);
        !           545:        }
        !           546:                
        !           547:        return(JS_TRUE);
        !           548: }
        !           549: 
        !           550: char*
        !           551: DLLCALL js_ValueToStringBytes(JSContext* cx, jsval val, size_t* len)
        !           552: {
        !           553:        JSString* str;
        !           554:        
        !           555:        if((str=JS_ValueToString(cx, val))==NULL)
        !           556:                return(NULL);
        !           557: 
        !           558:        if(len!=NULL)
        !           559:                *len = JS_GetStringLength(str);
        !           560: 
        !           561:        return(JS_GetStringBytes(str));
        !           562: }
        !           563: 
        !           564: static JSBool
        !           565: js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           566: {
        !           567:     uintN              i=0;
        !           568:        int32           level=LOG_INFO;
        !           569:     JSString*  str=NULL;
        !           570:        sbbs_t*         sbbs;
        !           571: 
        !           572:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           573:                return(JS_FALSE);
        !           574: 
        !           575:        if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
        !           576:                JS_ValueToInt32(cx,argv[i++],&level);
        !           577: 
        !           578:     for(; i<argc; i++) {
        !           579:                if((str=JS_ValueToString(cx, argv[i]))==NULL)
        !           580:                    return(JS_FALSE);
        !           581:                if(sbbs->online==ON_LOCAL) {
        !           582:                        if(startup!=NULL && startup->event_lputs!=NULL)
        !           583:                                startup->event_lputs(level,JS_GetStringBytes(str));
        !           584:                } else
        !           585:                        lputs(level,JS_GetStringBytes(str));
        !           586:        }
        !           587: 
        !           588:        if(str==NULL)
        !           589:                *rval = JSVAL_VOID;
        !           590:        else
        !           591:                *rval = STRING_TO_JSVAL(str);
        !           592:     return(JS_TRUE);
        !           593: }
        !           594: 
        !           595: static JSBool
        !           596: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           597: {
        !           598:        uchar*          buf;
        !           599:        int32           len=128;
        !           600:        sbbs_t*         sbbs;
        !           601: 
        !           602:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           603:                return(JS_FALSE);
        !           604: 
        !           605:        if(argc)
        !           606:                JS_ValueToInt32(cx,argv[0],&len);
        !           607: 
        !           608:        if((buf=(uchar*)malloc(len))==NULL)
        !           609:                return(JS_TRUE);
        !           610: 
        !           611:        len=RingBufRead(&sbbs->inbuf,buf,len);
        !           612: 
        !           613:        if(len>0)
        !           614:                *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len));
        !           615: 
        !           616:        free(buf);
        !           617:        return(JS_TRUE);
        !           618: }
        !           619: 
        !           620: static JSBool
        !           621: js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           622: {
        !           623:        char*           buf;
        !           624:        int32           len=128;
        !           625:        sbbs_t*         sbbs;
        !           626: 
        !           627:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           628:                return(JS_FALSE);
        !           629: 
        !           630:        if(argc)
        !           631:                JS_ValueToInt32(cx,argv[0],&len);
        !           632: 
        !           633:        if((buf=(char*)malloc(len))==NULL)
        !           634:                return(JS_TRUE);
        !           635: 
        !           636:        len=sbbs->getstr(buf,len,K_NONE);
        !           637: 
        !           638:        if(len>0)
        !           639:                *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf));
        !           640: 
        !           641:        free(buf);
        !           642:        return(JS_TRUE);
        !           643: }
        !           644: 
        !           645: static JSBool
        !           646: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           647: {
        !           648:     uintN              i;
        !           649:     JSString*  str=NULL;
        !           650:        sbbs_t*         sbbs;
        !           651: 
        !           652:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           653:                return(JS_FALSE);
        !           654: 
        !           655:     for (i = 0; i < argc; i++) {
        !           656:                if((str=JS_ValueToString(cx, argv[i]))==NULL)
        !           657:                    return(JS_FALSE);
        !           658:                if(sbbs->online==ON_LOCAL)
        !           659:                        eprintf(LOG_INFO,"%s",JS_GetStringBytes(str));
        !           660:                else
        !           661:                        sbbs->bputs(JS_GetStringBytes(str));
        !           662:        }
        !           663: 
        !           664:        if(str==NULL)
        !           665:                *rval = JSVAL_VOID;
        !           666:        else
        !           667:                *rval = STRING_TO_JSVAL(str);
        !           668:     return(JS_TRUE);
        !           669: }
        !           670: 
        !           671: static JSBool
        !           672: js_write_raw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           673: {
        !           674:     uintN              i;
        !           675:     char*      str=NULL;
        !           676:        size_t          len;
        !           677:        sbbs_t*         sbbs;
        !           678: 
        !           679:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           680:                return(JS_FALSE);
        !           681: 
        !           682:     for (i = 0; i < argc; i++) {
        !           683:                if((str=js_ValueToStringBytes(cx, argv[i], &len))==NULL)
        !           684:                    return(JS_FALSE);
        !           685:                sbbs->putcom(str, len);
        !           686:        }
        !           687: 
        !           688:     return(JS_TRUE);
        !           689: }
        !           690: 
        !           691: static JSBool
        !           692: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           693: {
        !           694:        sbbs_t*         sbbs;
        !           695: 
        !           696:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           697:                return(JS_FALSE);
        !           698: 
        !           699:        js_write(cx,obj,argc,argv,rval);
        !           700:        if(sbbs->online==ON_REMOTE)
        !           701:                sbbs->bputs(crlf);
        !           702: 
        !           703:     return(JS_TRUE);
        !           704: }
        !           705: 
        !           706: static JSBool
        !           707: js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           708: {
        !           709:        char*           p;
        !           710:        sbbs_t*         sbbs;
        !           711: 
        !           712:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           713:                return(JS_FALSE);
        !           714: 
        !           715:        if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
        !           716:                JS_ReportError(cx,"js_sprintf failed");
        !           717:                return(JS_FALSE);
        !           718:        }
        !           719: 
        !           720:        if(sbbs->online==ON_LOCAL)
        !           721:                eprintf(LOG_INFO,"%s",p);
        !           722:        else
        !           723:                sbbs->bputs(p);
        !           724: 
        !           725:        *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p));
        !           726: 
        !           727:        js_sprintf_free(p);
        !           728: 
        !           729:     return(JS_TRUE);
        !           730: }
        !           731: 
        !           732: static JSBool
        !           733: js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           734: {
        !           735:     JSString * str;
        !           736:        sbbs_t*         sbbs;
        !           737: 
        !           738:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           739:                return(JS_FALSE);
        !           740: 
        !           741:        if((str=JS_ValueToString(cx, argv[0]))==NULL)
        !           742:            return(JS_FALSE);
        !           743: 
        !           744:        sbbs->attr(sbbs->cfg.color[clr_err]);
        !           745:        sbbs->bputs(JS_GetStringBytes(str));
        !           746:        sbbs->attr(LIGHTGRAY);
        !           747:        sbbs->bputs(crlf);
        !           748: 
        !           749:     return(JS_TRUE);
        !           750: }
        !           751: 
        !           752: static JSBool
        !           753: js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           754: {
        !           755:     JSString * str;
        !           756:        sbbs_t*         sbbs;
        !           757: 
        !           758:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           759:                return(JS_FALSE);
        !           760: 
        !           761:        if((str=JS_ValueToString(cx, argv[0]))==NULL)
        !           762:            return(JS_FALSE);
        !           763: 
        !           764:        *rval = BOOLEAN_TO_JSVAL(sbbs->yesno(JS_GetStringBytes(str)));
        !           765:        return(JS_TRUE);
        !           766: }
        !           767: 
        !           768: static JSBool
        !           769: js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !           770: {
        !           771:        char            instr[81];
        !           772:     JSString * prompt;
        !           773:     JSString * str;
        !           774:        sbbs_t*         sbbs;
        !           775: 
        !           776:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           777:                return(JS_FALSE);
        !           778: 
        !           779:        if((prompt=JS_ValueToString(cx, argv[0]))==NULL)
        !           780:            return(JS_FALSE);
        !           781: 
        !           782:        if(argc>1) {
        !           783:                if((str=JS_ValueToString(cx, argv[1]))==NULL)
        !           784:                    return(JS_FALSE);
        !           785:                SAFECOPY(instr,JS_GetStringBytes(str));
        !           786:        } else
        !           787:                instr[0]=0;
        !           788: 
        !           789:        sbbs->bprintf("\1n\1y\1h%s\1w: ",JS_GetStringBytes(prompt));
        !           790: 
        !           791:        if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
        !           792:                *rval = JSVAL_NULL;
        !           793:                return(JS_TRUE);
        !           794:        }
        !           795: 
        !           796:        if((str=JS_NewStringCopyZ(cx, instr))==NULL)
        !           797:            return(JS_FALSE);
        !           798: 
        !           799:        *rval = STRING_TO_JSVAL(str);
        !           800:     return(JS_TRUE);
        !           801: }
        !           802: 
        !           803: static jsSyncMethodSpec js_global_functions[] = {
        !           804:        {"log",                         js_log,                         1,      JSTYPE_STRING,  JSDOCSTR("[level,] value [,value]")
        !           805:        ,JSDOCSTR("add a line of text to the server and/or system log, "
        !           806:                "<i>values</i> are typically string constants or variables, "
        !           807:                "<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
        !           808:        ,311
        !           809:        },
        !           810:        {"read",                        js_read,                        0,      JSTYPE_STRING,  JSDOCSTR("[count]")
        !           811:        ,JSDOCSTR("read up to count characters from input stream")
        !           812:        ,311
        !           813:        },
        !           814:        {"readln",                      js_readln,                      0,      JSTYPE_STRING,  JSDOCSTR("[count]")
        !           815:        ,JSDOCSTR("read a single line, up to count characters, from input stream")
        !           816:        ,311
        !           817:        },
        !           818:        {"write",                       js_write,                       0,      JSTYPE_VOID,    JSDOCSTR("value [,value]")
        !           819:        ,JSDOCSTR("send one or more values (typically strings) to the server output")
        !           820:        ,311
        !           821:        },
        !           822:        {"write_raw",                   js_write_raw,                   0,      JSTYPE_VOID,    JSDOCSTR("value [,value]")
        !           823:        ,JSDOCSTR("send a stream of bytes (possibly containing NULLs or special control code sequences) to the server output")
        !           824:        ,314
        !           825:        },
        !           826:        {"print",                       js_writeln,                     0,      JSTYPE_ALIAS },
        !           827:     {"writeln",         js_writeln,         0, JSTYPE_VOID,    JSDOCSTR("value [,value]")
        !           828:        ,JSDOCSTR("send a line of text to the console or event log with automatic line termination (CRLF), "
        !           829:                "<i>values</i> are typically string constants or variables (AKA print)")
        !           830:        ,311
        !           831:        },
        !           832:     {"printf",          js_printf,          1, JSTYPE_STRING,  JSDOCSTR("string format [,value][,value]")
        !           833:        ,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
        !           834:        ,310
        !           835:        },      
        !           836:        {"alert",                       js_alert,                       1,      JSTYPE_VOID,    JSDOCSTR("value")
        !           837:        ,JSDOCSTR("print an alert message (ala client-side JS)")
        !           838:        ,310
        !           839:        },
        !           840:        {"prompt",                      js_prompt,                      1,      JSTYPE_STRING,  JSDOCSTR("[value]")
        !           841:        ,JSDOCSTR("displays a prompt (<i>value</i>) and returns a string of user input (ala clent-side JS)")
        !           842:        ,310
        !           843:        },
        !           844:        {"confirm",                     js_confirm,                     1,      JSTYPE_BOOLEAN, JSDOCSTR("value")
        !           845:        ,JSDOCSTR("displays a Yes/No prompt and returns <i>true</i> or <i>false</i> "
        !           846:                "based on users confirmation (ala client-side JS)")
        !           847:        ,310
        !           848:        },
        !           849:     {0}
        !           850: };
        !           851: 
        !           852: static void
        !           853: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
        !           854: {
        !           855:        char    line[64];
        !           856:        char    file[MAX_PATH+1];
        !           857:        sbbs_t* sbbs;
        !           858:        const char*     warning;
        !           859: 
        !           860:        if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
        !           861:                return;
        !           862:        
        !           863:        if(report==NULL) {
        !           864:                lprintf(LOG_ERR,"!JavaScript: %s", message);
        !           865:                return;
        !           866:     }
        !           867: 
        !           868:        if(report->filename)
        !           869:                SAFEPRINTF(file," %s",report->filename);
        !           870:        else
        !           871:                file[0]=0;
        !           872: 
        !           873:        if(report->lineno)
        !           874:                SAFEPRINTF(line," line %d",report->lineno);
        !           875:        else
        !           876:                line[0]=0;
        !           877: 
        !           878:        if(JSREPORT_IS_WARNING(report->flags)) {
        !           879:                if(JSREPORT_IS_STRICT(report->flags))
        !           880:                        warning="strict warning";
        !           881:                else
        !           882:                        warning="warning";
        !           883:        } else
        !           884:                warning=nulstr;
        !           885: 
        !           886:        if(sbbs->online==ON_LOCAL) 
        !           887:                eprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
        !           888:        else {
        !           889:                lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
        !           890:                sbbs->bprintf("!JavaScript %s%s%s: %s\r\n",warning,file,line,message);
        !           891:        }
        !           892: }
        !           893: 
        !           894: bool sbbs_t::js_init(ulong* stack_frame)
        !           895: {
        !           896:        char            node[128];
        !           897: 
        !           898:     if(cfg.node_num)
        !           899:        SAFEPRINTF(node,"Node %d",cfg.node_num);
        !           900:     else
        !           901:        SAFECOPY(node,client_name);
        !           902: 
        !           903:        if(startup->js.max_bytes==0)                    startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
        !           904:        if(startup->js.cx_stack==0)                             startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
        !           905: 
        !           906:        lprintf(LOG_DEBUG,"%s JavaScript: Creating runtime: %lu bytes"
        !           907:                ,node,startup->js.max_bytes);
        !           908: 
        !           909:        if((js_runtime = JS_NewRuntime(startup->js.max_bytes))==NULL)
        !           910:                return(false);
        !           911: 
        !           912:        lprintf(LOG_DEBUG,"%s JavaScript: Initializing context (stack: %lu bytes)"
        !           913:                ,node,startup->js.cx_stack);
        !           914: 
        !           915:     if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL)
        !           916:                return(false);
        !           917:        
        !           918:        memset(&js_branch,0,sizeof(js_branch));
        !           919:        js_branch.limit = startup->js.branch_limit;
        !           920:        js_branch.gc_interval = startup->js.gc_interval;
        !           921:        js_branch.yield_interval = startup->js.yield_interval;
        !           922:        js_branch.terminated = &terminated;
        !           923:        js_branch.auto_terminate = TRUE;
        !           924: 
        !           925:        bool success=false;
        !           926: 
        !           927:        do {
        !           928: 
        !           929:                JS_SetErrorReporter(js_cx, js_ErrorReporter);
        !           930: 
        !           931:                JS_SetContextPrivate(js_cx, this);      /* Store a pointer to sbbs_t instance */
        !           932: 
        !           933:                /* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
        !           934:                if((js_glob=js_CreateCommonObjects(js_cx, &scfg, &cfg, js_global_functions
        !           935:                                        ,uptime, startup->host_name, SOCKLIB_DESC       /* system */
        !           936:                                        ,&js_branch                                                                     /* js */
        !           937:                                        ,&client, client_socket                                         /* client */
        !           938:                                        ,&js_server_props                                                       /* server */
        !           939:                        ))==NULL)
        !           940:                        break;
        !           941: 
        !           942:                /* BBS Object */
        !           943:                if(js_CreateBbsObject(js_cx, js_glob)==NULL)
        !           944:                        break;
        !           945: 
        !           946:                /* Console Object */
        !           947:                if(js_CreateConsoleObject(js_cx, js_glob)==NULL)
        !           948:                        break;
        !           949: 
        !           950:                if(startup->js.thread_stack) {
        !           951:                        ulong stack_limit;
        !           952: 
        !           953: #if JS_STACK_GROWTH_DIRECTION > 0
        !           954:                        stack_limit=((ulong)stack_frame)+startup->js.thread_stack;
        !           955: #else
        !           956:                        stack_limit=((ulong)stack_frame)-startup->js.thread_stack;
        !           957: #endif
        !           958:                        JS_SetThreadStackLimit(js_cx, stack_limit);
        !           959: 
        !           960:                        lprintf(LOG_DEBUG,"%s JavaScript: Thread stack limit: %lu bytes"
        !           961:                                ,node, startup->js.thread_stack);
        !           962:                }
        !           963: 
        !           964:                success=true;
        !           965: 
        !           966:        } while(0);
        !           967: 
        !           968:        if(!success) {
        !           969:                JS_DestroyContext(js_cx);
        !           970:                js_cx=NULL;
        !           971:                return(false);
        !           972:        }
        !           973: 
        !           974:        return(true);
        !           975: }
        !           976: 
        !           977: void sbbs_t::js_create_user_objects(void)
        !           978: {
        !           979:        if(js_cx==NULL)
        !           980:                return;
        !           981: 
        !           982:        if(!js_CreateUserObjects(js_cx, js_glob, &cfg, &useron, NULL, subscan)) 
        !           983:                lprintf(LOG_ERR,"!JavaScript ERROR creating user objects");
        !           984: }
        !           985: 
        !           986: #endif /* JAVASCRIPT */
        !           987: 
        !           988: static BYTE* telnet_interpret(sbbs_t* sbbs, BYTE* inbuf, int inlen,
        !           989:                                                                        BYTE* outbuf, int& outlen)
        !           990: {
        !           991:        BYTE*   first_iac=NULL;
        !           992:        BYTE*   first_cr=NULL;
        !           993:        int     i;
        !           994: 
        !           995:        if(inlen<1) {
        !           996:                outlen=0;
        !           997:                return(inbuf);  // no length? No interpretation
        !           998:        }
        !           999: 
        !          1000:     first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen);
        !          1001: 
        !          1002:        if(!(sbbs->telnet_mode&TELNET_MODE_GATE) 
        !          1003:                && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
        !          1004:                && !(sbbs->console&CON_RAW_IN)) {
        !          1005:                if(sbbs->telnet_last_rxch==CR)
        !          1006:                        first_cr=inbuf;
        !          1007:                else
        !          1008:                        first_cr=(BYTE*)memchr(inbuf, CR, inlen);
        !          1009:        }
        !          1010: 
        !          1011:     if(!sbbs->telnet_cmdlen    && first_iac==NULL && first_cr==NULL) {
        !          1012:         outlen=inlen;
        !          1013:         return(inbuf); // no interpretation needed
        !          1014:     }
        !          1015: 
        !          1016:     if(first_iac!=NULL || first_cr!=NULL) {
        !          1017:                if(first_iac!=NULL && (first_cr==NULL || first_iac<first_cr))
        !          1018:                        outlen=first_iac-inbuf;
        !          1019:                else
        !          1020:                        outlen=first_cr-inbuf;
        !          1021:            memcpy(outbuf, inbuf, outlen);
        !          1022:     } else
        !          1023:        outlen=0;
        !          1024: 
        !          1025:     for(i=outlen;i<inlen;i++) {
        !          1026:                if(!(sbbs->telnet_mode&TELNET_MODE_GATE) 
        !          1027:                        && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
        !          1028:                        && !(sbbs->console&CON_RAW_IN)) {
        !          1029:                        if(sbbs->telnet_last_rxch==CR
        !          1030:                                && (inbuf[i]==LF || inbuf[i]==0)) { // CR/LF or CR/NUL, ignore 2nd char
        !          1031: #if 0 /* Debug CR/LF problems */
        !          1032:                                lprintf(LOG_INFO,"Node %d CR/%02Xh detected and ignored"
        !          1033:                                        ,sbbs->cfg.node_num, inbuf[i]);
        !          1034: #endif
        !          1035:                                sbbs->telnet_last_rxch=inbuf[i];
        !          1036:                                continue;
        !          1037:                        }
        !          1038:                        sbbs->telnet_last_rxch=inbuf[i];
        !          1039:                }
        !          1040: 
        !          1041:         if(inbuf[i]==TELNET_IAC && sbbs->telnet_cmdlen==1) { /* escaped 255 */
        !          1042:             sbbs->telnet_cmdlen=0;
        !          1043:             outbuf[outlen++]=TELNET_IAC;
        !          1044:             continue;
        !          1045:         }
        !          1046:         if(inbuf[i]==TELNET_IAC || sbbs->telnet_cmdlen) {
        !          1047: 
        !          1048:                        if(sbbs->telnet_cmdlen<sizeof(sbbs->telnet_cmd))
        !          1049:                                sbbs->telnet_cmd[sbbs->telnet_cmdlen++]=inbuf[i];
        !          1050: 
        !          1051:                        uchar command   = sbbs->telnet_cmd[1];
        !          1052:                        uchar option    = sbbs->telnet_cmd[2];
        !          1053: 
        !          1054:                        if(sbbs->telnet_cmdlen>=2 && command==TELNET_SB) {
        !          1055:                                if(inbuf[i]==TELNET_SE 
        !          1056:                                        && sbbs->telnet_cmd[sbbs->telnet_cmdlen-2]==TELNET_IAC) {
        !          1057:                                        /* sub-option terminated */
        !          1058:                                        if(option==TELNET_TERM_TYPE
        !          1059:                                                && sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
        !          1060:                                                sprintf(sbbs->terminal,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
        !          1061:                                                lprintf(LOG_DEBUG,"Node %d %s telnet terminal type: %s"
        !          1062:                                        ,sbbs->cfg.node_num
        !          1063:                                                        ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
        !          1064:                                                        ,sbbs->terminal);
        !          1065: 
        !          1066:                                        } else if(option==TELNET_TERM_SPEED
        !          1067:                                                && sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
        !          1068:                                                char speed[128];
        !          1069:                                                sprintf(speed,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
        !          1070:                                                lprintf(LOG_DEBUG,"Node %d %s telnet terminal speed: %s"
        !          1071:                                        ,sbbs->cfg.node_num
        !          1072:                                                        ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
        !          1073:                                                        ,speed);
        !          1074: 
        !          1075:                                        } else if(option==TELNET_NEGOTIATE_WINDOW_SIZE) {
        !          1076:                                                long cols = (sbbs->telnet_cmd[3]<<8) | sbbs->telnet_cmd[4];
        !          1077:                                                long rows = (sbbs->telnet_cmd[5]<<8) | sbbs->telnet_cmd[6];
        !          1078:                                                lprintf(LOG_DEBUG,"Node %d %s telnet window size: %ux%u"
        !          1079:                                        ,sbbs->cfg.node_num
        !          1080:                                                        ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
        !          1081:                                                        ,cols
        !          1082:                                                        ,rows);
        !          1083:                                                if(rows && !sbbs->useron.rows)  /* auto-detect rows */
        !          1084:                                                        sbbs->rows=rows;
        !          1085:                                                if(cols)
        !          1086:                                                        sbbs->cols=cols;
        !          1087: 
        !          1088:                                        } else if(startup->options&BBS_OPT_DEBUG_TELNET)
        !          1089:                                lprintf(LOG_DEBUG,"Node %d %s unsupported telnet sub-negotiation cmd: %s"
        !          1090:                                        ,sbbs->cfg.node_num
        !          1091:                                                        ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
        !          1092:                                        ,telnet_opt_desc(option));
        !          1093:                                        sbbs->telnet_cmdlen=0;
        !          1094:                                }
        !          1095:                        }
        !          1096:             else if(sbbs->telnet_cmdlen==2 && inbuf[i]<TELNET_WILL) {
        !          1097:                                if(startup->options&BBS_OPT_DEBUG_TELNET)
        !          1098:                        lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s"
        !          1099:                                ,sbbs->cfg.node_num
        !          1100:                                                ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
        !          1101:                                ,telnet_cmd_desc(option));
        !          1102:                 sbbs->telnet_cmdlen=0;
        !          1103:             }
        !          1104:             else if(sbbs->telnet_cmdlen>=3) {  /* telnet option negotiation */
        !          1105: 
        !          1106:                                if(startup->options&BBS_OPT_DEBUG_TELNET)
        !          1107:                                        lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s %s"
        !          1108:                                                ,sbbs->cfg.node_num
        !          1109:                                                ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
        !          1110:                                                ,telnet_cmd_desc(command)
        !          1111:                                                ,telnet_opt_desc(option));
        !          1112: 
        !          1113:                                if(!(sbbs->telnet_mode&TELNET_MODE_GATE)) {
        !          1114:                                        if(command==TELNET_DO || command==TELNET_DONT) {        /* local options */
        !          1115:                                                if(sbbs->telnet_local_option[option]!=command) {
        !          1116:                                                        sbbs->telnet_local_option[option]=command;
        !          1117:                                                        sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
        !          1118:                                                }
        !          1119:                                        } else { /* WILL/WONT (remote options) */ 
        !          1120:                                                if(sbbs->telnet_remote_option[option]!=command) {       
        !          1121:                                                
        !          1122:                                                        switch(option) {
        !          1123:                                                                case TELNET_BINARY_TX:
        !          1124:                                                                case TELNET_ECHO:
        !          1125:                                                                case TELNET_TERM_TYPE:
        !          1126:                                                                case TELNET_TERM_SPEED:
        !          1127:                                                                case TELNET_SUP_GA:
        !          1128:                                                                case TELNET_NEGOTIATE_WINDOW_SIZE:
        !          1129:                                                                        sbbs->telnet_remote_option[option]=command;
        !          1130:                                                                        sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
        !          1131:                                                                        break;
        !          1132:                                                                default: /* unsupported remote options */
        !          1133:                                                                        if(command==TELNET_WILL) /* NAK */
        !          1134:                                                                                sbbs->send_telnet_cmd(telnet_opt_nak(command),option);
        !          1135:                                                                        break;
        !          1136:                                                        }
        !          1137:                                                }
        !          1138: 
        !          1139:                                                if(command==TELNET_WILL && option==TELNET_TERM_TYPE) {
        !          1140:                                                        if(startup->options&BBS_OPT_DEBUG_TELNET)
        !          1141:                                                                lprintf(LOG_DEBUG,"Node %d requesting telnet terminal type"
        !          1142:                                                                        ,sbbs->cfg.node_num);
        !          1143: 
        !          1144:                                                        char    buf[64];
        !          1145:                                                        sprintf(buf,"%c%c%c%c%c%c"
        !          1146:                                                                ,TELNET_IAC,TELNET_SB
        !          1147:                                                                ,TELNET_TERM_TYPE,TELNET_TERM_SEND
        !          1148:                                                                ,TELNET_IAC,TELNET_SE);
        !          1149:                                                        sbbs->putcom(buf,6);
        !          1150:                                                }
        !          1151:                                                else if(command==TELNET_WILL && option==TELNET_TERM_SPEED) {
        !          1152:                                                        if(startup->options&BBS_OPT_DEBUG_TELNET)
        !          1153:                                                                lprintf(LOG_DEBUG,"Node %d requesting telnet terminal speed"
        !          1154:                                                                        ,sbbs->cfg.node_num);
        !          1155: 
        !          1156:                                                        char    buf[64];
        !          1157:                                                        sprintf(buf,"%c%c%c%c%c%c"
        !          1158:                                                                ,TELNET_IAC,TELNET_SB
        !          1159:                                                                ,TELNET_TERM_SPEED,TELNET_TERM_SEND
        !          1160:                                                                ,TELNET_IAC,TELNET_SE);
        !          1161:                                                        sbbs->putcom(buf,6);
        !          1162:                                                }
        !          1163:                                        }
        !          1164:                                }
        !          1165: 
        !          1166:                 sbbs->telnet_cmdlen=0;
        !          1167: 
        !          1168:             }
        !          1169:                        if(sbbs->telnet_mode&TELNET_MODE_GATE)  // Pass-through commads
        !          1170:                                outbuf[outlen++]=inbuf[i];
        !          1171:         } else
        !          1172:                outbuf[outlen++]=inbuf[i];
        !          1173:     }
        !          1174:     return(outbuf);
        !          1175: }
        !          1176: 
        !          1177: void sbbs_t::send_telnet_cmd(uchar cmd, uchar opt)
        !          1178: {
        !          1179:        char buf[16];
        !          1180:        
        !          1181:        if(telnet_mode&TELNET_MODE_OFF) 
        !          1182:                return;
        !          1183: 
        !          1184:        if(cmd<TELNET_WILL) {
        !          1185:                if(startup->options&BBS_OPT_DEBUG_TELNET)
        !          1186:             lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s"
        !          1187:                    ,cfg.node_num
        !          1188:                 ,telnet_cmd_desc(cmd));
        !          1189:                sprintf(buf,"%c%c",TELNET_IAC,cmd);
        !          1190:                putcom(buf,2);
        !          1191:        } else {
        !          1192:                if(startup->options&BBS_OPT_DEBUG_TELNET)
        !          1193:                        lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s %s"
        !          1194:                                ,cfg.node_num
        !          1195:                                ,telnet_cmd_desc(cmd)
        !          1196:                                ,telnet_opt_desc(opt));
        !          1197:                sprintf(buf,"%c%c%c",TELNET_IAC,cmd,opt);
        !          1198:                putcom(buf,3);
        !          1199:        }
        !          1200: }
        !          1201: 
        !          1202: void sbbs_t::request_telnet_opt(uchar cmd, uchar opt)
        !          1203: {
        !          1204:        if(cmd==TELNET_DO || cmd==TELNET_DONT) {        /* remote option */
        !          1205:                if(telnet_remote_option[opt]==telnet_opt_ack(cmd))
        !          1206:                        return; /* already set in this mode, do nothing */
        !          1207:                telnet_remote_option[opt]=telnet_opt_ack(cmd);
        !          1208:        } else {        /* local option */
        !          1209:                if(telnet_local_option[opt]==telnet_opt_ack(cmd))
        !          1210:                        return; /* already set in this mode, do nothing */
        !          1211:                telnet_local_option[opt]=telnet_opt_ack(cmd);
        !          1212:        }
        !          1213:        send_telnet_cmd(cmd,opt);
        !          1214: }
        !          1215: 
        !          1216: void input_thread(void *arg)
        !          1217: {
        !          1218:        BYTE            inbuf[4000];
        !          1219:        BYTE            telbuf[sizeof(inbuf)];
        !          1220:     BYTE               *wrbuf;
        !          1221:     int                        i,rd,wr,avail;
        !          1222:        ulong           total_recv=0;
        !          1223:        ulong           total_pkts=0;
        !          1224:        fd_set          socket_set;
        !          1225:        sbbs_t*         sbbs = (sbbs_t*) arg;
        !          1226:        struct timeval  tv;
        !          1227:        SOCKET          high_socket;
        !          1228:        SOCKET          sock;
        !          1229: 
        !          1230:        thread_up(TRUE /* setuid */);
        !          1231: 
        !          1232: #ifdef _DEBUG
        !          1233:        lprintf(LOG_DEBUG,"Node %d input thread started",sbbs->cfg.node_num);
        !          1234: #endif
        !          1235: 
        !          1236:        pthread_mutex_init(&sbbs->input_thread_mutex,NULL);
        !          1237:     sbbs->input_thread_running = true;
        !          1238:        sbbs->console|=CON_R_INPUT;
        !          1239: 
        !          1240:        while(sbbs->online && sbbs->client_socket!=INVALID_SOCKET
        !          1241:                && node_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
        !          1242: 
        !          1243:                if(pthread_mutex_lock(&sbbs->input_thread_mutex)!=0)
        !          1244:                        sbbs->errormsg(WHERE,ERR_LOCK,"input_thread_mutex",0);
        !          1245: 
        !          1246:                FD_ZERO(&socket_set);
        !          1247:                FD_SET(sbbs->client_socket,&socket_set);
        !          1248:                high_socket=sbbs->client_socket;
        !          1249: #ifdef __unix__
        !          1250:                if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET)  {
        !          1251:                        FD_SET(uspy_socket[sbbs->cfg.node_num-1],&socket_set);
        !          1252:                        if(uspy_socket[sbbs->cfg.node_num-1] > high_socket)
        !          1253:                                high_socket=uspy_socket[sbbs->cfg.node_num-1];
        !          1254:                }
        !          1255: #endif
        !          1256: 
        !          1257:                tv.tv_sec=1;
        !          1258:                tv.tv_usec=0;
        !          1259: 
        !          1260:                if((i=select(high_socket+1,&socket_set,NULL,NULL,&tv))<1) {
        !          1261:                        if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
        !          1262:                                sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
        !          1263:                        if(i==0) {
        !          1264:                                YIELD();        /* This kludge is necessary on some Linux distros */
        !          1265:                                continue;       /* to allow other threads to lock the input_thread_mutex */
        !          1266:                        }
        !          1267: 
        !          1268:                        if(sbbs->client_socket==INVALID_SOCKET)
        !          1269:                                break;
        !          1270: #ifdef __unix__
        !          1271:                        if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
        !          1272:                                if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
        !          1273:                                        close_socket(uspy_socket[sbbs->cfg.node_num-1]);
        !          1274:                                        lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
        !          1275:                                        uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
        !          1276:                                        continue;
        !          1277:                                }
        !          1278:                        }
        !          1279: #endif
        !          1280:                if(ERROR_VALUE == ENOTSOCK)
        !          1281:                lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
        !          1282:                        else if(ERROR_VALUE==ESHUTDOWN)
        !          1283:                                lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
        !          1284:                        else if(ERROR_VALUE==EINTR)
        !          1285:                                lprintf(LOG_DEBUG,"Node %d input thread interrupted",sbbs->cfg.node_num);
        !          1286:             else if(ERROR_VALUE==ECONNRESET) 
        !          1287:                                lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
        !          1288:                else if(ERROR_VALUE==ECONNABORTED) 
        !          1289:                                lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
        !          1290:                        else
        !          1291:                                lprintf(LOG_WARNING,"Node %d !ERROR %d input->select socket %d"
        !          1292:                                ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
        !          1293:                        break;
        !          1294:                }
        !          1295: 
        !          1296:                if(sbbs->client_socket==INVALID_SOCKET) {
        !          1297:                        if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
        !          1298:                                sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
        !          1299:                        break;
        !          1300:                }
        !          1301: 
        !          1302: /*         ^          ^
        !          1303:  *             \______    ______/
        !          1304:  *       \  * \   / *   /
        !          1305:  *        -----   ------           /----\
        !          1306:  *              ||               -< Boo! |
        !          1307:  *             /__\                \----/
        !          1308:  *       \______________/
        !          1309:  *        \/\/\/\/\/\/\/
        !          1310:  *         ------------
        !          1311:  */
        !          1312: 
        !          1313:                if(FD_ISSET(sbbs->client_socket,&socket_set))
        !          1314:                        sock=sbbs->client_socket;
        !          1315: #ifdef __unix__
        !          1316:                else if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET
        !          1317:                                && FD_ISSET(uspy_socket[sbbs->cfg.node_num-1],&socket_set))  {
        !          1318:                        if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
        !          1319:                                close_socket(uspy_socket[sbbs->cfg.node_num-1]);
        !          1320:                                lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
        !          1321:                                uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
        !          1322:                                if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
        !          1323:                                        sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
        !          1324:                                continue;
        !          1325:                        }
        !          1326:                        sock=uspy_socket[sbbs->cfg.node_num-1];
        !          1327:                }
        !          1328: #endif
        !          1329:                else {
        !          1330:                        if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
        !          1331:                                sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
        !          1332:                        continue;
        !          1333:                }
        !          1334: 
        !          1335:        rd=RingBufFree(&sbbs->inbuf);
        !          1336: 
        !          1337:                if(!rd) { // input buffer full
        !          1338:                        lprintf(LOG_WARNING,"Node %d !WARNING input buffer full", sbbs->cfg.node_num);
        !          1339:                // wait up to 5 seconds to empty (1 byte min)
        !          1340:                        time_t start=time(NULL);
        !          1341:             while((rd=RingBufFree(&sbbs->inbuf))==0) {
        !          1342:                if(time(NULL)-start>=5) {
        !          1343:                        rd=1;
        !          1344:                                        if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
        !          1345:                                                sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
        !          1346:                        break;
        !          1347:                 }
        !          1348:                 YIELD();
        !          1349:             }
        !          1350:                }
        !          1351:                
        !          1352:            if(rd > (int)sizeof(inbuf))
        !          1353:                rd=sizeof(inbuf);
        !          1354: 
        !          1355: #ifdef USE_CRYPTLIB
        !          1356:                if(sbbs->ssh_mode && sock==sbbs->client_socket) {
        !          1357:                        if(!cryptStatusOK(cryptPopData(sbbs->ssh_session, (char*)inbuf, rd, &i)))
        !          1358:                                rd=0;
        !          1359:                        else {
        !          1360:                                if(!i) {
        !          1361:                                        if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
        !          1362:                                                sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
        !          1363:                                        continue;
        !          1364:                                }
        !          1365:                                rd=i;
        !          1366:                        }
        !          1367:                }
        !          1368:                else
        !          1369: #endif
        !          1370:        rd = recv(sock, (char*)inbuf, rd, 0);
        !          1371: 
        !          1372:                if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
        !          1373:                        sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
        !          1374: 
        !          1375:                if(rd == SOCKET_ERROR)
        !          1376:                {
        !          1377: #ifdef __unix__
        !          1378:                        if(sock==sbbs->client_socket)  {
        !          1379: #endif
        !          1380:                        if(ERROR_VALUE == ENOTSOCK)
        !          1381:                    lprintf(LOG_NOTICE,"Node %d socket closed by peer on receive", sbbs->cfg.node_num);
        !          1382:                    else if(ERROR_VALUE==ECONNRESET) 
        !          1383:                                        lprintf(LOG_NOTICE,"Node %d connection reset by peer on receive", sbbs->cfg.node_num);
        !          1384:                                else if(ERROR_VALUE==ESHUTDOWN)
        !          1385:                                        lprintf(LOG_NOTICE,"Node %d socket shutdown on receive", sbbs->cfg.node_num);
        !          1386:                    else if(ERROR_VALUE==ECONNABORTED) 
        !          1387:                                        lprintf(LOG_NOTICE,"Node %d connection aborted by peer on receive", sbbs->cfg.node_num);
        !          1388:                                else
        !          1389:                                        lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from socket %d"
        !          1390:                                ,sbbs->cfg.node_num, ERROR_VALUE, sock);
        !          1391:                                break;
        !          1392: #ifdef __unix__
        !          1393:                        } else  {
        !          1394:                                if(ERROR_VALUE != EAGAIN)  {
        !          1395:                                        lprintf(LOG_ERR,"Node %d !ERROR %d on local spy socket %d receive"
        !          1396:                                                , sbbs->cfg.node_num, errno, sock);
        !          1397:                                        close_socket(uspy_socket[sbbs->cfg.node_num-1]);
        !          1398:                                        uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
        !          1399:                                }
        !          1400:                                continue;
        !          1401:                        }
        !          1402: #endif
        !          1403:                }
        !          1404: 
        !          1405:                if(rd == 0 && sock==sbbs->client_socket)
        !          1406:                {
        !          1407:                        lprintf(LOG_NOTICE,"Node %d disconnected", sbbs->cfg.node_num);
        !          1408:                        break;
        !          1409:                }
        !          1410: 
        !          1411:                total_recv+=rd;
        !          1412:                total_pkts++;
        !          1413: 
        !          1414:         // telbuf and wr are modified to reflect telnet escaped data
        !          1415:                wr=rd;
        !          1416: #ifdef __unix__
        !          1417:                if(sock!=sbbs->client_socket)
        !          1418:                        wrbuf=inbuf;
        !          1419:                else
        !          1420: #endif
        !          1421:                if(sbbs->telnet_mode&TELNET_MODE_OFF)
        !          1422:                        wrbuf=inbuf;
        !          1423:                else
        !          1424:                        wrbuf=telnet_interpret(sbbs, inbuf, rd, telbuf, wr);
        !          1425:                if(wr > (int)sizeof(telbuf)) 
        !          1426:                        lprintf(LOG_ERR,"!TELBUF OVERFLOW (%d>%d)",wr,sizeof(telbuf));
        !          1427: 
        !          1428:                /* First level Ctrl-C checking */
        !          1429:                if(!(sbbs->cfg.ctrlkey_passthru&(1<<CTRL_C))
        !          1430:                        && sbbs->rio_abortable 
        !          1431:                        && !(sbbs->telnet_mode&TELNET_MODE_GATE)
        !          1432:                        && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
        !          1433:                        && memchr(wrbuf, CTRL_C, wr)) { 
        !          1434:                        if(RingBufFull(&sbbs->inbuf))
        !          1435:                        lprintf(LOG_DEBUG,"Node %d Ctrl-C hit with %lu bytes in input buffer"
        !          1436:                                        ,sbbs->cfg.node_num,RingBufFull(&sbbs->inbuf));
        !          1437:                        if(RingBufFull(&sbbs->outbuf))
        !          1438:                        lprintf(LOG_DEBUG,"Node %d Ctrl-C hit with %lu bytes in output buffer"
        !          1439:                                        ,sbbs->cfg.node_num,RingBufFull(&sbbs->outbuf));
        !          1440:                        sbbs->sys_status|=SS_ABORT;
        !          1441:                        RingBufReInit(&sbbs->inbuf);    /* Purge input buffer */
        !          1442:                RingBufReInit(&sbbs->outbuf);   /* Purge output buffer */
        !          1443:                        sem_post(&sbbs->inbuf.sem);
        !          1444:                        continue;       // Ignore the entire buffer
        !          1445:                }
        !          1446: 
        !          1447:                avail=RingBufFree(&sbbs->inbuf);
        !          1448: 
        !          1449:         if(avail<wr)
        !          1450:                        lprintf(LOG_ERR,"!INPUT BUFFER FULL (%d free)", avail);
        !          1451:         else
        !          1452:                        RingBufWrite(&sbbs->inbuf, wrbuf, wr);
        !          1453: //             if(wr>100)
        !          1454: //                     mswait(500);    // Throttle sender
        !          1455:        }
        !          1456:        sbbs->online=FALSE;
        !          1457:        sbbs->sys_status|=SS_ABORT;     /* as though Ctrl-C were hit */
        !          1458: 
        !          1459:     sbbs->input_thread_running = false;
        !          1460:        if(node_socket[sbbs->cfg.node_num-1]==INVALID_SOCKET)   // Shutdown locally
        !          1461:                sbbs->terminated = true;        // Signal JS to stop execution
        !          1462: 
        !          1463:        while(pthread_mutex_destroy(&sbbs->input_thread_mutex)==EBUSY)
        !          1464:                mswait(1);
        !          1465: 
        !          1466:        thread_down();
        !          1467:        lprintf(LOG_DEBUG,"Node %d input thread terminated (received %lu bytes in %lu blocks)"
        !          1468:                ,sbbs->cfg.node_num, total_recv, total_pkts);
        !          1469: }
        !          1470: 
        !          1471: #ifdef USE_CRYPTLIB
        !          1472: /*
        !          1473:  * This thread copies anything recieved from the client to the passthru_socket
        !          1474:  * It can only do that when the input thread is locked.
        !          1475:  * Luckily, the input thread is currently locked exactly when we want it to be.
        !          1476:  * Since the passthru socket is 8-bit clean and does NOT use a protocol,
        !          1477:  * we must handle telnet stuff HERE.
        !          1478:  * However, for JS stuff, direct operations on client_socket should generally
        !          1479:  * be done to the passthru_socket instead... THIS is the biggest problem here.
        !          1480:  */
        !          1481: void passthru_output_thread(void* arg)
        !          1482: {
        !          1483:        fd_set  socket_set;
        !          1484:        sbbs_t  *sbbs = (sbbs_t*) arg;
        !          1485:        struct  timeval tv;
        !          1486:        int             i;
        !          1487:        BYTE    inbuf[4000];
        !          1488:        BYTE    telbuf[sizeof(inbuf)];
        !          1489:        BYTE    *wrbuf;
        !          1490:        int             rd;
        !          1491:        int             wr;
        !          1492: 
        !          1493:        thread_up(FALSE /* setuid */);
        !          1494: 
        !          1495:     sbbs->passthru_output_thread_running = true;
        !          1496: 
        !          1497:        while(sbbs->client_socket!=INVALID_SOCKET && sbbs->passthru_socket!=INVALID_SOCKET && !terminate_server) {
        !          1498:                if(!sbbs->input_thread_mutex_locked) {
        !          1499:                        SLEEP(1);
        !          1500:                        continue;
        !          1501:                }
        !          1502:                
        !          1503:                FD_ZERO(&socket_set);
        !          1504:                FD_SET(sbbs->client_socket,&socket_set);
        !          1505: 
        !          1506:                tv.tv_sec=1;
        !          1507:                tv.tv_usec=0;
        !          1508: 
        !          1509:                if((i=select(sbbs->client_socket+1,&socket_set,NULL,NULL,&tv))<1) {
        !          1510:                        if(i==0) {
        !          1511:                                YIELD();        /* This kludge is necessary on some Linux distros */
        !          1512:                                continue;       /* to allow other threads to lock the input_thread_mutex */
        !          1513:                        }
        !          1514: 
        !          1515:                        if(sbbs->client_socket==INVALID_SOCKET)
        !          1516:                                break;
        !          1517:                if(ERROR_VALUE == ENOTSOCK)
        !          1518:                lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
        !          1519:                        else if(ERROR_VALUE==ESHUTDOWN)
        !          1520:                                lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
        !          1521:                        else if(ERROR_VALUE==EINTR)
        !          1522:                                lprintf(LOG_DEBUG,"Node %d passthru output thread interrupted",sbbs->cfg.node_num);
        !          1523:             else if(ERROR_VALUE==ECONNRESET) 
        !          1524:                                lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
        !          1525:                else if(ERROR_VALUE==ECONNABORTED) 
        !          1526:                                lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
        !          1527:                        else
        !          1528:                                lprintf(LOG_WARNING,"Node %d !ERROR %d ->select socket %d"
        !          1529:                                ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
        !          1530:                        break;
        !          1531:                }
        !          1532: 
        !          1533:                if(sbbs->client_socket==INVALID_SOCKET)
        !          1534:                        break;
        !          1535: 
        !          1536:        rd=sizeof(inbuf);
        !          1537: 
        !          1538: #ifdef USE_CRYPTLIB
        !          1539:                if(sbbs->ssh_mode) {
        !          1540:                        if(!cryptStatusOK(cryptPopData(sbbs->ssh_session, (char*)inbuf, rd, &i)))
        !          1541:                                rd=0;
        !          1542:                        else {
        !          1543:                                if(!i)
        !          1544:                                        continue;
        !          1545:                                rd=i;
        !          1546:                        }
        !          1547:                }
        !          1548:                else
        !          1549: #endif
        !          1550:        rd = recv(sbbs->client_socket, (char*)inbuf, rd, 0);
        !          1551: 
        !          1552:                if(rd == SOCKET_ERROR)
        !          1553:                {
        !          1554:                if(ERROR_VALUE == ENOTSOCK)
        !          1555:                lprintf(LOG_NOTICE,"Node %d socket closed by peer on receive", sbbs->cfg.node_num);
        !          1556:             else if(ERROR_VALUE==ECONNRESET) 
        !          1557:                                lprintf(LOG_NOTICE,"Node %d connection reset by peer on receive", sbbs->cfg.node_num);
        !          1558:                        else if(ERROR_VALUE==ESHUTDOWN)
        !          1559:                                lprintf(LOG_NOTICE,"Node %d socket shutdown on receive", sbbs->cfg.node_num);
        !          1560:             else if(ERROR_VALUE==ECONNABORTED) 
        !          1561:                                lprintf(LOG_NOTICE,"Node %d connection aborted by peer on receive", sbbs->cfg.node_num);
        !          1562:                        else
        !          1563:                                lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from socket %d"
        !          1564:                        ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
        !          1565:                        break;
        !          1566:                }
        !          1567: 
        !          1568:                if(rd == 0)
        !          1569:                {
        !          1570:                        lprintf(LOG_NOTICE,"Node %d passthru input socket disconnected", sbbs->cfg.node_num);
        !          1571:                        break;
        !          1572:                }
        !          1573: 
        !          1574:         // telbuf and wr are modified to reflect telnet escaped data
        !          1575:                wr=rd;
        !          1576:                if(sbbs->telnet_mode&TELNET_MODE_OFF)
        !          1577:                        wrbuf=inbuf;
        !          1578:                else
        !          1579:                        wrbuf=telnet_interpret(sbbs, inbuf, rd, telbuf, wr);
        !          1580:                if(wr > (int)sizeof(telbuf)) 
        !          1581:                        lprintf(LOG_ERR,"!TELBUF OVERFLOW (%d>%d)",wr,sizeof(telbuf));
        !          1582: 
        !          1583:                /*
        !          1584:                 * TODO: This should check for writability etc.
        !          1585:                 */
        !          1586:                sendsocket(sbbs->passthru_socket, (char*)wrbuf, wr);
        !          1587:        }
        !          1588: 
        !          1589:        sbbs->passthru_output_thread_running = false;
        !          1590: }
        !          1591: 
        !          1592: /*
        !          1593:  * This thread simply copies anything it manages to read from the
        !          1594:  * passthru_socket into the output ringbuffer.
        !          1595:  */
        !          1596: void passthru_input_thread(void* arg)
        !          1597: {
        !          1598:        fd_set  r_set;
        !          1599:        sbbs_t  *sbbs = (sbbs_t*) arg;
        !          1600:        struct  timeval tv;
        !          1601:        BYTE    ch;
        !          1602:        int             i;
        !          1603: 
        !          1604:        thread_up(FALSE /* setuid */);
        !          1605: 
        !          1606:        sbbs->passthru_input_thread_running = true;
        !          1607: 
        !          1608:        while(sbbs->passthru_socket!=INVALID_SOCKET && !terminate_server) {
        !          1609:                tv.tv_sec=1;
        !          1610:                tv.tv_usec=0;
        !          1611: 
        !          1612:                FD_ZERO(&r_set);
        !          1613:                FD_SET(sbbs->passthru_socket,&r_set);
        !          1614:                if((i=select(sbbs->passthru_socket+1,&r_set,NULL,NULL,&tv))<1) {
        !          1615:                        if(i==0) {
        !          1616:                                YIELD();        /* This kludge is necessary on some Linux distros */
        !          1617:                                continue;       /* to allow other threads to lock the input_thread_mutex */
        !          1618:                        }
        !          1619: 
        !          1620:                        if(sbbs->passthru_socket==INVALID_SOCKET)
        !          1621:                                break;
        !          1622:                if(ERROR_VALUE == ENOTSOCK)
        !          1623:                lprintf(LOG_NOTICE,"Node %d socket closed by peer on passthru->select", sbbs->cfg.node_num);
        !          1624:                        else if(ERROR_VALUE==ESHUTDOWN)
        !          1625:                                lprintf(LOG_NOTICE,"Node %d socket shutdown on passthru->select", sbbs->cfg.node_num);
        !          1626:                        else if(ERROR_VALUE==EINTR)
        !          1627:                                lprintf(LOG_DEBUG,"Node %d passthru thread interrupted",sbbs->cfg.node_num);
        !          1628:             else if(ERROR_VALUE==ECONNRESET) 
        !          1629:                                lprintf(LOG_NOTICE,"Node %d connection reset by peer on passthru->select", sbbs->cfg.node_num);
        !          1630:                else if(ERROR_VALUE==ECONNABORTED) 
        !          1631:                                lprintf(LOG_NOTICE,"Node %d connection aborted by peer on passthru->select", sbbs->cfg.node_num);
        !          1632:                        else
        !          1633:                                lprintf(LOG_WARNING,"Node %d !ERROR %d passthru->select socket %d"
        !          1634:                                ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->passthru_socket);
        !          1635:                        break;
        !          1636:                }
        !          1637:                if(!RingBufFree(&sbbs->outbuf))
        !          1638:                        continue;
        !          1639: 
        !          1640:        i = recv(sbbs->passthru_socket, (char*)(&ch), 1, 0);
        !          1641: 
        !          1642:                if(i == SOCKET_ERROR)
        !          1643:                {
        !          1644:                        if(ERROR_VALUE == ENOTSOCK)
        !          1645:                    lprintf(LOG_NOTICE,"Node %d passthru socket closed by peer on receive", sbbs->cfg.node_num);
        !          1646:                    else if(ERROR_VALUE==ECONNRESET) 
        !          1647:                                        lprintf(LOG_NOTICE,"Node %d passthru connection reset by peer on receive", sbbs->cfg.node_num);
        !          1648:                                else if(ERROR_VALUE==ESHUTDOWN)
        !          1649:                                        lprintf(LOG_NOTICE,"Node %d passthru socket shutdown on receive", sbbs->cfg.node_num);
        !          1650:                    else if(ERROR_VALUE==ECONNABORTED) 
        !          1651:                                        lprintf(LOG_NOTICE,"Node %d passthru connection aborted by peer on receive", sbbs->cfg.node_num);
        !          1652:                                else
        !          1653:                                        lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from passthru socket %d"
        !          1654:                                ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->passthru_socket);
        !          1655:                                break;
        !          1656:                }
        !          1657: 
        !          1658:                if(i == 0)
        !          1659:                {
        !          1660:                        lprintf(LOG_NOTICE,"Node %d passthru disconnected", sbbs->cfg.node_num);
        !          1661:                        break;
        !          1662:                }
        !          1663: 
        !          1664:        if(!RingBufWrite(&sbbs->outbuf, &ch, 1)) {
        !          1665:                        lprintf(LOG_ERR,"Cannot pass from passthru socket to outbuf");
        !          1666:                        break;
        !          1667:                }
        !          1668:        }
        !          1669:        if(sbbs->passthru_socket!=INVALID_SOCKET) {
        !          1670:                close_socket(sbbs->passthru_socket);
        !          1671:                sbbs->passthru_socket=INVALID_SOCKET;
        !          1672:        }
        !          1673:        thread_down();
        !          1674: 
        !          1675:        sbbs->passthru_input_thread_running = false;
        !          1676: }
        !          1677: #endif
        !          1678: 
        !          1679: void output_thread(void* arg)
        !          1680: {
        !          1681:        char            node[128];
        !          1682:        char            stats[128];
        !          1683:     BYTE               buf[IO_THREAD_BUF_SIZE];
        !          1684:        int                     i;
        !          1685:     ulong              avail;
        !          1686:        ulong           total_sent=0;
        !          1687:        ulong           total_pkts=0;
        !          1688:        ulong           short_sends=0;
        !          1689:     ulong              bufbot=0;
        !          1690:     ulong              buftop=0;
        !          1691:        sbbs_t*         sbbs = (sbbs_t*) arg;
        !          1692:        fd_set          socket_set;
        !          1693:        struct timeval tv;
        !          1694:        ulong           mss=IO_THREAD_BUF_SIZE;
        !          1695: 
        !          1696:        thread_up(TRUE /* setuid */);
        !          1697: 
        !          1698:     if(sbbs->cfg.node_num)
        !          1699:        SAFEPRINTF(node,"Node %d",sbbs->cfg.node_num);
        !          1700:     else
        !          1701:        SAFECOPY(node,sbbs->client_name);
        !          1702: #ifdef _DEBUG
        !          1703:        lprintf(LOG_DEBUG,"%s output thread started",node);
        !          1704: #endif
        !          1705: 
        !          1706:     sbbs->output_thread_running = true;
        !          1707:        sbbs->console|=CON_R_ECHO;
        !          1708: 
        !          1709: #ifdef TCP_MAXSEG
        !          1710:        /*
        !          1711:         * Auto-tune the highwater mark to be the negotiated MSS for the
        !          1712:      * socket (when possible)
        !          1713:         */
        !          1714:        if(!sbbs->outbuf.highwater_mark) {
        !          1715:                socklen_t       sl;
        !          1716:                sl=sizeof(i);
        !          1717:                if(!getsockopt(sbbs->client_socket, IPPROTO_TCP, TCP_MAXSEG, &i, &sl)) {
        !          1718:                        /* Check for sanity... */
        !          1719:                        if(i>100) {
        !          1720:                                sbbs->outbuf.highwater_mark=i;
        !          1721:                                lprintf(LOG_DEBUG,"Autotuning outbuf highwater mark to %d based on MSS",i);
        !          1722:                                mss=sbbs->outbuf.highwater_mark;
        !          1723:                                if(mss>IO_THREAD_BUF_SIZE) {
        !          1724:                                        mss=IO_THREAD_BUF_SIZE;
        !          1725:                                        lprintf(LOG_DEBUG,"MSS (%d) is higher than IO_THREAD_BUF_SIZE (%d)",i,IO_THREAD_BUF_SIZE);
        !          1726:                                }
        !          1727:                        }
        !          1728:                }
        !          1729:        }
        !          1730: #endif
        !          1731: 
        !          1732:        while(sbbs->client_socket!=INVALID_SOCKET && !terminate_server) {
        !          1733:                /*
        !          1734:                 * I'd like to check the linear buffer against the highwater
        !          1735:                 * at this point, but it would get too clumsy imho - Deuce
        !          1736:                 *
        !          1737:                 * Actually, another option would just be to have the size
        !          1738:                 * of the linear buffer equal to the MSS... any larger and
        !          1739:                 * you could have small sends off the end.  this would
        !          1740:                 * probobly be even clumbsier
        !          1741:                 */
        !          1742:                if(bufbot == buftop) {
        !          1743:                        /* Wait for something to output in the RingBuffer */
        !          1744:                        if((avail=RingBufFull(&sbbs->outbuf))==0) {     /* empty */
        !          1745:                                if(sem_trywait_block(&sbbs->outbuf.sem,1000))
        !          1746:                                        continue;
        !          1747:                                /* Check for spurious sem post... */
        !          1748:                                if((avail=RingBufFull(&sbbs->outbuf))==0)
        !          1749:                                        continue;
        !          1750:                        }
        !          1751:                        else
        !          1752:                                sem_trywait(&sbbs->outbuf.sem);
        !          1753: 
        !          1754:                        /* Wait for full buffer or drain timeout */
        !          1755:                        if(sbbs->outbuf.highwater_mark) {
        !          1756:                                if(avail<sbbs->outbuf.highwater_mark) {
        !          1757:                                        sem_trywait_block(&sbbs->outbuf.highwater_sem,startup->outbuf_drain_timeout);
        !          1758:                                        /* We (potentially) blocked, so get fill level again */
        !          1759:                                avail=RingBufFull(&sbbs->outbuf);
        !          1760:                                } else
        !          1761:                                        sem_trywait(&sbbs->outbuf.highwater_sem);       
        !          1762:                        }
        !          1763: 
        !          1764:                        /*
        !          1765:                         * At this point, there's something to send and,
        !          1766:                         * if the highwater mark is set, the timeout has
        !          1767:                         * passed or we've hit highwater.  Read ring buffer
        !          1768:                         * into linear buffer.
        !          1769:                         */
        !          1770:                if(avail>sizeof(buf)) {
        !          1771:                        lprintf(LOG_WARNING,"!%s: Insufficient linear output buffer (%lu > %lu)"
        !          1772:                                        ,node, avail, sizeof(buf));
        !          1773:                        avail=sizeof(buf);
        !          1774:                }
        !          1775:                        /* If we know the MSS, use it as the max send() size. */
        !          1776:                        if(avail>mss)
        !          1777:                                avail=mss;
        !          1778:                buftop=RingBufRead(&sbbs->outbuf, buf, avail);
        !          1779:                bufbot=0;
        !          1780:                }
        !          1781: 
        !          1782:                /* Check socket for writability (using select) */
        !          1783:                tv.tv_sec=0;
        !          1784:                tv.tv_usec=1000;
        !          1785: 
        !          1786:                FD_ZERO(&socket_set);
        !          1787:                FD_SET(sbbs->client_socket,&socket_set);
        !          1788: 
        !          1789:                i=select(sbbs->client_socket+1,NULL,&socket_set,NULL,&tv);
        !          1790:                if(i==SOCKET_ERROR) {
        !          1791:                        if(sbbs->client_socket!=INVALID_SOCKET)
        !          1792:                                lprintf(LOG_ERR,"!%s: ERROR %d selecting socket %u for send"
        !          1793:                                        ,node,ERROR_VALUE,sbbs->client_socket);
        !          1794:                        if(sbbs->cfg.node_num)  /* Only break if node output (not server) */
        !          1795:                                break;
        !          1796:                        RingBufReInit(&sbbs->outbuf);   /* Flush output buffer */
        !          1797:                        continue;
        !          1798:                }
        !          1799:                if(i<1) {
        !          1800:                        continue;
        !          1801:                }
        !          1802: 
        !          1803: #ifdef USE_CRYPTLIB
        !          1804:                if(sbbs->ssh_mode) {
        !          1805:                        if(!cryptStatusOK(cryptPushData(sbbs->ssh_session, (char*)buf+bufbot, buftop-bufbot, &i)))
        !          1806:                                i=-1;
        !          1807:                        else
        !          1808:                                cryptFlushData(sbbs->ssh_session);
        !          1809:                }
        !          1810:                else
        !          1811: #endif
        !          1812:                i=sendsocket(sbbs->client_socket, (char*)buf+bufbot, buftop-bufbot);
        !          1813:                if(i==SOCKET_ERROR) {
        !          1814:                if(ERROR_VALUE == ENOTSOCK)
        !          1815:                 lprintf(LOG_NOTICE,"%s client socket closed on send", node);
        !          1816:             else if(ERROR_VALUE==ECONNRESET) 
        !          1817:                                lprintf(LOG_NOTICE,"%s connection reset by peer on send", node);
        !          1818:             else if(ERROR_VALUE==ECONNABORTED) 
        !          1819:                                lprintf(LOG_NOTICE,"%s connection aborted by peer on send", node);
        !          1820:                        else
        !          1821:                                lprintf(LOG_WARNING,"!%s: ERROR %d sending on socket %d"
        !          1822:                        ,node, ERROR_VALUE, sbbs->client_socket);
        !          1823:                        sbbs->online=FALSE;
        !          1824:                        /* was break; on 4/7/00 */
        !          1825:                        i=buftop-bufbot;        // Pretend we sent it all
        !          1826:                }
        !          1827: 
        !          1828:                if(sbbs->cfg.node_num>0 && !(sbbs->sys_status&SS_FILEXFER)) {
        !          1829:                        /* Spy on the user locally */
        !          1830:                        if(startup->node_spybuf!=NULL 
        !          1831:                                && startup->node_spybuf[sbbs->cfg.node_num-1]!=NULL) {
        !          1832:                                RingBufWrite(startup->node_spybuf[sbbs->cfg.node_num-1],buf+bufbot,i);
        !          1833:                                /* Signal spy output semaphore? */
        !          1834:                                if(startup->node_spysem!=NULL 
        !          1835:                                        && startup->node_spysem[sbbs->cfg.node_num-1]!=NULL)
        !          1836:                                        sem_post(startup->node_spysem[sbbs->cfg.node_num-1]);
        !          1837:                        }
        !          1838:                        /* Spy on the user remotely */
        !          1839:                        if(spy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) 
        !          1840:                                sendsocket(spy_socket[sbbs->cfg.node_num-1],(char*)buf+bufbot,i);
        !          1841: #ifdef __unix__
        !          1842:                        if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET)
        !          1843:                                sendsocket(uspy_socket[sbbs->cfg.node_num-1],(char*)buf+bufbot,i);
        !          1844: #endif
        !          1845:                }
        !          1846: 
        !          1847:                if(i!=(int)(buftop-bufbot)) {
        !          1848:                        lprintf(LOG_WARNING,"!%s: Short socket send (%u instead of %u)"
        !          1849:                                ,node, i ,buftop-bufbot);
        !          1850:                        short_sends++;
        !          1851:                }
        !          1852:                bufbot+=i;
        !          1853:                total_sent+=i;
        !          1854:                total_pkts++;
        !          1855:     }
        !          1856: 
        !          1857:        sbbs->spymsg("Disconnected");
        !          1858: 
        !          1859:     sbbs->output_thread_running = false;
        !          1860: 
        !          1861:        if(total_sent)
        !          1862:                sprintf(stats,"(sent %lu bytes in %lu blocks, %lu average, %lu short)"
        !          1863:                        ,total_sent, total_pkts, total_sent/total_pkts, short_sends);
        !          1864:        else
        !          1865:                stats[0]=0;
        !          1866: 
        !          1867:        thread_down();
        !          1868:        lprintf(LOG_DEBUG,"%s output thread terminated %s", node, stats);
        !          1869: }
        !          1870: 
        !          1871: void event_thread(void* arg)
        !          1872: {
        !          1873:        ulong           stack_frame;
        !          1874:        char            str[MAX_PATH+1];
        !          1875:        char            bat_list[MAX_PATH+1];
        !          1876:        char            semfile[MAX_PATH+1];
        !          1877:        int                     i,j,k;
        !          1878:        int                     file;
        !          1879:        int                     offset;
        !          1880:        bool            check_semaphores;
        !          1881:        bool            packed_rep;
        !          1882:        ulong           l;
        !          1883:        time_t          now;
        !          1884:        time_t          start;
        !          1885:        time_t          lastsemchk=0;
        !          1886:        time_t          lastnodechk=0;
        !          1887:        time_t          lastprepack=0;
        !          1888:        node_t          node;
        !          1889:        glob_t          g;
        !          1890:        sbbs_t*         sbbs = (sbbs_t*) arg;
        !          1891:        struct tm       now_tm;
        !          1892:        struct tm       tm;
        !          1893: 
        !          1894:        eprintf(LOG_DEBUG,"BBS Events thread started");
        !          1895: 
        !          1896:        sbbs->event_thread_running = true;
        !          1897: 
        !          1898:        sbbs_srand();   /* Seed random number generator */
        !          1899: 
        !          1900:        thread_up(TRUE /* setuid */);
        !          1901: 
        !          1902: #ifdef JAVASCRIPT
        !          1903:        if(!(startup->options&BBS_OPT_NO_JAVASCRIPT)) {
        !          1904:                if(!sbbs->js_init(&stack_frame)) /* This must be done in the context of the event thread */
        !          1905:                        lprintf(LOG_ERR,"!JavaScript Initialization FAILURE");
        !          1906:        }
        !          1907: #endif
        !          1908: 
        !          1909:        // Read TIME.DAB
        !          1910:        SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
        !          1911:        if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
        !          1912:                sbbs->errormsg(WHERE,ERR_OPEN,str,0);
        !          1913:        else {
        !          1914:                for(i=0;i<sbbs->cfg.total_events;i++) {
        !          1915:                        sbbs->cfg.event[i]->last=0;
        !          1916:                        if(filelength(file)<(long)(sizeof(time_t)*(i+1))) {
        !          1917:                                eprintf(LOG_WARNING,"Initializing last run time for event: %s"
        !          1918:                                        ,sbbs->cfg.event[i]->code);
        !          1919:                                write(file,&sbbs->cfg.event[i]->last,sizeof(time_t));
        !          1920:                        } else {
        !          1921:                                if(read(file,&sbbs->cfg.event[i]->last,sizeof(time_t))!=sizeof(time_t))
        !          1922:                                        sbbs->errormsg(WHERE,ERR_READ,str,sizeof(time_t));
        !          1923:                        }
        !          1924:                        /* Event always runs after initialization? */
        !          1925:                        if(sbbs->cfg.event[i]->misc&EVENT_INIT)
        !          1926:                                sbbs->cfg.event[i]->last=-1;
        !          1927:                }
        !          1928:                lastprepack=0;
        !          1929:                read(file,&lastprepack,sizeof(time_t)); /* expected to fail first time */
        !          1930:                close(file);
        !          1931:        }
        !          1932: 
        !          1933:        // Read QNET.DAB
        !          1934:        SAFEPRINTF(str,"%sqnet.dab",sbbs->cfg.ctrl_dir);
        !          1935:        if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
        !          1936:                sbbs->errormsg(WHERE,ERR_OPEN,str,0);
        !          1937:        else {
        !          1938:                for(i=0;i<sbbs->cfg.total_qhubs;i++) {
        !          1939:                        sbbs->cfg.qhub[i]->last=0;
        !          1940:                        if(filelength(file)<(long)(sizeof(time_t)*(i+1))) {
        !          1941:                                eprintf(LOG_WARNING,"Initializing last call-out time for QWKnet hub: %s"
        !          1942:                                        ,sbbs->cfg.qhub[i]->id);
        !          1943:                                write(file,&sbbs->cfg.qhub[i]->last,sizeof(time_t));
        !          1944:                        } else {
        !          1945:                                if(read(file,&sbbs->cfg.qhub[i]->last,sizeof(time_t))!=sizeof(time_t))
        !          1946:                                        sbbs->errormsg(WHERE,ERR_READ,str,sizeof(time_t));
        !          1947:                        }
        !          1948:                }
        !          1949:                close(file);
        !          1950:        }
        !          1951: 
        !          1952:        // Read PNET.DAB
        !          1953:        SAFEPRINTF(str,"%spnet.dab",sbbs->cfg.ctrl_dir);
        !          1954:        if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
        !          1955:                sbbs->errormsg(WHERE,ERR_OPEN,str,0);
        !          1956:        else {
        !          1957:                for(i=0;i<sbbs->cfg.total_phubs;i++) {
        !          1958:                        sbbs->cfg.phub[i]->last=0;
        !          1959:                        if(filelength(file)<(long)(sizeof(time_t)*(i+1)))
        !          1960:                                write(file,&sbbs->cfg.phub[i]->last,sizeof(time_t));
        !          1961:                        else
        !          1962:                                read(file,&sbbs->cfg.phub[i]->last,sizeof(time_t)); 
        !          1963:                }
        !          1964:                close(file);
        !          1965:        }
        !          1966: 
        !          1967:        while(!sbbs->terminated && !terminate_server) {
        !          1968: 
        !          1969:                if(startup->options&BBS_OPT_NO_EVENTS) {
        !          1970:                        SLEEP(1000);
        !          1971:                        continue;
        !          1972:                }
        !          1973: 
        !          1974:                now=time(NULL);
        !          1975:                localtime_r(&now,&now_tm);
        !          1976: 
        !          1977:                if(now-lastsemchk>=sbbs->cfg.node_sem_check) {
        !          1978:                        check_semaphores=true;
        !          1979:                        lastsemchk=now;
        !          1980:                } else
        !          1981:                        check_semaphores=false;
        !          1982: 
        !          1983:                sbbs->online=FALSE;     /* reset this from ON_LOCAL */
        !          1984: 
        !          1985:                /* QWK events */
        !          1986:                if(check_semaphores && !(startup->options&BBS_OPT_NO_QWK_EVENTS)) {
        !          1987:                        /* Import any REP files that have magically appeared (via FTP perhaps) */
        !          1988:                        SAFEPRINTF(str,"%sfile/",sbbs->cfg.data_dir);
        !          1989:                        offset=strlen(str);
        !          1990:                        strcat(str,"*.rep");
        !          1991:                        glob(str,0,NULL,&g);
        !          1992:                        for(i=0;i<(int)g.gl_pathc;i++) {
        !          1993:                                sbbs->useron.number=atoi(g.gl_pathv[i]+offset);
        !          1994:                                getuserdat(&sbbs->cfg,&sbbs->useron);
        !          1995:                                if(sbbs->useron.number && flength(g.gl_pathv[i])>0) {
        !          1996:                                        SAFEPRINTF(semfile,"%s.lock",g.gl_pathv[i]);
        !          1997:                                        if(!fmutex(semfile,startup->host_name,24*60*60))
        !          1998:                                                continue;
        !          1999:                                        sbbs->online=ON_LOCAL;
        !          2000:                                        eprintf(LOG_INFO,"Un-packing QWK Reply packet from %s",sbbs->useron.alias);
        !          2001:                                        sbbs->getusrsubs();
        !          2002:                                        sbbs->unpack_rep(g.gl_pathv[i]);
        !          2003:                                        sbbs->batch_create_list();      /* FREQs? */
        !          2004:                                        sbbs->batdn_total=0;
        !          2005:                                        
        !          2006:                                        /* putuserdat? */
        !          2007:                                        remove(g.gl_pathv[i]);
        !          2008:                                        remove(semfile);
        !          2009:                                }
        !          2010:                        }
        !          2011:                        globfree(&g);
        !          2012: 
        !          2013:                        /* Create any QWK files that have magically appeared (via FTP perhaps) */
        !          2014:                        SAFEPRINTF(str,"%spack*.now",sbbs->cfg.data_dir);
        !          2015:                        offset=strlen(sbbs->cfg.data_dir)+4;
        !          2016:                        glob(str,0,NULL,&g);
        !          2017:                        for(i=0;i<(int)g.gl_pathc;i++) {
        !          2018:                                eprintf(LOG_DEBUG,"QWK pack semaphore signaled: %s", g.gl_pathv[i]);
        !          2019:                                sbbs->useron.number=atoi(g.gl_pathv[i]+offset);
        !          2020:                                SAFEPRINTF2(semfile,"%spack%04u.lock",sbbs->cfg.data_dir,sbbs->useron.number);
        !          2021:                                if(!fmutex(semfile,startup->host_name,24*60*60)) {
        !          2022:                                        eprintf(LOG_WARNING,"%s exists (already being packed?)", semfile);
        !          2023:                                        continue;
        !          2024:                                }
        !          2025:                                getuserdat(&sbbs->cfg,&sbbs->useron);
        !          2026:                                if(sbbs->useron.number && !(sbbs->useron.misc&(DELETED|INACTIVE))) {
        !          2027:                                        eprintf(LOG_INFO,"Packing QWK Message Packet for %s",sbbs->useron.alias);
        !          2028:                                        sbbs->online=ON_LOCAL;
        !          2029:                                        delfiles(sbbs->cfg.temp_dir,ALLFILES);
        !          2030:                                        sbbs->getmsgptrs();
        !          2031:                                        sbbs->getusrsubs();
        !          2032:                                        sbbs->batdn_total=0;
        !          2033: 
        !          2034:                                        sbbs->last_ns_time=sbbs->ns_time=sbbs->useron.ns_time;
        !          2035:                                        SAFEPRINTF2(bat_list,"%sfile/%04u.dwn",sbbs->cfg.data_dir,sbbs->useron.number);
        !          2036:                                        sbbs->batch_add_list(bat_list);
        !          2037: 
        !          2038:                                        SAFEPRINTF3(str,"%sfile%c%04u.qwk"
        !          2039:                                                ,sbbs->cfg.data_dir,PATH_DELIM,sbbs->useron.number);
        !          2040:                                        if(sbbs->pack_qwk(str,&l,true /* pre-pack/off-line */)) {
        !          2041:                                                eprintf(LOG_INFO,"Packing completed");
        !          2042:                                                sbbs->qwk_success(l,0,1);
        !          2043:                                                sbbs->putmsgptrs(); 
        !          2044:                                                remove(bat_list);
        !          2045:                                        } else
        !          2046:                                                eprintf(LOG_INFO,"No packet created (no new messages)");
        !          2047:                                        delfiles(sbbs->cfg.temp_dir,ALLFILES);
        !          2048:                                        sbbs->online=FALSE;
        !          2049:                                }
        !          2050:                                remove(g.gl_pathv[i]);
        !          2051:                                remove(semfile);
        !          2052:                        }
        !          2053:                        globfree(&g);
        !          2054: 
        !          2055:                        /* Create (pre-pack) QWK files for users configured as such */
        !          2056:                        SAFEPRINTF(semfile,"%sprepack.now",sbbs->cfg.data_dir);
        !          2057:                        if(sbbs->cfg.preqwk_ar[0] 
        !          2058:                                && (fexistcase(semfile) || (now-lastprepack)/60>(60*24))) {
        !          2059:                                j=lastuser(&sbbs->cfg);
        !          2060:                                eprintf(LOG_INFO,"Pre-packing QWK Message packets...");
        !          2061:                                for(i=1;i<=j;i++) {
        !          2062: 
        !          2063:                                        SAFEPRINTF2(str,"%5u of %-5u",i,j);
        !          2064:                                        //status(str);
        !          2065:                                        sbbs->useron.number=i;
        !          2066:                                        getuserdat(&sbbs->cfg,&sbbs->useron);
        !          2067: 
        !          2068:                                        if(sbbs->useron.number
        !          2069:                                                && !(sbbs->useron.misc&(DELETED|INACTIVE))       /* Pre-QWK */
        !          2070:                                                && sbbs->chk_ar(sbbs->cfg.preqwk_ar,&sbbs->useron)) { 
        !          2071:                                                for(k=1;k<=sbbs->cfg.sys_nodes;k++) {
        !          2072:                                                        if(sbbs->getnodedat(k,&node,0)!=0)
        !          2073:                                                                continue;
        !          2074:                                                        if((node.status==NODE_INUSE || node.status==NODE_QUIET
        !          2075:                                                                || node.status==NODE_LOGON) && node.useron==i)
        !          2076:                                                                break; 
        !          2077:                                                }
        !          2078:                                                if(k<=sbbs->cfg.sys_nodes)      /* Don't pre-pack with user online */
        !          2079:                                                        continue;
        !          2080:                                                eprintf(LOG_INFO,"Pre-packing QWK for %s",sbbs->useron.alias);
        !          2081:                                                sbbs->online=ON_LOCAL;
        !          2082:                                                delfiles(sbbs->cfg.temp_dir,ALLFILES);
        !          2083:                                                sbbs->getmsgptrs();
        !          2084:                                                sbbs->getusrsubs();
        !          2085:                                                sbbs->batdn_total=0;
        !          2086:                                                SAFEPRINTF3(str,"%sfile%c%04u.qwk"
        !          2087:                                                        ,sbbs->cfg.data_dir,PATH_DELIM,sbbs->useron.number);
        !          2088:                                                if(sbbs->pack_qwk(str,&l,true /* pre-pack */)) {
        !          2089:                                                        sbbs->qwk_success(l,0,1);
        !          2090:                                                        sbbs->putmsgptrs(); 
        !          2091:                                                }
        !          2092:                                                delfiles(sbbs->cfg.temp_dir,ALLFILES);
        !          2093:                                                sbbs->online=FALSE;
        !          2094:                                        } 
        !          2095:                                }
        !          2096:                                lastprepack=now;
        !          2097:                                SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
        !          2098:                                if((file=sbbs->nopen(str,O_WRONLY))==-1) {
        !          2099:                                        sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
        !          2100:                                        break; 
        !          2101:                                }
        !          2102:                                lseek(file,(long)sbbs->cfg.total_events*4L,SEEK_SET);
        !          2103:                                write(file,&lastprepack,sizeof(time_t));
        !          2104:                                close(file);
        !          2105: 
        !          2106:                                remove(semfile);
        !          2107:                                //status(STATUS_WFC);
        !          2108:                        }
        !          2109:                }
        !          2110: 
        !          2111:                if(check_semaphores) {
        !          2112: 
        !          2113:                        /* Run daily maintenance? */
        !          2114:                        sbbs->cfg.node_num=0;
        !          2115:                        sbbs->logonstats();
        !          2116:                        if(sbbs->sys_status&SS_DAILY)
        !          2117:                                sbbs->daily_maint();
        !          2118: 
        !          2119:                        /* Node Daily Events */
        !          2120:                        for(i=first_node;i<=last_node;i++) {
        !          2121:                                // Node Daily Event
        !          2122:                                node.status=NODE_INVALID_STATUS;
        !          2123:                                if(sbbs->getnodedat(i,&node,0)!=0)
        !          2124:                                        continue;
        !          2125:                                if(node.misc&NODE_EVENT && node.status==NODE_WFC) {
        !          2126:                                        sbbs->getnodedat(i,&node,1);
        !          2127:                                        node.status=NODE_EVENT_RUNNING;
        !          2128:                                        sbbs->putnodedat(i,&node);
        !          2129:                                        if(sbbs->cfg.node_daily[0]) {
        !          2130:                                                sbbs->cfg.node_num=i;
        !          2131:                                                strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[i-1]);
        !          2132: 
        !          2133:                                                eprintf(LOG_INFO,"Running node %d daily event",i);
        !          2134:                                                sbbs->online=ON_LOCAL;
        !          2135:                                                sbbs->logentry("!:","Run node daily event");
        !          2136:                                                sbbs->external(
        !          2137:                                                         sbbs->cmdstr(sbbs->cfg.node_daily,nulstr,nulstr,NULL)
        !          2138:                                                        ,EX_OFFLINE);
        !          2139:                                        }
        !          2140:                                        sbbs->getnodedat(i,&node,1);
        !          2141:                                        node.misc&=~NODE_EVENT;
        !          2142:                                        node.status=NODE_WFC;
        !          2143:                                        node.useron=0;
        !          2144:                                        sbbs->putnodedat(i,&node); 
        !          2145:                                }
        !          2146:                        }
        !          2147: 
        !          2148:                        /* QWK Networking Call-out sempahores */
        !          2149:                        for(i=0;i<sbbs->cfg.total_qhubs;i++) {
        !          2150:                                if(sbbs->cfg.qhub[i]->node<first_node 
        !          2151:                                        || sbbs->cfg.qhub[i]->node>last_node)
        !          2152:                                        continue;
        !          2153:                                if(sbbs->cfg.qhub[i]->last==-1) // already signaled
        !          2154:                                        continue;
        !          2155:                                SAFEPRINTF2(str,"%sqnet/%s.now",sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
        !          2156:                                if(fexistcase(str)) {
        !          2157:                                        strcpy(str,sbbs->cfg.qhub[i]->id);
        !          2158:                                        eprintf(LOG_INFO,"Semaphore signaled for QWK Network Hub: %s",strupr(str));
        !          2159:                                        sbbs->cfg.qhub[i]->last=-1; 
        !          2160:                                }
        !          2161:                        }
        !          2162: 
        !          2163:                        /* Timed Event sempahores */
        !          2164:                        for(i=0;i<sbbs->cfg.total_events;i++) {
        !          2165:                                if((sbbs->cfg.event[i]->node<first_node
        !          2166:                                        || sbbs->cfg.event[i]->node>last_node)
        !          2167:                                        && !(sbbs->cfg.event[i]->misc&EVENT_EXCL))
        !          2168:                                        continue;       // ignore non-exclusive events for other instances
        !          2169:                                if(sbbs->cfg.event[i]->misc&EVENT_DISABLED)
        !          2170:                                        continue;
        !          2171:                                if(sbbs->cfg.event[i]->last==-1) // already signaled
        !          2172:                                        continue;
        !          2173:                                SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
        !          2174:                                if(fexistcase(str)) {
        !          2175:                                        strcpy(str,sbbs->cfg.event[i]->code);
        !          2176:                                        eprintf(LOG_INFO,"Semaphore signaled for Timed Event: %s",strupr(str));
        !          2177:                                        sbbs->cfg.event[i]->last=-1; 
        !          2178:                                }
        !          2179:                        }
        !          2180:                }
        !          2181: 
        !          2182:                /* QWK Networking Call-out Events */
        !          2183:                for(i=0;i<sbbs->cfg.total_qhubs;i++) {
        !          2184:                        if(sbbs->cfg.qhub[i]->node<first_node ||
        !          2185:                                sbbs->cfg.qhub[i]->node>last_node)
        !          2186:                                continue;
        !          2187: 
        !          2188:                        if(check_semaphores) {
        !          2189:                                // See if any packets have come in
        !          2190:                                for(j=0;j<101;j++) {
        !          2191:                                        SAFEPRINTF4(str,"%s%s.q%c%c",sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id
        !          2192:                                                ,j>10 ? ((j-1)/10)+'0' : 'w'
        !          2193:                                                ,j ? ((j-1)%10)+'0' : 'k');
        !          2194:                                        if(fexistcase(str) && flength(str)>0) { /* silently ignore 0-byte QWK packets */
        !          2195:                                                eprintf(LOG_DEBUG,"Inbound QWK Packet detected: %s", str);
        !          2196:                                                delfiles(sbbs->cfg.temp_dir,ALLFILES);
        !          2197:                                                sbbs->online=ON_LOCAL;
        !          2198:                                                sbbs->console|=CON_L_ECHO;
        !          2199:                                                if(sbbs->unpack_qwk(str,i)==false) {
        !          2200:                                                        char newname[MAX_PATH+1];
        !          2201:                                                        SAFEPRINTF2(newname,"%s.%lx.bad",str,(long)now);
        !          2202:                                                        remove(newname);
        !          2203:                                                        if(rename(str,newname)==0) {
        !          2204:                                                                char logmsg[MAX_PATH*3];
        !          2205:                                                                SAFEPRINTF2(logmsg,"%s renamed to %s",str,newname);
        !          2206:                                                                sbbs->logline("Q!",logmsg);
        !          2207:                                                        }
        !          2208:                                                }
        !          2209:                                                sbbs->console&=~CON_L_ECHO;
        !          2210:                                                sbbs->online=FALSE;
        !          2211:                                                remove(str);
        !          2212:                                        } 
        !          2213:                                }
        !          2214:                        }
        !          2215: 
        !          2216:                        /* Qnet call out based on time */
        !          2217:                        if(localtime_r(&sbbs->cfg.qhub[i]->last,&tm)==NULL)
        !          2218:                                memset(&tm,0,sizeof(tm));
        !          2219:                        if((sbbs->cfg.qhub[i]->last==-1L                                        /* or frequency */
        !          2220:                                || ((sbbs->cfg.qhub[i]->freq
        !          2221:                                        && (now-sbbs->cfg.qhub[i]->last)/60>sbbs->cfg.qhub[i]->freq)
        !          2222:                                        || (sbbs->cfg.qhub[i]->time
        !          2223:                                                && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.qhub[i]->time
        !          2224:                                                && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
        !          2225:                                                && sbbs->cfg.qhub[i]->days&(1<<now_tm.tm_wday))) {
        !          2226:                                SAFEPRINTF2(str,"%sqnet/%s.now"
        !          2227:                                        ,sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
        !          2228:                                if(fexistcase(str))
        !          2229:                                        remove(str);                                    /* Remove semaphore file */
        !          2230:                                SAFEPRINTF2(str,"%sqnet/%s.ptr"
        !          2231:                                        ,sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
        !          2232:                                file=sbbs->nopen(str,O_RDONLY);
        !          2233:                                for(j=0;j<sbbs->cfg.qhub[i]->subs;j++) {
        !          2234:                                        sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr=0;
        !          2235:                                        if(file!=-1) {
        !          2236:                                                lseek(file,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(long),SEEK_SET);
        !          2237:                                                read(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(long)); 
        !          2238:                                        }
        !          2239:                                }
        !          2240:                                if(file!=-1)
        !          2241:                                        close(file);
        !          2242:                                sbbs->console|=CON_L_ECHO;
        !          2243:                                packed_rep=sbbs->pack_rep(i);
        !          2244:                                sbbs->console&=~CON_L_ECHO;
        !          2245:                                if(packed_rep) {
        !          2246:                                        if((file=sbbs->nopen(str,O_WRONLY|O_CREAT))==-1)
        !          2247:                                                sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT);
        !          2248:                                        else {
        !          2249:                                                for(j=l=0;j<sbbs->cfg.qhub[i]->subs;j++) {
        !          2250:                                                        while(filelength(file)<
        !          2251:                                                                sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*4L)
        !          2252:                                                                write(file,&l,4);               /* initialize ptrs to null */
        !          2253:                                                        lseek(file
        !          2254:                                                                ,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(long)
        !          2255:                                                                ,SEEK_SET);
        !          2256:                                                        write(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(long)); 
        !          2257:                                                }
        !          2258:                                                close(file); 
        !          2259:                                        } 
        !          2260:                                }
        !          2261:                                delfiles(sbbs->cfg.temp_dir,ALLFILES);
        !          2262: 
        !          2263:                                sbbs->cfg.qhub[i]->last=time(NULL);
        !          2264:                                SAFEPRINTF(str,"%sqnet.dab",sbbs->cfg.ctrl_dir);
        !          2265:                                if((file=sbbs->nopen(str,O_WRONLY))==-1) {
        !          2266:                                        sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
        !          2267:                                        break; 
        !          2268:                                }
        !          2269:                                lseek(file,sizeof(time_t)*i,SEEK_SET);
        !          2270:                                write(file,&sbbs->cfg.qhub[i]->last,sizeof(time_t));
        !          2271:                                close(file);
        !          2272: 
        !          2273:                                if(sbbs->cfg.qhub[i]->call[0]) {
        !          2274:                                        sbbs->cfg.node_num=sbbs->cfg.qhub[i]->node;
        !          2275:                                        if(sbbs->cfg.node_num<1) 
        !          2276:                                                sbbs->cfg.node_num=1;
        !          2277:                                        strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
        !          2278:                                        eprintf(LOG_INFO,"QWK Network call-out: %s",sbbs->cfg.qhub[i]->id); 
        !          2279:                                        sbbs->online=ON_LOCAL;
        !          2280:                                        sbbs->external(
        !          2281:                                                 sbbs->cmdstr(sbbs->cfg.qhub[i]->call
        !          2282:                                                        ,sbbs->cfg.qhub[i]->id,sbbs->cfg.qhub[i]->id,NULL)
        !          2283:                                                ,EX_OFFLINE|EX_SH);     /* sh for Unix perl scripts */
        !          2284:                                }
        !          2285:                        } 
        !          2286:                }
        !          2287: 
        !          2288:                /* PostLink Networking Call-out Events */
        !          2289:                for(i=0;i<sbbs->cfg.total_phubs;i++) {
        !          2290:                        if(sbbs->cfg.phub[i]->node<first_node 
        !          2291:                                || sbbs->cfg.phub[i]->node>last_node)
        !          2292:                                continue;
        !          2293:                        /* PostLink call out based on time */
        !          2294:                        if(localtime_r(&sbbs->cfg.phub[i]->last,&tm)==NULL)
        !          2295:                                memset(&tm,0,sizeof(tm));
        !          2296:                        if(sbbs->cfg.phub[i]->last==-1
        !          2297:                                || (((sbbs->cfg.phub[i]->freq                                                           /* or frequency */
        !          2298:                                        && (now-sbbs->cfg.phub[i]->last)/60>sbbs->cfg.phub[i]->freq)
        !          2299:                                || (sbbs->cfg.phub[i]->time
        !          2300:                                        && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.phub[i]->time
        !          2301:                                && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
        !          2302:                                && sbbs->cfg.phub[i]->days&(1<<now_tm.tm_wday))) {
        !          2303: 
        !          2304:                                sbbs->cfg.phub[i]->last=time(NULL);
        !          2305:                                SAFEPRINTF(str,"%spnet.dab",sbbs->cfg.ctrl_dir);
        !          2306:                                if((file=sbbs->nopen(str,O_WRONLY))==-1) {
        !          2307:                                        sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
        !          2308:                                        break; 
        !          2309:                                }
        !          2310:                                lseek(file,sizeof(time_t)*i,SEEK_SET);
        !          2311:                                write(file,&sbbs->cfg.phub[i]->last,sizeof(time_t));
        !          2312:                                close(file);
        !          2313: 
        !          2314:                                if(sbbs->cfg.phub[i]->call[0]) {
        !          2315:                                        sbbs->cfg.node_num=sbbs->cfg.phub[i]->node;
        !          2316:                                        if(sbbs->cfg.node_num<1) 
        !          2317:                                                sbbs->cfg.node_num=1;
        !          2318:                                        strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
        !          2319:                                        eprintf(LOG_INFO,"PostLink Network call-out: %s",sbbs->cfg.phub[i]->name); 
        !          2320:                                        sbbs->online=ON_LOCAL;
        !          2321:                                        sbbs->external(
        !          2322:                                                 sbbs->cmdstr(sbbs->cfg.phub[i]->call,nulstr,nulstr,NULL)
        !          2323:                                                ,EX_OFFLINE|EX_SH);     /* sh for Unix perl scripts */
        !          2324:                                } 
        !          2325:                        }
        !          2326:                }
        !          2327: 
        !          2328:                /* Timed Events */
        !          2329:                for(i=0;i<sbbs->cfg.total_events;i++) {
        !          2330:                        if(!sbbs->cfg.event[i]->node 
        !          2331:                                || sbbs->cfg.event[i]->node>sbbs->cfg.sys_nodes)
        !          2332:                                continue;       // ignore events for invalid nodes
        !          2333: 
        !          2334:                        if(sbbs->cfg.event[i]->misc&EVENT_DISABLED)
        !          2335:                                continue;
        !          2336: 
        !          2337:                        if((sbbs->cfg.event[i]->node<first_node
        !          2338:                                || sbbs->cfg.event[i]->node>last_node)
        !          2339:                                && !(sbbs->cfg.event[i]->misc&EVENT_EXCL))
        !          2340:                                continue;       // ignore non-exclusive events for other instances
        !          2341: 
        !          2342:                        if(localtime_r(&sbbs->cfg.event[i]->last,&tm)==NULL)
        !          2343:                                memset(&tm,0,sizeof(tm));
        !          2344:                        if(sbbs->cfg.event[i]->last==-1 ||
        !          2345:                                (((sbbs->cfg.event[i]->freq 
        !          2346:                                        && (now-sbbs->cfg.event[i]->last)/60>sbbs->cfg.event[i]->freq)
        !          2347:                                ||      (!sbbs->cfg.event[i]->freq 
        !          2348:                                        && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.event[i]->time
        !          2349:                                && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
        !          2350:                                && sbbs->cfg.event[i]->days&(1<<now_tm.tm_wday)
        !          2351:                                && (sbbs->cfg.event[i]->mdays==0 
        !          2352:                                        || sbbs->cfg.event[i]->mdays&(1<<now_tm.tm_mday)))) 
        !          2353:                        {
        !          2354:                                if(sbbs->cfg.event[i]->misc&EVENT_EXCL) { /* exclusive event */
        !          2355: 
        !          2356:                                        if(sbbs->cfg.event[i]->node<first_node
        !          2357:                                                || sbbs->cfg.event[i]->node>last_node) {
        !          2358:                                                eprintf(LOG_INFO,"Waiting for node %d to run timed event: %s"
        !          2359:                                                        ,sbbs->cfg.event[i]->node,sbbs->cfg.event[i]->code);
        !          2360:                                                eprintf(LOG_DEBUG,"%s event last run: %s (0x%08lx)"
        !          2361:                                                        ,sbbs->cfg.event[i]->code
        !          2362:                                                        ,timestr(&sbbs->cfg, &sbbs->cfg.event[i]->last, str)
        !          2363:                                                        ,sbbs->cfg.event[i]->last);
        !          2364:                                                lastnodechk=0;   /* really last event time check */
        !          2365:                                                start=time(NULL);
        !          2366:                                                while(!sbbs->terminated) {
        !          2367:                                                        mswait(1000);
        !          2368:                                                        now=time(NULL);
        !          2369:                                                        if(now-start>10 && now-lastnodechk<10)
        !          2370:                                                                continue;
        !          2371:                                                        for(j=first_node;j<=last_node;j++) {
        !          2372:                                                                if(sbbs->getnodedat(j,&node,1)!=0)
        !          2373:                                                                        continue;
        !          2374:                                                                if(node.status==NODE_WFC)
        !          2375:                                                                        node.status=NODE_EVENT_LIMBO;
        !          2376:                                                                node.aux=sbbs->cfg.event[i]->node;
        !          2377:                                                                sbbs->putnodedat(j,&node);
        !          2378:                                                        }
        !          2379: 
        !          2380:                                                        lastnodechk=now;
        !          2381:                                                        SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
        !          2382:                                                        if((file=sbbs->nopen(str,O_RDONLY))==-1) {
        !          2383:                                                                sbbs->errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
        !          2384:                                                                sbbs->cfg.event[i]->last=now;
        !          2385:                                                                continue; 
        !          2386:                                                        }
        !          2387:                                                        lseek(file,(long)i*4L,SEEK_SET);
        !          2388:                                                        read(file,&sbbs->cfg.event[i]->last,sizeof(time_t));
        !          2389:                                                        close(file);
        !          2390:                                                        if(now-sbbs->cfg.event[i]->last<(60*60))        /* event is done */
        !          2391:                                                                break; 
        !          2392:                                                        if(now-start>(90*60)) {
        !          2393:                                                                eprintf(LOG_WARNING,"!TIMEOUT waiting for event to complete");
        !          2394:                                                                break;
        !          2395:                                                        }
        !          2396:                                                }
        !          2397:                                                SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
        !          2398:                                                if(fexistcase(str))
        !          2399:                                                        remove(str);
        !          2400:                                                sbbs->cfg.event[i]->last=now;
        !          2401:                                        } else {        // Exclusive event to run on a node under our control
        !          2402:                                                eprintf(LOG_INFO,"Waiting for all nodes to become inactive before "
        !          2403:                                                        "running timed event: %s",sbbs->cfg.event[i]->code);
        !          2404:                                                lastnodechk=0;
        !          2405:                                                start=time(NULL);
        !          2406:                                                while(!sbbs->terminated) {
        !          2407:                                                        mswait(1000);
        !          2408:                                                        now=time(NULL);
        !          2409:                                                        if(now-start>10 && now-lastnodechk<10)
        !          2410:                                                                continue;
        !          2411:                                                        lastnodechk=now;
        !          2412:                                                        // Check/change the status of the nodes that we're in control of
        !          2413:                                                        for(j=first_node;j<=last_node;j++) {
        !          2414:                                                                if(sbbs->getnodedat(j,&node,1)!=0)
        !          2415:                                                                        continue;
        !          2416:                                                                if(node.status==NODE_WFC) {
        !          2417:                                                                        if(j==sbbs->cfg.event[i]->node)
        !          2418:                                                                                node.status=NODE_EVENT_WAITING;
        !          2419:                                                                        else
        !          2420:                                                                                node.status=NODE_EVENT_LIMBO;
        !          2421:                                                                        node.aux=sbbs->cfg.event[i]->node;
        !          2422:                                                                }
        !          2423:                                                                sbbs->putnodedat(j,&node);
        !          2424:                                                        }
        !          2425: 
        !          2426:                                                        for(j=1;j<=sbbs->cfg.sys_nodes;j++) {
        !          2427:                                                                if(sbbs->getnodedat(j,&node,0)!=0)
        !          2428:                                                                        continue;
        !          2429:                                                                if(j==sbbs->cfg.event[i]->node) {
        !          2430:                                                                        if(node.status!=NODE_EVENT_WAITING)
        !          2431:                                                                                break;
        !          2432:                                                                } else {
        !          2433:                                                                        if(node.status!=NODE_OFFLINE
        !          2434:                                                                                && node.status!=NODE_EVENT_LIMBO)
        !          2435:                                                                                break; 
        !          2436:                                                                }
        !          2437:                                                        }
        !          2438:                                                        if(j>sbbs->cfg.sys_nodes) /* all nodes either offline or in limbo */
        !          2439:                                                                break;
        !          2440:                                                        eprintf(LOG_DEBUG,"Waiting for node %d (status=%d)",j,node.status);
        !          2441:                                                        if(now-start>(90*60)) {
        !          2442:                                                                eprintf(LOG_WARNING,"!TIMEOUT waiting for node %d to become inactive",j);
        !          2443:                                                                break;
        !          2444:                                                        }
        !          2445:                                                } 
        !          2446:                                        } 
        !          2447:                                }
        !          2448: #if 0 // removed Jun-23-2002
        !          2449:                                else {  /* non-exclusive */
        !          2450:                                        sbbs->getnodedat(sbbs->cfg.event[i]->node,&node,0);
        !          2451:                                        if(node.status!=NODE_WFC)
        !          2452:                                                continue;
        !          2453:                                }
        !          2454: #endif
        !          2455:                                if(sbbs->cfg.event[i]->node<first_node 
        !          2456:                                        || sbbs->cfg.event[i]->node>last_node) {
        !          2457:                                        eprintf(LOG_NOTICE,"Changing node status for nodes %d through %d to WFC"
        !          2458:                                                ,first_node,last_node);
        !          2459:                                        sbbs->cfg.event[i]->last=now;
        !          2460:                                        for(j=first_node;j<=last_node;j++) {
        !          2461:                                                node.status=NODE_INVALID_STATUS;
        !          2462:                                                if(sbbs->getnodedat(j,&node,1)!=0)
        !          2463:                                                        continue;
        !          2464:                                                node.status=NODE_WFC;
        !          2465:                                                sbbs->putnodedat(j,&node);
        !          2466:                                        }
        !          2467:                                }
        !          2468:                                else {
        !          2469:                                        sbbs->cfg.node_num=sbbs->cfg.event[i]->node;
        !          2470:                                        if(sbbs->cfg.node_num<1) 
        !          2471:                                                sbbs->cfg.node_num=1;
        !          2472:                                        strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
        !          2473:                                
        !          2474:                                        SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
        !          2475:                                        if(fexistcase(str))
        !          2476:                                                remove(str);
        !          2477:                                        if(sbbs->cfg.event[i]->misc&EVENT_EXCL) {
        !          2478:                                                sbbs->getnodedat(sbbs->cfg.event[i]->node,&node,1);
        !          2479:                                                node.status=NODE_EVENT_RUNNING;
        !          2480:                                                sbbs->putnodedat(sbbs->cfg.event[i]->node,&node);
        !          2481:                                        }
        !          2482:                                        strcpy(str,sbbs->cfg.event[i]->code);
        !          2483:                                        eprintf(LOG_INFO,"Running timed event: %s",strupr(str));
        !          2484:                                        int ex_mode = EX_OFFLINE;
        !          2485:                                        if(!(sbbs->cfg.event[i]->misc&EVENT_EXCL)
        !          2486:                                                && sbbs->cfg.event[i]->misc&EX_BG)
        !          2487:                                                ex_mode |= EX_BG;
        !          2488:                                        if(sbbs->cfg.event[i]->misc&XTRN_SH)
        !          2489:                                                ex_mode |= EX_SH;
        !          2490:                                        ex_mode|=(sbbs->cfg.event[i]->misc&EX_NATIVE);
        !          2491:                                        sbbs->online=ON_LOCAL;
        !          2492:                                        sbbs->external(
        !          2493:                                                 sbbs->cmdstr(sbbs->cfg.event[i]->cmd,nulstr,sbbs->cfg.event[i]->dir,NULL)
        !          2494:                                                ,ex_mode
        !          2495:                                                ,sbbs->cfg.event[i]->dir);
        !          2496:                                        sbbs->cfg.event[i]->last=time(NULL);
        !          2497:                                        SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
        !          2498:                                        if((file=sbbs->nopen(str,O_WRONLY))==-1) {
        !          2499:                                                sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
        !          2500:                                                break; 
        !          2501:                                        }
        !          2502:                                        lseek(file,(long)i*4L,SEEK_SET);
        !          2503:                                        write(file,&sbbs->cfg.event[i]->last,sizeof(time_t));
        !          2504:                                        close(file);
        !          2505: 
        !          2506:                                        if(sbbs->cfg.event[i]->misc&EVENT_EXCL) { /* exclusive event */
        !          2507:                                                // Check/change the status of the nodes that we're in control of
        !          2508:                                                for(j=first_node;j<=last_node;j++) {
        !          2509:                                                        node.status=NODE_INVALID_STATUS;
        !          2510:                                                        if(sbbs->getnodedat(j,&node,1)!=0)
        !          2511:                                                                continue;
        !          2512:                                                        node.status=NODE_WFC;
        !          2513:                                                        sbbs->putnodedat(j,&node);
        !          2514:                                                }
        !          2515:                                        }
        !          2516:                                } 
        !          2517:                        } 
        !          2518:                }
        !          2519:                mswait(1000);
        !          2520:        }
        !          2521:        sbbs->cfg.node_num=0;
        !          2522:     sbbs->event_thread_running = false;
        !          2523: 
        !          2524:        thread_down();
        !          2525:        eprintf(LOG_DEBUG,"BBS Event thread terminated (%u threads remain)", thread_count);
        !          2526: }
        !          2527: 
        !          2528: 
        !          2529: //****************************************************************************
        !          2530: sbbs_t::sbbs_t(ushort node_num, DWORD addr, char* name, SOCKET sd,
        !          2531:                           scfg_t* global_cfg, char* global_text[], client_t* client_info)
        !          2532: {
        !          2533:        char    nodestr[32];
        !          2534:        char    path[MAX_PATH+1];
        !          2535:        uint    i;
        !          2536: 
        !          2537:     if(node_num)
        !          2538:        SAFEPRINTF(nodestr,"Node %d",node_num);
        !          2539:     else
        !          2540:        strcpy(nodestr,name);
        !          2541: 
        !          2542:        lprintf(LOG_DEBUG,"%s constructor using socket %d (settings=%lx)"
        !          2543:                ,nodestr, sd, global_cfg->node_misc);
        !          2544: 
        !          2545:        startup = ::startup;    // Convert from global to class member
        !          2546: 
        !          2547:        memcpy(&cfg, global_cfg, sizeof(cfg));
        !          2548: 
        !          2549:        cfg.node_num=node_num;
        !          2550:        if(node_num>0) {
        !          2551:                strcpy(cfg.node_dir, cfg.node_path[node_num-1]);
        !          2552:                prep_dir(cfg.node_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
        !          2553:        } else {        /* event thread needs exclusive-use temp_dir */
        !          2554:                if(startup->temp_dir[0])
        !          2555:                        SAFECOPY(cfg.temp_dir,startup->temp_dir);
        !          2556:                else
        !          2557:                        SAFECOPY(cfg.temp_dir,"../temp");
        !          2558:        prep_dir(cfg.ctrl_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
        !          2559:                md(cfg.temp_dir);
        !          2560:                if(sd==INVALID_SOCKET) {        /* events thread */
        !          2561:                        if(startup->first_node==1)
        !          2562:                                SAFEPRINTF(path,"%sevent",cfg.temp_dir);
        !          2563:                        else
        !          2564:                                SAFEPRINTF2(path,"%sevent%u",cfg.temp_dir,startup->first_node);
        !          2565:                        backslash(path);
        !          2566:                        SAFECOPY(cfg.temp_dir,path);
        !          2567:                }
        !          2568:        }
        !          2569:        lprintf(LOG_DEBUG,"%s temporary file directory: %s", nodestr, cfg.temp_dir);
        !          2570: 
        !          2571:        terminated = false;
        !          2572:        event_thread_running = false;
        !          2573:     input_thread_running = false;
        !          2574:     output_thread_running = false;
        !          2575:        input_thread_mutex_locked = false;
        !          2576: 
        !          2577:        if(client_info==NULL)
        !          2578:                memset(&client,0,sizeof(client));
        !          2579:        else
        !          2580:                memcpy(&client,client_info,sizeof(client));
        !          2581:        client_addr = addr;
        !          2582:        client_socket = sd;
        !          2583:        SAFECOPY(client_name, name);
        !          2584:        client_socket_dup=INVALID_SOCKET;
        !          2585:        client_ident[0]=0;
        !          2586: 
        !          2587:        terminal[0]=0;
        !          2588:        rlogin_name[0]=0;
        !          2589:        rlogin_pass[0]=0;
        !          2590: 
        !          2591:        /* Init some important variables */
        !          2592: 
        !          2593: #ifdef USE_CRYPTLIB
        !          2594:        ssh_mode=false;
        !          2595:     passthru_input_thread_running = false;
        !          2596:     passthru_output_thread_running = false;
        !          2597: #endif
        !          2598: 
        !          2599:        rio_abortable=false;
        !          2600: 
        !          2601:        console = 0;
        !          2602:        online = 0;
        !          2603:        outchar_esc = 0;
        !          2604:        nodemsg_inside = 0;     /* allows single nest */
        !          2605:        hotkey_inside = 0;      /* allows single nest */
        !          2606:        event_time = 0;
        !          2607:        event_code = nulstr;
        !          2608:        nodesync_inside = false;
        !          2609:        errorlog_inside = false;
        !          2610:        errormsg_inside = false;
        !          2611:        gettimeleft_inside = false;
        !          2612:        timeleft = 60*10;       /* just incase this is being used for calling gettimeleft() */
        !          2613:        uselect_total = 0;
        !          2614:        lbuflen = 0;
        !          2615:        keybufbot=keybuftop=0;  /* initialize [unget]keybuf pointers */
        !          2616:        connection="Telnet";
        !          2617: 
        !          2618:        ZERO_VAR(telnet_local_option);
        !          2619:        ZERO_VAR(telnet_remote_option);
        !          2620: 
        !          2621:     telnet_cmdlen=0;
        !          2622:        telnet_mode=0;
        !          2623:        telnet_last_rxch=0;
        !          2624: 
        !          2625:        sys_status=lncntr=tos=criterrs=slcnt=0L;
        !          2626:        curatr=LIGHTGRAY;
        !          2627:        attr_sp=0;      /* attribute stack pointer */
        !          2628:        errorlevel=0;
        !          2629:        logcol=1;
        !          2630:        logfile_fp=NULL;
        !          2631:        nodefile=-1;
        !          2632:        node_ext=-1;
        !          2633:        nodefile_fp=NULL;
        !          2634:        node_ext_fp=NULL;
        !          2635:        current_msg=NULL;
        !          2636:        mnestr=NULL;
        !          2637: 
        !          2638: #ifdef JAVASCRIPT
        !          2639:        js_runtime=NULL;        /* runtime */
        !          2640:        js_cx=NULL;                     /* context */
        !          2641: #endif
        !          2642: 
        !          2643:        for(i=0;i<TOTAL_TEXT;i++)
        !          2644:                text[i]=text_sav[i]=global_text[i];
        !          2645: 
        !          2646:        ZERO_VAR(main_csi);
        !          2647:        ZERO_VAR(thisnode);
        !          2648:        ZERO_VAR(useron);
        !          2649:        ZERO_VAR(inbuf);
        !          2650:        ZERO_VAR(outbuf);
        !          2651:        ZERO_VAR(smb);
        !          2652:        ZERO_VAR(nodesync_user);
        !          2653: 
        !          2654:        action=NODE_MAIN;
        !          2655:        global_str_vars=0;
        !          2656:        global_str_var=NULL;
        !          2657:        global_str_var_name=NULL;
        !          2658:        global_int_vars=0;
        !          2659:        global_int_var=NULL;
        !          2660:        global_int_var_name=NULL;
        !          2661:        sysvar_li=0;
        !          2662:        sysvar_pi=0;
        !          2663: 
        !          2664:        cursub=NULL;
        !          2665:        usrgrp=NULL;
        !          2666:        usrsubs=NULL;
        !          2667:        usrsub=NULL;
        !          2668:        usrgrp_total=0;
        !          2669: 
        !          2670:        subscan=NULL;
        !          2671: 
        !          2672:        curdir=NULL;
        !          2673:        usrlib=NULL;
        !          2674:        usrdirs=NULL;
        !          2675:        usrdir=NULL;
        !          2676:        usrlib_total=0;
        !          2677: 
        !          2678:        batup_desc=NULL;
        !          2679:        batup_name=NULL;
        !          2680:        batup_misc=NULL;
        !          2681:        batup_dir=NULL;
        !          2682:        batup_alt=NULL;
        !          2683: 
        !          2684:        batdn_name=NULL;
        !          2685:        batdn_dir=NULL;
        !          2686:        batdn_offset=NULL;
        !          2687:        batdn_size=NULL;
        !          2688:        batdn_alt=NULL;
        !          2689:        batdn_cdt=NULL;
        !          2690: 
        !          2691:        spymsg("Connected");
        !          2692: }
        !          2693: 
        !          2694: //****************************************************************************
        !          2695: bool sbbs_t::init()
        !          2696: {
        !          2697:        char            str[MAX_PATH+1];
        !          2698:        char            tmp[128];
        !          2699:        int                     result;
        !          2700:        uint            i,j,k,l;
        !          2701:        node_t          node;
        !          2702:        socklen_t       addr_len;
        !          2703:        SOCKADDR_IN     addr;
        !          2704: 
        !          2705:        if(cfg.node_num>0) {
        !          2706:                RingBufInit(&inbuf, IO_THREAD_BUF_SIZE);
        !          2707:                node_inbuf[cfg.node_num-1]=&inbuf;
        !          2708:        }
        !          2709: 
        !          2710:     RingBufInit(&outbuf, IO_THREAD_BUF_SIZE);
        !          2711:        outbuf.highwater_mark=startup->outbuf_highwater_mark;
        !          2712: 
        !          2713:        if(cfg.node_num && client_socket!=INVALID_SOCKET) {
        !          2714: 
        !          2715: #ifdef _WIN32
        !          2716:                if(!DuplicateHandle(GetCurrentProcess(),
        !          2717:                        (HANDLE)client_socket,
        !          2718:                        GetCurrentProcess(),
        !          2719:                        (HANDLE*)&client_socket_dup,
        !          2720:                        0,
        !          2721:                        TRUE, // Inheritable
        !          2722:                        DUPLICATE_SAME_ACCESS)) {
        !          2723:                        errormsg(WHERE,ERR_CREATE,"duplicate socket handle",client_socket);
        !          2724:                        return(false);
        !          2725:                }
        !          2726: #else
        !          2727:                client_socket_dup = client_socket;
        !          2728: #endif
        !          2729: 
        !          2730:                addr_len=sizeof(addr);
        !          2731:                if((result=getsockname(client_socket, (struct sockaddr *)&addr,&addr_len))!=0) {
        !          2732:                        lprintf(LOG_ERR,"Node %d !ERROR %d (%d) getting address/port"
        !          2733:                                ,cfg.node_num, result, ERROR_VALUE);
        !          2734:                        return(false);
        !          2735:                } 
        !          2736:                lprintf(LOG_INFO,"Node %d attached to local interface %s port %d"
        !          2737:                        ,cfg.node_num, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
        !          2738: 
        !          2739:                local_addr=addr.sin_addr.s_addr;
        !          2740:        }
        !          2741: 
        !          2742:        if((comspec=os_cmdshell())==NULL) {
        !          2743:                errormsg(WHERE, ERR_CHK, OS_CMD_SHELL_ENV_VAR" environment variable", 0);
        !          2744:                return(false);
        !          2745:        }
        !          2746: 
        !          2747:        md(cfg.temp_dir);
        !          2748: 
        !          2749:        /* Shared NODE files */
        !          2750:        SAFEPRINTF2(str,"%s%s",cfg.ctrl_dir,"node.dab");
        !          2751:        if((nodefile=nopen(str,O_DENYNONE|O_RDWR|O_CREAT))==-1) {
        !          2752:                errormsg(WHERE, ERR_OPEN, str, cfg.node_num);
        !          2753:                return(false); 
        !          2754:        }
        !          2755:        memset(&node,0,sizeof(node_t));  /* write NULL to node struct */
        !          2756:        node.status=NODE_OFFLINE;
        !          2757:        while(filelength(nodefile)<(long)(cfg.sys_nodes*sizeof(node_t))) {
        !          2758:                lseek(nodefile,0L,SEEK_END);
        !          2759:                if(write(nodefile,&node,sizeof(node_t))!=sizeof(node_t)) {
        !          2760:                        errormsg(WHERE,ERR_WRITE,str,sizeof(node_t));
        !          2761:                        break; 
        !          2762:                }
        !          2763:        }
        !          2764:        for(i=0; cfg.node_num>0 && i<LOOP_NODEDAB; i++) {
        !          2765:                if(lock(nodefile,(cfg.node_num-1)*sizeof(node_t),sizeof(node_t))==0) {
        !          2766:                        unlock(nodefile,(cfg.node_num-1)*sizeof(node_t),sizeof(node_t));
        !          2767:                        break;
        !          2768:                }
        !          2769:                mswait(100);
        !          2770:        }
        !          2771:        if(cfg.node_misc&NM_CLOSENODEDAB) {
        !          2772:                close(nodefile);
        !          2773:                nodefile=-1;
        !          2774:        }
        !          2775: 
        !          2776:        if(i>=LOOP_NODEDAB) {
        !          2777:                errormsg(WHERE, ERR_LOCK, str, cfg.node_num);
        !          2778:                return(false); 
        !          2779:        }
        !          2780: 
        !          2781:        if(cfg.node_num) {
        !          2782:                SAFEPRINTF(str,"%snode.log",cfg.node_dir);
        !          2783:                if((logfile_fp=fopen(str,"a+b"))==NULL) {
        !          2784:                        errormsg(WHERE, ERR_OPEN, str, 0);
        !          2785:                        lprintf(LOG_ERR,"Perhaps this node is already running");
        !          2786:                        return(false); 
        !          2787:                }
        !          2788: 
        !          2789:                if(filelength(fileno(logfile_fp))) {
        !          2790:                        log(crlf);
        !          2791:                        now=time(NULL);
        !          2792:                        struct tm tm;
        !          2793:                        localtime_r(&now,&tm);
        !          2794:                        sprintf(str,"%s  %s %s %02d %u  "
        !          2795:                                "End of preexisting log entry (possible crash)"
        !          2796:                                ,hhmmtostr(&cfg,&tm,tmp)
        !          2797:                                ,wday[tm.tm_wday]
        !          2798:                                ,mon[tm.tm_mon],tm.tm_mday,tm.tm_year+1900);
        !          2799:                        logline("L!",str);
        !          2800:                        log(crlf);
        !          2801:                        catsyslog(1)        !          2802:                }
        !          2803: 
        !          2804:                getnodedat(cfg.node_num,&thisnode,1);
        !          2805:                /* thisnode.status=0; */
        !          2806:                thisnode.action=0;
        !          2807:                thisnode.useron=0;
        !          2808:                thisnode.aux=0;
        !          2809:                thisnode.misc&=(NODE_EVENT|NODE_LOCK|NODE_RRUN);
        !          2810:                criterrs=thisnode.errors;
        !          2811:                putnodedat(cfg.node_num,&thisnode);
        !          2812:        }
        !          2813: 
        !          2814: /** Put in if(cfg.node_num) ? (not needed for server and event threads) */
        !          2815:        backout();
        !          2816: 
        !          2817:        /* Reset COMMAND SHELL */
        !          2818: 
        !          2819:        main_csi.str=(char *)malloc(1024);
        !          2820:        if(main_csi.str==NULL) {
        !          2821:                errormsg(WHERE,ERR_ALLOC,"main_csi.str",1024);
        !          2822:                return(false); 
        !          2823:        }
        !          2824:        memset(main_csi.str,0,1024);
        !          2825: /***/
        !          2826: 
        !          2827:        if(cfg.total_grps) {
        !          2828: 
        !          2829:                usrgrp_total = cfg.total_grps;
        !          2830: 
        !          2831:                if((cursub=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
        !          2832:                        errormsg(WHERE, ERR_ALLOC, "cursub", sizeof(uint)*usrgrp_total);
        !          2833:                        return(false);
        !          2834:                }
        !          2835: 
        !          2836:                if((usrgrp=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
        !          2837:                        errormsg(WHERE, ERR_ALLOC, "usrgrp", sizeof(uint)*usrgrp_total);
        !          2838:                        return(false);
        !          2839:                }
        !          2840: 
        !          2841:                if((usrsubs=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
        !          2842:                        errormsg(WHERE, ERR_ALLOC, "usrsubs", sizeof(uint)*usrgrp_total);
        !          2843:                        return(false);
        !          2844:                }
        !          2845: 
        !          2846:                if((usrsub=(uint **)calloc(usrgrp_total,sizeof(uint *)))==NULL) {
        !          2847:                        errormsg(WHERE, ERR_ALLOC, "usrsub", sizeof(uint)*usrgrp_total);
        !          2848:                        return(false);
        !          2849:                }
        !          2850:  
        !          2851:                if((subscan=(subscan_t *)malloc(sizeof(subscan_t)*cfg.total_subs))==NULL) {
        !          2852:                        errormsg(WHERE, ERR_ALLOC, "subscan", sizeof(subscan_t)*cfg.total_subs);
        !          2853:                        return(false);
        !          2854:                }
        !          2855:        }
        !          2856: 
        !          2857:        for(i=l=0;i<(uint)cfg.total_grps;i++) {
        !          2858:                for(j=k=0;j<cfg.total_subs;j++)
        !          2859:                        if(cfg.sub[j]->grp==i)
        !          2860:                                k++;    /* k = number of subs per grp[i] */
        !          2861:                if(k>l) l=k;    /* l = the largest number of subs per grp */
        !          2862:        }
        !          2863:        if(l)
        !          2864:                for(i=0;i<cfg.total_grps;i++)
        !          2865:                        if((usrsub[i]=(uint *)malloc(sizeof(uint)*l))==NULL) {
        !          2866:                                errormsg(WHERE, ERR_ALLOC, "usrsub[x]", sizeof(uint)*l);
        !          2867:                                return(false);
        !          2868:                        }
        !          2869: 
        !          2870:        if(cfg.total_libs) {
        !          2871: 
        !          2872:                usrlib_total = cfg.total_libs;
        !          2873: 
        !          2874:                if((curdir=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
        !          2875:                        errormsg(WHERE, ERR_ALLOC, "curdir", sizeof(uint)*usrlib_total);
        !          2876:                        return(false);
        !          2877:                }
        !          2878: 
        !          2879:                if((usrlib=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
        !          2880:                        errormsg(WHERE, ERR_ALLOC, "usrlib", sizeof(uint)*usrlib_total);
        !          2881:                        return(false);
        !          2882:                }
        !          2883: 
        !          2884:                if((usrdirs=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
        !          2885:                        errormsg(WHERE, ERR_ALLOC, "usrdirs", sizeof(uint)*usrlib_total);
        !          2886:                        return(false);
        !          2887:                }
        !          2888: 
        !          2889:                if((usrdir=(uint **)calloc(usrlib_total,sizeof(uint *)))==NULL) {
        !          2890:                        errormsg(WHERE, ERR_ALLOC, "usrdir", sizeof(uint)*usrlib_total);
        !          2891:                        return(false);
        !          2892:                }
        !          2893:        }
        !          2894: 
        !          2895:        for(i=l=0;i<cfg.total_libs;i++) {
        !          2896:                for(j=k=0;j<cfg.total_dirs;j++)
        !          2897:                        if(cfg.dir[j]->lib==i)
        !          2898:                                k++;
        !          2899:                if(k>l) l=k;    /* l = largest number of dirs in a lib */
        !          2900:        }
        !          2901:        if(l) {
        !          2902:                l++;    /* for temp dir */
        !          2903:                for(i=0;i<cfg.total_libs;i++)
        !          2904:                        if((usrdir[i]=(uint *)malloc(sizeof(uint)*l))==NULL) {
        !          2905:                                errormsg(WHERE, ERR_ALLOC, "usrdir[x]", sizeof(uint)*l);
        !          2906:                                return(false);
        !          2907:                        }
        !          2908:        }
        !          2909:  
        !          2910:        if(cfg.max_batup) {
        !          2911: 
        !          2912:                if((batup_desc=(char **)malloc(sizeof(char *)*cfg.max_batup))==NULL) {
        !          2913:                        errormsg(WHERE, ERR_ALLOC, "batup_desc", sizeof(char *)*cfg.max_batup);
        !          2914:                        return(false);
        !          2915:                }
        !          2916:                if((batup_name=(char **)malloc(sizeof(char *)*cfg.max_batup))==NULL) {
        !          2917:                        errormsg(WHERE, ERR_ALLOC, "batup_name", sizeof(char *)*cfg.max_batup);
        !          2918:                        return(false);
        !          2919:                }
        !          2920:                if((batup_misc=(long *)malloc(sizeof(long)*cfg.max_batup))==NULL) {
        !          2921:                        errormsg(WHERE, ERR_ALLOC, "batup_misc", sizeof(char *)*cfg.max_batup);
        !          2922:                        return(false);
        !          2923:                }
        !          2924:                if((batup_dir=(uint *)malloc(sizeof(uint)*cfg.max_batup))==NULL) {
        !          2925:                        errormsg(WHERE, ERR_ALLOC, "batup_dir", sizeof(char *)*cfg.max_batup);
        !          2926:                        return(false);
        !          2927:                }
        !          2928:                if((batup_alt=(ushort *)malloc(sizeof(ushort)*cfg.max_batup))==NULL) {
        !          2929:                        errormsg(WHERE, ERR_ALLOC, "batup_alt", sizeof(char *)*cfg.max_batup);
        !          2930:                        return(false);
        !          2931:                }
        !          2932:                for(i=0;i<cfg.max_batup;i++) {
        !          2933:                        if((batup_desc[i]=(char *)malloc(59))==NULL) {
        !          2934:                                errormsg(WHERE, ERR_ALLOC, "batup_desc[x]", 59);
        !          2935:                                return(false);
        !          2936:                        }
        !          2937:                        if((batup_name[i]=(char *)malloc(13))==NULL) {
        !          2938:                                errormsg(WHERE, ERR_ALLOC, "batup_name[x]", 13);
        !          2939:                                return(false);
        !          2940:                        } 
        !          2941:                } 
        !          2942:        }
        !          2943: 
        !          2944:        if(cfg.max_batdn) {
        !          2945: 
        !          2946:                if((batdn_name=(char **)malloc(sizeof(char *)*cfg.max_batdn))==NULL) {
        !          2947:                        errormsg(WHERE, ERR_ALLOC, "batdn_name", sizeof(char *)*cfg.max_batdn);
        !          2948:                        return(false);
        !          2949:                }
        !          2950:                if((batdn_dir=(uint *)malloc(sizeof(uint)*cfg.max_batdn))==NULL)  {
        !          2951:                        errormsg(WHERE, ERR_ALLOC, "batdn_dir", sizeof(uint)*cfg.max_batdn);
        !          2952:                        return(false);
        !          2953:                }
        !          2954:                if((batdn_offset=(long *)malloc(sizeof(long)*cfg.max_batdn))==NULL)  {
        !          2955:                        errormsg(WHERE, ERR_ALLOC, "batdn_offset", sizeof(long)*cfg.max_batdn);
        !          2956:                        return(false);
        !          2957:                }
        !          2958:                if((batdn_size=(ulong *)malloc(sizeof(ulong)*cfg.max_batdn))==NULL) {
        !          2959:                        errormsg(WHERE, ERR_ALLOC, "batdn_size", sizeof(ulong)*cfg.max_batdn);
        !          2960:                        return(false);
        !          2961:                }
        !          2962:                if((batdn_cdt=(ulong *)malloc(sizeof(ulong)*cfg.max_batdn))==NULL) {
        !          2963:                        errormsg(WHERE, ERR_ALLOC, "batdn_cdt", sizeof(long)*cfg.max_batdn);
        !          2964:                        return(false);
        !          2965:                }
        !          2966:                if((batdn_alt=(ushort *)malloc(sizeof(ushort)*cfg.max_batdn))==NULL) {
        !          2967:                        errormsg(WHERE, ERR_ALLOC, "batdn_alt", sizeof(ushort)*cfg.max_batdn);
        !          2968:                        return(false);
        !          2969:                }
        !          2970:                for(i=0;i<cfg.max_batdn;i++)
        !          2971:                        if((batdn_name[i]=(char *)malloc(13))==NULL) {
        !          2972:                                errormsg(WHERE, ERR_ALLOC, "batdn_name[x]", 13);
        !          2973:                                return(false);
        !          2974:                        } 
        !          2975:        }
        !          2976: 
        !          2977:        reset_logon_vars();
        !          2978: 
        !          2979:        online=ON_REMOTE;
        !          2980: 
        !          2981:        return(true);
        !          2982: }
        !          2983: 
        !          2984: //****************************************************************************
        !          2985: sbbs_t::~sbbs_t()
        !          2986: {
        !          2987:        uint i;
        !          2988:        char node[32];
        !          2989: 
        !          2990:     if(cfg.node_num)
        !          2991:        SAFEPRINTF(node,"Node %d", cfg.node_num);
        !          2992:     else
        !          2993:        SAFECOPY(node,client_name);
        !          2994: #ifdef _DEBUG
        !          2995:        lprintf(LOG_DEBUG,"%s destructor begin", node);
        !          2996: #endif
        !          2997: 
        !          2998: //     if(!cfg.node_num)
        !          2999: //             rmdir(cfg.temp_dir);
        !          3000: 
        !          3001:        if(client_socket_dup!=INVALID_SOCKET && client_socket_dup!=client_socket)
        !          3002:                closesocket(client_socket_dup); /* close duplicate handle */
        !          3003: 
        !          3004:        if(cfg.node_num>0)
        !          3005:                node_inbuf[cfg.node_num-1]=NULL;
        !          3006:        if(cfg.node_num>0 && !input_thread_running)
        !          3007:                RingBufDispose(&inbuf);
        !          3008:        if(!output_thread_running)
        !          3009:                RingBufDispose(&outbuf);
        !          3010: 
        !          3011:        /* Close all open files */
        !          3012:        if(nodefile!=-1) {
        !          3013:                close(nodefile);
        !          3014:                nodefile=-1;
        !          3015:        }
        !          3016:        if(node_ext!=-1) {
        !          3017:                close(node_ext);
        !          3018:                node_ext=-1;
        !          3019:        }
        !          3020:        if(logfile_fp!=NULL) {
        !          3021:                fclose(logfile_fp);
        !          3022:                logfile_fp=NULL;
        !          3023:        }
        !          3024: 
        !          3025:        /********************************/
        !          3026:        /* Free allocated class members */
        !          3027:        /********************************/
        !          3028: 
        !          3029: #ifdef JAVASCRIPT
        !          3030:        /* Free Context */
        !          3031:        if(js_cx!=NULL) {       
        !          3032:                lprintf(LOG_DEBUG,"%s JavaScript: Destroying context",node);
        !          3033:                JS_DestroyContext(js_cx);
        !          3034:                js_cx=NULL;
        !          3035:        }
        !          3036: 
        !          3037:        if(js_runtime!=NULL) {
        !          3038:                lprintf(LOG_DEBUG,"%s JavaScript: Destroying runtime",node);
        !          3039:                JS_DestroyRuntime(js_runtime);
        !          3040:                js_runtime=NULL;
        !          3041:        }
        !          3042: #endif
        !          3043: 
        !          3044:        /* Reset text.dat */
        !          3045: 
        !          3046:        for(i=0;i<TOTAL_TEXT && text!=NULL;i++)
        !          3047:                if(text[i]!=text_sav[i]) {
        !          3048:                        if(text[i]!=nulstr)
        !          3049:                                free(text[i]); 
        !          3050:                }
        !          3051: 
        !          3052:        /* Global command shell vars */
        !          3053: 
        !          3054:        freevars(&main_csi);
        !          3055:        clearvars(&main_csi);
        !          3056:        FREE_AND_NULL(main_csi.str);    /* crash */
        !          3057:        FREE_AND_NULL(main_csi.cs);
        !          3058: 
        !          3059:        for(i=0;i<global_str_vars && global_str_var!=NULL;i++)
        !          3060:                FREE_AND_NULL(global_str_var[i]);
        !          3061: 
        !          3062:        FREE_AND_NULL(global_str_var);
        !          3063:        FREE_AND_NULL(global_str_var_name);
        !          3064:        global_str_vars=0;
        !          3065: 
        !          3066:        FREE_AND_NULL(global_int_var);
        !          3067:        FREE_AND_NULL(global_int_var_name);
        !          3068:        global_int_vars=0;
        !          3069: 
        !          3070:        /* Sub-board variables */
        !          3071:        for(i=0;i<usrgrp_total && usrsub!=NULL;i++)
        !          3072:                FREE_AND_NULL(usrsub[i]);       /* exception here (ptr=0xfdfdfdfd) on exit July-10-2002 */
        !          3073: 
        !          3074:        FREE_AND_NULL(cursub);
        !          3075:        FREE_AND_NULL(usrgrp);
        !          3076:        FREE_AND_NULL(usrsubs);
        !          3077:        FREE_AND_NULL(usrsub);
        !          3078:        FREE_AND_NULL(subscan);
        !          3079: 
        !          3080:        /* File Directory variables */
        !          3081:        for(i=0;i<usrlib_total && usrdir!=NULL;i++)
        !          3082:                FREE_AND_NULL(usrdir[i]);
        !          3083: 
        !          3084:        FREE_AND_NULL(curdir);
        !          3085:        FREE_AND_NULL(usrlib);
        !          3086:        FREE_AND_NULL(usrdirs);
        !          3087:        FREE_AND_NULL(usrdir);
        !          3088: 
        !          3089:        /* Batch upload vars */
        !          3090:        for(i=0;i<cfg.max_batup && batup_desc!=NULL && batup_name!=NULL;i++) {
        !          3091:                FREE_AND_NULL(batup_desc[i]);
        !          3092:                FREE_AND_NULL(batup_name[i]);
        !          3093:        }
        !          3094: 
        !          3095:        FREE_AND_NULL(batup_desc);
        !          3096:        FREE_AND_NULL(batup_name);
        !          3097:        FREE_AND_NULL(batup_misc);
        !          3098:        FREE_AND_NULL(batup_dir);
        !          3099:        FREE_AND_NULL(batup_alt);
        !          3100: 
        !          3101:        /* Batch download vars */
        !          3102:        for(i=0;i<cfg.max_batdn && batdn_name!=NULL;i++)
        !          3103:                FREE_AND_NULL(batdn_name[i]); 
        !          3104: 
        !          3105:        FREE_AND_NULL(batdn_name);
        !          3106:        FREE_AND_NULL(batdn_dir);
        !          3107:        FREE_AND_NULL(batdn_offset);
        !          3108:        FREE_AND_NULL(batdn_size);
        !          3109:        FREE_AND_NULL(batdn_cdt);
        !          3110:        FREE_AND_NULL(batdn_alt);
        !          3111: 
        !          3112: #if 0 && defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
        !          3113:        if(!_CrtCheckMemory())
        !          3114:                lprintf(LOG_ERR,"!MEMORY ERRORS REPORTED IN DATA/DEBUG.LOG!");
        !          3115: #endif
        !          3116: 
        !          3117: #ifdef _DEBUG
        !          3118:        lprintf(LOG_DEBUG,"%s destructor end", node);
        !          3119: #endif
        !          3120: }
        !          3121: 
        !          3122: /****************************************************************************/
        !          3123: /* Network open function. Opens all files DENYALL and retries LOOP_NOPEN    */
        !          3124: /* number of times if the attempted file is already open or denying access  */
        !          3125: /* for some other reason.                                                                                                      */
        !          3126: /* All files are opened in BINARY mode, unless O_TEXT access bit is set.       */
        !          3127: /****************************************************************************/
        !          3128: int sbbs_t::nopen(char *str, int access)
        !          3129: {
        !          3130:        char logstr[256];
        !          3131:        int file,share,count=0;
        !          3132: 
        !          3133:     if(access&O_DENYNONE) {
        !          3134:         share=SH_DENYNO;
        !          3135:         access&=~O_DENYNONE; 
        !          3136:        }
        !          3137:     else if(access==O_RDONLY) share=SH_DENYWR;
        !          3138:     else share=SH_DENYRW;
        !          3139:        if(!(access&O_TEXT))
        !          3140:                access|=O_BINARY;
        !          3141:     while(((file=sopen(str,access,share,S_IREAD|S_IWRITE))==-1)
        !          3142:         && (errno==EACCES || errno==EAGAIN) && count++<LOOP_NOPEN)
        !          3143:            mswait(100);
        !          3144:     if(count>(LOOP_NOPEN/2) && count<=LOOP_NOPEN) {
        !          3145:         SAFEPRINTF2(logstr,"NOPEN COLLISION - File: \"%s\" Count: %d"
        !          3146:             ,str,count);
        !          3147:         logline("!!",logstr); 
        !          3148:        }
        !          3149:     if(file==-1 && (errno==EACCES || errno==EAGAIN)) {
        !          3150:         SAFEPRINTF2(logstr,"NOPEN ACCESS DENIED - File: \"%s\" errno: %d"
        !          3151:                        ,str,errno);
        !          3152:                logline("!!",logstr);
        !          3153:                bputs("\7\r\nNOPEN: ACCESS DENIED\r\n\7");
        !          3154:        }
        !          3155:     return(file);
        !          3156: }
        !          3157: 
        !          3158: void sbbs_t::spymsg(char* msg)
        !          3159: {
        !          3160:        char str[512];
        !          3161:        struct in_addr addr;
        !          3162: 
        !          3163:        if(cfg.node_num<1)
        !          3164:                return;
        !          3165: 
        !          3166:        addr.s_addr=client_addr;
        !          3167:        SAFEPRINTF4(str,"\r\n\r\n*** Spy Message ***\r\nNode %d: %s [%s]\r\n*** %s ***\r\n\r\n"
        !          3168:                ,cfg.node_num,client_name,inet_ntoa(addr),msg);
        !          3169:        if(startup->node_spybuf!=NULL 
        !          3170:                && startup->node_spybuf[cfg.node_num-1]!=NULL) {
        !          3171:                RingBufWrite(startup->node_spybuf[cfg.node_num-1],(uchar*)str,strlen(str));
        !          3172:                /* Signal spy output semaphore? */
        !          3173:                if(startup->node_spysem!=NULL 
        !          3174:                        && startup->node_spysem[sbbs->cfg.node_num-1]!=NULL)
        !          3175:                        sem_post(startup->node_spysem[sbbs->cfg.node_num-1]);
        !          3176:        }
        !          3177: 
        !          3178:        if(cfg.node_num && spy_socket[cfg.node_num-1]!=INVALID_SOCKET) 
        !          3179:                sendsocket(spy_socket[cfg.node_num-1],str,strlen(str));
        !          3180: #ifdef __unix__
        !          3181:        if(cfg.node_num && uspy_socket[cfg.node_num-1]!=INVALID_SOCKET) 
        !          3182:                sendsocket(uspy_socket[cfg.node_num-1],str,strlen(str));
        !          3183: #endif
        !          3184: }
        !          3185: 
        !          3186: #define MV_BUFLEN      4096
        !          3187: 
        !          3188: /****************************************************************************/
        !          3189: /* Moves or copies a file from one dir to another                           */
        !          3190: /* both 'src' and 'dest' must contain full path and filename                */
        !          3191: /* returns 0 if successful, -1 if error                                     */
        !          3192: /****************************************************************************/
        !          3193: int sbbs_t::mv(char *src, char *dest, char copy)
        !          3194: {
        !          3195:        char    str[MAX_PATH+1],*buf,atr=curatr;
        !          3196:        int             ind,outd;
        !          3197:        uint    chunk=MV_BUFLEN;
        !          3198:        ulong   length,l;
        !          3199:        time_t  ftime;
        !          3200:        FILE *inp,*outp;
        !          3201: 
        !          3202:     if(!stricmp(src,dest))      /* source and destination are the same! */
        !          3203:         return(0);
        !          3204:     if(!fexistcase(src)) {
        !          3205:         bprintf("\r\n\7MV ERROR: Source doesn't exist\r\n'%s'\r\n"
        !          3206:             ,src);
        !          3207:         return(-1); 
        !          3208:        }
        !          3209:     if(!copy && fexistcase(dest)) {
        !          3210:         bprintf("\r\n\7MV ERROR: Destination already exists\r\n'%s'\r\n"
        !          3211:             ,dest);
        !          3212:         return(-1); 
        !          3213:        }
        !          3214: #ifndef __unix__       /* need to determine if on same mount device */
        !          3215:     if(!copy && ((src[1]!=':' && dest[1]!=':')
        !          3216:         || (src[1]==':' && dest[1]==':' && toupper(src[0])==toupper(dest[0])))) {
        !          3217:         if(rename(src,dest)) {                                         /* same drive, so move */
        !          3218:             bprintf("\r\nMV ERROR: Error renaming '%s'"
        !          3219:                     "\r\n                      to '%s'\r\n\7",src,dest);
        !          3220:             return(-1); 
        !          3221:                }
        !          3222:         return(0)        !          3223:        }
        !          3224: #endif
        !          3225:     attr(WHITE);
        !          3226:     if((ind=nopen(src,O_RDONLY))==-1) {
        !          3227:         errormsg(WHERE,ERR_OPEN,src,O_RDONLY);
        !          3228:         return(-1); 
        !          3229:        }
        !          3230:     if((inp=fdopen(ind,"rb"))==NULL) {
        !          3231:         close(ind);
        !          3232:         errormsg(WHERE,ERR_FDOPEN,str,O_RDONLY);
        !          3233:         return(-1); 
        !          3234:        }
        !          3235:     setvbuf(inp,NULL,_IOFBF,32*1024);
        !          3236:     if((outd=nopen(dest,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
        !          3237:         fclose(inp);
        !          3238:         errormsg(WHERE,ERR_OPEN,dest,O_WRONLY|O_CREAT|O_TRUNC);
        !          3239:         return(-1); 
        !          3240:        }
        !          3241:     if((outp=fdopen(outd,"wb"))==NULL) {
        !          3242:         close(outd);
        !          3243:         fclose(inp);
        !          3244:         errormsg(WHERE,ERR_FDOPEN,dest,O_WRONLY|O_CREAT|O_TRUNC);
        !          3245:         return(-1); 
        !          3246:        }
        !          3247:     setvbuf(outp,NULL,_IOFBF,8*1024);
        !          3248:        ftime=filetime(ind);
        !          3249:     length=filelength(ind);
        !          3250:     if(length) {       /* Something to copy */
        !          3251:                if((buf=(char *)malloc(MV_BUFLEN))==NULL) {
        !          3252:                        fclose(inp);
        !          3253:                        fclose(outp);
        !          3254:                        errormsg(WHERE,ERR_ALLOC,nulstr,MV_BUFLEN);
        !          3255:                        return(-1); 
        !          3256:                }
        !          3257:                l=0L;
        !          3258:                while(l<length) {
        !          3259:                        bprintf("%2lu%%",l ? (long)(100.0/((float)length/l)) : 0L);
        !          3260:                        if(l+chunk>length)
        !          3261:                                chunk=length-l;
        !          3262:                        if(fread(buf,1,chunk,inp)!=chunk) {
        !          3263:                                free(buf);
        !          3264:                                fclose(inp);
        !          3265:                                fclose(outp);
        !          3266:                                errormsg(WHERE,ERR_READ,src,chunk);
        !          3267:                                return(-1); 
        !          3268:                        }
        !          3269:                        if(fwrite(buf,1,chunk,outp)!=chunk) {
        !          3270:                                free(buf);
        !          3271:                                fclose(inp);
        !          3272:                                fclose(outp);
        !          3273:                                errormsg(WHERE,ERR_WRITE,dest,chunk);
        !          3274:                                return(-1); 
        !          3275:                        }
        !          3276:                        l+=chunk;
        !          3277:                        bputs("\b\b\b"); 
        !          3278:                }
        !          3279:                bputs("   \b\b\b");  /* erase it */
        !          3280:                attr(atr);
        !          3281:                free(buf);
        !          3282:        }
        !          3283:     fclose(inp);
        !          3284:     fclose(outp);
        !          3285:        setfdate(dest,ftime);   /* Would be nice if we could use futime() instead */
        !          3286:     if(!copy && remove(src)) {
        !          3287:         errormsg(WHERE,ERR_REMOVE,src,0);
        !          3288:         return(-1); 
        !          3289:        }
        !          3290:     return(0);
        !          3291: }
        !          3292: 
        !          3293: void sbbs_t::hangup(void)
        !          3294: {
        !          3295:        if(client_socket_dup!=INVALID_SOCKET && client_socket_dup!=client_socket)
        !          3296:                closesocket(client_socket_dup);
        !          3297:        client_socket_dup=INVALID_SOCKET;
        !          3298: 
        !          3299:        if(client_socket!=INVALID_SOCKET) {
        !          3300:                mswait(1000);   /* Give socket output buffer time to flush */
        !          3301:                client_off(client_socket);
        !          3302:                close_socket(client_socket);
        !          3303:                client_socket=INVALID_SOCKET;
        !          3304:        }
        !          3305:        sem_post(&outbuf.sem);
        !          3306:        online=FALSE;
        !          3307: }
        !          3308: 
        !          3309: int sbbs_t::incom(unsigned long timeout)
        !          3310: {
        !          3311:        uchar   ch;
        !          3312: 
        !          3313: #if 0  /* looping version */
        !          3314:        while(!RingBufRead(&inbuf, &ch, 1))
        !          3315:                if(sem_trywait_block(&inbuf.sem,timeout)!=0 || sys_status&SS_ABORT)
        !          3316:                        return(NOINP);
        !          3317: #else
        !          3318:        if(!RingBufRead(&inbuf, &ch, 1)) {
        !          3319:                if(sem_trywait_block(&inbuf.sem,timeout)!=0)
        !          3320:                        return(NOINP);
        !          3321:                if(!RingBufRead(&inbuf, &ch, 1))
        !          3322:                        return(NOINP);
        !          3323:        }
        !          3324: #endif
        !          3325:        return(ch);
        !          3326: }
        !          3327: 
        !          3328: int sbbs_t::outcom(uchar ch)
        !          3329: {
        !          3330:        if(!RingBufFree(&outbuf))
        !          3331:                return(TXBOF);
        !          3332:     if(!RingBufWrite(&outbuf, &ch, 1))
        !          3333:                return(TXBOF);
        !          3334:        return(0);
        !          3335: }
        !          3336: 
        !          3337: void sbbs_t::putcom(char *str, int len)
        !          3338: {
        !          3339:        int i;
        !          3340: 
        !          3341:     if(!len)
        !          3342:        len=strlen(str);
        !          3343:     for(i=0;i<len && online; i++)
        !          3344:         outcom(str[i]);
        !          3345: }
        !          3346: 
        !          3347: /* Legacy Remote I/O Control Interface */
        !          3348: int sbbs_t::rioctl(ushort action)
        !          3349: {
        !          3350:        int             mode;
        !          3351:        int             state;
        !          3352: 
        !          3353:        switch(action) {
        !          3354:                case GVERS:     /* Get version */
        !          3355:                        return(0x200);
        !          3356:                case GUART:     /* Get UART I/O address, not available */
        !          3357:                        return(0xffff);
        !          3358:                case GIRQN:     /* Get IRQ number, not available */
        !          3359:                        return((int)client_socket);
        !          3360:                case GBAUD:     /* Get current bit rate */
        !          3361:                        return(0xffff);
        !          3362:                case RXBC:              /* Get receive buffer count */
        !          3363:                        // ulong        cnt;
        !          3364:                        // ioctlsocket (client_socket,FIONREAD,&cnt);
        !          3365:                        return(/* cnt+ */RingBufFull(&inbuf));
        !          3366:                case RXBS:              /* Get receive buffer size */
        !          3367:                        return(inbuf.size);
        !          3368:                case TXBC:              /* Get transmit buffer count */
        !          3369:                        return(RingBufFull(&outbuf));
        !          3370:                case TXBS:              /* Get transmit buffer size */
        !          3371:                        return(outbuf.size);
        !          3372:                case TXBF:              /* Get transmit buffer free space */
        !          3373:                        return(RingBufFree(&outbuf));
        !          3374:                case IOMODE:
        !          3375:                        mode=0;
        !          3376:                        if(rio_abortable)
        !          3377:                                mode|=ABORT;
        !          3378:                        return(mode);
        !          3379:                case IOSTATE:
        !          3380:                        state=0;
        !          3381:                        if(sys_status&SS_ABORT)
        !          3382:                                state|=ABORT;
        !          3383:                        return(state);
        !          3384:                case IOFI:              /* Flush input buffer */
        !          3385:                        RingBufReInit(&inbuf);
        !          3386:                        break;
        !          3387:                case IOFO:              /* Flush output buffer */
        !          3388:                RingBufReInit(&outbuf);
        !          3389:                        break;
        !          3390:                case IOFB:              /* Flush both buffers */
        !          3391:                        RingBufReInit(&inbuf);
        !          3392:                        RingBufReInit(&outbuf);
        !          3393:                        break;
        !          3394:                case LFN81:
        !          3395:                case LFE71:
        !          3396:                case FIFOCTL:
        !          3397:                        return(0);
        !          3398:                }
        !          3399: 
        !          3400:        if((action&0xff)==IOSM) {       /* Get/Set/Clear mode */
        !          3401:                if(action&ABORT)
        !          3402:                        rio_abortable=true;
        !          3403:                return(0)        !          3404:        }
        !          3405: 
        !          3406:        if((action&0xff)==IOCM) {       /* Get/Set/Clear mode */
        !          3407:                if(action&ABORT)
        !          3408:                        rio_abortable=false;
        !          3409:                return(0)        !          3410:        }
        !          3411: 
        !          3412:        if((action&0xff)==IOSS) {       /* Set state */
        !          3413:                if(action&ABORT)
        !          3414:                        sys_status|=SS_ABORT;
        !          3415:                return(0)        !          3416:        }
        !          3417: 
        !          3418:        if((action&0xff)==IOCS) {       /* Clear state */
        !          3419:                if(action&ABORT)
        !          3420:                        sys_status&=~SS_ABORT;
        !          3421:                return(0)        !          3422:        }
        !          3423: 
        !          3424:        return(0);
        !          3425: }
        !          3426: 
        !          3427: void sbbs_t::reset_logon_vars(void)
        !          3428: {
        !          3429:        int i;
        !          3430: 
        !          3431:     /* bools */
        !          3432:     qwklogon=false;
        !          3433: 
        !          3434:     sys_status&=~(SS_USERON|SS_TMPSYSOP|SS_LCHAT|SS_ABORT
        !          3435:         |SS_PAUSEON|SS_PAUSEOFF|SS_EVENT|SS_NEWUSER|SS_NEWDAY);
        !          3436:     cid[0]=0;
        !          3437:     wordwrap[0]=0;
        !          3438:     question[0]=0;
        !          3439:     menu_dir[0]=0;
        !          3440:     menu_file[0]=0;
        !          3441:     rows=24;
        !          3442:        cols=80;
        !          3443:     lncntr=0;
        !          3444:     autoterm=0;
        !          3445:     lbuflen=0;
        !          3446:     slcnt=0;
        !          3447:     altul=0;
        !          3448:     timeleft_warn=0;
        !          3449:        keybufbot=keybuftop=0;
        !          3450:     logon_uls=logon_ulb=logon_dls=logon_dlb=0;
        !          3451:     logon_posts=logon_emails=logon_fbacks=0;
        !          3452:     batdn_total=batup_total=0;
        !          3453:     usrgrps=usrlibs=0;
        !          3454:     curgrp=curlib=0;
        !          3455:     for(i=0;i<cfg.total_libs;i++)
        !          3456:         curdir[i]=0;
        !          3457:     for(i=0;i<cfg.total_grps;i++)
        !          3458:         cursub[i]=0;
        !          3459:        cur_cps=3000;
        !          3460:     cur_rate=30000;
        !          3461:     dte_rate=38400;
        !          3462:        main_cmds=xfer_cmds=posts_read=0;
        !          3463:        lastnodemsg=0;
        !          3464:        lastnodemsguser[0]=0;
        !          3465: }
        !          3466: 
        !          3467: /****************************************************************************/
        !          3468: /* Writes NODE.LOG at end of SYSTEM.LOG                                                                                */
        !          3469: /****************************************************************************/
        !          3470: void sbbs_t::catsyslog(int crash)
        !          3471: {
        !          3472:        char str[MAX_PATH+1];
        !          3473:        char *buf;
        !          3474:        int  i,file;
        !          3475:        long length;
        !          3476:        struct tm tm;
        !          3477: 
        !          3478:        if(logfile_fp==NULL) {
        !          3479:                SAFEPRINTF(str,"%snode.log",cfg.node_dir);
        !          3480:                if((logfile_fp=fopen(str,"rb"))==NULL) {
        !          3481:                        errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
        !          3482:                        return; 
        !          3483:                }
        !          3484:        }
        !          3485:        length=ftell(logfile_fp);
        !          3486:        if(length) {
        !          3487:                if((buf=(char *)malloc(length))==NULL) {
        !          3488:                        errormsg(WHERE,ERR_ALLOC,str,length);
        !          3489:                        return; 
        !          3490:                }
        !          3491:                rewind(logfile_fp);
        !          3492:                if(fread(buf,1,length,logfile_fp)!=(size_t)length) {
        !          3493:                        errormsg(WHERE,ERR_READ,"log file",length);
        !          3494:                        free((char *)buf);
        !          3495:                        return; 
        !          3496:                }
        !          3497:                now=time(NULL);
        !          3498:                localtime_r(&now,&tm);
        !          3499:                SAFEPRINTF4(str,"%slogs/%2.2d%2.2d%2.2d.log",cfg.logs_dir,tm.tm_mon+1,tm.tm_mday
        !          3500:                        ,TM_YEAR(tm.tm_year));
        !          3501:                if((file=nopen(str,O_WRONLY|O_APPEND|O_CREAT))==-1) {
        !          3502:                        errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_APPEND|O_CREAT);
        !          3503:                        free((char *)buf);
        !          3504:                        return; 
        !          3505:                }
        !          3506:                if(lwrite(file,buf,length)!=length) {
        !          3507:                        close(file);
        !          3508:                        errormsg(WHERE,ERR_WRITE,str,length);
        !          3509:                        free((char *)buf);
        !          3510:                        return; 
        !          3511:                }
        !          3512:                close(file);
        !          3513:                if(crash) {
        !          3514:                        for(i=0;i<2;i++) {
        !          3515:                                SAFEPRINTF(str,"%scrash.log",i ? cfg.logs_dir : cfg.node_dir);
        !          3516:                                if((file=nopen(str,O_WRONLY|O_APPEND|O_CREAT))==-1) {
        !          3517:                                        errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_APPEND|O_CREAT);
        !          3518:                                        free((char *)buf);
        !          3519:                                        return; 
        !          3520:                                }
        !          3521:                                if(lwrite(file,buf,length)!=length) {
        !          3522:                                        close(file);
        !          3523:                                        errormsg(WHERE,ERR_WRITE,str,length);
        !          3524:                                        free((char *)buf);
        !          3525:                                        return; 
        !          3526:                                }
        !          3527:                                close(file); 
        !          3528:                        } 
        !          3529:                }
        !          3530:                free((char *)buf); 
        !          3531:        }
        !          3532: 
        !          3533:        fclose(logfile_fp);
        !          3534: 
        !          3535:        SAFEPRINTF(str,"%snode.log",cfg.node_dir);
        !          3536:        if((logfile_fp=fopen(str,"w+b"))==NULL) /* Truncate NODE.LOG */
        !          3537:                errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_TRUNC);
        !          3538: }
        !          3539: 
        !          3540: 
        !          3541: void sbbs_t::logoffstats()
        !          3542: {
        !          3543:     char str[MAX_PATH+1];
        !          3544:     int i,file;
        !          3545:     stats_t stats;
        !          3546: 
        !          3547:        if(REALSYSOP && !(cfg.sys_misc&SM_SYSSTAT))
        !          3548:                return;
        !          3549:        
        !          3550:        for(i=0;i<2;i++) {
        !          3551:                SAFEPRINTF(str,"%sdsts.dab",i ? cfg.ctrl_dir : cfg.node_dir);
        !          3552:                if((file=nopen(str,O_RDWR))==-1) {
        !          3553:                        errormsg(WHERE,ERR_OPEN,str,O_RDWR);
        !          3554:                        return; 
        !          3555:                }
        !          3556:                memset(&stats,0,sizeof(stats));
        !          3557:                lseek(file,4L,SEEK_SET);   /* Skip timestamp, logons and logons today */
        !          3558:                read(file,&stats,sizeof(stats));  
        !          3559: 
        !          3560:                if(!(useron.rest&FLAG('Q'))) {  /* Don't count QWKnet nodes */
        !          3561:                        stats.timeon+=(now-logontime)/60;
        !          3562:                        stats.ttoday+=(now-logontime)/60;
        !          3563:                        stats.ptoday+=logon_posts;
        !          3564:                }
        !          3565:                stats.uls+=logon_uls;
        !          3566:                stats.ulb+=logon_ulb;
        !          3567:                stats.dls+=logon_dls;
        !          3568:                stats.dlb+=logon_dlb;
        !          3569:                stats.etoday+=logon_emails;
        !          3570:                stats.ftoday+=logon_fbacks;
        !          3571: 
        !          3572: #if 0 // This is now handled in newuserdat()
        !          3573:                if(sys_status&SS_NEWUSER)
        !          3574:                        stats.nusers++;
        !          3575: #endif
        !          3576: 
        !          3577:                lseek(file,4L,SEEK_SET);
        !          3578:                write(file,&stats,sizeof(stats));
        !          3579:                close(file); 
        !          3580:        }
        !          3581: }
        !          3582: 
        !          3583: void node_thread(void* arg)
        !          3584: {
        !          3585:        ulong                   stack_frame;
        !          3586:        char                    str[128];
        !          3587:        int                             file;
        !          3588:        uint                    curshell=0;
        !          3589:        node_t                  node;
        !          3590:        sbbs_t*                 sbbs = (sbbs_t*) arg;
        !          3591: 
        !          3592:        update_clients();
        !          3593:        thread_up(TRUE /* setuid */);
        !          3594: 
        !          3595: #ifdef _DEBUG
        !          3596:        lprintf(LOG_DEBUG,"Node %d thread started",sbbs->cfg.node_num);
        !          3597: #endif
        !          3598: 
        !          3599:        sbbs_srand();           /* Seed random number generator */
        !          3600: 
        !          3601: #ifdef JAVASCRIPT
        !          3602:        if(!(startup->options&BBS_OPT_NO_JAVASCRIPT)) {
        !          3603:                if(!sbbs->js_init(&stack_frame)) /* This must be done in the context of the node thread */
        !          3604:                        lprintf(LOG_ERR,"Node %d !JavaScript Initialization FAILURE",sbbs->cfg.node_num);
        !          3605:        }
        !          3606: #endif
        !          3607: 
        !          3608:        if(sbbs->answer()) {
        !          3609: 
        !          3610:                if(sbbs->qwklogon) {
        !          3611:                        sbbs->getsmsg(sbbs->useron.number);
        !          3612:                        sbbs->qwk_sec();
        !          3613:                } else while(sbbs->useron.number 
        !          3614:                        && (sbbs->main_csi.misc&CS_OFFLINE_EXEC || sbbs->online)) {
        !          3615: 
        !          3616:                        if(!sbbs->main_csi.cs || curshell!=sbbs->useron.shell) {
        !          3617:                                if(sbbs->useron.shell>=sbbs->cfg.total_shells)
        !          3618:                                        sbbs->useron.shell=0;
        !          3619:                                SAFEPRINTF2(str,"%s%s.bin",sbbs->cfg.mods_dir
        !          3620:                                        ,sbbs->cfg.shell[sbbs->useron.shell]->code);
        !          3621:                                if(sbbs->cfg.mods_dir[0]==0 || !fexistcase(str))
        !          3622:                                        SAFEPRINTF2(str,"%s%s.bin",sbbs->cfg.exec_dir
        !          3623:                                                ,sbbs->cfg.shell[sbbs->useron.shell]->code);
        !          3624:                                if((file=sbbs->nopen(str,O_RDONLY))==-1) {
        !          3625:                                        sbbs->errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
        !          3626:                                        sbbs->hangup();
        !          3627:                                        break; 
        !          3628:                                }
        !          3629:                                FREE_AND_NULL(sbbs->main_csi.cs);
        !          3630:                                sbbs->freevars(&sbbs->main_csi);
        !          3631:                                sbbs->clearvars(&sbbs->main_csi);
        !          3632: 
        !          3633:                                sbbs->main_csi.length=filelength(file);
        !          3634:                                if((sbbs->main_csi.cs=(uchar *)malloc(sbbs->main_csi.length))==NULL) {
        !          3635:                                        close(file);
        !          3636:                                        sbbs->errormsg(WHERE,ERR_ALLOC,str,sbbs->main_csi.length);
        !          3637:                                        sbbs->hangup();
        !          3638:                                        break; 
        !          3639:                                }
        !          3640: 
        !          3641:                                if(lread(file,sbbs->main_csi.cs,sbbs->main_csi.length)
        !          3642:                                        !=(int)sbbs->main_csi.length) {
        !          3643:                                        sbbs->errormsg(WHERE,ERR_READ,str,sbbs->main_csi.length);
        !          3644:                                        close(file);
        !          3645:                                        free(sbbs->main_csi.cs);
        !          3646:                                        sbbs->main_csi.cs=NULL;
        !          3647:                                        sbbs->hangup();
        !          3648:                                        break; 
        !          3649:                                }
        !          3650:                                close(file);
        !          3651: 
        !          3652:                                curshell=sbbs->useron.shell;
        !          3653:                                sbbs->main_csi.ip=sbbs->main_csi.cs;
        !          3654:                                sbbs->menu_dir[0]=0;
        !          3655:                                sbbs->menu_file[0]=0;
        !          3656:                                }
        !          3657:                        if(sbbs->exec(&sbbs->main_csi))
        !          3658:                                break;
        !          3659:                }
        !          3660:        }
        !          3661: 
        !          3662: #ifdef _WIN32
        !          3663:        if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
        !          3664:                PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
        !          3665: #endif
        !          3666: 
        !          3667:        sbbs->hangup(); /* closes sockets, calls client_off, and shuts down the output_thread */
        !          3668:     node_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
        !          3669: 
        !          3670:        sbbs->logout();
        !          3671:        sbbs->logoffstats();    /* Updates both system and node dsts.dab files */
        !          3672: 
        !          3673:        if(sbbs->sys_status&SS_DAILY) { // New day, run daily events/maintenance
        !          3674:                sbbs->daily_maint();
        !          3675:        }
        !          3676: 
        !          3677: #if 0  /* this is handled in the event_thread now */
        !          3678:        // Node Daily Event
        !          3679:        sbbs->getnodedat(sbbs->cfg.node_num,&node,0);
        !          3680:        if(node.misc&NODE_EVENT) {
        !          3681:                sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
        !          3682:                node.status=NODE_EVENT_RUNNING;
        !          3683:                sbbs->putnodedat(sbbs->cfg.node_num,&node);
        !          3684:                if(sbbs->cfg.node_daily[0]) {
        !          3685:                        sbbs->logentry("!:","Run node daily event");
        !          3686:                        sbbs->external(
        !          3687:                                 sbbs->cmdstr(sbbs->cfg.node_daily,nulstr,nulstr,NULL)
        !          3688:                                ,EX_OFFLINE);
        !          3689:                }
        !          3690:                sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
        !          3691:                node.misc&=~NODE_EVENT;
        !          3692:                sbbs->putnodedat(sbbs->cfg.node_num,&node); 
        !          3693:        }
        !          3694: #endif
        !          3695: 
        !          3696:     // Wait for all node threads to terminate
        !          3697:        if(sbbs->input_thread_running || sbbs->output_thread_running
        !          3698: #ifdef USE_CRYPTLIB
        !          3699:                || sbbs->passthru_input_thread_running || sbbs->passthru_output_thread_running
        !          3700: #endif
        !          3701:                ) {
        !          3702:                lprintf(LOG_INFO,"Node %d Waiting for %s to terminate..."
        !          3703:                        ,sbbs->cfg.node_num
        !          3704:                        ,(sbbs->input_thread_running && sbbs->output_thread_running) ?
        !          3705:                        "I/O threads" : sbbs->input_thread_running
        !          3706:                                ? "input thread" : "output thread");
        !          3707:                time_t start=time(NULL);
        !          3708:                while(sbbs->input_thread_running
        !          3709:                || sbbs->output_thread_running
        !          3710: #ifdef USE_CRYPTLIB
        !          3711:                        || sbbs->passthru_input_thread_running || sbbs->passthru_output_thread_running
        !          3712: #endif
        !          3713:                        ) {
        !          3714:                        if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
        !          3715:                                lprintf(LOG_NOTICE,"Node %d !TIMEOUT waiting for %s to terminate"
        !          3716:                                        , sbbs->cfg.node_num
        !          3717:                                        ,(sbbs->input_thread_running && sbbs->output_thread_running) ?
        !          3718:                                "I/O threads"
        !          3719:                                        : sbbs->input_thread_running
        !          3720:                                ? "input thread" : "output thread");
        !          3721:                                break;
        !          3722:                        }
        !          3723:                        mswait(100);
        !          3724:                }
        !          3725:        }
        !          3726: 
        !          3727:        sbbs->catsyslog(0);
        !          3728: 
        !          3729:        status(STATUS_WFC);
        !          3730: 
        !          3731:        sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
        !          3732:        if(node.misc&NODE_DOWN)
        !          3733:                node.status=NODE_OFFLINE;
        !          3734:        else
        !          3735:                node.status=NODE_WFC;
        !          3736:        node.misc&=~(NODE_DOWN|NODE_INTR|NODE_MSGW|NODE_NMSG
        !          3737:                                |NODE_UDAT|NODE_POFF|NODE_AOFF|NODE_EXT);
        !          3738: /*     node.useron=0; needed for hang-ups while in multinode chat */
        !          3739:        sbbs->putnodedat(sbbs->cfg.node_num,&node);
        !          3740: 
        !          3741:        if(node_threads_running>0)
        !          3742:                node_threads_running--;
        !          3743:        lprintf(LOG_DEBUG,"Node %d thread terminated (%u node threads remain, %lu clients served)"
        !          3744:                ,sbbs->cfg.node_num, node_threads_running, served);
        !          3745:     if(!sbbs->input_thread_running && !sbbs->output_thread_running)
        !          3746:                delete sbbs;
        !          3747:     else
        !          3748:                lprintf(LOG_WARNING,"Node %d !ORPHANED I/O THREAD(s)",sbbs->cfg.node_num);
        !          3749: 
        !          3750:        update_clients();
        !          3751:        thread_down();
        !          3752: }
        !          3753: 
        !          3754: void sbbs_t::daily_maint(void)
        !          3755: {
        !          3756:        char                    str[128];
        !          3757:        char                    uname[LEN_ALIAS+1];
        !          3758:        uint                    i;
        !          3759:        uint                    usernum;
        !          3760:        uint                    lastusernum;
        !          3761:        node_t                  node;
        !          3762:        user_t                  user;
        !          3763: 
        !          3764:        now=time(NULL);
        !          3765: 
        !          3766:        sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
        !          3767:        node.status=NODE_EVENT_RUNNING;
        !          3768:        sbbs->putnodedat(sbbs->cfg.node_num,&node);
        !          3769: 
        !          3770:        sbbs->logentry("!:","Ran system daily maintenance");
        !          3771: 
        !          3772:        if(sbbs->cfg.user_backup_level) {
        !          3773:                lputs(LOG_INFO,"Backing-up user data...");
        !          3774:                SAFEPRINTF(str,"%suser/user.dat",sbbs->cfg.data_dir);
        !          3775:                backup(str,sbbs->cfg.user_backup_level,FALSE);
        !          3776:                SAFEPRINTF(str,"%suser/name.dat",sbbs->cfg.data_dir);
        !          3777:                backup(str,sbbs->cfg.user_backup_level,FALSE);
        !          3778:        }
        !          3779: 
        !          3780:        if(sbbs->cfg.mail_backup_level) {
        !          3781:                lputs(LOG_INFO,"Backing-up mail data...");
        !          3782:                SAFEPRINTF(str,"%smail.shd",sbbs->cfg.data_dir);
        !          3783:                backup(str,sbbs->cfg.mail_backup_level,FALSE);
        !          3784:                SAFEPRINTF(str,"%smail.sha",sbbs->cfg.data_dir);
        !          3785:                backup(str,sbbs->cfg.mail_backup_level,FALSE);
        !          3786:                SAFEPRINTF(str,"%smail.sdt",sbbs->cfg.data_dir);
        !          3787:                backup(str,sbbs->cfg.mail_backup_level,FALSE);
        !          3788:                SAFEPRINTF(str,"%smail.sda",sbbs->cfg.data_dir);
        !          3789:                backup(str,sbbs->cfg.mail_backup_level,FALSE);
        !          3790:                SAFEPRINTF(str,"%smail.sid",sbbs->cfg.data_dir);
        !          3791:                backup(str,sbbs->cfg.mail_backup_level,FALSE);
        !          3792:                SAFEPRINTF(str,"%smail.sch",sbbs->cfg.data_dir);
        !          3793:                backup(str,sbbs->cfg.mail_backup_level,FALSE);
        !          3794:        }
        !          3795: 
        !          3796:        lputs(LOG_INFO,"Checking for inactive/expired user records...");
        !          3797:        lastusernum=lastuser(&sbbs->cfg);
        !          3798:        for(usernum=1;usernum<=lastusernum;usernum++) {
        !          3799: 
        !          3800:                SAFEPRINTF2(str,"%5u of %-5u",usernum,lastusernum);
        !          3801:                status(str);
        !          3802:                user.number=usernum;
        !          3803:                if((i=getuserdat(&sbbs->cfg,&user))!=0) {
        !          3804:                        SAFEPRINTF(str,"user record %u",usernum);
        !          3805:                        sbbs->errormsg(WHERE,ERR_READ,str,i);
        !          3806:                        continue;
        !          3807:                }
        !          3808: 
        !          3809:                /***********************************************/
        !          3810:                /* Fix name (name.dat and user.dat) mismatches */
        !          3811:                /***********************************************/
        !          3812:                username(&sbbs->cfg,user.number,uname);
        !          3813:                if(user.misc&DELETED) {
        !          3814:                        if(strcmp(uname,"DELETED USER"))
        !          3815:                                putusername(&sbbs->cfg,user.number,nulstr);
        !          3816:                        continue; 
        !          3817:                }
        !          3818: 
        !          3819:                if(strcmp(user.alias,uname))
        !          3820:                        putusername(&sbbs->cfg,user.number,user.alias);
        !          3821: 
        !          3822:                if(user.number==1)
        !          3823:                        continue;       /* skip expiration/inactivity checks for user #1 */
        !          3824: 
        !          3825:                if(!(user.misc&(DELETED|INACTIVE))
        !          3826:                        && user.expire && (ulong)user.expire<=(ulong)now) {
        !          3827:                        putsmsg(&sbbs->cfg,user.number,sbbs->text[AccountHasExpired]);
        !          3828:                        SAFEPRINTF2(str,"%s #%u Expired",user.alias,user.number);
        !          3829:                        sbbs->logentry("!%",str);
        !          3830:                        if(sbbs->cfg.level_misc[user.level]&LEVEL_EXPTOVAL
        !          3831:                                && sbbs->cfg.level_expireto[user.level]<10) {
        !          3832:                                user.flags1=sbbs->cfg.val_flags1[sbbs->cfg.level_expireto[user.level]];
        !          3833:                                user.flags2=sbbs->cfg.val_flags2[sbbs->cfg.level_expireto[user.level]];
        !          3834:                                user.flags3=sbbs->cfg.val_flags3[sbbs->cfg.level_expireto[user.level]];
        !          3835:                                user.flags4=sbbs->cfg.val_flags4[sbbs->cfg.level_expireto[user.level]];
        !          3836:                                user.exempt=sbbs->cfg.val_exempt[sbbs->cfg.level_expireto[user.level]];
        !          3837:                                user.rest=sbbs->cfg.val_rest[sbbs->cfg.level_expireto[user.level]];
        !          3838:                                if(sbbs->cfg.val_expire[sbbs->cfg.level_expireto[user.level]])
        !          3839:                                        user.expire=now
        !          3840:                                                +(sbbs->cfg.val_expire[sbbs->cfg.level_expireto[user.level]]*24*60*60);
        !          3841:                                else
        !          3842:                                        user.expire=0;
        !          3843:                                user.level=sbbs->cfg.val_level[sbbs->cfg.level_expireto[user.level]]; 
        !          3844:                        }
        !          3845:                        else {
        !          3846:                                if(sbbs->cfg.level_misc[user.level]&LEVEL_EXPTOLVL)
        !          3847:                                        user.level=sbbs->cfg.level_expireto[user.level];
        !          3848:                                else
        !          3849:                                        user.level=sbbs->cfg.expired_level;
        !          3850:                                user.flags1&=~sbbs->cfg.expired_flags1; /* expired status */
        !          3851:                                user.flags2&=~sbbs->cfg.expired_flags2; /* expired status */
        !          3852:                                user.flags3&=~sbbs->cfg.expired_flags3; /* expired status */
        !          3853:                                user.flags4&=~sbbs->cfg.expired_flags4; /* expired status */
        !          3854:                                user.exempt&=~sbbs->cfg.expired_exempt;
        !          3855:                                user.rest|=sbbs->cfg.expired_rest;
        !          3856:                                user.expire=0; 
        !          3857:                        }
        !          3858:                        putuserrec(&sbbs->cfg,user.number,U_LEVEL,2,ultoa(user.level,str,10));
        !          3859:                        putuserrec(&sbbs->cfg,user.number,U_FLAGS1,8,ultoa(user.flags1,str,16));
        !          3860:                        putuserrec(&sbbs->cfg,user.number,U_FLAGS2,8,ultoa(user.flags2,str,16));
        !          3861:                        putuserrec(&sbbs->cfg,user.number,U_FLAGS3,8,ultoa(user.flags3,str,16));
        !          3862:                        putuserrec(&sbbs->cfg,user.number,U_FLAGS4,8,ultoa(user.flags4,str,16));
        !          3863:                        putuserrec(&sbbs->cfg,user.number,U_EXPIRE,8,ultoa(user.expire,str,16));
        !          3864:                        putuserrec(&sbbs->cfg,user.number,U_EXEMPT,8,ultoa(user.exempt,str,16));
        !          3865:                        putuserrec(&sbbs->cfg,user.number,U_REST,8,ultoa(user.rest,str,16));
        !          3866:                        if(sbbs->cfg.expire_mod[0]) {
        !          3867:                                sbbs->useron=user;
        !          3868:                                sbbs->online=ON_LOCAL;
        !          3869:                                sbbs->exec_bin(sbbs->cfg.expire_mod,&sbbs->main_csi);
        !          3870:                                sbbs->online=FALSE; 
        !          3871:                        }
        !          3872:                }
        !          3873: 
        !          3874:                /***********************************************************/
        !          3875:                /* Auto deletion based on expiration date or days inactive */
        !          3876:                /***********************************************************/
        !          3877:                if(!(user.exempt&FLAG('P'))     /* Not a permanent account */
        !          3878:                        && !(user.misc&(DELETED|INACTIVE))       /* alive */
        !          3879:                        && (sbbs->cfg.sys_autodel && (now-user.laston)/(long)(24L*60L*60L)
        !          3880:                        > sbbs->cfg.sys_autodel)) {                     /* Inactive too long */
        !          3881:                        SAFEPRINTF2(str,"Auto-Deleted %s #%u",user.alias,user.number);
        !          3882:                        sbbs->logentry("!*",str);
        !          3883:                        sbbs->delallmail(user.number);
        !          3884:                        putusername(&sbbs->cfg,user.number,nulstr);
        !          3885:                        putuserrec(&sbbs->cfg,user.number,U_MISC,8,ultoa(user.misc|DELETED,str,16)); 
        !          3886:                }
        !          3887:        }
        !          3888: 
        !          3889:        lputs(LOG_INFO,"Purging deleted/expired e-mail");
        !          3890:        SAFEPRINTF(sbbs->smb.file,"%smail",sbbs->cfg.data_dir);
        !          3891:        sbbs->smb.retry_time=sbbs->cfg.smb_retry_time;
        !          3892:        sbbs->smb.subnum=INVALID_SUB;
        !          3893:        if((i=smb_open(&sbbs->smb))!=0)
        !          3894:                sbbs->errormsg(WHERE,ERR_OPEN,sbbs->smb.file,i,sbbs->smb.last_error);
        !          3895:        else {
        !          3896:                if(filelength(fileno(sbbs->smb.shd_fp))>0) {
        !          3897:                        if((i=smb_locksmbhdr(&sbbs->smb))!=0)
        !          3898:                                sbbs->errormsg(WHERE,ERR_LOCK,sbbs->smb.file,i,sbbs->smb.last_error);
        !          3899:                        else
        !          3900:                                sbbs->delmail(0,MAIL_ALL);
        !          3901:                }
        !          3902:                smb_close(&sbbs->smb); 
        !          3903:        }
        !          3904: 
        !          3905:        sbbs->sys_status&=~SS_DAILY;
        !          3906:        if(sbbs->cfg.sys_daily[0]) {
        !          3907: //                     status("Running system daily event");
        !          3908:                sbbs->logentry("!:","Ran system daily event");
        !          3909:                sbbs->external(sbbs->cmdstr(sbbs->cfg.sys_daily,nulstr,nulstr,NULL)
        !          3910:                        ,EX_OFFLINE); 
        !          3911:        }
        !          3912: }
        !          3913: 
        !          3914: time_t checktime(void)
        !          3915: {
        !          3916:        struct tm tm;
        !          3917: 
        !          3918:     memset(&tm,0,sizeof(tm));
        !          3919:     tm.tm_year=94;
        !          3920:     tm.tm_mday=1;
        !          3921:     return(mktime(&tm)-0x2D24BD00L);
        !          3922: }
        !          3923: 
        !          3924: const char* DLLCALL js_ver(void)
        !          3925: {
        !          3926: #ifdef JAVASCRIPT
        !          3927:        return(JS_GetImplementationVersion());
        !          3928: #else
        !          3929:        return("");
        !          3930: #endif
        !          3931: }
        !          3932: 
        !          3933: /* Returns char string of version and revision */
        !          3934: const char* DLLCALL bbs_ver(void)
        !          3935: {
        !          3936:        static char ver[256];
        !          3937:        char compiler[32];
        !          3938: 
        !          3939:        if(ver[0]==0) { /* uninitialized */
        !          3940:                DESCRIBE_COMPILER(compiler);
        !          3941: 
        !          3942:                sprintf(ver,"%s %s%c%s  SMBLIB %s  Compiled %s %s with %s"
        !          3943:                        ,TELNET_SERVER
        !          3944:                        ,VERSION, REVISION
        !          3945: #ifdef _DEBUG
        !          3946:                        ," Debug"
        !          3947: #else
        !          3948:                        ,""
        !          3949: #endif
        !          3950:                        ,smb_lib_ver()
        !          3951:                        ,__DATE__, __TIME__, compiler
        !          3952:                        );
        !          3953:        }
        !          3954:        return(ver);
        !          3955: }
        !          3956: 
        !          3957: /* Returns binary-coded version and revision (e.g. 0x31000 == 3.10a) */
        !          3958: long DLLCALL bbs_ver_num(void)
        !          3959: {
        !          3960:        return(VERSION_HEX);
        !          3961: }
        !          3962: 
        !          3963: void DLLCALL bbs_terminate(void)
        !          3964: {
        !          3965:        lprintf(LOG_DEBUG,"BBS Server terminate");
        !          3966:        terminate_server=true;
        !          3967: }
        !          3968: 
        !          3969: static void cleanup(int code)
        !          3970: {
        !          3971:     lputs(LOG_INFO,"BBS System thread terminating");
        !          3972: 
        !          3973:        if(telnet_socket!=INVALID_SOCKET) {
        !          3974:                close_socket(telnet_socket);
        !          3975:                telnet_socket=INVALID_SOCKET;
        !          3976:        }
        !          3977:        if(rlogin_socket!=INVALID_SOCKET) {
        !          3978:                close_socket(rlogin_socket);
        !          3979:                rlogin_socket=INVALID_SOCKET;
        !          3980:        }
        !          3981: #ifdef USE_CRYPTLIB
        !          3982:        if(ssh_socket!=INVALID_SOCKET) {
        !          3983:                close_socket(ssh_socket);
        !          3984:                ssh_socket=INVALID_SOCKET;
        !          3985:        }
        !          3986: #endif
        !          3987: 
        !          3988: 
        !          3989: #ifdef _WINSOCKAPI_
        !          3990:        if(WSAInitialized && WSACleanup()!=0) 
        !          3991:                lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
        !          3992: #endif
        !          3993: 
        !          3994:        free_cfg(&scfg);
        !          3995:        free_text(text);
        !          3996: 
        !          3997:        semfile_list_free(&recycle_semfiles);
        !          3998:        semfile_list_free(&shutdown_semfiles);
        !          3999: 
        !          4000: #ifdef _WIN32
        !          4001:        if(exec_mutex!=NULL) {
        !          4002:                CloseHandle(exec_mutex);
        !          4003:                exec_mutex=NULL;
        !          4004:        }
        !          4005: 
        !          4006:        if(hK32!=NULL) {
        !          4007:                FreeLibrary(hK32);
        !          4008:                hK32=NULL;
        !          4009:        }
        !          4010: 
        !          4011: #if 0 && defined(_DEBUG) && defined(_MSC_VER)
        !          4012:        _CrtMemDumpAllObjectsSince(&mem_chkpoint);
        !          4013: 
        !          4014:        if(debug_log!=INVALID_HANDLE_VALUE) {
        !          4015:                CloseHandle(debug_log);
        !          4016:                debug_log=INVALID_HANDLE_VALUE;
        !          4017:        }
        !          4018: #endif // _DEBUG && _MSC_VER
        !          4019: #endif // _WIN32
        !          4020: 
        !          4021:        status("Down");
        !          4022:        thread_down();
        !          4023:        if(terminate_server || code)
        !          4024:                lprintf(LOG_INFO,"BBS System thread terminated (%u threads remain, %lu clients served)"
        !          4025:                        ,thread_count, served);
        !          4026:        if(startup->terminated!=NULL)
        !          4027:                startup->terminated(startup->cbdata,code);
        !          4028: }
        !          4029: 
        !          4030: void DLLCALL bbs_thread(void* arg)
        !          4031: {
        !          4032:        char*                   host_name;
        !          4033:        char*                   identity;
        !          4034:        char*                   p;
        !          4035:     char                       str[MAX_PATH+1];
        !          4036:        char                    logstr[256];
        !          4037:        SOCKADDR_IN             server_addr={0};
        !          4038:        SOCKADDR_IN             client_addr;
        !          4039:        socklen_t               client_addr_len;
        !          4040:        SOCKET                  client_socket=INVALID_SOCKET;
        !          4041:        fd_set                  socket_set;
        !          4042:        SOCKET                  high_socket_set;
        !          4043:        int                             i;
        !          4044:     int                                file;
        !          4045:        int                             result;
        !          4046:        time_t                  t;
        !          4047:        time_t                  start;
        !          4048:        time_t                  initialized=0;
        !          4049:        node_t                  node;
        !          4050:        sbbs_t*                 events=NULL;
        !          4051:        client_t                client;
        !          4052:        startup=(bbs_startup_t*)arg;
        !          4053:        BOOL                    is_client=FALSE;
        !          4054: #ifdef __unix__
        !          4055:        SOCKET  uspy_listen_socket[MAX_NODES];
        !          4056:        struct sockaddr_un uspy_addr;
        !          4057:        socklen_t               uspy_addr_len;
        !          4058: #endif
        !          4059: #ifdef USE_CRYPTLIB
        !          4060:        CRYPT_CONTEXT   ssh_context;
        !          4061: #endif
        !          4062: 
        !          4063:     if(startup==NULL) {
        !          4064:        sbbs_beep(100,500);
        !          4065:        fprintf(stderr, "No startup structure passed!\n");
        !          4066:        return;
        !          4067:     }
        !          4068: 
        !          4069:        if(startup->size!=sizeof(bbs_startup_t)) {      // verify size
        !          4070:                sbbs_beep(100,500);
        !          4071:                sbbs_beep(300,500);
        !          4072:                sbbs_beep(100,500);
        !          4073:                fprintf(stderr, "Invalid startup structure!\n");
        !          4074:                return;
        !          4075:        }
        !          4076: 
        !          4077: #ifdef _THREAD_SUID_BROKEN
        !          4078:        if(thread_suid_broken)
        !          4079:                startup->seteuid(TRUE);
        !          4080: #endif
        !          4081: 
        !          4082:        /* Setup intelligent defaults */
        !          4083:        if(startup->telnet_port==0)                             startup->telnet_port=IPPORT_TELNET;
        !          4084:        if(startup->rlogin_port==0)                             startup->rlogin_port=513;
        !          4085: #ifdef USE_CRYPTLIB
        !          4086:        if(startup->ssh_port==0)                                startup->ssh_port=22;
        !          4087: #endif
        !          4088:        if(startup->outbuf_drain_timeout==0)    startup->outbuf_drain_timeout=10;
        !          4089:        if(startup->sem_chk_freq==0)                    startup->sem_chk_freq=2;
        !          4090:        if(startup->temp_dir[0])                                backslash(startup->temp_dir);
        !          4091: 
        !          4092:        ZERO_VAR(js_server_props);
        !          4093:        SAFEPRINTF3(js_server_props.version,"%s %s%c",TELNET_SERVER,VERSION,REVISION);
        !          4094:        js_server_props.version_detail=bbs_ver();
        !          4095:        js_server_props.clients=&node_threads_running;
        !          4096:        js_server_props.options=&startup->options;
        !          4097:        js_server_props.interface_addr=&startup->telnet_interface;
        !          4098: 
        !          4099:        uptime=0;
        !          4100:        served=0;
        !          4101:        startup->recycle_now=FALSE;
        !          4102:        startup->shutdown_now=FALSE;
        !          4103:        terminate_server=false;
        !          4104: 
        !          4105:        do {
        !          4106: 
        !          4107:        thread_up(FALSE /* setuid */);
        !          4108: 
        !          4109:        status("Initializing");
        !          4110: 
        !          4111:        /* Defeat the lameo hex0rs - the name and copyright must remain intact */
        !          4112:        if(crc32(COPYRIGHT_NOTICE,0)!=COPYRIGHT_CRC 
        !          4113:                || crc32(VERSION_NOTICE,10)!=SYNCHRONET_CRC) {
        !          4114:                lprintf(LOG_ERR,"!CORRUPTED LIBRARY FILE");
        !          4115:                cleanup(1);
        !          4116:                return;
        !          4117:        }
        !          4118: 
        !          4119:        memset(text, 0, sizeof(text));
        !          4120:     memset(&scfg, 0, sizeof(scfg));
        !          4121: 
        !          4122:        node_threads_running=0;
        !          4123:        lastuseron[0]=0;
        !          4124: 
        !          4125:        char compiler[32];
        !          4126:        DESCRIBE_COMPILER(compiler);
        !          4127: 
        !          4128:        lprintf(LOG_INFO,"%s Version %s Revision %c%s"
        !          4129:                ,TELNET_SERVER
        !          4130:                ,VERSION
        !          4131:                ,toupper(REVISION)
        !          4132: #ifdef _DEBUG
        !          4133:                ," Debug"
        !          4134: #else
        !          4135:                ,""
        !          4136: #endif
        !          4137:                );
        !          4138:        lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
        !          4139:        lprintf(LOG_INFO,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);
        !          4140: 
        !          4141:     if(startup->first_node<1 || startup->first_node>startup->last_node) {
        !          4142:        lprintf(LOG_ERR,"!ILLEGAL node configuration (first: %d, last: %d)"
        !          4143:                ,startup->first_node, startup->last_node);
        !          4144:                cleanup(1);
        !          4145:         return;
        !          4146:     }
        !          4147: 
        !          4148: #ifdef __BORLANDC__
        !          4149:        #pragma warn -8008      /* Disable "Condition always false" warning */
        !          4150:        #pragma warn -8066      /* Disable "Unreachable code" warning */
        !          4151: #endif
        !          4152:        if(sizeof(node_t)!=SIZEOF_NODE_T) {
        !          4153:                lprintf(LOG_ERR,"!COMPILER ERROR: sizeof(node_t)=%d instead of %d"
        !          4154:                        ,sizeof(node_t),SIZEOF_NODE_T);
        !          4155:                cleanup(1);
        !          4156:                return;
        !          4157:        }
        !          4158: 
        !          4159: #ifdef _WIN32
        !          4160:     if((exec_mutex=CreateMutex(NULL,false,NULL))==NULL) {
        !          4161:        lprintf(LOG_ERR,"!ERROR %d creating exec_mutex", GetLastError());
        !          4162:                cleanup(1);
        !          4163:         return;
        !          4164:     }
        !          4165:        hK32 = LoadLibrary("KERNEL32");
        !          4166: #endif // _WIN32
        !          4167: 
        !          4168:        if(!winsock_startup()) {
        !          4169:                cleanup(1);
        !          4170:                return;
        !          4171:        }
        !          4172: 
        !          4173:        t=time(NULL);
        !          4174:        lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
        !          4175:                ,CTIME_R(&t,str),startup->options);
        !          4176: 
        !          4177:        if(chdir(startup->ctrl_dir)!=0)
        !          4178:                lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir);
        !          4179: 
        !          4180:        /* Initial configuration and load from CNF files */
        !          4181:     SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
        !          4182:     lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
        !          4183:        scfg.size=sizeof(scfg);
        !          4184:        scfg.node_num=startup->first_node;
        !          4185:        SAFECOPY(logstr,UNKNOWN_LOAD_ERROR);
        !          4186:        if(!load_cfg(&scfg, text, TRUE, logstr)) {
        !          4187:                lprintf(LOG_ERR,"!ERROR %s",logstr);
        !          4188:                lprintf(LOG_ERR,"!FAILED to load configuration files");
        !          4189:                cleanup(1);
        !          4190:                return;
        !          4191:        }
        !          4192:        
        !          4193:        if(startup->host_name[0]==0)
        !          4194:                SAFECOPY(startup->host_name,scfg.sys_inetaddr);
        !          4195: 
        !          4196:        if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&BBS_OPT_LOCAL_TIMEZONE)) {
        !          4197:                if(putenv("TZ=UTC0"))
        !          4198:                        lprintf(LOG_ERR,"!putenv() FAILED");
        !          4199:                tzset();
        !          4200: 
        !          4201:                if((t=checktime())!=0) {   /* Check binary time */
        !          4202:                        lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
        !          4203:                        cleanup(1);
        !          4204:                        return;
        !          4205:                }
        !          4206:        }
        !          4207: 
        !          4208:        if(uptime==0)
        !          4209:                uptime=time(NULL);      /* this must be done *after* setting the timezone */
        !          4210: 
        !          4211:     if(startup->last_node>scfg.sys_nodes) {
        !          4212:        lprintf(LOG_NOTICE,"Specified last_node (%d) > sys_nodes (%d), auto-corrected"
        !          4213:                ,startup->last_node, scfg.sys_nodes);
        !          4214:         startup->last_node=scfg.sys_nodes;
        !          4215:     }
        !          4216: 
        !          4217:        /* Create missing directories */
        !          4218:        lprintf(LOG_INFO,"Verifying/creating data directories");
        !          4219:        make_data_dirs(&scfg);
        !          4220: 
        !          4221:        /* Create missing node directories and dsts.dab files */
        !          4222:        lprintf(LOG_INFO,"Verifying/creating node directories");
        !          4223:        for(i=0;i<=scfg.sys_nodes;i++) {
        !          4224:                if(i)
        !          4225:                        md(scfg.node_path[i-1]);
        !          4226:                SAFEPRINTF(str,"%sdsts.dab",i ? scfg.node_path[i-1] : scfg.ctrl_dir);
        !          4227:                if(flength(str)<DSTSDABLEN) {
        !          4228:                        if((file=sopen(str,O_WRONLY|O_CREAT|O_APPEND, SH_DENYNO, S_IREAD|S_IWRITE))==-1) {
        !          4229:                                lprintf(LOG_ERR,"!ERROR %d creating %s",errno, str);
        !          4230:                                cleanup(1);
        !          4231:                                return; 
        !          4232:                        }
        !          4233:                        while(filelength(file)<DSTSDABLEN)
        !          4234:                                if(write(file,"\0",1)!=1)
        !          4235:                                        break;                          /* Create NULL system dsts.dab */
        !          4236:                        close(file); 
        !          4237:                } 
        !          4238:        }
        !          4239: 
        !          4240:        /* Initial global node variables */
        !          4241:        for(i=0;i<MAX_NODES;i++) {
        !          4242:                node_inbuf[i]=NULL;
        !          4243:        node_socket[i]=INVALID_SOCKET;
        !          4244:                spy_socket[i]=INVALID_SOCKET;
        !          4245: #ifdef __unix__
        !          4246:                uspy_socket[i]=INVALID_SOCKET;
        !          4247:                uspy_listen_socket[i]=INVALID_SOCKET;
        !          4248: #endif
        !          4249:        }
        !          4250: 
        !          4251:        startup->node_inbuf=node_inbuf;
        !          4252: 
        !          4253:     /* open a socket and wait for a client */
        !          4254: 
        !          4255:     telnet_socket = open_socket(SOCK_STREAM, "telnet");
        !          4256: 
        !          4257:        if(telnet_socket == INVALID_SOCKET) {
        !          4258:                lprintf(LOG_ERR,"!ERROR %d creating Telnet socket", ERROR_VALUE);
        !          4259:                cleanup(1);
        !          4260:                return;
        !          4261:        }
        !          4262: 
        !          4263:     lprintf(LOG_INFO,"Telnet socket %d opened",telnet_socket);
        !          4264: 
        !          4265:        /*****************************/
        !          4266:        /* Listen for incoming calls */
        !          4267:        /*****************************/
        !          4268:     memset(&server_addr, 0, sizeof(server_addr));
        !          4269: 
        !          4270:        server_addr.sin_addr.s_addr = htonl(startup->telnet_interface);
        !          4271:     server_addr.sin_family = AF_INET;
        !          4272:     server_addr.sin_port   = htons(startup->telnet_port);
        !          4273: 
        !          4274:        if(startup->seteuid!=NULL)
        !          4275:                startup->seteuid(FALSE);
        !          4276:     result = retry_bind(telnet_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
        !          4277:                ,startup->bind_retry_count,startup->bind_retry_delay,"Telnet Server",lprintf);
        !          4278:        if(startup->seteuid!=NULL)
        !          4279:                startup->seteuid(TRUE);
        !          4280:        if(result != 0) {
        !          4281:                lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
        !          4282:                cleanup(1);
        !          4283:                return;
        !          4284:        }
        !          4285: 
        !          4286:     result = listen(telnet_socket, 1);
        !          4287: 
        !          4288:        if(result != 0) {
        !          4289:                lprintf(LOG_ERR,"!ERROR %d (%d) listening on Telnet socket", result, ERROR_VALUE);
        !          4290:                cleanup(1);
        !          4291:                return;
        !          4292:        }
        !          4293:        lprintf(LOG_INFO,"Telnet server listening on port %d",startup->telnet_port);
        !          4294: 
        !          4295:        if(startup->options&BBS_OPT_ALLOW_RLOGIN) {
        !          4296: 
        !          4297:                /* open a socket and wait for a client */
        !          4298: 
        !          4299:                rlogin_socket = open_socket(SOCK_STREAM, "rlogin");
        !          4300: 
        !          4301:                if(rlogin_socket == INVALID_SOCKET) {
        !          4302:                        lprintf(LOG_ERR,"!ERROR %d creating RLogin socket", ERROR_VALUE);
        !          4303:                        cleanup(1);
        !          4304:                        return;
        !          4305:                }
        !          4306: 
        !          4307:                lprintf(LOG_INFO,"RLogin socket %d opened",rlogin_socket);
        !          4308: 
        !          4309:                /*****************************/
        !          4310:                /* Listen for incoming calls */
        !          4311:                /*****************************/
        !          4312:                memset(&server_addr, 0, sizeof(server_addr));
        !          4313: 
        !          4314:                server_addr.sin_addr.s_addr = htonl(startup->rlogin_interface);
        !          4315:                server_addr.sin_family = AF_INET;
        !          4316:                server_addr.sin_port   = htons(startup->rlogin_port);
        !          4317: 
        !          4318:                if(startup->seteuid!=NULL)
        !          4319:                        startup->seteuid(FALSE);
        !          4320:                result = retry_bind(rlogin_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
        !          4321:                        ,startup->bind_retry_count,startup->bind_retry_delay,"RLogin Server",lprintf);
        !          4322:                if(startup->seteuid!=NULL)
        !          4323:                        startup->seteuid(TRUE);
        !          4324:                if(result != 0) {
        !          4325:                        lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
        !          4326:                        cleanup(1);
        !          4327:                        return;
        !          4328:                }
        !          4329: 
        !          4330:                result = listen(rlogin_socket, 1);
        !          4331: 
        !          4332:                if(result != 0) {
        !          4333:                        lprintf(LOG_ERR,"!ERROR %d (%d) listening on RLogin socket", result, ERROR_VALUE);
        !          4334:                        cleanup(1);
        !          4335:                        return;
        !          4336:                }
        !          4337:                lprintf(LOG_INFO,"RLogin server listening on port %d",startup->rlogin_port);
        !          4338:        }
        !          4339: 
        !          4340: #ifdef USE_CRYPTLIB
        !          4341: #if CRYPTLIB_VERSION < 3300
        !          4342:        #warning This version of Cryptlib is known to crash Synchronet.  Upgrade to at least version 3.3 or do not build with Cryptlib support.
        !          4343: #endif
        !          4344:        if(startup->options&BBS_OPT_ALLOW_SSH) {
        !          4345:                bool                    loaded_key=false;
        !          4346: 
        !          4347:                CRYPT_KEYSET    ssh_keyset;
        !          4348: 
        !          4349:                cryptInit();
        !          4350:                cryptAddRandom(NULL,CRYPT_RANDOM_SLOWPOLL);
        !          4351:                /* Get the private key... first try loading it from a file... */
        !          4352:                SAFEPRINTF2(str,"%s%s",scfg.ctrl_dir,"cryptlib.key");
        !          4353:                if(cryptStatusOK(cryptKeysetOpen(&ssh_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_NONE))) {
        !          4354:                        if(cryptStatusOK(cryptGetPrivateKey(ssh_keyset, &ssh_context, CRYPT_KEYID_NAME, "ssh_server", scfg.sys_pass)))
        !          4355:                                loaded_key=true;
        !          4356:                        cryptKeysetClose(ssh_keyset);
        !          4357:                        /* Failed to load the key... delete the keyfile and create a new one */
        !          4358:                        if(!loaded_key)
        !          4359:                                remove(str);
        !          4360:                }
        !          4361: 
        !          4362:                if(!loaded_key) {
        !          4363:                        /* Couldn't do that... create a new context and use the key from there... */
        !          4364: 
        !          4365:                        if(!cryptStatusOK(i=cryptCreateContext(&ssh_context, CRYPT_UNUSED, CRYPT_ALGO_RSA))) {
        !          4366:                                lprintf(LOG_ERR,"Cryptlib error %d creating context",i);
        !          4367:                                goto NO_SSH;
        !          4368:                        }
        !          4369:                        if(!cryptStatusOK(i=cryptSetAttributeString(ssh_context, CRYPT_CTXINFO_LABEL, "ssh_server", 10))) {
        !          4370:                                lprintf(LOG_ERR,"Cryptlib error %d setting key label",i);
        !          4371:                                goto NO_SSH;
        !          4372:                        }
        !          4373:                        if(!cryptStatusOK(i=cryptGenerateKey(ssh_context))) {
        !          4374:                                lprintf(LOG_ERR,"Cryptlib error %d generating key",i);
        !          4375:                                goto NO_SSH;
        !          4376:                        }
        !          4377: 
        !          4378:                        /* Ok, now try saving this one... use the syspass to enctrpy it. */
        !          4379:                        if(cryptStatusOK(cryptKeysetOpen(&ssh_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_CREATE))) {
        !          4380:                                cryptAddPrivateKey(ssh_keyset, ssh_context, scfg.sys_pass);
        !          4381:                                cryptKeysetClose(ssh_keyset);
        !          4382:                        }
        !          4383:                }
        !          4384: 
        !          4385:                /* open a socket and wait for a client */
        !          4386: 
        !          4387:                ssh_socket = open_socket(SOCK_STREAM, "ssh");
        !          4388: 
        !          4389:                if(ssh_socket == INVALID_SOCKET) {
        !          4390:                        lprintf(LOG_ERR,"!ERROR %d creating SSH socket", ERROR_VALUE);
        !          4391:                        cleanup(1);
        !          4392:                        return;
        !          4393:                }
        !          4394: 
        !          4395:                lprintf(LOG_INFO,"SSH socket %d opened",ssh_socket);
        !          4396: 
        !          4397:                /*****************************/
        !          4398:                /* Listen for incoming calls */
        !          4399:                /*****************************/
        !          4400:                memset(&server_addr, 0, sizeof(server_addr));
        !          4401: 
        !          4402:                server_addr.sin_addr.s_addr = htonl(startup->ssh_interface);
        !          4403:                server_addr.sin_family = AF_INET;
        !          4404:                server_addr.sin_port   = htons(startup->ssh_port);
        !          4405: 
        !          4406:                if(startup->seteuid!=NULL)
        !          4407:                        startup->seteuid(FALSE);
        !          4408:                result = retry_bind(ssh_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
        !          4409:                        ,startup->bind_retry_count,startup->bind_retry_delay,"SSH Server",lprintf);
        !          4410:                if(startup->seteuid!=NULL)
        !          4411:                        startup->seteuid(TRUE);
        !          4412:                if(result != 0) {
        !          4413:                        lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
        !          4414:                        cleanup(1);
        !          4415:                        return;
        !          4416:                }
        !          4417: 
        !          4418:                result = listen(ssh_socket, 1);
        !          4419: 
        !          4420:                if(result != 0) {
        !          4421:                        lprintf(LOG_ERR,"!ERROR %d (%d) listening on SSH socket", result, ERROR_VALUE);
        !          4422:                        cleanup(1);
        !          4423:                        return;
        !          4424:                }
        !          4425:                lprintf(LOG_INFO,"SSH server listening on port %d",startup->ssh_port);
        !          4426:        }
        !          4427: NO_SSH:
        !          4428: #endif
        !          4429: 
        !          4430:        sbbs = new sbbs_t(0, server_addr.sin_addr.s_addr
        !          4431:                ,"BBS System", telnet_socket, &scfg, text, NULL);
        !          4432:     sbbs->online = 0;
        !          4433:        if(sbbs->init()==false) {
        !          4434:                lputs(LOG_ERR,"!BBS initialization failed");
        !          4435:                cleanup(1);
        !          4436:                return;
        !          4437:        }
        !          4438:        _beginthread(output_thread, 0, sbbs);
        !          4439: 
        !          4440:        if(!(startup->options&BBS_OPT_NO_EVENTS)) {
        !          4441:                events = new sbbs_t(0, server_addr.sin_addr.s_addr
        !          4442:                        ,"BBS Events", INVALID_SOCKET, &scfg, text, NULL);
        !          4443:                events->online = 0;
        !          4444:                if(events->init()==false) {
        !          4445:                        lputs(LOG_ERR,"!Events initialization failed");
        !          4446:                        cleanup(1);
        !          4447:                        return;
        !          4448:                }
        !          4449:                _beginthread(event_thread, 0, events);
        !          4450:        }
        !          4451: 
        !          4452:        /* Save these values incase they're changed dynamically */
        !          4453:        first_node=startup->first_node;
        !          4454:        last_node=startup->last_node;
        !          4455: 
        !          4456:        for(i=first_node;i<=last_node;i++) {
        !          4457:                sbbs->getnodedat(i,&node,1);
        !          4458:                node.status=NODE_WFC;
        !          4459:                node.misc&=NODE_EVENT;
        !          4460:                node.action=0;
        !          4461:                sbbs->putnodedat(i,&node);
        !          4462:        }
        !          4463: 
        !          4464:        lprintf(LOG_INFO,"BBS System thread started for nodes %d through %d", first_node, last_node);
        !          4465:        status(STATUS_WFC);
        !          4466: 
        !          4467: #if defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
        !          4468:        
        !          4469:        SAFEPRINTF(str,"%sDEBUG.LOG",scfg.logs_dir);
        !          4470:        if((debug_log=CreateFile(
        !          4471:                str,                            // pointer to name of the file
        !          4472:                GENERIC_READ|GENERIC_WRITE,
        !          4473:                FILE_SHARE_READ|FILE_SHARE_WRITE,
        !          4474:                NULL,               // pointer to security attributes
        !          4475:                OPEN_ALWAYS,            // how to create
        !          4476:                FILE_ATTRIBUTE_NORMAL, // file attributes
        !          4477:                NULL                            // handle to file with attributes to 
        !          4478:                ))==INVALID_HANDLE_VALUE) {
        !          4479:                lprintf(LOG_ERR,"!ERROR %ld creating %s",GetLastError(),str);
        !          4480:                cleanup(1);
        !          4481:                return;
        !          4482:        }
        !          4483: 
        !          4484:        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
        !          4485:        _CrtSetReportFile(_CRT_WARN, debug_log);
        !          4486:        _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE|_CRTDBG_MODE_WNDW);
        !          4487:        _CrtSetReportFile(_CRT_ERROR, debug_log);
        !          4488:        _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE|_CRTDBG_MODE_WNDW);
        !          4489:        _CrtSetReportFile(_CRT_ASSERT, debug_log);
        !          4490: 
        !          4491:        /* Turns on memory leak checking during program termination */
        !          4492: //     _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
        !          4493: 
        !          4494:        /* Save this allocation point for comparison */
        !          4495:        _CrtMemCheckpoint(&mem_chkpoint);
        !          4496: 
        !          4497: #endif // _WIN32 && _DEBUG && _MSC_VER
        !          4498: 
        !          4499:        /* Setup recycle/shutdown semaphore file lists */
        !          4500:        shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","telnet");
        !          4501:        recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","telnet");
        !          4502:        SAFEPRINTF(str,"%stelnet.rec",scfg.ctrl_dir);   /* legacy */
        !          4503:        semfile_list_add(&recycle_semfiles,str);
        !          4504:        if(!initialized) {
        !          4505:                semfile_list_check(&initialized,recycle_semfiles);
        !          4506:                semfile_list_check(&initialized,shutdown_semfiles);
        !          4507:        }
        !          4508: 
        !          4509: #ifdef __unix__        //      unix-domain spy sockets
        !          4510:        for(i=first_node;i<=last_node && !(startup->options&BBS_OPT_NO_SPY_SOCKETS);i++)  {
        !          4511:            if((uspy_listen_socket[i-1]=socket(PF_UNIX,SOCK_STREAM,0))==INVALID_SOCKET)
        !          4512:                lprintf(LOG_ERR,"Node %d !ERROR %d creating local spy socket"
        !          4513:                    , i, errno);
        !          4514:            else {
        !          4515:                lprintf(LOG_INFO,"Node %d local spy using socket %d", i, uspy_listen_socket[i-1]);
        !          4516:                if(startup!=NULL && startup->socket_open!=NULL)
        !          4517:                    startup->socket_open(startup->cbdata,TRUE);
        !          4518:            }
        !          4519:        
        !          4520:            uspy_addr.sun_family=AF_UNIX;
        !          4521:            if((unsigned int)snprintf(str,sizeof(uspy_addr.sun_path),
        !          4522:                    "%slocalspy%d.sock", startup->temp_dir, i)
        !          4523:                    >=sizeof(uspy_addr.sun_path))
        !          4524:                uspy_listen_socket[i-1]=INVALID_SOCKET;
        !          4525:            else  {
        !          4526:                strcpy(uspy_addr.sun_path,str);
        !          4527:                if(fexist(str))
        !          4528:                    unlink(str);
        !          4529:            }
        !          4530:            if(uspy_listen_socket[i-1]!=INVALID_SOCKET) {
        !          4531:                uspy_addr_len=SUN_LEN(&uspy_addr);
        !          4532:                if(bind(uspy_listen_socket[i-1], (struct sockaddr *) &uspy_addr, uspy_addr_len)) {
        !          4533:                    lprintf(LOG_ERR,"Node %d !ERROR %d binding local spy socket %d to %s"
        !          4534:                        , i, errno, uspy_listen_socket[i-1], uspy_addr.sun_path);
        !          4535:                    close_socket(uspy_listen_socket[i-1]);
        !          4536:                                uspy_listen_socket[i-1]=INVALID_SOCKET;
        !          4537:                    continue;
        !          4538:                }
        !          4539:             lprintf(LOG_INFO,"Node %d local spy socket %d bound to %s"
        !          4540:                 , i, uspy_listen_socket[i-1], uspy_addr.sun_path);
        !          4541:                if(listen(uspy_listen_socket[i-1],1))  {
        !          4542:                    lprintf(LOG_ERR,"Node %d !ERROR %d listening local spy socket %d"
        !          4543:                                        ,i, errno, uspy_listen_socket[i-1]);
        !          4544:                    close_socket(uspy_listen_socket[i-1]);
        !          4545:                                uspy_listen_socket[i-1]=INVALID_SOCKET;
        !          4546:                    continue;
        !          4547:                        }
        !          4548:                uspy_addr_len=sizeof(uspy_addr);
        !          4549:            }
        !          4550:        }
        !          4551: #endif // __unix__ (unix-domain spy sockets)
        !          4552: 
        !          4553:        /* signal caller that we've started up successfully */
        !          4554:     if(startup->started!=NULL)
        !          4555:        startup->started(startup->cbdata);
        !          4556: 
        !          4557: 
        !          4558:        while(!terminate_server) {
        !          4559: 
        !          4560:                if(node_threads_running==0) {   /* check for re-run flags */
        !          4561:                        bool rerun=false;
        !          4562:                        for(i=first_node;i<=last_node;i++) {
        !          4563:                                if(sbbs->getnodedat(i,&node,0)!=0)
        !          4564:                                        continue;
        !          4565:                                if(node.misc&NODE_RRUN) {
        !          4566:                                        sbbs->getnodedat(i,&node,1);
        !          4567:                                        if(!rerun)
        !          4568:                                                lprintf(LOG_INFO,"Node %d flagged for re-run",i);
        !          4569:                                        rerun=true;
        !          4570:                                        node.misc&=~NODE_RRUN;
        !          4571:                                        sbbs->putnodedat(i,&node);
        !          4572:                                }
        !          4573:                        }
        !          4574:                        if(rerun)
        !          4575:                                break;
        !          4576:                        if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
        !          4577:                                if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
        !          4578:                                        lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected"
        !          4579:                                                ,telnet_socket,p);
        !          4580:                                        break;
        !          4581:                                }
        !          4582: #if 0  /* unused */
        !          4583:                                if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0)
        !          4584:                                        startup->recycle_now=TRUE;
        !          4585: #endif
        !          4586:                                if(startup->recycle_now==TRUE) {
        !          4587:                                        lprintf(LOG_INFO,"%04d Recycle semaphore signaled",telnet_socket);
        !          4588:                                        startup->recycle_now=FALSE;
        !          4589:                                        break;
        !          4590:                                }
        !          4591:                        }
        !          4592:                        if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
        !          4593:                                        && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected"
        !          4594:                                                ,telnet_socket,p))
        !          4595:                                || (startup->shutdown_now==TRUE
        !          4596:                                        && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled"
        !          4597:                                                ,telnet_socket))) {
        !          4598:                                startup->shutdown_now=FALSE;
        !          4599:                                terminate_server=TRUE;
        !          4600:                                break;
        !          4601:                        }
        !          4602:                }
        !          4603: 
        !          4604:        sbbs->online=FALSE;
        !          4605: #ifdef USE_CRYPTLIB
        !          4606:                sbbs->ssh_mode=false;
        !          4607: #endif
        !          4608: 
        !          4609:                /* now wait for connection */
        !          4610: 
        !          4611:                FD_ZERO(&socket_set);
        !          4612:                high_socket_set=0;
        !          4613:                if(telnet_socket!=INVALID_SOCKET) {
        !          4614:                        FD_SET(telnet_socket,&socket_set);
        !          4615:                        high_socket_set=telnet_socket+1;
        !          4616:                }
        !          4617:                if(startup->options&BBS_OPT_ALLOW_RLOGIN 
        !          4618:                        && rlogin_socket!=INVALID_SOCKET) {
        !          4619:                        FD_SET(rlogin_socket,&socket_set);
        !          4620:                        if(rlogin_socket+1>high_socket_set)
        !          4621:                                high_socket_set=rlogin_socket+1;
        !          4622:                }
        !          4623: #ifdef USE_CRYPTLIB
        !          4624:                if(startup->options&BBS_OPT_ALLOW_SSH
        !          4625:                        && ssh_socket!=INVALID_SOCKET) {
        !          4626:                        FD_SET(ssh_socket,&socket_set);
        !          4627:                        if(ssh_socket+1>high_socket_set)
        !          4628:                                high_socket_set=ssh_socket+1;
        !          4629:                }
        !          4630: #endif
        !          4631: #ifdef __unix__
        !          4632:                for(i=first_node;i<=last_node;i++)  {
        !          4633:                        if(uspy_listen_socket[i-1]!=INVALID_SOCKET)  {
        !          4634:                                FD_SET(uspy_listen_socket[i-1],&socket_set);
        !          4635:                                if(uspy_listen_socket[i-1]+1>high_socket_set)
        !          4636:                                        high_socket_set=uspy_listen_socket[i-1]+1;
        !          4637:                        }
        !          4638:                        if(uspy_socket[i-1]!=INVALID_SOCKET)  {
        !          4639:                                FD_SET(uspy_socket[i-1],&socket_set);
        !          4640:                                if(uspy_socket[i-1]+1>high_socket_set)
        !          4641:                                        high_socket_set=uspy_listen_socket[i-1]+1;
        !          4642:                        }
        !          4643:                }
        !          4644: #endif
        !          4645: 
        !          4646:                struct timeval tv;
        !          4647:                tv.tv_sec=startup->sem_chk_freq;
        !          4648:                tv.tv_usec=0;
        !          4649: 
        !          4650:                if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
        !          4651:                        if(i==0)
        !          4652:                                continue;
        !          4653:                        if(ERROR_VALUE==EINTR)
        !          4654:                                lprintf(LOG_DEBUG,"Telnet Server listening interrupted");
        !          4655:                        else if(ERROR_VALUE == ENOTSOCK)
        !          4656:                lprintf(LOG_NOTICE,"Telnet Server sockets closed");
        !          4657:                        else
        !          4658:                                lprintf(LOG_WARNING,"!ERROR %d selecting sockets",ERROR_VALUE);
        !          4659:                        continue;
        !          4660:                }
        !          4661: 
        !          4662:                if(terminate_server)    /* terminated */
        !          4663:                        break;
        !          4664: 
        !          4665:                client_addr_len = sizeof(client_addr);
        !          4666: 
        !          4667:                bool rlogin = false;
        !          4668: #ifdef USE_CRYPTLIB
        !          4669:                bool ssh = false;
        !          4670: #endif
        !          4671: 
        !          4672:                is_client=FALSE;
        !          4673:                if(telnet_socket!=INVALID_SOCKET 
        !          4674:                        && FD_ISSET(telnet_socket,&socket_set)) {
        !          4675:                        client_socket = accept_socket(telnet_socket, (struct sockaddr *)&client_addr
        !          4676:                        ,&client_addr_len);
        !          4677:                is_client=TRUE;
        !          4678:                } else if(rlogin_socket!=INVALID_SOCKET 
        !          4679:                        && FD_ISSET(rlogin_socket,&socket_set)) {
        !          4680:                        client_socket = accept_socket(rlogin_socket, (struct sockaddr *)&client_addr
        !          4681:                        ,&client_addr_len);
        !          4682:                        rlogin = true;
        !          4683:                        is_client=TRUE;
        !          4684: #ifdef USE_CRYPTLIB
        !          4685:                } else if(ssh_socket!=INVALID_SOCKET 
        !          4686:                        && FD_ISSET(ssh_socket,&socket_set)) {
        !          4687: 
        !          4688:                        client_socket = accept_socket(ssh_socket, (struct sockaddr *)&client_addr
        !          4689:                        ,&client_addr_len);
        !          4690:                        ssh = true;
        !          4691:                        is_client=TRUE;
        !          4692:                        sbbs->ssh_mode=true;
        !          4693: #endif
        !          4694:                } else {
        !          4695: #ifdef __unix__
        !          4696:                        for(i=first_node;i<=last_node;i++)  {
        !          4697:                                if(uspy_socket[i-1]!=INVALID_SOCKET
        !          4698:                                && FD_ISSET(uspy_socket[i-1],&socket_set)) {
        !          4699:                                        if(node_socket[i-1]==INVALID_SOCKET)
        !          4700:                                                read(uspy_socket[i-1],str,sizeof(str));
        !          4701:                                        if(!socket_check(uspy_socket[i-1],NULL,NULL,0)) {
        !          4702:                                                lprintf(LOG_NOTICE,"Spy socket for node %d disconnected",i);
        !          4703:                                                close_socket(uspy_socket[i-1]);
        !          4704:                                                uspy_socket[i-1]=INVALID_SOCKET;
        !          4705:                                        }
        !          4706:                                }
        !          4707:                                if(uspy_listen_socket[i-1]!=INVALID_SOCKET
        !          4708:                                && FD_ISSET(uspy_listen_socket[i-1],&socket_set)) {
        !          4709:                                        BOOL already_connected=(uspy_socket[i-1]!=INVALID_SOCKET);
        !          4710:                                        SOCKET new_socket=INVALID_SOCKET;
        !          4711:                                        new_socket = accept(uspy_listen_socket[i-1], (struct sockaddr *)&uspy_addr
        !          4712:                                                ,&uspy_addr_len);
        !          4713:                                        if(new_socket < 0)  {
        !          4714:                                                lprintf(LOG_ERR,"!ERROR Spy socket for node %d unable to accept()",i);
        !          4715:                                                close_socket(uspy_listen_socket[i-1]);
        !          4716:                                                uspy_listen_socket[i-1]=INVALID_SOCKET;
        !          4717:                                        }
        !          4718:                                        fcntl(new_socket,F_SETFL,fcntl(new_socket,F_GETFL)|O_NONBLOCK);
        !          4719:                                        if(already_connected)  {
        !          4720:                                                lprintf(LOG_ERR,"!ERROR Spy socket %s already in use",uspy_addr.sun_path);
        !          4721:                                                send(new_socket,"Spy socket already in use.\r\n",27,0);
        !          4722:                                                close_socket(new_socket);
        !          4723:                                        }
        !          4724:                                        else  {
        !          4725:                                                lprintf(LOG_ERR,"!Spy socket %s (%d) connected",uspy_addr.sun_path,new_socket);
        !          4726:                                                uspy_socket[i-1]=new_socket;
        !          4727:                                                SAFEPRINTF(str,"Spy connection established to node %d\r\n",i);
        !          4728:                                                send(uspy_socket[i-1],str,strlen(str),0);
        !          4729:                                        }
        !          4730:                                }
        !          4731:                        }
        !          4732: #else
        !          4733:                        lprintf(LOG_ERR,"!NO SOCKETS set by select");
        !          4734: #endif
        !          4735:                }
        !          4736: 
        !          4737:                if(!is_client) {
        !          4738:                        /* Do not need to close_socket(client_socket) here */
        !          4739:                        continue;
        !          4740:                }
        !          4741: 
        !          4742:                if(client_socket == INVALID_SOCKET)     {
        !          4743: #if 0  /* is this necessary still? */
        !          4744:                        if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR || ERROR_VALUE == EINVAL) {
        !          4745:                lputs(LOG_NOTICE,"BBS socket closed");
        !          4746:                                break;
        !          4747:                        }
        !          4748: #endif
        !          4749:                        lprintf(LOG_ERR,"!ERROR %d accepting connection", ERROR_VALUE);
        !          4750: #ifdef _WIN32
        !          4751:                        if(WSAGetLastError()==WSAENOBUFS)       /* recycle (re-init WinSock) on this error */
        !          4752:                                break;
        !          4753: #endif
        !          4754:                        SSH_END();
        !          4755:                        continue;
        !          4756:                }
        !          4757:                char host_ip[32];
        !          4758: 
        !          4759:                strcpy(host_ip,inet_ntoa(client_addr.sin_addr));
        !          4760: 
        !          4761:                if(trashcan(&scfg,host_ip,"ip-silent")) {
        !          4762:                        SSH_END();
        !          4763:                        close_socket(client_socket);
        !          4764:                        continue;
        !          4765:                }
        !          4766: 
        !          4767:                lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u"
        !          4768:                        ,client_socket
        !          4769: #ifdef USE_CRYPTLIB
        !          4770:                        ,rlogin ? "RLogin" : (ssh ? "SSH" : "Telnet")
        !          4771: #else
        !          4772:                        ,rlogin ? "RLogin" : "Telnet"
        !          4773: #endif
        !          4774:                        , host_ip, ntohs(client_addr.sin_port));
        !          4775: 
        !          4776: #ifdef _WIN32
        !          4777:                if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
        !          4778:                        PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
        !          4779: #endif
        !          4780: 
        !          4781:                /* Do SSH stuff here */
        !          4782: 
        !          4783:                if(ssh) {
        !          4784:                        if(!cryptStatusOK(i=cryptCreateSession(&sbbs->ssh_session, CRYPT_UNUSED, CRYPT_SESSION_SSH_SERVER))) {
        !          4785:                                lprintf(LOG_ERR,"%04d Cryptlib error %d creating session", client_socket, i);
        !          4786:                                close_socket(client_socket);
        !          4787:                                continue;
        !          4788:                        }
        !          4789:                        if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_PRIVATEKEY, ssh_context))) {
        !          4790:                                lprintf(LOG_ERR,"%04d Cryptlib error %d setting private key",client_socket, i);
        !          4791:                                cryptDestroySession(sbbs->ssh_session);
        !          4792:                                close_socket(client_socket);
        !          4793:                                continue;
        !          4794:                        }
        !          4795:                        /* Accept any credentials */
        !          4796:                        if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_AUTHRESPONSE, 1))) {
        !          4797:                                lprintf(LOG_ERR,"%04d Cryptlib error %d setting AUTHRESPONSE",client_socket, i);
        !          4798:                                cryptDestroySession(sbbs->ssh_session);
        !          4799:                                close_socket(client_socket);
        !          4800:                                continue;
        !          4801:                        }
        !          4802:                        if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_NETWORKSOCKET, client_socket))) {
        !          4803:                                lprintf(LOG_ERR,"%04d Cryptlib error %d setting socket",client_socket, i);
        !          4804:                                cryptDestroySession(sbbs->ssh_session);
        !          4805:                                close_socket(client_socket);
        !          4806:                                continue;
        !          4807:                        }
        !          4808:                        if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_ACTIVE, 1))) {
        !          4809:                                lprintf(LOG_ERR,"%04d Cryptlib error %d setting session active",client_socket, i);
        !          4810:                                cryptDestroySession(sbbs->ssh_session);
        !          4811:                                close_socket(client_socket);
        !          4812:                                continue;
        !          4813:                        }
        !          4814:                        cryptPopData(sbbs->ssh_session, str, sizeof(str), &i);
        !          4815:                }
        !          4816: 
        !          4817:                sbbs->client_socket=client_socket;      // required for output to the user
        !          4818:         sbbs->online=ON_REMOTE;
        !          4819: 
        !          4820:                if(sbbs->trashcan(host_ip,"ip")) {
        !          4821:                        SSH_END();
        !          4822:                        close_socket(client_socket);
        !          4823:                        lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can"
        !          4824:                                ,client_socket);
        !          4825:                        SAFEPRINTF(logstr, "Blocked IP: %s",host_ip);
        !          4826:                        sbbs->syslog("@!",logstr);
        !          4827:                        continue;
        !          4828:                }
        !          4829: 
        !          4830:                if(rlogin)
        !          4831:                        sbbs->outcom(0); /* acknowledge RLogin per RFC 1282 */
        !          4832: 
        !          4833:                sbbs->putcom(crlf);
        !          4834:                sbbs->putcom(VERSION_NOTICE);
        !          4835:                sbbs->putcom(crlf);
        !          4836: 
        !          4837:                sbbs->bprintf("Connection from: %s\r\n", host_ip);
        !          4838: 
        !          4839:                struct hostent* h;
        !          4840:                if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
        !          4841:                        h=NULL;
        !          4842:                else {
        !          4843:                        sbbs->bprintf("Resolving hostname...");
        !          4844:                        h=gethostbyaddr((char *)&client_addr.sin_addr
        !          4845:                                ,sizeof(client_addr.sin_addr),AF_INET);
        !          4846:                        sbbs->putcom(crlf);
        !          4847:                }
        !          4848:                if(h!=NULL && h->h_name!=NULL)
        !          4849:                        host_name=h->h_name;
        !          4850:                else
        !          4851:                        host_name="<no name>";
        !          4852: 
        !          4853:                if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP)) {
        !          4854:                        lprintf(LOG_INFO,"%04d Hostname: %s", client_socket, host_name);
        !          4855:                        for(i=0;h!=NULL && h->h_aliases!=NULL && h->h_aliases[i]!=NULL;i++)
        !          4856:                                lprintf(LOG_INFO,"%04d HostAlias: %s", client_socket, h->h_aliases[i]);
        !          4857:                }
        !          4858: 
        !          4859:                if(sbbs->trashcan(host_name,"host")) {
        !          4860:                        SSH_END();
        !          4861:                        close_socket(client_socket);
        !          4862:                        lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can",client_socket);
        !          4863:                        SAFEPRINTF(logstr, "Blocked Hostname: %s",host_name);
        !          4864:                        sbbs->syslog("@!",logstr);
        !          4865:                        continue;
        !          4866:                }
        !          4867: 
        !          4868:                identity=NULL;
        !          4869:                if(startup->options&BBS_OPT_GET_IDENT) {
        !          4870:                        sbbs->bprintf("Resolving identity...");
        !          4871:                        identify(&client_addr, startup->telnet_port, str, sizeof(str)-1,0);
        !          4872:                        identity=strrchr(str,':');
        !          4873:                        if(identity!=NULL) {
        !          4874:                                identity++;     /* skip colon */
        !          4875:                                while(*identity && *identity<=' ') /* point to user name */
        !          4876:                                        identity++;
        !          4877:                                lprintf(LOG_INFO,"%04d Identity: %s",client_socket, identity);
        !          4878:                        }
        !          4879:                        sbbs->putcom(crlf);
        !          4880:                }
        !          4881:                /* Initialize client display */
        !          4882:                client.size=sizeof(client);
        !          4883:                client.time=time(NULL);
        !          4884:                SAFECOPY(client.addr,host_ip);
        !          4885:                SAFECOPY(client.host,host_name);
        !          4886:                client.port=ntohs(client_addr.sin_port);
        !          4887: #ifdef USE_CRYPTLIB
        !          4888:                client.protocol=rlogin ? "RLogin":(ssh ? "SSH" : "Telnet");
        !          4889: #else
        !          4890:                client.protocol=rlogin ? "RLogin":"Telnet";
        !          4891: #endif
        !          4892:                client.user="<unknown>";
        !          4893:                client_on(client_socket,&client,FALSE /* update */);
        !          4894: 
        !          4895:                for(i=first_node;i<=last_node;i++) {
        !          4896:                        /* paranoia: make sure node.status!=NODE_WFC by default */
        !          4897:                        node.status=NODE_INVALID_STATUS;        
        !          4898:                        if(sbbs->getnodedat(i,&node,1)!=0)
        !          4899:                                continue;
        !          4900:                        if(node.status==NODE_WFC) {
        !          4901:                                node.status=NODE_LOGON;
        !          4902:                                sbbs->putnodedat(i,&node);
        !          4903:                                break;
        !          4904:                        }
        !          4905:                        sbbs->putnodedat(i,&node);
        !          4906:                }
        !          4907: 
        !          4908:                if(i>last_node) {
        !          4909:                        lprintf(LOG_WARNING,"%04d !No nodes available for login.",client_socket);
        !          4910:                        SAFEPRINTF(str,"%snonodes.txt",scfg.text_dir);
        !          4911:                        if(fexist(str))
        !          4912:                                sbbs->printfile(str,P_NOABORT);
        !          4913:                        else {
        !          4914:                                sbbs->putcom("\r\nSorry, all telnet nodes are in use or otherwise unavailable.\r\n");
        !          4915:                                sbbs->putcom("Please try again later.\r\n");
        !          4916:                        }
        !          4917:                        mswait(3000);
        !          4918:                        client_off(client_socket);
        !          4919:                        SSH_END();
        !          4920:                        close_socket(client_socket);
        !          4921:                        continue;
        !          4922:                }
        !          4923: 
        !          4924:         node_socket[i-1]=client_socket;
        !          4925: 
        !          4926:                sbbs_t* new_node = new sbbs_t(i, client_addr.sin_addr.s_addr, host_name
        !          4927:                ,client_socket
        !          4928:                        ,&scfg, text, &client);
        !          4929: 
        !          4930:                new_node->client=client;
        !          4931: #ifdef USE_CRYPTLIB
        !          4932:                if(ssh) {
        !          4933:                        new_node->ssh_session=sbbs->ssh_session;
        !          4934:                        new_node->ssh_mode=true;
        !          4935:                }
        !          4936: #endif
        !          4937: 
        !          4938:                /* copy the IDENT response, if any */
        !          4939:                if(identity!=NULL)
        !          4940:                        SAFECOPY(new_node->client_ident,identity);
        !          4941: 
        !          4942:                if(new_node->init()==false) {
        !          4943:                        lprintf(LOG_INFO,"%04d !Node %d Initialization failure"
        !          4944:                                ,client_socket,new_node->cfg.node_num);
        !          4945:                        SAFEPRINTF(str,"%snonodes.txt",scfg.text_dir);
        !          4946:                        if(fexist(str))
        !          4947:                                sbbs->printfile(str,P_NOABORT);
        !          4948:                        else 
        !          4949:                                sbbs->putcom("\r\nSorry, initialization failed. Try again later.\r\n");
        !          4950:                        mswait(3000);
        !          4951:                        sbbs->getnodedat(new_node->cfg.node_num,&node,1);
        !          4952:                        node.status=NODE_WFC;
        !          4953:                        sbbs->putnodedat(new_node->cfg.node_num,&node);
        !          4954:                        delete new_node;
        !          4955:                        node_socket[i-1]=INVALID_SOCKET;
        !          4956:                        client_off(client_socket);
        !          4957:                        SSH_END();
        !          4958:                        close_socket(client_socket);
        !          4959:                        continue;
        !          4960:                }
        !          4961: 
        !          4962:                if(rlogin==true) {
        !          4963:                        new_node->connection="RLogin";
        !          4964:                        new_node->sys_status|=SS_RLOGIN;
        !          4965:                        new_node->telnet_mode|=TELNET_MODE_OFF; // RLogin does not use Telnet commands
        !          4966:                }
        !          4967: #ifdef USE_CRYPTLIB
        !          4968:                if(ssh) {
        !          4969:                        SOCKET  tmp_sock;
        !          4970:                        SOCKADDR_IN             tmp_addr={0};
        !          4971:                        socklen_t               tmp_addr_len;
        !          4972: 
        !          4973:                /* open a socket and connect to yourself */
        !          4974: 
        !          4975:                tmp_sock = open_socket(SOCK_STREAM, "passthru");
        !          4976: 
        !          4977:                        if(tmp_sock == INVALID_SOCKET) {
        !          4978:                                lprintf(LOG_ERR,"!ERROR %d creating passthru listen socket", ERROR_VALUE);
        !          4979:                                goto NO_PASSTHRU;
        !          4980:                        }
        !          4981: 
        !          4982:                lprintf(LOG_INFO,"passthru listen socket %d opened",tmp_sock);
        !          4983: 
        !          4984:                        /*****************************/
        !          4985:                        /* Listen for incoming calls */
        !          4986:                        /*****************************/
        !          4987:                memset(&tmp_addr, 0, sizeof(tmp_addr));
        !          4988: 
        !          4989:                        tmp_addr.sin_addr.s_addr = htonl(0x7f000001U);
        !          4990:                tmp_addr.sin_family = AF_INET;
        !          4991:                tmp_addr.sin_port   = 0;
        !          4992: 
        !          4993:                result = bind(tmp_sock,(struct sockaddr *)&tmp_addr,sizeof(tmp_addr));
        !          4994:                        if(result != 0) {
        !          4995:                                lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
        !          4996:                                close_socket(tmp_sock);
        !          4997:                                goto NO_PASSTHRU;
        !          4998:                        }
        !          4999: 
        !          5000:                    result = listen(tmp_sock, 1);
        !          5001: 
        !          5002:                        if(result != 0) {
        !          5003:                                lprintf(LOG_ERR,"!ERROR %d (%d) listening on passthru socket", result, ERROR_VALUE);
        !          5004:                                close_socket(tmp_sock);
        !          5005:                                goto NO_PASSTHRU;
        !          5006:                        }
        !          5007:                        lprintf(LOG_INFO,"Listening passthru socket listening on port %d",htons(tmp_addr.sin_port));
        !          5008: 
        !          5009:                new_node->passthru_socket = open_socket(SOCK_STREAM, "passthru");
        !          5010: 
        !          5011:                        if(new_node->passthru_socket == INVALID_SOCKET) {
        !          5012:                                lprintf(LOG_ERR,"!ERROR %d creating passthru connecting socket", ERROR_VALUE);
        !          5013:                                close_socket(tmp_sock);
        !          5014:                                goto NO_PASSTHRU;
        !          5015:                        }
        !          5016: 
        !          5017:                lprintf(LOG_INFO,"passthru connect socket %d opened",new_node->passthru_socket);
        !          5018: 
        !          5019:                        tmp_addr_len=sizeof(tmp_addr);
        !          5020:                        if(getsockname(tmp_sock, (struct sockaddr *)&tmp_addr, &tmp_addr_len)) {
        !          5021:                                lprintf(LOG_ERR,"!ERROR %d getting passthru listener address", ERROR_VALUE);
        !          5022:                                close_socket(tmp_sock);
        !          5023:                                close_socket(new_node->passthru_socket);
        !          5024:                                new_node->passthru_socket=INVALID_SOCKET;
        !          5025:                                goto NO_PASSTHRU;
        !          5026:                        }
        !          5027: 
        !          5028:                    result = connect(new_node->passthru_socket, (struct sockaddr *)&tmp_addr, tmp_addr_len);
        !          5029: 
        !          5030:                        if(result != 0) {
        !          5031:                                lprintf(LOG_ERR,"!ERROR %d (%d) connecting to passthru socket", result, ERROR_VALUE);
        !          5032:                                close_socket(new_node->passthru_socket);
        !          5033:                                new_node->passthru_socket=INVALID_SOCKET;
        !          5034:                                close_socket(tmp_sock);
        !          5035:                                goto NO_PASSTHRU;
        !          5036:                        }
        !          5037: 
        !          5038:                        new_node->client_socket_dup=accept(tmp_sock, (struct sockaddr *)&tmp_addr, &tmp_addr_len);
        !          5039: 
        !          5040:                        if(new_node->client_socket_dup == INVALID_SOCKET) {
        !          5041:                                lprintf(LOG_ERR,"!ERROR (%d) connecting accept()ing on passthru socket", ERROR_VALUE);
        !          5042:                                lprintf(LOG_WARNING,"!WARNING native doors which use sockets will not function");
        !          5043:                                close_socket(new_node->passthru_socket);
        !          5044:                                new_node->passthru_socket=INVALID_SOCKET;
        !          5045:                                close_socket(tmp_sock);
        !          5046:                                goto NO_PASSTHRU;
        !          5047:                        }
        !          5048:                        close_socket(tmp_sock);
        !          5049:                        _beginthread(passthru_output_thread, 0, new_node);
        !          5050:                        _beginthread(passthru_input_thread, 0, new_node);
        !          5051: 
        !          5052: NO_PASSTHRU:
        !          5053:                        new_node->connection="SSH";
        !          5054:                        new_node->sys_status|=SS_SSH;
        !          5055:                        new_node->telnet_mode|=TELNET_MODE_OFF; // SSH does not use Telnet commands
        !          5056:                        new_node->ssh_session=sbbs->ssh_session;
        !          5057:                        /* Wait for pending data to be sent then turn off ssh_mode for uber-output */
        !          5058:                        while(RingBufFull(&sbbs->outbuf))
        !          5059:                                SLEEP(1);
        !          5060:                        sbbs->ssh_mode=false;
        !          5061:                }
        !          5062: #endif
        !          5063: 
        !          5064:            node_threads_running++;
        !          5065:                new_node->input_thread=(HANDLE)_beginthread(input_thread,0, new_node);
        !          5066:                _beginthread(output_thread, 0, new_node);
        !          5067:                _beginthread(node_thread, 0, new_node);
        !          5068:                served++;
        !          5069:        }
        !          5070: 
        !          5071:     // Close all open sockets
        !          5072:     for(i=0;i<MAX_NODES;i++)  {
        !          5073:        if(node_socket[i]!=INVALID_SOCKET) {
        !          5074:                lprintf(LOG_INFO,"Closing node %d socket %d", i+1, node_socket[i]);
        !          5075:                close_socket(node_socket[i]);
        !          5076:                        node_socket[i]=INVALID_SOCKET;
        !          5077:         }
        !          5078: #ifdef __unix__
        !          5079:                if(uspy_listen_socket[i]!=INVALID_SOCKET) {
        !          5080:                        close_socket(uspy_listen_socket[i]);
        !          5081:                        uspy_listen_socket[i]=INVALID_SOCKET;
        !          5082:                        snprintf(str,sizeof(uspy_addr.sun_path),"%slocalspy%d.sock", startup->temp_dir, i+1);
        !          5083:                        if(fexist(str))
        !          5084:                                unlink(str);
        !          5085:                }
        !          5086:                if(uspy_socket[i]!=INVALID_SOCKET) {
        !          5087:                        close_socket(uspy_socket[i]);
        !          5088:                        uspy_socket[i]=INVALID_SOCKET;
        !          5089:                }               
        !          5090: #endif
        !          5091:        }
        !          5092: 
        !          5093:        sbbs->client_socket=INVALID_SOCKET;
        !          5094:        if(events!=NULL)
        !          5095:                events->terminated=true;
        !          5096:     // Wake-up BBS output thread so it can terminate
        !          5097:     sem_post(&sbbs->outbuf.sem);
        !          5098: 
        !          5099:     // Wait for all node threads to terminate
        !          5100:        if(node_threads_running) {
        !          5101:                lprintf(LOG_INFO,"Waiting for %d node threads to terminate...", node_threads_running);
        !          5102:                start=time(NULL);
        !          5103:                while(node_threads_running) {
        !          5104:                        if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
        !          5105:                                lprintf(LOG_ERR,"!TIMEOUT waiting for %d node thread(s) to "
        !          5106:                        "terminate", node_threads_running);
        !          5107:                                break;
        !          5108:                        }
        !          5109:                        mswait(100);
        !          5110:                }
        !          5111:        }
        !          5112: 
        !          5113:        // Wait for Events thread to terminate
        !          5114:        if(events!=NULL && events->event_thread_running) {
        !          5115:                lprintf(LOG_INFO,"Waiting for event thread to terminate...");
        !          5116:                start=time(NULL);
        !          5117:                while(events->event_thread_running) {
        !          5118: #if 0 /* the event thread can/will segfault if it continues to run and dereference sbbs->cfg */
        !          5119:                        if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
        !          5120:                                lprintf(LOG_ERR,"!TIMEOUT waiting for BBS event thread to "
        !          5121:                        "terminate");
        !          5122:                                break;
        !          5123:                        }
        !          5124: #endif
        !          5125:                        mswait(100);
        !          5126:                }
        !          5127:        }
        !          5128: 
        !          5129:     // Wait for BBS output thread to terminate
        !          5130:        if(sbbs->output_thread_running) {
        !          5131:                lprintf(LOG_INFO,"Waiting for system output thread to terminate...");
        !          5132:                start=time(NULL);
        !          5133:                while(sbbs->output_thread_running) {
        !          5134:                        if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
        !          5135:                                lprintf(LOG_ERR,"!TIMEOUT waiting for BBS output thread to "
        !          5136:                        "terminate");
        !          5137:                                break;
        !          5138:                        }
        !          5139:                        mswait(100);
        !          5140:                }
        !          5141:        }
        !          5142: 
        !          5143:     // Set all nodes' status to OFFLINE
        !          5144:     for(i=first_node;i<=last_node;i++) {
        !          5145:         sbbs->getnodedat(i,&node,1);
        !          5146:         node.status=NODE_OFFLINE;
        !          5147:         sbbs->putnodedat(i,&node);
        !          5148:     }
        !          5149: 
        !          5150:        if(events!=NULL && !events->event_thread_running)
        !          5151:                delete events;
        !          5152: 
        !          5153:     if(!sbbs->output_thread_running)
        !          5154:            delete sbbs;
        !          5155: 
        !          5156:        cleanup(0);
        !          5157: 
        !          5158:        if(!terminate_server) {
        !          5159:                lprintf(LOG_INFO,"Recycling server...");
        !          5160:                mswait(2000);
        !          5161:                if(startup->recycle!=NULL)
        !          5162:                        startup->recycle(startup->cbdata);
        !          5163:        }
        !          5164: 
        !          5165:        } while(!terminate_server);
        !          5166: 
        !          5167: }
        !          5168: 
        !          5169: 
        !          5170: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.