Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2765 lines
72 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
Events.cpp
Abstract:
This file provides implementation of the service
notification mechanism.
Author:
Oded Sacher (OdedS) Jan, 2000
Revision History:
--*/
#include "faxsvc.h"
static
DWORD
FaxCloseConnection(
HANDLE hContext
)
{
DWORD rVal = ERROR_SUCCESS;
HANDLE hClientContext = hContext;
DEBUG_FUNCTION_NAME(TEXT("FaxCloseConnection"));
RpcTryExcept
{
//
// Close the context handle
//
rVal = FAX_CloseConnection( &hClientContext );
if (ERROR_SUCCESS != rVal)
{
DumpRPCExtendedStatus();
DebugPrintEx(DEBUG_ERR,TEXT("FAX_CloseConnection() failed, ec=0x%08x"), rVal );
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
DumpRPCExtendedStatus();
rVal = GetExceptionCode();
DebugPrintEx(
DEBUG_ERR,
TEXT("FAX_CloseConnection failed (exception = %ld)"),
rVal);
}
RpcEndExcept
return rVal;
}
/************************************
* *
* Globals *
* *
************************************/
CClientsMap* g_pClientsMap; // Map of clients ID to client.
HANDLE g_hDispatchEventsCompPort; // Events completion port. Te events are dispatched to the client in the client map.
HANDLE g_hSendEventsCompPort; // Completion port of client IDs that have events in their queue.
DWORDLONG g_dwlClientID; // Client ID
/***********************************
* *
* CFaxEventExtended Methodes *
* *
***********************************/
void
CFaxEventExtended::RemoveOffendingExtendedJobStatus ()
{
//
// Client talks with API version 0
// We can't send JS_EX_CALL_COMPLETED and JS_EX_CALL_ABORTED
//
if ((FAX_EVENT_TYPE_IN_QUEUE == m_pEvent->EventType) ||
(FAX_EVENT_TYPE_OUT_QUEUE == m_pEvent->EventType))
{
//
// Queue event
//
if (FAX_JOB_EVENT_TYPE_STATUS == m_pEvent->EventInfo.JobInfo.Type)
{
//
// This is a status event
//
PFAX_JOB_STATUS pStatus = PFAX_JOB_STATUS(DWORD_PTR(m_pEvent) + DWORD_PTR(m_pEvent->EventInfo.JobInfo.pJobData));
if (FAX_API_VER_0_MAX_JS_EX < pStatus->dwExtendedStatus)
{
//
// Offending extended status - clear it
//
pStatus->dwExtendedStatus = 0;
pStatus->dwValidityMask &= ~FAX_JOB_FIELD_STATUS_EX;
}
}
}
return;
}
DWORD
CFaxEventExtended::GetEvent (LPBYTE* lppBuffer, LPDWORD lpdwBufferSize) const
/*++
Routine name : CFaxEventExtended::GetEvent
Routine description:
Returns a buffer filled with serialized FAX_EVENT_EX.
The caller must call MemFree to deallocate memory.
Must be called inside critical section g_CsClients.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
lppBuffer [out] - Address of a pointer to a buffer to recieve the serialized info.
lpdwBufferSize [out] - Pointer to a DWORD to recieve the allocated buffer size.
Return Value:
Standard Win32 error code.
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CFaxEventExtended::GetEvent"));
Assert (lppBuffer && lpdwBufferSize);
*lppBuffer = (LPBYTE)MemAlloc(m_dwEventSize);
if (NULL == *lppBuffer)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocating event buffer"));
return ERROR_OUTOFMEMORY;
}
CopyMemory (*lppBuffer, m_pEvent, m_dwEventSize);
*lpdwBufferSize = m_dwEventSize;
return ERROR_SUCCESS;
} // CFaxEventExtended::GetEvent
CFaxEventExtended::CFaxEventExtended(
const FAX_EVENT_EX* pEvent,
DWORD dwEventSize,
PSID pSid) : m_dwEventSize(dwEventSize), m_pSid(NULL)
{
Assert (pEvent != NULL);
Assert (dwEventSize != 0);
m_pEvent = (PFAX_EVENT_EX)MemAlloc (dwEventSize);
if (NULL == m_pEvent)
{
throw runtime_error("CFaxEventExtended::CFaxEventExtended Can not allocate FAX_EVENT_EX");
}
CopyMemory ((void*)m_pEvent, pEvent, dwEventSize);
if (NULL != pSid)
{
if (!IsValidSid(pSid))
{
MemFree ((void*)m_pEvent);
throw runtime_error ("CFaxEventExtended:: CFaxEventExtended Invalid Sid");
}
DWORD dwSidLength = GetLengthSid(pSid);
m_pSid = (PSID)MemAlloc (dwSidLength);
if (NULL == m_pSid)
{
MemFree ((void*)m_pEvent);
throw runtime_error ("CFaxEventExtended:: CFaxEventExtended Can not allocate Sid");
}
if (!CopySid(dwSidLength, m_pSid, pSid))
{
MemFree ((void*)m_pEvent);
MemFree (m_pSid);
throw runtime_error ("CFaxEventExtended:: CFaxEventExtended CopySid failed Sid");
}
}
}
BOOL
CFaxEventExtended::MatchEvent(PSID pUserSid, DWORD dwEventTypes, BOOL bAllQueueMessages, BOOL bAllOutArchiveMessages) const
{
BOOL bViewAllMessages;
//
// Extended event
//
if (0 == (m_pEvent->EventType & dwEventTypes))
{
//
// Client is not registered for this kind of evevnts
//
return FALSE;
}
//
// Client is registered for this kind of evevnts
//
switch (m_pEvent->EventType)
{
case FAX_EVENT_TYPE_OUT_QUEUE:
bViewAllMessages = bAllQueueMessages;
break;
case FAX_EVENT_TYPE_OUT_ARCHIVE:
bViewAllMessages = bAllOutArchiveMessages;
break;
default:
// Other kind of event - bViewAllMessages is not relevant
bViewAllMessages = TRUE;
}
//
// Check if the user is allowed to see this event
//
if (FALSE == bViewAllMessages)
{
Assert (pUserSid && m_pSid);
//
// The user is not allowed to see all messages
//
if (!EqualSid (pUserSid, m_pSid))
{
//
// Do not send the event to this client.
//
return FALSE;
}
}
return TRUE;
}
/***********************************
* *
* CFaxEventLegacy Methodes *
* *
***********************************/
DWORD
CFaxEventLegacy::GetEvent (LPBYTE* lppBuffer, LPDWORD lpdwBufferSize) const
/*++
Routine name : CFaxEventLegacy::GetEvent
Routine description:
Returns a buffer filled with serialized FAX_EVENT.
The caller must call MemFree to deallocate memory.
Must be called inside critical section g_CsClients.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
lppBuffer [out] - Address of a pointer to a buffer to recieve the serialized info.
lpdwBufferSize [out] - Pointer to a DWORD to recieve the allocated buffer size.
Return Value:
Standard Win32 error code.
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CFaxEventLegacy::GetEvent"));
Assert (lppBuffer && lpdwBufferSize);
*lppBuffer = (LPBYTE)MemAlloc(sizeof(FAX_EVENT));
if (NULL == *lppBuffer)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocating event buffer"));
return ERROR_OUTOFMEMORY;
}
CopyMemory (*lppBuffer, m_pEvent, sizeof(FAX_EVENT));
*lpdwBufferSize = sizeof(FAX_EVENT);
return ERROR_SUCCESS;
} // CFaxEventLegacy::GetEvent
CFaxEventLegacy::CFaxEventLegacy(
const FAX_EVENT* pEvent)
{
Assert (pEvent != NULL);
m_pEvent = (PFAX_EVENT)MemAlloc (sizeof(FAX_EVENT));
if (NULL == m_pEvent)
{
throw runtime_error("CFaxEventExtended::CFaxEventExtended Can not allocate FAX_EVENT_EX");
}
CopyMemory ((void*)m_pEvent, pEvent, sizeof(FAX_EVENT));
}
BOOL
CFaxEventLegacy::MatchEvent(PSID pUserSid, DWORD dwEventTypes, BOOL bAllQueueMessages, BOOL bAllOutArchiveMessages) const
{
if (FAX_EVENT_TYPE_LEGACY == dwEventTypes)
{
//
// Client is registered for this kind of evevnts
//
return TRUE;
}
return FALSE;
}
/***********************************
* *
* CClientID Methodes *
* *
***********************************/
bool
CClientID::operator < ( const CClientID &other ) const
/*++
Routine name : operator <
Class: CClientID
Routine description:
Compares myself with another client ID key
Author:
Oded Sacher (Odeds), Jan, 2000
Arguments:
other [in] - Other key
Return Value:
true only is i'm less than the other key
--*/
{
if (m_dwlClientID < other.m_dwlClientID)
{
return true;
}
return false;
} // CClientID::operator <
/***********************************
* *
* CClient Methodes *
* *
***********************************/
//
// Ctor
//
CClient::CClient (CClientID ClientID,
PSID pUserSid,
DWORD dwEventTypes,
handle_t hFaxHandle,
BOOL bAllQueueMessages,
BOOL bAllOutArchiveMessages,
DWORD dwAPIVersion) :
m_dwEventTypes(dwEventTypes),
m_ClientID(ClientID),
m_bPostClientID(TRUE),
m_bAllQueueMessages(bAllQueueMessages),
m_bAllOutArchiveMessages(bAllOutArchiveMessages),
m_dwAPIVersion(dwAPIVersion),
m_dwRefCount(1)
{
m_FaxHandle = hFaxHandle;
m_hFaxClientContext = NULL;
m_pUserSid = NULL;
if (NULL != pUserSid)
{
if (!IsValidSid(pUserSid))
{
throw runtime_error ("CClient:: CClient Invalid Sid");
}
DWORD dwSidLength = GetLengthSid(pUserSid);
m_pUserSid = (PSID)MemAlloc (dwSidLength);
if (NULL == m_pUserSid)
{
throw runtime_error ("CClient:: CClient Can not allocate Sid");
}
if (!CopySid(dwSidLength, m_pUserSid, pUserSid))
{
MemFree (m_pUserSid);
m_pUserSid = NULL;
throw runtime_error ("CClient:: CClient CopySid failed Sid");
}
}
}
//
// Assignment
//
CClient& CClient::operator= (const CClient& rhs)
{
if (this == &rhs)
{
return *this;
}
m_FaxHandle = rhs.m_FaxHandle;
m_dwEventTypes = rhs.m_dwEventTypes;
m_Events = rhs.m_Events;
m_ClientID = rhs.m_ClientID;
m_hFaxClientContext = rhs.m_hFaxClientContext;
m_bPostClientID = rhs.m_bPostClientID;
m_bAllQueueMessages = rhs.m_bAllQueueMessages;
m_bAllOutArchiveMessages = rhs.m_bAllOutArchiveMessages;
m_dwAPIVersion = rhs.m_dwAPIVersion;
m_dwRefCount = rhs.m_dwRefCount;
MemFree (m_pUserSid);
m_pUserSid = NULL;
if (NULL != rhs.m_pUserSid)
{
if (!IsValidSid(rhs.m_pUserSid))
{
throw runtime_error ("CClient::operator= Invalid Sid");
}
DWORD dwSidLength = GetLengthSid(rhs.m_pUserSid);
m_pUserSid = (PSID)MemAlloc (dwSidLength);
if (NULL == m_pUserSid)
{
throw runtime_error ("CClient::operator= Can not allocate Sid");
}
if (!CopySid(dwSidLength, m_pUserSid, rhs.m_pUserSid))
{
throw runtime_error ("CClient::operator= CopySid failed Sid");
}
}
return *this;
}
//
// Copy Ctor
//
CClient::CClient (const CClient& rhs) : m_ClientID(rhs.m_ClientID)
{
m_FaxHandle = rhs.m_FaxHandle;
m_dwEventTypes = rhs.m_dwEventTypes;
m_Events = rhs.m_Events;
m_hFaxClientContext = rhs.m_hFaxClientContext;
m_bPostClientID = rhs.m_bPostClientID;
m_bAllQueueMessages = rhs.m_bAllQueueMessages;
m_bAllOutArchiveMessages = rhs.m_bAllOutArchiveMessages;
m_dwAPIVersion = rhs.m_dwAPIVersion;
m_dwRefCount = rhs.m_dwRefCount;
m_pUserSid = NULL;
if (NULL != rhs.m_pUserSid)
{
if (!IsValidSid(rhs.m_pUserSid))
{
throw runtime_error("CClient::CopyCtor Invalid Sid");
}
DWORD dwSidLength = GetLengthSid(rhs.m_pUserSid);
m_pUserSid = (PSID)MemAlloc (dwSidLength);
if (NULL == m_pUserSid)
{
throw runtime_error("CClient::CopyCtor Can not allocate Sid");
}
if (!CopySid(dwSidLength, m_pUserSid, rhs.m_pUserSid))
{
throw runtime_error("CClient::CopyCtor CopySid failed");
}
}
return;
}
//
// Dtor
//
CClient::~CClient ()
{
DEBUG_FUNCTION_NAME(TEXT("CClient::~CClient"));
try
{
while (FALSE == m_Events.empty())
{
CFaxEvent* pFaxEvent = m_Events.front();
m_Events.pop();
delete pFaxEvent;
}
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("queue or CFaxEvent caused exception (%S)"),
ex.what());
}
MemFree (m_pUserSid);
m_pUserSid = NULL;
return;
}
DWORD
CClient::AddEvent(CFaxEvent* pFaxEvent)
/*++
Routine name : CClient::AddEvent
Routine description:
Adds CFaxEvent object to the client's events queue.
Must be called inside critical section g_CsClients.
The function frees pFaxEvent if it is not added to the client queue.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
pFaxEvent [in] - Pointer to CFaxEvnet object
Return Value:
Standard Win32 error code
--*/
{
DWORD dwRes = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClient::AddEvent"));
BOOL bEventAdded = FALSE;
if (!pFaxEvent->MatchEvent(m_pUserSid, m_dwEventTypes, m_bAllQueueMessages, m_bAllOutArchiveMessages))
{
//
// Client is not registered for this event. Free the event report success
//
goto exit;
}
if (FAX_API_VERSION_1 > m_dwAPIVersion)
{
//
// Client talks with API version 0
// We can't send JS_EX_CALL_COMPLETED and JS_EX_CALL_ABORTED
pFaxEvent->RemoveOffendingExtendedJobStatus();
}
try
{
//
// Add the event to the client queue
//
m_Events.push(pFaxEvent);
if (TRUE == m_bPostClientID)
{
//
// events in queue - Notify the completion port threads of the client's queued events
//
CClientID* pClientID = new (std::nothrow) CClientID(m_ClientID);
if (NULL == pClientID)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Can not allocate CClientID object"));
dwRes = ERROR_OUTOFMEMORY;
m_Events.pop();
goto exit;
}
//
// post CLIENT_COMPLETION_KEY to the completion port
//
if (!PostQueuedCompletionStatus( g_hSendEventsCompPort,
sizeof(CClientID),
CLIENT_COMPLETION_KEY,
(LPOVERLAPPED) pClientID))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("PostQueuedCompletionStatus failed. (ec: %ld)"),
dwRes);
delete pClientID;
pClientID = NULL;
m_Events.pop();
goto exit;
}
m_bPostClientID = FALSE;
}
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("queue or CFaxEvent caused exception (%S)"),
ex.what());
dwRes = ERROR_GEN_FAILURE;
goto exit;
}
bEventAdded = TRUE;
Assert (ERROR_SUCCESS == dwRes);
exit:
if (FALSE == bEventAdded)
{
delete pFaxEvent;
}
return dwRes;
} // CClient::AddEvent
DWORD
CClient::GetEvent (LPBYTE* lppBuffer, LPDWORD lpdwBufferSize, PHANDLE phClientContext) const
/*++
Routine name : CClient::GetEvent
Routine description:
Gets a serialized FAX_EVENT_EX buffer to be sent
to a client using the client context handle (obtained from OpenConnection()).
The caller must call MemFree to deallocate memory.
Must be called inside critical section g_CsClients.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
lppBuffer [out] - Address of a pointer to a buffer to recieve the serialized info.
lpdwBufferSize [out] - Pointer to a DWORD to recieve the allocated buffer size.
phClientContext [out] - Pointer to a HANDLE to recieve the client context handle.
Return Value:
Standard Win32 error code.
--*/
{
DWORD dwRes = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClient::GetEvent"));
Assert ( lppBuffer && phClientContext);
try
{
// get a reference to the top event
const CFaxEvent* pFaxEvent = m_Events.front();
//
// get the serialized FAX_EVENT_EX or FAX_EVENT buffer
//
dwRes = pFaxEvent->GetEvent(lppBuffer ,lpdwBufferSize);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CFaxEvent::GetEvent failed with error = %ld)"),
dwRes);
goto exit;
}
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("queue or CFaxEvent caused exception (%S)"),
ex.what());
dwRes = ERROR_GEN_FAILURE;
goto exit;
}
//
// Get the client context handle
//
*phClientContext = m_hFaxClientContext;
Assert (ERROR_SUCCESS == dwRes);
exit:
return dwRes;
} // CClient::GetEvent
DWORD
CClient::DelEvent ()
/*++
Routine name : CClient::DelEvent
Routine description:
Removes the first event from the queue.
Must be called inside critical section g_CsClients.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
Return Value:
Standard Win32 error code
--*/
{
DWORD dwRes = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClient::DelEvent"));
Assert (m_bPostClientID == FALSE);
try
{
CFaxEvent* pFaxEvent = m_Events.front();
m_Events.pop();
delete pFaxEvent;
if (m_Events.empty())
{
// last event was poped ,next event will notify of client queued events
m_bPostClientID = TRUE;
}
else
{
//
// More events in queue - Notify the completion port of queued events
//
CClientID* pClientID = new (std::nothrow) CClientID(m_ClientID);
if (NULL == pClientID)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Can not allocate CClientID object"));
dwRes = ERROR_OUTOFMEMORY;
goto exit;
}
if (!PostQueuedCompletionStatus( g_hSendEventsCompPort,
sizeof(CClientID),
CLIENT_COMPLETION_KEY,
(LPOVERLAPPED) pClientID))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("PostQueuedCompletionStatus failed. (ec: %ld)"),
dwRes);
delete pClientID;
pClientID = NULL;
m_bPostClientID = TRUE; // try to notify when the next event is queued
goto exit;
}
m_bPostClientID = FALSE;
}
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("queue caused exception (%S)"),
ex.what());
dwRes = ERROR_GEN_FAILURE;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
return dwRes;
} // CClient::DelEvent
/***********************************
* *
* CClientsMap Methodes *
* *
***********************************/
DWORD
CClientsMap::AddClient (const CClient& Client)
/*++
Routine name : CClientsMap::AddClient
Routine description:
Adds a new client to the global map.
Must be called inside critical section g_CsClients.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
Client [in ] - A reference to the new client object
Return Value:
Standard Win32 error code
--*/
{
CLIENTS_MAP::iterator it;
DWORD dwRes = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClientsMap::AddClient"));
pair <CLIENTS_MAP::iterator, bool> p;
try
{
//
// Add new map entry
//
p = m_ClientsMap.insert (CLIENTS_MAP::value_type(Client.GetClientID(), Client));
//
// See if entry exists in map
//
if (p.second == FALSE)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Client allready in the clients map"));
dwRes = ERROR_DUP_NAME;
Assert (p.second == TRUE); // Assert FALSE
goto exit;
}
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("map or Client caused exception (%S)"),
ex.what());
dwRes = ERROR_GEN_FAILURE;
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
return dwRes;
} // CClientsMap::AddClient
DWORD
CClientsMap::ReleaseClient (const CClientID& ClientID, BOOL fRunDown /* = FALSE */)
/*++
Routine name : CClientsMap::ReleaseClient
Routine description:
Decrease a client refertnce count. If refcount is 0, Deletes it from the global clients map.
DO NOT call ReleaseClient when holding g_CsClients!
A call to ReleaseClient can cause FaxCloseConnection to be called. As FaxCloseConnection is
a RPC call, it (ReleaseClient) must be called when g_CsClients is NOT held.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
ClientID [in ] - Reference to the client ID key
fRunDown [in ] - The call is a result of RPC rundown
Return Value:
Standard Win32 error code
--*/
{
DWORD dwRes = ERROR_SUCCESS;
CClient* pClient = NULL;
CLIENTS_MAP::iterator it;
DWORD dwRefCount;
HANDLE hClientContext = NULL;
HANDLE hBindingHandle = NULL;
DWORD rVal = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClientsMap::ReleaseClient"));
//
// Enter g_CsClients while searching for the client.
//
EnterCriticalSection (&g_CsClients);
try
{
//
// See if entry exists in map
//
if((it = m_ClientsMap.find(ClientID)) == m_ClientsMap.end())
{
dwRes = ERROR_SUCCESS; // Client was removed from map
DebugPrintEx(
DEBUG_WRN,
TEXT("client not found, Client ID %I64"),
ClientID.GetID());
goto exit;
}
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("map caused exception (%S)"),
ex.what());
dwRes = ERROR_GEN_FAILURE;
goto exit;
}
pClient = &((*it).second);
Assert (pClient);
if (TRUE == fRunDown)
{
//
// Prevent further RPC calls to this client
//
pClient->SetContextHandle(NULL);
}
dwRefCount = pClient->Release();
if (dwRefCount > 0)
{
//
// Client can not be removed from the map yet
//
goto exit;
}
hClientContext = pClient->GetContextHandle();
if (NULL != hClientContext)
{
//
// Block other threads from sending events to this client
//
pClient->SetContextHandle(NULL);
//
// A connection to the client was opened.
// leave g_CsClients while trying to close the connection
//
LeaveCriticalSection (&g_CsClients);
rVal = FaxCloseConnection(hClientContext);
if (ERROR_SUCCESS != rVal)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("FaxCloseConnection failed with ec = %ld"),
rVal);
}
EnterCriticalSection (&g_CsClients);
}
hBindingHandle = pClient->GetFaxHandle();
Assert (hBindingHandle);
dwRes = RpcBindingFree(&hBindingHandle);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_WRN,
TEXT("RpcBindingFree failed, ec: %ld"),
dwRes);
}
//
// Delete the map entry
//
m_ClientsMap.erase (it);
exit:
LeaveCriticalSection (&g_CsClients);
return ((ERROR_SUCCESS != rVal) ? rVal : dwRes);
} // CClientsMap::ReleaseClient
PCCLIENT
CClientsMap::FindClient (const CClientID& ClientID) const
/*++
Routine name : CClientsMap::FindClient
Routine description:
Returns a pointer to a client object specified by its ID object.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
ClientID [in] - The clients's ID object
Return Value:
Pointer to the found rule object. If it is null the client was not found.
If the client object was returned, the caller must call CClientsMap::ReleaseClient to release the client object.
--*/
{
CLIENTS_MAP::iterator it;
PCCLIENT pClient = NULL;
DWORD ec = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClientsMap::FindClient"));
EnterCriticalSection (&g_CsClients);
try
{
//
// See if entry exists in map
//
if((it = m_ClientsMap.find(ClientID)) == m_ClientsMap.end())
{
ec = ERROR_NOT_FOUND;
goto exit;
}
pClient = &((*it).second);
if (0 != pClient->GetRefCount())
{
//
// Increase the client reference count, so it will not be deleted.
//
pClient->Lock();
}
else
{
//
// The client is being deleted.
//
pClient = NULL;
ec = ERROR_NOT_FOUND;
}
goto exit;
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("map caused exception (%S)"),
ex.what());
ec = ERROR_GEN_FAILURE;
}
exit:
LeaveCriticalSection (&g_CsClients);
if (NULL == pClient)
{
Assert (ERROR_SUCCESS != ec);
SetLastError(ec);
}
return pClient;
} // CClientsMap::FindClient
DWORD
CClientsMap::AddEvent(CFaxEvent* pFaxEvent)
/*++
Routine name : CClientsMap::AddEvent
Routine description:
Adds event to the events queue of each client that is registered for this kind of event
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
pFaxEvent [in] - Pointer to CFaxEvnet object
Return Value:
Standard Win32 error code
--*/
{
CLIENTS_MAP::iterator it;
DWORD dwRes = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClientsMap::AddEvent"));
CClient* pClient;
EnterCriticalSection (&g_CsClients);
try
{
for (it = m_ClientsMap.begin(); it != m_ClientsMap.end(); it++)
{
pClient = &((*it).second);
if (pClient->IsConnectionOpened())
{
CFaxEvent* pNewFaxEvent = pFaxEvent->Clone();
if (NULL == pNewFaxEvent)
{
dwRes = ERROR_OUTOFMEMORY;
DebugPrintEx(
DEBUG_ERR,
TEXT("CCLient::AddEvent failed)"));
goto exit;
}
else
{
dwRes = pClient->AddEvent (pNewFaxEvent);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CCLient::AddEvent failed with error = %ld)"),
dwRes);
goto exit;
}
}
}
}
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("map caused exception (%S)"),
ex.what());
dwRes = ERROR_GEN_FAILURE;
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
LeaveCriticalSection (&g_CsClients);
return dwRes;
} // CClientsMap::AddEvent
DWORD
CClientsMap::Notify (const CClientID& ClientID)
/*++
Routine name : CClientsMap::Notify
Routine description:
Sends the first event in the specified client events queue
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
pClientID [in ] - Pointer to the client ID object
Return Value:
Standard Win32 error code
--*/
{
DWORD dwRes = ERROR_SUCCESS;
DWORD rVal = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClientsMap::Notify"));
CClient* pClient = NULL;
HANDLE hClientContext;
LPBYTE pBuffer = NULL;
DWORD dwBufferSize = 0;
BOOL fLegacyClient;
//
// Find the client in the map, this will also lock the client
//
pClient = FindClient (ClientID);
if (NULL == pClient)
{
dwRes = GetLastError();
if (ERROR_NOT_FOUND != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClientsMap::FindClient failed with ec = %ld"),
dwRes);
}
else
{
dwRes = ERROR_SUCCESS; // Client was removed from map
DebugPrintEx(
DEBUG_WRN,
TEXT("CClientsMap::FindClient client not found, Client ID %I64"),
ClientID.GetID());
}
return dwRes;
}
//
// Client is locked (will not be deleted), we must call ReleaseClient
// When getting the client data (event and context handle, we must lock g_CsClients as well).
//
EnterCriticalSection (&g_CsClients);
if (FALSE == pClient->IsConnectionOpened())
{
dwRes = ERROR_SUCCESS; // Client closed the connection
DebugPrintEx(
DEBUG_WRN,
TEXT("Client already closed the connection, Client ID %I64"),
ClientID.GetID());
LeaveCriticalSection (&g_CsClients);
goto exit;
}
dwRes = pClient->GetEvent (&pBuffer, &dwBufferSize, &hClientContext);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClient::GetEvent failed with ec = %ld"),
dwRes);
LeaveCriticalSection (&g_CsClients);
goto exit;
}
fLegacyClient = pClient->IsLegacyClient();
//
// Leave g_CsClients before calling RPC
//
LeaveCriticalSection (&g_CsClients);
RpcTryExcept
{
//
// post the event to the client
//
if (FALSE == fLegacyClient)
{
dwRes = FAX_ClientEventQueueEx( hClientContext, pBuffer, dwBufferSize);
if (ERROR_SUCCESS != dwRes)
{
DumpRPCExtendedStatus ();
DebugPrintEx(DEBUG_ERR,TEXT("FAX_ClientEventQueueEX() failed, ec=0x%08x"), dwRes );
}
}
else
{
dwRes = FAX_ClientEventQueue( hClientContext, *((PFAX_EVENT)pBuffer));
if (ERROR_SUCCESS != dwRes)
{
DumpRPCExtendedStatus ();
DebugPrintEx(DEBUG_ERR,TEXT("FAX_ClientEventQueue() failed, ec=0x%08x"), dwRes );
}
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
DumpRPCExtendedStatus ();
dwRes = GetExceptionCode();
DebugPrintEx(
DEBUG_ERR,
TEXT("FAX_ClientEventQueueEX failed (exception = %ld)"),
dwRes);
}
RpcEndExcept
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to post event, Client context handle 0X%x, (ec: %ld)"),
hClientContext,
dwRes);
}
rVal = dwRes;
exit:
//
// Remove the event from the client's queue. CClient::DelEvent must be called so CClient::m_bPostClientID will be set.
//
EnterCriticalSection (&g_CsClients);
dwRes = pClient->DelEvent ();
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClient::DelEvent failed with ec = %ld"),
dwRes);
}
LeaveCriticalSection (&g_CsClients);
DWORD ec = ReleaseClient(ClientID);
if (ERROR_SUCCESS != ec)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClientsMap::ReleaseClient failed with ec = %ld"),
ec);
}
MemFree(pBuffer);
pBuffer = NULL;
return ((ERROR_SUCCESS != rVal) ? rVal : dwRes);
} // CClientsMap::Notify
DWORD
CClientsMap::OpenClientConnection (const CClientID& ClientID)
/*++
Routine name : CClientsMap::OpenClientConnection
Routine description:
Opens a connection to a client
Author:
Oded Sacher (OdedS), Sep, 2000
Arguments:
pClientID [in ] - Pointer to the client ID object
Return Value:
Standard Win32 error code
--*/
{
DWORD dwRes = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CClientsMap::OpenClientConnection"));
CClient* pClient;
handle_t hFaxHandle = NULL;
ULONG64 Context = 0;
HANDLE hFaxClientContext = NULL;
BOOL fLegacyClient;
pClient = FindClient (ClientID);
if (NULL == pClient)
{
dwRes = GetLastError();
if (ERROR_NOT_FOUND != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClientsMap::FindClient failed with ec = %ld"),
dwRes);
}
else
{
dwRes = ERROR_SUCCESS; // Client was removed from map
DebugPrintEx(
DEBUG_WRN,
TEXT("CClientsMap::FindClient client not found, Client ID %I64"),
ClientID.GetID());
}
return dwRes;
}
//
// Client is now locked, we must call ReleaseClient
// Aquire g_CsClients when reading the client data
//
EnterCriticalSection (&g_CsClients);
hFaxHandle = pClient->GetFaxHandle();
Context = (pClient->GetClientID()).GetContext();
fLegacyClient = pClient->IsLegacyClient();
//
// leave g_CsClients while trying to send the notification
//
LeaveCriticalSection (&g_CsClients);
RpcTryExcept
{
//
// Get a context handle from the client
//
dwRes = FAX_OpenConnection( hFaxHandle, Context, &hFaxClientContext );
if (ERROR_SUCCESS != dwRes)
{
DumpRPCExtendedStatus();
DebugPrintEx(DEBUG_WRN,TEXT("First attempt of FAX_OpenConnection() failed, ec=0x%08x"), dwRes );
//
// We are trying again.
//
// This is why we shuld retry,
//
// When using secure channle we sometimes manage to establish a connection, but failing when we checke the state of the connection.
// This is usually indicative of old connections to the server which we just discovered were broken
//
// The server could have come down, but RPC still save the connection (for ~2 minutes).
// On secure channel the RPC can't retry the connection attemp, so we must try to connect again.
//
//
// Retry to open connection to fax client
//
dwRes = FAX_OpenConnection( hFaxHandle, Context, &hFaxClientContext );
if (ERROR_SUCCESS != dwRes)
{
DumpRPCExtendedStatus();
DebugPrintEx(DEBUG_WRN,TEXT("second attempt of FAX_OpenConnection() failed, ec=0x%08x"), dwRes );
//
// We are dropping the authenticate level to RPC_C_AUTHN_LEVEL_NONE trying again.
// We probably talking to a down-level client running on Windows 2000 RTM (SP1 and above are excluded) or earlier OS.
//
//
// We might get access denied while trying to connect to a remote fax server.
// This is probably the RPC infrastructure failing us.
// This only happens because we're using RPC_C_AUTHN_LEVEL_PKT_PRIVACY authentication level
// and the calling user is not trusted.
// This is usally happens when talking to Windows NT4 (all flavors) and Windows 2000 RTM (SP1 and above are excluded).
//
// We might get RPC_S_INVALID_AUTH_IDENTITY:
// This means the client cannot get credentials to authenticate.
// In this case, drop the RPC authentication level back to RPC_C_AUTHN_LEVEL_NONE
//
// We might get RPC_S_UNKNOWN_AUTHN_SERVICE:
// We probably dealing with Win9x or winMe OS.
// Drop the authenticate level to RPC_C_AUTHN_LEVEL_NONE when talking to this downlevel client
//
// Or we might get another error code and we should try to drop the auth level
//
// There is no security hole here - the down level clients that supports private channel will
// reject unsecured notifications
//
// Ask for no privacy.
//
RPC_SECURITY_QOS rpcSecurityQOS = { RPC_C_SECURITY_QOS_VERSION,
RPC_C_QOS_CAPABILITIES_DEFAULT,
RPC_C_QOS_IDENTITY_STATIC,
RPC_C_IMP_LEVEL_IDENTIFY // Server can obtain information about
// client security identifiers and privileges,
// but cannot impersonate the client.
};
dwRes = RpcBindingSetAuthInfoEx (
hFaxHandle, // RPC binding handle
TEXT(""), // Server principal name - ignored for RPC_C_AUTHN_WINNT
RPC_C_AUTHN_LEVEL_NONE, // Authentication level - NONE
// Authenticates, verifies, and privacy-encrypts the arguments passed
// to every remote call.
RPC_C_AUTHN_WINNT, // Authentication service (NTLMSSP)
NULL, // Authentication identity - use currently logged on user
0, // Unused when Authentication service == RPC_C_AUTHN_WINNT
&rpcSecurityQOS); // Defines the security quality-of-service
if (RPC_S_OK != dwRes)
{
//
// Couldn't set RPC authentication mode
//
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcBindingSetAuthInfoEx (RPC_C_AUTHN_LEVEL_NONE) failed. (ec: %lu)"),
dwRes);
}
else
{
dwRes = FAX_OpenConnection( hFaxHandle, Context, &hFaxClientContext );
if (ERROR_SUCCESS != dwRes)
{
DumpRPCExtendedStatus();
DebugPrintEx(DEBUG_ERR,TEXT("third attempt of FAX_OpenConnection() with RPC_C_AUTHN_LEVEL_NONE failed, ec=0x%08x"), dwRes );
}
}
}
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
dwRes = GetExceptionCode();
DebugPrintEx(
DEBUG_ERR,
TEXT("FAX_OpenConnection failed (exception = %ld)"),
dwRes);
}
RpcEndExcept
if (ERROR_SUCCESS != dwRes)
{
DumpRPCExtendedStatus();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to open connection with ec=0x%08x"),
dwRes);
goto exit;
}
//
// For legacy clients we need to send FEI_FAXSVC_STARTED
//
if (TRUE == fLegacyClient)
{
FAX_EVENT FaxEvent = {0};
DWORD ec;
FaxEvent.SizeOfStruct = sizeof(FAX_EVENT);
GetSystemTimeAsFileTime( &FaxEvent.TimeStamp );
FaxEvent.EventId = FEI_FAXSVC_STARTED;
FaxEvent.DeviceId = 0;
FaxEvent.JobId = 0xffffffff;
RpcTryExcept
{
//
// Send FEI_FAXSVC_STARTED to the client
//
ec = FAX_ClientEventQueue( hFaxClientContext, FaxEvent );
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
ec = GetExceptionCode();
DebugPrintEx(
DEBUG_ERR,
TEXT("FAX_ClientEventQueue failed (exception = %ld)"),
ec);
}
RpcEndExcept
if (ERROR_SUCCESS != ec)
{
DumpRPCExtendedStatus ();
DebugPrintEx(
DEBUG_ERR,
TEXT("FAX_ClientEventQueue Failed with ec = %ld"),
ec);
}
}
//
// success - Set the context handle in the client object
//
EnterCriticalSection (&g_CsClients);
pClient->SetContextHandle(hFaxClientContext);
LeaveCriticalSection (&g_CsClients);
Assert (ERROR_SUCCESS == dwRes);
exit:
DWORD rVal = ReleaseClient(ClientID);
if (ERROR_SUCCESS != rVal)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClientsMap::ReleaseClient Failed with ec = %ld"),
rVal);
}
return dwRes;
} // CClientsMap::OpenClientConnection
/************************************
* *
* Functions *
* *
************************************/
DWORD
FaxSendEventThread(
LPVOID UnUsed
)
/*++
Routine Description:
This fuction runs asychronously as a separate thread to
query the send events completion port
Arguments:
UnUsed - UnUsed pointer
Return Value:
Always zero.
--*/
{
DWORD dwBytes;
ULONG_PTR CompletionKey;
DWORD dwRes;
CClientID* pClientID=NULL;
DEBUG_FUNCTION_NAME(TEXT("FaxSendEventThread"));
while( TRUE )
{
if (!GetQueuedCompletionStatus( g_hSendEventsCompPort,
&dwBytes,
&CompletionKey,
(LPOVERLAPPED*) &pClientID,
INFINITE
))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"),
GetLastError());
continue;
}
Assert (CLIENT_COMPLETION_KEY == CompletionKey ||
CLIENT_OPEN_CONN_COMPLETION_KEY == CompletionKey ||
SERVICE_SHUT_DOWN_KEY == CompletionKey);
//
// if service is going down skip the notification
//
if (CLIENT_COMPLETION_KEY == CompletionKey &&
FALSE == g_bServiceIsDown )
{
//
// Send notification to the client
//
dwRes = g_pClientsMap->Notify (*pClientID);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClientsMap::Notify() failed, ec=0x%08x"),
dwRes);
}
delete pClientID;
pClientID = NULL;
continue;
}
else if (CLIENT_OPEN_CONN_COMPLETION_KEY == CompletionKey &&
FALSE == g_bServiceIsDown )
{
//
// Open connection to the client - Get context handle
//
dwRes = g_pClientsMap->OpenClientConnection (*pClientID);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClientsMap::OpenClientConnection() failed, ec=0x%08x"),
dwRes);
//
// Remove this client fromm the map
//
dwRes = g_pClientsMap->ReleaseClient(*pClientID);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CClientsMap::ReleaseClient() failed, ec=0x%08x"),
dwRes);
}
}
delete pClientID;
pClientID = NULL;
continue;
}
else if (SERVICE_SHUT_DOWN_KEY == CompletionKey)
{
//
// Terminate events thread - Notify another event thread
//
if (!PostQueuedCompletionStatus(
g_hSendEventsCompPort,
0,
SERVICE_SHUT_DOWN_KEY,
(LPOVERLAPPED) NULL))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("PostQueuedCompletionStatus failed (SERVICE_SHUT_DOWN_KEY). (ec: %ld)"),
dwRes);
}
break;
}
else
{
//
// if service is going down skip the event adding
//
delete pClientID;
pClientID = NULL;
}
}
if (!DecreaseServiceThreadsCount())
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
GetLastError());
}
return ERROR_SUCCESS;
} // FaxSendEventThread
DWORD
FaxDispatchEventThread(
LPVOID UnUsed
)
/*++
Routine Description:
This fuction runs asychronously as a separate thread to
query the dispatch events completion port
Arguments:
UnUsed - UnUsed pointer
Return Value:
Always zero.
--*/
{
DWORD dwBytes;
ULONG_PTR CompletionKey;
DWORD dwRes;
CFaxEvent* pFaxEvent = NULL;
DEBUG_FUNCTION_NAME(TEXT("FaxDispatchEventThread"));
while( TRUE )
{
if (!GetQueuedCompletionStatus( g_hDispatchEventsCompPort,
&dwBytes,
&CompletionKey,
(LPOVERLAPPED*) &pFaxEvent,
INFINITE
))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"),
GetLastError());
continue;
}
Assert (EVENT_COMPLETION_KEY == CompletionKey ||
SERVICE_SHUT_DOWN_KEY == CompletionKey);
//
// if service is going down skip the notification
//
if (EVENT_COMPLETION_KEY == CompletionKey &&
FALSE == g_bServiceIsDown )
{
//
// Add event to the clients in the clients map
//
dwRes = g_pClientsMap->AddEvent(pFaxEvent);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"),
dwRes);
}
delete pFaxEvent;
pFaxEvent = NULL;
continue;
}
else if (SERVICE_SHUT_DOWN_KEY == CompletionKey)
{
break;
}
else
{
//
// if service is going down skip the event adding
//
delete pFaxEvent;
pFaxEvent = NULL;
}
}
if (!DecreaseServiceThreadsCount())
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
GetLastError());
}
return ERROR_SUCCESS;
} // FaxDispatchEventThread
DWORD
InitializeServerEvents ()
/*++
Routine name : InitializeServerEvents
Routine description:
Creates the events completion ports and the Event Threads
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
Return Value:
Standard Win32 error code
--*/
{
DWORD dwRes = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("InitializeServerEvents"));
DWORD i;
DWORD ThreadId;
HANDLE hSendEventThreads[TOTAL_EVENTS_THREADS] = {0};
HANDLE hDispatchEventThread = NULL;
//
// create send event completion port.
//
g_hSendEventsCompPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE,
NULL,
0,
MAX_EVENTS_THREADS
);
if (!g_hSendEventsCompPort)
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create g_hSendEventsCompPort (ec: %ld)"),
dwRes);
return dwRes;
}
//
// create dispatch event completion port.
//
g_hDispatchEventsCompPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE,
NULL,
0,
1);
if (!g_hDispatchEventsCompPort)
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create g_hDispatchEventsCompPort (ec: %ld)"),
dwRes);
return dwRes;
}
//
// Create FaxSendEventThread
//
for (i = 0; i < TOTAL_EVENTS_THREADS; i++)
{
hSendEventThreads[i] = CreateThreadAndRefCount(
NULL,
0,
(LPTHREAD_START_ROUTINE) FaxSendEventThread,
NULL,
0,
&ThreadId
);
if (!hSendEventThreads[i])
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create send event thread %d (CreateThreadAndRefCount)(ec=0x%08x)."),
i,
dwRes);
goto exit;
}
}
//
// Create FaxDispatchEventThread
//
hDispatchEventThread = CreateThreadAndRefCount(
NULL,
0,
(LPTHREAD_START_ROUTINE) FaxDispatchEventThread,
NULL,
0,
&ThreadId
);
if (!hDispatchEventThread)
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create dispatch event(CreateThreadAndRefCount)(ec=0x%08x)."),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
//
// Close the thread handles we no longer need them
//
for (i = 0; i < TOTAL_EVENTS_THREADS; i++)
{
if (NULL != hSendEventThreads[i])
{
if (!CloseHandle(hSendEventThreads[i]))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to close thread handle at index %ld [handle = 0x%08X] (ec=0x%08x)."),
i,
hSendEventThreads[i],
GetLastError());
}
}
}
if (NULL != hDispatchEventThread)
{
if (!CloseHandle(hDispatchEventThread))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to close thread handle [handle = 0x%08X] (ec=0x%08x)."),
hDispatchEventThread,
GetLastError());
}
}
return dwRes;
} // InitializeServerEvents
DWORD
PostFaxEventEx (
PFAX_EVENT_EX pFaxEvent,
DWORD dwEventSize,
PSID pUserSid)
/*++
Routine name : PostFaxEventEx
Routine description:
Posts a CFaxEventExtended object to the events completion port.
FaxSendEventThread must call delete to deallocate the object.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
pFaxEvent [in] - Pointer to the serialized FAX_EVENT_EX buffer
dwEventSize [in] - The FAX_EVENT_EX buffer size
pUserSid [in] - The user sid to associate with the event
Return Value:
Standard Win32 error code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("PostFaxEventEx"));
Assert (pFaxEvent && (dwEventSize >= sizeof(FAX_EVENT_EX)));
DWORD dwRes = ERROR_SUCCESS;
if (TRUE == g_bServiceIsDown)
{
//
// The service is going down, no need to post this Event
//
DebugPrintEx(
DEBUG_WRN,
TEXT("Service is going down, no need to post this Event.")
);
return ERROR_SERVICE_NOT_ACTIVE;
}
CFaxEventExtended* pExtendedEvent = NULL;
try
{
pExtendedEvent = new (std::nothrow) CFaxEventExtended(
pFaxEvent,
dwEventSize,
pUserSid);
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CFaxEventExtended caused exception (%S)"),
ex.what());
}
if (NULL == pExtendedEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to allocate new CFaxEventExtended"));
return ERROR_OUTOFMEMORY;
}
//
// post the CFaxEventExtended object to the event completion port
//
if (!PostQueuedCompletionStatus( g_hDispatchEventsCompPort,
sizeof(CFaxEventExtended*),
EVENT_COMPLETION_KEY,
(LPOVERLAPPED) pExtendedEvent))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("PostQueuedCompletionStatus failed. (ec: %ld)"),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
if (ERROR_SUCCESS != dwRes)
{
delete pExtendedEvent;
}
return dwRes;
} // PostFaxEventEx
DWORD
CreateQueueEvent (
FAX_ENUM_JOB_EVENT_TYPE JobEventType,
const PJOB_QUEUE lpcJobQueue
)
/*++
Routine name : CreateQueueEvent
Routine description:
Creates FAX_EVENT_TYPE_*_QUEUE event.
Must be called inside critical section and g_CsQueue and if there is job status inside g_CsJob also.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
JobEventType [in] - Specifies the job event type FAX_ENUM_JOB_EVENT_TYPE
lpcJobQueue [in] - Pointer to the job queue entry
Return Value:
Standard Win32 error code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CreateQueueEvent"));
ULONG_PTR dwOffset = sizeof(FAX_EVENT_EX);
DWORD dwRes = ERROR_SUCCESS;
PFAX_EVENT_EXW pEvent = NULL;
FAX_ENUM_EVENT_TYPE EventType;
PSID pUserSid = NULL;
DWORDLONG dwlMessageId;
Assert (lpcJobQueue);
dwlMessageId = lpcJobQueue->UniqueId;
if (JT_SEND == lpcJobQueue->JobType)
{
// outbound job
Assert (lpcJobQueue->lpParentJob);
EventType = FAX_EVENT_TYPE_OUT_QUEUE;
pUserSid = lpcJobQueue->lpParentJob->UserSid;
}
else
{
// Inbound job
Assert (JT_RECEIVE == lpcJobQueue->JobType ||
JT_ROUTING == lpcJobQueue->JobType);
EventType = FAX_EVENT_TYPE_IN_QUEUE;
}
if (FAX_JOB_EVENT_TYPE_ADDED == JobEventType ||
FAX_JOB_EVENT_TYPE_REMOVED == JobEventType)
{
// No job status
pEvent = (PFAX_EVENT_EX)MemAlloc (dwOffset);
if (NULL == pEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocatin FAX_EVENT_EX"));
return ERROR_OUTOFMEMORY;
}
(pEvent->EventInfo).JobInfo.pJobData = NULL;
}
else
{
//
// Status change
//
Assert (FAX_JOB_EVENT_TYPE_STATUS == JobEventType);
//
// Get the needed buffer size to hold FAX_JOB_STATUSW serialized info
//
if (!GetJobStatusDataEx (NULL,
NULL,
FAX_API_VERSION_1, // Always pick full data
lpcJobQueue,
&dwOffset,
0))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("GetJobStatusDataEx failed (ec: %ld)"),
dwRes);
return dwRes;
}
//
// Allocate the buffer
//
DWORD dwEventSize = dwOffset;
pEvent = (PFAX_EVENT_EXW)MemAlloc (dwEventSize);
if (NULL == pEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocatin FAX_EVENT_EX"));
return ERROR_OUTOFMEMORY;
}
//
// Fill the buffer
//
dwOffset = sizeof(FAX_EVENT_EXW);
(pEvent->EventInfo).JobInfo.pJobData = (PFAX_JOB_STATUSW)dwOffset;
PFAX_JOB_STATUSW pFaxStatus = (PFAX_JOB_STATUSW) ((LPBYTE)pEvent + (ULONG_PTR)dwOffset);
dwOffset += sizeof(FAX_JOB_STATUSW);
if (!GetJobStatusDataEx ((LPBYTE)pEvent,
pFaxStatus,
FAX_API_VERSION_1, // Always pick full data
lpcJobQueue,
&dwOffset,
dwEventSize
))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("GetJobStatusDataEx failed (ec: %ld)"),
dwRes);
goto exit;
}
}
pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EXW);
GetSystemTimeAsFileTime( &(pEvent->TimeStamp) );
pEvent->EventType = EventType;
(pEvent->EventInfo).JobInfo.dwlMessageId = dwlMessageId;
(pEvent->EventInfo).JobInfo.Type = JobEventType;
dwRes = PostFaxEventEx (pEvent, dwOffset, pUserSid);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostFaxEventEx failed (ec: %ld)"),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
MemFree (pEvent);
return dwRes;
} // CreateQueueEvent
DWORD
CreateConfigEvent (
FAX_ENUM_CONFIG_TYPE ConfigType
)
/*++
Routine name : CreateConfigEvent
Routine description:
Creates FAX_EVENT_TYPE_CONFIG event.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
ConfigType [in ] - The configuration event type FAX_ENUM_CONFIG_TYPE
Return Value:
Standard Win32 error code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CreateConfigEvent"));
PFAX_EVENT_EX pEvent = NULL;
DWORD dwRes = ERROR_SUCCESS;
DWORD dwEventSize = sizeof(FAX_EVENT_EX);
pEvent = (PFAX_EVENT_EX)MemAlloc (dwEventSize);
if (NULL == pEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocatin FAX_EVENT_EX"));
return ERROR_OUTOFMEMORY;
}
pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EX);
GetSystemTimeAsFileTime( &(pEvent->TimeStamp) );
pEvent->EventType = FAX_EVENT_TYPE_CONFIG;
(pEvent->EventInfo).ConfigType = ConfigType;
dwRes = PostFaxEventEx (pEvent, dwEventSize, NULL);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostFaxEventEx failed (ec: %ld)"),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
MemFree (pEvent);
return dwRes;
} // CreateConfigEvent
DWORD
CreateQueueStateEvent (
DWORD dwQueueState
)
/*++
Routine name : CreateQueueStateEvent
Routine description:
Creates FAX_EVENT_TYPE_QUEUE_STATE event.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
dwQueueState [in ] - The new queue state
Return Value:
Standard Win32 error code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CreateQueueStateEvent"));
DWORD dwRes = ERROR_SUCCESS;
DWORD dwEventSize = sizeof(FAX_EVENT_EX);
PFAX_EVENT_EX pEvent = NULL;
Assert ( (dwQueueState == 0) ||
(dwQueueState & FAX_INCOMING_BLOCKED) ||
(dwQueueState & FAX_OUTBOX_BLOCKED) ||
(dwQueueState & FAX_OUTBOX_PAUSED) );
pEvent = (PFAX_EVENT_EX)MemAlloc (dwEventSize);
if (NULL == pEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocatin FAX_EVENT_EX"));
return ERROR_OUTOFMEMORY;
}
pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EX);
GetSystemTimeAsFileTime( &(pEvent->TimeStamp) );
pEvent->EventType = FAX_EVENT_TYPE_QUEUE_STATE;
(pEvent->EventInfo).dwQueueStates = dwQueueState;
dwRes = PostFaxEventEx (pEvent, dwEventSize, NULL);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostFaxEventEx failed (ec: %ld)"),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
MemFree (pEvent);
return dwRes;
} // CreateQueueStateEvent
DWORD
CreateDeviceEvent (
PLINE_INFO pLine,
BOOL bRinging
)
/*++
Routine name : CreateDeviceEvent
Routine description:
Creates FAX_EVENT_TYPE_DEVICE_STATUS event.
Author:
Eran Yariv (EranY), July, 2000
Arguments:
pLine [in] - Device
bRinging [in] - Is the device ringing now?
Return Value:
Standard Win32 error code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CreateDeviceEvent"));
DWORD dwRes = ERROR_SUCCESS;
DWORD dwEventSize = sizeof(FAX_EVENT_EX);
PFAX_EVENT_EX pEvent = NULL;
pEvent = (PFAX_EVENT_EX)MemAlloc (dwEventSize);
if (NULL == pEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocatin FAX_EVENT_EX"));
return ERROR_OUTOFMEMORY;
}
pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EX);
GetSystemTimeAsFileTime( &(pEvent->TimeStamp) );
pEvent->EventType = FAX_EVENT_TYPE_DEVICE_STATUS;
EnterCriticalSection (&g_CsLine);
(pEvent->EventInfo).DeviceStatus.dwDeviceId = pLine->PermanentLineID;;
(pEvent->EventInfo).DeviceStatus.dwNewStatus =
(pLine->dwReceivingJobsCount ? FAX_DEVICE_STATUS_RECEIVING : 0) |
(pLine->dwSendingJobsCount ? FAX_DEVICE_STATUS_SENDING : 0) |
(bRinging ? FAX_DEVICE_STATUS_RINGING : 0);
LeaveCriticalSection (&g_CsLine);
dwRes = PostFaxEventEx (pEvent, dwEventSize, NULL);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostFaxEventEx failed (ec: %ld)"),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
MemFree (pEvent);
return dwRes;
} // CreateDeviceEvent
DWORD
CreateArchiveEvent (
DWORDLONG dwlMessageId,
FAX_ENUM_EVENT_TYPE EventType,
FAX_ENUM_JOB_EVENT_TYPE MessageEventType,
PSID pUserSid
)
/*++
Routine name : CreateArchiveEvent
Routine description:
Creates archive event.
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
dwlMessageId [in] - The message unique id
EventType [in] - Specifies the event type (In or Out archive)
pUserSid [in] - The user sid to associate with the event
MessageEventType [in] - Message event type (added or removed).
Return Value:
Standard Win32 error code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CreateArchiveEvent"));
DWORD dwRes = ERROR_SUCCESS;
DWORD dwEventSize = sizeof(FAX_EVENT_EX);
PFAX_EVENT_EX pEvent = NULL;
Assert ( EventType == FAX_EVENT_TYPE_IN_ARCHIVE ||
EventType == FAX_EVENT_TYPE_OUT_ARCHIVE);
Assert ( MessageEventType == FAX_JOB_EVENT_TYPE_ADDED ||
MessageEventType == FAX_JOB_EVENT_TYPE_REMOVED );
pEvent = (PFAX_EVENT_EX)MemAlloc (dwEventSize);
if (NULL == pEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocatin FAX_EVENT_EX"));
return ERROR_OUTOFMEMORY;
}
pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EX);
GetSystemTimeAsFileTime( &(pEvent->TimeStamp) );
pEvent->EventType = EventType;
(pEvent->EventInfo).JobInfo.pJobData = NULL;
(pEvent->EventInfo).JobInfo.dwlMessageId = dwlMessageId;
(pEvent->EventInfo).JobInfo.Type = MessageEventType;
dwRes = PostFaxEventEx (pEvent, dwEventSize, pUserSid);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostFaxEventEx failed (ec: %ld)"),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
MemFree (pEvent);
return dwRes;
} // CreateArchiveEvent
DWORD
CreateActivityEvent ()
/*++
Routine name : CreateActivityEvent
Routine description:
Creates FAX_EVENT_TYPE_ACTIVITY event.
Must be called inside critical section g_CsActivity
Author:
Oded Sacher (OdedS), Jan, 2000
Arguments:
None
Return Value:
Standard Win32 error code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("CreateActivityEvent"));
DWORD dwRes = ERROR_SUCCESS;
DWORD dwEventSize = sizeof(FAX_EVENT_EX);
PFAX_EVENT_EX pEvent = NULL;
pEvent = (PFAX_EVENT_EX)MemAlloc (dwEventSize);
if (NULL == pEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Error allocatin FAX_EVENT_EX"));
return ERROR_OUTOFMEMORY;
}
pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EX);
GetSystemTimeAsFileTime( &(pEvent->TimeStamp) );
pEvent->EventType = FAX_EVENT_TYPE_ACTIVITY;
CopyMemory (&((pEvent->EventInfo).ActivityInfo), &g_ServerActivity, sizeof(FAX_SERVER_ACTIVITY));
GetEventsCounters ( &((pEvent->EventInfo).ActivityInfo.dwErrorEvents),
&((pEvent->EventInfo).ActivityInfo.dwWarningEvents),
&((pEvent->EventInfo).ActivityInfo.dwInformationEvents));
dwRes = PostFaxEventEx (pEvent, dwEventSize, NULL);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostFaxEventEx failed (ec: %ld)"),
dwRes);
goto exit;
}
Assert (ERROR_SUCCESS == dwRes);
exit:
MemFree (pEvent);
return dwRes;
} // CreateActivityEvent
#ifdef DBG
LPWSTR lpszEventCodes[]= {
L"FEI_DIALING",
L"FEI_SENDING",
L"FEI_RECEIVING",
L"FEI_COMPLETED",
L"FEI_BUSY",
L"FEI_NO_ANSWER",
L"FEI_BAD_ADDRESS",
L"FEI_NO_DIAL_TONE",
L"FEI_DISCONNECTED",
L"FEI_FATAL_ERROR",
L"FEI_NOT_FAX_CALL",
L"FEI_CALL_DELAYED",
L"FEI_CALL_BLACKLISTED",
L"FEI_RINGING",
L"FEI_ABORTING",
L"FEI_ROUTING",
L"FEI_MODEM_POWERED_ON",
L"FEI_MODEM_POWERED_OFF",
L"FEI_IDLE",
L"FEI_FAXSVC_ENDED",
L"FEI_ANSWERED",
L"FEI_JOB_QUEUED",
L"FEI_DELETED",
L"FEI_INITIALIZING",
L"FEI_LINE_UNAVAILABLE",
L"FEI_HANDLED",
L"FEI_FAXSVC_STARTED"};
LPTSTR GetEventCodeString(DWORD dwEventCode)
{
if (dwEventCode<FEI_DIALING || dwEventCode>FEI_FAXSVC_STARTED)
{
return L"*** INVALID EVENT CODE ***";
}
else
{
return lpszEventCodes[dwEventCode-1];
}
}
#endif
//*********************************************************************************
//* Name: CreateFaxEvent()
//* Author: Ronen Barenboim
//* Date: March 21, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Creates a CFaxEventLegacy object. Initializes it and posts it to the
//* events completion port with completion key EVENT_COMPLETION_KEY.
//* FaxDispatchEventThread should call delete to deallocate the object.
//* PARAMETERS:
//* DeviceId
//*
//* EventId
//*
//* DWORD JobId
//*
//* RETURN VALUE:
//* FALSE
//* If not enough memory is available to allocated the FAX_EVENT structure
//* TRUE
//* If the operation completed successfully
//*
//* To get extended error information, call GetLastError .
//*
//* REMARKS:
//*
//*********************************************************************************
BOOL CreateFaxEvent(
DWORD DeviceId,
DWORD EventId,
DWORD JobId
)
{
CFaxEventLegacy* pFaxLegacyEvent = NULL;
FAX_EVENT FaxEvent = {0};
DEBUG_FUNCTION_NAME(TEXT("CreateFaxEvent"));
if (TRUE == g_bServiceIsDown)
{
//
// The service is going down, no need to post this Event
//
DebugPrintEx(
DEBUG_WRN,
TEXT("Service is going down, no need to post this Event.")
);
return FALSE;
}
if (NULL == g_hDispatchEventsCompPort)
{
//
// Events mechanism is not yet initialized
//
DebugPrintEx(
DEBUG_WRN,
TEXT("Events mechanism is not yet initialized"));
return TRUE;
}
//
// Note: W2K Fax did issue notifications with EventId == 0 whenever an
// FSP reported proprietry status code. To keep backward compatability
// we keep up this behaviour although it might be regarded as a bug
//
FaxEvent.SizeOfStruct = sizeof(FAX_EVENT);
GetSystemTimeAsFileTime( &FaxEvent.TimeStamp );
FaxEvent.EventId = EventId;
FaxEvent.DeviceId = DeviceId;
FaxEvent.JobId = JobId;
#if DBG
WCHAR szTime[256] = {0};
DebugDateTime(*(DWORDLONG *)&FaxEvent.TimeStamp, szTime, ARR_SIZE(szTime));
DebugPrintEx(DEBUG_MSG,TEXT("Sending notification. Event = %s(0x%0X), Device Id = 0x%0X , Time = %s"), GetEventCodeString(EventId), EventId, DeviceId, szTime);
#endif
try
{
pFaxLegacyEvent = new (std::nothrow) CFaxEventLegacy(&FaxEvent);
}
catch (exception &ex)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CFaxEventLegacy caused exception (%S)"),
ex.what());
}
if (NULL == pFaxLegacyEvent)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to allocate new pFaxLegacyEvent"));
return FALSE;
}
if (!PostQueuedCompletionStatus(
g_hDispatchEventsCompPort,
sizeof(CFaxEventLegacy*),
EVENT_COMPLETION_KEY,
(LPOVERLAPPED) pFaxLegacyEvent))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostQueuedCompletionStatus failed. (ec: %ld)"),
GetLastError());
delete pFaxLegacyEvent;
return FALSE;
}
return TRUE;
}