/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    isapiext.hxx

Abstract:

    SMTP ISAPI layer header file. These definitions should only be 
    available to the ISAPI layer and the SMTP service.

Author:

    keithlau        June 28, 1996    Created file

Revision History:

--*/

#ifndef _ISAPIEXT_H_
#define _ISAPIEXT_H_


// ====================================================================
// Include files
//
#include "smtpext.h"


// ====================================================================
// Defined values
//

#define SSE_ISAPI_SIGNATURE					0xa5a5
#define SSE_EXT_SIGNATURE					0x5a5a
#define SSE_CONTEXT_SIGNATURE				0xaa55


// ====================================================================
// Return codes
//

#define SSE_STATUS_INTERNAL_ERROR			0x1000
#define SSE_STATUS_EXCEPTION				0x1001


// ====================================================================
// Type definitions
//

// Enumerated type for different types of extensions in the future
typedef enum _EXTENSION_TYPE
{
	DELIVERY_EXTENSION = 0,		// Delivery extension
	MESSAGE_EXTENSION,			// Message extension
								// Add new types here ...
	INVALID_EXTENSION_TYPE		// THIS MUST ALWAYS BE THE LAST TYPE

} EXTENSION_TYPE;

// Since we use a DWORD to store extension attributes, we have
// a realistic upper bound for binding points
#define MAX_POSSIBLE_EXTENSION_TYPE			32


// The SMTP server fills in one of these before it calls 
// CallDeliveryExtension()
typedef struct _DELIVERY_EXTENSION_BLOCK
{
	HANDLE	hImpersonation;			// Token for user impersonation

	LPSTR	lpszSender;				// Address of the message sender	
	LPSTR	lpszRecipient;			// Recipient currently being processed
	LPSTR	lpszMailboxPath;		// Recipient's mailbox path
	LPSTR	lpszMessageFileName;	// Message file path
	LPBYTE	lpbMapping;				// Memory-mapped buffer of message
	DWORD	dwSize;					// Size of memory map
	CHAR	szErrorTranscript[SSE_MAX_EXT_DLL_NAME_LEN];
									// Buffer for returning error transcript

} DELIVERY_EXTENSION_BLOCK, *LPDELIVERY_EXTENSION_BLOCK;

// The SMTP server fills in one of these before it calls 
// CallMessageExtension()
typedef struct _MESSAGE_EXTENSION_BLOCK
{
	LPSTR	lpszSender;				// Address of the message sender	
	LPSTR	lpszRecipientList;		// Recipient list
	LPSTR	lpszMessageFileName;	// Message file path
	LPBYTE	lpbMapping;				// Memory-mapped buffer of message
	DWORD	dwSize;					// Size of memory map
	CHAR	szErrorTranscript[SSE_MAX_EXT_DLL_NAME_LEN];
									// Buffer for returning error transcript

} MESSAGE_EXTENSION_BLOCK, *LPMESSAGE_EXTENSION_BLOCK;


// ====================================================================
// External declaration for the one and only SMTP ISAPI object instance
//

class SMTP_ISAPI;

// ====================================================================
// Entry point exposed to the SMTP service
//

// Entry point to initialize the ISAPI layer
extern "C" SMTP_ISAPI *InitializeExtensions( DWORD dwInstanceId, 
											 LPSTR *aszMultiSzExtensionList );

// Entry point to shut down the ISAPI layer
extern "C" VOID TerminateExtensions( SMTP_ISAPI *pSdkInstance );

// Entry point for the SMTP service to call into server extensions
extern "C" DWORD CallDeliveryExtension( SMTP_ISAPI *pSdkInstance,
										LPDELIVERY_EXTENSION_BLOCK lpDeb );

// Entry point for the SMTP service to call into server extensions
extern "C" DWORD CallMessageExtension( SMTP_ISAPI *pSdkInstance,
										LPMESSAGE_EXTENSION_BLOCK lpMeb );

// Function to query if extensions are initialized
extern "C" BOOL ExtensionLayerIsInitialized( SMTP_ISAPI *pSdkInstance );

// Function to query if extensions are loaded for a particular binding point
extern "C" BOOL ExtensionsAreLoaded( SMTP_ISAPI *pSdkInstance,
										EXTENSION_TYPE ExtensionType );


// ====================================================================
// Define types for entry points
//

typedef BOOL  (WINAPI * PFN_GETEXTENSIONVERSION)( SSE_VERSION_INFO *pVer );
typedef DWORD (WINAPI * PFN_EXTENSIONPROC)( SSE_EXTENSION_CONTROL_BLOCK *pServerContext );


// ====================================================================
// Top-level SMTP ISAPI object
//

class SMTP_EXT;
class SMTP_SERVER_CONTEXT;

class SMTP_ISAPI
{
public:

    SMTP_ISAPI();
    ~SMTP_ISAPI();

	void *operator new (size_t cSize) { return LocalAlloc(0, cSize); }
	void operator delete (void *pInstance) { LocalFree(pInstance); }

	BOOL Initialize( DWORD dwInstanceId, LPSTR *aszMultiSzExtensionList );

    VOID Terminate( VOID );

	BOOL IsInitialized( VOID )
	{
		return(m_dwSignature == SSE_ISAPI_SIGNATURE);
	}
    
    DWORD CallExtension( EXTENSION_TYPE	ExtensionType,
						 LPVOID			lpvExtensionBlock );

    LIST_ENTRY * QueryExtensionList( VOID )
    {
        return( &m_ExtensionList );
    }

    LIST_ENTRY * QueryContextList( VOID )
    {
        return( &m_ContextList );
    }

	DWORD QueryExtensionsForType( EXTENSION_TYPE ExtensionType )
	{
		return(m_dwExtensionsOfType[ExtensionType]);
	}

	BOOL IsExtensionTypeValid( EXTENSION_TYPE ExtensionType )
	{
		return( (ExtensionType < INVALID_EXTENSION_TYPE)?TRUE:FALSE );
	}

    VOID LockContextList( VOID )
    {
        EnterCriticalSection( &m_csContextLock );
    }

    VOID UnlockContextList( VOID )
    {
        LeaveCriticalSection( &m_csContextLock );
    }

	// This logs any exceptions
	static VOID LogExceptionEvent( DWORD dwInstanceId, SMTP_EXT *lpExtension );

	// Catch-all check to see if a given string terminates
	// within the given length, should be used after IsBadStringPtr
	static BOOL StringIsTerminated(	CHAR	*lpszString,
									DWORD	dwMaxLength)
	{
		while (dwMaxLength--) 
			if (!*lpszString++) 
				return(TRUE);
		return(FALSE);
	}

private:

	SMTP_EXT *SearchExtensionFromList(CHAR *lpszModuleName);

	DWORD ChainExtensions(SMTP_SERVER_CONTEXT	*lpServerContext,
						  EXTENSION_TYPE		ExtensionType);

	BOOL LoadExtensionDll(CHAR				*lpszModuleName,
						  EXTENSION_TYPE	ExtensionType);

	BOOL LoadExtensions(LPSTR			lpszList,
						EXTENSION_TYPE	ExtensionType);

	VOID FreeExtensionsOfType(EXTENSION_TYPE ExtensionType);

	VOID FreeUnusedExtensions( VOID );

	VOID FreeExtensions( VOID );

	BOOL ComposeErrorTranscript(SMTP_SERVER_CONTEXT	*lpServerContext,
								CHAR				*lpszExtensionName,
								DWORD				dwResult);

	// These are extension type-specific conversion routines
	BOOL CreateEcbFromDeb(	SMTP_SERVER_CONTEXT				*lpContext,
							LPDELIVERY_EXTENSION_BLOCK		lpDeb,
							LPSSE_EXTENSION_CONTROL_BLOCK	lpEcb);

	// These are extension type-specific conversion routines
	BOOL CreateEcbFromMeb(	SMTP_SERVER_CONTEXT				*lpContext,
							LPMESSAGE_EXTENSION_BLOCK		lpDeb,
							LPSSE_EXTENSION_CONTROL_BLOCK	lpEcb);

    DWORD					m_dwSignature;		// Signature

	DWORD					m_dwExtensionsOfType[MAX_POSSIBLE_EXTENSION_TYPE];
												// Counter for each binding point

	DWORD					m_dwInstanceId;		// Server instance ID

    LIST_ENTRY				m_ExtensionList;	// Extension List Head
    LIST_ENTRY				m_ContextList;		// Server Context List Head

    CRITICAL_SECTION		m_csContextLock;	// Server context list lock

};


// ====================================================================
// SMTP server extension object, one exists for each extension loaded
//

class SMTP_EXT
{
public:

    SMTP_EXT(	SMTP_ISAPI				*lpSdkInstance,
				CHAR					*lpszModuleName,
				EXTENSION_TYPE			ExtensionType,
				HMODULE					hMod,
				PFN_EXTENSIONPROC		pfnEntryPoint )
	{
		_ASSERT(lpszModuleName);
		_ASSERT(lpSdkInstance);
		_ASSERT(lpSdkInstance->StringIsTerminated(lpszModuleName,
						SSE_MAX_EXT_DLL_NAME_LEN));

		m_dwSignature = 0;

		// Initialize members
		lstrcpy(m_szModuleName, lpszModuleName);
		m_hMod = hMod;

		m_dwExtensionType = 0;
		for (DWORD i = 0; i < MAX_POSSIBLE_EXTENSION_TYPE; i++)
			m_pfnEntryPoint[i] = NULL;
		IncludeExtensionType(ExtensionType, pfnEntryPoint);

		m_dwSignature = SSE_EXT_SIGNATURE;
	}

    ~SMTP_EXT( VOID ) {}

    BOOL IsValid( VOID )
    {
        return( m_dwSignature == SSE_EXT_SIGNATURE );
    }

    LIST_ENTRY * QueryListEntry( VOID )
    {
        return( &m_ListEntry );
    }

	BOOL ExtensionTypeIncludes(EXTENSION_TYPE ExtensionType)
	{
		_ASSERT(ExtensionType < MAX_POSSIBLE_EXTENSION_TYPE);
		return( (m_dwExtensionType & (1 << ExtensionType)) != 0 );
	}

	BOOL ExtensionIsUnused( VOID )
	{
		return(m_dwExtensionType == 0);
	}

    PFN_EXTENSIONPROC QueryEntryPoint(EXTENSION_TYPE ExtensionType)
    {
		if (ExtensionTypeIncludes(ExtensionType))
		    return( m_pfnEntryPoint[ExtensionType] );
		else
			return(NULL);
	}

	VOID IncludeExtensionType(EXTENSION_TYPE	ExtensionType,
							  PFN_EXTENSIONPROC	pfnEntryPoint)
	{
		_ASSERT(ExtensionType < MAX_POSSIBLE_EXTENSION_TYPE);
		m_dwExtensionType |= (1 << ExtensionType);
		m_pfnEntryPoint[ExtensionType] = pfnEntryPoint;
	}

	VOID RemoveExtensionType(EXTENSION_TYPE	ExtensionType)
	{
		_ASSERT(ExtensionType < MAX_POSSIBLE_EXTENSION_TYPE);
		m_dwExtensionType &= (~(1 << ExtensionType));
		m_pfnEntryPoint[ExtensionType] = NULL;
	}

    CHAR * QueryModuleName( VOID )
    {
        return( m_szModuleName );
    }

    HMODULE QueryHMod( VOID )
    {
        return( m_hMod );
    }

    LIST_ENTRY				m_ListEntry;

private:

	DWORD				m_dwExtensionType;	// Extension type(s)
    CHAR				m_szModuleName[SSE_MAX_EXT_DLL_NAME_LEN];	
											// Name of module
    HMODULE				m_hMod;				// Handle to library
    PFN_EXTENSIONPROC	m_pfnEntryPoint[MAX_POSSIBLE_EXTENSION_TYPE];
											// Entry point into binding points

    DWORD				m_dwSignature;		// Signature

}; 


// ====================================================================
// SMTP server context, one exists for every thread inside ISAPI
//

typedef LPVOID LPSSE_ECB;
typedef LPVOID LPSSE_SEB;

class SMTP_SERVER_CONTEXT
{
public:

    SMTP_SERVER_CONTEXT(SMTP_ISAPI		*pSdkInstance,
						EXTENSION_TYPE	ExtensionType,
						LPSSE_SEB		lpDeb,
						LPSSE_ECB		lpEcb,
						HANDLE			hImpersonation);

    ~SMTP_SERVER_CONTEXT( VOID );

	BOOL Initialize( VOID );

    BOOL IsValid( VOID )
    {
        return( m_dwSignature == SSE_CONTEXT_SIGNATURE );
    }

    LIST_ENTRY * QueryListEntry( VOID )
    {
        return( &m_ListEntry );
    }

    HANDLE QueryThread( VOID )
    {
        return( m_hCurrentThread );
    }

    SMTP_EXT * QueryExtension( VOID )
    {
        return( m_pCurrentExtension );
    }

    VOID SetExtension( SMTP_EXT *pExtension )
    {
        m_pCurrentExtension = pExtension;
    }

	EXTENSION_TYPE QueryExtensionType( VOID )
	{
		return( m_ExtensionType );
	}

    VOID SetExtensionType( EXTENSION_TYPE ExtensionType )
    {
		_ASSERT(ExtensionType < MAX_POSSIBLE_EXTENSION_TYPE);
        m_ExtensionType = ExtensionType;
    }

    LPSSE_ECB QueryExtensionControlBlock( VOID )
    {
        return( m_lpEcb );
    }

	LPSSE_SEB QueryServerExtensionBlock( VOID )
	{
		return( m_lpSeb );
	}

    BOOL QueryValidTimeout( VOID )
    {
        return( (InterlockedDecrement(&m_InterlockedState) < 0)?TRUE:FALSE );
    }

    BOOL QueryValidCompletion( VOID )
    {
        return( (InterlockedIncrement(&m_InterlockedState) > 0)?TRUE:FALSE );
    }

    LIST_ENTRY				m_ListEntry;			// Link to next server context

private:

	SMTP_ISAPI				*m_pSdkInstance;		// Pointer to SDK instance

	LPSSE_SEB				m_lpSeb;				// Ext. block passed from SMTP
													// server, this is used to fill
													// in the fields of m_lpEcb

	LPSSE_ECB				m_lpEcb;				// Extension control block

    DWORD					m_dwSignature;			// Signature

    SMTP_EXT                *m_pCurrentExtension;	// Current extension object
	EXTENSION_TYPE			m_ExtensionType;		// Current extension type

    HANDLE					m_hCurrentThread;		// Handle of current thread
    long					m_InterlockedState;		// Sync. variable

	HANDLE					m_hImpersonation;		// Impersonation handle
};



#endif