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

1.1       root        1: /* xtrn.cpp */
                      2: 
                      3: /* Synchronet external program support routines */
                      4: 
                      5: /* $Id: xtrn.cpp,v 1.199 2006/11/16 20:41:06 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 "cmdshell.h"
                     40: #include "telnet.h"
                     41: 
                     42: #include <signal.h>                    // kill()
                     43: 
                     44: #ifdef __unix__
                     45:        #include <sys/wait.h>   // WEXITSTATUS
                     46: 
                     47:        #define TTYDEFCHARS             // needed for ttydefchars definition
                     48: 
                     49: #if defined(__FreeBSD__)
                     50:        #include <libutil.h>    // forkpty()
                     51: #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DARWIN__)
                     52:        #include <util.h>
                     53: #elif defined(__linux__)
                     54:        #include <pty.h>
                     55: #elif defined(__QNX__)
                     56: #if 0
                     57:        #include <unix.h>
                     58: #else
                     59:        #define NEEDS_FORKPTY
                     60: #endif
                     61: #endif
                     62: 
                     63:        #ifdef NEEDS_FORKPTY
                     64:        #include <grp.h>
                     65:        #endif
                     66: 
                     67:        #include <termios.h>
                     68: 
                     69: /*
                     70:  * Control Character Defaults
                     71:  */
                     72: #ifndef CTRL
                     73:        #define CTRL(x) (x&037)
                     74: #endif
                     75: #ifndef CEOF
                     76:        #define CEOF            CTRL('d')
                     77: #endif
                     78: #ifndef CEOL
                     79:        #define CEOL            0xff            /* XXX avoid _POSIX_VDISABLE */
                     80: #endif
                     81: #ifndef CERASE
                     82:        #define CERASE          0177
                     83: #endif
                     84: #ifndef CERASE2
                     85:        #define CERASE2         CTRL('h')
                     86: #endif
                     87: #ifndef CINTR
                     88:        #define CINTR           CTRL('c')
                     89: #endif
                     90: #ifndef CSTATUS
                     91:        #define CSTATUS         CTRL('t')
                     92: #endif
                     93: #ifndef CKILL
                     94:        #define CKILL           CTRL('u')
                     95: #endif
                     96: #ifndef CMIN
                     97:        #define CMIN            1
                     98: #endif
                     99: #ifndef CQUIT
                    100:        #define CQUIT           034             /* FS, ^\ */
                    101: #endif
                    102: #ifndef CSUSP
                    103:        #define CSUSP           CTRL('z')
                    104: #endif
                    105: #ifndef CTIME
                    106:        #define CTIME           0
                    107: #endif
                    108: #ifndef CDSUSP
                    109:        #define CDSUSP          CTRL('y')
                    110: #endif
                    111: #ifndef CSTART
                    112:        #define CSTART          CTRL('q')
                    113: #endif
                    114: #ifndef CSTOP
                    115:        #define CSTOP           CTRL('s')
                    116: #endif
                    117: #ifndef CLNEXT
                    118:        #define CLNEXT          CTRL('v')
                    119: #endif
                    120: #ifndef CDISCARD
                    121:        #define CDISCARD        CTRL('o')
                    122: #endif
                    123: #ifndef CWERASE
                    124:        #define CWERASE         CTRL('w')
                    125: #endif
                    126: #ifndef CREPRINT
                    127:        #define CREPRINT        CTRL('r')
                    128: #endif
                    129: #ifndef CEOT
                    130:        #define CEOT            CEOF
                    131: #endif
                    132: /* compat */
                    133: #ifndef CBRK
                    134:        #define CBRK            CEOL
                    135: #endif
                    136: #ifndef CRPRNT
                    137:        #define CRPRNT          CREPRINT
                    138: #endif
                    139: #ifndef CFLUSH
                    140:        #define CFLUSH          CDISCARD
                    141: #endif
                    142: 
                    143: #ifndef TTYDEF_IFLAG
                    144:        #define TTYDEF_IFLAG    (BRKINT | ICRNL | IMAXBEL | IXON | IXANY)
                    145: #endif
                    146: #ifndef TTYDEF_OFLAG
                    147:        #define TTYDEF_OFLAG    (OPOST | ONLCR)
                    148: #endif
                    149: #ifndef TTYDEF_LFLAG
                    150:        #define TTYDEF_LFLAG    (ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL)
                    151: #endif
                    152: #ifndef TTYDEF_CFLAG
                    153:        #define TTYDEF_CFLAG    (CREAD | CS8 | HUPCL)
                    154: #endif
                    155: #if defined(__QNX__) || defined(__solaris__) || defined(__NetBSD__)
                    156:        static cc_t     ttydefchars[NCCS] = {
                    157:         CEOF,   CEOL,   CEOL,   CERASE, CWERASE, CKILL, CREPRINT,
                    158:         CERASE2, CINTR, CQUIT,  CSUSP,  CDSUSP, CSTART, CSTOP,  CLNEXT,
                    159:         CDISCARD, CMIN, CTIME,  CSTATUS
                    160: #ifndef __solaris__
                    161:        , _POSIX_VDISABLE
                    162: #endif
                    163:        };
                    164: #endif
                    165: 
                    166: #endif /* __unix__ */
                    167: 
                    168: #define XTRN_IO_BUF_LEN 10000  /* 50% of IO_THREAD_BUF_SIZE */
                    169: 
                    170: /*****************************************************************************/
                    171: /* Interrupt routine to expand WWIV Ctrl-C# codes into ANSI escape sequences */
                    172: /*****************************************************************************/
                    173: BYTE* wwiv_expand(BYTE* buf, ulong buflen, BYTE* outbuf, ulong& newlen
                    174:        ,ulong user_misc, bool& ctrl_c)
                    175: {
                    176:     char       ansi_seq[32];
                    177:        ulong   i,j,k;
                    178: 
                    179:     for(i=j=0;i<buflen;i++) {
                    180:         if(buf[i]==CTRL_C) {   /* WWIV color escape char */
                    181:             ctrl_c=true;
                    182:             continue;
                    183:         }
                    184:         if(!ctrl_c) {
                    185:             outbuf[j++]=buf[i];
                    186:             continue;
                    187:         }
                    188:         ctrl_c=false;
                    189:         if(user_misc&ANSI) {
                    190:             switch(buf[i]) {
                    191:                 default:
                    192:                     strcpy(ansi_seq,"\x1b[0m");          /* low grey */
                    193:                     break;
                    194:                 case '1':
                    195:                     strcpy(ansi_seq,"\x1b[0;1;36m");     /* high cyan */
                    196:                     break;
                    197:                 case '2':
                    198:                     strcpy(ansi_seq,"\x1b[0;1;33m");     /* high yellow */
                    199:                     break;
                    200:                 case '3':
                    201:                     strcpy(ansi_seq,"\x1b[0;35m");       /* low magenta */
                    202:                     break;
                    203:                 case '4':
                    204:                     strcpy(ansi_seq,"\x1b[0;1;44m");     /* white on blue */
                    205:                     break;
                    206:                 case '5':
                    207:                     strcpy(ansi_seq,"\x1b[0;32m");       /* low green */
                    208:                     break;
                    209:                 case '6':
                    210:                     strcpy(ansi_seq,"\x1b[0;1;5;31m");   /* high blinking red */
                    211:                     break;
                    212:                 case '7':
                    213:                     strcpy(ansi_seq,"\x1b[0;1;34m");     /* high blue */
                    214:                     break;
                    215:                 case '8':
                    216:                     strcpy(ansi_seq,"\x1b[0;34m");       /* low blue */
                    217:                     break;
                    218:                 case '9':
                    219:                     strcpy(ansi_seq,"\x1b[0;36m");       /* low cyan */
                    220:                     break;
                    221:             }
                    222:             for(k=0;ansi_seq[k];k++)
                    223:                 outbuf[j++]=ansi_seq[k];
                    224:         }
                    225:     }
                    226:     newlen=j;
                    227:     return(outbuf);
                    228: }
                    229: 
                    230: /*****************************************************************************/
                    231: // Escapes Telnet IAC (255) by doubling the IAC char
                    232: /*****************************************************************************/
                    233: BYTE* telnet_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen)
                    234: {
                    235:        BYTE*   first_iac;
                    236:        ulong   i,outlen;
                    237: 
                    238:     first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen);
                    239: 
                    240:        if(first_iac==NULL) {   /* Nothing to expand */
                    241:                newlen=inlen;
                    242:                return(inbuf);
                    243:        }
                    244: 
                    245:        outlen=first_iac-inbuf;
                    246:        memcpy(outbuf, inbuf, outlen);
                    247: 
                    248:     for(i=outlen;i<inlen;i++) {
                    249:                if(inbuf[i]==TELNET_IAC)
                    250:                        outbuf[outlen++]=TELNET_IAC;
                    251:                outbuf[outlen++]=inbuf[i];
                    252:        }
                    253:     newlen=outlen;
                    254:     return(outbuf);
                    255: }
                    256: 
                    257: static bool native_executable(scfg_t* cfg, const char* cmdline, long mode)
                    258: {
                    259:        char*   p;
                    260:        char    str[MAX_PATH+1];
                    261:        char    name[64];
                    262:        char    base[64];
                    263:        unsigned i;
                    264: 
                    265:        if(mode&EX_NATIVE)
                    266:                return(TRUE);
                    267: 
                    268:     SAFECOPY(str,cmdline);                             /* Set str to program name only */
                    269:        truncstr(str," ");
                    270:     SAFECOPY(name,getfname(str));
                    271:        SAFECOPY(base,name);
                    272:        if((p=getfext(base))!=NULL)
                    273:                *p=0;
                    274: 
                    275:     for(i=0;i<cfg->total_natvpgms;i++)
                    276:         if(stricmp(name,cfg->natvpgm[i]->name)==0
                    277:                || stricmp(base,cfg->natvpgm[i]->name)==0)
                    278:             break;
                    279:     return(i<cfg->total_natvpgms);
                    280: }
                    281: 
                    282: #define XTRN_LOADABLE_MODULE                                                           \
                    283:        if(cmdline[0]=='*')             /* Baja module or JavaScript */ \
                    284:                return(exec_bin(cmdline+1,&main_csi))                           
                    285: #ifdef JAVASCRIPT
                    286:        #define XTRN_LOADABLE_JS_MODULE                                                 \
                    287:        if(cmdline[0]=='?')     /* JavaScript */                                \
                    288:                return(js_execfile(cmdline+1))                                          
                    289: #else
                    290:        #define XTRN_LOADABLE_JS_MODULE
                    291: #endif
                    292: 
                    293: #ifdef _WIN32
                    294: 
                    295: #include "execvxd.h"   /* Win9X FOSSIL VxD API */
                    296: 
                    297: extern SOCKET node_socket[];
                    298: 
                    299: // -------------------------------------------------------------------------
                    300: // GetAddressOfOpenVxDHandle
                    301: //
                    302: // This function returns the address of OpenVxDHandle. OpenVxDHandle is a 
                    303: // KERNEL32 function that returns a ring 0 event handle that corresponds to a
                    304: // given ring 3 event handle. The ring 0 handle can be used by VxDs to
                    305: // synchronize with the Win32 app.
                    306: //
                    307: typedef HANDLE (WINAPI *OPENVXDHANDLE)(HANDLE);
                    308: 
                    309: OPENVXDHANDLE GetAddressOfOpenVxDHandle(void)
                    310: {
                    311:        return((OPENVXDHANDLE)GetProcAddress(hK32, "OpenVxDHandle"));
                    312: }
                    313: 
                    314: /*****************************************************************************/
                    315: // Expands Single CR to CRLF
                    316: /*****************************************************************************/
                    317: BYTE* cr_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen)
                    318: {
                    319:        ulong   i,j;
                    320: 
                    321:        for(i=j=0;i<inlen;i++) {
                    322:                outbuf[j++]=inbuf[i];
                    323:                if(inbuf[i]=='\r')
                    324:                        outbuf[j++]='\n';
                    325:        }
                    326:        newlen=j;
                    327:     return(outbuf);
                    328: }
                    329: 
                    330: static void add_env_var(str_list_t* list, const char* var, const char* val)
                    331: {
                    332:        char    str[MAX_PATH*2];
                    333:        SetEnvironmentVariable(var,NULL);       /* Delete in current process env */
                    334:        SAFEPRINTF2(str,"%s=%s",var,val);
                    335:        strListPush(list,str);
                    336: }
                    337: 
                    338: /* Clean-up resources while preserving current LastError value */
                    339: #define XTRN_CLEANUP                                                                                           \
                    340:        last_error=GetLastError();                                                                              \
                    341:     if(vxd!=INVALID_HANDLE_VALUE)              CloseHandle(vxd);                       \
                    342:        if(rdslot!=INVALID_HANDLE_VALUE)        CloseHandle(rdslot);            \
                    343:        if(wrslot!=INVALID_HANDLE_VALUE)        CloseHandle(wrslot);            \
                    344:        if(start_event!=NULL)                           CloseHandle(start_event);       \
                    345:        if(hungup_event!=NULL)                          CloseHandle(hungup_event);      \
                    346:        if(hangup_event!=NULL)                          CloseHandle(hangup_event);      \
                    347:        ReleaseMutex(exec_mutex);                                                                               \
                    348:        SetLastError(last_error)
                    349: 
                    350: /****************************************************************************/
                    351: /* Runs an external program                                                                                            */
                    352: /****************************************************************************/
                    353: int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
                    354: {
                    355:        char    str[MAX_PATH+1];
                    356:        char*   env_block=NULL;
                    357:        char*   env_strings;
                    358:        const char* p_startup_dir;
                    359:        char    path[MAX_PATH+1];
                    360:     char       fullcmdline[MAX_PATH+1];
                    361:        char    realcmdline[MAX_PATH+1];
                    362:        char    comspec_str[MAX_PATH+1];
                    363:        char    title[MAX_PATH+1];
                    364:        BYTE    buf[XTRN_IO_BUF_LEN],*bp;
                    365:     BYTE       telnet_buf[XTRN_IO_BUF_LEN*2];
                    366:     BYTE       output_buf[XTRN_IO_BUF_LEN*2];
                    367:     BYTE       wwiv_buf[XTRN_IO_BUF_LEN*2];
                    368:     bool       wwiv_flag=false;
                    369:     bool       native=false;                   // DOS program by default
                    370:        bool    nt=false;                               // WinNT/2K? 
                    371:     bool       was_online=true;
                    372:        bool    rio_abortable_save=rio_abortable;
                    373:        bool    use_pipes=false;        // NT-compatible console redirection
                    374:        BOOL    success;
                    375:        BOOL    processTerminated=false;
                    376:        uint    i;
                    377:     time_t     hungup=0;
                    378:        HANDLE  vxd=INVALID_HANDLE_VALUE;
                    379:        HANDLE  rdslot=INVALID_HANDLE_VALUE;
                    380:        HANDLE  wrslot=INVALID_HANDLE_VALUE;
                    381:        HANDLE  start_event=NULL;
                    382:        HANDLE  hungup_event=NULL;
                    383:        HANDLE  hangup_event=NULL;
                    384:        HANDLE  rdoutpipe;
                    385:        HANDLE  wrinpipe;
                    386:     PROCESS_INFORMATION process_info;
                    387:        DWORD   hVM;
                    388:        DWORD   rd;
                    389:     DWORD      wr;
                    390:     DWORD      len;
                    391:     DWORD      avail;
                    392:        DWORD   dummy;
                    393:        DWORD   msglen;
                    394:        DWORD   retval;
                    395:        DWORD   last_error;
                    396:        DWORD   loop_since_io=0;
                    397:        struct  tm tm;
                    398:        str_list_t      env_list;
                    399:        sbbsexec_start_t start;
                    400:        OPENVXDHANDLE OpenVxDHandle;
                    401: 
                    402:        if(online==ON_LOCAL)
                    403:                eprintf(LOG_INFO,"Executing external: %s",cmdline);
                    404:        else
                    405:                lprintf(LOG_INFO,"Node %d Executing external: %s",cfg.node_num,cmdline);
                    406: 
                    407:        XTRN_LOADABLE_MODULE;
                    408:        XTRN_LOADABLE_JS_MODULE;
                    409: 
                    410:        attr(cfg.color[clr_external]);          /* setup default attributes */
                    411: 
                    412:        native = native_executable(&cfg, cmdline, mode);
                    413: 
                    414:        if(mode&EX_SH || strcspn(cmdline,"<>|")!=strlen(cmdline)) 
                    415:                sprintf(comspec_str,"%s /C ", comspec);
                    416:        else
                    417:                comspec_str[0]=0;
                    418: 
                    419:     if(startup_dir && cmdline[1]!=':' && cmdline[0]!='/'
                    420:        && cmdline[0]!='\\' && cmdline[0]!='.')
                    421:                sprintf(fullcmdline, "%s%s%s", comspec_str, startup_dir, cmdline);
                    422:     else
                    423:        sprintf(fullcmdline, "%s%s", comspec_str, cmdline);
                    424: 
                    425:        SAFECOPY(realcmdline, fullcmdline);     // for errormsg if failed to execute
                    426: 
                    427:        now=time(NULL);
                    428:        if(localtime_r(&now,&tm)==NULL)
                    429:                memset(&tm,0,sizeof(tm));
                    430: 
                    431:        OpenVxDHandle=GetAddressOfOpenVxDHandle();
                    432: 
                    433:        if(OpenVxDHandle==NULL) 
                    434:                nt=true;        // Windows NT/2000
                    435: 
                    436:        if(!nt && !native && !(cfg.xtrn_misc&XTRN_NO_MUTEX)
                    437:                && (retval=WaitForSingleObject(exec_mutex,5000))!=WAIT_OBJECT_0) {
                    438:                errormsg(WHERE, ERR_TIMEOUT, "exec_mutex", retval);
                    439:                return(GetLastError());
                    440:        }
                    441: 
                    442:        if(native && mode&EX_OUTR && !(mode&EX_OFFLINE))
                    443:                use_pipes=true;
                    444: 
                    445:        if(native) { // Native (32-bit) external
                    446: 
                    447:                if((env_list=strListInit())==NULL) {
                    448:                        XTRN_CLEANUP;
                    449:                errormsg(WHERE, ERR_CREATE, "env_list", 0);
                    450:             return(errno);
                    451:                }
                    452: 
                    453:                // Current environment passed to child process
                    454:                sprintf(str,"%sprotocol.log",cfg.node_dir);                     
                    455:                add_env_var(&env_list,"DSZLOG",str);
                    456:                add_env_var(&env_list,"SBBSNODE",cfg.node_dir);
                    457:                add_env_var(&env_list,"SBBSCTRL",cfg.ctrl_dir);
                    458:                add_env_var(&env_list,"SBBSDATA",cfg.data_dir);
                    459:                add_env_var(&env_list,"SBBSEXEC",cfg.exec_dir);
                    460:                sprintf(str,"%d",cfg.node_num);
                    461:                add_env_var(&env_list,"SBBSNNUM",str);
                    462:                /* date/time env vars */
                    463:                sprintf(str,"%02u",tm.tm_mday); 
                    464:                add_env_var(&env_list,"DAY",str);
                    465:                add_env_var(&env_list,"WEEKDAY",wday[tm.tm_wday]);
                    466:                add_env_var(&env_list,"MONTHNAME",mon[tm.tm_mon]);
                    467:                sprintf(str,"%02u",tm.tm_mon+1);
                    468:                add_env_var(&env_list,"MONTH",str);
                    469:                sprintf(str,"%u",1900+tm.tm_year);
                    470:                add_env_var(&env_list,"YEAR",str);
                    471: 
                    472:                env_strings=GetEnvironmentStrings();
                    473:                env_block=strListCopyBlock(env_strings);
                    474:                if(env_strings!=NULL)
                    475:                        FreeEnvironmentStrings(env_strings);
                    476:                env_block=strListAppendBlock(env_block,env_list);
                    477:                strListFree(&env_list);
                    478:                if(env_block==NULL) {
                    479:                        XTRN_CLEANUP;
                    480:                errormsg(WHERE, ERR_CREATE, "env_block", 0);
                    481:             return(errno);
                    482:                }
                    483: 
                    484:     } else { // DOS external
                    485: 
                    486:                // DOS-compatible (short) paths
                    487:                char node_dir[MAX_PATH+1];
                    488:                char ctrl_dir[MAX_PATH+1];
                    489:                char data_dir[MAX_PATH+1];
                    490:                char exec_dir[MAX_PATH+1];
                    491: 
                    492:                // in case GetShortPathName fails
                    493:                SAFECOPY(node_dir,cfg.node_dir);
                    494:                SAFECOPY(ctrl_dir,cfg.ctrl_dir);
                    495:                SAFECOPY(data_dir,cfg.data_dir);
                    496:                SAFECOPY(exec_dir,cfg.exec_dir);
                    497: 
                    498:                GetShortPathName(cfg.node_dir,node_dir,sizeof(node_dir));
                    499:                GetShortPathName(cfg.ctrl_dir,ctrl_dir,sizeof(node_dir));
                    500:                GetShortPathName(cfg.data_dir,data_dir,sizeof(data_dir));
                    501:                GetShortPathName(cfg.exec_dir,exec_dir,sizeof(exec_dir));
                    502: 
                    503:                sprintf(path,"%sDOSXTRN.RET", cfg.node_dir);
                    504:                remove(path);
                    505: 
                    506:        // Create temporary environment file
                    507:        sprintf(path,"%sDOSXTRN.ENV", node_dir);
                    508:         FILE* fp=fopen(path,"w");
                    509:         if(fp==NULL) {
                    510:                        XTRN_CLEANUP;
                    511:                errormsg(WHERE, ERR_CREATE, path, 0);
                    512:             return(errno);
                    513:         }
                    514:         fprintf(fp, "%s\n", fullcmdline);
                    515:                fprintf(fp, "DSZLOG=%sPROTOCOL.LOG\n", node_dir);
                    516:         fprintf(fp, "SBBSNODE=%s\n", node_dir);
                    517:         fprintf(fp, "SBBSCTRL=%s\n", ctrl_dir);
                    518:                fprintf(fp, "SBBSDATA=%s\n", data_dir);
                    519:                fprintf(fp, "SBBSEXEC=%s\n", exec_dir);
                    520:         fprintf(fp, "SBBSNNUM=%d\n", cfg.node_num);
                    521:                /* date/time env vars */
                    522:                fprintf(fp, "DAY=%02u\n", tm.tm_mday);
                    523:                fprintf(fp, "WEEKDAY=%s\n",wday[tm.tm_wday]);
                    524:                fprintf(fp, "MONTHNAME=%s\n",mon[tm.tm_mon]);
                    525:                fprintf(fp, "MONTH=%02u\n",tm.tm_mon+1);
                    526:                fprintf(fp, "YEAR=%u\n",1900+tm.tm_year);
                    527:         fclose(fp);
                    528: 
                    529:         sprintf(fullcmdline, "%sDOSXTRN.EXE %s", cfg.exec_dir, path);
                    530: 
                    531:                if(!(mode&EX_OFFLINE) && nt) {  // Windows NT/2000
                    532:                        i=SBBSEXEC_MODE_FOSSIL;
                    533:                        if(mode&EX_INR)
                    534:                        i|=SBBSEXEC_MODE_DOS_IN;
                    535:                        if(mode&EX_OUTR)
                    536:                        i|=SBBSEXEC_MODE_DOS_OUT;
                    537:                        sprintf(str," NT %u %u"
                    538:                                ,cfg.node_num,i);
                    539:                        strcat(fullcmdline,str);
                    540: 
                    541:                        sprintf(str,"sbbsexec_hungup%d",cfg.node_num);
                    542:                        if((hungup_event=CreateEvent(
                    543:                                 NULL   // pointer to security attributes
                    544:                                ,TRUE   // flag for manual-reset event
                    545:                                ,FALSE  // flag for initial state
                    546:                                ,str    // pointer to event-object name
                    547:                                ))==NULL) {
                    548:                                XTRN_CLEANUP;
                    549:                                errormsg(WHERE, ERR_CREATE, str, 0);
                    550:                                return(GetLastError());
                    551:                        }
                    552: 
                    553:                        sprintf(str,"sbbsexec_hangup%d",cfg.node_num);
                    554:                        if((hangup_event=CreateEvent(
                    555:                                 NULL   // pointer to security attributes
                    556:                                ,TRUE   // flag for manual-reset event
                    557:                                ,FALSE  // flag for initial state
                    558:                                ,str    // pointer to event-object name
                    559:                                ))==NULL) {
                    560:                                XTRN_CLEANUP;
                    561:                                errormsg(WHERE, ERR_CREATE, str, 0);
                    562:                                return(GetLastError());
                    563:                        }
                    564: 
                    565:                        sprintf(str,"\\\\.\\mailslot\\sbbsexec\\rd%d"
                    566:                                ,cfg.node_num);
                    567:                        rdslot=CreateMailslot(str
                    568:                                ,sizeof(buf)/2                  // Maximum message size (0=unlimited)
                    569:                                ,0                                              // Read time-out
                    570:                                ,NULL);                 // Security
                    571:                        if(rdslot==INVALID_HANDLE_VALUE) {
                    572:                                XTRN_CLEANUP;
                    573:                                errormsg(WHERE, ERR_CREATE, str, 0);
                    574:                                return(GetLastError());
                    575:                        }
                    576:                }
                    577:                else if(!(mode&EX_OFFLINE)) {
                    578: 
                    579:                        // Load vxd to intercept interrupts
                    580: 
                    581:                        sprintf(str,"\\\\.\\%s%s",cfg.exec_dir, SBBSEXEC_VXD);
                    582:                        if((vxd=CreateFile(str,0,0,0
                    583:                                ,CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE,0))
                    584:                                 ==INVALID_HANDLE_VALUE) {
                    585:                                XTRN_CLEANUP;
                    586:                                errormsg(WHERE, ERR_OPEN, str, 0);
                    587:                                return(GetLastError());
                    588:                        }
                    589: 
                    590:                        if((start_event=CreateEvent(
                    591:                                 NULL   // pointer to security attributes
                    592:                                ,TRUE   // flag for manual-reset event
                    593:                                ,FALSE  // flag for initial state
                    594:                                ,NULL   // pointer to event-object name
                    595:                                ))==NULL) {
                    596:                                XTRN_CLEANUP;
                    597:                                errormsg(WHERE, ERR_CREATE, "exec start event", 0);
                    598:                                return(GetLastError());
                    599:                        }
                    600: 
                    601:                        if(OpenVxDHandle!=NULL)
                    602:                                start.event=OpenVxDHandle(start_event);
                    603:                        else
                    604:                                start.event=start_event;
                    605: 
                    606:                        start.mode=SBBSEXEC_MODE_FOSSIL;
                    607:                        if(mode&EX_INR)
                    608:                        start.mode|=SBBSEXEC_MODE_DOS_IN;
                    609:                        if(mode&EX_OUTR)
                    610:                        start.mode|=SBBSEXEC_MODE_DOS_OUT;
                    611: 
                    612:                        sprintf(str," 95 %u %u"
                    613:                                ,cfg.node_num,start.mode);
                    614:                        strcat(fullcmdline,str);
                    615: 
                    616:                        if(!DeviceIoControl(
                    617:                                vxd,                                    // handle to device of interest
                    618:                                SBBSEXEC_IOCTL_START,   // control code of operation to perform
                    619:                                &start,                                 // pointer to buffer to supply input data
                    620:                                sizeof(start),                  // size of input buffer
                    621:                                NULL,                                   // pointer to buffer to receive output data
                    622:                                0,                                              // size of output buffer
                    623:                                &rd,                                    // pointer to variable to receive output byte count
                    624:                                NULL                                    // Overlapped I/O
                    625:                                )) {
                    626:                                XTRN_CLEANUP;
                    627:                                errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_START);
                    628:                                return(GetLastError());
                    629:                        }
                    630:                }
                    631:     }
                    632: 
                    633:        if(startup_dir!=NULL && startup_dir[0])
                    634:                p_startup_dir=startup_dir;
                    635:        else
                    636:                p_startup_dir=NULL;
                    637:     STARTUPINFO startup_info={0};
                    638:     startup_info.cb=sizeof(startup_info);
                    639:        if(mode&EX_OFFLINE)
                    640:                startup_info.lpTitle=NULL;
                    641:        else {
                    642:                sprintf(title,"%s running %s on node %d"
                    643:                        ,useron.number ? useron.alias : "Event"
                    644:                        ,realcmdline
                    645:                        ,cfg.node_num);
                    646:                startup_info.lpTitle=title;
                    647:        }
                    648:     if(startup->options&BBS_OPT_XTRN_MINIMIZED) {
                    649:        startup_info.wShowWindow=SW_SHOWMINNOACTIVE;
                    650:         startup_info.dwFlags|=STARTF_USESHOWWINDOW;
                    651:     }
                    652:        if(use_pipes) {
                    653:                // Set up the security attributes struct.
                    654:                SECURITY_ATTRIBUTES sa;
                    655:                memset(&sa,0,sizeof(sa));
                    656:                sa.nLength= sizeof(SECURITY_ATTRIBUTES);
                    657:                sa.lpSecurityDescriptor = NULL;
                    658:                sa.bInheritHandle = TRUE;
                    659: 
                    660:                // Create the child output pipe (override default 4K buffer size)
                    661:                if(!CreatePipe(&rdoutpipe,&startup_info.hStdOutput,&sa,sizeof(buf))) {
                    662:                        errormsg(WHERE,ERR_CREATE,"stdout pipe",0);
                    663:                        return(GetLastError());
                    664:                }
                    665:                startup_info.hStdError=startup_info.hStdOutput;
                    666: 
                    667:                // Create the child input pipe.
                    668:                if(!CreatePipe(&startup_info.hStdInput,&wrinpipe,&sa,sizeof(buf))) {
                    669:                        errormsg(WHERE,ERR_CREATE,"stdin pipe",0);
                    670:                        return(GetLastError());
                    671:                }
                    672: 
                    673:                DuplicateHandle(
                    674:                        GetCurrentProcess(), rdoutpipe,
                    675:                        GetCurrentProcess(), &rdslot, 0, FALSE, DUPLICATE_SAME_ACCESS);
                    676: 
                    677:                DuplicateHandle(
                    678:                        GetCurrentProcess(), wrinpipe,
                    679:                        GetCurrentProcess(), &wrslot, 0, FALSE, DUPLICATE_SAME_ACCESS);
                    680: 
                    681:                CloseHandle(rdoutpipe);
                    682:                CloseHandle(wrinpipe);
                    683: 
                    684:                startup_info.dwFlags|=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
                    685:        startup_info.wShowWindow=SW_HIDE;
                    686:        }
                    687:        if(native && !(mode&EX_OFFLINE)) {
                    688: 
                    689:                if(!(mode&EX_INR) && input_thread_running) {
                    690:                        pthread_mutex_lock(&input_thread_mutex);
                    691:                        input_thread_mutex_locked=true;
                    692:                }
                    693: 
                    694:                if(!(mode&EX_OUTR)) {    /* Native Socket I/O program */
                    695:                        /* Enable the Nagle algorithm */
                    696:                        BOOL nodelay=FALSE;
                    697:                        setsockopt(client_socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
                    698:                }
                    699:        }
                    700: 
                    701:     success=CreateProcess(
                    702:                NULL,                   // pointer to name of executable module
                    703:                fullcmdline,    // pointer to command line string
                    704:                NULL,                   // process security attributes
                    705:                NULL,                   // thread security attributes
                    706:                native && !(mode&EX_OFFLINE),                           // handle inheritance flag
                    707:                CREATE_NEW_CONSOLE/*|CREATE_SEPARATE_WOW_VDM*/, // creation flags
                    708:         env_block,             // pointer to new environment block
                    709:                p_startup_dir,  // pointer to current directory name
                    710:                &startup_info,  // pointer to STARTUPINFO
                    711:                &process_info   // pointer to PROCESS_INFORMATION
                    712:                );
                    713: 
                    714:        strListFreeBlock(env_block);
                    715: 
                    716:        if(!success) {
                    717:                XTRN_CLEANUP;
                    718:                if(input_thread_mutex_locked && input_thread_running) {
                    719:                        pthread_mutex_unlock(&input_thread_mutex);
                    720:                        input_thread_mutex_locked=false;
                    721:                }
                    722:                SetLastError(last_error);       /* Restore LastError */
                    723:         errormsg(WHERE, ERR_EXEC, realcmdline, mode);
                    724:         return(GetLastError());
                    725:     }
                    726: 
                    727: #if 0
                    728:        char dbgstr[256];
                    729:        sprintf(dbgstr,"Node %d created: hProcess %X hThread %X processID %X threadID %X\n"
                    730:                ,cfg.node_num
                    731:                ,process_info.hProcess 
                    732:                ,process_info.hThread 
                    733:                ,process_info.dwProcessId 
                    734:                ,process_info.dwThreadId); 
                    735:        OutputDebugString(dbgstr);
                    736: #endif
                    737: 
                    738:        CloseHandle(process_info.hThread);
                    739: 
                    740:        if(!native) {
                    741: 
                    742:                if(!(mode&EX_OFFLINE) && !nt) {
                    743:                // Wait for notification from VXD that new VM has started
                    744:                        if((retval=WaitForSingleObject(start_event, 5000))!=WAIT_OBJECT_0) {
                    745:                                XTRN_CLEANUP;
                    746:                 TerminateProcess(process_info.hProcess, __LINE__);
                    747:                                CloseHandle(process_info.hProcess);
                    748:                                errormsg(WHERE, ERR_TIMEOUT, "start_event", retval);
                    749:                                return(GetLastError());
                    750:                        }
                    751: 
                    752:                        CloseHandle(start_event);
                    753:                        start_event=NULL;       /* Mark as closed */
                    754: 
                    755:                        if(!DeviceIoControl(
                    756:                                vxd,                                    // handle to device of interest
                    757:                                SBBSEXEC_IOCTL_COMPLETE,        // control code of operation to perform
                    758:                                NULL,                                   // pointer to buffer to supply input data
                    759:                                0,                                              // size of input buffer
                    760:                                &hVM,                                   // pointer to buffer to receive output data
                    761:                                sizeof(hVM),                    // size of output buffer
                    762:                                &rd,                                    // pointer to variable to receive output byte count
                    763:                                NULL                                    // Overlapped I/O
                    764:                                )) {
                    765:                                XTRN_CLEANUP;
                    766:                 TerminateProcess(process_info.hProcess, __LINE__);
                    767:                                CloseHandle(process_info.hProcess);
                    768:                                errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_COMPLETE);
                    769:                                return(GetLastError());
                    770:                        }
                    771:                }
                    772:        }
                    773:     ReleaseMutex(exec_mutex);
                    774: 
                    775:        /* Disable Ctrl-C checking */
                    776:        if(!(mode&EX_OFFLINE))
                    777:                rio_abortable=false;
                    778: 
                    779:     // Executing app in foreground?, monitor
                    780:     retval=STILL_ACTIVE;
                    781:     while(!(mode&EX_BG)) {
                    782:                if(mode&EX_CHKTIME)
                    783:                        gettimeleft();
                    784:         if(!online && !(mode&EX_OFFLINE)) { // Tell VXD/VDD and external that user hung-up
                    785:                if(was_online) {
                    786:                                sprintf(str,"%s hung-up in external program",useron.alias);
                    787:                                logline("X!",str);
                    788:                hungup=time(NULL);
                    789:                                if(!native) {
                    790:                                        if(nt)
                    791:                                                SetEvent(hungup_event);
                    792:                                        else if(!DeviceIoControl(
                    793:                                                vxd,            // handle to device of interest
                    794:                                                SBBSEXEC_IOCTL_DISCONNECT,      // operation to perform
                    795:                                                &hVM,           // pointer to buffer to supply input data
                    796:                                                sizeof(hVM),// size of input buffer
                    797:                                                NULL,           // pointer to buffer to receive output data
                    798:                                                0,                      // size of output buffer
                    799:                                                &rd,            // pointer to variable to receive output byte count
                    800:                                                NULL            // Overlapped I/O
                    801:                                                )) {
                    802:                                                errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_DISCONNECT);
                    803:                                                break;
                    804:                                        }
                    805:                                }
                    806:                    was_online=false;
                    807:             }
                    808:             if(hungup && time(NULL)-hungup>5 && !processTerminated) {
                    809:                                lprintf(LOG_INFO,"Node %d Terminating process from line %d",cfg.node_num,__LINE__);
                    810:                                processTerminated=TerminateProcess(process_info.hProcess, 2112);
                    811:                        }
                    812:         }
                    813:                if((native && !use_pipes) || mode&EX_OFFLINE) { 
                    814:                        /* Monitor for process termination only */
                    815:                        if(WaitForSingleObject(process_info.hProcess,1000)==WAIT_OBJECT_0)
                    816:                                break;
                    817:                } else {
                    818: 
                    819:                        if(nt || use_pipes) {   // Windows NT/2000
                    820: 
                    821:                                /* Write to VDD */
                    822: 
                    823:                                wr=RingBufPeek(&inbuf,buf,sizeof(buf));
                    824:                                if(wr) {
                    825:                                        if(!use_pipes && wrslot==INVALID_HANDLE_VALUE) {
                    826:                                                sprintf(str,"\\\\.\\mailslot\\sbbsexec\\wr%d"
                    827:                                                        ,cfg.node_num);
                    828:                                                wrslot=CreateFile(str
                    829:                                                        ,GENERIC_WRITE
                    830:                                                        ,FILE_SHARE_READ
                    831:                                                        ,NULL
                    832:                                                        ,OPEN_EXISTING
                    833:                                                        ,FILE_ATTRIBUTE_NORMAL
                    834:                                                        ,(HANDLE) NULL);
                    835: #if 0
                    836:                                                if(wrslot==INVALID_HANDLE_VALUE) {
                    837:                                                        errormsg(WHERE,ERR_OPEN,str,0);
                    838:                                                        break;
                    839:                                                }
                    840: #endif
                    841:                                        }
                    842:                                        
                    843:                                        /* CR expansion */
                    844:                                        if(use_pipes) 
                    845:                                                bp=cr_expand(buf,wr,output_buf,wr);
                    846:                                        else
                    847:                                                bp=buf;
                    848: 
                    849:                                        if(wrslot!=INVALID_HANDLE_VALUE
                    850:                                                && WriteFile(wrslot,bp,wr,&len,NULL)==TRUE) {
                    851:                                                RingBufRead(&inbuf, NULL, len);
                    852:                                                wr=len;
                    853:                                                if(use_pipes && !(mode&EX_NOECHO)) {
                    854:                                                        /* echo */
                    855:                                                        RingBufWrite(&outbuf, bp, wr);
                    856:                                                }
                    857:                                        } else          // VDD not loaded yet
                    858:                                                wr=0;
                    859:                                }
                    860: 
                    861:                                /* Read from VDD */
                    862: 
                    863:                                rd=0;
                    864:                                len=sizeof(buf);
                    865:                                avail=RingBufFree(&outbuf)/2;   // leave room for wwiv/telnet expansion
                    866: #if 0
                    867:                                if(avail==0)
                    868:                                        lprintf("Node %d !output buffer full (%u bytes)"
                    869:                                                ,cfg.node_num,RingBufFull(&outbuf));
                    870: #endif
                    871:                                if(len>avail)
                    872:                        len=avail;
                    873: 
                    874:                                while(rd<len) {
                    875:                                        DWORD waiting=0;
                    876: 
                    877:                                        if(use_pipes)
                    878:                                                PeekNamedPipe(
                    879:                                                        rdslot,             // handle to pipe to copy from
                    880:                                                        NULL,               // pointer to data buffer
                    881:                                                        0,                                      // size, in bytes, of data buffer
                    882:                                                        NULL,                           // pointer to number of bytes read
                    883:                                                        &waiting,                       // pointer to total number of bytes available
                    884:                                                        NULL                            // pointer to unread bytes in this message
                    885:                                                        );
                    886:                                        else
                    887:                                                GetMailslotInfo(
                    888:                                                        rdslot,                         // mailslot handle 
                    889:                                                        NULL,                           // address of maximum message size 
                    890:                                                        NULL,                           // address of size of next message 
                    891:                                                        &waiting,                       // address of number of messages 
                    892:                                                        NULL                            // address of read time-out 
                    893:                                                        );
                    894:                                        if(!waiting)
                    895:                                                break;
                    896:                                        if(ReadFile(rdslot,buf+rd,len-rd,&msglen,NULL)==FALSE || msglen<1)
                    897:                                                break;
                    898:                                        rd+=msglen;
                    899:                                }
                    900: 
                    901:                                if(rd) {
                    902:                                        if(mode&EX_WWIV) {
                    903:                                bp=wwiv_expand(buf, rd, wwiv_buf, rd, useron.misc, wwiv_flag);
                    904:                                                if(rd>sizeof(wwiv_buf))
                    905:                                                        errorlog("WWIV_BUF OVERRUN");
                    906:                                        } else if(telnet_mode&TELNET_MODE_OFF) {
                    907:                                                bp=buf;
                    908:                                        } else {
                    909:                                bp=telnet_expand(buf, rd, telnet_buf, rd);
                    910:                                                if(rd>sizeof(telnet_buf))
                    911:                                                        errorlog("TELNET_BUF OVERRUN");
                    912:                                        }
                    913:                                        if(rd>RingBufFree(&outbuf)) {
                    914:                                                errorlog("output buffer overflow");
                    915:                                                rd=RingBufFree(&outbuf);
                    916:                                        }
                    917:                                        RingBufWrite(&outbuf, bp, rd);
                    918:                                }
                    919:                        } else {        // Windows 9x
                    920: 
                    921:                                /* Write to VXD */
                    922: 
                    923:                                wr=RingBufPeek(&inbuf, buf+sizeof(hVM),sizeof(buf)-sizeof(hVM));
                    924:                                if(wr) {
                    925:                                        *(DWORD*)buf=hVM;
                    926:                                        wr+=sizeof(hVM);
                    927:                                        if(!DeviceIoControl(
                    928:                                                vxd,                                    // handle to device of interest
                    929:                                                SBBSEXEC_IOCTL_WRITE,   // control code of operation to perform
                    930:                                                buf,                                    // pointer to buffer to supply input data
                    931:                                                wr,                                             // size of input buffer
                    932:                                                &rd,                                    // pointer to buffer to receive output data
                    933:                                                sizeof(rd),                             // size of output buffer
                    934:                                                &dummy,                                 // pointer to variable to receive output byte count
                    935:                                                NULL                                    // Overlapped I/O
                    936:                                                )) {
                    937:                                                errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_READ);
                    938:                                                break;
                    939:                                        }
                    940:                                        RingBufRead(&inbuf, NULL, rd);
                    941:                                        wr=rd;
                    942:                                }
                    943:                        /* Read from VXD */
                    944:                                rd=0;
                    945:                                len=sizeof(buf);
                    946:                                avail=RingBufFree(&outbuf)/2;   // leave room for wwiv/telnet expansion
                    947: #if 0
                    948:                                if(avail==0) 
                    949:                                        lprintf("Node %d !output buffer full (%u bytes)"
                    950:                                                ,cfg.node_num,RingBufFull(&outbuf));
                    951: #endif
                    952: 
                    953:                                if(len>avail)
                    954:                        len=avail;
                    955:                                if(len) {
                    956:                                        if(!DeviceIoControl(
                    957:                                                vxd,                                    // handle to device of interest
                    958:                                                SBBSEXEC_IOCTL_READ,    // control code of operation to perform
                    959:                                                &hVM,                                   // pointer to buffer to supply input data
                    960:                                                sizeof(hVM),                    // size of input buffer
                    961:                                                buf,                                    // pointer to buffer to receive output data
                    962:                                                len,                                    // size of output buffer
                    963:                                                &rd,                                    // pointer to variable to receive output byte count
                    964:                                                NULL                                    // Overlapped I/O
                    965:                                                )) {
                    966:                                                errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_READ);
                    967:                                                break;
                    968:                                        }
                    969:                                        if(mode&EX_WWIV) {
                    970:                                bp=wwiv_expand(buf, rd, wwiv_buf, rd, useron.misc, wwiv_flag);
                    971:                                                if(rd>sizeof(wwiv_buf))
                    972:                                                        errorlog("WWIV_BUF OVERRUN");
                    973:                                        } else if(telnet_mode&TELNET_MODE_OFF) {
                    974:                                                bp=buf;
                    975:                                        } else {
                    976:                                bp=telnet_expand(buf, rd, telnet_buf, rd);
                    977:                                                if(rd>sizeof(telnet_buf))
                    978:                                                        errorlog("TELNET_BUF OVERRUN");
                    979:                                        }
                    980:                                        if(rd>RingBufFree(&outbuf)) {
                    981:                                                errorlog("output buffer overflow");
                    982:                                                rd=RingBufFree(&outbuf);
                    983:                                        }
                    984:                                        RingBufWrite(&outbuf, bp, rd);
                    985:                                }
                    986:                        }
                    987: #if defined(_DEBUG) && 0
                    988:                        if(rd>1) {
                    989:                                sprintf(str,"Node %d read %5d bytes from xtrn", cfg.node_num, rd);
                    990:                                OutputDebugString(str);
                    991:                        }
                    992: #endif
                    993:             if((!rd && !wr) || hungup) {
                    994: 
                    995:                                loop_since_io++;        /* number of loop iterations with no I/O */
                    996: 
                    997:                                /* only check process termination after 300 milliseconds of no I/O */
                    998:                                /* to allow for last minute reception of output from DOS programs */
                    999:                                if(loop_since_io>=3) {
                   1000: 
                   1001:                                        if(online && hangup_event!=NULL
                   1002:                                                && WaitForSingleObject(hangup_event,0)==WAIT_OBJECT_0) {
                   1003:                                                lprintf(LOG_NOTICE,"Node %d External program requested hangup (dropped DTR)"
                   1004:                                                        ,cfg.node_num);
                   1005:                                                hangup();
                   1006:                                        }
                   1007: 
                   1008:                                        if(WaitForSingleObject(process_info.hProcess,0)==WAIT_OBJECT_0)
                   1009:                                                break;  /* Process Terminated */
                   1010:                                }
                   1011: 
                   1012:                                /* only check node for interrupt flag every 3 seconds of no I/O */
                   1013:                                if((loop_since_io%30)==0) {     
                   1014:                                        // Check if the node has been interrupted
                   1015:                                        getnodedat(cfg.node_num,&thisnode,0);
                   1016:                                        if(thisnode.misc&NODE_INTR)
                   1017:                                                break;
                   1018:                                }
                   1019: 
                   1020:                                /* only send telnet GA every 30 seconds of no I/O */
                   1021:                                if((loop_since_io%300)==0) {
                   1022: #if defined(_DEBUG)
                   1023:                                        sprintf(str,"Node %d xtrn idle\n",cfg.node_num);
                   1024:                                        OutputDebugString(str);
                   1025: #endif
                   1026:                                        // Let's make sure the socket is up
                   1027:                                        // Sending will trigger a socket d/c detection
                   1028:                                        if(!(startup->options&BBS_OPT_NO_TELNET_GA))
                   1029:                                                send_telnet_cmd(TELNET_GA,0);
                   1030:                                }
                   1031:                                sem_trywait_block(&inbuf.sem,100);
                   1032:             } else
                   1033:                                loop_since_io=0;
                   1034:         }
                   1035:        }
                   1036: 
                   1037:        if(!native && !(mode&EX_OFFLINE) && !nt) {
                   1038:                if(!DeviceIoControl(
                   1039:                        vxd,                                    // handle to device of interest
                   1040:                        SBBSEXEC_IOCTL_STOP,    // control code of operation to perform
                   1041:                        &hVM,                                   // pointer to buffer to supply input data
                   1042:                        sizeof(hVM),                    // size of input buffer
                   1043:                        NULL,                                   // pointer to buffer to receive output data
                   1044:                        0,                                              // size of output buffer
                   1045:                        &rd,                                    // pointer to variable to receive output byte count
                   1046:                        NULL                                    // Overlapped I/O
                   1047:                        )) {
                   1048:                        errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_STOP);
                   1049:                }
                   1050:        }
                   1051: 
                   1052:     if(!(mode&EX_BG)) {                        /* !background execution */
                   1053: 
                   1054:         if(GetExitCodeProcess(process_info.hProcess, &retval)==FALSE)
                   1055:             errormsg(WHERE, ERR_CHK, "ExitCodeProcess",(DWORD)process_info.hProcess);
                   1056: 
                   1057:                if(retval==STILL_ACTIVE) {
                   1058:                        lprintf(LOG_INFO,"Node %d Terminating process from line %d",cfg.node_num,__LINE__);
                   1059:                        TerminateProcess(process_info.hProcess, GetLastError());
                   1060:                }       
                   1061: 
                   1062:                // Get return value
                   1063:                if(!native) {
                   1064:                sprintf(str,"%sDOSXTRN.RET", cfg.node_dir);
                   1065:                        FILE* fp=fopen(str,"r");
                   1066:                        if(fp!=NULL) {
                   1067:                                fscanf(fp,"%d",&retval);
                   1068:                                fclose(fp);
                   1069:                        }
                   1070:                }
                   1071:        }
                   1072: 
                   1073:        XTRN_CLEANUP;
                   1074:        CloseHandle(process_info.hProcess);
                   1075: 
                   1076:        if(!(mode&EX_OFFLINE)) {        /* !off-line execution */
                   1077: 
                   1078:                if(native) {
                   1079:                        
                   1080:                        /* Re-enable blocking (incase disabled by xtrn program) */
                   1081:                        ulong l=0;
                   1082:                        ioctlsocket(client_socket, FIONBIO, &l);
                   1083: 
                   1084:                        /* Re-set socket options */
                   1085:                        if(set_socket_options(&cfg, client_socket, client.protocol, str, sizeof(str)))
                   1086:                                lprintf(LOG_ERR,"%04d !ERROR %s",client_socket, str);
                   1087: 
                   1088:                        if(input_thread_mutex_locked && input_thread_running) {
                   1089:                                pthread_mutex_unlock(&input_thread_mutex);
                   1090:                                input_thread_mutex_locked=false;
                   1091:                        }
                   1092:                }
                   1093: 
                   1094:                curatr=~0;                      // Can't guarantee current attributes
                   1095:                attr(LIGHTGRAY);        // Force to "normal"
                   1096: 
                   1097:                rio_abortable=rio_abortable_save;       // Restore abortable state
                   1098: 
                   1099:                /* Got back to Text/NVT mode */
                   1100:                request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
                   1101:        }
                   1102: 
                   1103: //     lprintf("%s returned %d",realcmdline, retval);
                   1104: 
                   1105:        errorlevel = retval; // Baja or JS retrievable error value
                   1106: 
                   1107:        return(retval);
                   1108: }
                   1109: 
                   1110: #else  /* !WIN32 */
                   1111: 
                   1112: /*****************************************************************************/
                   1113: // Expands Unix LF to CRLF
                   1114: /*****************************************************************************/
                   1115: BYTE* lf_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen)
                   1116: {
                   1117:        ulong   i,j;
                   1118: 
                   1119:        for(i=j=0;i<inlen;i++) {
                   1120:                if(inbuf[i]=='\n' && (!i || inbuf[i-1]!='\r'))
                   1121:                        outbuf[j++]='\r';
                   1122:                outbuf[j++]=inbuf[i];
                   1123:        }
                   1124:        newlen=j;
                   1125:     return(outbuf);
                   1126: }
                   1127: 
                   1128: #define MAX_ARGS 1000
                   1129: 
                   1130: #ifdef NEEDS_SETENV
                   1131: static int setenv(const char *name, const char *value, int overwrite)
                   1132: {
                   1133:        char *envstr;
                   1134:        char *oldenv;
                   1135:        if(overwrite || getenv(name)==NULL) {
                   1136:                envstr=(char *)malloc(strlen(name)+strlen(value)+2);
                   1137:                if(envstr==NULL) {
                   1138:                        errno=ENOMEM;
                   1139:                        return(-1);
                   1140:                }
                   1141:                /* Note, on some platforms, this can be free()d... */
                   1142:                sprintf(envstr,"%s=%s",name,value);
                   1143:                putenv(envstr);
                   1144:        }
                   1145:        return(0);
                   1146: }
                   1147: #endif
                   1148: 
                   1149: #ifdef NEEDS_CFMAKERAW
                   1150: void
                   1151: cfmakeraw(struct termios *t)
                   1152: {
                   1153:        t->c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
                   1154:        t->c_oflag &= ~OPOST;
                   1155:        t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
                   1156:        t->c_cflag &= ~(CSIZE|PARENB);
                   1157:        t->c_cflag |= CS8;
                   1158: }
                   1159: #endif
                   1160: 
                   1161: #ifdef NEEDS_FORKPTY
                   1162: static int login_tty(int fd)
                   1163: {
                   1164:        (void) setsid();
                   1165:        if (!isatty(fd))
                   1166:                return (-1);
                   1167:        (void) dup2(fd, 0);
                   1168:        (void) dup2(fd, 1);
                   1169:        (void) dup2(fd, 2);
                   1170:        if (fd > 2)
                   1171:                (void) close(fd);
                   1172:        return (0);
                   1173: }
                   1174: 
                   1175: #ifdef NEEDS_DAEMON
                   1176: /****************************************************************************/
                   1177: /* Daemonizes the process                                                   */
                   1178: /****************************************************************************/
                   1179: int
                   1180: daemon(int nochdir, int noclose)
                   1181: {
                   1182:     int fd;
                   1183: 
                   1184:     switch (fork()) {
                   1185:     case -1:
                   1186:         return (-1);
                   1187:     case 0:
                   1188:         break;
                   1189:     default:
                   1190:         _exit(0);
                   1191:     }
                   1192: 
                   1193:     if (setsid() == -1)
                   1194:         return (-1);
                   1195: 
                   1196:     if (!nochdir)
                   1197:         (void)chdir("/");
                   1198: 
                   1199:     if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
                   1200:         (void)dup2(fd, STDIN_FILENO);
                   1201:         (void)dup2(fd, STDOUT_FILENO);
                   1202:         (void)dup2(fd, STDERR_FILENO);
                   1203:         if (fd > 2)
                   1204:             (void)close(fd);
                   1205:     }
                   1206:     return (0);
                   1207: }
                   1208: #endif
                   1209: 
                   1210: static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, winsize *winp)
                   1211: {
                   1212:        char line[] = "/dev/ptyXX";
                   1213:        const char *cp1, *cp2;
                   1214:        int master, slave, ttygid;
                   1215:        struct group *gr;
                   1216: 
                   1217:        if ((gr = getgrnam("tty")) != NULL)
                   1218:                ttygid = gr->gr_gid;
                   1219:        else
                   1220:                ttygid = -1;
                   1221: 
                   1222:        for (cp1 = "pqrsPQRS"; *cp1; cp1++) {
                   1223:                line[8] = *cp1;
                   1224:                for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) {
                   1225:                        line[5] = 'p';
                   1226:                        line[9] = *cp2;
                   1227:                        if ((master = open(line, O_RDWR, 0)) == -1) {
                   1228:                                if (errno == ENOENT)
                   1229:                                        break; /* try the next pty group */
                   1230:                        } else {
                   1231:                                line[5] = 't';
                   1232:                                (void) chown(line, getuid(), ttygid);
                   1233:                                (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
                   1234:                                /* Hrm... SunOS doesn't seem to have revoke
                   1235:                                (void) revoke(line); */
                   1236:                                if ((slave = open(line, O_RDWR, 0)) != -1) {
                   1237:                                        *amaster = master;
                   1238:                                        *aslave = slave;
                   1239:                                        if (name)
                   1240:                                                strcpy(name, line);
                   1241:                                        if (termp)
                   1242:                                                (void) tcsetattr(slave,
                   1243:                                                        TCSAFLUSH, termp);
                   1244:                                        if (winp)
                   1245:                                                (void) ioctl(slave, TIOCSWINSZ,
                   1246:                                                        (char *)winp);
                   1247:                                        return (0);
                   1248:                                }
                   1249:                                (void) close(master);
                   1250:                        }
                   1251:                }
                   1252:        }
                   1253:        errno = ENOENT; /* out of ptys */
                   1254:        return (-1);
                   1255: }
                   1256: 
                   1257: static int forkpty(int *amaster, char *name, termios *termp, winsize *winp)
                   1258: {
                   1259:        int master, slave, pid;
                   1260: 
                   1261:        if (openpty(&master, &slave, name, termp, winp) == -1)
                   1262:                return (-1);
                   1263:        switch (pid = FORK()) {
                   1264:        case -1:
                   1265:                return (-1);
                   1266:        case 0:
                   1267:                /*
                   1268:                 * child
                   1269:                 */
                   1270:                (void) close(master);
                   1271:                login_tty(slave);
                   1272:                return (0);
                   1273:        }
                   1274:        /*
                   1275:         * parent
                   1276:         */
                   1277:        *amaster = master;
                   1278:        (void) close(slave);
                   1279:        return (pid);
                   1280: }
                   1281: #endif /* NEED_FORKPTY */
                   1282: 
                   1283: int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
                   1284: {
                   1285:        char    str[MAX_PATH+1];
                   1286:        char    fname[MAX_PATH+1];
                   1287:        char    fullpath[MAX_PATH+1];
                   1288:        char    fullcmdline[MAX_PATH+1];
                   1289:        char*   argv[MAX_ARGS];
                   1290:        char*   p;
                   1291:        BYTE*   bp;
                   1292:        BYTE    buf[XTRN_IO_BUF_LEN];
                   1293:     BYTE       output_buf[XTRN_IO_BUF_LEN*2];
                   1294:        ulong   avail;
                   1295:     ulong      output_len;
                   1296:        bool    native=false;                   // DOS program by default
                   1297:        bool    rio_abortable_save=rio_abortable;
                   1298:        int             i;
                   1299:        bool    data_waiting;
                   1300:        int             rd;
                   1301:        int             wr;
                   1302:        int             argc;
                   1303:        pid_t   pid;
                   1304:        int             in_pipe[2];
                   1305:        int             out_pipe[2];
                   1306:        int             err_pipe[2];
                   1307:        fd_set ibits;
                   1308:        int     high_fd;
                   1309:        struct timeval timeout;
                   1310: 
                   1311:        if(online==ON_LOCAL)
                   1312:                eprintf(LOG_INFO,"Executing external: %s",cmdline);
                   1313: 
                   1314:        if(startup_dir==NULL)
                   1315:                startup_dir=nulstr;
                   1316: 
                   1317:        XTRN_LOADABLE_MODULE;
                   1318:        XTRN_LOADABLE_JS_MODULE;
                   1319: 
                   1320:        attr(cfg.color[clr_external]);  /* setup default attributes */
                   1321: 
                   1322:        native = native_executable(&cfg, cmdline, mode);
                   1323: 
                   1324:     SAFECOPY(str,cmdline);                     /* Set fname to program name only */
                   1325:        truncstr(str," ");
                   1326:     SAFECOPY(fname,getfname(str));
                   1327: 
                   1328:        sprintf(fullpath,"%s%s",startup_dir,fname);
                   1329:        if(startup_dir!=NULL && cmdline[0]!='/' && cmdline[0]!='.' && fexist(fullpath))
                   1330:                sprintf(fullcmdline,"%s%s",startup_dir,cmdline);
                   1331:        else
                   1332:                SAFECOPY(fullcmdline,cmdline);
                   1333: 
                   1334:        if(native) { // Native (32-bit) external
                   1335: 
                   1336:                // Current environment passed to child process
                   1337:                sprintf(dszlog,"%sPROTOCOL.LOG",cfg.node_dir);
                   1338:                setenv("DSZLOG",dszlog,1);              /* Makes the DSZ LOG active */
                   1339:                setenv("SBBSNODE",cfg.node_dir,1);
                   1340:                setenv("SBBSCTRL",cfg.ctrl_dir,1);
                   1341:                setenv("SBBSDATA",cfg.data_dir,1);
                   1342:                setenv("SBBSEXEC",cfg.exec_dir,1);
                   1343:                sprintf(str,"%u",cfg.node_num);
                   1344:                if(setenv("SBBSNNUM",str,1))
                   1345:                errormsg(WHERE,ERR_WRITE,"environment",0);
                   1346: 
                   1347:        } else {
                   1348: #if defined(__FreeBSD__)
                   1349:                /* ToDo: This seems to work for every door except Iron Ox
                   1350:                   ToDo: Iron Ox is unique in that it runs perfectly from
                   1351:                   ToDo: tcsh but not at all from anywhere else, complaining
                   1352:                   ToDo: about corrupt files.  I've ruled out the possibilty
                   1353:                   ToDo: of it being a terminal mode issue... no other ideas
                   1354:                   ToDo: come to mind. */
                   1355: 
                   1356:                FILE * doscmdrc;
                   1357: 
                   1358:                sprintf(str,"%s.doscmdrc",cfg.node_dir);
                   1359:                if((doscmdrc=fopen(str,"w+"))==NULL)  {
                   1360:                        errormsg(WHERE,ERR_CREATE,str,0);
                   1361:                        return(-1);
                   1362:                }
                   1363:                if(startup_dir!=NULL && startup_dir[0])
                   1364:                        fprintf(doscmdrc,"assign C: %s\n",startup_dir);
                   1365:                else
                   1366:                        fprintf(doscmdrc,"assign C: .\n");
                   1367: 
                   1368:                fprintf(doscmdrc,"assign D: %s\n",cfg.node_dir);
                   1369:                SAFECOPY(str,cfg.exec_dir);
                   1370:                if((p=strrchr(str,'/'))!=NULL)
                   1371:                        *p=0;
                   1372:                if((p=strrchr(str,'/'))!=NULL)
                   1373:                        *p=0;
                   1374:                fprintf(doscmdrc,"assign E: %s\n",str);
                   1375:                
                   1376:                /* setup doscmd env here */
                   1377:                /* ToDo Note, this assumes that the BBS uses standard dir names */
                   1378:                fprintf(doscmdrc,"DSZLOG=E:\\node%d\\PROTOCOL.LOG\n",cfg.node_num);
                   1379:                fprintf(doscmdrc,"SBBSNODE=D:\\\n");
                   1380:                fprintf(doscmdrc,"SBBSCTRL=E:\\ctrl\\\n");
                   1381:                fprintf(doscmdrc,"SBBSDATA=E:\\data\\\n");
                   1382:                fprintf(doscmdrc,"SBBSEXEC=E:\\exec\\\n");
                   1383:                fprintf(doscmdrc,"SBBSNNUM=%d\n",cfg.node_num);
                   1384: 
                   1385:                fclose(doscmdrc);
                   1386:                SAFECOPY(str,fullcmdline);
                   1387:                sprintf(fullcmdline,"%s -F %s",startup->dosemu_path,str);
                   1388: 
                   1389: #elif defined(__linux__) && defined(USE_DOSEMU)
                   1390: 
                   1391:                /* dosemu integration  --  Ryan Underwood, <nemesis @ icequake.net> */
                   1392: 
                   1393:                FILE *dosemubat;
                   1394:                int setup_override;
                   1395:                char tok[MAX_PATH+1];
                   1396:  
                   1397:                char dosemuconf[MAX_PATH+1];
                   1398:                char dosemubinloc[MAX_PATH+1];
                   1399:                char virtualconf[75];
                   1400:                char dosterm[15];
                   1401:                char log_external[MAX_PATH+1];
                   1402: 
                   1403:                /*  on the Unix side. xtrndir is the parent of the door's startup dir. */
                   1404:                char xtrndir[MAX_PATH+1];
                   1405: 
                   1406:                /*  on the DOS side.  */
                   1407:                char xtrndir_dos[MAX_PATH+1];
                   1408:                char ctrldir_dos[MAX_PATH+1];
                   1409:                char datadir_dos[MAX_PATH+1];
                   1410:                char execdir_dos[MAX_PATH+1];
                   1411: 
                   1412:                /* Default locations that can be overridden by 
                   1413:                 * the sysop in emusetup.bat */
                   1414: 
                   1415:                const char nodedrive[] = "D:";
                   1416:                const char xtrndrive[] = "E:";
                   1417:                const char ctrldrive[] = "F:";
                   1418:                const char datadrive[] = "G:";
                   1419:                const char execdrive[] = "H:";
                   1420: 
                   1421:                SAFECOPY(str,startup_dir);
                   1422:                if(*(p=lastchar(str))=='/')             /* kill trailing slash */
                   1423:                        *p=0;
                   1424:                if((p=strrchr(str,'/'))!=NULL)  /* kill the last element of the path */
                   1425:                        *p=0;
                   1426: 
                   1427:                SAFECOPY(xtrndir,str);
                   1428: 
                   1429:                /* construct DOS equivalents for the unix directories */
                   1430: 
                   1431:                SAFECOPY(ctrldir_dos,cfg.ctrl_dir);
                   1432:                REPLACE_CHARS(ctrldir_dos,'/','\\',p);
                   1433: 
                   1434:                p=lastchar(ctrldir_dos);
                   1435:                if (*p=='\\') *p=0;
                   1436: 
                   1437:                SAFECOPY(datadir_dos,cfg.data_dir);
                   1438:                REPLACE_CHARS(datadir_dos,'/','\\',p);
                   1439: 
                   1440:                p=lastchar(datadir_dos);
                   1441:                if (*p=='\\') *p=0;
                   1442: 
                   1443:                SAFECOPY(execdir_dos,cfg.exec_dir);
                   1444:                REPLACE_CHARS(execdir_dos,'/','\\',p);
                   1445: 
                   1446:                p=lastchar(execdir_dos);
                   1447:                if (*p=='\\') *p=0;
                   1448: 
                   1449:                SAFECOPY(xtrndir_dos,xtrndir);
                   1450:                REPLACE_CHARS(xtrndir_dos,'/','\\',p);
                   1451: 
                   1452:                /* check for existence of a dosemu.conf in the door directory.
                   1453:                 * It is a good idea to be able to use separate configs for each
                   1454:                 * door. */
                   1455: 
                   1456:                sprintf(str,"%sdosemu.conf",startup_dir);
                   1457:                if (!fexist(str)) {
                   1458: 
                   1459:                /* If we can't find it in the door dir, look for a global one
                   1460:                 * in the ctrl dir. */
                   1461: 
                   1462:                        sprintf(str,"%sdosemu.conf",cfg.ctrl_dir);
                   1463:                        if (!fexist(str)) {
                   1464: 
                   1465:                /* If we couldn't find either, try for the system one, then
                   1466:                 * error out. */
                   1467:                                SAFECOPY(str,"/etc/dosemu/dosemu.conf");
                   1468:                                if (!fexist(str)) {
                   1469:                                
                   1470:                                        SAFECOPY(str,"/etc/dosemu.conf");
                   1471:                                        if (!fexist(str)) {
                   1472:                                                errormsg(WHERE,ERR_READ,str,0);
                   1473:                                                return(-1);
                   1474:                                        }
                   1475:                                        else SAFECOPY(dosemuconf,str);  /* using system conf */
                   1476:                                }
                   1477:                                else SAFECOPY(dosemuconf,str);  /* using system conf */
                   1478:                        }
                   1479:                        else SAFECOPY(dosemuconf,str);   /* using global conf */
                   1480:                }
                   1481:                else SAFECOPY(dosemuconf,str);  /* using door-specific conf */
                   1482: 
                   1483:                /* same deal for emusetup.bat. */
                   1484: 
                   1485:                sprintf(str,"%semusetup.bat",startup_dir);
                   1486:                fprintf(stderr, str);
                   1487:                if (!fexist(str)) {
                   1488: 
                   1489:                /* If we can't find it in the door dir, look for a global one
                   1490:                 * in the ctrl dir. */
                   1491: 
                   1492:                        sprintf(str,"%semusetup.bat",cfg.ctrl_dir);
                   1493:                        if (!fexist(str)) {
                   1494: 
                   1495:                /* If we couldn't find either, set an error condition. */
                   1496:                                setup_override = -1;
                   1497:                        }
                   1498:                        else setup_override = 0;  /* using global bat */
                   1499:                }
                   1500:                else setup_override = 1;  /* using door-specific bat */
                   1501: 
                   1502:                /* Create the external bat here to be placed in the node dir. */
                   1503: 
                   1504:                sprintf(str,"%sexternal.bat",cfg.node_dir);
                   1505:                if(!(dosemubat=fopen(str,"w+"))) {
                   1506:                        errormsg(WHERE,ERR_CREATE,str,0);
                   1507:                        return(-1);
                   1508:                }
                   1509: 
                   1510:                fprintf(dosemubat,"@echo off\r\n");
                   1511:                fprintf(dosemubat,"set DSZLOG=%s\\PROTOCOL.LOG\r\n",nodedrive);
                   1512:                fprintf(dosemubat,"set SBBSNODE=%s\r\n",nodedrive);
                   1513:                fprintf(dosemubat,"set SBBSNNUM=%d\r\n",cfg.node_num);
                   1514:                fprintf(dosemubat,"set SBBSCTRL=%s\r\n",ctrldrive);
                   1515:                fprintf(dosemubat,"set SBBSDATA=%s\r\n",datadrive);
                   1516:                fprintf(dosemubat,"set SBBSEXEC=%s\r\n",execdrive);
                   1517: 
                   1518:                /* clear existing redirections on dos side */
                   1519:                fprintf(dosemubat,"lredir del %s\r\nlredir del %s\r\nlredir del %s\r\nlredir del %s\r\n",xtrndrive,ctrldrive,datadrive,execdrive);
                   1520: 
                   1521:                /* redirect necessary drive letters to unix paths */
                   1522:                fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",xtrndrive,xtrndir_dos);
                   1523:                fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",ctrldrive,ctrldir_dos);
                   1524:                fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",datadrive,datadir_dos);
                   1525:                fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",execdrive,execdir_dos);
                   1526: 
                   1527:                /* change to the drive where the parent of the startup_dir is mounted */
                   1528:                fprintf(dosemubat,"%s\r\n",xtrndrive);
                   1529: 
                   1530:                if(startup_dir!=NULL && startup_dir[0]) {
                   1531: 
                   1532:                        SAFECOPY(str,startup_dir);
                   1533: 
                   1534:                /* if theres a trailing slash, dump it */
                   1535: 
                   1536:                        p=lastchar(str);
                   1537:                        if (*p=='/') *p=0;
                   1538: 
                   1539:                        if ((p=strrchr(str, '/'))!=NULL)
                   1540:                                SAFECOPY(str,p+1);  /* str = game's starting dir */
                   1541: 
                   1542:                        else str[0] = '\0';
                   1543:                }
                   1544: 
                   1545:                else str[0] = '\0';
                   1546: 
                   1547:                fprintf(dosemubat,"cd %s\r\n",str);  /* startup_dir  */
                   1548: 
                   1549:                if (setup_override == 1)
                   1550:                        fprintf(dosemubat,"call %s\\%s\\emusetup.bat %s\r\n",xtrndrive,str,cmdline);
                   1551:                else if (setup_override == 0)
                   1552:                        fprintf(dosemubat,"call %s\\emusetup.bat\r\n",ctrldrive);
                   1553:                /* if (setup_override == -1) do_nothing */
                   1554: 
                   1555:                /*  Check if it's a bat file, to prepend "call" to the command  */
                   1556: 
                   1557:                SAFECOPY(tok,cmdline);
                   1558:                truncstr(tok," ");
                   1559: 
                   1560:                p = strstr(tok, ".bat");  /*  check if it's a bat file  */
                   1561:                if (p)
                   1562:                        fprintf(dosemubat,"call ");  /* if so, "call" it */
                   1563: 
                   1564:                fprintf(dosemubat,"%s\r\n",cmdline);
                   1565:                fprintf(dosemubat,"exitemu\r\n");
                   1566: 
                   1567:                /* Check the "Stdio Interception" flag from scfg for this door.  If it's
                   1568:                 * enabled, we enable doorway mode.  Else, it's vmodem for us, unless
                   1569:                 * it's a timed event.
                   1570:                 */
                   1571: 
                   1572:                if (!(mode&(EX_INR|EX_OUTR)) && online!=ON_LOCAL)
                   1573:                        SAFECOPY(virtualconf,"-I\"serial { virtual com 1 }\"");
                   1574:                else
                   1575:                        virtualconf[0] = '\0';
                   1576: 
                   1577:                /* Set the interception bits, since we are always going to want Synchronet
                   1578:                 * to intercept dos programs under Unix.
                   1579:                 */
                   1580: 
                   1581:                mode |= (EX_INR|EX_OUTR);
                   1582: 
                   1583:                /* See if we have the dosemu link in the door's dir.  If so, use the dosemu
                   1584:                 * that it points to as our command to execute.  If not, use DOSemuPath.
                   1585:                 */
                   1586:  
                   1587:                sprintf(str,"%sdosemu.bin",startup_dir);
                   1588:                if (!fexist(str)) {
                   1589:                        SAFECOPY(dosemubinloc,(cmdstr(startup->dosemu_path,nulstr,nulstr,tok)));
                   1590:                }
                   1591:                else {
                   1592:                        SAFECOPY(dosemubinloc,str);
                   1593:                }
                   1594: 
                   1595:                /* Attempt to keep dosemu from prompting for a disclaimer. */
                   1596: 
                   1597:                sprintf(str, "%s/.dosemu", cfg.ctrl_dir);
                   1598:                if (!isdir(str)) {
                   1599:                        mkdir(str, 0755);
                   1600:                }
                   1601: 
                   1602:                strcat(str, "/disclaimer");
                   1603:                ftouch(str);
                   1604: 
                   1605:                /* Set up the command for dosemu to execute with 'unix -e'. */
                   1606: 
                   1607:                sprintf(str,"%sexternal.bat",nodedrive);
                   1608: 
                   1609:                /* need TERM=linux for maintenance programs to work
                   1610:                 * (dosemu won't start with no controlling terminal)
                   1611:                 * Also, redirect stdout to a log if it's a timed event.
                   1612:                 */
                   1613:                 
                   1614:                if (online==ON_LOCAL) {
                   1615:                        SAFECOPY(dosterm,"TERM=linux");
                   1616:                        sprintf(log_external,">> %sdosevent_%s.log",cfg.logs_dir,fname);
                   1617:                }
                   1618:                else {
                   1619:                        dosterm[0]='\0';
                   1620:                        log_external[0] = '\0';
                   1621:                }
                   1622: 
                   1623:                /* Drum roll. */
                   1624: 
                   1625:                sprintf(fullcmdline,
                   1626:                "/usr/bin/env %s HOME=%s QUIET=1 DOSDRIVE_D=%s %s -I\"video { none }\" -I\"keystroke \\r\" %s -f%s -E%s -o%sdosemu.log 2> %sdosemu_boot.log %s",
                   1627:                        dosterm,cfg.ctrl_dir,cfg.node_dir,dosemubinloc,virtualconf,dosemuconf,str,cfg.node_dir,cfg.node_dir,log_external);
                   1628: 
                   1629:                fprintf(dosemubat,"REM For debugging: %s\r\n",fullcmdline);
                   1630:                fclose(dosemubat);
                   1631: 
                   1632: #else
                   1633:                bprintf("\r\nExternal DOS programs are not yet supported in \r\n%s\r\n"
                   1634:                        ,VERSION_NOTICE);
                   1635:                return(-1);
                   1636: #endif
                   1637:        }
                   1638: 
                   1639:        if(!(mode&EX_INR) && input_thread_running) {
                   1640:                lprintf(LOG_DEBUG,"Locking input thread mutex"); 
                   1641:                if(pthread_mutex_lock(&input_thread_mutex)!=0)
                   1642:                        errormsg(WHERE,ERR_LOCK,"input_thread_mutex",0);
                   1643:                input_thread_mutex_locked=true;
                   1644:        }
                   1645: 
                   1646: #ifdef XTERN_LOG_STDERR
                   1647:        if(pipe(err_pipe)!=0) {
                   1648:                errormsg(WHERE,ERR_CREATE,"err_pipe",0);
                   1649:                return(-1);
                   1650: }
                   1651: #endif
                   1652: 
                   1653:        if((mode&EX_INR) && (mode&EX_OUTR))  {
                   1654:                struct winsize winsize;
                   1655:                struct termios term;
                   1656:                memset(&term,0,sizeof(term));
                   1657:                cfsetispeed(&term,B19200);
                   1658:                cfsetospeed(&term,B19200);
                   1659:                if(mode&EX_BIN)
                   1660:                        cfmakeraw(&term);
                   1661:                else {
                   1662:                        term.c_iflag = TTYDEF_IFLAG;
                   1663:                        term.c_oflag = TTYDEF_OFLAG;
                   1664:                        term.c_lflag = TTYDEF_LFLAG;
                   1665:                        term.c_cflag = TTYDEF_CFLAG;
                   1666:                        memcpy(term.c_cc,ttydefchars,sizeof(term.c_cc));
                   1667:                }
                   1668:                winsize.ws_row=rows;
                   1669:                winsize.ws_col=cols;
                   1670:                if((pid=forkpty(&in_pipe[1],NULL,&term,&winsize))==-1) {
                   1671:                        if(input_thread_mutex_locked && input_thread_running) {
                   1672:                                if(pthread_mutex_unlock(&input_thread_mutex)!=0)
                   1673:                                        errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
                   1674:                                input_thread_mutex_locked=false;
                   1675:                        }
                   1676:                        errormsg(WHERE,ERR_EXEC,fullcmdline,0);
                   1677:                        return(-1);
                   1678:                }
                   1679:                out_pipe[0]=in_pipe[1];
                   1680:        }
                   1681:        else  {
                   1682:                if(mode&EX_INR)
                   1683:                        if(pipe(in_pipe)!=0) {
                   1684:                                errormsg(WHERE,ERR_CREATE,"in_pipe",0);
                   1685:                                return(-1);
                   1686:                        }
                   1687:                if(mode&EX_OUTR)
                   1688:                        if(pipe(out_pipe)!=0) {
                   1689:                                errormsg(WHERE,ERR_CREATE,"out_pipe",0);
                   1690:                                return(-1);
                   1691:                        }
                   1692: 
                   1693: 
                   1694:                if((pid=FORK())==-1) {
                   1695:                        if(input_thread_mutex_locked && input_thread_running) {
                   1696:                                if(pthread_mutex_unlock(&input_thread_mutex)!=0)
                   1697:                                        errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
                   1698:                                input_thread_mutex_locked=false;
                   1699:                        }
                   1700:                        errormsg(WHERE,ERR_EXEC,fullcmdline,0);
                   1701:                        return(-1);
                   1702:                }
                   1703:        }
                   1704:        if(pid==0) {    /* child process */
                   1705:                /* Give away all privs for good now */
                   1706:                if(startup->setuid!=NULL)
                   1707:                        startup->setuid(TRUE);
                   1708: 
                   1709:                sigset_t        sigs;
                   1710:                sigfillset(&sigs);
                   1711:                sigprocmask(SIG_UNBLOCK,&sigs,NULL);
                   1712:                if(!(mode&EX_BIN))  {
                   1713:                        static char     term_env[256];
                   1714:                        if(term_supports(ANSI))
                   1715:                                sprintf(term_env,"TERM=%s",startup->xtrn_term_ansi);
                   1716:                        else
                   1717:                                sprintf(term_env,"TERM=%s",startup->xtrn_term_dumb);
                   1718:                        putenv(term_env);
                   1719:                }
                   1720: #ifdef __FreeBSD__
                   1721:                if(!native)
                   1722:                        chdir(cfg.node_dir);
                   1723:                else
                   1724: #endif
                   1725:                if(startup_dir!=NULL && startup_dir[0])
                   1726:                        chdir(startup_dir);
                   1727: 
                   1728:                if(mode&EX_SH || strcspn(fullcmdline,"<>|;\"")!=strlen(fullcmdline)) {
                   1729:                        argv[0]=comspec;
                   1730:                        argv[1]="-c";
                   1731:                        argv[2]=fullcmdline;
                   1732:                        argv[3]=NULL;
                   1733:                } else {
                   1734:                        argv[0]=fullcmdline;    /* point to the beginning of the string */
                   1735:                        argc=1;
                   1736:                        for(i=0;fullcmdline[i] && argc<MAX_ARGS;i++)    /* Break up command line */
                   1737:                                if(fullcmdline[i]==' ') {
                   1738:                                        fullcmdline[i]=0;                       /* insert nulls */
                   1739:                                        argv[argc++]=fullcmdline+i+1; /* point to the beginning of the next arg */
                   1740:                                }
                   1741:                        argv[argc]=NULL;
                   1742:                }
                   1743: 
                   1744:                if(mode&EX_INR && !(mode&EX_OUTR))  {
                   1745:                        close(in_pipe[1]);              /* close write-end of pipe */
                   1746:                        dup2(in_pipe[0],0);             /* redirect stdin */
                   1747:                        close(in_pipe[0]);              /* close excess file descriptor */
                   1748:                }
                   1749: 
                   1750:                if(mode&EX_OUTR && !(mode&EX_INR)) {
                   1751:                        close(out_pipe[0]);             /* close read-end of pipe */
                   1752:                        dup2(out_pipe[1],1);    /* stdout */
                   1753: #ifndef XTERN_LOG_STDERR
                   1754:                        dup2(out_pipe[1],2);    /* stderr */
                   1755: #endif
                   1756:                        close(out_pipe[1]);             /* close excess file descriptor */
                   1757:                }
                   1758: 
                   1759:                if(mode&EX_BG)  /* background execution, detach child */
                   1760:                {
                   1761:                        lprintf(LOG_INFO,"Detaching external process");
                   1762:                        daemon(TRUE,FALSE);
                   1763:            }
                   1764: 
                   1765: #ifdef XTERN_LOG_STDERR
                   1766:                close(err_pipe[0]);             /* close read-end of pipe */
                   1767:                dup2(err_pipe[1],2);    /* stderr */
                   1768: #endif
                   1769:        
                   1770:                execvp(argv[0],argv);
                   1771:                sprintf(str,"!ERROR %d executing %s",errno,argv[0]);
                   1772:                errorlog(str);
                   1773:                _exit(-1);      /* should never get here */
                   1774:        }
                   1775: 
                   1776:        if(online!=ON_LOCAL)
                   1777:                lprintf(LOG_INFO,"Node %d executing external: %s",cfg.node_num,fullcmdline);
                   1778: 
                   1779:        /* Disable Ctrl-C checking */
                   1780:        if(!(mode&EX_OFFLINE))
                   1781:                rio_abortable=false;
                   1782:        
                   1783: #ifdef XTERN_LOG_STDERR
                   1784:        close(err_pipe[1]);     /* close write-end of pipe */
                   1785: #endif
                   1786: 
                   1787:        if(mode&EX_OUTR) {
                   1788:                if(!(mode&EX_INR))
                   1789:                        close(out_pipe[1]);     /* close write-end of pipe */
                   1790:                while(!terminated) {
                   1791:                        if(waitpid(pid, &i, WNOHANG)!=0)        /* child exited */
                   1792:                                break;
                   1793: 
                   1794:                        if(mode&EX_CHKTIME)
                   1795:                                gettimeleft();
                   1796:                        
                   1797:                        if(!online && !(mode&EX_OFFLINE)) {
                   1798:                                sprintf(str,"%s hung-up in external program",useron.alias);
                   1799:                                logline("X!",str);
                   1800:                                break;
                   1801:                        }
                   1802: 
                   1803:                        /* Input */     
                   1804:                        if(mode&EX_INR && RingBufFull(&inbuf)) {
                   1805:                                if((wr=RingBufRead(&inbuf,buf,sizeof(buf)))!=0)
                   1806:                                        write(in_pipe[1],buf,wr);
                   1807:                        }
                   1808:                                
                   1809:                        /* Error Output */
                   1810:                        FD_ZERO(&ibits);
                   1811: #ifdef XTERN_LOG_STDERR
                   1812:                        FD_SET(err_pipe[0],&ibits);
                   1813:                        high_fd=err_pipe[0];
                   1814: #endif
                   1815:                        FD_SET(out_pipe[0],&ibits);
                   1816: #ifdef XTERN_LOG_STDERR
                   1817:                        if(out_pipe[0]>err_pipe[0])
                   1818:                                high_fd=out_pipe[0];
                   1819: #else
                   1820:                        high_fd=out_pipe[0];
                   1821: #endif
                   1822:                        timeout.tv_sec=0;
                   1823:                        timeout.tv_usec=1000;
                   1824:                        bp=buf;
                   1825:                        i=0;
                   1826: #ifndef XTERN_LOG_STDERR
                   1827:                        select(high_fd+1,&ibits,NULL,NULL,&timeout);
                   1828: #else
                   1829:                        while ((select(high_fd+1,&ibits,NULL,NULL,&timeout)>0) && FD_ISSET(err_pipe[0],&ibits) && (i<(int)sizeof(buf)-1))  {
                   1830:                                if((rd=read(err_pipe[0],bp,1))>0)  {
                   1831:                                        i+=rd;
                   1832:                                        bp++;
                   1833:                                        if(*(bp-1)=='\n')
                   1834:                                                break;
                   1835:                                }
                   1836:                                else
                   1837:                                        break;
                   1838:                                FD_ZERO(&ibits);
                   1839:                                FD_SET(err_pipe[0],&ibits);
                   1840:                                FD_SET(out_pipe[0],&ibits);
                   1841:                                timeout.tv_sec=0;
                   1842:                                timeout.tv_usec=1000;
                   1843:                        }
                   1844:                        if(i)
                   1845:                                lprintf(LOG_NOTICE,"%.*s",i,buf);               /* lprintf mangles i? */
                   1846: 
                   1847:                        /* Eat stderr if mode is EX_BIN */
                   1848:                        if(mode&EX_BIN)  {
                   1849:                                bp=buf;
                   1850:                                i=0;
                   1851:                        }
                   1852: #endif
                   1853: 
                   1854:                        data_waiting=FD_ISSET(out_pipe[0],&ibits);
                   1855:                        if(i==0 && data_waiting==0)
                   1856:                                continue;
                   1857: 
                   1858:                        avail=(RingBufFree(&outbuf)-i)/2;       // Leave room for wwiv/telnet expansion
                   1859:                        if(avail==0) {
                   1860: #if 0
                   1861:                                lprintf("Node %d !output buffer full (%u bytes)"
                   1862:                                                ,cfg.node_num,RingBufFull(&outbuf));
                   1863: #endif
                   1864:                                YIELD();
                   1865:                                continue;
                   1866:                        }
                   1867: 
                   1868:                        rd=avail;
                   1869: 
                   1870:                        if(rd>((int)sizeof(buf)-i))
                   1871:                                rd=sizeof(buf)-i;
                   1872: 
                   1873:                        if(data_waiting)  {
                   1874:                                rd=read(out_pipe[0],bp,rd);
                   1875:                                if(rd<1 && i==0)
                   1876:                                        continue;
                   1877:                                if(rd<0)
                   1878:                                        rd=0;
                   1879:                        }
                   1880:                        else
                   1881:                                rd=0;
                   1882: 
                   1883:                        rd += i;
                   1884: 
                   1885:                        if(mode&EX_BIN) {
                   1886:                                if(telnet_mode&TELNET_MODE_OFF) {
                   1887:                                        bp=buf;
                   1888:                                        output_len=rd;
                   1889:                                }
                   1890:                                else
                   1891:                                bp=telnet_expand(buf, rd, output_buf, output_len);
                   1892:                        } else                  /* LF to CRLF expansion */
                   1893:                                bp=lf_expand(buf, rd, output_buf, output_len);
                   1894: 
                   1895:                        /* Did expansion overrun the output buffer? */
                   1896:                        if(output_len>sizeof(output_buf)) {
                   1897:                                errorlog("OUTPUT_BUF OVERRUN");
                   1898:                                output_len=sizeof(output_buf);
                   1899:                        }
                   1900: 
                   1901:                        /* Does expanded size fit in the ring buffer? */
                   1902:                        if(output_len>RingBufFree(&outbuf)) {
                   1903:                                errorlog("output buffer overflow");
                   1904:                                output_len=RingBufFree(&outbuf);
                   1905:                        }
                   1906: 
                   1907:                        RingBufWrite(&outbuf, bp, output_len);
                   1908: 
                   1909:                }
                   1910: 
                   1911:                if(waitpid(pid, &i, WNOHANG)==0)  {             // Child still running? 
                   1912:                        kill(pid, SIGHUP);                                      // Tell child user has hung up
                   1913:                        time_t start=time(NULL);                        // Wait up to 10 seconds
                   1914:                        while(time(NULL)-start<10) {            // for child to terminate
                   1915:                                if(waitpid(pid, &i, WNOHANG)!=0)
                   1916:                                        break;
                   1917:                                mswait(500);
                   1918:                        }
                   1919:                        if(waitpid(pid, &i, WNOHANG)==0)        // Child still running?
                   1920:                                kill(pid, SIGKILL);                             // terminate child process
                   1921:                }
                   1922:                /* close unneeded descriptors */
                   1923:                if(mode&EX_INR)
                   1924:                        close(in_pipe[1]);
                   1925:                close(out_pipe[0]);
                   1926:        }
                   1927: #if 0
                   1928:        else {
                   1929:                /* Enable the Nagle algorithm */
                   1930:                int nodelay=FALSE;
                   1931:                setsockopt(client_socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
                   1932:        }
                   1933: #endif
                   1934: #ifdef XTERN_LOG_STDERR
                   1935:        while(waitpid(pid, &i, WNOHANG)==0)  {
                   1936:                FD_ZERO(&ibits);
                   1937:                FD_SET(err_pipe[0],&ibits);
                   1938:                timeout.tv_sec=1;
                   1939:                timeout.tv_usec=0;
                   1940:                bp=buf;
                   1941:                i=0;
                   1942:                while ((select(err_pipe[0]+1,&ibits,NULL,NULL,&timeout)>0) && (i<XTRN_IO_BUF_LEN-1))  {
                   1943:                        if((rd=read(err_pipe[0],bp,1))>0)  {
                   1944:                                i+=rd;
                   1945:                                if(*bp=='\n') {
                   1946:                                        lprintf(LOG_NOTICE,"%.*s",i-1,buf);
                   1947:                                        i=0;
                   1948:                                        bp=buf;
                   1949:                                }
                   1950:                                else
                   1951:                                        bp++;
                   1952:                        }
                   1953:                        else
                   1954:                                break;
                   1955:                }
                   1956:                if(i)
                   1957:                        lprintf(LOG_NOTICE,"%.*s",i,buf);
                   1958:        }
                   1959: #else
                   1960:        waitpid(pid, &i, 0)==0;
                   1961: #endif
                   1962: 
                   1963:        if(!(mode&EX_OFFLINE)) {        /* !off-line execution */
                   1964: 
                   1965:                /* Re-enable blocking (incase disabled by xtrn program) */
                   1966:                ulong l=0;
                   1967:                ioctlsocket(client_socket, FIONBIO, &l);
                   1968: 
                   1969:                /* Re-set socket options */
                   1970:                if(set_socket_options(&cfg, client_socket, client.protocol, str, sizeof(str)))
                   1971:                        lprintf(LOG_ERR,"%04d !ERROR %s",client_socket, str);
                   1972: 
                   1973:                curatr=~0;                      // Can't guarantee current attributes
                   1974:                attr(LIGHTGRAY);        // Force to "normal"
                   1975: 
                   1976:                rio_abortable=rio_abortable_save;       // Restore abortable state
                   1977: 
                   1978:                /* Got back to Text/NVT mode */
                   1979:                request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
                   1980:        }
                   1981: 
                   1982: #ifdef XTERN_LOG_STDERR
                   1983:        close(err_pipe[0]);
                   1984: #endif
                   1985: 
                   1986:        if(input_thread_mutex_locked && input_thread_running) {
                   1987:                if(pthread_mutex_unlock(&input_thread_mutex)!=0)
                   1988:                        errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
                   1989:                input_thread_mutex_locked=false;
                   1990:        }
                   1991: 
                   1992:        return(errorlevel = WEXITSTATUS(i));
                   1993: }
                   1994: 
                   1995: #endif /* !WIN32 */
                   1996: 
                   1997: const char* quoted_string(const char* str, char* buf, size_t maxlen)
                   1998: {
                   1999:        if(strchr(str,' ')==NULL)
                   2000:                return(str);
                   2001:        safe_snprintf(buf,maxlen,"\"%s\"",str);
                   2002:        return(buf);
                   2003: }
                   2004: 
                   2005: #define QUOTED_STRING(ch, str, buf, maxlen) \
                   2006:        ((isalpha(ch) && isupper(ch)) ? str : quoted_string(str,buf,maxlen))
                   2007:        
                   2008: /*****************************************************************************/
                   2009: /* Returns command line generated from instr with %c replacments             */
                   2010: /*****************************************************************************/
                   2011: char* sbbs_t::cmdstr(char *instr, char *fpath, char *fspec, char *outstr)
                   2012: {
                   2013:        char    str[MAX_PATH+1],*cmd;
                   2014:     int                i,j,len;
                   2015: 
                   2016:     if(outstr==NULL)
                   2017:         cmd=cmdstr_output;
                   2018:     else
                   2019:         cmd=outstr;
                   2020:     len=strlen(instr);
                   2021:     for(i=j=0;i<len && j<(int)sizeof(cmdstr_output);i++) {
                   2022:         if(instr[i]=='%') {
                   2023:             i++;
                   2024:             cmd[j]=0;
                   2025:                        char ch=instr[i];
                   2026:                        if(isalpha(ch))
                   2027:                                ch=toupper(ch);
                   2028:             switch(ch) {
                   2029:                 case 'A':   /* User alias */
                   2030:                     strcat(cmd,QUOTED_STRING(instr[i],useron.alias,str,sizeof(str)));
                   2031:                     break;
                   2032:                 case 'B':   /* Baud (DTE) Rate */
                   2033:                     strcat(cmd,ultoa(dte_rate,str,10));
                   2034:                     break;
                   2035:                 case 'C':   /* Connect Description */
                   2036:                     strcat(cmd,connection);
                   2037:                     break;
                   2038:                 case 'D':   /* Connect (DCE) Rate */
                   2039:                     strcat(cmd,ultoa((ulong)cur_rate,str,10));
                   2040:                     break;
                   2041:                 case 'E':   /* Estimated Rate */
                   2042:                     strcat(cmd,ultoa((ulong)cur_cps*10,str,10));
                   2043:                     break;
                   2044:                 case 'F':   /* File path */
                   2045:                     strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str)));
                   2046:                     break;
                   2047:                 case 'G':   /* Temp directory */
                   2048:                     strcat(cmd,cfg.temp_dir);
                   2049:                     break;
                   2050:                 case 'H':   /* Port Handle or Hardware Flow Control */
                   2051:                     strcat(cmd,ultoa(client_socket_dup,str,10));
                   2052:                     break;
                   2053:                 case 'I':   /* IP address */
                   2054:                     strcat(cmd,cid);
                   2055:                     break;
                   2056:                 case 'J':
                   2057:                     strcat(cmd,cfg.data_dir);
                   2058:                     break;
                   2059:                 case 'K':
                   2060:                     strcat(cmd,cfg.ctrl_dir);
                   2061:                     break;
                   2062:                 case 'L':   /* Lines per message */
                   2063:                     strcat(cmd,ultoa(cfg.level_linespermsg[useron.level],str,10));
                   2064:                     break;
                   2065:                 case 'M':   /* Minutes (credits) for user */
                   2066:                     strcat(cmd,ultoa(useron.min,str,10));
                   2067:                     break;
                   2068:                 case 'N':   /* Node Directory (same as SBBSNODE environment var) */
                   2069:                     strcat(cmd,cfg.node_dir);
                   2070:                     break;
                   2071:                 case 'O':   /* SysOp */
                   2072:                     strcat(cmd,QUOTED_STRING(instr[i],cfg.sys_op,str,sizeof(str)));
                   2073:                     break;
                   2074:                 case 'P':   /* Client protocol */
                   2075:                     strcat(cmd,client.protocol);
                   2076:                     break;
                   2077:                 case 'Q':   /* QWK ID */
                   2078:                     strcat(cmd,cfg.sys_id);
                   2079:                     break;
                   2080:                 case 'R':   /* Rows */
                   2081:                     strcat(cmd,ultoa(rows,str,10));
                   2082:                     break;
                   2083:                 case 'S':   /* File Spec (or Baja command str) */
                   2084:                     strcat(cmd,fspec);
                   2085:                     break;
                   2086:                 case 'T':   /* Time left in seconds */
                   2087:                     gettimeleft();
                   2088:                     strcat(cmd,ultoa(timeleft,str,10));
                   2089:                     break;
                   2090:                 case 'U':   /* UART I/O Address (in hex) */
                   2091:                     strcat(cmd,ultoa(cfg.com_base,str,16));
                   2092:                     break;
                   2093:                 case 'V':   /* Synchronet Version */
                   2094:                     sprintf(str,"%s%c",VERSION,REVISION);
                   2095:                     break;
                   2096:                 case 'W':   /* Time-slice API type (mswtype) */
                   2097:                     break;
                   2098:                 case 'X':
                   2099:                     strcat(cmd,cfg.shell[useron.shell]->code);
                   2100:                     break;
                   2101:                 case '&':   /* Address of msr */
                   2102:                     break;
                   2103:                 case 'Y':
                   2104:                     strcat(cmd,comspec);
                   2105:                     break;
                   2106:                 case 'Z':
                   2107:                     strcat(cmd,cfg.text_dir);
                   2108:                     break;
                   2109:                                case '~':       /* DOS-compatible (8.3) filename */
                   2110: #ifdef _WIN32
                   2111:                                        char sfpath[MAX_PATH+1];
                   2112:                                        SAFECOPY(sfpath,fpath);
                   2113:                                        GetShortPathName(fpath,sfpath,sizeof(sfpath));
                   2114:                                        strcat(cmd,sfpath);
                   2115: #else
                   2116:                     strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str)));
                   2117: #endif                 
                   2118:                                        break;
                   2119:                 case '!':   /* EXEC Directory */
                   2120:                     strcat(cmd,cfg.exec_dir);
                   2121:                     break;
                   2122:                 case '@':   /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
                   2123: #ifndef __unix__
                   2124:                     strcat(cmd,cfg.exec_dir);
                   2125: #endif
                   2126:                     break;
                   2127: 
                   2128:                 case '#':   /* Node number (same as SBBSNNUM environment var) */
                   2129:                     sprintf(str,"%d",cfg.node_num);
                   2130:                     strcat(cmd,str);
                   2131:                     break;
                   2132:                 case '*':
                   2133:                     sprintf(str,"%03d",cfg.node_num);
                   2134:                     strcat(cmd,str);
                   2135:                     break;
                   2136:                 case '$':   /* Credits */
                   2137:                     strcat(cmd,ultoa(useron.cdt+useron.freecdt,str,10));
                   2138:                     break;
                   2139:                 case '%':   /* %% for percent sign */
                   2140:                     strcat(cmd,"%");
                   2141:                     break;
                   2142:                                case '.':       /* .exe for DOS/OS2/Win32, blank for Unix */
                   2143: #ifndef __unix__
                   2144:                                        strcat(cmd,".exe");
                   2145: #endif
                   2146:                                        break;
                   2147:                                case '?':       /* Platform */
                   2148: #ifdef __OS2__
                   2149:                                        strcpy(str,"OS2");
                   2150: #else
                   2151:                                        strcpy(str,PLATFORM_DESC);
                   2152: #endif
                   2153:                                        strlwr(str);
                   2154:                                        strcat(cmd,str);
                   2155:                                        break;
                   2156:                 default:    /* unknown specification */
                   2157:                     if(isdigit(instr[i])) {
                   2158:                         sprintf(str,"%0*d",instr[i]&0xf,useron.number);
                   2159:                         strcat(cmd,str); }
                   2160:                     break; }
                   2161:             j=strlen(cmd); }
                   2162:         else
                   2163:             cmd[j++]=instr[i]; }
                   2164:     cmd[j]=0;
                   2165: 
                   2166:     return(cmd);
                   2167: }
                   2168: 
                   2169: /****************************************************************************/
                   2170: /* Returns command line generated from instr with %c replacments            */
                   2171: /* This is the C-exported version                                                                                      */
                   2172: /****************************************************************************/
                   2173: extern "C" 
                   2174: char* DLLCALL cmdstr(scfg_t* cfg, user_t* user, const char* instr, const char* fpath
                   2175:                                                ,const char* fspec, char* cmd)
                   2176: {
                   2177:        char    str[MAX_PATH+1];
                   2178:     int                i,j,len;
                   2179: 
                   2180:     len=strlen(instr);
                   2181:     for(i=j=0;i<len && j<MAX_PATH;i++) {
                   2182:         if(instr[i]=='%') {
                   2183:             i++;
                   2184:             cmd[j]=0;
                   2185:                        char ch=instr[i];
                   2186:                        if(isalpha(ch))
                   2187:                                ch=toupper(ch);
                   2188:             switch(ch) {
                   2189:                 case 'A':   /* User alias */
                   2190:                                        if(user!=NULL)
                   2191:                                                strcat(cmd,QUOTED_STRING(instr[i],user->alias,str,sizeof(str)));
                   2192:                     break;
                   2193:                 case 'B':   /* Baud (DTE) Rate */
                   2194:                     break;
                   2195:                 case 'C':   /* Connect Description */
                   2196:                     break;
                   2197:                 case 'D':   /* Connect (DCE) Rate */
                   2198:                     break;
                   2199:                 case 'E':   /* Estimated Rate */
                   2200:                     break;
                   2201:                 case 'F':   /* File path */
                   2202:                     strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str)));
                   2203:                     break;
                   2204:                 case 'G':   /* Temp directory */
                   2205:                     strcat(cmd,cfg->temp_dir);
                   2206:                     break;
                   2207:                 case 'H':   /* Port Handle or Hardware Flow Control */
                   2208:                     break;
                   2209:                 case 'I':   /* IP address */
                   2210:                     break;
                   2211:                 case 'J':
                   2212:                     strcat(cmd,cfg->data_dir);
                   2213:                     break;
                   2214:                 case 'K':
                   2215:                     strcat(cmd,cfg->ctrl_dir);
                   2216:                     break;
                   2217:                 case 'L':   /* Lines per message */
                   2218:                                        if(user!=NULL)
                   2219:                                                strcat(cmd,ultoa(cfg->level_linespermsg[user->level],str,10));
                   2220:                     break;
                   2221:                 case 'M':   /* Minutes (credits) for user */
                   2222:                                        if(user!=NULL)
                   2223:                                                strcat(cmd,ultoa(user->min,str,10));
                   2224:                     break;
                   2225:                 case 'N':   /* Node Directory (same as SBBSNODE environment var) */
                   2226:                     strcat(cmd,cfg->node_dir);
                   2227:                     break;
                   2228:                 case 'O':   /* SysOp */
                   2229:                     strcat(cmd,QUOTED_STRING(instr[i],cfg->sys_op,str,sizeof(str)));
                   2230:                     break;
                   2231:                 case 'P':   /* Client protocol */
                   2232:                     break;
                   2233:                 case 'Q':   /* QWK ID */
                   2234:                     strcat(cmd,cfg->sys_id);
                   2235:                     break;
                   2236:                 case 'R':   /* Rows */
                   2237:                                        if(user!=NULL)
                   2238:                                                strcat(cmd,ultoa(user->rows,str,10));
                   2239:                     break;
                   2240:                 case 'S':   /* File Spec */
                   2241:                     strcat(cmd,fspec);
                   2242:                     break;
                   2243:                 case 'T':   /* Time left in seconds */
                   2244:                     break;
                   2245:                 case 'U':   /* UART I/O Address (in hex) */
                   2246:                     strcat(cmd,ultoa(cfg->com_base,str,16));
                   2247:                     break;
                   2248:                 case 'V':   /* Synchronet Version */
                   2249:                     sprintf(str,"%s%c",VERSION,REVISION);
                   2250:                     break;
                   2251:                 case 'W':   /* Time-slice API type (mswtype) */
                   2252:                     break;
                   2253:                 case 'X':
                   2254:                                        if(user!=NULL)
                   2255:                                                strcat(cmd,cfg->shell[user->shell]->code);
                   2256:                     break;
                   2257:                 case '&':   /* Address of msr */
                   2258:                     break;
                   2259:                 case 'Y':
                   2260:                     break;
                   2261:                 case 'Z':
                   2262:                     strcat(cmd,cfg->text_dir);
                   2263:                     break;
                   2264:                                case '~':       /* DOS-compatible (8.3) filename */
                   2265: #ifdef _WIN32
                   2266:                                        char sfpath[MAX_PATH+1];
                   2267:                                        SAFECOPY(sfpath,fpath);
                   2268:                                        GetShortPathName(fpath,sfpath,sizeof(sfpath));
                   2269:                                        strcat(cmd,sfpath);
                   2270: #else
                   2271:                     strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str)));
                   2272: #endif                 
                   2273:                                        break;
                   2274:                 case '!':   /* EXEC Directory */
                   2275:                     strcat(cmd,cfg->exec_dir);
                   2276:                     break;
                   2277:                 case '@':   /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
                   2278: #ifndef __unix__
                   2279:                     strcat(cmd,cfg->exec_dir);
                   2280: #endif
                   2281:                     break;
                   2282: 
                   2283:                 case '#':   /* Node number (same as SBBSNNUM environment var) */
                   2284:                     sprintf(str,"%d",cfg->node_num);
                   2285:                     strcat(cmd,str);
                   2286:                     break;
                   2287:                 case '*':
                   2288:                     sprintf(str,"%03d",cfg->node_num);
                   2289:                     strcat(cmd,str);
                   2290:                     break;
                   2291:                 case '$':   /* Credits */
                   2292:                                        if(user!=NULL)
                   2293:                                                strcat(cmd,ultoa(user->cdt+user->freecdt,str,10));
                   2294:                     break;
                   2295:                 case '%':   /* %% for percent sign */
                   2296:                     strcat(cmd,"%");
                   2297:                     break;
                   2298:                                case '.':       /* .exe for DOS/OS2/Win32, blank for Unix */
                   2299: #ifndef __unix__
                   2300:                                        strcat(cmd,".exe");
                   2301: #endif
                   2302:                                        break;
                   2303:                                case '?':       /* Platform */
                   2304: #ifdef __OS2__
                   2305:                                        strcpy(str,"OS2");
                   2306: #else
                   2307:                                        strcpy(str,PLATFORM_DESC);
                   2308: #endif
                   2309:                                        strlwr(str);
                   2310:                                        strcat(cmd,str);
                   2311:                                        break;
                   2312:                 default:    /* unknown specification */
                   2313:                     if(isdigit(instr[i]) && user!=NULL) {
                   2314:                         sprintf(str,"%0*d",instr[i]&0xf,user->number);
                   2315:                         strcat(cmd,str); 
                   2316:                                        }
                   2317:                     break; 
                   2318:                        }
                   2319:             j=strlen(cmd); 
                   2320:                }
                   2321:         else
                   2322:             cmd[j++]=instr[i]; 
                   2323:        }
                   2324:     cmd[j]=0;
                   2325: 
                   2326:     return(cmd);
                   2327: }
                   2328: 

unix.superglobalmegacorp.com

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