
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 1992-1996 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3.c
//
//  Contents:   This file contains the code to handle each pop command
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

char *  PopCommands[] = {   POP3_HELO,
                            POP3_USER,
                            POP3_PASS,
                            POP3_QUIT,
                            POP3_STAT,
                            POP3_LIST,
                            POP3_RETR,
                            POP3_DELE,
                            POP3_LAST,
                            POP3_RSET };

Pop3DispatchFn  Pop3DispatchTable[] = {
                            HeloDispatch,
                            UserDispatch,
                            PassDispatch,
                            QuitDispatch,
                            StatDispatch,
                            ListDispatch,
                            RetrDispatch,
                            DeleDispatch,
                            LastDispatch,
                            RsetDispatch };

#define POP3_STAT_RESPONSE  POP3_GOOD_RESPONSE " %d %d\r\n"
#define POP3_GOOD_RESPLINE  POP3_GOOD_RESPONSE "\r\n"
#define POP3_LIST_RESPLINE  "%d %d\r\n"
#define POP3_TERMINATOR     ".\r\n"
#define POP3_RETR_RESPONSE  POP3_GOOD_RESPONSE " %d octets\r\n"
#define POP3_LAST_RESPONSE  POP3_GOOD_RESPONSE " %d\r\n"

#define BANNER_STRING   POP3_GOOD_RESPONSE " POP3 Example Server Ready\r\n"
#define ERROR_INVALID_STATE     POP3_BAD_RESPONSE " Command not valid in this state\r\n"
#define ERROR_INVALID_USERNAME  POP3_BAD_RESPONSE " Username not valid\r\n"
#define ERROR_AUTH_FAILED       POP3_BAD_RESPONSE " Unable to log on\r\n"
#define ERROR_LOCK_FAILED_S     POP3_BAD_RESPONSE " Unable to lock directory\r\n"
#define ERROR_UNKNOWN_MESSAGE   POP3_BAD_RESPONSE " Unknown message\r\n"
#define ERROR_READ_FAILED_S     POP3_BAD_RESPONSE " Unable to read directory\r\n"
#define ERROR_SERVER_FAILURE    POP3_BAD_RESPONSE " General failure on server\r\n"


//+---------------------------------------------------------------------------
//
//  Function:   Pop3CrackCommand
//
//  Synopsis:   Takes an input buffer as received from a client, and cracks
//              it to determine the command to execute
//
//  Arguments:  [Request]    -- Input buffer
//              [RequestLen] -- Input buffer length
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
int
Pop3CrackCommand(
    PUCHAR      Request,
    DWORD       RequestLen)
{
    int i;

    if (RequestLen < 4)
    {
        return(-1);
    }

    for (i = 0; i < MAX_POP3_COMMAND ; i++ )
    {
        if (_strnicmp((PCHAR) Request, PopCommands[i], 4) == 0 )
        {
            return(i);
        }
    }

    return(-1);
}

//+---------------------------------------------------------------------------
//
//  Function:   CreatePop3Context
//
//  Synopsis:   Creates a client context to keep associated with the socket
//
//  Arguments:  (none)
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PVOID
CreatePop3Context(void)
{
    PPopContext pContext;

    pContext = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(PopContext));
    if (pContext)
    {
        pContext->State = PopAuthorization;
        ReportServiceEvent(
            EVENTLOG_INFORMATION_TYPE,
            POP3EVENT_USER_CONNECT,
            0, NULL, 0);
    }
    return(pContext);
}

//+---------------------------------------------------------------------------
//
//  Function:   DeletePop3Context
//
//  Synopsis:   Gets rid of the context when the socket closes
//
//  Arguments:  [pvContext] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
DeletePop3Context(PVOID pvContext)
{
    PPopContext pContext;

    pContext = (PPopContext) pvContext;

    if (pContext->pDirectory)
    {
        FreeMailDirectory(pContext->pDirectory);
        UnlockMailDirectory(pContext->hDirectoryLock);
        CloseHandle(pContext->hUserToken);
    }

    LocalFree(pContext);

}


//+---------------------------------------------------------------------------
//
//  Function:   AllocAndCopyString
//
//  Synopsis:   Copies a string into local alloc memory, so that socket code
//              can send it off, then free it.
//
//  Arguments:  [pszString] -- string to copy
//              [pcbString] -- returned length of string
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PUCHAR
AllocAndCopyString(
    PCHAR   pszString,
    PDWORD  pcbString)
{
    PCHAR   pszNewString;

    *pcbString = strlen(pszString);
    pszNewString = LocalAlloc(LMEM_FIXED, *pcbString + 1);
    if (pszNewString)
    {
        strcpy(pszNewString, pszString);
    }
    return((PUCHAR) pszNewString);
}

//+---------------------------------------------------------------------------
//
//  Function:   HeloDispatch
//
//  Synopsis:   Handles the HELO command, which, perversely, never gets sent
//              by most clients.
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
HeloDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DebugLog((DEB_TRACE_PROT, "Received HELO\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if ((pContext->State == PopTransact) ||
        (pContext->State == PopUpdate) )
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // Put us into Authorization state
    //
    pContext->State = PopAuthorization;

    //
    // No file, but copy the banner string and return it.
    //
    *SendHandle = NULL;
    *OutputBuffer = AllocAndCopyString(BANNER_STRING, OutputBufferLen);

    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   UserDispatch
//
//  Synopsis:   Handles the USER command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
UserDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    char * Src;
    char * Dst;

    DebugLog((DEB_TRACE_PROT, "Received USER\n"));


    //
    // Verify context, and that the context says we can receive this command
    //
    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopAuthorization)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    if (InputBufferLen - POP3_COMMAND_LENGTH > USERNAME_LENGTH)
    {
        *OutputBuffer = ERROR_INVALID_USERNAME;
        *OutputBufferLen = sizeof(ERROR_INVALID_USERNAME);
        return(Pop3_SendError);
    }

    //
    // Copy in the user name without trailing \r or \n.
    //

    Src = InputBuffer + POP3_COMMAND_LENGTH;
    Dst = pContext->UserName;
    while (*Src && (*Src != '\r') && (*Src != '\n'))
    {
        *Dst++ = *Src++;
    }
    *Dst = '\0';

    //
    // Set the state so that we are expecting the password
    //
    pContext->State = PopAuthorization2;

    *SendHandle = NULL;
    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);

    return(Pop3_SendBuffer);

}


//+---------------------------------------------------------------------------
//
//  Function:   PassDispatch
//
//  Synopsis:   Handles the PASS command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
PassDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    CHAR    pass[MAX_PATH];
    char *  Dst;
    char *  Src;


    DebugLog((DEB_TRACE_PROT, "Received PASS\n"));


    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopAuthorization2)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // Copy in the password without trailing \r or \n.
    //

    Src = InputBuffer + POP3_COMMAND_LENGTH;
    Dst = pass;
    while (*Src && (*Src != '\r') && (*Src != '\n'))
    {
        *Dst++ = *Src++;
    }
    *Dst = '\0';


#ifdef SECURE_BUILD

    //
    // Attempt to log the user on, meaning verify the name and password
    // provided, and get a token back.  The "." we're passing in means
    // use the domain if this machine is part of a domain, or use the local
    // accounts domain.
    //
    if (!LogonUserA(    pContext->UserName,
                        ".",
                        pass,
                        LOGON32_LOGON_INTERACTIVE,
                        LOGON32_PROVIDER_DEFAULT,
                        &pContext->hUserToken) )
    {
        pContext->State = PopAuthorization;
        ReportServiceEvent(
            EVENTLOG_WARNING_TYPE,
            POP3EVENT_LOGON_FAILURE,
            0, NULL,
            0);

        *OutputBuffer = ERROR_AUTH_FAILED;
        *OutputBufferLen = sizeof(ERROR_AUTH_FAILED);
        return(Pop3_SendError);
    }

#endif

    //
    // Now, open and read the mail drop directory
    //

    pContext->hDirectoryLock = LockMailDirectory(pContext->hUserToken,
                                                 pContext->UserName);

    if (!pContext->hDirectoryLock)
    {
        //
        // Could not lock directory.  Basically, we shut down this connection,
        // and let it go.
        //

        DebugLog((DEB_ERROR, "Unable to lock directory, %d\n", GetLastError()));

        pContext->State = PopAuthorization;

        //
        // Close the token.  This logs the user off.
        //
        CloseHandle(pContext->hUserToken);
        pContext->hUserToken = NULL;

        *OutputBuffer = ERROR_LOCK_FAILED_S;
        *OutputBufferLen = sizeof(ERROR_LOCK_FAILED_S);
        return(Pop3_SendError);
    }

    pContext->pDirectory = ReadMailDirectory(pContext->hUserToken,
                                             pContext->UserName);

    if (!pContext->pDirectory)
    {
        //
        // Couldn't read the directory.  So, we panic and
        // throw it all away.
        //

        DebugLog((DEB_ERROR, "Unable to read directory!\n"));

        pContext->State = PopAuthorization;
        UnlockMailDirectory(pContext->hDirectoryLock);
        pContext->hDirectoryLock = NULL;
        CloseHandle(pContext->hUserToken);

        *OutputBuffer = ERROR_READ_FAILED_S;
        *OutputBufferLen = sizeof(ERROR_LOCK_FAILED_S);
        return(Pop3_SendError);
    }

    pContext->State = PopTransact;
    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   QuitDispatch
//
//  Synopsis:   Handles the QUIT command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
QuitDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{

    DebugLog((DEB_TRACE_PROT, "Received QUIT\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State == PopTransact)
    {
        pContext->State = PopUpdate;

        CommitMailDirectory(pContext->pDirectory, pContext->hUserToken);
        FreeMailDirectory(pContext->pDirectory);
        UnlockMailDirectory(pContext->hDirectoryLock);
        CloseHandle(pContext->hUserToken);
    }

    pContext->State = PopShutdown;

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);

    return(Pop3_SendBuffer);

}


//+---------------------------------------------------------------------------
//
//  Function:   StatDispatch
//
//  Synopsis:   Handles the STAT command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
StatDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{

    char    StatResp[80];

    DebugLog((DEB_TRACE_PROT, "Received STAT\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // Print the response into the buffer, then copy it and return it.
    //

    sprintf(StatResp, POP3_STAT_RESPONSE, pContext->pDirectory->cAvailMessages,
                        pContext->pDirectory->AvailSize);

    *OutputBuffer = AllocAndCopyString(StatResp, OutputBufferLen);
    *SendHandle = NULL;

    DebugLog((DEB_TRACE_PROT, "STAT: Sending %s\n", StatResp));

    return(Pop3_SendBuffer);

}


//+---------------------------------------------------------------------------
//
//  Function:   ListDispatch
//
//  Synopsis:   Handles the LIST command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
ListDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    PCHAR       pszBuffer;
    PCHAR       pszWalk;
    DWORD       TotalAlloc;
    DWORD       UsedAlloc;
    DWORD       CharCount;
    DWORD       SingleMessage;
    DWORD       i;
    char        SingleLine[80];


    DebugLog((DEB_TRACE_PROT, "Received LIST\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // If there is a parameter, process it so we can find out what specific
    // message they want
    //

    if (InputBufferLen > POP3_COMMAND_LENGTH)
    {
        SingleMessage = (DWORD) -1;
        sscanf(&InputBuffer[POP3_COMMAND_LENGTH], " %d", &SingleMessage);

        //
        // Pop is 1-based, we're 0-based
        //
        SingleMessage--;

        if ((SingleMessage > pContext->pDirectory->cMessages) ||
            (pContext->pDirectory->Messages[SingleMessage].Flags & POP3_MESSAGE_DELETE))
        {
            *OutputBuffer = ERROR_UNKNOWN_MESSAGE;
            *OutputBufferLen = sizeof(ERROR_UNKNOWN_MESSAGE);
            return(Pop3_SendError);
        }

        sprintf(SingleLine, POP3_STAT_RESPONSE, SingleMessage + 1,
                    pContext->pDirectory->Messages[SingleMessage].Size);

        *OutputBuffer = AllocAndCopyString(SingleLine, OutputBufferLen);
        DebugLog((DEB_TRACE_PROT, "LIST: Sending %s", SingleLine));
        return(Pop3_SendBuffer);
    }

    //
    // If they want the whole list, send the whole list
    //
    TotalAlloc = pContext->pDirectory->cAvailMessages * 16 + 24;
    pszBuffer = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, TotalAlloc);
    if (!pszBuffer)
    {
        *OutputBuffer = ERROR_SERVER_FAILURE;
        *OutputBufferLen = sizeof(ERROR_SERVER_FAILURE);
        return(Pop3_SendError);
        
    }
    pszWalk = pszBuffer;
    UsedAlloc = 0;

    //
    // Copy the header
    //
    CharCount = sprintf(pszWalk, POP3_STAT_RESPONSE,
                        pContext->pDirectory->cAvailMessages,
                        pContext->pDirectory->AvailSize);

    pszWalk += CharCount;
    UsedAlloc += CharCount;
    for (i = 0; i < pContext->pDirectory->cMessages ; i++ )
    {
        //
        // For every message not marked deleted, copy it in
        //
        if ((pContext->pDirectory->Messages[i].Flags & POP3_MESSAGE_DELETE) == 0)
        {
            CharCount = sprintf(pszWalk, POP3_LIST_RESPLINE,
                                i+1, pContext->pDirectory->Messages[i].Size);
            pszWalk += CharCount;
            UsedAlloc += CharCount;
        }
    }

    strcpy(pszWalk, POP3_TERMINATOR);
    UsedAlloc += 3;

    *OutputBuffer = pszBuffer;
    *OutputBufferLen = UsedAlloc;

    DebugLog((DEB_TRACE_PROT, "LIST: Sending %s\n", pszBuffer));

    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   RetrDispatch
//
//  Synopsis:   Handles the RETR command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
RetrDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DWORD   MessageId;
    char    Line[40];


    DebugLog((DEB_TRACE_PROT, "Received RETR\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    MessageId = (DWORD) -1;

    if (InputBufferLen > POP3_COMMAND_LENGTH)
    {
        sscanf(&InputBuffer[POP3_COMMAND_LENGTH], "%d", &MessageId);
    }

    //
    // Pop is 1-based, we're 0-based
    //
    MessageId--;

    if ((MessageId > pContext->pDirectory->cMessages) ||
        (pContext->pDirectory->Messages[MessageId].Flags & POP3_MESSAGE_DELETE))
    {
        *OutputBuffer = ERROR_UNKNOWN_MESSAGE;
        *OutputBufferLen = sizeof(ERROR_UNKNOWN_MESSAGE);
        return(Pop3_SendError);
    }


    //
    // Open the mail message.  We pass in the user's token, so that
    // we can impersonate when we do the open.
    //

    *SendHandle = OpenMailMessage(  MessageId,
                                    pContext->pDirectory,
                                    pContext->hUserToken);

    if (*SendHandle)
    {
        sprintf(Line, POP3_RETR_RESPONSE,
                pContext->pDirectory->Messages[MessageId].Size);
        *OutputBuffer = AllocAndCopyString(Line, OutputBufferLen);
        if (pContext->pDirectory->NextMessage < MessageId)
        {
            pContext->pDirectory->NextMessage = MessageId;
        }

        return(Pop3_SendBufferThenFile);

    }

    *OutputBuffer = ERROR_SERVER_FAILURE;
    *OutputBufferLen = sizeof(ERROR_SERVER_FAILURE);
    return(Pop3_SendError);
}


//+---------------------------------------------------------------------------
//
//  Function:   DeleDispatch
//
//  Synopsis:   Handles the DELE command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
DeleDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DWORD   MessageId;


    DebugLog((DEB_TRACE_PROT, "Received DELE\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    MessageId = (DWORD) -1;

    if (InputBufferLen > POP3_COMMAND_LENGTH)
    {
        sscanf(&InputBuffer[POP3_COMMAND_LENGTH], "%d", &MessageId);
    }

    //
    // Pop is 1-based, we're 0-based
    //
    MessageId--;

    if (MessageId > pContext->pDirectory->cMessages ||
        (pContext->pDirectory->Messages[MessageId].Flags & POP3_MESSAGE_DELETE))
    {
        *OutputBuffer = ERROR_UNKNOWN_MESSAGE;
        *OutputBufferLen = sizeof(ERROR_UNKNOWN_MESSAGE);
        return(Pop3_SendError);
    }

    //
    // Update directory with "deleted" file
    //
    pContext->pDirectory->Messages[MessageId].Flags |= POP3_MESSAGE_DELETE;
    pContext->pDirectory->cAvailMessages--;
    pContext->pDirectory->AvailSize -= pContext->pDirectory->Messages[MessageId].Size;

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);
}

//+---------------------------------------------------------------------------
//
//  Function:   NoopDispatch
//
//  Synopsis:   Handles the NOOP command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
NoopDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{

    DebugLog((DEB_TRACE_PROT, "Received NOOP\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);
}


//+---------------------------------------------------------------------------
//
//  Function:   LastDispatch
//
//  Synopsis:   Handles the LAST command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
LastDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    char    Line[40];

    DebugLog((DEB_TRACE_PROT, "Received LAST\n"));

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    sprintf(Line, POP3_LAST_RESPONSE, pContext->pDirectory->NextMessage+1);
    *OutputBuffer = AllocAndCopyString(Line, OutputBufferLen);

    return(Pop3_SendBuffer);
}


//+---------------------------------------------------------------------------
//
//  Function:   RsetDispatch
//
//  Synopsis:   Handles the RSET command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
RsetDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DWORD   i;

    DebugLog((DEB_TRACE_PROT, "Received LAST\n"));

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_INVALID_STATE);
        return(Pop3_SendError);
    }

    for (i = 0; i < pContext->pDirectory->cMessages ; i++ )
    {
        pContext->pDirectory->Messages[i].Flags = 0;
    }

    pContext->pDirectory->NextMessage = 0;
    pContext->pDirectory->cAvailMessages = pContext->pDirectory->cMessages;
    pContext->pDirectory->AvailSize = pContext->pDirectory->TotalSize;

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);
}



//+---------------------------------------------------------------------------
//
//  Function:   Pop3Dispatch
//
//  Synopsis:   Dispatch the command
//
//  Arguments:  [pContext]        --    Context for client connection
//              [InputBuffer]     --    Input Buffer (stuff from client)
//              [InputBufferLen]  --    How much stuff
//              [SendHandle]      --    Handle to Transmit, if so required
//              [OutputBuffer]    --    (returned) pointer of stuff to send
//              [OutputBufferLen] --    (returned) size of stuff to send
//
//  Returns:    a Pop3Disposition, which indicates what the socket code
//              should do with this stuff.  See pop3ctx.h for the dispositions
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
Pop3Dispatch(
    PVOID       pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    )

{
    PPopContext pPopContext;
    int     Command;

    pPopContext = (PPopContext) pContext;

    //
    // Null terminate, so string functions won't get confused later.
    //

    InputBuffer[InputBufferLen] = L'\0';

    //
    // Figure the command out
    //

    Command = Pop3CrackCommand(InputBuffer, InputBufferLen);

    //
    // If it didn't parse, throw it away
    //
    if (Command == -1)
    {
        return(Pop3_Discard);
    }

    //
    // Let the command handlers do their thing
    //
    return(Pop3DispatchTable[Command](  pPopContext,
                                        InputBuffer,
                                        InputBufferLen,
                                        SendHandle,
                                        OutputBuffer,
                                        OutputBufferLen) );


}

#if DBG

//
// Interactive debug testing
//
void TestLoop(void)
{
    char    InputBuffer[256];
    DWORD   InputBufferLen;
    char *  OutputBuffer;
    DWORD   OutputBufferLen;
    HANDLE  hTest;
    PVOID   pContext;
    Pop3Disposition Disposition;
    extern WCHAR BaseDirectory[];

    printf("-------------------\n");
    printf("Interactive Test Mode\n");

    pContext = CreatePop3Context();
    do
    {
        gets(InputBuffer);
        InputBufferLen = strlen(InputBuffer);
        if (InputBufferLen)
        {
            Disposition = Pop3Dispatch( pContext,
                                        InputBuffer,
                                        InputBufferLen,
                                        &hTest,
                                        &OutputBuffer,
                                        &OutputBufferLen);

            switch (Disposition)
            {
                case Pop3_Discard:
                    printf("[Discard message]\n");
                    break;

                case Pop3_SendError:
                    printf("%s", OutputBuffer);
                    break;

                case Pop3_SendBuffer:
                    printf("%s", OutputBuffer);
                    LocalFree(OutputBuffer);
                    break;

                case Pop3_SendFile:
                    printf("Transmit file %x\n", hTest);
                    printf(".\r\n");
                    CloseHandle(hTest);
                    break;

                case Pop3_SendBufferThenFile:
                    printf("%s", OutputBuffer);
                    printf("Transmit file %x\n", hTest);
                    printf(".\r\n");
                    CloseHandle(hTest);
                    LocalFree(OutputBuffer);
                    break;

                case Pop3_SendFileThenBuffer:
                    printf("Transmit file %x\n", hTest);
                    printf("%s", OutputBuffer);
                    CloseHandle(hTest);
                    LocalFree(OutputBuffer);
                    break;

            }

        }

    } while (InputBufferLen);

}
#endif
