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

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

unix.superglobalmegacorp.com

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