/*==========================================================================
 * filename:		../plogd/plogd.c
 *
 * This software is distributed under the terms of the Livermore Public
 * License version 1.0.
 *
 *       The Livermore PUBLIC LICENSE (LPL) Version 1.0
 *
 * Copyright (C) 1999-2000 Freemont Avenue Software, Inc, Houston, Texas, USA
 *
 * Everyone is permitted to copy and distribute this license document.  The
 * intent of this license is to establish freedom to share and change the
 * software regulated by this license under the open source model.
 *
 * This license applies to any software containing a notice placed by the
 * copyright holder saying that it may be distributed under the terms of the
 * Livermore Public  License version 1.0. Such software is herein referred to as
 * the Software. This license covers modification and distribution of the
 * Software, use of third-party application programs based on the Software, and
 * development of free software which uses the Software.
 *
 * Granted Rights
 *
 * 1. You are granted the non-exclusive rights set forth in this license
 *    provided you agree to and comply with any and all conditions in this
 *    license. Whole or partial distribution of the Software, or software items
 *    that link with the Software, in any form signifies acceptance of this
 *    license.
 *
 * 2. You may copy and distribute the Software in unmodified form provided that
 *    the entire package, including, but not restricted to, copyright, trademark
 *    notices and disclaimers, as released by the initial developer of the
 *    Software, is distributed.
 *
 * 3. You may make modifications to the Software and distribute your
 *    modifications, in a form that is separate from the Software, such as
 *    patches. The following restrictions apply to modifications:
 *
 *    a. Modifications must not alter or remove any copyright notices in the
 *       Software.
 *
 *    b. When modifications to the Software are released under this license, a
 *       non-exclusive royalty-free right is granted to the initial developer of
 *       the Software to distribute your modification in future versions of the
 *       Software provided such versions remain available under these terms in
 *       addition to any other license(s) of the initial developer.
 *
 * 4. You may distribute machine-executable forms of the Software or
 *    machine-executable forms of modified versions of the Software, provided
 *    that you meet these restrictions:
 *
 *    a. You must include this license document in the distribution.
 *
 *    b. You must ensure that all recipients of the machine-executable forms are
 *       also able to receive the complete machine-readable source code to the
 *       distributed Software, including all modifications, without any charge
 *       beyond the costs of data transfer, and place prominent notices in the
 *       distribution explaining this.
 *
 *    c. You must ensure that all modifications included in the machine-
 *       -executable forms are available under the terms of this license.
 *
 * 5. You may use the original or modified versions of the Software to compile,
 *    link and run application programs legally developed by you or by others.
 *
 * 6. You may develop application programs, reusable components and other
 *    software items that link with the original or modified versions of the
 *    Software. These items, when distributed, are subject to the following
 *    requirements:
 *
 *    a. You must ensure that all recipients of machine-executable forms of
 *       these items are also able to receive and use the complete
 *       machine-readable source code to the items without any charge beyond
 *       the costs of data transfer.
 *
 *    b. You must explicitly license all recipients of your items to use and
 *       re-distribute original and modified versions of the items in both
 *       machine-executable and source code forms. The recipients must  be able
 *       to do so without any charges whatsoever, and they must be able to
 *       re-distribute to anyone they choose.
 *
 *    c. If the items are not available to the general public, and the initial
 *       developer of the Software requests a copy of the items, then you must
 *       supply one.
 *
 * Limitations of Liability
 *
 * In no event shall the initial developers or copyright holders be liable for
 * any damages whatsoever, including - but not restricted to - lost revenue or
 * profits or other direct, indirect, special, incidental or consequential
 * damages, even if they have been advised of the possibility of such damages,
 * except to the extent invariable law, if any, provides otherwise.
 *
 * THE SOFTWARE IS PROVIDED BY FREEMONT AVENUE SOFTWARE  ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL FREEMONT AVENUE SOFTWARE OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * This license is governed by the Laws of the State of Texas and any disputes
 * shall be decided by mediation.
 *
 * T.REX, T.REX free edition and other T.REX names are trademarks of Freemont
 * Avenue Software, Inc.
 *=============================================================================
 *
 * written by:		Tommy Chan
 *
 *
 * (C) COPYRIGHT R. James Livermore 1998-2000.  All rights reserved.
 *
 * (C) COPYRIGHT Freemont Avenue Software, Incorporated 1998-2000.
 * All rights reserved. 
 * 
 *
 * date written:	September 26, 1998
 * last modified:       Fenruary  14, 2000 - added LPL
 *
 *
 * function:
 *
 *============================================================================*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "plogd.h"

//
//  Socket related data and defines.
//

#define DESIRED_WINSOCK_VERSION         0x0101  // we'd like winsock ver 1.1...
#define MINIMUM_WINSOCK_VERSION         0x0001  // ...but we'll take ver 1.0
#define MAIN_TIMER_ID                  1
#define SHOWTIME 			15			// ~= 1.5 seconds

//
//  Various handles.
//

HINSTANCE       hInst;          // The current instance handle.
HWND            hwndFrame;      // Main  window handle.

//
//  Window class names.
//

LPSTR           pszFrameClass;  // Main window class.

//
//  Miscellaneous data.
//

LPSTR           pszAppName;     // Application name.
INT             tmAveCharWidth; // TEXTMETRIC.tmAveCharWidth.
INT             tmHeight;       // TEXTMETRIC.tmHeight.

//
// Message types to/from firewall
//

#define		ZYNC	   0xfd
#define		PROMPT	1
#define		SHOW		2
#define		PROMPTRESPONSE	1

/*=======================================================================
 * New plogd stuff
 *======================================================================*/
#define MAXNAMLEN 256
struct logrec {
   char           outfile[MAXNAMLEN];
   INT            outfd;
   unsigned long  fromaddr;		// in net order
   unsigned short toport;		// in net order
   SOCKET         listenSock;
   SOCKET         activeSock;
   struct logrec *next;
};
typedef struct logrec logrec;
static logrec  *head = NULL;

int             myState,		// 0, PROMPT or SHOW
                gotSock,		// remember to close on exit
                showTimer;		// time remaining on SHOW command ..
                			//   in 100 ms increments
//
//	Function prototypes
//

BOOL CALLBACK   About_DlgProc(HWND hwnd,
                              UINT nMessage,
                              WPARAM wParam,
                              LPARAM lParam);
VOID            About_OnCommand(HWND hwnd,
                                INT id,
                                HWND hwndCtl,
                                UINT codeNotify);

VOID            Main_OnCommand(HWND hwnd,
                               INT id,
                               HWND hwndCtl,
                               UINT codeNotify);

BOOL            Main_OnCreate(HWND hwnd,
                              CREATESTRUCT FAR * pCreateStruct);

long            Main_OnDestroy(HWND hwnd);

VOID            Main_OnTimer(HWND hwnd,
                             UINT id);

VOID            Main_OnSocketSelect(HWND hwnd,
                                    SOCKET sock,
                                    SOCKERR serr,
                                    SOCKEVENT sevent);

VOID				 Main_OnPopUp(HWND hwnd,
                                              HMENU hMenu,
                                              INT  nIndex,
                                              BOOL lSystemMenu);

long				 Main_OnQuery(HWND hwnd);


VOID            Main_OnPaint(HWND hwnd);

VOID            Main_OnChar(HWND hwnd, UINT wParam, LONG lParam);

VOID            Main_OnFocus(HWND hwnd, UINT wParam);

VOID            Main_OnKillFocus(HWND hwnd, UINT wParam);

/*******************************************************************

    NAME:       AboutDialog

    SYNOPSIS:   Displays the plogd "About" box.

    ENTRY:      hwndParent - The parent window for this dialog.

********************************************************************/
VOID            AboutDialog(HWND hwndParent)
{
   FARPROC         pfnAbout;

   pfnAbout = MakeProcInstance((FARPROC) About_DlgProc, hInst);

   DialogBox(hInst,
             IDD_ABOUT,
             hwndParent,
             (DLGPROC) pfnAbout);

   FreeProcInstance(pfnAbout);

}                               // AboutDialog


/*******************************************************************

    NAME:       About_DlgProc

    SYNOPSIS:   Dialog procedure for the "About" dialog.

    ENTRY:      hwnd - Dialog box handle.

                nMessage - The message.

                wParam - The first message parameter.

                lParam - The second message parameter.

    RETURNS:    BOOL - TRUE if we handle the message, FALSE otherwise.

********************************************************************/
BOOL CALLBACK   About_DlgProc(HWND hwnd,
                                              UINT nMessage,
                                              WPARAM wParam,
                                              LPARAM lParam)
{
   switch (nMessage)
   {	
         HANDLE_MSG(hwnd, WM_COMMAND, About_OnCommand);
   }

   return FALSE;

}                               // AboutDlgProc

/*******************************************************************

    NAME:       About_OnCommand

    SYNOPSIS:   Handles WM_COMMAND messages set to the "About"
                dialog window.

    ENTRY:      hwnd - Dialog box handle.

                id - Identifies the menu/control/accelerator.

                hwndCtl - Identifies the control sending the command.

                codeNotify - A notification code.  Will be zero for
                    menus, one for accelerators.

********************************************************************/
VOID            About_OnCommand(HWND hwnd,
                                                INT id,
                                                HWND hwndCtl,
                                                UINT codeNotify)
{
   if ((id == IDOK) || (id == IDCANCEL))
   {
      EndDialog(hwnd, TRUE);
   }
}                               // About_OnCommand

/*******************************************************************

    NAME:       Main_WndProc

    SYNOPSIS:   Window procedure for the Main Window.

    ENTRY:      hwnd - Window handle.

                nMessage - The message.

                wParam - The first message parameter.

                lParam - The second message parameter.

    RETURNS:    LRESULT - Depends on the actual message.


********************************************************************/

LRESULT CALLBACK Main_WndProc(HWND hwnd,
                                               UINT nMessage,
                                               WPARAM wParam,
                                               LPARAM lParam)
{
   switch (nMessage)
   {
         HANDLE_MSG(hwnd, WM_COMMAND, 			Main_OnCommand);
         HANDLE_MSG(hwnd, WM_PAINT, 			Main_OnPaint);
         HANDLE_MSG(hwnd, WM_CREATE, 			Main_OnCreate);
         HANDLE_MSG(hwnd, WM_SETFOCUS, 		Main_OnFocus);
         HANDLE_MSG(hwnd, WM_KILLFOCUS, 		Main_OnKillFocus);
         HANDLE_MSG(hwnd, WM_CHAR, 				Main_OnChar);
         HANDLE_MSG(hwnd, WM_INITMENUPOPUP,	Main_OnPopUp);
         HANDLE_MSG(hwnd, WM_QUERYOPEN,	   Main_OnQuery);
         HANDLE_MSG(hwnd, WM_DESTROY, 			Main_OnDestroy);
         HANDLE_MSG(hwnd, WM_TIMER, 			Main_OnTimer);
         HANDLE_MSG(hwnd, WM_SOCKET_SELECT,	Main_OnSocketSelect);
   }

   return DefWindowProc(hwnd, nMessage, wParam, lParam);

}                               // Main_WndProc
/*******************************************************
*/
long				Main_OnQuery(HWND hwnd)
{	
	return (myState!=PROMPT && myState!=SHOW) ? 0 : 1;
	   
}
/*******************************************************
*/
VOID				Main_OnPopUp(HWND hwnd,
                                                     HMENU hMenu,
                                                     INT   nIndex,
                                                     BOOL  lSystemMenu)
{	if (lSystemMenu)
	{
		EnableMenuItem(hMenu,0,MF_BYPOSITION | MF_GRAYED);
		EnableMenuItem(hMenu,2,MF_BYPOSITION | MF_GRAYED);
		EnableMenuItem(hMenu,4,MF_BYPOSITION | MF_GRAYED);
	}
}

/*******************************************************
*/
VOID            Main_OnFocus(HWND hwnd, UINT wParam)
{
   CreateCaret(hwnd, NULL, tmAveCharWidth, tmHeight);

   if (myState == PROMPT)
      ShowCaret(hwnd);

}
/*******************************************************
*/
VOID            Main_OnKillFocus(HWND hwnd, UINT wParam)
{
   HideCaret(hwnd);
   DestroyCaret();

}
/*******************************************************
*/
BOOL            StartTimer(HWND hwnd)
{
   if (SetTimer(hwnd, MAIN_TIMER_ID, 100, NULL) == 0)
   {
      MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Cannot create timeout timer");

      return FALSE;
   }
   showTimer = SHOWTIME;
}

/*******************************************************************

    NAME:       Main_OnChar

    SYNOPSIS:   Handles WM_CHAR messages.

    ENTRY:      hwnd - Window handle.

********************************************************************/

VOID            Main_OnChar(HWND hwnd, UINT wParam, LONG lParam)
{
   return;
}
/*******************************************************************

    NAME:       Main_OnCommand

    SYNOPSIS:   Handles WM_COMMAND messages.

    ENTRY:      hwnd - Window handle.

                id - Identifies the menu/control/accelerator.

                hwndCtl - Identifies the control sending the command.

                codeNotify - A notification code.  Will be zero for
                    menus, one for accelerators.

********************************************************************/
VOID            Main_OnCommand(HWND hwnd,
                                                INT id,
                                                HWND hwndCtl,
                                                UINT codeNotify)
{
   switch (id)
   {	
      case IDM_CONNECTION_EXIT:
         PostMessage(hwnd, WM_CLOSE, 0, 0);
         break;

      case IDM_HELP_ABOUT:
         AboutDialog(hwnd);
         break;

      default:
         break;
   }

}                               // Main_OnCommand


/*******************************************************************

    NAME:       Main_OnCreate

    SYNOPSIS:   Handles WM_CREATE messages.

    ENTRY:      hwnd - Window handle.

                pCreateStruct - Contains window creation parameters.

    RETURNS:    BOOL - TRUE if window created OK, FALSE otherwise.

********************************************************************/
BOOL            Main_OnCreate(HWND hwnd,
                                           CREATESTRUCT FAR * pCreateStruct)
{
   HDC             hdc;
   TEXTMETRIC      tm;

   //
   // Get textmetric data.
   //

   hdc = GetDC(hwnd);
   GetTextMetrics(hdc, &tm);
   ReleaseDC(hwnd, hdc);

   tmAveCharWidth = (INT) tm.tmAveCharWidth;
   tmHeight = (INT) tm.tmHeight;

   return TRUE;                 // Success!

}                               // Main_OnCreate


/*******************************************************************

    NAME:       Main_OnDestroy

    SYNOPSIS:   Handles WM_DESTROY messages.

    ENTRY:      hwnd - Window handle.

********************************************************************/
long            Main_OnDestroy(HWND hwnd)
{
   logrec *tmp;
   //
   // Disconnect from the sockets library before terminating.
   //
   for (tmp = head; tmp != NULL; tmp = tmp->next) {
      if (tmp->listenSock != INVALID_SOCKET)
         closesocket(tmp->listenSock);
      if (tmp->activeSock != INVALID_SOCKET)
         closesocket(tmp->activeSock);
      tmp->listenSock = INVALID_SOCKET;
      tmp->activeSock = INVALID_SOCKET;
   }

   if (gotSock)
   	WSACleanup();
   PostQuitMessage(0);
   return 0;

}                               // Main_OnDestroy


/*******************************************************************

    NAME:       Main_OnTimer

    SYNOPSIS:   Handles WM_TIMER messages.  The timer is only active when a
		 type SHOW message is being displayed. This allows us to
		 queue either the next PROMPT or socket close to insure that
		 the SHOW text appears long enough for the user to read it.

    ENTRY:      hwnd - Window handle.

                id - Timer ID.

********************************************************************/
VOID            Main_OnTimer(HWND hwnd,
                                              UINT id)
{
    return;
}                               // Main_OnTimer

/*******************************************************************

    NAME:       Main_OnSocketSelect

    SYNOPSIS:   Handles WM_SOCKET_SELECT messages.

    ENTRY:      hwnd - Window handle.

                sock - The socket that generated the message.

                serr - A socket error code.

                sevent - One of the FD_* events that occurred.

********************************************************************/
VOID            Main_OnSocketSelect(HWND hwnd,
                                                     SOCKET sock,
                                                     SOCKERR serr,
                                                     SOCKEVENT sevent)
{
   logrec         *cur;
   SOCKET          tmpsock;

   SOCKADDR_IN     addr;
   INT             cbAddr = sizeof(addr);

   if (sevent == FD_ACCEPT)			//
   {
      /*=======================================================================
       * Find the logrec that generated this FD_ACCEPT event.  Note that only
       * listenSocks should do this.
       *======================================================================*/
      for (cur = head; cur != NULL; cur = cur->next) {
         if (cur->listenSock == sock)
            break;
      }

      /*=======================================================================
       * Accept the connection no matter what.  May have to reset later.
       *======================================================================*/
      tmpsock = accept(sock, (SOCKADDR FAR *) & addr, &cbAddr);
      if (tmpsock == INVALID_SOCKET) {
         serr = WSAGetLastError();

         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Accept connection error %d: %s",
             serr,
             SockerrToString(serr));
         return;
      }

      /*=======================================================================
       * If sock does not match any of ours, reset connection.
       *======================================================================*/
      if (cur == NULL) {
         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Socket %d not found in logrec listenSock list",
             sock);
         return;
      }

      if (cur->activeSock != INVALID_SOCKET) {
         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "The log to %s is already active, cannot have multiple logs "
             "to the same file",
             cur->outfile);
         closesocket(tmpsock);
         return;
      }
      cur->activeSock = tmpsock;

      return;
   }
   /*=======================================================================
    * Find the logrec that generated this event.  Note that only activeSocks
    * lshould do this.
    *======================================================================*/
   for (cur = head; cur != NULL; cur = cur->next) {
      if (cur->activeSock == sock)
         break;
   }

   if (cur == NULL) {
      MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Socket %d not found in logrec activeSock list",
             sock);
   }

   if (sevent == FD_READ) {                     //
      INT nread;
      char buf[BUFSIZ];

      /*=================================================================
       * Open the log file
       *================================================================*/
      if ((cur->outfd = open(cur->outfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR))
								== NULL) {
         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Cannot open output log file %s",
             cur->outfile);
         exit(1);
      }

      /*=================================================================
       * Seek to the end so we can start appending
       *================================================================*/
      if (lseek(cur->outfd, 0, SEEK_END) < 0) {
         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Cannot position to EOF of output log file %s",
             cur->outfile);
             exit(1);
      }

      /*=================================================================
       * Read a block
       *================================================================*/
      while ((nread = recv(cur->activeSock,
                 buf,
                 sizeof(buf),
                 0)) < 0 && (serr = WSAGetLastError()) == WSAEWOULDBLOCK);

      if (nread < 0) {
         serr = WSAGetLastError();

         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Data receive error %d: %s",
             serr,
             SockerrToString(serr));
         exit(1);
      }
      /*=================================================================
       * At EOF of the socket, close the output file and the socket.
       *================================================================*/
      if (nread == 0) {
         close(cur->outfd);
         closesocket(sock);
         cur->activeSock = INVALID_SOCKET;
         return;
      }
      /*=================================================================
       * Write the block
       *================================================================*/
      if (write(cur->outfd, buf, nread) != nread) {
         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Data write error to file %s",
             cur->outfile);
         exit(1);
      }
      close(cur->outfd);
      return;
   }

   if (sevent == FD_CLOSE)	// Firewall went away
   {
      if (sock != INVALID_SOCKET)
         closesocket(sock);
      cur->activeSock = INVALID_SOCKET;

      return;
   }
}                                     // Main_OnSocketSelect


/*******************************************************************

    NAME:       InitApplication

    SYNOPSIS:   Performs application-wide initialization.

    ENTRY:      hInstance - The current instance handle.

    RETURNS:    BOOL - TRUE if everything initialized OK, FALSE
                    if something tragic happened.

********************************************************************/
BOOL            InitApplication(HINSTANCE hInstance)
{
   WNDCLASS        WndClass;

   //
   // Save the instance handle.  We'll need it often.
   //

   hInst = hInstance;

   //
   // Initialize some of the global strings.
   //

   pszAppName = "plogd";
   pszFrameClass = "plogdClass";

   //
   // Initialize & register the window class.
   //

   WndClass.style = CS_HREDRAW | CS_VREDRAW;
   WndClass.lpfnWndProc = (WNDPROC) Main_WndProc;
   WndClass.cbClsExtra = 0;
   WndClass.cbWndExtra = 0;
   WndClass.hInstance = hInstance;
   WndClass.hIcon = LoadIcon(hInstance, IDI_FRAME);
   WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
   WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
   WndClass.lpszMenuName = IDM_FRAME;
   WndClass.lpszClassName = pszFrameClass;

   if (!RegisterClass(&WndClass))
      return FALSE;             // Failure

   return TRUE;                 // Success!

}                               // InitApplication

/**************************************************
*/
char           *SetExtn(char *path, char *extn)
{
   char           *p1 = path + strlen(path);

   while (*(p1 - 1) != '.')
      p1--;                     // bactrack until '.'
   while (*p1++ = *extn++);     // copy new extension
   return path;

}
/*******************************************************
*/
void CfgFail(char *msg, char *path)
{	
	MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "No %s defined in %s", msg, path);
	exit(0);     
}

/*******************************************************************

    NAME:       InitInstance

    SYNOPSIS:   Performs instance-wide initialization.

    ENTRY:      hInstance - The current instance handle.

                pszCmdLine - Command line arguments.

                nCmdShow - ShowWindow parameter.

    RETURNS:    BOOL - TRUE if everything initialized OK, FALSE
                    if something tragic happened.

********************************************************************/
BOOL            InitInstance(HINSTANCE hInstance,
                                             LPSTR pszCmdLine,
                                             INT nCmdShow)
{

   logrec         *cur = NULL;
   logrec         *tmp;
   WSADATA         wsadata;
   SOCKERR         serr;

   HDC             hdc;
   TEXTMETRIC      tm;
   char            myName[200],
                   token1[MAXNAMLEN],
                   token2[MAXNAMLEN],
                   token3[MAXNAMLEN],
                   token4[MAXNAMLEN];
   char            buf[BUFSIZ];

   FILE           *cfg;
   int             linen = 0;

   //
   // Create the main window.
   //

   hdc = GetDC(GetDesktopWindow());
   GetTextMetrics(hdc, &tm);		// get average char width and height
   ReleaseDC(GetDesktopWindow(), hdc);

   hwndFrame = CreateWindow(pszFrameClass,
                            pszAppName,
                            WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            tm.tmAveCharWidth * 40, tm.tmHeight * 10,
                            NULL,
                            NULL,
                            hInstance,
                            NULL);

   if (hwndFrame == NULL)
   {
      return FALSE;
   }
   //
   // Display the main window minimized.
   //

   ShowWindow(hwndFrame, SW_MINIMIZE);		// I'm just an icon for now
   UpdateWindow(hwndFrame);
//
// Open up the .cfg file
//
   GetModuleFileName(hInstance, myName, sizeof(myName));    // where am I ?
   if ((cfg = fopen(SetExtn(myName, "cfg"), "rt")) == NULL)
   {
      MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Cannot open config file %s",
             myName);
		exit(0);     
   }
//
// Read and process the .cfg file records
// log <local_log_filename> <from_ip_address> <to_port>
//
   while (fgets(buf, sizeof(buf), cfg) != NULL)
   {
      linen++;
      *token1 = *token2 = *token3 = *token4 = '\0';
      if (sscanf(buf, " %255[^= ] %255[^= ] %255[^= ] %255[^\n]",
				token1, token2, token3, token4) != EOF)
      {
         if (*token1 == '#')
            continue;
         if (!strcmpi(token1, "log")) {
            tmp = cur;		// save last pointer
            /*===============================================================
             * malloc a new record and set pointer in the linked list
             *==============================================================*/
            if ((cur = malloc(sizeof(logrec))) == NULL) {
                MsgBox(NULL,
                       MB_ICONSTOP | MB_OK,
                       "malloc failed to get space for logrec");
                exit(1);
            }
            /*===============================================================
             * Initialize new record
             *==============================================================*/
            memset(cur, 0, sizeof(logrec));
            cur->listenSock = INVALID_SOCKET;
            cur->activeSock = INVALID_SOCKET;

            if (head == NULL)
               head = cur;
            else
               tmp->next = cur;

            strncpy(cur->outfile, token2, sizeof(cur->outfile));
            cur->fromaddr = inet_addr(token3);
            cur->toport = htons(atoi(token4));
            if (cur->fromaddr == INADDR_NONE || cur->fromaddr == INADDR_ANY) {
               MsgBox(NULL,
                      MB_ICONSTOP | MB_OK,
                      "Invalid from IP address (%s) on line %d of %s",
                      token3,
                      linen,
                      myName);
               exit(1);
            }
            if (cur->toport == INADDR_NONE || cur->toport == INADDR_ANY) {
               MsgBox(NULL,
                      MB_ICONSTOP | MB_OK,
                      "Invalid dest port number (%s) on line %d of %s",
                      token4,
                      linen,
                      myName);
               exit(1);
            }
            /*===============================================================
             * Compare filename with every other one before
             *==============================================================*/
            for (tmp = head; tmp != NULL && tmp != cur; tmp = tmp->next) {
               if (strcmp(tmp->outfile, cur->outfile) == NULL) {
                  MsgBox(NULL,
                      MB_ICONSTOP | MB_OK,
                      "Line %d of %s specifes a log file (%s) that has already "
                      "been specified earlier",
                      linen,
                      myName,
                      tmp->outfile);
                  exit(1);
               }
            }
         }
         else {
            MsgBox(NULL,
                   MB_ICONSTOP | MB_OK,
                   "Invalid statement on line %d of %s",
                   linen,
                   myName);
            exit(1);
         }
      }
   }

   fclose(cfg);
//
//  Now start the sockets interface
//


sockwait:

   serr = WSAStartup(DESIRED_WINSOCK_VERSION, &wsadata);

   if (serr != 0)
   {	if (serr==WSASYSNOTREADY)
   		goto sockwait;
      MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Cannot initialize socket library, error %d: %s",
             serr,
             SockerrToString(serr));

      return FALSE;
   }
   if (wsadata.wVersion < MINIMUM_WINSOCK_VERSION)
   {
      MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Windows Sockets version %02X.%02X, I need at least %02X.%02X",
             LOBYTE(wsadata.wVersion),
             HIBYTE(wsadata.wVersion),
             LOBYTE(MINIMUM_WINSOCK_VERSION),
             HIBYTE(MINIMUM_WINSOCK_VERSION));

      return FALSE;
   }

	gotSock=1;
   //
   // Create the command socket.
   //

   for (tmp = head; tmp != NULL; tmp = tmp->next) {
      serr = CreateSocket(&tmp->listenSock,
                       SOCK_STREAM,
                       htonl(INADDR_ANY),
                       tmp->toport);

      if (serr == 0) {
         if (WSAAsyncSelect(tmp->listenSock,
                         hwndFrame,
                         WM_SOCKET_SELECT,
                         FD_READ | FD_ACCEPT | FD_CLOSE) != 0)
         {
            serr = WSAGetLastError();
         }
      }
      if (serr != 0) {
         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Cannot create command socket, error %d: %s",
             serr,
             SockerrToString(serr));

         return FALSE;
      }
      if (listen(tmp->listenSock, 5) != 0) {
         //
         // Cannot listen on the socket.
         //
      
         serr = WSAGetLastError();
      
         MsgBox(NULL,
             MB_ICONSTOP | MB_OK,
             "Cannot listen on command socket, error %d: %s",
             serr,
             SockerrToString(serr));

         return FALSE;

      }
   }
   //
   // Success!
   //

   return TRUE;

}                               // InitInstance

/*******************************************************************

    NAME:       Main_OnPaint

    SYNOPSIS:   Handles WM_PAINT messages.

    ENTRY:      hwnd - Window handle.

********************************************************************/
VOID            Main_OnPaint(HWND hwnd)
{
   return;
}                               // Main_OnPaint

/*******************************************************************

    NAME:       MsgBox

    SYNOPSIS:   A printf-like interface to MessageBox.

    ENTRY:      hwndParent - The "owning" parent window.

                fuType - A set of MB_* flags.

                pszFormat - A printf-like format string.

                ... - Other printf-like arguments as needed.

    RETURNS:    INT - The result of the MessageBox API.

********************************************************************/
INT             MsgBox(HWND hwndParent,
                                       UINT fuType,
                                       LPSTR pszFormat,
                       ...)
{
   CHAR            szOutput[MAX_PRINTF_OUTPUT];
   va_list         ArgList;

   va_start(ArgList, pszFormat);
   wvsprintf(szOutput, pszFormat, ArgList);
   va_end(ArgList);

   return MessageBox(hwndParent, szOutput, pszAppName, fuType);

}                               // MsgBox


/*******************************************************************

    NAME:       WinPrintf

    SYNOPSIS:   A printf-like interface to TextOut

    ENTRY:      hdc - Display context for the text.

                row - Starting row (* tmHeight).

                col - Starting column (* tmAveCharWidth).

                pszFormat - A printf-like format string.

                ... - Other printf-like arguments as needed.

********************************************************************/
VOID            WinPrintf(HDC hdc,
                                          INT row,
                                          INT col,
                                          LPSTR pszFormat,
                          ...)
{
   CHAR            szOutput[MAX_PRINTF_OUTPUT];
   INT             cbOutput;
   va_list         ArgList;

   va_start(ArgList, pszFormat);
   cbOutput = wvsprintf(szOutput, pszFormat, ArgList);
   va_end(ArgList);

   TextOut(hdc,
           col * tmAveCharWidth,
           row * tmHeight,
           szOutput,
           cbOutput);

}                               // WinPrintf


/*******************************************************************

    NAME:       SockerrToString

    SYNOPSIS:   Maps a socket error (like WSAEINTR) to a displayable
                form (like "Interrupted system call").

    ENTRY:      serr - The error to map.

    RETURNS:    LPSTR - The displayable form of the error.  Will be
                    "Unknown" for unknown errors.

********************************************************************/
LPSTR           SockerrToString(SOCKERR serr)
{
   switch (serr)
   {
 		case WSAENAMETOOLONG:
         return "Name too long";

      case WSANOTINITIALISED:
         return "Not initialized";

      case WSASYSNOTREADY:
         return "System not ready";

      case WSAVERNOTSUPPORTED:
         return "Version is not supported";

      case WSAESHUTDOWN:
         return "Can't send after socket shutdown";

      case WSAEINTR:
         return "Interrupted system call";

      case WSAHOST_NOT_FOUND:
         return "Host not found";

      case WSATRY_AGAIN:
         return "Try again";

      case WSANO_RECOVERY:
         return "Non-recoverable error";

      case WSANO_DATA:
         return "No data record available";

      case WSAEBADF:
         return "Bad file number";

      case WSAEWOULDBLOCK:
         return "Operation would block";

      case WSAEINPROGRESS:
         return "Operation now in progress";

      case WSAEALREADY:
         return "Operation already in progress";

      case WSAEFAULT:
         return "Bad address";

      case WSAEDESTADDRREQ:
         return "Destination address required";

      case WSAEMSGSIZE:
         return "Message too long";

      case WSAEPFNOSUPPORT:
         return "Protocol family not supported";

      case WSAENOTEMPTY:
         return "Directory not empty";

      case WSAEPROCLIM:
         return "EPROCLIM returned";

      case WSAEUSERS:
         return "EUSERS returned";

      case WSAEDQUOT:
         return "Disk quota exceeded";

      case WSAESTALE:
         return "ESTALE returned";

      case WSAEINVAL:
         return "Invalid argument";

      case WSAEMFILE:
         return "Too many open files";

      case WSAEACCES:
         return "Access denied";

      case WSAELOOP:
         return "Too many levels of symbolic links";

      case WSAEREMOTE:
         return "The object is remote";

      case WSAENOTSOCK:
         return "Socket operation on non-socket";

      case WSAEADDRNOTAVAIL:
         return "Can't assign requested address";

      case WSAEADDRINUSE:
         return "Address already in use";

      case WSAEAFNOSUPPORT:
         return "Address family not supported by protocol family";

      case WSAESOCKTNOSUPPORT:
         return "Socket type not supported";

      case WSAEPROTONOSUPPORT:
         return "Protocol not supported";

      case WSAENOBUFS:
         return "No buffer space is supported";

      case WSAETIMEDOUT:
         return "Connection timed out";

      case WSAEISCONN:
         return "Socket is already connected";

      case WSAENOTCONN:
         return "Socket is not connected";

      case WSAENOPROTOOPT:
         return "Bad protocol option";

      case WSAECONNRESET:
         return "Connection reset by peer";

      case WSAECONNABORTED:
         return "Software caused connection abort";

      case WSAENETDOWN:
         return "Network is down";

      case WSAENETRESET:
         return "Network was reset";

      case WSAECONNREFUSED:
         return "Connection refused";

      case WSAEHOSTDOWN:
         return "Host is down";

      case WSAEHOSTUNREACH:
         return "Host is unreachable";

      case WSAEPROTOTYPE:
         return "Protocol is wrong type for socket";

      case WSAEOPNOTSUPP:
         return "Operation not supported on socket";

      case WSAENETUNREACH:
         return "ICMP network unreachable";

      case WSAETOOMANYREFS:
         return "Too many references";

      default:
         return "Unknown";
   }
}


/*******************************************************************

    NAME:       ResetSocket

    SYNOPSIS:   Performs a "hard" close on the given socket.

    ENTRY:      sock - The socket to close.

    RETURNS:    SOCKERR - 0 if successful, !0 if not.

********************************************************************/
SOCKERR         ResetSocket(SOCKET sock)
{
   LINGER          linger;

   if (sock == INVALID_SOCKET)
   {
      //
      // Ignore invalid sockets.
      //

      return 0;
   }
   //
   // Enable linger with a timeout of zero.  This will
   // force the hard close when we call closesocket().
   //
   // We ignore the error return from setsockopt.  If it
   // fails, we'll just try to close the socket anyway.
   //

   linger.l_onoff = TRUE;
   linger.l_linger = 0;

   setsockopt(sock,
              SOL_SOCKET,
              SO_LINGER,
              (CHAR FAR *) & linger,
              sizeof(linger));

   //
   // Close the socket.
   //

   return closesocket(sock);

}                               // ResetSocket


/*******************************************************************

    NAME:       CreateSocket

    SYNOPSIS:   Creates a data socket for the specified address & port.

    ENTRY:      psock - Will receive the new socket ID if successful.

                type - The socket type (SOCK_DGRAM or SOCK_STREAM).

                address - The IP address for the socket.

                port - The port for the socket.

    RETURNS:    SOCKERR - 0 if successful, !0 if not.

********************************************************************/
SOCKERR         CreateSocket(SOCKET FAR * psock,
                                             INT type,
                                             ULONG address,
                                             PORT port)
{
   SOCKET          sNew;
   SOCKERR         serr;

   //
   // Create the socket.
   //

   sNew = socket(PF_INET, type, 0);
   serr = (sNew == INVALID_SOCKET) ? WSAGetLastError() : 0;

   if (serr == 0)
   {
      SOCKADDR_IN     sockAddr;

      //
      // Bind an address to the socket.
      //

      sockAddr.sin_family = AF_INET;
      sockAddr.sin_addr.s_addr = address;
      sockAddr.sin_port = port;

      if (bind(sNew, (SOCKADDR FAR *) & sockAddr, sizeof(sockAddr)) != 0)
      {
         serr = WSAGetLastError();
      }
   }
   if (serr != 0)
   {
      ResetSocket(sNew);
      sNew = INVALID_SOCKET;
   }
   *psock = sNew;

   return serr;

}                               // CreateSocket


/*******************************************************************

    NAME:       WinMain

    SYNOPSIS:   Normal Windows application entry point.

    ENTRY:      hInstance - The current application instance.

                hPrev - The previous instance.  Don't depend on this
                    value, as under Win32 it is always NULL.

                pszCmdLine - Points to command line arguments.

                nCmdShow - Specifies how the initial window should
                    be displayed.  Should just be passed onto
                    ShowWindow.

********************************************************************/
INT PASCAL      WinMain(HINSTANCE hInstance,
                                        HINSTANCE hPrev,
                                        LPSTR pszCmdLine,
                                        INT nCmdShow)
{
   MSG             msg;

   //
   // Application-level initialization.
   //

   if (!InitApplication(hInstance))
   {
      return FALSE;
   }
   //
   // Instance-level initialization.
   //

   if (!InitInstance(hInstance, pszCmdLine, nCmdShow))
   {
      return FALSE;
   }
   while (GetMessage(&msg, 0, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }

   return msg.wParam;

}                               // WinMain
