/*++ Copyright (c) 1999 Microsoft Corporation Module Name: ExtensionData.cpp Abstract: This file provides implementation of the named extension data functions (get / set / notify) Author: Eran Yariv (EranY) Nov, 1999 Revision History: --*/ #include "faxsvc.h" #pragma hdrstop static DWORD FAXGetExtensionData ( IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN OUT LPBYTE *ppData, IN OUT LPDWORD lpdwDataSize ); static DWORD FAXSetExtensionData ( IN HINSTANCE hInst, IN LPCWSTR lpcwstrComputerName, IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN LPBYTE pData, IN DWORD dwDataSize ); static BOOL FindTAPIPermanentLineIdFromFaxDeviceId ( IN DWORD dwFaxDeviceId, OUT LPDWORD lpdwTapiLineId ); BOOL CExtNotifyCallbackPacket::Init( PFAX_EXT_CONFIG_CHANGE pCallback, DWORD dwDeviceId, LPCWSTR lpcwstrDataGuid, LPBYTE lpbData, DWORD dwDataSize) { DEBUG_FUNCTION_NAME(TEXT("CExtNotifyCallbackPacket::Init")); DWORD ec = ERROR_SUCCESS; Assert(pCallback); Assert(lpcwstrDataGuid); Assert(lpbData); Assert(dwDataSize); Assert(m_lpbData == NULL); m_pCallback = pCallback; m_dwDeviceId = dwDeviceId; m_lpwstrGUID = StringDup (lpcwstrDataGuid); if (!m_lpwstrGUID) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Cannot allocate memory to copy string %s"), lpcwstrDataGuid); goto Error; } m_dwDataSize = dwDataSize; m_lpbData = (LPBYTE)MemAlloc(m_dwDataSize); if (!m_lpbData) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocate data for callback packet. Size (%ld)"), m_dwDataSize); goto Error; } memcpy(m_lpbData, lpbData, m_dwDataSize); goto Exit; Error: MemFree(m_lpwstrGUID); MemFree(m_lpbData); m_lpwstrGUID = NULL; m_lpbData = NULL; Exit: return (ERROR_SUCCESS == ec); }; CExtNotifyCallbackPacket::CExtNotifyCallbackPacket() { m_lpwstrGUID = NULL; m_lpbData = NULL; } CExtNotifyCallbackPacket::~CExtNotifyCallbackPacket() { MemFree(m_lpwstrGUID); MemFree(m_lpbData); } /************************************ * * * CDeviceAndGUID * * * ************************************/ bool CDeviceAndGUID::operator < ( const CDeviceAndGUID &other ) const /*++ Routine name : operator < Class: CDeviceAndGUID Routine description: Compares myself with another Device and GUID key Author: Eran Yariv (EranY), Nov, 1999 Arguments: other [in] - Other key Return Value: true only is i'm less than the other key --*/ { if (m_dwDeviceId < other.m_dwDeviceId) { return true; } if (m_dwDeviceId > other.m_dwDeviceId) { return false; } // // Equal device id, comapre GUIDs // return (m_strGUID.compare (other.m_strGUID) < 0); } // CDeviceAndGUID::operator < /************************************ * * * CLocalNotificationSink * * * ************************************/ CLocalNotificationSink::CLocalNotificationSink ( PFAX_EXT_CONFIG_CHANGE lpConfigChangeCallback, DWORD dwNotifyDeviceId, HINSTANCE hInst) : CNotificationSink (), m_lpConfigChangeCallback (lpConfigChangeCallback), m_dwNotifyDeviceId (dwNotifyDeviceId), m_hInst (hInst) /*++ Routine name : CLocalNotificationSink::CLocalNotificationSink Class: CLocalNotificationSink Routine description: CEtensionNotificationSink constractor Author: Eran Yariv (EranY), Nov, 1999 Arguments: lpConfigChangeCallback [in] - Pointer to notification callback dwNotifyDeviceId [in] - Device id to notify with Return Value: None. --*/ { m_type = SINK_TYPE_LOCAL; } // CLocalNotificationSink::CLocalNotificationSink bool CLocalNotificationSink::operator == ( const CNotificationSink &rhs ) const { Assert (SINK_TYPE_UNKNOWN != rhs.Type()); // // Comapre types and then downcast to CLocalNotificationSink and compare pointers // return ((SINK_TYPE_LOCAL == rhs.Type()) && (m_lpConfigChangeCallback == (static_cast(rhs)).m_lpConfigChangeCallback ) ); } // CLocalNotificationSink::operator == HRESULT CLocalNotificationSink::Notify ( DWORD dwDeviceId, LPCWSTR lpcwstrNameGUID, LPCWSTR lpcwstrComputerName, HANDLE hModule, LPBYTE lpData, DWORD dwDataSize, LPBOOL lpbRemove ) /*++ Routine name : CLocalNotificationSink::Notify Class: CLocalNotificationSink Routine description: Notify the sink Author: Eran Yariv (EranY), Nov, 1999 Arguments: dwDeviceId [in ] - Device id lpcwstrNameGUID [in ] - Data name lpData [in ] - Pointer to data dwDataSize [in ] - Data size lpbRemove [out] - Set to TRUE if this sink cannot be used and must be removed. Return Value: Standard HRESULT. --*/ { HRESULT hr = NOERROR; CExtNotifyCallbackPacket * pCallbackPacket = NULL; DEBUG_FUNCTION_NAME(TEXT("CLocalNotificationSink::Notify")); Assert (m_lpConfigChangeCallback); // Should have caught it in FaxExtRegisterForExtensionEvents *lpbRemove = FALSE; if (!lstrcmp (TEXT(""), lpcwstrComputerName)) { // // The source of the data change was local (extension) // if (hModule == m_hInst) { // // The source of the data change is the same module this sink notifies to. // Don't notify and return success. // DebugPrintEx( DEBUG_MSG, TEXT("Local extension (hInst = %ld) set data and the notification for it was blocked"), m_hInst); return hr; } } pCallbackPacket = new (std::nothrow) CExtNotifyCallbackPacket(); if (!pCallbackPacket) { DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocate callback packet")); hr = HRESULT_FROM_WIN32(GetLastError()); goto Error; } if (!pCallbackPacket->Init( m_lpConfigChangeCallback, m_dwNotifyDeviceId, lpcwstrNameGUID, lpData, dwDataSize)) { DWORD ec; ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Failed to initialize callback packet (ec: %ld)"), ec); hr = HRESULT_FROM_WIN32(ec); goto Error; } if (!PostQueuedCompletionStatus ( g_pNotificationMap->m_hCompletionPort, 0, 0, (LPOVERLAPPED)pCallbackPacket )) { DWORD dwRes; dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("PostQueuedCompletionStatus failed. (ec: %ld)"), dwRes); hr = HRESULT_FROM_WIN32(dwRes); goto Error; } goto Exit; Error: if (pCallbackPacket) { delete pCallbackPacket; } Exit: return hr; } // CLocalNotificationSink::Notify /************************************ * * * CSinksList * * * ************************************/ CSinksList::~CSinksList () { DEBUG_FUNCTION_NAME(TEXT("CSinksList::~CSinksList")); try { for (SINKS_LIST::iterator it = m_List.begin(); it != m_List.end(); ++it) { CNotificationSink *pSink = *it; delete pSink; } } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("Got an STL exception while clearing a sinks list (%S)"), ex.what()); } } // CSinksList::~CSinksList () /************************************ * * * CNotificationMap * * * ************************************/ CNotificationMap::~CNotificationMap () { DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::~CNotificationMap")); try { for (NOTIFY_MAP::iterator it = m_Map.begin(); it != m_Map.end(); ++it) { CSinksList *pSinksList = (*it).second; delete pSinksList; } } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("Got an STL exception while clearing the notifications map (%S)"), ex.what()); } // // Handle our completion port threads now // if (m_hCompletionPort) { CloseHandle (m_hCompletionPort); } // // Close our critical section // m_CsExtensionData.SafeDelete(); } // CNotificationMap::~CNotificationMap void CNotificationMap::Notify ( DWORD dwDeviceId, LPCWSTR lpcwstrNameGUID, LPCWSTR lpcwstrComputerName, HANDLE hModule, LPBYTE lpData, DWORD dwDataSize) /*++ Routine name : CNotificationMap::Notify Class: CNotificationMap Routine description: Notify the all the sinks (in a list) for a map lookup value. Each sink that returns a failure code (FALSE) is deleted and removed from the list. After the list is traversed, if it becomes empty, it is deleted and removed from the map. Author: Eran Yariv (EranY), Nov, 1999 Arguments: dwDeviceId [in] - Device id lpcwstrNameGUID [in] - Data name lpcwstrComputerName [in] - Computer where data changing module runs hModule [in] - Handle of the module that changed the data lpData [in] - Pointer to new data dwDataSize [in] - New data size Return Value: None. --*/ { SINKS_LIST::iterator ListIter; CSinksList *pList; DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::Notify")); // // We're notifying now - block calls to Add*Sink and Remove*Sink // if (g_bServiceIsDown) { // // We don't supply extension data services when the service is going down // DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return; } Assert (!m_bNotifying); m_bNotifying = TRUE; CDeviceAndGUID key (dwDeviceId, lpcwstrNameGUID); NOTIFY_MAP::iterator it; if((it = m_Map.find(key)) == m_Map.end()) { // // Key not found in map - no one to notify // DebugPrintEx( DEBUG_MSG, TEXT("No one to notify")); goto exit; } // // Retrieve list // pList = (*it).second; // // If the list is already being notified, we're in a loop here - quit now // if (pList->m_bNotifying) { // // OK, here's what happened. // We were walking the list and notifying each sink. One sink, while processing // it's notification, called FaxExtSetData on the same GUID + device ID. // This resulted in a 2nd notification attempt that we're now catching. // The second notification will not be sent !!! // DebugPrintEx( DEBUG_MSG, TEXT("Notification loop caught on device ID = %ld, GUID = %s. 2nd notification cancelled"), dwDeviceId, lpcwstrNameGUID); goto exit; } // // Mark map value as busy notifying // pList->m_bNotifying = TRUE; // // Walk the list and notify each element // for (ListIter = pList->m_List.begin(); ListIter != pList->m_List.end(); ++ListIter) { CNotificationSink *pSink = (*ListIter); BOOL bRemove; pSink->Notify ( dwDeviceId, lpcwstrNameGUID, lpcwstrComputerName, hModule, lpData, dwDataSize, &bRemove ); if (bRemove) { // // The notification indicates that the sink became invalid. // This is a good time to remove it from the list. // // // Tell the sink to gracefully disconnect // HRESULT hr = pSink->Disconnect (); delete pSink; // // Remove item from list, advancing the iterator to next item (or end) // ListIter = pList->m_List.erase (ListIter); } } // // Mark map value as not busy notifying // pList->m_bNotifying = FALSE; // // We might get an empty list here at the end // if (pList->m_List.empty()) { // // Remove empty list from map // delete pList; m_Map.erase (key); } exit: // // We're not notifying any more - allow calls to Add*Sink and Remove*Sink // m_bNotifying = FALSE; } // CNotificationMap::Notify CNotificationSink * CNotificationMap::AddLocalSink ( HINSTANCE hInst, DWORD dwDeviceId, DWORD dwNotifyDeviceId, LPCWSTR lpcwstrNameGUID, PFAX_EXT_CONFIG_CHANGE lpConfigChangeCallback ) /*++ Routine name : CNotificationMap::AddLocalSink Class: CNotificationMap Routine description: Adds a local sink to the list of sinks for a given device id + GUID Author: Eran Yariv (EranY), Nov, 1999 Arguments: hInst [in] - Instance of extension dwDeviceId [in] - Device id to listen to dwNotifyDeviceId [in] - Device id to report in callback lpcwstrNameGUID [in] - Data name lpConfigChangeCallback [in] - Pointer to notification callback Return Value: Pointer to newly created sink. If NULL, sets the last error. --*/ { DWORD dwRes = ERROR_SUCCESS; SINKS_LIST::iterator ListIter; NOTIFY_MAP::iterator it; CSinksList *pList; CNotificationSink *pSink = NULL; DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::AddLocalSink")); Assert (lpConfigChangeCallback); // Should have caught it in FaxExtRegisterForExtensionEvents if (m_bNotifying) { // // We're notifying now - can't change the list. // DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to add a local sink to a notification list while notifying")); SetLastError (ERROR_BUSY); // The requested resource is in use. return NULL; } // // See if entry exists in map // CDeviceAndGUID key (dwDeviceId, lpcwstrNameGUID); if((it = m_Map.find(key)) == m_Map.end()) { // // Key not found in map - add it with a new list // pList = new (std::nothrow) CSinksList; if (!pList) { DebugPrintEx( DEBUG_ERR, TEXT("Cannot allocate a new sinks list")); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; } m_Map[key] = pList; } else { // // Get the existing list // pList = (*it).second; } // // Create new sink // pSink = new (std::nothrow) CLocalNotificationSink (lpConfigChangeCallback, dwNotifyDeviceId, hInst); if (!pSink) { // // Can't crate sink // DebugPrintEx( DEBUG_ERR, TEXT("Cannot allocate a notification sink")); SetLastError (ERROR_NOT_ENOUGH_MEMORY); goto exit; } // // Scan the list to see if an identical sink already exists. // for (ListIter = pList->m_List.begin(); ListIter != pList->m_List.end(); ++ListIter) { CNotificationSink *pCurSink = (*ListIter); if (*pSink == *pCurSink) { // // Ooops, same sink already exists // DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to add an indetical local sink to a notification list")); SetLastError (ERROR_ALREADY_ASSIGNED); // // Tell the sink to gracefully disconnect // HRESULT hr = pSink->Disconnect (); delete pSink; pSink = NULL; goto exit; } } // // Add the new sink // pList->m_List.insert (pList->m_List.end(), pSink); exit: if (pList->m_List.empty()) { // // Remove empty list from map // delete pList; m_Map.erase (key); } return pSink; } // CNotificationMap::AddLocalSink DWORD CNotificationMap::RemoveSink ( CNotificationSink *pSinkToRemove ) /*++ Routine name : CNotificationMap::RemoveSink Class: CNotificationMap Routine description: Removes a sink from the list of sinks for a given sink pointer. If the list is empty, it is deleted and removed from the map. Author: Eran Yariv (EranY), Nov, 1999 Arguments: Return Value: Standard Win32 error code. --*/ { DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::RemoveSink")); if (m_bNotifying) { // // We're notifying now - can't change the list. // DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to add a local sink to a notification list while notifying")); return ERROR_BUSY; // The requested resource is in use. } // // Lookup the sink // NOTIFY_MAP::iterator it; BOOL bFound = FALSE; for (it = m_Map.begin(); it != m_Map.end (); ++it) { // // Get map value (list of sinks) // CSinksList *pList = (*it).second; // // Lookup sink in list // SINKS_LIST::iterator ListIter; CNotificationSink *pSink = NULL; for (ListIter = pList->m_List.begin(); ListIter != pList->m_List.end(); ++ListIter) { pSink = (*ListIter); if (pSinkToRemove == pSink) // Pointer comparison !!!! { // // Found the sink - remove it // pList->m_List.erase (ListIter); HRESULT hr = pSinkToRemove->Disconnect (); delete pSinkToRemove; bFound = TRUE; break; } } if (bFound) { // // Since we removed a sink from the list, the list may become empty now // if (pList->m_List.empty()) { // // Remove empty list // m_Map.erase (it); delete pList; } // // Break the map search // break; } } if (!bFound) { // // Reached the end of the map but the requested sink could not be found // DebugPrintEx( DEBUG_MSG, TEXT("Caller tried to to remove a non-existent sink")); return ERROR_NOT_FOUND; // Element not found. } return ERROR_SUCCESS; } // CNotificationMap::RemoveSink DWORD CNotificationMap::ExtNotificationThread( LPVOID UnUsed ) /*++ Routine name : CNotificationMap::ExtNotificationThread Routine description: This is the main thread function of the thread(s) that dequeue the notification completion port. This is a static class function !!!! Pointers to instances of ExtNotificationDataPacket are dequeued by this function and the map notificiation function is called on them. Author: Eran Yariv (EranY), Dec, 1999 Arguments: UnUsed [in] - Unused Return Value: Standard Win32 Error code --*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::ExtNotificationThread")); for (;;) { DWORD dwNumBytes; ULONG_PTR CompletionKey; CExtNotifyCallbackPacket *pPacket; if (!GetQueuedCompletionStatus ( g_pNotificationMap->m_hCompletionPort, &dwNumBytes, &CompletionKey, (LPOVERLAPPED*) &pPacket, INFINITE )) { dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("GetQueuedCompletionStatus failed with error = %ld. Aborting thread"), dwRes); return dwRes; } if (SERVICE_SHUT_DOWN_KEY == CompletionKey) { // // This is a special signal from the service that all thread should die now. Tell all other notification threads to die // if (!PostQueuedCompletionStatus( g_pNotificationMap->m_hCompletionPort, 0, SERVICE_SHUT_DOWN_KEY, (LPOVERLAPPED) NULL)) { dwRes = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("PostQueuedCompletionStatus failed (SERVICE_SHUT_DOWN_KEY). (ec: %ld)"), dwRes); } if (!DecreaseServiceThreadsCount()) { DebugPrintEx( DEBUG_ERR, TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"), GetLastError()); } return ERROR_SUCCESS; } Assert (pPacket && pPacket->m_lpbData && pPacket->m_dwDataSize && pPacket->m_lpwstrGUID ); // // Do the notification // DebugPrintEx( DEBUG_MSG, TEXT("Calling notification callback %p. DeviceId: %ld GUID: %s Data: %p DataSize: %ld"), pPacket->m_pCallback, pPacket->m_dwDeviceId, pPacket->m_lpwstrGUID, pPacket->m_lpbData, pPacket->m_dwDataSize); pPacket->m_pCallback(pPacket->m_dwDeviceId, // Notify with internal device id. pPacket->m_lpwstrGUID, pPacket->m_lpbData, pPacket->m_dwDataSize); // // Kill notification object // delete pPacket; } // Dequeue loop UNREFERENCED_PARAMETER (UnUsed); } // CNotificationMap::ExtNotificationThread DWORD CNotificationMap::Init () /*++ Routine name : CNotificationMap::Init Routine description: Initialize the notification map Author: Eran Yariv (EranY), Dec, 1999 Arguments: Return Value: Standard Win32 error code --*/ { DWORD dwRes; DWORD dwNumThreads = 0; DEBUG_FUNCTION_NAME(TEXT("CNotificationMap::Init")); // // Try to init our critical section // if (!m_CsExtensionData.Initialize()) { dwRes = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("CFaxCriticalSection::Initialize(&m_CsExtensionData) failed: err = %d"), dwRes); return dwRes; } // // Create the completion port // m_hCompletionPort = CreateIoCompletionPort ( INVALID_HANDLE_VALUE, // No device NULL, // New one 0, // Key MAX_CONCURRENT_EXT_DATA_SET_THREADS); if (NULL == m_hCompletionPort) { dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("CreateIoCompletionPort failed with %ld"), dwRes); return dwRes; } // // Create completion port dequeueing thread(s) // for (DWORD dw = 0; dw < NUM_EXT_DATA_SET_THREADS; dw++) { HANDLE hThread = CreateThreadAndRefCount ( NULL, // Security 0, // Stack size g_pNotificationMap->ExtNotificationThread, // Start routine 0, // Parameter 0, // Creation flag(s) NULL); // Don't want thread id if (NULL == hThread) { dwRes = GetLastError (); DebugPrintEx( DEBUG_ERR, TEXT("CreateThreadAndRefCount failed with %ld"), dwRes); } else { dwNumThreads++; CloseHandle(hThread); } } if (!dwNumThreads) { // // Not even a single thread was created // CloseHandle (m_hCompletionPort); m_hCompletionPort = NULL; return dwRes; } return ERROR_SUCCESS; } // CNotificationMap::Init /************************************ * * * CMapDeviceId * * * ************************************/ DWORD CMapDeviceId::AddDevice ( DWORD dwDeviceId, DWORD dwFaxId ) /*++ Routine name : CMapDeviceId::AddDevice Routine description: Adds a new device to the devices map Author: Eran Yariv (EranY), Dec, 1999 Arguments: dwDeviceId [in] - The source id of the device dwFaxId [in] - The unique fax device id (destination id) Return Value: Standard Win32 error code --*/ { DEVICE_IDS_MAP::iterator it; DWORD dwRes = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(TEXT("CMapDeviceId::AddDevice")); EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { // // See if entry exists in map // if((it = m_Map.find(dwDeviceId)) != m_Map.end()) { dwRes = ERROR_ALREADY_ASSIGNED; goto exit; } // // Add new map entry // m_Map[dwDeviceId] = dwFaxId; } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("map caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; } exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // CMapDeviceId::AddDevice DWORD CMapDeviceId::RemoveDevice ( DWORD dwDeviceId ) /*++ Routine name : CMapDeviceId::RemoveDevice Routine description: Removes an existing device from the devices map Author: Eran Yariv (EranY), Dec, 1999 Arguments: dwDeviceId [in] - The source id of the device Return Value: Standard Win32 error code --*/ { DEVICE_IDS_MAP::iterator it; DWORD dwRes = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(TEXT("CMapDeviceId::RemoveDevice")); EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { // // See if entry exists in map // if((it = m_Map.find(dwDeviceId)) == m_Map.end()) { dwRes = ERROR_NOT_FOUND; goto exit; } // // Remove map entry // m_Map.erase (it); } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("map caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; } exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // CMapDeviceId::RemoveDevice DWORD CMapDeviceId::LookupUniqueId ( DWORD dwOtherId, LPDWORD lpdwFaxId ) const /*++ Routine name : CMapDeviceId::LookupUniqueId Routine description: Looks up a unique fax device id from a given device id Author: Eran Yariv (EranY), Dec, 1999 Arguments: dwOtherId [in ] - Given device it (lookup source) lpdwFaxId [out] - Fax unique device id Return Value: Standard Win32 error code --*/ { DEVICE_IDS_MAP::iterator it; DWORD dwRes = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(TEXT("CMapDeviceId::LookupUniqueId")); if (!dwOtherId) { // // Special device id == 0 case // *lpdwFaxId = 0; return dwRes; } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { // // See if entry exists in map // if((it = m_Map.find(dwOtherId)) == m_Map.end()) { dwRes = ERROR_NOT_FOUND; goto exit; } *lpdwFaxId = (*it).second; } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("map caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; } exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // CMapDeviceId::LookupUniqueId /************************************ * * * Globals * * * ************************************/ CNotificationMap* g_pNotificationMap; // Map of DeviceId+GUID to list of notification sinks /* The map maps between TAPI permanent line ids to fax unique ids. TAPI-based FSPs / EFPSs talk to us using TAPI-permamnet line ids and have no clue of the fax unique device ids. This map is here for quick lookup for TAPI-based FSPs. */ CMapDeviceId* g_pTAPIDevicesIdsMap; // Map between TAPI permanent line id and fax unique device id. DWORD LookupUniqueFaxDeviceId ( DWORD dwDeviceId, LPDWORD lpdwResult, FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc) /*++ Routine name : LookupUniqueFaxDeviceId Routine description: Looks up a fax unique device id from a general device id. Author: Eran Yariv (EranY), Dec, 1999 Arguments: dwDeviceId [in ] - Original device id lpdwResult [out] - Looked up device id DevIdSrc [in ] - Source of device id Return Value: Standard Win32 error code. --*/ { DEBUG_FUNCTION_NAME(TEXT("LookupUniqueFaxDeviceId")); switch (DevIdSrc) { case DEV_ID_SRC_FAX: // No maping required *lpdwResult = dwDeviceId; return ERROR_SUCCESS; case DEV_ID_SRC_TAPI: return g_pTAPIDevicesIdsMap->LookupUniqueId (dwDeviceId, lpdwResult); default: DebugPrintEx( DEBUG_ERR, TEXT("Invalid device id source (%ld)"), DevIdSrc); ASSERT_FALSE; return ERROR_INVALID_PARAMETER; } } // LookupUniqueFaxDeviceId /************************************ * * * Get/Set Data * * * ************************************/ static BOOL FindTAPIPermanentLineIdFromFaxDeviceId ( IN DWORD dwFaxDeviceId, OUT LPDWORD lpdwTapiLineId ) /*++ Routine name : FindTAPIPermanentLineIdFromFaxDeviceId Routine description: Given a fax device id, returns the TAPI permanent line id associated with this fax device. If the fax device is not found or is a virtual fax (no TAPI association), the search fails. Author: Eran Yariv (EranY), Feb, 2000 Arguments: dwFaxDeviceId [in] - Fax device id lpdwTapiLineId [out] - TAPI permanent line id Return Value: TRUE if the search succeeed. FALSE otherwise. --*/ { BOOL bRes = FALSE; DEBUG_FUNCTION_NAME(TEXT("FindTAPIPermanentLineIdFromFaxDeviceId")); EnterCriticalSection(&g_CsLine); PLINE_INFO pLine = GetTapiLineFromDeviceId (dwFaxDeviceId, FALSE); if (!pLine) { goto exit; } if (pLine->Flags & FPF_VIRTUAL) { // // This fax device is virtual. It does not have a corresponding TAPI line. // goto exit; } *lpdwTapiLineId = pLine->TapiPermanentLineId; bRes = TRUE; exit: LeaveCriticalSection(&g_CsLine); return bRes; } // FindTAPIPermanentLineIdFromFaxDeviceId DWORD FAXGetExtensionData ( IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN OUT LPBYTE *ppData, IN OUT LPDWORD lpdwDataSize ) /*++ Routine name : FAXGetExtensionData Routine description: Gets the extension data for a device (internal) Author: Eran Yariv (EranY), Feb, 2000 Arguments: dwOrigDeviceId [in] - Original device id (as arrived from extension) DevIdSrc [in] - Device id source (Fax / TAPI) lpctstrNameGUID [in] - Data GUID ppData [out] - Pointer to data buffer lpdwDataSize [out] - Pointer to retrieved data size Return Value: Standard Win32 error code --*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("FAXGetExtensionData")); if (!lpctstrNameGUID || !ppData || !lpdwDataSize) { return ERROR_INVALID_PARAMETER; } if ((DevIdSrc != DEV_ID_SRC_FAX) && (DevIdSrc != DEV_ID_SRC_TAPI)) { // // Invalid device id class // return ERROR_INVALID_PARAMETER; } dwRes = IsValidGUID (lpctstrNameGUID); if (ERROR_SUCCESS != dwRes) { if (ERROR_WMI_GUID_NOT_FOUND == dwRes) { // // Return a more conservative error code // dwRes = ERROR_INVALID_PARAMETER; } return dwRes; } if (DEV_ID_SRC_FAX == DevIdSrc) { // // Try to see if this fax device has a matching tapi line id. // DWORD dwTapiLineId; if (FindTAPIPermanentLineIdFromFaxDeviceId (dwOrigDeviceId, &dwTapiLineId)) { // // Matching tapi line id found. Use it to read the data. // DevIdSrc = DEV_ID_SRC_TAPI; dwOrigDeviceId = dwTapiLineId; } } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); dwRes = ReadExtensionData ( dwOrigDeviceId, DevIdSrc, lpctstrNameGUID, ppData, lpdwDataSize ); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("Reading extension data for device id %ld, GUID %s failed with %ld"), dwOrigDeviceId, lpctstrNameGUID, dwRes); } LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // FAXGetExtensionData DWORD FAXSetExtensionData ( IN HINSTANCE hInst, IN LPCWSTR lpcwstrComputerName, IN DWORD dwOrigDeviceId, IN FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, IN LPCWSTR lpctstrNameGUID, IN LPBYTE pData, IN DWORD dwDataSize ) /*++ Routine name : FAXSetExtensionData Routine description: Writes the extension data for a device (internal) Author: Eran Yariv (EranY), Feb, 2000 Arguments: hInst [in] - Caller's instance lpcwstrComputerName [in] - Calling module computer name dwOrigDeviceId [in] - Original device id (as arrived from extension) DevIdSrc [in] - Device id source (Fax / TAPI) lpctstrNameGUID [in] - Data GUID pData [in] - Pointer to data buffer dwDataSize [in] - Data size Return Value: Standard Win32 error code --*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("FAXSetExtensionData")); if (!lpctstrNameGUID || !pData || !dwDataSize || !lpcwstrComputerName) { return ERROR_INVALID_PARAMETER; } if ((DevIdSrc != DEV_ID_SRC_FAX) && (DevIdSrc != DEV_ID_SRC_TAPI)) { // // Invalid device id class // return ERROR_INVALID_PARAMETER; } dwRes = IsValidGUID (lpctstrNameGUID); if (ERROR_SUCCESS != dwRes) { if (ERROR_WMI_GUID_NOT_FOUND == dwRes) { // // Return a more conservative error code // dwRes = ERROR_INVALID_PARAMETER; } return dwRes; } FAX_ENUM_DEVICE_ID_SOURCE RegistryDeviceIdSource = DevIdSrc; DWORD dwRegistryDeviceId = dwOrigDeviceId; if (DEV_ID_SRC_FAX == DevIdSrc) { // // Try to see if this fax device has a matching tapi line id. // DWORD dwTapiLineId; if (FindTAPIPermanentLineIdFromFaxDeviceId (dwOrigDeviceId, &dwTapiLineId)) { // // Matching tapi line id found. Use it to read the data. // RegistryDeviceIdSource = DEV_ID_SRC_TAPI; dwRegistryDeviceId = dwTapiLineId; } } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); dwRes = WriteExtensionData (dwRegistryDeviceId, RegistryDeviceIdSource, lpctstrNameGUID, pData, dwDataSize ); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("Writing extension data for device id %ld (registry device id = %ld), GUID %s failed with %ld"), dwOrigDeviceId, dwRegistryDeviceId, lpctstrNameGUID, dwRes); goto exit; } // // Notification is always done using the fax id (not the TAPI device id). // We must lookup the fax id from the TAPI id before we attempt notification registration. // DWORD dwFaxUniqueID; dwRes = LookupUniqueFaxDeviceId (dwOrigDeviceId, &dwFaxUniqueID, DevIdSrc); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("LookupUniqueFaxDeviceId failed for device id %ld (ec: %ld). No write notification will be performed."), dwOrigDeviceId, dwRes); // // We support writing to non-exiting devices configuration data // } if (ERROR_SUCCESS == dwRes) { try { g_pNotificationMap->Notify ( dwFaxUniqueID, // Device for which data has changed lpctstrNameGUID, // Name of data lpcwstrComputerName,// Computer name from which data has chnaged hInst, // Module handle from which data has changed pData, // Pointer to new data dwDataSize); // Size of new data } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("Notify() caused exception (%S)"), ex.what()); } } exit: LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // FAXSetExtensionData /************************************ * * * RPC handlers * * * ************************************/ extern "C" error_status_t FAX_GetExtensionData ( IN handle_t hFaxHandle, IN DWORD dwDeviceId, IN LPCWSTR lpctstrNameGUID, IN OUT LPBYTE *ppData, IN OUT LPDWORD lpdwDataSize ) /*++ Routine name : FAX_GetExtensionData Routine description: Read the extension's private data - Implements FaxGetExtensionData Author: Eran Yariv (EranY), Nov, 1999 Arguments: hFaxHandle [in ] - Unused dwDeviceId [in ] - Device identifier. 0 = Unassociated data lpctstrNameGUID [in ] - GUID of named data ppData [out] - Pointer to data buffer lpdwDataSize [out] - Returned size of data Return Value: Standard RPC error codes --*/ { DWORD dwRes; BOOL fAccess; DEBUG_FUNCTION_NAME(TEXT("FAX_GetExtensionData")); // // Access check // dwRes = FaxSvcAccessCheck (FAX_ACCESS_QUERY_CONFIG, &fAccess, NULL); if (ERROR_SUCCESS != dwRes) { DebugPrintEx(DEBUG_ERR, TEXT("FaxSvcAccessCheck Failed, Error : %ld"), dwRes); return dwRes; } if (FALSE == fAccess) { DebugPrintEx(DEBUG_ERR, TEXT("The user does not have the FAX_ACCESS_QUERY_CONFIG right")); return ERROR_ACCESS_DENIED; } return FAXGetExtensionData (dwDeviceId, DEV_ID_SRC_FAX, // RPC clients do not know the TAPI line ids lpctstrNameGUID, ppData, lpdwDataSize); } // FAX_GetExtensionData extern "C" error_status_t FAX_SetExtensionData ( IN handle_t hFaxHandle, IN LPCWSTR lpcwstrComputerName, IN DWORD dwDeviceId, IN LPCWSTR lpctstrNameGUID, IN LPBYTE pData, IN DWORD dwDataSize ) /*++ Routine name : FAX_SetExtensionData Routine description: Write the extension's private data - Implements FaxSetExtensionData Author: Eran Yariv (EranY), Nov, 1999 Arguments: hFaxHandle [in] - The handle of the module that sets the data lpcwstrComputerName [in] - The computer name of the module that sets the data dwDeviceId [in] - Device identifier. 0 = Unassociated data lpctstrNameGUID [in] - GUID of named data pData [in] - Pointer to data dwDataSize [in] - Size of data Return Value: Standard RPC error codes --*/ { DWORD dwRes; BOOL fAccess; DEBUG_FUNCTION_NAME(TEXT("FAX_SetExtensionData")); // // Access check // dwRes = FaxSvcAccessCheck (FAX_ACCESS_MANAGE_CONFIG, &fAccess, NULL); if (ERROR_SUCCESS != dwRes) { DebugPrintEx(DEBUG_ERR, TEXT("FaxSvcAccessCheck Failed, Error : %ld"), dwRes); return dwRes; } if (FALSE == fAccess) { DebugPrintEx(DEBUG_ERR, TEXT("The user does not have the FAX_ACCESS_MANAGE_CONFIG right")); return ERROR_ACCESS_DENIED; } return FAXSetExtensionData ((HINSTANCE)hFaxHandle, lpcwstrComputerName, dwDeviceId, DEV_ID_SRC_FAX, // RPC clients do not know the TAPI line ids lpctstrNameGUID, pData, dwDataSize); } // FAX_SetExtensionData /************************************ * * * Callback functions (fxsext.h) * * * ************************************/ DWORD FaxExtGetData ( DWORD dwDeviceId, // Device id (0 = No device) FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, // The source of the device id LPCWSTR lpcwstrNameGUID,// GUID of data LPBYTE *ppData, // (Out) Pointer to allocated data LPDWORD lpdwDataSize // (Out) Pointer to data size ) /*++ Routine name : FaxExtGetData Routine description: Callback function (called from the fax extension). Gets data for a device + GUID Author: Eran Yariv (EranY), Dec, 1999 Arguments: dwDeviceId [in] - Device id (0 = No device) DevIdSrc [in] - The source of the device id lpcwstrNameGUID [in] - GUID of data ppData [in] - Pointer to data buffer lpdwDataSize [in] - Data size retrieved Return Value: Standard Win32 error code --*/ { DEBUG_FUNCTION_NAME(TEXT("FaxExtGetData")); if (g_bServiceIsDown) { // // We don't supply extension data services when the service is going down // DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return ERROR_SHUTDOWN_IN_PROGRESS; } return FAXGetExtensionData ( dwDeviceId, DevIdSrc, lpcwstrNameGUID, ppData, lpdwDataSize ); } // FaxExtGetData DWORD FaxExtSetData ( HINSTANCE hInst, DWORD dwDeviceId, FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, LPCWSTR lpcwstrNameGUID, LPBYTE pData, DWORD dwDataSize ) /*++ Routine name : FaxExtSetData Routine description: Callback function (called from the fax extension). Sets data for a device + GUID Author: Eran Yariv (EranY), Dec, 1999 Arguments: hInst [in] - Extension DLL instance dwDeviceId [in] - Device id (0 = No device) DevIdSrc [in] - The source of the device id lpcwstrNameGUID [in] - GUID of data pData [in] - Pointer to data size [in] - Data size Return Value: Standard Win32 error code --*/ { DEBUG_FUNCTION_NAME(TEXT("FaxExtSetData")); if (g_bServiceIsDown) { // // We don't supply extension data services when the service is going down // DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return ERROR_SHUTDOWN_IN_PROGRESS; } return FAXSetExtensionData ( hInst, TEXT (""), // No computer name - a local extension sets the data dwDeviceId, DevIdSrc, lpcwstrNameGUID, pData, dwDataSize ); } // FaxExtSetData HANDLE FaxExtRegisterForEvents ( HINSTANCE hInst, DWORD dwDeviceId, // Device id (0 = No device) FAX_ENUM_DEVICE_ID_SOURCE DevIdSrc, // The source of the device id LPCWSTR lpcwstrNameGUID, // GUID of data PFAX_EXT_CONFIG_CHANGE lpConfigChangeCallback // Notification callback function ) /*++ Routine name : FaxExtRegisterForEvents Routine description: Register a local callback for notifications on a data change for a device and GUID Author: Eran Yariv (EranY), Nov, 1999 Arguments: hInst [in] - Instance of calling extension dwDeviceId [in] - Device id bTapiDevice [in] - If TRUE, the function attempts to convert to a Fax unique device id. The callback will receive the device id specified in dwDeviceId regardless of the lookup. lpcwstrNameGUID [in] - Data name lpConfigChangeCallback [in] - Pointer to notification callback function Return Value: Notification HANDLE. If NULL, call GetLastError () to retrieve error code. --*/ { HANDLE h = NULL; DEBUG_FUNCTION_NAME(TEXT("FaxExtRegisterForEvents")); if (g_bServiceIsDown) { // // We don't supply extension data services when the service is going down // DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); SetLastError (ERROR_SHUTDOWN_IN_PROGRESS); return NULL; } if (!lpConfigChangeCallback) { SetLastError (ERROR_INVALID_PARAMETER); return NULL; } DWORD dwRes = IsValidGUID (lpcwstrNameGUID); if (ERROR_SUCCESS != dwRes) { if (ERROR_WMI_GUID_NOT_FOUND == dwRes) { // // Return a more conservative error code // dwRes = ERROR_INVALID_PARAMETER; } SetLastError (dwRes); return NULL; } // // Notification is always done using the fax id (not the TAPI device id). // We must lookup the fax id from the TAPI id before we attempt notification registration. // DWORD dwFaxUniqueID; dwRes = LookupUniqueFaxDeviceId (dwDeviceId, &dwFaxUniqueID, DevIdSrc); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("LookupUniqueFaxDeviceId failed for device id %ld (ec: %ld)"), dwDeviceId, dwRes); SetLastError (dwRes); return NULL; } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { // // STL throws exceptions // h = (HANDLE) g_pNotificationMap->AddLocalSink ( hInst, // Instance of extension dwFaxUniqueID, // Listen to fax device unique id dwDeviceId, // Report the id specified by the caller. lpcwstrNameGUID, lpConfigChangeCallback); } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("AddLocalSink() caused exception (%S)"), ex.what()); SetLastError (ERROR_GEN_FAILURE); } LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return h; } // FaxExtRegisterForEvents DWORD FaxExtUnregisterForEvents ( HANDLE hNotification ) /*++ Routine name : FaxExtUnregisterForEvents Routine description: Unregsiters a local callback for notifications on a data change for a device and GUID. The functions succeeds only if the same callback function was previously registered (by calling FaxExtRegisterForEvents) to the same device id and GUID. Author: Eran Yariv (EranY), Nov, 1999 Arguments: hNotification [in] - Notification handle returned by FaxExtRegisterForExtensionEvents Return Value: Standard Win32 error code. --*/ { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("FaxExtUnregisterForEvents")); if (g_bServiceIsDown) { // // We don't supply extension data services when the service is going down // DebugPrintEx( DEBUG_ERR, TEXT("Called while service is shutting - operation canceled")); return ERROR_SHUTDOWN_IN_PROGRESS; } EnterCriticalSection (&g_pNotificationMap->m_CsExtensionData); try { // // STL throws exceptions // dwRes = g_pNotificationMap->RemoveSink ( (CNotificationSink *)(hNotification) ); } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("RemoveLocalSink() caused exception (%S)"), ex.what()); dwRes = ERROR_GEN_FAILURE; } LeaveCriticalSection (&g_pNotificationMap->m_CsExtensionData); return dwRes; } // FaxExtUnregisterForEvents