// ServerNode.cpp: implementation of the CServerNode class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #define __FILE_ID__ 23 #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif IMPLEMENT_DYNAMIC(CServerNode, CTreeNode) ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CServerNode::CServerNode() : CTreeNode(FOLDER_TYPE_SERVER), m_bValid(TRUE), m_dwRights (0), m_dwQueueState (0), m_bSelfDestruct (FALSE), m_hConnection (NULL), m_hNotification (NULL), m_bCsBuildupValid (FALSE), m_bCsBuildupThreadValid(FALSE), m_hBuildupThread (NULL), m_bInBuildup (FALSE), m_dwLastRPCError (0), m_Inbox (FOLDER_TYPE_INBOX, FAX_MESSAGE_FOLDER_INBOX), m_SentItems (FOLDER_TYPE_SENT_ITEMS, FAX_MESSAGE_FOLDER_SENTITEMS), m_Outbox (FOLDER_TYPE_OUTBOX, JT_SEND), m_Incoming (FOLDER_TYPE_INCOMING, JT_RECEIVE | JT_ROUTING) {} CServerNode::~CServerNode() { DBG_ENTER(TEXT("CServerNode::~CServerNode"), TEXT("%s"), m_cstrMachine); DWORD dwRes = StopBuildThread (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("CServerNode::StopBuildThread"), dwRes); } dwRes = Disconnect (TRUE); // Shutdown aware if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("CServerNode::Disconnect"), dwRes); } if (m_bCsBuildupValid) { DeleteCriticalSection (&m_csBuildup); } if(m_bCsBuildupThreadValid) { DeleteCriticalSection (&m_csBuildupThread); } } void CServerNode::AssertValid() const { CObject::AssertValid(); } void CServerNode::Dump( CDumpContext &dc ) const { CObject::Dump( dc ); dc << TEXT(" Server = ") << m_cstrMachine; } // // Static class members: // CServerNode::MESSAGES_MAP CServerNode::m_sMsgs; DWORD CServerNode::m_sdwMinFreeMsg = WM_SERVER_NOTIFY_BASE; CRITICAL_SECTION CServerNode::m_sMsgsCs; BOOL CServerNode::m_sbMsgsCsInitialized = FALSE; DWORD CServerNode::InitMsgsMap () /*++ Routine name : CServerNode::InitMsgsMap Routine description: Initializes the notification messages map Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::InitMsgsMap"), dwRes); ASSERTION (!m_sbMsgsCsInitialized); try { InitializeCriticalSection (&m_sMsgsCs); } catch (...) { dwRes = ERROR_NOT_ENOUGH_MEMORY; CALL_FAIL (MEM_ERR, TEXT ("InitializeCriticalSection"), dwRes); return dwRes; } m_sbMsgsCsInitialized = TRUE; return dwRes; } // CServerNode::InitMsgsMap DWORD CServerNode::ShutdownMsgsMap () /*++ Routine name : CServerNode::ShutdownMsgsMap Routine description: Shuts down the notification messages map Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::ShutdownMsgsMap"), dwRes); if (!m_sbMsgsCsInitialized) { return dwRes; } EnterCriticalSection (&m_sMsgsCs); m_sMsgs.clear (); LeaveCriticalSection (&m_sMsgsCs); m_sbMsgsCsInitialized = FALSE; DeleteCriticalSection (&m_sMsgsCs); return dwRes; } // CServerNode::ShutdownMsgsMap CServerNode * CServerNode::LookupServerFromMessageId ( DWORD dwMsgId ) /*++ Routine name : CServerNode::LookupServerFromMessageId Routine description: Given a mesage id, looks up the server this message was sent to Author: Eran Yariv (EranY), Jan, 2000 Arguments: dwMsgId [in] - Message id Return Value: Server node that should process the message or NULL if message id is not mapped --*/ { DBG_ENTER(TEXT("CServerNode::LookupServerFromMessageId"), TEXT("%ld"), dwMsgId); CServerNode *pRes = NULL; if (!m_sbMsgsCsInitialized) { return pRes; } EnterCriticalSection (&m_sMsgsCs); MESSAGES_MAP::iterator it = m_sMsgs.find (dwMsgId); if (m_sMsgs.end() == it) { // // Item not found there // VERBOSE (DBG_MSG, TEXT("Notification message %ld has no associated server"), dwMsgId); } else { pRes = (*it).second; } LeaveCriticalSection (&m_sMsgsCs); return pRes; } // CServerNode::LookupServerFromMessageId DWORD CServerNode::AllocateNewMessageId ( CServerNode *pServer, DWORD &dwMsgId ) /*++ Routine name : CServerNode::AllocateNewMessageId Routine description: Allocates a new message id for server's notification Author: Eran Yariv (EranY), Jan, 2000 Arguments: pServer [in] - Pointer to server dwMsgId [out] - New message id Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::AllocateNewMessageId"), dwRes, TEXT("%s"), pServer->Machine()); if (!m_sbMsgsCsInitialized) { // // Map no longer exists // dwRes = ERROR_SHUTDOWN_IN_PROGRESS; return dwRes; } EnterCriticalSection (&m_sMsgsCs); for (DWORD dw = m_sdwMinFreeMsg; ; dw++) { CServerNode *pSrv = LookupServerFromMessageId (dw); if (!pSrv) { // // Free spot found // dwMsgId = dw; try { m_sMsgs[dwMsgId] = pServer; } catch (...) { // // Not enough memory // dwRes = ERROR_NOT_ENOUGH_MEMORY; CALL_FAIL (MEM_ERR, TEXT("map::operator []"), dwRes); goto exit; } // // Success // VERBOSE (DBG_MSG, TEXT("Server %s registered for notification on message 0x%08x"), pServer->Machine(), dwMsgId); goto exit; } } exit: LeaveCriticalSection (&m_sMsgsCs); return dwRes; } // CServerNode::AllocateNewMessageId DWORD CServerNode::FreeMessageId ( DWORD dwMsgId ) /*++ Routine name : CServerNode::FreeMessageId Routine description: Frees a message id back to the map Author: Eran Yariv (EranY), Jan, 2000 Arguments: dwMsgId [in] - Message id to free Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::FreeMessageId"), dwRes); if (!m_sbMsgsCsInitialized) { // // Map no longer exists // dwRes = ERROR_SHUTDOWN_IN_PROGRESS; return dwRes; } EnterCriticalSection (&m_sMsgsCs); try { m_sMsgs.erase (dwMsgId); } catch (...) { // // Not enough memory // dwRes = ERROR_NOT_ENOUGH_MEMORY; CALL_FAIL (MEM_ERR, TEXT("map::erase"), dwRes); goto exit; } // // Success // VERBOSE (DBG_MSG, TEXT("Server unregistered for notification on message 0x%08x"), dwMsgId); if (dwMsgId < m_sdwMinFreeMsg) { // // Free spot was created lower than before. // m_sdwMinFreeMsg = dwMsgId; } exit: LeaveCriticalSection (&m_sMsgsCs); return dwRes; } // CServerNode::FreeMessageId DWORD CServerNode::Connect() /*++ Routine name : CServerNode::Connect Routine description: Connects to the fax server Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::Connect"), dwRes, TEXT("%s"), m_cstrMachine); ASSERTION (!m_hConnection); START_RPC_TIME(TEXT("FaxConnectFaxServer")); if (!FaxConnectFaxServer ((LPCTSTR)m_cstrMachine, &m_hConnection)) { dwRes = GetLastError (); SetLastRPCError (dwRes, FALSE); // Don't disconnect on error CALL_FAIL (RPC_ERR, TEXT("FaxConnectFaxServer"), dwRes); m_hConnection = NULL; } END_RPC_TIME(TEXT("FaxConnectFaxServer")); return dwRes; } // CServerNode::Connect DWORD CServerNode::Disconnect( BOOL bShutdownAware, BOOL bWaitForBuildThread ) /*++ Routine name : CServerNode::Disconnect Routine description: Disconnects from the server and closes the notification handle. Author: Eran Yariv (EranY), Jan, 2000 Arguments: bShutdownAware [in] - If TRUE, disables disconnection while application is shutting down bWaitForBuildThread [in] - If TRUE, wait for build threads stop Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::Disconnect"), dwRes, TEXT("%s"), m_cstrMachine); if(bWaitForBuildThread) { // // just turn on m_bStopBuildup flag // StopBuildThread (FALSE); m_Inbox.StopBuildThread(FALSE); m_SentItems.StopBuildThread(FALSE); m_Outbox.StopBuildThread(FALSE); m_Incoming.StopBuildThread(FALSE); // // wait for the all threads finish // dwRes = StopBuildThread(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("CServerNode::StopBuildThread"), dwRes); } dwRes = m_Inbox.StopBuildThread(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_Inbox.StopBuildThread"), dwRes); } dwRes = m_SentItems.StopBuildThread(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_SentItems.StopBuildThread"), dwRes); } dwRes = m_Outbox.StopBuildThread(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_Outbox.StopBuildThread"), dwRes); } dwRes = m_Incoming.StopBuildThread(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_Incoming.StopBuildThread"), dwRes); } } if (!m_hConnection) { // // Already disconnected // return dwRes; } if (bShutdownAware && CClientConsoleDoc::ShuttingDown()) { VERBOSE (DBG_MSG, TEXT("Left open connection (and notification) to %s because we're shutting down."), m_cstrMachine); return dwRes; } if (m_hNotification) { // // Unregister server notifications // START_RPC_TIME(TEXT("FaxUnregisterForServerEvents")); if (!FaxUnregisterForServerEvents (m_hNotification)) { dwRes = GetLastError (); END_RPC_TIME(TEXT("FaxUnregisterForServerEvents")); SetLastRPCError (dwRes, FALSE); // Don't disconnect on error CALL_FAIL (RPC_ERR, TEXT("FaxUnregisterForServerEvents"), dwRes); // // Carry on with disconnection // } END_RPC_TIME(TEXT("FaxUnregisterForServerEvents")); // // Free the message id back to the map // dwRes = FreeMessageId (m_dwMsgId); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("FreeMessageId"), dwRes); // // Carry on with disconnection // } } // // Close connection // START_RPC_TIME(TEXT("FaxClose")); if (!FaxClose (m_hConnection)) { dwRes = GetLastError (); END_RPC_TIME(TEXT("FaxClose")); SetLastRPCError (dwRes, FALSE); // Don't disconnect on error CALL_FAIL (RPC_ERR, TEXT("FaxClose"), dwRes); m_hConnection = NULL; return dwRes; } END_RPC_TIME(TEXT("FaxClose")); m_hConnection = NULL; m_hNotification = NULL; return dwRes; } // CServerNode::Disconnect DWORD CServerNode::GetConnectionHandle ( HANDLE &hFax ) /*++ Routine name : CServerNode::GetConnectionHandle Routine description: Retrieves connection handle (re-connects if neeed) Author: Eran Yariv (EranY), Jan, 2000 Arguments: hFax [out] - Connection handle Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::GetConnectionHandle"), dwRes); // // Protect the m_hBuildupThread from CloseHandle() more then once // EnterCriticalSection (&m_csBuildupThread); if (m_hConnection) { // // We already have a live connection // hFax = m_hConnection; goto exit; } // // Refresh server state with first connection. // RefreshState() creates a background thread that will call Connect() // dwRes = RefreshState(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT ("RefreshState"), dwRes); goto exit; } if(NULL != m_hBuildupThread) { // // Wait for that background thread to end // dwRes = WaitForThreadDeathOrShutdown (m_hBuildupThread); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("WaitForThreadDeathOrShutdown"), dwRes); } CloseHandle (m_hBuildupThread); m_hBuildupThread = NULL; } hFax = m_hConnection; if(!hFax) { dwRes = ERROR_INVALID_HANDLE; } exit: LeaveCriticalSection (&m_csBuildupThread); return dwRes; } // CServerNode::GetConnectionHandle DWORD CServerNode::Init ( LPCTSTR tstrMachine ) /*++ Routine name : CServerNode::Init Routine description: Inits server node information Author: Eran Yariv (EranY), Jan, 2000 Arguments: tstrMachine [in] - Server machine name Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::Init"), dwRes); ASSERTION (!m_bCsBuildupValid); ASSERTION (!m_hConnection); // // Create buildup thread critical section // try { InitializeCriticalSection (&m_csBuildup); } catch (...) { dwRes = ERROR_NOT_ENOUGH_MEMORY; CALL_FAIL (MEM_ERR, TEXT ("InitializeCriticalSection(&m_csBuildup)"), dwRes); return dwRes; } m_bCsBuildupValid = TRUE; try { InitializeCriticalSection (&m_csBuildupThread); } catch (...) { dwRes = ERROR_NOT_ENOUGH_MEMORY; CALL_FAIL (MEM_ERR, TEXT ("InitializeCriticalSection(&m_csBuildupThread)"), dwRes); return dwRes; } m_bCsBuildupThreadValid = TRUE; // // Save our connection + server names // try { m_cstrMachine = tstrMachine; // // Remove leading backslashes from machine's name // m_cstrMachine.Remove (TEXT('\\')); } catch (CException *pException) { TCHAR wszCause[1024]; pException->GetErrorMessage (wszCause, 1024); pException->Delete (); VERBOSE (EXCEPTION_ERR, TEXT("CString::operator = caused exception : %s"), wszCause); dwRes = ERROR_NOT_ENOUGH_MEMORY; return dwRes; } dwRes = CreateFolders (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("CreateFolders"), dwRes); } return dwRes; } // CServerNode::Init DWORD CServerNode::SetNewQueueState ( DWORD dwNewState ) /*++ Routine name : CServerNode::SetNewQueueState Routine description: Sets the news state of the queue Author: Eran Yariv (EranY), Jan, 2000 Arguments: dwNewState [in] - New queue state Return Value: Standard Win32 error code --*/ { HANDLE hFax; DWORD dwRes; DBG_ENTER(TEXT("CServerNode::SetNewQueueState"), dwRes); dwRes = GetConnectionHandle(hFax); if (ERROR_SUCCESS != dwRes) { return dwRes; } START_RPC_TIME(TEXT("FaxSetQueue")); if (!FaxSetQueue (hFax, dwNewState)) { dwRes = GetLastError (); END_RPC_TIME(TEXT("FaxSetQueue")); SetLastRPCError (dwRes); CALL_FAIL (RPC_ERR, TEXT("FaxSetQueue"), dwRes); return dwRes; } END_RPC_TIME(TEXT("FaxSetQueue")); return dwRes; } // CServerNode::SetNewQueueState DWORD CServerNode::BlockIncoming ( BOOL bBlock ) /*++ Routine name : CServerNode::BlockIncoming Routine description: Blocks / unblocks the incoming queue Author: Eran Yariv (EranY), Jan, 2000 Arguments: bBlock [in] - TRUE if block Return Value: Standard Win32 error code --*/ { DWORD dwRes; DBG_ENTER(TEXT("CServerNode::BlockIncoming"), dwRes); DWORD dwNewState = bBlock ? (m_dwQueueState | FAX_INCOMING_BLOCKED) : (m_dwQueueState & ~FAX_INCOMING_BLOCKED); dwRes = SetNewQueueState (dwNewState); if (ERROR_SUCCESS == dwRes) { m_dwQueueState = dwNewState; } return dwRes; } // CServerNode::BlockIncoming DWORD CServerNode::BlockOutbox ( BOOL bBlock ) /*++ Routine name : CServerNode::BlockOutbox Routine description: Blocks / unblocks the outgoing queue Author: Eran Yariv (EranY), Jan, 2000 Arguments: bBlock [in] - TRUE if block Return Value: Standard Win32 error code --*/ { DWORD dwRes; DBG_ENTER(TEXT("CServerNode::BlockOutbox"), dwRes); DWORD dwNewState = bBlock ? (m_dwQueueState | FAX_OUTBOX_BLOCKED) : (m_dwQueueState & ~FAX_OUTBOX_BLOCKED); dwRes = SetNewQueueState (dwNewState); if (ERROR_SUCCESS == dwRes) { m_dwQueueState = dwNewState; } return dwRes; } // CServerNode::BlockOutbox DWORD CServerNode::PauseOutbox ( BOOL bPause ) /*++ Routine name : CServerNode::PauseOutbox Routine description: Pauses / resumes the outgoing queue Author: Eran Yariv (EranY), Jan, 2000 Arguments: bPause [in] - TRUE if pause Return Value: Standard Win32 error code --*/ { DWORD dwRes; DBG_ENTER(TEXT("CServerNode::PauseOutbox"), dwRes); DWORD dwNewState = bPause ? (m_dwQueueState | FAX_OUTBOX_PAUSED) : (m_dwQueueState & ~FAX_OUTBOX_PAUSED); dwRes = SetNewQueueState (dwNewState); if (ERROR_SUCCESS == dwRes) { m_dwQueueState = dwNewState; } return dwRes; } // CServerNode::PauseOutbox DWORD CServerNode::CreateFolders () /*++ Routine name : CServerNode::CreateFolders Routine description: Creates the 4 folders of the server Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::CreateFolders"), dwRes); // // Create inbox folder // m_Inbox.SetServer(this); dwRes = m_Inbox.Init (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (WINDOW_ERR, TEXT("CMessageFolder::Init"), dwRes); goto error; } // // Create outbox folder // m_Outbox.SetServer(this); dwRes = m_Outbox.Init (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (WINDOW_ERR, TEXT("CQueueFolder::Init"), dwRes); goto error; } // // Create sentitems folder // m_SentItems.SetServer(this); dwRes = m_SentItems.Init (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (WINDOW_ERR, TEXT("CMessageFolder::Init"), dwRes); goto error; } // // Create incoming folder // m_Incoming.SetServer(this); dwRes = m_Incoming.Init (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (WINDOW_ERR, TEXT("CQueueFolder::Init"), dwRes); goto error; } ASSERTION (ERROR_SUCCESS == dwRes); error: return dwRes; } // CServerNode::CreateFolders DWORD CServerNode::InvalidateSubFolders ( BOOL bClearView ) /*++ Routine name : CServerNode::InvalidateSubFolders Routine description: Invalidates the contents of all 4 sub folders Author: Eran Yariv (EranY), Jan, 2000 Arguments: bClearView [in] Should we clear attached view ? Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::InvalidateSubFolders"), dwRes); dwRes = m_Inbox.InvalidateContents (bClearView); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_Inbox.InvalidateContents"), dwRes); return dwRes; } dwRes = m_Outbox.InvalidateContents (bClearView); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_Outbox.InvalidateContents"), dwRes); return dwRes; } dwRes = m_SentItems.InvalidateContents (bClearView); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_SentItems.InvalidateContents"), dwRes); return dwRes; } dwRes = m_Incoming.InvalidateContents (bClearView); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("m_Incoming.InvalidateContents"), dwRes); return dwRes; } return dwRes; } // CServerNode::InvalidateSubFolders // // Buildup thread functions: // DWORD CServerNode::ClearContents () /*++ Routine name : CServerNode::ClearContents Routine description: Clears the server's contents Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes; DBG_ENTER(TEXT("CServerNode::ClearContents"), dwRes); dwRes = Disconnect (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (RPC_ERR, TEXT("Disconnect"), dwRes); return dwRes; } dwRes = InvalidateSubFolders(FALSE); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (RPC_ERR, TEXT("InvalidateSubFolders"), dwRes); return dwRes; } return dwRes; } // CServerNode::ClearContents DWORD CServerNode::RefreshState() /*++ Routine name : CServerNode::RefreshState Routine description: Refreshes the server's state Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes; DBG_ENTER(TEXT("CServerNode::RefreshState"), dwRes, TEXT("%s"), m_cstrMachine); DWORD dwThreadId; // // Stop the current (if any) buildup thread and make sure it's dead // dwRes = StopBuildThread (); EnterCriticalSection (&m_csBuildup); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (RESOURCE_ERR, TEXT("CServerNode::StopBuildThread"), dwRes); goto exit; } // // Tell our view to refresh our image // m_bInBuildup = TRUE; // // Start the thread that will fill the data (in the background) // m_bStopBuildup = FALSE; m_hBuildupThread = CreateThread ( NULL, // No security 0, // Default stack size BuildupThreadProc, // Thread procedure (LPVOID)this, // Parameter 0, // Normal creation &dwThreadId // We must have a thread id for win9x ); if (NULL == m_hBuildupThread) { dwRes = GetLastError (); CALL_FAIL (RESOURCE_ERR, TEXT("CreateThread"), dwRes); goto exit; } ASSERTION (ERROR_SUCCESS == dwRes); exit: LeaveCriticalSection (&m_csBuildup); if (ERROR_SUCCESS != dwRes) { // // Build up failed // m_bInBuildup = FALSE; } return dwRes; } // CServerNode::RefreshState DWORD CServerNode::Buildup () /*++ Routine name : CServerNode::Buildup Routine description: Server refresh worker thread function. Works in a background thread and performs the following: 1. FaxConnectFaxServer (if not already connected) 2. FaxGetQueueStates 3. FaxAccessCheckEx (MAXIMUM_ALLOWED) 4. FaxRegisterForServerEvents Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::Buildup"), dwRes, TEXT("%s"), m_cstrMachine); HANDLE hFax; HWND hwnd; CMainFrame *pMainFrm = NULL; DWORD dwEventTypes = FAX_EVENT_TYPE_OUT_QUEUE | // Outbox events FAX_EVENT_TYPE_QUEUE_STATE | // Paused / blocked queue events FAX_EVENT_TYPE_OUT_ARCHIVE | // SentItems events FAX_EVENT_TYPE_FXSSVC_ENDED; // Server shutdown events // // get connection // if (m_hConnection) { hFax = m_hConnection; } else { dwRes = Connect (); if (ERROR_SUCCESS != dwRes) { goto exit; } ASSERTION (m_hConnection); hFax = m_hConnection; } if (m_bStopBuildup) { // // Thread should stop abruptly // dwRes = ERROR_CANCELLED; goto exit; } { START_RPC_TIME(TEXT("FaxGetQueueStates")); if (!FaxGetQueueStates (hFax, &m_dwQueueState)) { dwRes = GetLastError (); END_RPC_TIME(TEXT("FaxGetQueueStates")); SetLastRPCError (dwRes); CALL_FAIL (RPC_ERR, TEXT("FaxGetQueueStates"), dwRes); goto exit; } END_RPC_TIME(TEXT("FaxGetQueueStates")); } if (m_bStopBuildup) { // // Thread should stop abruptly // dwRes = ERROR_CANCELLED; goto exit; } { // // retrieve the access rights of the caller // START_RPC_TIME(TEXT("FaxAccessCheckEx")); if (!FaxAccessCheckEx (hFax, MAXIMUM_ALLOWED, &m_dwRights)) { dwRes = GetLastError (); END_RPC_TIME(TEXT("FaxAccessCheckEx")); SetLastRPCError (dwRes); CALL_FAIL (RPC_ERR, TEXT("FaxAccessCheckEx"), dwRes); goto exit; } END_RPC_TIME(TEXT("FaxAccessCheckEx")); } if (m_bStopBuildup) { // // Thread should stop abruptly // dwRes = ERROR_CANCELLED; goto exit; } // // Register for notifications - start by allocating a message id // if(m_hNotification) { // // already registered // goto exit; } dwRes = AllocateNewMessageId (this, m_dwMsgId); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("AllocateNewMessageId"), dwRes); goto exit; } // // Ask the server for a notification handle // pMainFrm = GetFrm(); if (NULL == pMainFrm) { // // No main frame - probably we're shutting down // goto exit; } hwnd = pMainFrm->m_hWnd; if (CanSeeAllJobs()) { dwEventTypes |= FAX_EVENT_TYPE_IN_QUEUE; // Incoming folder events } if (CanSeeInbox()) { dwEventTypes |= FAX_EVENT_TYPE_IN_ARCHIVE; // Inbox folder events } { START_RPC_TIME(TEXT("FaxRegisterForServerEvents")); if (!FaxRegisterForServerEvents ( hFax, dwEventTypes, // Types of events to receive NULL, // Not using completion ports 0, // Not using completion ports hwnd, // Handle of window to receive notification messages m_dwMsgId, // Message id &m_hNotification// Notification handle )) { dwRes = GetLastError (); SetLastRPCError (dwRes, FALSE); // Do not auto-disconnect CALL_FAIL (RPC_ERR, TEXT("FaxRegisterForServerEvents"), dwRes); m_hNotification = NULL; goto exit; } END_RPC_TIME(TEXT("FaxRegisterForServerEvents")); } ASSERTION (ERROR_SUCCESS == dwRes); exit: m_bInBuildup = FALSE; if (!m_bStopBuildup) { if (ERROR_SUCCESS != dwRes) { // // Some error occured during refresh // Disconnect (FALSE, FALSE); } } // // Check if the frame still alive // pMainFrm = GetFrm(); if (pMainFrm) { pMainFrm->RefreshStatusBar (); } return dwRes; } // CServerNode::Buildup DWORD WINAPI CServerNode::BuildupThreadProc ( LPVOID lpParameter ) /*++ Routine name : CServerNode::BuildupThreadProc Routine description: Server refresh thread entry point. This is a static function which accepts a pointer to the actual CServerNode instance and calls the Buildup function on the real instance. Author: Eran Yariv (EranY), Jan, 2000 Arguments: lpParameter [in] - Pointer to server node that created the thread Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::BuildupThreadProc"), dwRes); CServerNode *pServer = (CServerNode *)lpParameter; ASSERTION (pServer); ASSERT_KINDOF (CServerNode, pServer); dwRes = pServer->Buildup (); if (pServer->m_bSelfDestruct) { // // Object was waiting for thread to stop before it could destruct itself // delete pServer; } return dwRes; } // CServerNode::BuildupThreadProc DWORD CServerNode::StopBuildThread ( BOOL bWaitForDeath ) /*++ Routine name : CServerNode::StopBuildThread Routine description: Stops the server's buildup thread Author: Eran Yariv (EranY), Jan, 2000 Arguments: bWaitForDeath [in] - If TRUE, waits until the thread is dead Return Value: Standard Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::StopBuildThread"), dwRes); m_bStopBuildup = TRUE; if (!bWaitForDeath) { return dwRes; } // // Protect the m_hBuildupThread from CloseHandle() more then once // if(!m_bCsBuildupThreadValid) { return dwRes; } EnterCriticalSection (&m_csBuildupThread); if(NULL == m_hBuildupThread) { goto exit; } // // Wait for build thread to die // dwRes = WaitForThreadDeathOrShutdown (m_hBuildupThread); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("WaitForThreadDeathOrShutdown"), dwRes); } CloseHandle (m_hBuildupThread); m_hBuildupThread = NULL; exit: LeaveCriticalSection (&m_csBuildupThread); return dwRes; } // CServerNode::StopBuildThread BOOL CServerNode::FatalRPCError ( DWORD dwErr ) /*++ Routine name : CServerNode::FatalRPCError Routine description: Checks if an error code means a fatal RPC connection state Author: Eran Yariv (EranY), Feb, 2000 Arguments: dwErr [in] - Error code to check Return Value: TRUE if error code means a fatal RPC connection state, FALSE otherwise. --*/ { BOOL bRes = FALSE; DBG_ENTER(TEXT("CServerNode::FatalRPCError"), bRes); switch (bRes) { case RPC_S_INVALID_BINDING: case EPT_S_CANT_PERFORM_OP: case RPC_S_ADDRESS_ERROR: case RPC_S_COMM_FAILURE: case RPC_S_NO_BINDINGS: case RPC_S_SERVER_UNAVAILABLE: // // Something really bad happened to our RPC connection // bRes = TRUE; break; } return bRes; } // CServerNode::FatalRPCError void CServerNode::SetLastRPCError ( DWORD dwErr, BOOL DisconnectOnFailure ) /*++ Routine name : CServerNode::SetLastRPCError Routine description: Sets the last RPC error encountered on this server Author: Eran Yariv (EranY), Jan, 2000 Arguments: dwErr [in] - Error code DisconnectOnFailure [in] - If TRUE, disconnects from the server upon error Return Value: None. --*/ { DBG_ENTER(TEXT("CServerNode::SetLastRPCError"), TEXT("%ld"), dwErr); m_dwLastRPCError = dwErr; if (DisconnectOnFailure && FatalRPCError(dwErr)) { // // We have a real failure here - disconnect now. // DWORD dwRes = Disconnect (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (RPC_ERR, TEXT("CServerNode::Disconnect"), dwRes); } } } // CServerNode::SetLastRPCError void CServerNode::Destroy () /*++ Routine name : CServerNode::Destroy Routine description: Destroys the server's node. Since the dtor is private, this is the only way to destroy the server node. If the server is not busy refreshing itself, it deletes itself. Otherwise, it signals a suicide request and the thread destorys the node. Author: Eran Yariv (EranY), Jan, 2000 Arguments: Return Value: None. --*/ { if (m_bSelfDestruct) { // // Already being destroyed // return; } EnterCriticalSection (&m_csBuildup); m_bSelfDestruct = TRUE; if (m_hBuildupThread) { // // Thread is running, just mark request for self-destruction // LeaveCriticalSection (&m_csBuildup); } else { // // Suicide // LeaveCriticalSection (&m_csBuildup); try { delete this; } catch (...) { DBG_ENTER(TEXT("CServerNode::Destroy")); CALL_FAIL (GENERAL_ERR, TEXT("CServerNode::Destructor exception"), 0); ASSERTION_FAILURE; } } } // CServerNode::Destroy DWORD CServerNode::GetActivity( CString& cstrText, TreeIconType& iconIndex ) const /*++ Routine name : CServerNode::GetActivityStringResource Routine description: Returns the resource id of a string identifying the activity of the server Author: Eran Yariv (EranY), Jan, 2000 Arguments: cstrText [out] - activity string iconIndex [out] - icon index Return Value: Activity string resource id --*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::GetActivity"), dwRes); DWORD dwStrRes = 0; if (IsRefreshing()) { iconIndex = TREE_IMAGE_SERVER_REFRESHING; dwStrRes = IDS_SERVER_REFRESHING; } else if (IsOnline ()) { if (IsOutboxBlocked()) { // // Server's Outgoing queue is blocked // iconIndex = TREE_IMAGE_OUTBOX_BLOCKED; dwStrRes = IDS_SERVER_OUTBOX_BLOCKED; } else if (IsOutboxPaused()) { // // Server's Outgoing queue is paused // iconIndex = TREE_IMAGE_OUTBOX_PAUSED; dwStrRes = IDS_SERVER_OUTBOX_PAUSED; } else { // // Server's Outgoing queue is fully functional // iconIndex = TREE_IMAGE_SERVER_ONLINE; dwStrRes = IDS_SERVER_ONLINE; } } else { iconIndex = TREE_IMAGE_SERVER_OFFLINE; // // Server is offline // if (RPC_S_SERVER_UNAVAILABLE == m_dwLastRPCError) { // // The RPC server is unavailable. // dwStrRes = IDS_SERVER_OFFLINE; } else { // // General network / RPC error // dwStrRes = IDS_SERVER_NET_ERROR; } } dwRes = LoadResourceString(cstrText, dwStrRes); if(ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("LoadResourceString"), dwRes); return dwRes; } return dwRes; } // CServerNode::GetActivityStringResource DWORD CServerNode::OnNotificationMessage ( PFAX_EVENT_EX pEvent ) { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CServerNode::OnNotificationMessage"), dwRes); ASSERTION (pEvent); switch (pEvent->EventType) { case FAX_EVENT_TYPE_IN_QUEUE: // // Something happened in the incoming folder // if (m_Incoming.Locked() || !m_Incoming.IsValid()) { // // Folder is locked or invalid - do not process notifications // dwRes = ERROR_LOCK_VIOLATION; VERBOSE (DBG_MSG, TEXT("Incoming folder is locked or invalid - notification is NOT processed")); return dwRes; } switch (pEvent->EventInfo.JobInfo.Type) { case FAX_JOB_EVENT_TYPE_ADDED: // // A job was added // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_IN_QUEUE / FAX_JOB_EVENT_TYPE_ADDED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_Incoming.OnJobAdded (pEvent->EventInfo.JobInfo.dwlMessageId); break; case FAX_JOB_EVENT_TYPE_REMOVED: // // A job was removed // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_IN_QUEUE / FAX_JOB_EVENT_TYPE_REMOVED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_Incoming.OnJobRemoved (pEvent->EventInfo.JobInfo.dwlMessageId); break; case FAX_JOB_EVENT_TYPE_STATUS: // // A job has changed its status // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_IN_QUEUE / FAX_JOB_EVENT_TYPE_STATUS") TEXT(" / 0x%016I64x (status = 0x%08x)"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId, pEvent->EventInfo.JobInfo.pJobData->dwQueueStatus); if((pEvent->EventInfo.JobInfo.pJobData->dwQueueStatus & JS_COMPLETED) || (pEvent->EventInfo.JobInfo.pJobData->dwQueueStatus & JS_CANCELED)) { // // don't display completed or canceled jobs // dwRes = m_Incoming.OnJobRemoved (pEvent->EventInfo.JobInfo.dwlMessageId); } else { dwRes = m_Incoming.OnJobUpdated (pEvent->EventInfo.JobInfo.dwlMessageId, pEvent->EventInfo.JobInfo.pJobData); } break; default: dwRes = ERROR_GEN_FAILURE; VERBOSE (DBG_MSG, TEXT("Got unknown server notification from %s - ") TEXT("FAX_EVENT_TYPE_IN_QUEUE / %d / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.Type, pEvent->EventInfo.JobInfo.dwlMessageId); break; } break; case FAX_EVENT_TYPE_OUT_QUEUE: // // Something happened in the outbox folder // if (m_Outbox.Locked() || !m_Outbox.IsValid()) { // // Folder is locked or invalid - do not process notifications // dwRes = ERROR_LOCK_VIOLATION; VERBOSE (DBG_MSG, TEXT("Outbox folder is locked or invalid - notification is NOT processed")); return dwRes; } switch (pEvent->EventInfo.JobInfo.Type) { case FAX_JOB_EVENT_TYPE_ADDED: // // A job was added // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_OUT_QUEUE / FAX_JOB_EVENT_TYPE_ADDED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_Outbox.OnJobAdded (pEvent->EventInfo.JobInfo.dwlMessageId); break; case FAX_JOB_EVENT_TYPE_REMOVED: // // A job was removed // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_OUT_QUEUE / FAX_JOB_EVENT_TYPE_REMOVED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_Outbox.OnJobRemoved (pEvent->EventInfo.JobInfo.dwlMessageId); break; case FAX_JOB_EVENT_TYPE_STATUS: // // A job has changed its status // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_OUT_QUEUE / FAX_JOB_EVENT_TYPE_STATUS") TEXT(" / 0x%016I64x (status = 0x%08x)"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId, pEvent->EventInfo.JobInfo.pJobData->dwQueueStatus); if((pEvent->EventInfo.JobInfo.pJobData->dwQueueStatus & JS_COMPLETED) || (pEvent->EventInfo.JobInfo.pJobData->dwQueueStatus & JS_CANCELED)) { // // don't display completed or canceled jobs // dwRes = m_Outbox.OnJobRemoved (pEvent->EventInfo.JobInfo.dwlMessageId); } else { dwRes = m_Outbox.OnJobUpdated (pEvent->EventInfo.JobInfo.dwlMessageId, pEvent->EventInfo.JobInfo.pJobData); } break; default: dwRes = ERROR_GEN_FAILURE; VERBOSE (DBG_MSG, TEXT("Got unknown server notification from %s - ") TEXT("FAX_EVENT_TYPE_OUT_QUEUE / %d / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.Type, pEvent->EventInfo.JobInfo.dwlMessageId); break; } break; case FAX_EVENT_TYPE_QUEUE_STATE: // // Queue states have changed. // Update internal data with new queue states. // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - FAX_EVENT_TYPE_QUEUE_STATE / %d"), m_cstrMachine, pEvent->EventInfo.dwQueueStates); // // Assert valid values only // ASSERTION (0 == (pEvent->EventInfo.dwQueueStates & ~(FAX_INCOMING_BLOCKED | FAX_OUTBOX_BLOCKED | FAX_OUTBOX_PAUSED))); m_dwQueueState = pEvent->EventInfo.dwQueueStates; break; case FAX_EVENT_TYPE_IN_ARCHIVE: // // Something happened in the Inbox folder // if (m_Inbox.Locked() || !m_Inbox.IsValid()) { // // Folder is locked or invalid - do not process notifications // dwRes = ERROR_LOCK_VIOLATION; VERBOSE (DBG_MSG, TEXT("Inbox folder is locked or invalid - notification is NOT processed")); return dwRes; } switch (pEvent->EventInfo.JobInfo.Type) { case FAX_JOB_EVENT_TYPE_ADDED: // // A message was added // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_IN_ARCHIVE / FAX_JOB_EVENT_TYPE_ADDED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_Inbox.OnJobAdded (pEvent->EventInfo.JobInfo.dwlMessageId); break; case FAX_JOB_EVENT_TYPE_REMOVED: // // A message was removed // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_IN_ARCHIVE / FAX_JOB_EVENT_TYPE_REMOVED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_Inbox.OnJobRemoved (pEvent->EventInfo.JobInfo.dwlMessageId); break; default: dwRes = ERROR_GEN_FAILURE; VERBOSE (DBG_MSG, TEXT("Got unknown server notification from %s - ") TEXT("FAX_EVENT_TYPE_IN_ARCHIVE / %d / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.Type, pEvent->EventInfo.JobInfo.dwlMessageId); break; } break; case FAX_EVENT_TYPE_OUT_ARCHIVE: // // Something happened in the SentItems folder // if (m_SentItems.Locked() || !m_SentItems.IsValid()) { // // Folder is locked or invalid - do not process notifications // dwRes = ERROR_LOCK_VIOLATION; VERBOSE (DBG_MSG, TEXT("SentItems folder is locked or invalid - notification is NOT processed")); return dwRes; } switch (pEvent->EventInfo.JobInfo.Type) { case FAX_JOB_EVENT_TYPE_ADDED: // // A message was added // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_OUT_ARCHIVE / FAX_JOB_EVENT_TYPE_ADDED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_SentItems.OnJobAdded (pEvent->EventInfo.JobInfo.dwlMessageId); break; case FAX_JOB_EVENT_TYPE_REMOVED: // // A message was removed // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - ") TEXT("FAX_EVENT_TYPE_OUT_ARCHIVE / FAX_JOB_EVENT_TYPE_REMOVED") TEXT(" / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.dwlMessageId); dwRes = m_SentItems.OnJobRemoved (pEvent->EventInfo.JobInfo.dwlMessageId); break; default: dwRes = ERROR_GEN_FAILURE; VERBOSE (DBG_MSG, TEXT("Got unknown server notification from %s - ") TEXT("FAX_EVENT_TYPE_OUT_ARCHIVE / %d / 0x%016I64x"), m_cstrMachine, pEvent->EventInfo.JobInfo.Type, pEvent->EventInfo.JobInfo.dwlMessageId); break; } break; case FAX_EVENT_TYPE_FXSSVC_ENDED: // // Fax service is shutting down // VERBOSE (DBG_MSG, TEXT("Got server notification from %s - FAX_EVENT_TYPE_FXSSVC_ENDED"), m_cstrMachine); dwRes = Disconnect (); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("Disconnect"), dwRes); } dwRes = InvalidateSubFolders (TRUE); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("InvalidateSubFolders"), dwRes); } break; default: dwRes = ERROR_GEN_FAILURE; VERBOSE (DBG_MSG, TEXT("Got unknown server notification from %s - %d"), m_cstrMachine, pEvent->EventType); break; } return dwRes; } // CServerNode::OnNotificationMessage CFolder* CServerNode::GetFolder(FolderType type) { CFolder* pFolder=NULL; switch(type) { case FOLDER_TYPE_INBOX: pFolder = &m_Inbox; break; case FOLDER_TYPE_OUTBOX: pFolder = &m_Outbox; break; case FOLDER_TYPE_SENT_ITEMS: pFolder = &m_SentItems; break; case FOLDER_TYPE_INCOMING: pFolder = &m_Incoming; break; default: { DBG_ENTER(TEXT("CServerNode::GetFolder")); ASSERTION_FAILURE } break; } return pFolder; }