/* 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)) %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(iRcvSizesSocket=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>=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(iDestfifoOutputStop=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 ****/