home *** CD-ROM | disk | FTP | other *** search
/ synchro.net / synchro.net.tar / synchro.net / main / BBS / ODOORS62.ZIP / ODInEx2.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-19  |  56.8 KB  |  1,432 lines

  1. /* OpenDoors Online Software Programming Toolkit
  2.  * (C) Copyright 1991 - 1999 by Brian Pirie.
  3.  *
  4.  * Oct-2001 door32.sys/socket modifications by Rob Swindell (www.synchro.net)
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  *
  20.  *
  21.  *        File: ODInEx2.c
  22.  *
  23.  * Description: Performs OpenDoors initialization and shutdown operations
  24.  *              (od_init() and od_exit()), including drop file I/O. This
  25.  *              module is broken into two files, ODInEx1.c and ODInEx2.c.
  26.  *
  27.  *   Revisions: Date          Ver   Who  Change
  28.  *              ---------------------------------------------------------------
  29.  *              Oct 13, 1994  6.00  BP   New file header format.
  30.  *              Oct 21, 1994  6.00  BP   Further isolated com routines.
  31.  *              Oct 29, 1994  6.00  BP   New EXITINFO.BBS timelimit writing.
  32.  *              Nov 01, 1994  6.00  BP   New directory access functions.
  33.  *              Dec 09, 1994  6.00  BP   Standardized coding style.
  34.  *              Dec 13, 1994  6.00  BP   Remove include of dir.h.
  35.  *              Dec 31, 1994  6.00  BP   Add DIR_ATTRIB_ARCH in file search.
  36.  *              Dec 31, 1994  6.00  BP   Move _mt_init to new func in odplat.c
  37.  *              Jan 01, 1995  6.00  BP   _waitdrain() -> ODWaitDrain().
  38.  *              Aug 19, 1995  6.00  BP   32-bit portability.
  39.  *              Nov 11, 1995  6.00  BP   Removed register keyword.
  40.  *              Nov 14, 1995  6.00  BP   Added include of odscrn.h.
  41.  *              Nov 16, 1995  6.00  BP   Removed oddoor.h, added odcore.h.
  42.  *              Nov 17, 1995  6.00  BP   Use new input queue mechanism.
  43.  *              Dec 30, 1995  6.00  BP   Added ODCALL for calling convention.
  44.  *              Jan 01, 1996  6.00  BP   Added od_disable_dtr, DIS_DTR_DISABLE.
  45.  *              Jan 04, 1996  6.00  BP   tODInQueueEvent -> tODInputEvent.
  46.  *              Jan 19, 1996  6.00  BP   Don't use atexit() under Win32.
  47.  *              Jan 21, 1996  6.00  BP   Try DTR disable sequence twice.
  48.  *              Jan 21, 1996  6.00  BP   Use ODScrnShowMessage().
  49.  *              Jan 23, 1996  6.00  BP   Added od_exiting.
  50.  *              Jan 23, 1996  6.00  BP   Use ODProcessExit() instead of exit().
  51.  *              Jan 30, 1996  6.00  BP   Replaced od_yield() with od_sleep().
  52.  *              Jan 30, 1996  6.00  BP   Add ODInQueueGetNextEvent() timeout.
  53.  *              Jan 31, 1996  6.00  BP   Support new SFDOORS.DAT format.
  54.  *              Feb 02, 1996  6.00  BP   Added RA 2.50 EXITINFO.BBS support.
  55.  *              Feb 09, 1996  6.00  BP   Correctly translate RA 2.x sex field.
  56.  *              Feb 19, 1996  6.00  BP   Changed version number to 6.00.
  57.  *              Feb 23, 1996  6.00  BP   Make DTR disable code shared.
  58.  *              Mar 03, 1996  6.10  BP   Begin version 6.10.
  59.  *              Mar 06, 1996  6.10  BP   Added TRIBBS.SYS support.
  60.  *              Mar 27, 1996  6.10  BP   Added WCNODEID to
  61.  *              Jan 13, 1997  6.10  BP   Fixes for Door32 support.
  62.  *              Oct 19, 2001  6.20  RS   Added TCP/IP socket (telnet) support.
  63.  */
  64.  
  65. #define BUILDING_OPENDOORS
  66.  
  67. #include <stdio.h>
  68. #include <stdlib.h>
  69. #include <stdarg.h>
  70. #include <string.h>
  71. #include <ctype.h>
  72. #include <time.h>
  73.  
  74. #include "OpenDoor.h"
  75. #include "ODCore.h"
  76. #include "ODGen.h"
  77. #include "ODCom.h"
  78. #include "ODPlat.h"
  79. #include "ODTypes.h"
  80. #include "ODScrn.h"
  81. #include "ODInQue.h"
  82. #include "ODKrnl.h"
  83. #include "ODInEx.h"
  84. #include "ODUtil.h"
  85.  
  86.  
  87. /* Time difference leeway for door information files to be considered to */
  88. /* have been written during the same exit (door execution session).      */
  89. #define DROPFILE_TIME_LEEWAY 10
  90.  
  91. /* Maximum length of modem response string. */
  92. #define MAX_RESPONSE_LEN 40
  93.  
  94. /* Maximum time to wait for modem response string, in milliseconds. */
  95. #define RESPONSE_TIMEOUT 2000
  96.  
  97.  
  98. /* Environment variables that specify directories where drop files may be */
  99. /* found.                                                                 */
  100. static char *apszEnvVarNames[] =
  101. {
  102.    "RA",
  103.    "QUICK",
  104.    "PCB",
  105.    "BBS",
  106.    "WCNODEID",
  107. };
  108. #define NUM_DIR_ENV_VARS DIM(apszEnvVarNames)
  109.  
  110.  
  111. /* Local helper functions. */
  112. static INT ODSearchInDir(char **papszFileNames, INT nNumFileNames,
  113.    char *pszFound, char *pszDirectory);
  114.  
  115. /* Currently, the following functions are only used in the Win32 version. */
  116. #ifdef ODPLAT_WIN32
  117. static BOOL ODSendModemCommand(char *pszCommand, int nRetries);
  118. static BOOL ODSendModemCommandOnce(char *pszCommand);
  119. static BOOL ODWaitForString(char *pszResponse, tODMilliSec ResponseTimeout);
  120. #endif /* ODPLAT_WIN32 */
  121.  
  122. #ifdef OD_DIAGNOSTICS
  123. static char szDebugWorkString[500] = "";
  124. #endif /* OD_DIAGNOSTICS */
  125.  
  126.  
  127.  
  128. /* ----------------------------------------------------------------------------
  129.  * od_exit()
  130.  *
  131.  * Shuts down OpenDoors operations. Normally, the program is exited as soon
  132.  * as OpenDoors is shutdown.
  133.  *
  134.  * Parameters: nErrorLevel - Result code to exit program with.
  135.  *
  136.  *             bTermCall   - TRUE to disconnect the user before exiting,
  137.  *                           FALSE to leave the user connected.
  138.  *
  139.  *     Return: void
  140.  */
  141. ODAPIDEF void ODCALL od_exit(INT nErrorLevel, BOOL bTermCall)
  142. {
  143.    BYTE btCount;
  144.    FILE *pfDropFile;
  145.    time_t nMaxTime;
  146.    time_t nDoorEndTime;
  147.    void *pWindow = NULL;
  148.    DWORD dwActiveMinutes;
  149.    static BOOL bExiting = FALSE;
  150.  
  151.    /* Log function entry if running in trace mode */
  152.    TRACE(TRACE_API, "od_exit()");
  153.  
  154. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  155.    if(od_control.od_internal_debug)
  156.    {
  157.       MessageBox(NULL, "Starting up od_exit()", "OpenDoors Diagnostics",
  158.          MB_OK);
  159.    }
  160. #endif
  161.  
  162.    /* If this is a recursive od_exit() call, then ignore it. */
  163.    if(bExiting)
  164.    {
  165.       return;
  166.    }
  167.    bExiting = TRUE;
  168.  
  169.    /* If user called od_exit() before doing anything else, then we first */
  170.    /* initialize OpenDoors in order to shutdown and exit.                */
  171.    if(!bODInitialized) od_init();
  172.  
  173.    /* Update remaining time. */
  174.    od_control.user_timelimit += od_control.od_maxtime_deduction;
  175.  
  176.    /* Calculate deducted time */
  177.    time(&nDoorEndTime);
  178.    ODDWordDivide(&dwActiveMinutes, NULL, nDoorEndTime - nStartupUnixTime, 60L);
  179.    od_control.user_time_used += ((nInitialRemaining
  180.       - od_control.user_timelimit) - (int)dwActiveMinutes);
  181.  
  182.    /* Reset to original bps rate that was stored in drop file */
  183.    od_control.baud = dwFileBPS;
  184.  
  185.    /* If function hook is defined. */
  186.    if(od_control.od_before_exit != NULL)
  187.    {
  188.       /* Then call it. */
  189.       (*od_control.od_before_exit)();
  190.    }
  191.  
  192.    if(bTermCall && od_control.od_hanging_up != NULL)
  193.    {
  194.       pWindow = ODScrnShowMessage(od_control.od_hanging_up, 0);
  195.    }
  196.    else if(!bTermCall)
  197.    {
  198.       pWindow = ODScrnShowMessage(od_control.od_exiting, 0);
  199.    }
  200.  
  201.    if(szOriginalDir != NULL)
  202.    {
  203.       ODDirChangeCurrent(szOriginalDir);
  204.       free(szOriginalDir);
  205.       szOriginalDir=NULL;
  206.    }
  207.  
  208.    if(od_control.od_extended_info)     /* Update EXITINFO.BBS, if applicable */
  209.    {
  210.       ODMakeFilename(szExitinfoBBSPath, szExitinfoBBSPath, "EXITINFO.BBS",
  211.          sizeof(szExitinfoBBSPath));
  212.       if((pfDropFile = fopen(szExitinfoBBSPath, "r+b")) != NULL)
  213.       {
  214.          switch(od_control.od_info_type)
  215.          {
  216.             case RA2EXITINFO:
  217.                pRA2ExitInfoRecord->baud = (unsigned int)od_control.baud;
  218.                pRA2ExitInfoRecord->num_calls = od_control.system_calls;
  219.                ODStringCToPascal(pRA2ExitInfoRecord->last_caller,35,od_control.system_last_caller);
  220.                ODStringCToPascal(pRA2ExitInfoRecord->sLastHandle,35,od_control.system_last_handle);
  221.                ODStringCToPascal(pRA2ExitInfoRecord->start_date,8,od_control.timelog_start_date);
  222.                memcpy(&pRA2ExitInfoRecord->busyperhour,&od_control.timelog_busyperhour,62);
  223.                ODStringCToPascal(pRA2ExitInfoRecord->name,35,od_control.user_name);
  224.                ODStringCToPascal(pRA2ExitInfoRecord->location,25,od_control.user_location);
  225.                ODStringCToPascal(pRA2ExitInfoRecord->organisation,50,od_control.user_org);
  226.                for(btCount=0;btCount<3;++btCount)
  227.                   ODStringCToPascal(pRA2ExitInfoRecord->address[btCount],50,od_control.user_address[btCount]);
  228.                ODStringCToPascal(pRA2ExitInfoRecord->handle,35,od_control.user_handle);
  229.                ODStringCToPascal(pRA2ExitInfoRecord->comment,80,od_control.user_comment);
  230.                pRA2ExitInfoRecord->password_crc=od_control.user_pwd_crc;
  231.                ODStringCToPascal(pRA2ExitInfoRecord->dataphone,15,od_control.user_dataphone);
  232.                ODStringCToPascal(pRA2ExitInfoRecord->homephone,15,od_control.user_homephone);
  233.                ODStringCToPascal(pRA2ExitInfoRecord->lasttime,5,od_control.user_lasttime);
  234.                ODStringCToPascal(pRA2ExitInfoRecord->lastdate,8,od_control.user_lastdate);
  235.                pRA2ExitInfoRecord->attrib=od_control.user_attribute;
  236.                pRA2ExitInfoRecord->attrib2=od_control.user_attrib2;
  237.                memcpy(&pRA2ExitInfoRecord->flags,&od_control.user_flags,14);
  238.                pRA2ExitInfoRecord->sec=od_control.user_security;
  239.                pRA2ExitInfoRecord->lastread=od_control.user_lastread;
  240.                memcpy(&pRA2ExitInfoRecord->nocalls,&od_control.user_numcalls,29);
  241.                pRA2ExitInfoRecord->group=od_control.user_group;
  242.                memcpy(&pRA2ExitInfoRecord->combinedrecord,&od_control.user_combinedrecord,200);
  243.                ODStringCToPascal(pRA2ExitInfoRecord->firstcall,8,od_control.user_firstcall);
  244.                ODStringCToPascal(pRA2ExitInfoRecord->birthday,8,od_control.user_birthday);
  245.                ODStringCToPascal(pRA2ExitInfoRecord->subdate,8,od_control.user_subdate);
  246.                pRA2ExitInfoRecord->screenwidth=od_control.user_screenwidth;
  247.                pRA2ExitInfoRecord->language=od_control.user_language;
  248.                pRA2ExitInfoRecord->dateformat=od_control.user_date_format;
  249.                ODStringCToPascal(pRA2ExitInfoRecord->forwardto,35,od_control.user_forward_to);
  250.                memcpy(&pRA2ExitInfoRecord->msgarea,&od_control.user_msg_area,15);
  251.                pRA2ExitInfoRecord->sex = (od_control.user_sex == 'M') ? 1 : 2;
  252.                pRA2ExitInfoRecord->btAttribute3=od_control.user_attrib3;
  253.                ODStringCToPascal(pRA2ExitInfoRecord->sPassword,15,od_control.user_password);
  254.                pRA2ExitInfoRecord->status=od_control.event_status;
  255.                ODStringCToPascal(pRA2ExitInfoRecord->starttime,5,od_control.event_starttime);
  256.                memcpy(&pRA2ExitInfoRecord->errorlevel,&od_control.event_errorlevel,3);
  257.                ODStringCToPascal(pRA2ExitInfoRecord->lasttimerun,8,od_control.event_last_run);
  258.                memcpy(&pRA2ExitInfoRecord->netmailentered,&od_control.user_netmailentered,2);
  259.                ODStringCToPascal(pRA2ExitInfoRecord->logintime,5,od_control.user_logintime);
  260.                ODStringCToPascal(pRA2ExitInfoRecord->logindate,8,od_control.user_logindate);
  261.                memcpy(&pRA2ExitInfoRecord->timelimit,&od_control.user_timelimit,6);
  262.                memcpy(&pRA2ExitInfoRecord->userrecord,&od_control.user_num,8);
  263.                ODStringCToPascal(pRA2ExitInfoRecord->timeofcreation,5,od_control.user_timeofcreation);
  264.                pRA2ExitInfoRecord->logonpasswordcrc=od_control.user_logon_pwd_crc;
  265.                pRA2ExitInfoRecord->wantchat=od_control.user_wantchat;
  266.                pRA2ExitInfoRecord->deducted_time=od_control.user_deducted_time;
  267.                for(btCount=0;btCount<50;++btCount)
  268.                   ODStringCToPascal(pRA2ExitInfoRecord->menustack[btCount],8,od_control.user_menustack[btCount]);
  269.                pRA2ExitInfoRecord->menustackpointer=od_control.user_menustackpointer;
  270.                memcpy(&pRA2ExitInfoRecord->error_free,&od_control.user_error_free,3);
  271.                ODStringCToPascal(pRA2ExitInfoRecord->emsi_crtdef,40,od_control.user_emsi_crtdef);
  272.                ODStringCToPascal(pRA2ExitInfoRecord->emsi_protocols,40,od_control.user_emsi_protocols);
  273.                ODStringCToPascal(pRA2ExitInfoRecord->emsi_capabilities,40,od_control.user_emsi_capabilities);
  274.                ODStringCToPascal(pRA2ExitInfoRecord->emsi_requests,40,od_control.user_emsi_requests);
  275.                ODStringCToPascal(pRA2ExitInfoRecord->emsi_software,40,od_control.user_emsi_software);
  276.                memcpy(&pRA2ExitInfoRecord->hold_attr1,&od_control.user_hold_attr1,3);
  277.                ODStringCToPascal(pRA2ExitInfoRecord->page_reason,77,od_control.user_reasonforchat);
  278.                if(bRAStatus)
  279.                {
  280.                   pRA2ExitInfoRecord->status_line = btCurrentStatusLine + 1;
  281.                }
  282.  
  283.                ODStringCToPascal(pRA2ExitInfoRecord->last_cost_menu,9,od_control.user_last_cost_menu);
  284.                pRA2ExitInfoRecord->menu_cost_per_min=od_control.user_menu_cost;
  285.                pRA2ExitInfoRecord->has_rip=od_control.user_rip;
  286.                pRA2ExitInfoRecord->btRIPVersion=od_control.user_rip_ver;
  287.  
  288.                fwrite(pRA2ExitInfoRecord,1,sizeof(tRA2ExitInfoRecord),pfDropFile);
  289.                free(pRA2ExitInfoRecord);
  290.                break;
  291.  
  292.             case EXITINFO:
  293.                ODStringCToPascal(pExitInfoRecord->bbs.ra.timeofcreation,5,od_control.user_timeofcreation);
  294.                ODStringCToPascal(pExitInfoRecord->bbs.ra.logonpassword,15,od_control.user_logonpassword);
  295.                pExitInfoRecord->bbs.ra.wantchat=od_control.user_wantchat;
  296.  
  297.                ODWriteExitInfoPrimitive(pfDropFile,476);
  298.                break;
  299.  
  300.  
  301.             case RA1EXITINFO:
  302.                pExtendedExitInfo->deducted_time=od_control.user_deducted_time;
  303.  
  304.                for(btCount=0;btCount<50;++btCount)
  305.                {
  306.                   ODStringCToPascal(pExtendedExitInfo->menustack[btCount],8,od_control.user_menustack[btCount]);
  307.                }
  308.  
  309.                pExtendedExitInfo->menustackpointer=od_control.user_menustackpointer;
  310.                ODStringCToPascal(pExtendedExitInfo->userhandle,35,od_control.user_handle);
  311.                ODStringCToPascal(pExtendedExitInfo->comment,80,od_control.user_comment);
  312.                ODStringCToPascal(pExtendedExitInfo->firstcall,8,od_control.user_firstcall);
  313.                memcpy(pExtendedExitInfo->combinedrecord,od_control.user_combinedrecord,25);
  314.                ODStringCToPascal(pExtendedExitInfo->birthday,8,od_control.user_birthday);
  315.                ODStringCToPascal(pExtendedExitInfo->subdate,8,od_control.user_subdate);
  316.                pExtendedExitInfo->screenwidth=od_control.user_screenwidth;
  317.                pExtendedExitInfo->msgarea = (BYTE)od_control.user_msg_area;
  318.                pExtendedExitInfo->filearea = (BYTE)od_control.user_file_area;
  319.                pExtendedExitInfo->language=od_control.user_language;
  320.                pExtendedExitInfo->dateformat=od_control.user_date_format;
  321.                ODStringCToPascal(pExtendedExitInfo->forwardto,35,od_control.user_forward_to);
  322.                memcpy(&pExtendedExitInfo->error_free,&od_control.user_error_free,3);
  323.                ODStringCToPascal(pExtendedExitInfo->emsi_crtdef,40,od_control.user_emsi_crtdef);
  324.                ODStringCToPascal(pExtendedExitInfo->emsi_protocols,40,od_control.user_emsi_protocols);
  325.                ODStringCToPascal(pExtendedExitInfo->emsi_capabilities,40,od_control.user_emsi_capabilities);
  326.                ODStringCToPascal(pExtendedExitInfo->emsi_requests,40,od_control.user_emsi_requests);
  327.                ODStringCToPascal(pExtendedExitInfo->emsi_software,40,od_control.user_emsi_software);
  328.                memcpy(&pExtendedExitInfo->hold_attr1,&od_control.user_hold_attr1,3);
  329.  
  330.                ODStringCToPascal(pExitInfoRecord->bbs.ra.timeofcreation,5,od_control.user_timeofcreation);
  331.                ODStringCToPascal(pExitInfoRecord->bbs.ra.logonpassword,15,od_control.user_logonpassword);
  332.                pExitInfoRecord->bbs.ra.wantchat=od_control.user_wantchat;
  333.  
  334.                ODWriteExitInfoPrimitive(pfDropFile,476);
  335.                fwrite(pExtendedExitInfo,1,1017,pfDropFile);
  336.                free(pExtendedExitInfo);
  337.                break;
  338.  
  339.  
  340.             case QBBS275EXITINFO:
  341.                pExitInfoRecord->elapsed=nInitialElapsed;
  342.                pExitInfoRecord->bbs.qbbs.qwantchat=od_control.user_wantchat;
  343.                pExitInfoRecord->bbs.qbbs.gosublevel=od_control.user_menustackpointer;
  344.                for(btCount=0;btCount<pExitInfoRecord->bbs.qbbs.gosublevel;++btCount)
  345.                {
  346.                   ODStringCToPascal(pExitInfoRecord->bbs.qbbs.menustack[btCount],8,od_control.user_menustack[btCount]);
  347.                }
  348.                ODStringCToPascal(pExitInfoRecord->bbs.qbbs.menu,8,od_control.user_menustack[od_control.user_menustackpointer]);
  349.                pExitInfoRecord->bbs.qbbs.externlogoff = bTermCall ? 1 : 0;
  350.                pExitInfoRecord->bbs.qbbs.ripactive = od_control.user_rip ? 1 : 0;
  351.  
  352.                ODWriteExitInfoPrimitive(pfDropFile,644);
  353.          }
  354.  
  355.          fclose(pfDropFile);
  356.       }
  357.    }
  358.  
  359.  
  360.    switch(od_control.od_info_type)
  361.    {
  362.       case DOORSYS_GAP:
  363.       case DOORSYS_WILDCAT:
  364.          pfDropFile=fopen(szDropFilePath,"w");
  365.          if(od_control.baud==0L)
  366.          {
  367.             fprintf(pfDropFile,"COM0:\n");
  368.          }
  369.          else
  370.          {
  371.             fprintf(pfDropFile,"COM%d:\n",od_control.port+1);
  372.          }
  373.          fprintf(pfDropFile,"%s",apszDropFileInfo[0]);
  374.          fprintf(pfDropFile,"%s",apszDropFileInfo[1]);
  375.          fprintf(pfDropFile,"%u\n",od_control.od_node);
  376.          switch(btDoorSYSLock)
  377.          {
  378.             case 0:
  379.                fprintf(pfDropFile,"%lu\n",od_control.baud);
  380.                break;
  381.             case 1:
  382.                fprintf(pfDropFile,"N\n");
  383.                break;
  384.             case 2:
  385.                fprintf(pfDropFile,"Y\n");
  386.          }
  387.          fprintf(pfDropFile,"%s",apszDropFileInfo[3]);
  388.          fprintf(pfDropFile,"%s",apszDropFileInfo[4]);
  389.          fprintf(pfDropFile,"%s",apszDropFileInfo[5]);
  390.          fprintf(pfDropFile,"%s",apszDropFileInfo[22]);
  391.          strupr(od_control.user_name);
  392.          fprintf(pfDropFile,"%s\n",od_control.user_name);
  393.          fprintf(pfDropFile,"%s\n",od_control.user_location);
  394.          fprintf(pfDropFile,"%s\n",od_control.user_homephone);
  395.          fprintf(pfDropFile,"%s\n",od_control.user_dataphone);
  396.          fprintf(pfDropFile,"%s\n",od_control.user_password);
  397.          fprintf(pfDropFile,"%u\n",od_control.user_security);
  398.          fprintf(pfDropFile,"%d\n",od_control.user_numcalls);
  399.          fprintf(pfDropFile,"%s\n",od_control.user_lastdate);
  400.          fprintf(pfDropFile,"%u\n",(signed int)od_control.user_timelimit*60);
  401.          fprintf(pfDropFile,"%d\n",od_control.user_timelimit);
  402.          if(od_control.user_rip)
  403.          {
  404.             fprintf(pfDropFile,"RIP\n");
  405.          }
  406.          else if(od_control.user_ansi)
  407.          {
  408.             fprintf(pfDropFile,"GR\n");
  409.          }
  410.          else
  411.          {
  412.             fprintf(pfDropFile,"NG\n");
  413.          }
  414.          fprintf(pfDropFile,"%d\n",od_control.user_screen_length);
  415.          fprintf(pfDropFile,"%s",apszDropFileInfo[8]);
  416.          fprintf(pfDropFile,"%s",apszDropFileInfo[9]);
  417.          fprintf(pfDropFile,"%s",apszDropFileInfo[10]);
  418.          fprintf(pfDropFile,"%s\n",od_control.user_subdate);
  419.          fprintf(pfDropFile,"%u\n",od_control.user_num);
  420.          fprintf(pfDropFile,"%s",apszDropFileInfo[6]);
  421.          fprintf(pfDropFile,"%u\n",od_control.user_uploads);
  422.          fprintf(pfDropFile,"%u\n",od_control.user_downloads);
  423.          fprintf(pfDropFile,"%u\n",od_control.user_todayk);
  424.          fprintf(pfDropFile,"%s",apszDropFileInfo[21]);
  425.  
  426.  
  427.          if(od_control.od_info_type==DOORSYS_WILDCAT)
  428.          {
  429.             fprintf(pfDropFile,"%s\n",od_control.user_birthday);
  430.             fprintf(pfDropFile,"%s",apszDropFileInfo[11]);
  431.             fprintf(pfDropFile,"%s",apszDropFileInfo[12]);
  432.             fprintf(pfDropFile,"%s\n",od_control.sysop_name);
  433.             strupr(od_control.user_handle);
  434.             fprintf(pfDropFile,"%s\n",od_control.user_handle);
  435.             fprintf(pfDropFile,"%s\n",od_control.event_starttime);
  436.             if(od_control.user_error_free)
  437.                fprintf(pfDropFile,"Y\n");
  438.             else
  439.                fprintf(pfDropFile,"N\n");
  440.             fprintf(pfDropFile,"%s",apszDropFileInfo[7]);
  441.             fprintf(pfDropFile,"%s",apszDropFileInfo[13]);
  442.             fprintf(pfDropFile,"%s",apszDropFileInfo[14]);
  443.             fprintf(pfDropFile,"%s",apszDropFileInfo[15]);
  444.             fprintf(pfDropFile,"%s",apszDropFileInfo[16]);
  445.             fprintf(pfDropFile,"%s\n",od_control.user_logintime);
  446.             fprintf(pfDropFile,"%s\n",od_control.user_lasttime);
  447.             fprintf(pfDropFile,"%s",apszDropFileInfo[18]);
  448.             fprintf(pfDropFile,"%s",apszDropFileInfo[19]);
  449.             fprintf(pfDropFile,"%u\n",od_control.user_upk);
  450.             fprintf(pfDropFile,"%u\n",od_control.user_downk);
  451.             fprintf(pfDropFile,"%s\n",od_control.user_comment);
  452.             fprintf(pfDropFile,"%s",apszDropFileInfo[20]);
  453.             fprintf(pfDropFile,"%u\n",od_control.user_messages);
  454.          }
  455.  
  456.          fclose(pfDropFile);
  457.          break;
  458.  
  459.  
  460.       case DOORSYS_DRWY:
  461.          pfDropFile=fopen(szDropFilePath,"w");
  462.          fprintf(pfDropFile,"%s\n",od_control.user_name);
  463.  
  464.          if(od_control.baud==0L)
  465.          {
  466.             fprintf(pfDropFile,"-1\n");
  467.          }
  468.          else
  469.          {
  470.             fprintf(pfDropFile,"%d\n",od_control.port+1);
  471.          }
  472.  
  473.          fprintf(pfDropFile,"%lu\n",od_control.baud);
  474.  
  475.          fprintf(pfDropFile,"%d\n",od_control.user_timelimit);
  476.  
  477.          if(od_control.user_ansi)
  478.          {
  479.             fprintf(pfDropFile,"G\n");
  480.          }
  481.          else
  482.          {
  483.             fprintf(pfDropFile,"M\n");
  484.          }
  485.  
  486.          fclose(pfDropFile);
  487.          break;
  488.  
  489.  
  490.       case SFDOORSDAT:
  491.          pfDropFile=fopen(szDropFilePath,"w");
  492.  
  493.          fprintf(pfDropFile,"%u\n",od_control.user_num);
  494.          fprintf(pfDropFile,"%s\n",od_control.user_name);
  495.          fprintf(pfDropFile,"%s\n",od_control.user_password);
  496.          fprintf(pfDropFile,"%s",apszDropFileInfo[0]);
  497.          fprintf(pfDropFile,"%lu\n",od_control.baud);
  498.          fprintf(pfDropFile,"%d\n",od_control.port+1);
  499.          fprintf(pfDropFile,"%d\n",od_control.user_timelimit);
  500.          fprintf(pfDropFile,"%s",apszDropFileInfo[13]);
  501.          fprintf(pfDropFile,"%s",apszDropFileInfo[14]);
  502.          if(od_control.user_ansi)
  503.          {
  504.             fprintf(pfDropFile,"TRUE\n");
  505.          }
  506.          else
  507.          {
  508.             fprintf(pfDropFile,"FALSE\n");
  509.          }
  510.          fprintf(pfDropFile,"%u\n",od_control.user_security);
  511.          fprintf(pfDropFile,"%u\n",od_control.user_uploads);
  512.          fprintf(pfDropFile,"%u\n",od_control.user_downloads);
  513.          fprintf(pfDropFile,"%s",apszDropFileInfo[1]);
  514.          fprintf(pfDropFile,"%s",apszDropFileInfo[2]);
  515.          fprintf(pfDropFile,"%s",apszDropFileInfo[3]);
  516.          if(od_control.sysop_next)
  517.          {
  518.             fprintf(pfDropFile,"TRUE\n");
  519.          }
  520.          else
  521.          {
  522.             fprintf(pfDropFile,"FALSE\n");
  523.          }
  524.          fprintf(pfDropFile,"%s",apszDropFileInfo[4]);
  525.          fprintf(pfDropFile,"%s",apszDropFileInfo[5]);
  526.          fprintf(pfDropFile,"%s",apszDropFileInfo[6]);
  527.          if(od_control.user_error_free)
  528.          {
  529.             fprintf(pfDropFile,"TRUE\n");
  530.          }
  531.          else
  532.          {
  533.             fprintf(pfDropFile,"FALSE\n");
  534.          }
  535.  
  536.          fprintf(pfDropFile,"%u\n",od_control.user_msg_area);
  537.          fprintf(pfDropFile,"%u\n",od_control.user_file_area);
  538.          fprintf(pfDropFile,"%u\n",od_control.od_node);
  539.  
  540.          fprintf(pfDropFile,"%s",apszDropFileInfo[10]);
  541.          fprintf(pfDropFile,"%s",apszDropFileInfo[11]);
  542.          fprintf(pfDropFile,"%s",apszDropFileInfo[12]);
  543.          fprintf(pfDropFile,"%u\n",od_control.user_todayk);
  544.          fprintf(pfDropFile,"%u\n",od_control.user_upk);
  545.          fprintf(pfDropFile,"%u\n",od_control.user_downk);
  546.          fprintf(pfDropFile,"%s\n",od_control.user_homephone);
  547.          fprintf(pfDropFile,"%s\n",od_control.user_location);
  548.          if(apszDropFileInfo[15][0]!='\0')
  549.          {
  550.             fprintf(pfDropFile, "%s", apszDropFileInfo[15]);
  551.             fprintf(pfDropFile, od_control.user_rip ? "TRUE\n" : "FALSE\n");
  552.             fprintf(pfDropFile, od_control.user_wantchat ? "TRUE\n"
  553.                : "FALSE\n");
  554.             fprintf(pfDropFile, "%s", apszDropFileInfo[17]);
  555.             fprintf(pfDropFile, "%d\n", od_control.od_com_irq);
  556.             fprintf(pfDropFile, "%d\n", od_control.od_com_address);
  557.             fprintf(pfDropFile, "%s", apszDropFileInfo[18]);
  558.          }
  559.          fclose(pfDropFile);
  560.          break;
  561.  
  562.  
  563.         case CHAINTXT:
  564.            pfDropFile=fopen(szDropFilePath,"w");
  565.            fprintf(pfDropFile,"%d\n",od_control.user_num);
  566.            fprintf(pfDropFile,"%s\n",od_control.user_handle);
  567.            fprintf(pfDropFile,"%s\n",od_control.user_name);
  568.            fprintf(pfDropFile,"%s\n",od_control.user_callsign);
  569.            fprintf(pfDropFile,"%s",apszDropFileInfo[0]);
  570.            fprintf(pfDropFile,"%c\n",od_control.user_sex);
  571.            fprintf(pfDropFile,"%s",apszDropFileInfo[1]);
  572.            fprintf(pfDropFile,"%s\n",od_control.user_lastdate);
  573.            fprintf(pfDropFile,"%d\n",od_control.user_screenwidth);
  574.            fprintf(pfDropFile,"%d\n",od_control.user_screen_length);
  575.            fprintf(pfDropFile,"%d\n",od_control.user_security);
  576.            fprintf(pfDropFile,"%d\n",bIsSysop);
  577.            fprintf(pfDropFile,"%d\n",bIsCoSysop);
  578.            fprintf(pfDropFile,"%d\n",od_control.user_ansi);
  579.            if(od_control.baud==0L)
  580.            {
  581.               fprintf(pfDropFile,"0\n");
  582.            }
  583.            else
  584.            {
  585.               fprintf(pfDropFile,"1\n");
  586.            }
  587.            fprintf(pfDropFile,"    %d.00\n",od_control.user_timelimit*60);
  588.            fprintf(pfDropFile,"%s",apszDropFileInfo[3]);
  589.            fprintf(pfDropFile,"%s",apszDropFileInfo[4]);
  590.            fprintf(pfDropFile,"%s",apszDropFileInfo[5]);
  591.            if(od_control.baud==0L)
  592.            {
  593.               fprintf(pfDropFile,"KB\n");
  594.            }
  595.            else
  596.            {
  597.               fprintf(pfDropFile,"%lu\n",od_control.baud);
  598.            }
  599.            fprintf(pfDropFile,"%d\n",od_control.port+1);
  600.            fprintf(pfDropFile,"%s",apszDropFileInfo[6]);
  601.            fprintf(pfDropFile,"%s\n",od_control.user_password);
  602.            fprintf(pfDropFile,"%s",apszDropFileInfo[2]);
  603.            fprintf(pfDropFile,"%s",apszDropFileInfo[7]);
  604.            fprintf(pfDropFile,"%s",apszDropFileInfo[8]);
  605.            fprintf(pfDropFile,"%s",apszDropFileInfo[9]);
  606.            fprintf(pfDropFile,"%s",apszDropFileInfo[10]);
  607.            fprintf(pfDropFile,"%s",apszDropFileInfo[11]);
  608.            fprintf(pfDropFile,"%s",apszDropFileInfo[12]);
  609.            fclose(pfDropFile);
  610.            break;
  611.         case TRIBBSSYS:
  612.            pfDropFile = fopen(szDropFilePath, "w");
  613.            fprintf(pfDropFile, "%u\n", od_control.user_num);
  614.            fprintf(pfDropFile, "%s\n", od_control.user_name);
  615.            fprintf(pfDropFile, "%s\n", od_control.user_password);
  616.            fprintf(pfDropFile, "%u\n", od_control.user_security);
  617.            fprintf(pfDropFile, "%c\n", od_control.user_expert ? 'Y' : 'N');
  618.            fprintf(pfDropFile, "%c\n", od_control.user_ansi ? 'Y' : 'N');
  619.            fprintf(pfDropFile, "%d\n", od_control.user_timelimit);
  620.            fprintf(pfDropFile, "%s\n", od_control.user_homephone);
  621.            fprintf(pfDropFile, "%s\n", od_control.user_location);
  622.           od_control.user_birthday[2] = '/';
  623.           od_control.user_birthday[5] = '/';
  624.            fprintf(pfDropFile, "%s\n", od_control.user_birthday);
  625.            fprintf(pfDropFile, "%d\n", od_control.od_node);
  626.            fprintf(pfDropFile, "%d\n", od_control.port + 1);
  627.            fprintf(pfDropFile, "%lu\n", od_control.od_connect_speed);
  628.            fprintf(pfDropFile, "%lu\n", od_control.baud);
  629.            fprintf(pfDropFile, "%c\n", (od_control.od_com_flow_control
  630.               == COM_RTSCTS_FLOW) ? 'Y' : 'N');
  631.            fprintf(pfDropFile, "%c\n", od_control.user_error_free ? 'Y' : 'N');
  632.            fprintf(pfDropFile, "%s\n", od_control.system_name);
  633.            fprintf(pfDropFile, "%s\n", od_control.sysop_name);
  634.            fprintf(pfDropFile, "%s\n", od_control.user_handle);
  635.            fprintf(pfDropFile, "%c\n", od_control.user_rip ? 'Y' : 'N');
  636.            fclose(pfDropFile);
  637.            break;
  638.    }
  639.  
  640.    /* Deallocate temorary strings. */
  641.    for(btCount=0;btCount<25;++btCount)
  642.    {
  643.       free(apszDropFileInfo[btCount]);
  644.    }
  645.  
  646.    /* If logfile system is active. */
  647.    if(pfLogClose != NULL)
  648.    {
  649.       /* Then close the logfile. */
  650.       (*pfLogClose)(nErrorLevel);
  651.    }
  652.  
  653.    /* Disconnect the remote user if required. */
  654.    if(od_control.baud && bTermCall)
  655.    {
  656.       BOOL bCarrier;
  657.  
  658.       /* Wait up to ten seconds for bufffer to drain. */
  659.       ODWaitDrain(10000);
  660.  
  661.       /* Wait up to five seconds for no carrier */
  662.       ODComSetDTR(hSerialPort, FALSE);
  663.       nMaxTime = time(NULL) + 5L;
  664.  
  665.       do
  666.       {
  667.          ODComCarrier(hSerialPort, &bCarrier);
  668.       } while(bCarrier && time(NULL) <= nMaxTime);
  669.  
  670.       /* Raise DTR signal again. */
  671.       ODComSetDTR(hSerialPort, TRUE);
  672.    }
  673.  
  674.    /* In Win32 version, disable DTR before closing serial port, if */
  675.    /* required.                                                    */
  676. #ifdef ODPLAT_WIN32
  677.    /* If we are operating in remote mode, and we should not hangup on the */
  678.    /* caller ...                                                          */
  679.    if(!bTermCall && od_control.baud)
  680.    {
  681.       ODInExDisableDTR();
  682.    }
  683. #endif /* ODPLAT_WIN32 */
  684.  
  685.    /* Remove the message that indicates we are in the process of exiting */
  686.    /* or hanging up.                                                     */
  687.    ODScrnRemoveMessage(pWindow);
  688.  
  689. #ifndef ODPLAT_WIN32
  690.    /* Reset output area boundary to the entire screen. */
  691.    ODScrnSetBoundary(1,1,80,25);
  692.  
  693.    /* Reset text color. */
  694.    ODScrnSetAttribute(0x07);
  695.  
  696.    /* Clear screen if neccesary. */   
  697.    if(od_control.od_clear_on_exit)
  698.    {
  699.       ODScrnClear();
  700.    }
  701.    else
  702.    {
  703.       ODScrnSetCursorPos(1, 1);
  704.    }
  705. #endif /* !ODPLAT_WIN32 */
  706.  
  707. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  708.    if(od_control.od_internal_debug)
  709.    {
  710.       MessageBox(NULL, "Terminating kernel threads", "OpenDoors Diagnostics",
  711.          MB_OK);
  712.    }
  713. #endif
  714.    /* Shutdown the OpenDoors kernel. */
  715.    ODKrnlShutdown();
  716.  
  717. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  718.    if(od_control.od_internal_debug)
  719.    {
  720.       MessageBox(NULL, "Shutting down local screen", "OpenDoors Diagnostics",
  721.          MB_OK);
  722.    }
  723. #endif
  724.    /* Shutdown OpenDoors local screen module. */
  725.    ODScrnShutdown();
  726.  
  727. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  728.    if(od_control.od_internal_debug)
  729.    {
  730.       MessageBox(NULL, "Performing any final serial port deallocation",
  731.          "OpenDoors Diagnostics", MB_OK);
  732.    }
  733. #endif
  734.    /* If not operating in local mode, then deallocate serial port resources. */
  735.    if(od_control.baud != 0)
  736.    {
  737.       /* Close serial port. */
  738.       ODComClose(hSerialPort);
  739.  
  740.       /* Deallocate serial port object. */
  741.       ODComFree(hSerialPort);
  742.    }
  743.  
  744. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  745.    if(od_control.od_internal_debug)
  746.    {
  747.       MessageBox(NULL, "Deallocating common queue", "OpenDoors Diagnostics",
  748.          MB_OK);
  749.    }
  750. #endif
  751.    /* Deallocate input buffer. */
  752.    ODInQueueFree(hODInputQueue);
  753.  
  754. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  755.    if(od_control.od_internal_debug)
  756.    {
  757.       MessageBox(NULL, "Going to inactive mode", "OpenDoors Diagnostics",
  758.          MB_OK);
  759.    }
  760. #endif
  761.    /* OpenDoors is no longer active. */
  762.    bODInitialized = FALSE;
  763.  
  764.    /* od_exit() is no longer active. */
  765.    bExiting = FALSE;
  766.  
  767.    /* If the client does not want a call to od_exit() to shutdown the */
  768.    /* application, but just to shutdown OpenDoors, then return now.   */
  769.    if(od_control.od_noexit) return;
  770.  
  771.    /* If exit() has already been called, then do not call it again. */
  772.    if(bPreOrExit) return;
  773.  
  774. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  775.    if(od_control.od_internal_debug)
  776.    {
  777.       MessageBox(NULL, "Terminating process", "OpenDoors Diagnostics", MB_OK);
  778.    }
  779. #endif
  780.    /* Exit with appropriate errorlevel. */
  781.    ODProcessExit(nErrorLevel);
  782. }
  783.  
  784.  
  785. /* ----------------------------------------------------------------------------
  786.  * ODSearchForDropFile()
  787.  *
  788.  * Searches for a door information (drop) file, given a list of possible drop
  789.  * file names. Searches for the drop file first in the directory specified
  790.  * by od_control.info_path. If a directory was specified in the configuration
  791.  * file, this is where that directory name would be stored. This function will
  792.  * then proceed to search the current directory and any directories specified
  793.  * by recognized environment variables, until either a drop file is found, or
  794.  * until all possibilities are exhaused.
  795.  *
  796.  * If a directory contains more than one supported dropfile, the choice of
  797.  * drop files is narrowed to the most recently written file, and any files
  798.  * written in the ten seconds before that file was written. Of these files,
  799.  * the file with the highest priority (based on its position in the list of
  800.  * possible drop file names) is selected. This heuristic attempts to ignore
  801.  * any "old" drop files that may still be hanging around from another
  802.  * program or another login session, while still choosing the file with the
  803.  * most information.
  804.  *
  805.  * Parameters: papszFileNames - Array of possible drop file names.
  806.  *
  807.  *             nNumFilesNames - The number of names in papszFileNames.
  808.  *
  809.  *             pszFound       - If a drop file was found, this string
  810.  *                              will be changed to point to the filename
  811.  *                              of the file that was found.
  812.  *
  813.  *             pszDirectory   - If a drop file was found, this string
  814.  *                              will be changed to contain the name of
  815.  *                              the directory in which the file was found.
  816.  *
  817.  *     Return: Index in the array of the file that was found, or -1 if no
  818.  *             potential drop file was found.
  819.  */
  820. INT ODSearchForDropFile(char **papszFileNames, INT nNumFileNames,
  821.    char *pszFound, char *pszDirectory)
  822. {
  823.    BYTE btCount;
  824.    char *pszEnvVarSetting;
  825.    INT nResult;
  826.  
  827.    ASSERT(papszFileNames != NULL);
  828.    ASSERT(nNumFileNames > 0);
  829.    ASSERT(pszFound != NULL);
  830.  
  831.    /* First, look for the drop file(s) in the directory specified by */
  832.    /* od_control.info_path.                                          */
  833.    if(strlen(od_control.info_path) != 0)
  834.    {
  835.       if((nResult = ODSearchInDir(papszFileNames, nNumFileNames, pszFound,
  836.          od_control.info_path)) != -1)
  837.       {
  838.          if(pszDirectory != NULL) strcpy(pszDirectory, od_control.info_path);
  839.          return(nResult);
  840.       }
  841.    }
  842.  
  843.    /* Next, look for the drop file(s) in the current directory. */
  844.    if((nResult = ODSearchInDir(papszFileNames, nNumFileNames, pszFound,
  845.       ".\\")) != -1)
  846.    {
  847.       if(pszDirectory != NULL) strcpy(pszDirectory, ".\\");
  848.       return(nResult);
  849.    }
  850.  
  851.    /* Look through array of environment variables, checking whether any of */
  852.    /* them specify the name of a directory in which a drop file can be     */
  853.    /* found.                                                               */
  854.    ASSERT(DIM(apszEnvVarNames) == NUM_DIR_ENV_VARS);
  855.    for(btCount = 0; btCount < NUM_DIR_ENV_VARS; ++btCount)
  856.    {
  857.       if((pszEnvVarSetting = (char *)getenv(apszEnvVarNames[btCount])) != NULL)
  858.       {
  859.          if((nResult = ODSearchInDir(papszFileNames, nNumFileNames, pszFound,
  860.             pszEnvVarSetting)) != -1)
  861.          {
  862.             if(pszDirectory != NULL) strcpy(pszDirectory,pszEnvVarSetting);
  863.             return(nResult);
  864.          }
  865.       }
  866.    }
  867.  
  868.    return(-1);
  869. }
  870.  
  871.  
  872. /* ----------------------------------------------------------------------------
  873.  * ODSearchInDir()                                     *** PRIVATE FUNCTION ***
  874.  *
  875.  * Private helper function used by ODSearchForDropFile(). Searches for a drop
  876.  * file in a single specified directory. The heuristic for selecting a drop
  877.  * file, if more than one exists, is described in the header for the
  878.  * ODSearchForDropFile() function.
  879.  *
  880.  * Parameters: papszFileNames - Array of possible drop file names.
  881.  *
  882.  *             nNumFilesNames - The number of names in papszFileNames.
  883.  *
  884.  *             pszFound       - If a drop file was found, this string
  885.  *                              will be changed to point to the filename
  886.  *                              of the file that was found.
  887.  *
  888.  *             pszDirectory   - Name of the directory to search in.
  889.  *
  890.  *     Return: Index in the array of the file that was found, or -1 if no
  891.  *             potential drop file was found.
  892.  */
  893. static INT ODSearchInDir(char **papszFileNames, INT nNumFileNames,
  894.    char *pszFound, char *pszDirectory)
  895. {
  896.    BYTE btCount;
  897.    char szFullName[80];
  898.    INT nFound = -1;
  899.    tODDirHandle hDir;
  900.    tODDirEntry DirEntry;
  901.    time_t LatestTime = 0;
  902.  
  903.    ASSERT(papszFileNames != NULL);
  904.    ASSERT(nNumFileNames > 0);
  905.    ASSERT(pszFound != NULL);
  906.    ASSERT(pszDirectory != NULL);
  907.  
  908.    for(btCount=0; btCount < nNumFileNames; ++btCount)
  909.    {
  910.       /* Do not consider DORINFO1.DEF if a DORINFOx.DEF for this node has */
  911.       /* been found. */
  912.       if(btCount == 2 && nFound == 1)
  913.       {
  914.          continue;
  915.       }
  916.  
  917.       ASSERT(papszFileNames[btCount] != NULL);
  918.  
  919.       ODMakeFilename(szFullName, pszDirectory, (char *)papszFileNames[btCount],
  920.          sizeof(szFullName));
  921.  
  922.       /* Attempt to open directory. */
  923.       if(ODDirOpen(szFullName, DIR_ATTRIB_NORMAL | DIR_ATTRIB_ARCH, &hDir)
  924.          == kODRCSuccess)
  925.       {
  926.          /* Read the first matching entry in the directory. */
  927.          ODDirRead(hDir, &DirEntry);
  928.  
  929.          if(nFound == -1
  930.             || DirEntry.LastWriteTime > LatestTime + DROPFILE_TIME_LEEWAY)
  931.          {
  932.             if(!ODFileAccessMode(szFullName, 4))
  933.             {
  934.                nFound=btCount;
  935.                LatestTime = DirEntry.LastWriteTime;
  936.             }
  937.          }
  938.  
  939.          /* Close the open directory. */
  940.          ODDirClose(hDir);
  941.       }
  942.    }
  943.  
  944.    if(nFound != -1)
  945.    {
  946.       ODMakeFilename(pszFound, pszDirectory, (char *)papszFileNames[nFound],
  947.          160);
  948.    }
  949.  
  950.    return(nFound);
  951. }
  952.  
  953.  
  954. /* ----------------------------------------------------------------------------
  955.  * ODReadExitInfoPrimitive()
  956.  *
  957.  * Reads the core a of pre-RA2 style EXITINFO.BBS file.
  958.  *
  959.  * Parameters: pfDropFile - Pointer to already open EXITINFO.BBS file.
  960.  *
  961.  *             nCount     - Specifies the number of bytes to read.
  962.  *
  963.  *     Return: TRUE on success, or FALSE on failure.
  964.  */
  965. BOOL ODReadExitInfoPrimitive(FILE *pfDropFile, INT nCount)
  966. {
  967.    if((pExitInfoRecord=malloc(sizeof(tExitInfoRecord)))==NULL) return(FALSE);
  968.  
  969.    if(fread(pExitInfoRecord,1,nCount,pfDropFile)!=(size_t)nCount)
  970.    {
  971.       return(FALSE);
  972.    }
  973.  
  974.                                           /* now we read all the data from the */
  975.                                           /* EXITINFO structure to the OpenDoors */
  976.                                           /* control structure. This may look */
  977.                                           /* a bit messy, but it gets the job */
  978.                                           /* done, and allows the programmer */
  979.                                           /* to access all the strings in C */
  980.                                           /* format instead of Pascal */
  981.    od_control.baud=pExitInfoRecord->baud;
  982.    od_control.system_calls=pExitInfoRecord->num_calls;
  983.    ODStringPascalToC(od_control.system_last_caller,pExitInfoRecord->last_caller,35);
  984.    ODStringPascalToC(od_control.timelog_start_date,pExitInfoRecord->start_date,8);
  985.    memcpy(&od_control.timelog_busyperhour,&pExitInfoRecord->busyperhour,62);
  986.    ODStringPascalToC(od_control.user_name,pExitInfoRecord->uname,35);
  987.    ODStringPascalToC(od_control.user_location,pExitInfoRecord->uloc,25);
  988.    ODStringPascalToC(od_control.user_password,pExitInfoRecord->password,15);
  989.    ODStringPascalToC(od_control.user_dataphone,pExitInfoRecord->dataphone,12);
  990.    ODStringPascalToC(od_control.user_homephone,pExitInfoRecord->homephone,12);
  991.    ODStringPascalToC(od_control.user_lasttime,pExitInfoRecord->lasttime,5);
  992.    ODStringPascalToC(od_control.user_lastdate,pExitInfoRecord->lastdate,8);
  993.    memcpy(&od_control.user_attribute,&pExitInfoRecord->attrib,5);
  994.    od_control.user_net_credit=pExitInfoRecord->credit;
  995.    od_control.user_pending=pExitInfoRecord->pending;
  996.    od_control.user_messages=pExitInfoRecord->posted;
  997.    od_control.user_lastread=pExitInfoRecord->lastread;
  998.    od_control.user_security=pExitInfoRecord->sec;
  999.    od_control.user_numcalls=pExitInfoRecord->nocalls;
  1000.    od_control.user_uploads=pExitInfoRecord->ups;
  1001.    od_control.user_downloads=pExitInfoRecord->downs;
  1002.    od_control.user_upk=pExitInfoRecord->upk;
  1003.    od_control.user_downk=pExitInfoRecord->downk;
  1004.    od_control.user_todayk=pExitInfoRecord->todayk;
  1005.    memcpy(&od_control.user_time_used,&pExitInfoRecord->elapsed,6);
  1006.    od_control.user_group=pExitInfoRecord->group;
  1007.    od_control.user_xi_record=pExitInfoRecord->xirecord;
  1008.    od_control.event_status=pExitInfoRecord->status;
  1009.    ODStringPascalToC(od_control.event_starttime,pExitInfoRecord->starttime,5);
  1010.    memcpy(&od_control.event_errorlevel,&pExitInfoRecord->errorlevel,3);
  1011.    ODStringPascalToC(od_control.event_last_run,pExitInfoRecord->lasttimerun,8);
  1012.    memcpy(&od_control.user_netmailentered,&pExitInfoRecord->netmailentered,2);
  1013.    ODStringPascalToC(od_control.user_logintime,pExitInfoRecord->logintime,5);
  1014.    ODStringPascalToC(od_control.user_logindate,pExitInfoRecord->logindate,8);
  1015.  
  1016.    /* Note that the timelimit field is skipped here. This value has already */
  1017.    /* been read from the DORINFOx.DEF file, and is not consistently written */
  1018.    /* to the EXITINFO.BBS file by various BBS packages.                     */
  1019.  
  1020.    memcpy(&od_control.user_loginsec,&pExitInfoRecord->loginsec,16);
  1021.    od_control.user_ansi=od_control.user_attribute&8;
  1022.    od_control.user_avatar=od_control.user_attrib2&2;
  1023.  
  1024.    return(TRUE);
  1025. }
  1026.  
  1027.  
  1028. /* ----------------------------------------------------------------------------
  1029.  * ODWriteExitInfoPrimitive()
  1030.  *
  1031.  * Writes the core a of pre-RA2 style EXITINFO.BBS file.
  1032.  *
  1033.  * Parameters: pfDropFile - Pointer to already open EXITINFO.BBS file.
  1034.  *
  1035.  *             nCount     - Number of bytes to be written.
  1036.  *
  1037.  *     Return: Number of bytes actually written.
  1038.  */
  1039. INT ODWriteExitInfoPrimitive(FILE *pfDropFile, INT nCount)
  1040. {
  1041.    INT nToReturn;
  1042.    DWORD dwActiveMinutes;
  1043.    INT nUserTimeLost;
  1044.    INT nTimeSubtractedBySysop;
  1045.    time_t nCurrentUnixTime;
  1046.  
  1047.    pExitInfoRecord->num_calls=od_control.system_calls;
  1048.    ODStringCToPascal(pExitInfoRecord->last_caller,35,od_control.system_last_caller);
  1049.    ODStringCToPascal(pExitInfoRecord->start_date,8,od_control.timelog_start_date);
  1050.    memcpy(&pExitInfoRecord->busyperhour,&od_control.timelog_busyperhour,31);
  1051.    ODStringCToPascal(pExitInfoRecord->uname,35,od_control.user_name);
  1052.    ODStringCToPascal(pExitInfoRecord->uloc,25,od_control.user_location);
  1053.    ODStringCToPascal(pExitInfoRecord->password,15,od_control.user_password);
  1054.    ODStringCToPascal(pExitInfoRecord->dataphone,12,od_control.user_dataphone);
  1055.    ODStringCToPascal(pExitInfoRecord->homephone,12,od_control.user_homephone);
  1056.    ODStringCToPascal(pExitInfoRecord->lasttime,5,od_control.user_lasttime);
  1057.    ODStringCToPascal(pExitInfoRecord->lastdate,8,od_control.user_lastdate);
  1058.    memcpy(&pExitInfoRecord->attrib,&od_control.user_attribute,5);
  1059.    pExitInfoRecord->credit=(WORD)od_control.user_net_credit;
  1060.    pExitInfoRecord->pending=(WORD)od_control.user_pending;
  1061.    pExitInfoRecord->posted=(WORD)od_control.user_messages;
  1062.    pExitInfoRecord->lastread=(WORD)od_control.user_lastread;
  1063.    pExitInfoRecord->sec=(WORD)od_control.user_security;
  1064.    pExitInfoRecord->nocalls=(WORD)od_control.user_numcalls;
  1065.    pExitInfoRecord->ups=(WORD)od_control.user_uploads;
  1066.    pExitInfoRecord->downs=(WORD)od_control.user_downloads;
  1067.    pExitInfoRecord->upk=(WORD)od_control.user_upk;
  1068.    pExitInfoRecord->downk=(WORD)od_control.user_downk;
  1069.    pExitInfoRecord->todayk=(WORD)od_control.user_todayk;
  1070.    memcpy(&pExitInfoRecord->elapsed,&od_control.user_time_used,6);
  1071.    pExitInfoRecord->group = (BYTE)od_control.user_group;
  1072.    pExitInfoRecord->xirecord=(WORD)od_control.user_xi_record;
  1073.    pExitInfoRecord->status=od_control.event_status;
  1074.    pExitInfoRecord->status=od_control.event_status;
  1075.    ODStringCToPascal(pExitInfoRecord->starttime,5,od_control.event_starttime);
  1076.    memcpy(&pExitInfoRecord->errorlevel,&od_control.event_errorlevel,3);
  1077.    ODStringCToPascal(pExitInfoRecord->lasttimerun,8,od_control.event_last_run);
  1078.    memcpy(&pExitInfoRecord->netmailentered,&od_control.user_netmailentered,2);
  1079.    ODStringCToPascal(pExitInfoRecord->logintime,5,od_control.user_logintime);
  1080.    ODStringCToPascal(pExitInfoRecord->logindate,8,od_control.user_logindate);
  1081.  
  1082.    /* Calculate new time limit based on how time was adjusted during door's */
  1083.    /* execution.                                                            */
  1084.    time(&nCurrentUnixTime);
  1085.    ODDWordDivide(&dwActiveMinutes, NULL, nCurrentUnixTime-nStartupUnixTime, 60L);
  1086.    nUserTimeLost = (nInitialRemaining - od_control.user_timelimit);
  1087.    nTimeSubtractedBySysop = nUserTimeLost - (int)dwActiveMinutes;
  1088.    pExitInfoRecord->timelimit -= nTimeSubtractedBySysop;
  1089.  
  1090.    memcpy(&pExitInfoRecord->loginsec,&od_control.user_loginsec,16);
  1091.  
  1092.    nToReturn=(fwrite(pExitInfoRecord,1,nCount,pfDropFile) == (size_t)nCount);
  1093.    free(pExitInfoRecord);
  1094.    return(nToReturn);
  1095. }
  1096.  
  1097.  
  1098. /* ----------------------------------------------------------------------------
  1099.  * ODAtExitCallback()
  1100.  *
  1101.  * OpenDoors sets up the C library to call back this function when the program
  1102.  * is about to exit. OpenDoors uses this function to attempt to trap the
  1103.  * condition where the programmer exits the program without explicitly calling
  1104.  * od_exit(). If the program is about to exit, and OpenDoors is still active,
  1105.  * then od_exit() is called.
  1106.  *
  1107.  * It is not recommended that the programmer using OpenDoors rely on this
  1108.  * mechanism, because:
  1109.  *
  1110.  *    1. It doesn't seem to be supported by all compilers.
  1111.  *
  1112.  *    2. It doesn't permit OpenDoors to determine the actual error level
  1113.  *       that the program is exiting with in order to report this information
  1114.  *       in the log file (if enabled).
  1115.  *
  1116.  * Parameters: none
  1117.  *
  1118.  *     Return: void
  1119.  */
  1120. #ifndef ODPLAT_WIN32
  1121. void ODAtExitCallback(void)
  1122. {
  1123.    if(bODInitialized)
  1124.    {
  1125.       bPreOrExit = TRUE;
  1126.       if(od_control.od_errorlevel[0])
  1127.       {
  1128.          od_exit(od_control.od_errorlevel[7],FALSE);
  1129.       }
  1130.       else
  1131.       {
  1132.          od_exit(6,FALSE);
  1133.       }
  1134.    }
  1135. }
  1136. #endif /* !ODPLAT_WIN32 */
  1137.  
  1138.  
  1139. /* Currently, these functions are only used in the Win32 version. */
  1140. #ifdef ODPLAT_WIN32
  1141.  
  1142.  
  1143. /* ----------------------------------------------------------------------------
  1144.  * ODSendModemCommand()                                *** PRIVATE FUNCTION ***
  1145.  *
  1146.  * Sends a sequence of commands to the modem, waiting for the specified
  1147.  * response between each command. The command sequence is retried the specified
  1148.  * number of times.
  1149.  *
  1150.  * Parameters: pszCommand - Command string to send to the modem, along with
  1151.  *                          response strings. Each of these are separated by
  1152.  *                          a space character. A pipe character ('|') denotes a
  1153.  *                          CR, and a tilde character ('~') denotes a one
  1154.  *                          second pause.
  1155.  *
  1156.  *             nRetries   - Number of times to retry command sequence.
  1157.  *
  1158.  *     Return: TRUE on success, or FALSE if some expected response string was
  1159.  *             not received from the modem after modem response timeout period.
  1160.  */
  1161. static BOOL ODSendModemCommand(char *pszCommand, int nRetries)
  1162. {
  1163.    ASSERT(pszCommand != NULL);
  1164.    ASSERT(nRetries >= 1);
  1165.  
  1166.    while(nRetries--)
  1167.    {
  1168.       if(ODSendModemCommandOnce(pszCommand))
  1169.       {
  1170.          return(TRUE);
  1171.       }
  1172.    }
  1173.  
  1174.    return(FALSE);
  1175. }
  1176.  
  1177.  
  1178. /* ----------------------------------------------------------------------------
  1179.  * ODSendModemCommandOnce()                            *** PRIVATE FUNCTION ***
  1180.  *
  1181.  * Sends a series of commands to the modem, waiting for the specified response
  1182.  * between each command.
  1183.  *
  1184.  * Parameters: pszCommand - Command string to send to the modem, along with
  1185.  *                          response strings. Each of these are separated by
  1186.  *                          a space character. A pipe character ('|') denotes a
  1187.  *                          CR, and a tilde character ('~') denotes a one
  1188.  *                          second pause.
  1189.  *
  1190.  *     Return: TRUE on success, or FALSE if some expected response string was
  1191.  *             not received from the modem after modem response timeout period.
  1192.  */
  1193. static BOOL ODSendModemCommandOnce(char *pszCommand)
  1194. {
  1195.    char *pchCurrent;
  1196.    char szResponse[MAX_RESPONSE_LEN + 1];
  1197.    int nResponsePos;
  1198.    BOOL bSendingCommand = TRUE;
  1199.  
  1200.    ASSERT(pszCommand != NULL);
  1201.  
  1202.    /* We must be operating in remote mode. */
  1203.    ASSERT(od_control.baud != 0);
  1204.  
  1205.    /* Loop through each character in the string. */
  1206.    for(pchCurrent = pszCommand; *pchCurrent != '\0'; ++pchCurrent)
  1207.    {
  1208.       /* What we do with this character depends upon whether we are */
  1209.       /* currently sending a command, or waiting for a response.    */
  1210.       if(bSendingCommand)
  1211.       {
  1212.          switch(*pchCurrent)
  1213.          {
  1214.             case ' ':
  1215.                /* A space character denotes that we should toggle between */
  1216.                /* sending a command and receiving a response.             */
  1217.                bSendingCommand = FALSE;
  1218.  
  1219.                /* Start at the beginning of the empty response string. */
  1220.                nResponsePos = 0;
  1221.                szResponse[0] = '\0';
  1222.                break;
  1223.  
  1224.             case '|':
  1225.                /* A pipe character denotes that a carriage return should be */
  1226.                /* send to the modem.                                        */
  1227.                ODComSendByte(hSerialPort, '\r');
  1228. #ifdef OD_DIAGNOSTICS
  1229.                strcat(szDebugWorkString, "\n");
  1230. #endif /* OD_DIAGNOSTICS */
  1231.                break;
  1232.  
  1233.             case '~':
  1234.                /* A tilde character denotes a 1 second pause. */
  1235.                od_sleep(1000);
  1236.                break;
  1237.  
  1238.             default:
  1239.                /* Otherwise, send this character as is. */
  1240.                ODComSendByte(hSerialPort, *pchCurrent);
  1241. #ifdef OD_DIAGNOSTICS
  1242.                {
  1243.                   char szAppend[2];
  1244.                   szAppend[0] = *pchCurrent;
  1245.                   szAppend[1] = 0;
  1246.                   strcat(szDebugWorkString, szAppend);
  1247.                }
  1248. #endif /* OD_DIAGNOSTICS */
  1249.          }
  1250.  
  1251.          od_sleep(200);
  1252.       }
  1253.       else
  1254.       {
  1255.          /* We are currently building a string that we should wait for. */
  1256.          switch(*pchCurrent)
  1257.          {
  1258.             case ' ':
  1259.                /* A space character denotes that we should toggle between */
  1260.                /* sending a command and receiving a response.             */
  1261.  
  1262.                /* Wait until the response string we have built is received. */
  1263.                if(!ODWaitForString(szResponse, RESPONSE_TIMEOUT))
  1264.                {
  1265.                   /* If string was not received, then return now. */
  1266.                   return(FALSE);
  1267.                }
  1268.  
  1269.                /* Switch to sending command mode. */
  1270.                bSendingCommand = TRUE;
  1271.                break;
  1272.  
  1273.             case '~':
  1274.                /* Pauses are ignored in response strings. */
  1275.                break;
  1276.  
  1277.             default:
  1278.                /* Otherwise, add this character to the response string. */
  1279.                if(nResponsePos < MAX_RESPONSE_LEN)
  1280.                {
  1281.                   szResponse[nResponsePos] = *pchCurrent;
  1282.                   ++nResponsePos;
  1283.                   szResponse[nResponsePos] = '\0';
  1284.                }
  1285.          }
  1286.       }
  1287.    }
  1288.  
  1289.    /* Return with success. */
  1290.    return(TRUE);
  1291. }
  1292.  
  1293.  
  1294. /* ----------------------------------------------------------------------------
  1295.  * ODWaitForString()                                   *** PRIVATE FUNCTION ***
  1296.  *
  1297.  * Waits for the specified string to be received from the modem, for up to
  1298.  * the specified length of time.
  1299.  *
  1300.  * Parameters: pszResponse     - Pointer to the string to wait for.
  1301.  *
  1302.  *             ResponseTimeout - The maximum time, in milliseconds, to wait.
  1303.  *
  1304.  *     Return: TRUE on success, or FALSE if some expected response string was
  1305.  *             not received from the modem after modem response timeout period.
  1306.  */
  1307. static BOOL ODWaitForString(char *pszResponse, tODMilliSec ResponseTimeout)
  1308. {
  1309.    tODTimer Timer;
  1310.    char szReceived[MAX_RESPONSE_LEN + 1] = "\0";
  1311.    tODInputEvent InputEvent;
  1312.  
  1313.    ASSERT(pszResponse != NULL);
  1314.    ASSERT(ResponseTimeout > 0);
  1315.  
  1316.    /* We must be operating in remote mode. */
  1317.    ASSERT(od_control.baud != 0);
  1318.  
  1319.    /* If response string is empty, then we don't wait for anything. */
  1320.    if(strlen(pszResponse) == 0) return(TRUE);
  1321.  
  1322. #ifdef OD_DIAGNOSTICS
  1323.    strcat(szDebugWorkString, "[");
  1324. #endif /* OD_DIAGNOSTICS */
  1325.  
  1326.    ODTimerStart(&Timer, ResponseTimeout);
  1327.    while(!ODTimerElapsed(&Timer))
  1328.    {
  1329.       if(ODInQueueGetNextEvent(hODInputQueue, &InputEvent,
  1330.          ODTimerLeft(&Timer)) == kODRCSuccess)
  1331.       {
  1332.          if(InputEvent.bFromRemote && InputEvent.EventType == EVENT_CHARACTER)
  1333.          {
  1334. #ifdef OD_DIAGNOSTICS
  1335.             {
  1336.                char szAppend[2];
  1337.                szAppend[0] = InputEvent.chKeyPress;
  1338.                szAppend[1] = 0;
  1339.                strcat(szDebugWorkString, szAppend);
  1340.             }
  1341. #endif /* OD_DIAGNOSTICS */
  1342.  
  1343.             /* Add the received character to the received string. */
  1344.             if(strlen(szReceived) == MAX_RESPONSE_LEN)
  1345.             {
  1346.                memmove(szReceived, szReceived + 1, MAX_RESPONSE_LEN);
  1347.             }
  1348.             szReceived[strlen(szReceived) + 1] = '\0';
  1349.             szReceived[strlen(szReceived)] = InputEvent.chKeyPress;
  1350.  
  1351.             /* If the sequence has been received, then return with success. */
  1352.             if(strstr(szReceived, pszResponse) != NULL)
  1353.             {
  1354. #ifdef OD_DIAGNOSTICS
  1355.                strcat(szDebugWorkString, "]");
  1356. #endif /* OD_DIAGNOSTICS */
  1357.                return(TRUE);
  1358.             }
  1359.          }
  1360.       }
  1361.       else
  1362.       {
  1363.          /* When no characters are waiting, allow other processes to run. */
  1364.          od_sleep(0);
  1365.       }
  1366.    }
  1367.  
  1368. #ifdef OD_DIAGNOSTICS
  1369.    strcat(szDebugWorkString, "]");
  1370. #endif OD_DIAGNOSTICS
  1371.  
  1372.    /* Indicate that string was not received in the time alotted. */
  1373.    return(FALSE);
  1374. }
  1375.  
  1376.  
  1377. /* ----------------------------------------------------------------------------
  1378.  * ODInExDisableDTR()
  1379.  *
  1380.  * Disables DTR response by the modem, if required.
  1381.  *
  1382.  * Parameters: None
  1383.  *
  1384.  *     Return: void
  1385.  */
  1386. void ODInExDisableDTR(void)
  1387. {
  1388.    BOOL bCarrier;
  1389.  
  1390.    /* If we are using the Door32 interface, then do not disable DTR. */
  1391.    if(od_control.od_com_method == COM_DOOR32 || od_control.od_com_method == COM_SOCKET)
  1392.    {
  1393.       return;
  1394.    }
  1395.  
  1396.    /* Check that carrier detect signal is still present. */
  1397.    ODComCarrier(hSerialPort, &bCarrier);
  1398.    if(bCarrier)
  1399.    {
  1400.       /* Only disable DTR response if OpenDoors opened the serial port, */
  1401.       /* and DTR disabling has not been explicitly turned off.          */
  1402.       if(od_control.od_open_handle == 0
  1403.          && !(od_control.od_disable & DIS_DTR_DISABLE))
  1404.       {
  1405.          if(!ODSendModemCommand(od_control.od_disable_dtr, 2))
  1406.          {
  1407. #ifdef OD_DIAGNOSTICS
  1408.             if(od_control.od_internal_debug)
  1409.             {
  1410.                MessageBox(NULL, szDebugWorkString, "DTR Disable FAILED!",
  1411.                   MB_OK);
  1412.                szDebugWorkString[0] = '\0';
  1413.             }
  1414. #endif /* OD_DIAGNOSTICS */
  1415.          }
  1416.          else
  1417.          {
  1418. #ifdef OD_DIAGNOSTICS
  1419.             if(od_control.od_internal_debug)
  1420.             {
  1421.                MessageBox(NULL, szDebugWorkString, "DTR Disable Succeeded!",
  1422.                   MB_OK);
  1423.                szDebugWorkString[0] = '\0';
  1424.             }
  1425. #endif /* OD_DIAGNOSTICS */
  1426.          }
  1427.       }
  1428.    }
  1429. }
  1430.  
  1431. #endif /* ODPLAT_WIN32 */
  1432.