/* WSMTPSrv.C - Windows SMTP Server
    - WinSock V1.1 required

   Author: Ian Blenke

Ian Blenke cannot be held responsible for damages, expressed or implied, for
the use of this software. No commercial use can be made of this product
without the consent of the author. No profit of any kind can be made on the
sale or distribution of this program. If you wish to distribute this program
with other samples of WinSock programming, you must first contact the author
so that he can keep accurate records of its usage. If you write any programs
based on this source code, you may not sell them for any profit without the
written consent of the author. If you incorporate this source code into a
public domain program, all the author requires is a notification that "part
of the code was written by Ian Blenke" and some form of notification that
his name was used in the public domain software distribution. This does not
represent a contract on the part of the author. If any issues cannot clearly
be resolved by reading this text, immediately contact the author.

I don't like such agreements, but in today's world of lawyers and lawbreakers
I have little other choice. Enjoy!
*/

#include "WSMTPSrv.h"

/* DEBUGIT();
    Purpose: To print debugging messages to the debugging window/console.
*/
/*VOID DEBUGIT( LPSTR lpString)
{
 static char szErrors[256];

 wsprintf((LPSTR)szErrors, (LPSTR)"WSMTPD: %s\n\r", lpString);
 OutputDebugString( (LPSTR)szErrors );
}  /**/

/* WinMain();
    Purpose: The entry point for our app.
*/
UINT PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
 MSG        msg;
 WORD       wVersionRequested;
 WSADATA    wsaData;
 DLGPROC    dlgProc;
 WNDCLASS   wndclass;
 HICON      hIcon;

 hInst=hInstance;
 lstrcpy((LPSTR)szAppName, (LPSTR)APP_NAME);

 //DEBUGIT("WinMain() Begin");

 if(hPrevInstance)
 {
  smtpError(IDS_PREV_INSTANCE);
  return(NULL);
 }

 wVersionRequested=WSVERSION;
 if(WSAStartup(wVersionRequested, (LPWSADATA)&wsaData))
 {
  smtpError(IDS_NO_WINSOCK);
  return(NULL);
 }
 if((LOBYTE(wsaData.wVersion)!=1) ||
    (HIBYTE(wsaData.wVersion)!=1))
 {
  smtpError(IDS_BAD_VERSION);
  WSACleanup();
  //DEBUGIT("WinSock version is not v1.1");
  return(NULL);
 }

 #ifdef USE_3D
  Ctl3dRegister(hInst);
  Ctl3dAutoSubclass(hInst);
 #endif /*USE_3D*/


 dlgProc=(DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);
 hWndDlg=CreateDialog(hInst, (LPSTR)dlgWSMTPSRV,
              GetDesktopWindow(), dlgProc);

 if(!hWndDlg)
 {
  smtpError(IDS_SMTP_INIT);
#ifdef USE_3D
  Ctl3dUnregister(hInst);
#endif // USE_3D
  WSACleanup();
  //DEBUGIT("Modeless dialog window could not be opened");
  return(NULL);
 }

 hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONEMPTY));

 if(hIcon != NULL)
 {
  SetProp(hWndDlg, "icon", hIcon);
 }
// else DEBUGIT("IDI_ICONEMPTY Resource not found");

 SetClassWord(hWndDlg, GCW_HICON, NULL);

 ShowWindow(hWndDlg, nCmdShow);

 //DEBUGIT("Starting smtpInit");

 if(smtpInit(INI_FILE))
 {
  DestroyWindow(hWndDlg);
//  smtpError(IDS_SMTP_INIT);
#ifdef USE_3D
  Ctl3dUnregister(hInst);
#endif /* USE_3D */
  WSACleanup();
  //DEBUGIT("Daemon initialization error");
  return(NULL);
 }

 while(GetMessage(&msg, NULL, 0, 0))
 {
  if(!IsDialogMessage(hWndDlg, &msg))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }
 }

#ifdef USE_3D
 Ctl3dUnregister(hInst);
#endif

  WSACleanup();

  //DEBUGIT("WinMain() End");

  return msg.wParam;
} /* WinMain */


/* LRESULT CALLBACK DlgProc(HWND, UINT, WPARAM);
    Purpose: To handle the main dialog user intervention.
    Given/Returns: Standard Dialog procedure
*/
LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam,
                      LPARAM lParam)
{
 HICON hIcon;

 switch(Msg)
 {
  case WM_INITDIALOG:
    {
     if(iLogLevel==LOG_LOW)
     {
      CheckRadioButton(hWnd, IDR_NORMAL, IDR_DEBUG,
                       IDR_NORMAL);
     }
     else
     {
      CheckRadioButton(hWnd, IDR_NORMAL, IDR_DEBUG,
                       IDR_DEBUG);
     }
     SetDlgItemText(hWnd, IDE_PATH, (LPSTR)szLocalMailPath);
     SetDlgItemText(hWnd, IDE_HOSTNAME, (LPSTR)szLocalHostName);
     return(TRUE);
    }
    break;

  case WM_COMMAND:
  {
   switch(wParam)
   {
    case IDR_NORMAL:
    {
     iLogLevel=LOG_LOW;
     return(TRUE);
    }

    case IDR_DEBUG:
    {
     iLogLevel=LOG_HIGH;
     return(TRUE);
    }

    case IDB_PATH:
    {
     int iError;
     DWORD dwError;

     if(iError=GetOpenFileName((OPENFILENAME FAR *)&ofnMailPath))
     {
      WritePrivateProfileString(SECTION_MAIN, ENTRY_MAILPATH,
                         (LPSTR)szLocalMailPath,
                         (LPSTR)INI_FILE);
      SetDlgItemText(hWndDlg, IDE_PATH, (LPSTR)szLocalMailPath);
     }
     else
     {          // Debugging
      dwError=CommDlgExtendedError();
     }
     return(TRUE);
    }
   }
   break;

    // All of this, for a changing Icon - Sheesh
   case WM_PAINT:
   {
    if(IsIconic(hWndDlg))
    {
     PAINTSTRUCT ps;
     HDC hDC;
     DWORD dwOrg;
     HICON hIcon = GetProp(hWndDlg, "icon");

     if(hIcon != NULL)
     {
      hDC=BeginPaint(hWndDlg, &ps);

      dwOrg = GetWindowOrg(hDC);
      SendMessage(hWndDlg, WM_ICONERASEBKGND, hDC, 0L);
      DrawIcon(hDC, LOWORD(dwOrg)+2, HIWORD(dwOrg)+2, hIcon);
      EndPaint(hWndDlg, &ps);
      return(TRUE);
     }
    }
   }
   break;

   case WM_ERASEBKGND:
   {
    if(IsIconic(hWndDlg)) return(TRUE);
    else return(FALSE);
   }

   case WM_CLOSE:
   {
    RemoveProp(hWndDlg, "icon");
    netClose();
    DestroyWindow(hWndDlg);
    return(TRUE);
   }
   break;

   case WM_QUERYDRAGICON:
   {
    HICON hIcon;

    return(hIcon = GetProp(hWndDlg, "icon"));
   }

   /* Periodically check for mail until user reads it */
   case WM_TIMER:
   {
    if(!smtpMailCheck(szLocalMailPath))
    {
     KillTimer(hWndDlg, (UINT)wParam);
     SetWindowText(hWndDlg, (LPSTR)STRING_NOMAIL);
    }
    else
    {
     SetWindowText(hWndDlg, (LPSTR)STRING_HAVEMAIL);
    }
    return(TRUE);
   }
   break;

   /* Don't let the user ReSize/Maximize the window! */
   case WM_INITMENUPOPUP:
   {
    if(HIWORD(lParam))
    {
     HMENU hSystemMenu;

     hSystemMenu = GetSystemMenu(hWndDlg, FALSE);
     EnableMenuItem(hSystemMenu, SC_SIZE, MF_GRAYED);
     EnableMenuItem(hSystemMenu, SC_MAXIMIZE, MF_GRAYED);
    }
   }
   break;

   case WM_DESTROY:
   {
    PostQuitMessage(0);
    return(TRUE);
   }
   break;
  }
 }
 return(FALSE);
} /* dlgProc() */


/* void smtpError(int);
    Purpose: To print out a resource string with parameters
    Given:   ResourceID of format string, and variable arguements.
    Returns: Nothing.
*/
void smtpError(int ResourceID, ...)
{
 char szString[MAXLINE];
 char szBuffer[MAXLINE];
 va_list vaArgs;
 LPSTR lpArg1, lpArg2;

 va_start( vaArgs, ResourceID);
 lpArg1 = va_arg( vaArgs, LPSTR);
 lpArg2 = va_arg( vaArgs, LPSTR);

 if(IsIconic(hWndDlg))          // If in server mode, no modals!
 {
  smtpLog(LOG_HIGH | LOG_TIME, ResourceID, vaArgs);
  return;
 }

 if(!LoadString(hInst, ResourceID, (LPSTR)szString,
                MAXLINE))
    return;

 wsprintf((LPSTR)szBuffer, (LPSTR)szString, lpArg1, lpArg2);

 //DEBUGIT(szBuffer);
 MessageBeep(MB_ICONEXCLAMATION);
 MessageBox(NULL, (LPSTR)szBuffer, (LPSTR)szAppName,
            MB_OK | MB_ICONEXCLAMATION);
 return;
} /* smtpError */


/* void smtpLog(int, ...);
    Purpose: To print an item in the "log" listbox.
    Given:   Resource ID and variable args.
    Returns: Nothing
*/
void smtpLog(int iLevel, int ResourceID, ...)
{
 char szString[MAXLINE];
 char szLine[MAXLINE];
 char szTime[30];
 LPSTR lpArg1, lpArg2;
 va_list vaArgs;
 DWORD dwCount;

 if((iLevel&(LOG_TIME-1))<iLogLevel) return;          // Only log what we want

 va_start( vaArgs, ResourceID);

 if(!LoadString(hInst, ResourceID, (LPSTR)szLine, MAXLINE))
    return;

 lpArg1=va_arg(vaArgs, LPSTR);
 lpArg2=va_arg(vaArgs, LPSTR);

 wsprintf((LPSTR)szString, (LPSTR)szLine, lpArg1, lpArg2);

 if(iLevel&LOG_TIME)
 {
  netGetTimeAndDate((LPSTR)szTime, sizeof(szTime));
  wsprintf((LPSTR)szLine, "%s> %s", (LPSTR)szTime, (LPSTR)szString);
 }
 else
 {
  lstrcpy((LPSTR)szLine, (LPSTR)szString);
 }
 //DEBUGIT(szLine);
 SendDlgItemMessage(hWndDlg, IDL_LOG, LB_ADDSTRING, 0,
                    (LPARAM)(LPCSTR)szLine);

 dwCount=SendDlgItemMessage(hWndDlg, IDL_LOG, LB_GETCOUNT, 0, 0l);
 if(dwCount>MAXITEMS)
 {
  SendDlgItemMessage(hWndDlg, IDL_LOG, LB_RESETCONTENT, 0, 0l);
  wsprintf((LPSTR)szLine, "%s> Reset Log - over %d lines", (LPSTR)szTime,
           MAXITEMS);
  //DEBUGIT(szLine);
  SendDlgItemMessage(hWndDlg, IDL_LOG, LB_ADDSTRING, 0,
                    (LPARAM)(LPCSTR)szLine);
 }
} /* smtpLog */


/* BOOL smtpInit(PSTR);
    Purpose: To setup the SMTP protocol and structures.
    Given:   A filename.
    Returns: TRUE on error, FALSE if not.
*/
BOOL smtpInit(PSTR pFilename)
{
 LPSTR lpString;
 HANDLE hMenu;

 ofnMailPath.lStructSize=sizeof(ofnMailPath);
 ofnMailPath.hwndOwner=hWndDlg;
 ofnMailPath.hInstance=hInst;
 szMailPathFilter[0]='\0';
 LoadString(hInst, IDS_FILTER,
            (LPSTR)szMailPathFilter, sizeof(szMailPathFilter));
 ofnMailPath.lpstrFilter=(LPSTR)szMailPathFilter;
 ofnMailPath.nFilterIndex=2;
 ofnMailPath.lpstrCustomFilter=(LPSTR)NULL;
 ofnMailPath.lpstrFile=(LPSTR)szLocalMailPath;
 ofnMailPath.nMaxFile=sizeof(szLocalMailPath);
 ofnMailPath.lpstrFileTitle=(LPSTR)NULL;
 ofnMailPath.lpstrInitialDir=(LPSTR)NULL;
 ofnMailPath.lpstrTitle=(LPSTR)szAppName;
 ofnMailPath.Flags=OFN_CREATEPROMPT | OFN_NOREADONLYRETURN;
 ofnMailPath.lpstrDefExt=(LPSTR)NULL;

    // Initialize network windows & such
 if(netInit()) return(TRUE);

 iLogLevel=GetPrivateProfileInt(SECTION_MAIN, ENTRY_DEBUG,
                                LOG_HIGH, (LPSTR)pFilename);
 GetPrivateProfileString(SECTION_MAIN, ENTRY_MAILPATH,
                         (LPSTR)DEFAULTMAILFILE,
                         (LPSTR)szLocalMailPath,
                         sizeof(szLocalMailPath),
                         (LPSTR)pFilename);

 if(iLogLevel==LOG_LOW)
 {
  CheckRadioButton(hWndDlg, IDR_NORMAL, IDR_DEBUG,
                   IDR_NORMAL);
 }
 else
 {
  CheckRadioButton(hWndDlg, IDR_NORMAL, IDR_DEBUG,
                   IDR_DEBUG);
 }
 SetDlgItemText(hWndDlg, IDE_PATH, (LPSTR)szLocalMailPath);
 SetDlgItemText(hWndDlg, IDE_HOSTNAME, (LPSTR)szLocalHostName);

 return(FALSE);
} /* smtpInit */


/* BOOL smtpServer(LPSMTPCLIENT);
    Purpose: To be the heart & soul of the SMTP
             server.
    Given:   pointer to the Client that was active.
    Returns: FALSE
    Notes:   This is the heart & soul of the beast.
*/
BOOL smtpServer(LPSMTPCLIENT lpClient)
{
 static char szLine[MAXSNDBUFF];       // These are still holdovers
 static char szPaddedLine[MAXSNDBUFF]; // They should be replaced
 PSTR pLine;
 int iDebug;
 BOOL bCmdOk;

 if(!lpClient) return(TRUE);

 //DEBUGIT("smtpServer() Begin");

    // 8BITMIME hack
 if((lpClient->iExtendedFlags & ESMTP_8BITMIME) &&
    (lpClient->iState == STATE_WAIT_FOR_DOT))
 {
  //DEBUGIT("ESMTP_8BITMIME && STATE_WAIT_FOR_DOT");
  while(clientReceiveBlock(lpClient, szLine, sizeof(szLine)))
  {
   if(lpClient->hfFile!=HFILE_ERROR)
   {
    iDebug=_lwrite(lpClient->hfFile,
            (LPSTR)szLine, lstrlen((LPSTR)szLine));
   }
  } // While there is data
 } // If in DATA state with 8BITMIME active.

    // This is a hack for when a client QUIT's.
 bConnectionQuit=FALSE;

    // Get as many lines as are waiting
 while(!bConnectionQuit &&
       (clientReceiveLine(lpClient, szLine, sizeof(szLine)) >= 0))
 {
    // Treat them one at a time
  switch(lpClient->iState)
  { // Command mode?
   case STATE_WAIT_FOR_COMMAND:
   {
    int iLoop;

    //DEBUGIT("STATE_WAIT_FOR_COMMAND");
    if(!(*szLine))
    {
     smtpSendMessage(lpClient, 500, MSG_UNKNOWN);
     continue;  // CR, LF, or '\0' came across
    }

    pLine=szLine;
    while((*pLine!='\0')&&(*pLine!=' ')&&(*pLine!='\n'))
            pLine++;
    AnsiLower((LPSTR)szLine);
    if(*pLine!='\0')
    {
     *(pLine++)='\0';
    }
        // szLine now holds the lowercase command
        // pLine points to the remaining parameters
    iLoop=0;
    bCmdOk=FALSE;
    while(smtpCodesTable[iLoop].SMTPCommand != NULL)
    {
     if(lstrcmp((LPSTR)smtpCodesTable[iLoop].SMTPCommand,
                (LPSTR)szLine)==0)
     {
      smtpParser(lpClient, smtpCodesTable[iLoop].SMTPCode, pLine);
      bCmdOk=TRUE;
      break;
     }
     iLoop++;
    }
    if(!bCmdOk) smtpSendMessage(lpClient, 500, MSG_UNKNOWN);
   }
   break;

    // Data mode?
   case STATE_WAIT_FOR_DOT:
   {
    //DEBUGIT("STATE_WAIT_FOR_DOT");
    if((szLine[0]=='.')&&(szLine[1]=='\0'))
    {
     lpClient->iState=STATE_WAIT_FOR_COMMAND;
     if(lpClient->hfFile!=HFILE_ERROR)
     {
             _lclose(lpClient->hfFile);
     }
     lpClient->hfFile=HFILE_ERROR;
        // Handle MAIL/SAML/SOML as MAIL
     if((lpClient->iMessageType==CMDMAIL)||
        (lpClient->iMessageType==CMDSAML)||
        (lpClient->iMessageType==CMDSOML))
     {
      smtpAppendToFile(lpClient, szLocalMailPath,
                       lpClient->szFile);
     }
        // Handle SAML/SEND as SEND also
     if((lpClient->iMessageType==CMDSAML)||
        (lpClient->iMessageType==CMDSEND))
     {
      smtpDisplayFile(lpClient, lpClient->szFile);
     }
     smtpSendMessage(lpClient, 250, MSG_OK);
     continue;
    }
    else if((szLine[0]=='.')&&(szLine[1]=='.'))
    {   // Byte stuff it! Someone tried to send a '.' in their
        // message. Only the first '.' is doubled.
     lstrcpy((LPSTR)szPaddedLine, (LPSTR)(szLine+1));
     lstrcpy((LPSTR)szLine, (LPSTR)szPaddedLine);
    }
        // All non '.' lines are stored.
    if(lpClient->hfFile!=HFILE_ERROR)
    {
     wsprintf((LPSTR)szPaddedLine, "%s\r\n", (LPSTR)szLine);
     iDebug=_lwrite(lpClient->hfFile,
             (LPSTR)szPaddedLine, lstrlen((LPSTR)szPaddedLine));
    }
    continue;
   } /* case(data mode)  */
   break;
  } /* switch(mode);     */
  if(bConnectionQuit) break;
 } /* while(lines exist);*/
 //DEBUGIT("smtpServer() End");
 return(FALSE);
} /* smtpServer() */


/* BOOL smtpParser(LPSMTPCLIENT, int, LPSTR);
    Purpose: The actual parser of the SMTP "language"
    Given:   Client pointer, Token of command, and it's arguments
*/
BOOL smtpParser(LPSMTPCLIENT lpClient, int iToken, LPSTR lpArgs)
{
 //DEBUGIT("smtpParser() Begin");
 switch(iToken)
 {
    // HELO - SMTP client is identifying itself
  case CMDHELO:
  {
   //DEBUGIT("CMDHELO Begin");
   if(lstrcmpi(lpClient->szPeer, lpArgs) != 0)
   {
    if(lstrlen(lpArgs) == 0)
        smtpSendMessage(lpClient, 250, MSG_WHOAREYOU_S_S,
                    (LPSTR)szLocalHostName,
                    lpClient->szPeer);
    else
        smtpSendMessage(lpClient, 250, MSG_WHOAREYOU_S_S_S,
                    (LPSTR)szLocalHostName,
                    lpClient->szPeer,
                    lpArgs);
   }
   else
   {
    smtpSendMessage(lpClient, 250, MSG_HELLO_S_S,
                    (LPSTR)szLocalHostName,
                    lpArgs);
   }
   //DEBUGIT("CMDHELO End");
   return(FALSE);
  }

    // EHLO - ESMTP client is identifying itself
  case CMDEHLO:
  {
   //DEBUGIT("CMDEHLO Begin");
   if(lstrcmpi(lpClient->szPeer,
               lpArgs) != 0)
   {
    if(lstrlen(lpArgs) == 0)
        smtpSendMessage(lpClient, 250, MSG_HWOAREYOU_S_S,
                    (LPSTR)szLocalHostName,
                    lpClient->szPeer);
    else
        smtpSendMessage(lpClient, 250, MSG_HWOAREYOU_S_S_S,
                    (LPSTR)szLocalHostName,
                    lpClient->szPeer,
                    lpArgs);
   }
   else
   {
    smtpSendMessage(lpClient, 250, MSG_EHLLO_S_S,
                    (LPSTR)szLocalHostName,
                    lpArgs);
   }
   lpClient->iExtendedFlags |= ESMTP_EHLO_USED;
   //DEBUGIT("CMDEHLO End");
   return(FALSE);
  }

    // Oh, joy.
  case CMDSEND: // Send user a message to screen
  case CMDSAML: // Send to screen AND store as mail
  case CMDSOML: // Send to screen OR store as mail
  case CMDMAIL: // Store as mail to user
  {
   LPSTR lpPtr;
   LPSTR lpWalk;
   LPSTR lpMark;
   BOOL bSentReply = FALSE;

   //DEBUGIT("CMDMAIL Begin");
   lpClient->iMessageType=iToken;
   if(lpClient->bHaveFrom)
   {
    smtpSendMessage(lpClient, 503, MSG_ALREADYSENDER);
    return(FALSE);
   }
   else
   {
    if(*lpArgs=='\0') break;
    if( ( lpPtr = smtpSkipWord( lpClient, lpArgs, (LPSTR)"from" )) != NULL )
    {
     lpClient->bHaveFrom=TRUE;
        // No long name crashes!
     if(lstrlen(lpPtr)>=sizeof(lpClient->szFrom))
            lpPtr[sizeof(lpClient->szFrom)-1] = '\0';

        // 8BITMIME and other ESMTP hacks
     if(lpClient->iExtendedFlags & ESMTP_EHLO_USED)
     {
        // Check for ESMTP commands after the Sender's address
      lpWalk=lpPtr;
      while((*lpWalk!='>') && *lpWalk ) lpWalk++;
      if((*lpWalk)&&(*(lpWalk+1)))
      {
       if(*(++lpWalk)==' ')
       {
        while(*lpWalk==' ') *lpWalk++ = '\0';
        lpMark=lpWalk;
        while((*lpWalk!='=') && *lpWalk ) lpWalk++;
        if(*lpWalk) *lpWalk++ = '\0';
        if(lstrcmpi(lpMark, "BODY") == 0)
        {
         lpMark=lpWalk;
         while((*lpWalk!=' ') && *lpWalk ) lpWalk++;
         if(*lpWalk) *lpWalk++ = '\0';
         if(lstrcmpi(lpMark, "8BITMIME") == 0)
         {
          lpClient->iExtendedFlags |= ESMTP_8BITMIME;
          bSentReply=TRUE;
          smtpSendMessage(lpClient, 250, MSG_SENDEROK_8BITMIME_S,
                          (LPSTR)lpPtr);
         } // if 8BITMIME found
        } // if BODY= found
       } // if address is followed by a space
      } // if something was found after the address
     } // if extended commands are in effect

     lstrcpy(lpClient->szFrom,
             lpPtr);
     if(!bSentReply) smtpSendMessage(lpClient, 250, MSG_SENDEROK_S,
                                     lpPtr);
     //DEBUGIT("CMDMAIL End (Extended)");
     return(FALSE);
    }
        // a 501 error was already sent
    //DEBUGIT("CMDMAIL End (Already Sender)");
    return(FALSE);
   }
   //DEBUGIT("CMDHELO End (Normal)");
  } break;

    // RCPT - Client is identifying recipient's ID
  case CMDRCPT:
  {
   LPSTR lpPtr;

   //DEBUGIT("CMDRCPT Begin");
   if( *lpArgs == '\0' ) break;
   if( ( lpPtr = smtpSkipWord( lpClient, lpArgs, (LPSTR)"to" )) != NULL )
   {
        // No long name crashes!
    if(lstrlen(lpPtr)>=sizeof(lpClient->szTo))
            lpPtr[sizeof(lpClient->szTo)-1] = '\0';
    lstrcpy(lpClient->szTo,
            lpPtr);
    smtpSendMessage( lpClient, 250, MSG_RECIPIENTOK_S,
                     lpPtr );
    //DEBUGIT("CMDRCPT End (Recipient ok)");
    return(FALSE);
   }
       // A 501 error was already sent
   //DEBUGIT("CMDRCPT End (???)");
   return(FALSE);
  } break;

    // DATA - Client is sending actual mail message
  case CMDDATA:
  {
   HFILE hfFile;
   char szFile[MAXFILENAME];
   OFSTRUCT ofOpenFileStruct;

   //DEBUGIT("CMDDATA Begin");
   if(!lpClient->bHaveFrom)
   {
    smtpSendMessage(lpClient, 503, MSG_NEEDMAIL);
    //DEBUGIT("CMDDATA End (Need mail?)");
    return(FALSE);
   }
        // Open a temporary file
   GetTempFileName(0, (LPSTR)STRING_PREFIX, 0, (LPSTR)szFile);
   hfFile=OpenFile((LPSTR)szFile, (OFSTRUCT FAR *)&ofOpenFileStruct,
                    OF_CREATE | OF_WRITE | OF_SHARE_EXCLUSIVE);

   if(hfFile==HFILE_ERROR)
   {
    smtpSendMessage(lpClient, 503, MSG_NOTEMP);
    //DEBUGIT("CMDDATA End (No temporary file)");
    return(FALSE);
   }
        // Ok, everything is ready to start the message.
   lpClient->iState=STATE_WAIT_FOR_DOT;
   lpClient->hfFile=hfFile;
   lstrcpy(lpClient->szFile,
           (LPSTR)szFile);

   smtpMakeHeader(lpClient, hfFile, lpClient->szPeer,
                  lpClient->szFrom,
                  lpClient->szTo);

   if(lpClient->iExtendedFlags & ESMTP_8BITMIME)
     smtpSendMessage(lpClient, 354, MSG_SEND8BITMIME);
   else
     smtpSendMessage(lpClient, 354, MSG_SENDDATA);

   //DEBUGIT("CMDDATA End");
   return(FALSE);
  }

  case CMDRSET:
  {
   //DEBUGIT("CMDRSET Begin");
   lpClient->iState=STATE_WAIT_FOR_COMMAND;
   lpClient->iExtendedFlags=ESMTP_NONE;
   lpClient->bHaveFrom=FALSE;
   lpClient->szFrom[0]='\0';
   lpClient->szTo[0]='\0';
   smtpSendMessage(lpClient, 250, MSG_RESET);
   //DEBUGIT("CMDRSET End");
   return(FALSE);
  }

  case CMDVRFY:
  {
   //DEBUGIT("CMDVRFY Begin");
   smtpSendMessage(lpClient, 250, MSG_IDONTDOTHATYET);
   //DEBUGIT("CMDVRFY Begin");
   return(FALSE);
  }

  case CMDHELP:
  {
   //DEBUGIT("CMDHELP Begin");
   if(*lpArgs=='\0')
   {
    smtpSendHelp(lpClient, NULL);
   }
   else smtpSendHelp(lpClient, lpArgs);
   //DEBUGIT("CMDHELP End");
   return(FALSE);
  } break;

  case CMDNOOP:
  {
   //DEBUGIT("CMDNOOP Begin");
   smtpSendMessage(lpClient, 200, MSG_OK);
   //DEBUGIT("CMDNOOP Begin");
   return(FALSE);
  }

  case CMDQUIT:
  {
   //DEBUGIT("CMDQUIT Begin");
   smtpSendMessage(lpClient, 221, MSG_GOODBYE_S,
                   (LPSTR)szLocalHostName);
   smtpDestroyClient(lpClient);
   bConnectionQuit=TRUE;
   //DEBUGIT("CMDQUIT End");
   return(FALSE);
  }

  case CMDVERB:
  {
   //DEBUGIT("CMDVERB Begin");
   smtpSendMessage(lpClient, 200, MSG_VERBOSEMODE);
   //DEBUGIT("CMDVERB End");
   return(FALSE);
  }

  case CMDONEX:
  {
   //DEBUGIT("CMDONEX Begin");
   smtpSendMessage(lpClient, 200, MSG_ONETRANSACTION);
   //DEBUGIT("CMDONEX End");
   return(FALSE);
  }

  case CMDTICK:
  {
   //DEBUGIT("CMDTICK Begin");
   smtpSendMessage(lpClient, 250, MSG_OK);
   //DEBUGIT("CMDTICK End");
   return(FALSE);
  }

  case CMDXWIN3:
  {
   //DEBUGIT("CMDXWIN3 Begin");
   smtpSendMessage(lpClient, 250, MSG_WIN3OK);
   //DEBUGIT("CMDXWIN3 End");
   return(FALSE);
  }

  case CMDDBGQSHOW:
  {
   //DEBUGIT("CMDDBGQSHOW Begin");
   smtpSendMessage(lpClient, 200, MSG_SHOWQ);
   //DEBUGIT("CMDDBGQSHOW End");
   return(FALSE);
  }

  case CMDDBGDEBUG:
  {
   //DEBUGIT("CMDDBGDEBUG Begin");
   smtpSendMessage(lpClient, 200, MSG_DEBUGSET);
   //DEBUGIT("CMDDBGDEBUG End");
   return(FALSE);
  }

  case CMDMULT:
  {
   //DEBUGIT("CMDMULT Begin");
   smtpSendMessage(lpClient, 250, MSG_OK);
   //DEBUGIT("CMDMULT End");
   return(FALSE);
  }

  case CMDERROR:
    //DEBUGIT("CMDERROR");
  default:
  break;
 }
 smtpSendMessage(lpClient, 500, MSG_UNKNOWN);
 //DEBUGIT("smtpParser() End");
 return(FALSE);
} /* smtpParser() */


/* void smtpMakeHeader(LPSMTPCLIENT, HFILE, LPSTR, LPSTR, LPSTR);
    Purpose: To create the "header" portion of the mail
             file.
    Given:   Client pointer, Open (and valid) file handle,
             Peer name, From and To strings.
    Returns: Nothing.
*/
void smtpMakeHeader(LPSMTPCLIENT lpClient, HFILE hfFile, LPSTR lpPeer,
                    LPSTR lpFrom, LPSTR lpTo)
{
 int iSize;
 char szTime[30];
 char szBuffer[MAXLINE];
 LPSOCKADDR_IN lpsaHostAddr;

 //DEBUGIT("smtpMakeHeader() Begin");
 lpsaHostAddr=(LPSOCKADDR_IN)&(lpClient->saPeer);

 netGetTimeAndDate((LPSTR)szTime, sizeof(szTime));

 iSize=wsprintf((LPSTR)szBuffer, "\r\nFrom %s %s\r\n",
                lpFrom, (LPSTR)szTime);
 _lwrite(hfFile, (LPSTR)szBuffer, iSize);

 iSize=wsprintf((LPSTR)szBuffer, "X-Envelope-To: %s\r\n",
                lpTo);
 _lwrite(hfFile, szBuffer, iSize);

 iSize=wsprintf((LPSTR)szBuffer, "Return-Path: %s\r\n",
                lpFrom);
 _lwrite(hfFile, (LPSTR)szBuffer, iSize);

 if(lpClient->iExtendedFlags & ESMTP_8BITMIME)
 {
  iSize=wsprintf((LPSTR)szBuffer,
       "Received: from %s [%s] by %s (1.0/WINSMTPSRV/8BITMIME)\r\n",
                lpPeer,
                inet_ntoa(lpsaHostAddr->sin_addr),
/*  inet_ntoa() and [%s] or this and [%d.%d.%d.%d], take your pick
                lpsaHostAddr->sin_addr.S_un.S_un_b.s_b1,
                lpsaHostAddr->sin_addr.S_un.S_un_b.s_b2,
                lpsaHostAddr->sin_addr.S_un.S_un_b.s_b3,
                lpsaHostAddr->sin_addr.S_un.S_un_b.s_b4,
*/
                (LPSTR)szLocalHostName);
 }
 else
 {
  iSize=wsprintf((LPSTR)szBuffer,
       "Received: from %s [%s] by %s (1.61/WINSMTPSRV)\r\n",
                lpPeer,
                inet_ntoa(lpsaHostAddr->sin_addr),
                (LPSTR)szLocalHostName);
 }

 _lwrite(hfFile, (LPSTR)szBuffer, iSize);

     // Log incoming to/from
 wsprintf((LPSTR)szBuffer, "Message from: %s  to: %s",
          lpFrom, lpTo);
 SendDlgItemMessage(hWndDlg, IDL_LOG, LB_ADDSTRING, 0,
                     (LPARAM)(LPCSTR)szBuffer);
 //DEBUGIT("smtpMakeHeader() End");
 return;
} /* smtpMakeHeader */


/* void smtpSendHelp(LPSMTPCLIENT, LPSTR);
    Purpose: To give the remote user help (not batch, obviously)
    Given:   Client Index, Topic
    Returns: Nothing.
*/
void smtpSendHelp(LPSMTPCLIENT lpClient,LPSTR lpTopic)
{
 LPSTR lpLine;
 int  iLoop;
 WORD wTopic;
 char szBuffer[MAXSNDBUFF];

 //DEBUGIT("smtpSendHelp() Begin");
 if(lpTopic!=NULL)
 {
  lpLine=lpTopic;

  // pLine points to the HELP parameters
  iLoop=0;
  wTopic=CMDHELP;
  while(smtpCodesTable[iLoop].SMTPCommand != NULL)
  {
   if(lstrcmp((LPSTR)smtpCodesTable[iLoop].SMTPCommand,
              lpLine)==0)
   {
    wTopic=(WORD)smtpCodesTable[iLoop].SMTPCode;
    break;
   }
   iLoop++;
  }
 }
 else wTopic=CMDHELP;

 smtpSendMessage(lpClient, 214, wTopic);

    // Kludge
 if(lpTopic==NULL)
 {
  lstrcpy((LPSTR)szBuffer, "HELP");
 }
 else
 {
  lstrcpy((LPSTR)szBuffer, (LPSTR)smtpCodesTable[iLoop].SMTPCommand);
  AnsiUpper((LPSTR)szBuffer);
 }

 smtpSendMessage(lpClient, 214, MSG_END_HELP_S, (LPSTR)szBuffer);
 //DEBUGIT("smtpSendHelp() End");
 return;
} /* smtpSendHelp */


/* LPSTR smtpSkipWord(LPSMTPCLIENT, LPSTR, LPSTR);
    Purpose: To skip over words not neccisary on an SMTP line.
    Given:   Client pointer, string to examine, word to skip
    Returns: NULL if error, rest of line if not
*/
LPSTR smtpSkipWord(LPSMTPCLIENT lpClient, LPSTR lpString, LPSTR lpWord)
{
 LPSTR lpTemp;

 //DEBUGIT("smtpSkipWord() Begin");
 while((*lpString==' ')||(*lpString=='\t'))  lpString++;
 lpTemp=lpString;
 while((*lpString!='\0')&&(*lpString!=':')&&
       !((*lpString==' ')||(*lpString=='\t'))) lpString++;
 while((*lpString==' ')||(*lpString=='\t')) *(lpString++)=='\0';
 if(*lpString!=':')
 {
  smtpSendMessage(lpClient, 501, MSG_SYNTAXERROR);
  return(NULL);
 }
 *(lpString++)='\0';
 while((*lpString==' ')||(*lpString=='\t')) lpString++;
 if(lstrcmpi(lpTemp, lpWord))
 {
  smtpSendMessage(lpClient, 501, MSG_SYNTAXERROR);
  return(NULL);
 }
 //DEBUGIT("smtpSkipWord() End");
 return(lpString);
} /* smtpSkipWord */


/* void smtpAppendToFile(LPSMTPCLIENT, LPSTR, LPSTR);
    Purpose: To copy one file to the end of another.
    Given:   Pointer to a SMTPCLIENT for error messages,
             Filename of the file to append to,
             Filename of the file to append from.
    Returns: Nothing.
*/
void smtpAppendToFile(LPSMTPCLIENT lpClient, LPSTR lpTo, LPSTR lpFrom)
{
 HFILE hTo, hFrom;
 char szBuffer[MAXSNDBUFF];
 int iSize;
 OFSTRUCT ofToFileStruct,
          ofFromFileStruct;

 //DEBUGIT("smtpAppendToFile() Begin");
    // Open the MAIL file for reading
 hTo=OpenFile(lpTo, (OFSTRUCT FAR *)&ofToFileStruct,
              OF_WRITE | OF_SHARE_EXCLUSIVE);
    // Does the main MAIL file exist?
 if(hTo==HFILE_ERROR)
 {
    // No? Create one.
  hTo=OpenFile(lpTo, (OFSTRUCT FAR *)&ofToFileStruct,
               OF_CREATE | OF_WRITE | OF_SHARE_EXCLUSIVE);
    // Serious error!
  if(hTo==HFILE_ERROR)
  {
   smtpSendMessage(lpClient, 503, MSG_MAILOPEN);
   smtpError(IDS_COULDNTOPEN_S, lpTo);
   //DEBUGIT("smtpAppendToFile() End (File error 1)");
   return;
  }
 }

    // Open the temporary file
 hFrom=OpenFile(lpFrom, (OFSTRUCT FAR *)&ofToFileStruct,
                OF_READ | OF_SHARE_DENY_WRITE);
 if(hFrom==HFILE_ERROR)
 {
   smtpSendMessage(lpClient, 503, MSG_TEMPOPEN);
   smtpError(IDS_COULDNTOPEN_S, lpFrom);
   _lclose(hTo);
   //DEBUGIT("smtpAppendToFile() End (File error 2)");
   return;
 }

    // Seek to the end of the main MAIL file
 if(_llseek(hTo, 0, 2)==HFILE_ERROR)
 {
  smtpSendMessage(lpClient, 503, MSG_SEEKERROR);
  smtpError(IDS_FILEERROR_S, lpTo);
  _lclose(hTo);
  _lclose(hFrom);
   //DEBUGIT("smtpAppendToFile() End (File error 3)");
  return;
 }

    // Copy the temporary file to the end of the MAIL file
 while(iSize=_lread(hFrom, (LPSTR)szBuffer, sizeof(szBuffer)))
 {
  if((iSize==HFILE_ERROR)||
     (_lwrite(hTo, (LPSTR)szBuffer, iSize)==HFILE_ERROR)) break;
 }
    // Close 'em both
 _lclose(hTo);
 _lclose(hFrom);

    // Icon Flag!
 bMailArrived=TRUE;
 SetProp(hWndDlg, "icon", LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONFULL)));
 RedrawWindow(hWndDlg, NULL, NULL, RDW_INTERNALPAINT);

    // Tell user mail just came
 MessageBeep(MB_ICONEXCLAMATION);

    // Wait a bit and check to see if this mail still exists
 SetTimer(hWndDlg, (UINT)ID_TIMER, (UINT)TIME_CHECK, NULL);

    // Delete the temporary file
 if(!lpClient) return;
 if(lpClient->iMessageType!=CMDSAML)
 {
  OpenFile(lpFrom, (OFSTRUCT FAR *)&ofFromFileStruct,
           OF_DELETE);
 }
 //DEBUGIT("smtpAppendToFile() End");
 return;
} /* smtpAppendToFile() */


/* BOOL smtpMailCheck(LPSTR);
    Purpose: To check to see if the main mail file still exists.
    Given:   Filename of the main file to check for.
    Returns: Nothing.
    Other:   Modifies mail flag
*/
BOOL smtpMailCheck(LPSTR lpFilename)
{
 OFSTRUCT ofFile;
 HICON hIcon;

 if(OpenFile(lpFilename, (OFSTRUCT FAR *)&ofFile,
             OF_EXIST | OF_SHARE_COMPAT) != HFILE_ERROR)
 {
  if(!bMailArrived)
  {
   bMailArrived=TRUE;

   hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONFULL));
   SetProp(hWndDlg, "icon", hIcon);
   RedrawWindow(hWndDlg, NULL, NULL, RDW_INTERNALPAINT);
  }
 }
 else
 {
  if(bMailArrived)
  {
   bMailArrived=FALSE;
   hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONEMPTY));
   SetProp(hWndDlg, "icon", hIcon);
   RedrawWindow(hWndDlg, NULL, NULL, RDW_INTERNALPAINT);
  }
 }
 return(bMailArrived);
} /* smtpMailCheck() */


/* BOOL smtpDisplayFile(LPSMTPCLIENT, LPSTR);
    Purpose: To display a mail message to the screen for
             SEND and SAML.
    Given:   Pointer to a related SMTPCLIENT struct, filename
    Return:  TRUE if error, FALSE if not.
*/
BOOL smtpDisplayFile(LPSMTPCLIENT lpClient, LPSTR lpFilename)
{
 char szCommand[256];
 OFSTRUCT ofFile;

 //DEBUGIT("smtpDisplayFile() Begin");
    // Beep user to tell them mail came.
 MessageBeep(MB_ICONEXCLAMATION);

    // "Fork" off a notepad to handle it
 wsprintf((LPSTR)szCommand, "notepad %s", lpFilename);
 if(WinExec((LPSTR)szCommand, SW_SHOW)<32)
 {
  smtpAppendToFile(lpClient, (LPSTR)szLocalMailPath, lpFilename);
 }

    // Delete the temporary file
 if((lpClient->iMessageType==CMDSAML)||
    (lpClient->iMessageType==CMDSEND))
 {
  OpenFile(lpFilename, (OFSTRUCT FAR *)&ofFile,
           OF_DELETE);
 }
 //DEBUGIT("smtpDisplayFile() End");
 return(FALSE);
} /* smtpDisplayFile() */


/* LPSMTPCLIENT smtpAddClient(SOCKET);
    Purpose: Allocate a client structure and initilize it.
    Given:   SOCKET of a connection.
    Returns: A negative number on error, or a
             valid SMTPCLIENT index into
             smtpClientsTable
    Reusability notes:
             SMTPCLIENT smtpClientsTable;
             PHASE_INIT,3 MAXBUFF, MAXCLIENTS;
*/
LPSMTPCLIENT smtpAddClient(SOCKET sSocket)
{
 int iRcvSize;
 int iSizeofRcvSize;
 HANDLE hInput, hOutput;
 HCLIENT hClient;
 LPSTR lpInput, lpOutput;
 LPSMTPCLIENT lpClient, lpWalker;

 //DEBUGIT("smtpAddClient() Begin");
    // Find the receiver buffer size
 iSizeofRcvSize=sizeof(iRcvSize);
 getsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, (LPSTR)&iRcvSize,
            (LPINT)&iSizeofRcvSize );

    // Make sure we can handle a certain size line at a time
 if(iRcvSize<MAXSNDBUFF) iRcvSize=MAXSNDBUFF;

    // Make sure we can handle up to 2 buffers at a time
 iRcvSize=iRcvSize*2;

    // Allocate the receiver buffer
 hInput=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
                  iRcvSize);
 if(!hInput) return(NULL);
 lpInput=GlobalLock(hInput);
 if(!lpInput)
 {
  GlobalFree(hInput);
  return(NULL);
 }

    // Allocate the sending buffer
 // The size is permanent, we only need dynamic with incoming
 hOutput=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
                  MAXSNDBUFF);
 if(!hOutput)
 {
  GlobalUnlock(hInput);
  GlobalFree(hInput);
  return(NULL);
 }
 lpOutput=GlobalLock(hOutput);
 if(!lpOutput)
 {
  GlobalFree(hOutput);
  GlobalUnlock(hInput);
  GlobalFree(hInput);
  return(NULL);
 }

    // Allocate a SMTPCLIENT element
 hClient=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
                  sizeof(SMTPCLIENT));
 if(!hClient)
 {
  GlobalUnlock(hOutput);
  GlobalFree(hOutput);
  GlobalUnlock(hInput);
  GlobalFree(hInput);
  return(NULL);
 }
 lpClient=(LPSMTPCLIENT)GlobalLock(hClient);
 if(!lpClient)
 {
  GlobalFree(hClient);
  GlobalUnlock(hOutput);
  GlobalFree(hOutput);
  GlobalUnlock(hInput);
  GlobalFree(hInput);
  return(NULL);
 }

 lpClient->sSocket=sSocket;
 lpClient->bHaveFrom=FALSE;
 lpClient->hfFile=HFILE_ERROR;
 lpClient->hClient=hClient;

 lpClient->lpOutputBuffer=lpOutput;
 lpClient->hOutputBuffer=hOutput;
    // iOutputSize is MAXSNDBUFF
 lpClient->lpInputBuffer=lpInput;
 lpClient->hInputBuffer=hInput;
 lpClient->iInputSize=iRcvSize;

 lpClient->fifoOutputStart=0;
 lpClient->fifoOutputStop=0;
 lpClient->fifoInputStart=0;
 lpClient->fifoInputStop=0;
 lpClient->iState=STATE_WAIT_FOR_COMMAND;
 lpClient->iExtendedFlags=ESMTP_NONE;

 lpClient->lpNextClient=NULL;
 lpClient->lpPrevClient=NULL;

    // If this is the main listening socket, initialize List
 if(!smtpClientsHead)
 {
  smtpClientsHead=lpClient;
 }
 else
 {
  lpWalker=smtpClientsHead;
  while(lpWalker->lpNextClient) lpWalker=lpWalker->lpNextClient;
  lpWalker->lpNextClient=lpClient;  // Add new tail segment
  lpClient->lpPrevClient=lpWalker;  // Tell tail who's its parent
 }

 //DEBUGIT("smtpAddClient() End");
 return(lpClient);
} /* smtpAddClient */


/* LPSMTPCLIENT smtpSocketToClient(SOCKET sSocket) */
LPSMTPCLIENT smtpSocketToClient(SOCKET sSocket)
{
 LPSMTPCLIENT lpClient;

 if((!lpClient)||(sSocket==SOCKET_ERROR)) return(NULL);
 lpClient=smtpClientsHead;

 while(lpClient!=NULL)
 {
  if(lpClient->sSocket==sSocket) return(lpClient);
  lpClient=lpClient->lpNextClient;
 }
 return(lpClient);
} /* smtpSocketToClient() */


/* BOOL smtpDestroyClient(LPSMTPCLIENT);
    Purpose: To release a SMTPCLIENT structure.
    Given:   Linked list element pointer
    Returns: TRUE if error, FALSE if all OK
    Reusability notes:
*/
BOOL smtpDestroyClient(LPSMTPCLIENT lpClient)
{
 BOOL bLinger = FALSE;
 LPSMTPCLIENT lpNext, lpPrev;

 //DEBUGIT("smtpDestroyClient() Begin");
 if(!lpClient) return(TRUE);

    // This code was removed as "dead" code. The setsockopt() just sets
    // the SO_LINGER option to what it already should be by default
    // (thanks Bob (rcq@ftp.com))
// setsockopt(lpClient->sSocket,
//            SOL_SOCKET, SO_LINGER, (char FAR *)&bLinger,
//            sizeof(BOOL));
    // This shutdown isn't needed either...
// shutdown(lpClient->sSocket, 2);
 closesocket(lpClient->sSocket);

 smtpLog(LOG_HIGH | LOG_TIME, LOG_DISCONNECT_S, (LPSTR)lpClient->szPeer);

    // Hack Alert! This is a hack, this is only a hack. If this were
    // real winsock compliant code, it shouldn't have to do this!
    // But it doesn't hurt, and Lanera seems to need it?
    // Since this is non-standard, it is now no longer needed - but I
    // doubt if it will work on Lanera's stack anymore..
 //if(lpClient!=smtpClientsHead)
 //{
 // WSAAsyncSelect(smtpClientsHead->sSocket, hWndMain, NET_ACTIVITY,
 //                FD_ACCEPT | FD_READ | FD_WRITE | FD_CLOSE);
 //}

    // Is the DATA temporary file still open?
 if(lpClient->hfFile!=HFILE_ERROR)
 {
  _lclose(lpClient->hfFile);
    // Take it as valid - but don't send any messages
  smtpAppendToFile(NULL, szLocalMailPath,
                   lpClient->szFile);
 }

    // Patch out THIS segment.
 lpNext=lpClient->lpNextClient;
 lpPrev=lpClient->lpPrevClient;
 if(lpPrev) lpPrev->lpNextClient=lpNext;

    // Free all of the memory
 GlobalUnlock(lpClient->hInputBuffer);
 GlobalFree(lpClient->hInputBuffer);
 GlobalUnlock(lpClient->hOutputBuffer);
 GlobalFree(lpClient->hOutputBuffer);
 GlobalUnlock(lpClient->hClient);
 GlobalFree(lpClient->hClient);

 //DEBUGIT("smtpDestroyClient() End");
 return(FALSE);
} /* smtpDestroyClient */


/* void smtpSendMessage(LPSMTPCLIENT, int, int, ...);
    Purpose: To send a resource/reply message.
    Given:   index into smtpClientsTable
             SMTP reply number (211, 503)
             Resource ID of the reply string.
             ... arguments
    Returns: Nothing.
    Other:   If the Resource ID String has a '\n' in it,
             this routine will send it line at a time.
*/
void smtpSendMessage(LPSMTPCLIENT lpClient, int SMTPCode, int ResourceID, ...)
{
 int iCount;
 char szString[MAXSNDBUFF];
 char szLine[MAXSNDBUFF];
 LPSTR lpArg1, lpArg2, lpArg3;
 PSTR pStart, pStop;
 va_list vaArgs;

 //DEBUGIT("smtpSendMessage() Begin");
 if(!lpClient) return;

 va_start( vaArgs, ResourceID);

 if(!LoadString(hInst, ResourceID, (LPSTR)szLine, sizeof(szLine)))
    return;

 lpArg1=va_arg(vaArgs, LPSTR);
 lpArg2=va_arg(vaArgs, LPSTR);
 lpArg3=va_arg(vaArgs, LPSTR);

 wsprintf((LPSTR)szString, (LPSTR)szLine, lpArg1, lpArg2, lpArg3);

 pStart=pStop=szString;
 do     /* Line loop for embedded '\n's */
 {
  while((*pStop!='\0')&&(*pStop!='\n')) pStop++;
  if((*pStop!='\0'))
  {
   *(pStop++)='\0';
   wsprintf((LPSTR)szLine, "%d-%s", SMTPCode, (LPSTR)pStart);
   pStart=pStop;
  }
  else
  {
   wsprintf((LPSTR)szLine, "%d %s", SMTPCode, (LPSTR)pStart);
  }
  clientSendLine(lpClient, (LPSTR)szLine, lstrlen((LPSTR)szLine));
  //DEBUGIT((LPSTR)szLine);
  smtpLog(LOG_HIGH, LOG_SENT_S, (LPCSTR)szLine);
 } while(*pStop!='\0');
 //DEBUGIT("smtpSendMessage() End");
} /* smtpSendMessage */


/* int clientSendLine(LPSMTPCLIENT, LPSTR, int);
    Purpose: Send 1 line to the caller if it exists.
    Given:   SMTPCLIENT index, Line pointer,
             Line size. (for binary "string" sends if needed)
    Returns: 0 if everything isn't ok.
             >0 tells the caller how many characters it sent.
    Reusability requirements:
             Globals: SMTPCLIENT smtpClientsTable[]
             Defines: MAXCLIENTS, MAXBUFF
    Other:   THIS ROUTINE ADDS A CR/LF PAIR!
*/
int clientSendLine(LPSMTPCLIENT lpClient, LPSTR lpLine, int iLine)
{
 LPSTR lpBuffer;
 int fifoStart;
 int fifoStop;
 int fifoWalker;
 int iDest;

    /* Catch illegal arguements */
 if((!lpClient)||(lpLine==NULL)||(iLine==0))
     return(0);

    /* Make things easier to deal with */
 lpBuffer=lpClient->lpOutputBuffer;
 fifoStart=lpClient->fifoOutputStart;
 fifoWalker=fifoStart;
 fifoStop=lpClient->fifoOutputStop;

    /* Make sure there is enough room - Egad */
 if(( (fifoStop<fifoStart) &&
      (((fifoStop+iLine+2)&MAXSNDBUFF)>=fifoStart)
    )            ||
    ( (fifoStop>=fifoStart) &&
      (((fifoStop+iLine+2)/MAXSNDBUFF)==1) &&
      (((fifoStop+iLine+2)%MAXSNDBUFF)>=fifoStart)
    ))
    return(0);

    /* There is room at the end! Copy it in! */
 iDest=0;
 fifoWalker=fifoStop;
 while(iDest<iLine)
 {
  lpBuffer[fifoWalker]=lpLine[iDest];
  fifoWalker=(fifoWalker+1)%MAXSNDBUFF;
  iDest++;
 }
 lpBuffer[fifoWalker]='\r';      // Append CR on output
 fifoWalker=(fifoWalker+1)%MAXSNDBUFF;
 lpBuffer[fifoWalker]='\n';      // Append LF on output
 fifoWalker=(fifoWalker+1)%MAXSNDBUFF;

 lpClient->fifoOutputStop=fifoWalker;
    // This is important
 netSendData(lpClient);
 return(iDest);                  // Tell em how many we copied!
} /* clientSendLine */


/* int clientReceiveLine(LPSMTPCLIENT, LPSTR, int);
    Purpose: Receive 1 line for the caller if it exists.
    Given:   SMTPCLIENT pointer, Line buffer pointer,
             Line buffer size.
    Returns: -1 if no string found in buffer
             0 if just CR or NL
             >0 tells the caller how many characters it was.
    Reusability requirements:
             None!
    Notes:   This routines takes into account backspaces,
             deletes, multiple CRs and LFs.
*/
int clientReceiveLine(LPSMTPCLIENT lpClient, LPSTR lpLine, int iLine)
{
 LPSTR lpBuffer;
 int iCount;
 int fifoStart;
 int fifoStop;
 int fifoWalker;
 int iDest;
 int iMax;

 if((!lpClient)||(lpLine==NULL)||(iLine==0))
     return(-1);

     /* Make things easier to deal with */
 lpBuffer=lpClient->lpInputBuffer;
 fifoStart=lpClient->fifoInputStart;
 fifoWalker=fifoStart;
 fifoStop=lpClient->fifoInputStop;
 iMax=lpClient->iInputSize;

 if(fifoStart==fifoStop) return(-1);

    /* Search buffer for a LF or CR BEFORE the end of the buffer! */
 while((lpBuffer[fifoWalker]!='\n')&&
       (lpBuffer[fifoWalker]!='\r'))
 {
  fifoWalker=(fifoWalker+1)%iMax;
  if(fifoWalker==fifoStop)
    return(-1);
 }

    /* We found the end of a string! Copy it out. */
 *lpLine='\0';

 iDest=0;
 fifoWalker=fifoStart;
 while((lpBuffer[fifoWalker]!='\n')&&
       (lpBuffer[fifoWalker]!='\r'))
 {
  if((lpBuffer[fifoWalker]=='\b')||     // Backspace (^H)
     (lpBuffer[fifoWalker]=='\177'))    // Delete    (^?)
  { // Interactive User is talking to us (and making mistakes)
   if((--iDest)<0) iDest=0;             // Backup if we can
   fifoWalker=(fifoWalker+1)%iMax;      // Step over ^H or ^?
   continue;                            // for ^H^H... case
  }
  lpLine[iDest]=lpBuffer[fifoWalker];
  fifoWalker=(fifoWalker+1)%iMax;
  iDest++;
  if(iDest>=iLine)
     return(-1);  // Not enough string space!!!
 }
 lpLine[iDest]='\0';

                               // Step past the '\r'
 if((fifoWalker!=fifoStop)&&
    (lpBuffer[fifoWalker]=='\r')) fifoWalker=(fifoWalker+1)%iMax;

                               // Step past the '\n'
 if((fifoWalker!=fifoStop)&&
    (lpBuffer[fifoWalker]=='\n')) fifoWalker=(fifoWalker+1)%iMax;

 lpClient->fifoInputStart=fifoWalker;
 smtpLog(LOG_HIGH, LOG_RECEIVED_S, (LPCSTR)lpLine);
 return(iDest);              // Tell em how many we copied!
} /* clientReceiveLine */


/* int clientReceiveBlock(int, LPSTR, int);
    Purpose: Receive entire FIFO block for the caller if it exists.
    Given:   SMTPCLIENT index, Block buffer pointer,
             Block buffer size.
    Returns: 0 if everything isn't ok. (no characters waiting)
             >0 tells the caller how many characters it was.
    Reusability requirements:
             Globals: SMTPCLIENT smtpClientsTable[]
             Defines: MAXCLIENTS, MAXBUFF
    Notes:   This kind of defeats the idea of FIFO, but it does
             allow for 8 bit binary transfers.
             Since it was easier to implement 8BITMIME termination
             in here, I have left it so. This is not good design.
*/
int clientReceiveBlock(LPSMTPCLIENT lpClient, LPSTR lpLine, int iLine)
{
 LPSTR lpBuffer;
 int fifoStart;
 int fifoStop;
 int fifoWalker;
 int iDest;
 int iMax;

 if((!lpClient)||(lpLine==NULL)||(iLine==0))
     return(0);

    // Make things easier to deal with
 lpBuffer=lpClient->lpInputBuffer;
 fifoStart=lpClient->fifoInputStart;
 fifoStop=lpClient->fifoInputStop;
 iMax=lpClient->iInputSize;

 if(fifoStart==fifoStop) return(0);

 iDest=0;
 fifoWalker=fifoStart;
 while(fifoWalker!=fifoStop)
 {
  if(lpBuffer[fifoWalker]=='.')
  {
    // Check for presence of, and validity of "\r\n.\r\n"
    // Elegant, yet ugly
    // Possible bug: What if the above sequence comes split?
   if(((((fifoWalker-2)%iMax) != fifoStart) &&
       (lpBuffer[(fifoWalker-2)%iMax]=='\r'))
    &&((((fifoWalker-1)%iMax) != fifoStart) &&
       (lpBuffer[(fifoWalker-1)%iMax]=='\n'))
    &&((((fifoWalker+1)%iMax) != fifoStop) &&
       (lpBuffer[(fifoWalker+1)%iMax]=='\r'))
    &&((((fifoWalker+2)%iMax) != fifoStop) &&
       (lpBuffer[(fifoWalker+2)%iMax]=='\n'))
     )
   {    // End of message
     if(lpClient->hfFile!=HFILE_ERROR)
     {
             _lclose(lpClient->hfFile);
     }
     lpClient->hfFile=HFILE_ERROR;
     smtpAppendToFile(lpClient, szLocalMailPath,
                      lpClient->szFile);
     lpClient->iState=STATE_WAIT_FOR_COMMAND;
     smtpSendMessage(lpClient, 250, MSG_OK);
     fifoWalker=(fifoWalker+2)%iMax;
     break;
   }
  } // Pass EVERYTHING else through.
  lpLine[iDest]=lpBuffer[fifoWalker];
  fifoWalker=(fifoWalker+1)%iMax;
  iDest++;
 }
 lpLine[iDest]='\0';    // Zero terminate it, for luck

 lpClient->fifoInputStart=fifoWalker;

 smtpLog(LOG_HIGH, LOG_RECEIVED_S, (LPCSTR)lpLine);
 return(iDest);              // Tell em how many we copied!
} /* clientReceiveBlock */

/**** End of WSMTPSRV.C ****/
