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

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

unix.superglobalmegacorp.com

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