|
|
/*++
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; }
|