/*++ Copyright (c) 1996 Microsoft Corporation Module Name: tapi.c Abstract: This module wraps all of the TAPI calls. Author: Wesley Witt (wesw) 22-Jan-1996 Revision History: --*/ #include "faxsvc.h" #pragma hdrstop #include using namespace std; #include "tapiCountry.h" // // globals // HLINEAPP g_hLineApp; // application line handle HANDLE g_TapiCompletionPort; // HANDLE g_hTapiWorkerThread; // holds the TapiWorkerThread handle CFaxCriticalSection g_CsLine; // critical section for accessing tapi lines DWORD g_dwDeviceCount; // number of devices in the g_TapiLinesListHead LIST_ENTRY g_TapiLinesListHead; // linked list of tapi lines LIST_ENTRY g_RemovedTapiLinesListHead; // linked list of removed tapi lines LPBYTE g_pAdaptiveFileBuffer; // list of approved adaptive answer modems DWORD g_dwManualAnswerDeviceId; // Id of (one and only) device capable of manual answering (protected by g_CsLine) DWORD g_dwDeviceEnabledLimit; // Total number of devices DWORD g_dwDeviceEnabledCount; // Device limt by SKU static BOOL LoadAdaptiveFileBuffer(); static BOOL CreateLegacyVirtualDevices( PREG_FAX_SERVICE FaxReg, const REG_SETUP * lpRegSetup, DEVICE_PROVIDER * lpcProvider, LPDWORD lpdwDeviceCount); DWORD g_dwMaxLineCloseTime; // Wait interval in sec before trying to resend a powered off device BOOL AddNewDevice( DWORD DeviceId, LPLINEDEVCAPS LineDevCaps, BOOL fServerInitialization, PREG_FAX_DEVICES pInputFaxReg ); DWORD InitializeTapiLine( DWORD DeviceId, DWORD dwUniqueLineId, LPLINEDEVCAPS LineDevCaps, DWORD Rings, DWORD Flags, LPTSTR Csid, LPTSTR Tsid, LPTSTR lptstrDescription, BOOL fCheckDeviceLimit, DWORD dwDeviceType ); BOOL RemoveTapiDevice( DWORD dwTapiDeviceId ); void ResetDeviceFlags( PLINE_INFO pLineInfo ) { DWORD dwRes; DEBUG_FUNCTION_NAME(TEXT("ResetDeviceFlags")); Assert (pLineInfo); pLineInfo->Flags = (pLineInfo->Flags & FPF_VIRTUAL) ? FPF_VIRTUAL : 0; // send/receive disabled dwRes = RegSetFaxDeviceFlags( pLineInfo->PermanentLineID, pLineInfo->Flags); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("RegSetFaxDeviceFlags() (ec: %ld)"), dwRes); } if (pLineInfo->PermanentLineID == g_dwManualAnswerDeviceId) { g_dwManualAnswerDeviceId = 0; // Disable manual receive dwRes = WriteManualAnswerDeviceId (g_dwManualAnswerDeviceId); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("WriteManualAnswerDeviceId(0) (ec: %lc)"), dwRes); } } } LPTSTR FixupDeviceName( LPTSTR OrigDeviceName ) { LPTSTR NewDeviceName; LPTSTR p; NewDeviceName = StringDup( OrigDeviceName ); if (!NewDeviceName) { return NULL; } p = _tcschr( NewDeviceName, TEXT(',') ); if (!p) { return NewDeviceName; } p = NewDeviceName; while( p ) { p = _tcschr( p, TEXT(',') ); if (p) { *p = TEXT('_'); } } return NewDeviceName; } void FreeTapiLines( void ) { PLIST_ENTRY pNext; PLINE_INFO pLineInfo; pNext = g_TapiLinesListHead.Flink; while ((ULONG_PTR)pNext != (ULONG_PTR)&g_TapiLinesListHead) { pLineInfo = CONTAINING_RECORD( pNext, LINE_INFO, ListEntry ); pNext = pLineInfo->ListEntry.Flink; RemoveEntryList(&pLineInfo->ListEntry); FreeTapiLine(pLineInfo); } pNext = g_RemovedTapiLinesListHead.Flink; while ((ULONG_PTR)pNext != (ULONG_PTR)&g_RemovedTapiLinesListHead) { pLineInfo = CONTAINING_RECORD( pNext, LINE_INFO, ListEntry ); pNext = pLineInfo->ListEntry.Flink; RemoveEntryList(&pLineInfo->ListEntry); FreeTapiLine(pLineInfo); } } VOID FreeTapiLine( PLINE_INFO LineInfo ) { HLINE hLine = NULL; if (!LineInfo) { return; } if (LineInfo->hLine) { hLine = LineInfo->hLine; LineInfo->hLine = NULL; } MemFree( LineInfo->DeviceName ); MemFree( LineInfo->Tsid ); MemFree( LineInfo->Csid ); MemFree( LineInfo->lptstrDescription ); MemFree( LineInfo ); if (hLine) { lineClose( hLine ); } } int __cdecl DevicePriorityCompare( const void *arg1, const void *arg2 ) { if (((PDEVICE_SORT)arg1)->Priority < ((PDEVICE_SORT)arg2)->Priority) { return -1; } if (((PDEVICE_SORT)arg1)->Priority > ((PDEVICE_SORT)arg2)->Priority) { return 1; } return 0; } DWORD GetFaxDeviceCount( VOID ) /*++ Routine Description: counts the number of installed fax devices Arguments: NONE. Return Value: number of devices --*/ { DWORD FaxDevices = 0; PLIST_ENTRY Next; PLINE_INFO LineInfo; EnterCriticalSection(&g_CsLine); Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { LineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = LineInfo->ListEntry.Flink; if (LineInfo->PermanentLineID && LineInfo->DeviceName) { FaxDevices += 1; } } LeaveCriticalSection(&g_CsLine); return FaxDevices; } BOOL GetDeviceTypeCount( LPDWORD SendDevices, LPDWORD ReceiveDevices ) /*++ Routine Description: counts the number of devices with receive enabled, number with send enabled Arguments: SendDevices - receives number of send devices ReceiveDevices - receives number of receive devices Return Value: number of devices --*/ { DWORD Rx = 0, Tx = 0; PLIST_ENTRY Next; PLINE_INFO LineInfo; EnterCriticalSection(&g_CsLine); Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { LineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = LineInfo->ListEntry.Flink; if (LineInfo->PermanentLineID && LineInfo->DeviceName) { if ((LineInfo->Flags & FPF_SEND) == FPF_SEND) { Tx++; } if ((LineInfo->Flags & FPF_RECEIVE) == FPF_RECEIVE) { Rx++; } } } LeaveCriticalSection(&g_CsLine); if (SendDevices) { *SendDevices = Tx; } if (ReceiveDevices) { *ReceiveDevices = Rx; } return TRUE; } BOOL CommitDeviceChanges( PLINE_INFO LineInfo ) /*++ Routine Description: commit device changes to registry. Arguments: LineInfo - Pointer to the LINE_INFO describing the device to be commited. Return Value: TRUE for success. --*/ { EnterCriticalSection(&g_CsLine); RegAddNewFaxDevice( &g_dwLastUniqueLineId, &LineInfo->PermanentLineID, // Do not create new device. Update it. LineInfo->DeviceName, LineInfo->Provider->ProviderName, LineInfo->Provider->szGUID, LineInfo->Csid, LineInfo->Tsid, LineInfo->TapiPermanentLineId, LineInfo->Flags & 0x0fffffff, LineInfo->RingsForAnswer); LeaveCriticalSection(&g_CsLine); return TRUE; } BOOL SendIncomingCallEvent( PLINE_INFO LineInfo, LPLINEMESSAGE LineMsg, HCALL hCall ) /*++ Routine Description: This function posts FAX_EVENT_EX of FAX_EVENT_INCOMING_CALL type. Arguments: LineInfo - pointer to LINE_INFO structure LineMsg - pointer to LINEMESSAGE structure hCall - call handle to set into message Return Values: TRUE for success FALSE for failure --*/ { BOOL success = FALSE; DWORD dwEventSize; DWORD dwResult; PFAX_EVENT_EX pEvent = NULL; TCHAR CallerID[512]; DEBUG_FUNCTION_NAME(TEXT("SendIncomingCallEvent")); // // save the line msg so we could verify hCall later // CopyMemory( &LineInfo->LineMsgOffering, LineMsg, sizeof(LINEMESSAGE) ); // // allocate event structure, including caller ID info, if any // dwEventSize = sizeof(FAX_EVENT_EX); CallerID[0] = TEXT('\0'); if(GetCallerIDFromCall(LineMsg->hDevice, CallerID, ARR_SIZE(CallerID))) { dwEventSize += (lstrlen(CallerID) + 1) * sizeof(TCHAR); } pEvent = (PFAX_EVENT_EX)MemAlloc(dwEventSize); if(!pEvent) { DebugPrintEx( DEBUG_ERR, TEXT("Failed to notify clients of incoming call. Error allocating FAX_EVENT_EX")); goto Cleanup; } // // fill in event structure // ZeroMemory(pEvent, dwEventSize); pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EX); GetSystemTimeAsFileTime( &(pEvent->TimeStamp) ); pEvent->EventType = FAX_EVENT_TYPE_NEW_CALL; pEvent->EventInfo.NewCall.hCall = hCall; pEvent->EventInfo.NewCall.dwDeviceId = LineInfo->PermanentLineID; // // copy caller ID info, if available // if(CallerID[0] != TEXT('\0')) { pEvent->EventInfo.NewCall.lptstrCallerId = (LPTSTR) sizeof(FAX_EVENT_EX); lstrcpy((LPTSTR)((BYTE *)pEvent + sizeof(FAX_EVENT_EX)), CallerID); } // // post extended event to any clients // dwResult = PostFaxEventEx(pEvent, dwEventSize, NULL); if(dwResult != ERROR_SUCCESS) { DebugPrintEx( DEBUG_ERR, TEXT("Failed to notify clients of incoming call. PostFaxEventEx() returned %x"), dwResult); goto Cleanup; } success = TRUE; Cleanup: if (NULL != pEvent) { MemFree(pEvent); } return success; } ULONG TapiWorkerThread( LPVOID UnUsed ) /*++ Routine Description: This is worker thread for the FAX service. All queued requests are processed here. Arguments: None. Return Value: Thread return value. --*/ { PLINE_INFO LineInfo; BOOL Rval; DWORD Bytes; ULONG_PTR CompletionKey; LPLINEMESSAGE LineMsg = NULL; DWORD dwQueueState; BOOL fWakeupJobQueueThread; static BOOL fServiceIsDownSemaphoreWasReleased = FALSE; DEBUG_FUNCTION_NAME(TEXT("TapiWorkerThread")); while( TRUE ) { fWakeupJobQueueThread = FALSE; // We want to wake up the JobQueueThread if a new devce was added. if (LineMsg) { LocalFree( LineMsg ); } Rval = GetQueuedCompletionStatus( g_TapiCompletionPort, &Bytes, &CompletionKey, (LPOVERLAPPED*) &LineMsg, INFINITE ); if (!Rval) { Rval = GetLastError(); LineMsg = NULL; DebugPrintEx(DEBUG_ERR, TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"), Rval); continue; } if (SERVICE_SHUT_DOWN_KEY == CompletionKey) { // // Service is shutting down // DebugPrintEx( DEBUG_MSG, TEXT("Service is shutting down")); break; } if(CompletionKey == ANSWERNOW_EVENT_KEY) { // // this is an event posted by FAX_AnswerCall // // the LINEMESSAGE structure must be filled out // as follows: // // LineMsg->hDevice == 0 // LineMsg->dwMessageID == 0 // LineMsg->dwCallbackInstance == 0 // LineMsg->dwParam1 == Permanent device Id // LineMsg->dwParam2 == 0 // LineMsg->dwParam3 == 0 // PJOB_ENTRY pJobEntry; TCHAR FileName[MAX_PATH]; DWORD dwOldFlags; EnterCriticalSection( &g_CsJob ); EnterCriticalSection( &g_CsLine ); if (TRUE == g_bServiceIsDown) { // // Notify EndFaxSvc that we read the shutdown flag // if (FALSE == fServiceIsDownSemaphoreWasReleased) { if (!ReleaseSemaphore( g_hServiceIsDownSemaphore, // handle to semaphore 1, // count increment amount NULL // previous count )) { DebugPrintEx( DEBUG_ERR, TEXT("ReleaseSemaphore() failed, (ec = %ld)"), GetLastError()); } else { fServiceIsDownSemaphoreWasReleased = TRUE; } } // // don't process event - from now on TapiWorkerThread is in-active // and only sends notifications to FSP // goto next_event; } // // Get LineInfo from permanent device ID // LineInfo = GetTapiLineFromDeviceId( (DWORD) LineMsg->dwParam1, FALSE ); if(!LineInfo) { DebugPrintEx(DEBUG_ERR, TEXT("Line %ld not found"), LineMsg->dwParam1); goto next_event; } // // See if the device is still available // if(LineInfo->State != FPS_AVAILABLE || LineInfo->JobEntry) { DebugPrintEx(DEBUG_ERR, TEXT("Line is not available (LineState is 0x%08x) or JobEntry is not NULL."), LineInfo->State); goto next_event; } if (!LineInfo->LineMsgOffering.hDevice) { // // There's no offering call - this is the 'answer-now' mode. // // If the line is ringing at the same time (has a new call), we must close the line (to make // all active calls go away) and re-open it. // // From MSDN: "If an application calls lineClose while it still has active calls on the opened line, // the application's ownership of these calls is revoked. // If the application was the sole owner of these calls, the calls are dropped as well." // // Otherwise, when we call the FSP's FaxDevReceive() function with hCall=0, // it calls lineMakeCall (..., PASSTHROUGH) which always succeeds but doesn't get LINECALLSTATE_OFFERING // until the other offering call is over. // if (LineInfo->hLine) { LONG lRes = lineClose(LineInfo->hLine); if (ERROR_SUCCESS != lRes) { DebugPrintEx(DEBUG_ERR, TEXT("lineClose failed with 0x%08x"), lRes); } LineInfo->hLine = 0; } } if (LineInfo->hLine == NULL) { // // Line is closed - open it now // This can be because: // 1. This is the 'answer now' mode but the line was never send or receive enabled. // 2. This is the 'answer now' mode the line was open and there was no call offered, we closed the line (above). // if (!OpenTapiLine(LineInfo)) { DWORD dwRes = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("OpenTapiLine failed. (ec: %ld)"), dwRes); goto next_event; } } Assert (LineInfo->hLine); // // start a receive fax job // // If we don't fake FPF_RECEIVE, GetTapiLineForFaxOperation() will fail StartReceiveJob() if the device is not // set to receive (manually or automatically) // dwOldFlags = LineInfo->Flags; LineInfo->Flags |= FPF_RECEIVE; pJobEntry = StartReceiveJob(LineInfo->PermanentLineID); // // Restore original device flags. // LineInfo->Flags = dwOldFlags; if (pJobEntry) { if(ERROR_SUCCESS != StartFaxReceive( pJobEntry, (HCALL)LineInfo->LineMsgOffering.hDevice, // This is either 0 (answer now) or the active hCall (manual-answer) LineInfo, FileName, sizeof(FileName)/sizeof(FileName[0]) )) { DebugPrintEx( DEBUG_ERR, TEXT("StartFaxReceive failed. Line: %010d (%s) (ec: %ld)"), LineInfo->DeviceId, LineInfo->DeviceName, GetLastError()); // // NTRAID#EdgeBugs-12677-2001/05/14-t-nicali: Should place an EVENTLOG entry here // } } else { DebugPrintEx(DEBUG_ERR, TEXT("StartJob() failed, cannot receive incoming fax")); } goto next_event; } if ((CompletionKey == FAXDEV_EVENT_KEY) || (CompletionKey == EFAXDEV_EVENT_KEY)) { // // this is an event from a fax service provider // that has enumerated virtual device(s) // // the LINEMESSAGE structure must be filled out // as follows: // // LineMsg->hDevice == DeviceId from FaxDevStartJob() // LineMsg->dwMessageID == 0 // LineMsg->dwCallbackInstance == 0 // LineMsg->dwParam1 == LINEDEVSTATE_RINGING // LineMsg->dwParam2 == 0 // LineMsg->dwParam3 == 0 // EnterCriticalSection( &g_CsJob ); EnterCriticalSection( &g_CsLine ); if (TRUE == g_bServiceIsDown) { // // Notify EndFaxSvc that we read the shutdown flag // if (FALSE == fServiceIsDownSemaphoreWasReleased) { if (!ReleaseSemaphore( g_hServiceIsDownSemaphore, // handle to semaphore 1, // count increment amount NULL // previous count )) { DebugPrintEx( DEBUG_ERR, TEXT("ReleaseSemaphore() failed, (ec = %ld)"), GetLastError()); } else { fServiceIsDownSemaphoreWasReleased = TRUE; } } // // don't process event - from now on TapiWorkerThread is in-active // and only sends notifications to FSP // goto next_event; } LineInfo = GetTapiLineFromDeviceId( (DWORD) LineMsg->hDevice, CompletionKey == FAXDEV_EVENT_KEY); if (!LineInfo) { goto next_event; } if (LineMsg->dwParam1 == LINEDEVSTATE_RINGING) { DWORD dwRes; LineInfo->RingCount += 1; if( !CreateFaxEvent( LineInfo->PermanentLineID, FEI_RINGING, 0xffffffff ) ) { DebugPrintEx( DEBUG_ERR, TEXT("CreateFaxEvent failed. (ec: %ld)"), GetLastError()); } dwRes = CreateDeviceEvent (LineInfo, TRUE); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("CreateDeviceEvent() (ec: %lc)"), dwRes); } EnterCriticalSection (&g_CsConfig); dwQueueState = g_dwQueueState; LeaveCriticalSection (&g_CsConfig); if ((LineInfo->State == FPS_AVAILABLE) && // Device is available and !LineInfo->JobEntry && // no job on this device yet and !(dwQueueState & FAX_INCOMING_BLOCKED) && // The incoming queue is not blocked and (LineInfo->Flags & FPF_RECEIVE)) // Device is set to auto-receive { PJOB_ENTRY JobEntry; TCHAR FileName[MAX_PATH]; // // start a fax job // JobEntry = StartReceiveJob( LineInfo->PermanentLineID); if (JobEntry) { // // receive the fax // if (ERROR_SUCCESS != StartFaxReceive( JobEntry, 0, LineInfo, FileName, sizeof(FileName)/sizeof(FileName[0]) )) { DebugPrintEx( DEBUG_ERR, TEXT("StartFaxReceive failed. Line: 0x%08X (%s) (ec: %ld)"), LineInfo->DeviceId, LineInfo->DeviceName, GetLastError()); } } else { DebugPrintEx(DEBUG_ERR, TEXT("StartJob() failed, cannot receive incoming fax")); } } } goto next_event; } LineInfo = (PLINE_INFO) LineMsg->dwCallbackInstance; #if DBG ShowLineEvent( (HLINE) LineMsg->hDevice, (HCALL) LineMsg->hDevice, LineInfo == NULL ? TEXT("*NULL LineInfo*") : (LineInfo->JobEntry == NULL) ? TEXT("*NULL Job*") : NULL, LineMsg->dwCallbackInstance, LineMsg->dwMessageID, LineMsg->dwParam1, LineMsg->dwParam2, LineMsg->dwParam3 ); #endif // #if DBG EnterCriticalSection( &g_CsJob ); EnterCriticalSection( &g_CsLine ); if (TRUE == g_bServiceIsDown) { // // Notify EndFaxSvc that we read the shutdown flag // if (FALSE == fServiceIsDownSemaphoreWasReleased) { if (!ReleaseSemaphore( g_hServiceIsDownSemaphore, // handle to semaphore 1, // count increment amount NULL // previous count )) { DebugPrintEx( DEBUG_ERR, TEXT("ReleaseSemaphore() failed, (ec = %ld)"), GetLastError()); } else { fServiceIsDownSemaphoreWasReleased = TRUE; } } // // don't process event - from now on TapiWorkerThread is in-active // and only sends notifications to FSP // goto FSP_call_Back; } switch( LineMsg->dwMessageID ) { case LINE_ADDRESSSTATE: break; case LINE_CALLINFO: // // generating FAX_EVENT_EX of type FAX_EVENT_TYPE_NEW_CALL when // caller ID info becomes available // if((LineMsg->dwParam1 == LINECALLINFOSTATE_CALLERID) && (LineInfo->PermanentLineID == g_dwManualAnswerDeviceId) ) { // // Only send ringing event about the device set to manual answering // SendIncomingCallEvent(LineInfo, LineMsg, (HCALL)LineMsg->hDevice); } break; case LINE_CALLSTATE: if (LineMsg->dwParam1 == LINECALLSTATE_IDLE) { // // This is the last event on the call. make sure the line is deallocated. // if (NULL == LineInfo->JobEntry || (LineInfo->JobEntry && NULL == LineInfo->JobEntry->CallHandle)) { // // We do not have the hCall in the JobEntry, release the call to prevent leaks // DebugPrintEx(DEBUG_WRN, TEXT("We have LINE_CALLSTATE (IDLE) msg, doing 'ReleaseTapiLine'\r\n")); ReleaseTapiLine( LineInfo, (HCALL) LineMsg->hDevice ); } LineInfo->NewCall = FALSE; if ( !CreateFaxEvent( LineInfo->PermanentLineID, FEI_IDLE, 0xffffffff ) ) { DebugPrintEx( DEBUG_ERR, TEXT("CreateFaxEvent failed. (ec: %ld)"), GetLastError()); } DWORD dwRes = CreateDeviceEvent (LineInfo, FALSE); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("CreateDeviceEvent() (ec: %lc)"), dwRes); } } else { // // Update the hCall in the JobEntry, so that EndJob()/ReleaseJob()will finally call lineDeallocateCall() to free the hCall. // if (NULL != LineInfo->JobEntry) { if (NULL == LineInfo->JobEntry->CallHandle) { // // FSP did not report the hCall yet. // LineInfo->JobEntry->CallHandle = (HCALL) LineMsg->hDevice; } if (LineInfo->JobEntry->CallHandle != (HCALL) LineMsg->hDevice) { // // we have a mismatch between the reported hCall from the FSP or previuos TAPI event and the hCall reproted by TAPI. // DebugPrintEx( DEBUG_WRN, TEXT("Mismatch between the reported hCall from the FSP or previuos TAPI event and the hCall reproted by TAPI")); } } } if (LineInfo->NewCall && LineMsg->dwParam1 != LINECALLSTATE_OFFERING && LineInfo->State == FPS_AVAILABLE) { LineInfo->State = FPS_NOT_FAX_CALL; LineInfo->NewCall = FALSE; } // // generating FAX_EVENT_EX of type FAX_EVENT_NEW_INCOMING_CALL when // line call state changes // if (LineInfo->PermanentLineID == g_dwManualAnswerDeviceId) { // // Only send ringing event about the device set to manual answering // // When a call is offered to us, we send the event with hCall // and any caller ID info we might have // if(LineMsg->dwParam1 == LINECALLSTATE_OFFERING) { SendIncomingCallEvent(LineInfo, LineMsg, (HCALL)LineMsg->hDevice); } // // when the caller hangs up, we send the event without hCall // if(LineMsg->dwParam1 == LINECALLSTATE_IDLE) { SendIncomingCallEvent(LineInfo, LineMsg, NULL); } } if (LineMsg->dwParam1 == LINECALLSTATE_OFFERING) { // // we'll get a LINE_LINEDEVSTATE (RINGING) event, so we'll post the ring event there or we'll get a duplicate event // LineInfo->NewCall = FALSE; if ((LineInfo->State == FPS_AVAILABLE) && // Line is available and (LineInfo->Flags & FPF_RECEIVE)) // Line is set to receive { EnterCriticalSection (&g_CsConfig); dwQueueState = g_dwQueueState; LeaveCriticalSection (&g_CsConfig); if ((LineInfo->RingCount > LineInfo->RingsForAnswer) && // Rings exceeded threshold and !LineInfo->JobEntry && // no job on this device yet and !(dwQueueState & FAX_INCOMING_BLOCKED) // Incoming queue is not blocked ) { PJOB_ENTRY JobEntry; TCHAR FileName[MAX_PATH]; // // start a fax job // JobEntry = StartReceiveJob( LineInfo->PermanentLineID); if (JobEntry) { // // receive the fax // if (ERROR_SUCCESS != StartFaxReceive( JobEntry, (HCALL) LineMsg->hDevice, LineInfo, FileName, sizeof(FileName)/sizeof(FileName[0]) )) { DebugPrintEx( DEBUG_ERR, TEXT("StartFaxReceive failed. Line: 0x%08X (%s) (ec: %ld)"), LineInfo->DeviceId, LineInfo->DeviceName, GetLastError()); } } else { DebugPrintEx(DEBUG_ERR, TEXT("StartJob() failed, cannot receive incoming fax")); } } else { // // save the line msg // CopyMemory( &LineInfo->LineMsgOffering, LineMsg, sizeof(LINEMESSAGE) ); } } else { // // we are not supposed to answer the call, so give it to ras // HandoffCallToRas( LineInfo, (HCALL) LineMsg->hDevice ); } } break; case LINE_CLOSE: { // // this usually happens when something bad happens to the modem device. // DebugPrintEx( DEBUG_MSG, (TEXT("Received LINE_CLOSE message for device %x [%s]."), LineInfo->DeviceId, LineInfo->DeviceName) ); LineInfo->hLine = NULL; LineInfo->State = FPS_AVAILABLE; GetSystemTimeAsFileTime ((FILETIME*)&LineInfo->LastLineClose); if ((LineInfo->Flags & FPF_RECEIVE) || // Line is in auto-anser or (g_dwManualAnswerDeviceId == LineInfo->PermanentLineID)) // manual-answer mode { // // Try to reopen the line // if (!OpenTapiLine(LineInfo)) { DebugPrintEx( DEBUG_ERR, TEXT("OpenTapiLine failed for device %s"), LineInfo->DeviceName); } } else { LineInfo->Flags |= FPF_POWERED_OFF; } } break; case LINE_DEVSPECIFIC: break; case LINE_DEVSPECIFICFEATURE: break; case LINE_GATHERDIGITS: break; case LINE_GENERATE: break; case LINE_LINEDEVSTATE: if (LineMsg->dwParam1 == LINEDEVSTATE_RINGING) { DWORD dwRes; LineInfo->RingCount = (DWORD)LineMsg->dwParam3 + 1; if( !CreateFaxEvent( LineInfo->PermanentLineID, FEI_RINGING, 0xffffffff ) ) { DebugPrintEx( DEBUG_ERR, TEXT("CreateFaxEvent failed. (ec: %ld)"), GetLastError()); } dwRes = CreateDeviceEvent (LineInfo, TRUE); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("CreateDeviceEvent() (ec: %lc)"), dwRes); } // // Pick up the line only if the last inbound job has completed // if (LineInfo->State != FPS_AVAILABLE) { break; } EnterCriticalSection (&g_CsConfig); dwQueueState = g_dwQueueState; LeaveCriticalSection (&g_CsConfig); if (dwQueueState & FAX_INCOMING_BLOCKED) { // // Inbox is blocked - no incoming faxes will be received. // break; } if ((LineInfo->Flags & FPF_RECEIVE) && // Line is set to receive and (LineInfo->State == FPS_AVAILABLE)) // the line is available { if (LineInfo->LineMsgOffering.hDevice == 0) { // // wait for the offering message // break; } if ((LineInfo->RingCount > LineInfo->RingsForAnswer) && // Rings count match and !LineInfo->JobEntry // There's no job on the line ) { PJOB_ENTRY JobEntry; TCHAR FileName[MAX_PATH]; // // Start a fax job // JobEntry = StartReceiveJob( LineInfo->PermanentLineID); if (JobEntry) { // // Receive the fax // if (ERROR_SUCCESS != StartFaxReceive( JobEntry, (HCALL) LineInfo->LineMsgOffering.hDevice, LineInfo, FileName, sizeof(FileName)/sizeof(FileName[0]) )) { DebugPrintEx( DEBUG_ERR, TEXT("StartFaxReceive failed. Line: 0x%08X (%s) (ec: %ld)"), LineInfo->DeviceId, LineInfo->DeviceName, GetLastError()); } } else { DebugPrintEx(DEBUG_ERR, TEXT("StartJob() failed, cannot receive incoming fax")); } } } else { // // we are not supposed to answer the call, so give it to ras // HandoffCallToRas( LineInfo, (HCALL) LineInfo->LineMsgOffering.hDevice ); } } break; case LINE_MONITORDIGITS: break; case LINE_MONITORMEDIA: break; case LINE_MONITORTONE: break; case LINE_REPLY: break; case LINE_REQUEST: break; case PHONE_BUTTON: break; case PHONE_CLOSE: break; case PHONE_DEVSPECIFIC: break; case PHONE_REPLY: break; case PHONE_STATE: break; case LINE_CREATE: { LPLINEDEVCAPS LineDevCaps; LINEEXTENSIONID lineExtensionID; DWORD LocalTapiApiVersion; DWORD Rslt; DWORD DeviceId; DeviceId = (DWORD)LineMsg->dwParam1; Rslt = lineNegotiateAPIVersion( g_hLineApp, DeviceId, MIN_TAPI_LINE_API_VER, MAX_TAPI_LINE_API_VER, &LocalTapiApiVersion, &lineExtensionID ); if (Rslt == 0) { LineDevCaps = SmartLineGetDevCaps(g_hLineApp, DeviceId , LocalTapiApiVersion); if (LineDevCaps) { EnterCriticalSection(&g_CsLine); EnterCriticalSection(&g_CsConfig); if (!AddNewDevice( DeviceId, LineDevCaps, FALSE , NULL)) { DebugPrintEx( DEBUG_WRN, TEXT("AddNewDevice() failed for Tapi Permanent device id: %ld (ec: %ld)"), LineDevCaps->dwPermanentLineID, GetLastError()); } else { // // A new device was succesfully added - wake up the JobQueueThread // fWakeupJobQueueThread = TRUE; } LeaveCriticalSection(&g_CsConfig); LeaveCriticalSection(&g_CsLine); MemFree( LineDevCaps ); UpdateReceiveEnabledDevicesCount (); } } } break; case PHONE_CREATE: break; case LINE_AGENTSPECIFIC: break; case LINE_AGENTSTATUS: break; case LINE_APPNEWCALL: LineInfo->NewCall = TRUE; break; case LINE_PROXYREQUEST: break; case LINE_REMOVE: { DWORD dwDeviceId = (DWORD)LineMsg->dwParam1; EnterCriticalSection(&g_CsLine); EnterCriticalSection (&g_CsConfig); if (!RemoveTapiDevice(dwDeviceId)) { DebugPrintEx( DEBUG_WRN, TEXT("RemoveTapiDevice() failed for device id: %ld (ec: %ld)"), dwDeviceId, GetLastError()); } LeaveCriticalSection(&g_CsConfig); LeaveCriticalSection(&g_CsLine); UpdateReceiveEnabledDevicesCount (); } break; case PHONE_REMOVE: break; } FSP_call_Back: // // call the device provider's line callback function // if (LineInfo && LineInfo->JobEntry) { Assert (LineInfo->Provider && LineInfo->Provider->FaxDevCallback); __try { LineInfo->Provider->FaxDevCallback( (HANDLE) LineInfo->JobEntry->InstanceData, LineMsg->hDevice, LineMsg->dwMessageID, LineMsg->dwCallbackInstance, LineMsg->dwParam1, LineMsg->dwParam2, LineMsg->dwParam3 ); } __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, LineInfo->Provider->FriendlyName, GetExceptionCode())) { ASSERT_FALSE; } } next_event: LeaveCriticalSection( &g_CsLine ); LeaveCriticalSection( &g_CsJob ); // // Check if we should wake up the JobQueueThread (if a new device was added) // if (TRUE == fWakeupJobQueueThread) { if (!SetEvent( g_hJobQueueEvent )) { DebugPrintEx( DEBUG_ERR, TEXT("Failed to set g_hJobQueueEvent. (ec: %ld)"), GetLastError()); EnterCriticalSection (&g_CsQueue); g_ScanQueueAfterTimeout = TRUE; LeaveCriticalSection (&g_CsQueue); } } } // // Notify EndFaxSvc that we read the shutdown flag // if (FALSE == fServiceIsDownSemaphoreWasReleased) { if (!ReleaseSemaphore( g_hServiceIsDownSemaphore, // handle to semaphore 1, // count increment amount NULL // previous count )) { DebugPrintEx( DEBUG_ERR, TEXT("ReleaseSemaphore() failed, (ec = %ld)"), GetLastError()); } } return 0; } BOOL HandoffCallToRas( PLINE_INFO LineInfo, HCALL hCall ) /*++ Routine Description: This function will try to hand a call of to RAS. We do this under 2 circumstances: 1) we've answered an incoming call and determined that the call is NOT a fax call 2) the configuration for the line that is ringing is not configured for receiving faxes If the handoff fails and we have an open job for the line, then we have to call the device provider so that the line can be put on hook. Arguments: LineInfo - LineInfo structure for the line this call is on hCall - TAPI call handle Return Value: TRUE for success FALSE for failure --*/ { LONG Rval; DEBUG_FUNCTION_NAME(TEXT("HandoffCallToRas")); // // need to hand the call off to RAS // Rval = lineHandoff( hCall, RAS_MODULE_NAME, LINEMEDIAMODE_DATAMODEM ); if (Rval != 0 && LineInfo && LineInfo->JobEntry) { DebugPrintEx(DEBUG_ERR, TEXT("lineHandoff() failed, ec=0x%08x"), Rval); } else { DebugPrintEx(DEBUG_MSG, TEXT("call handed off to RAS")); } return Rval == 0; } PLINE_INFO GetTapiLineFromDeviceId( DWORD DeviceId, BOOL fLegacyId ) { PLIST_ENTRY Next; PLINE_INFO LineInfo; Next = g_TapiLinesListHead.Flink; if (!Next) { return NULL; } while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { LineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = LineInfo->ListEntry.Flink; if (fLegacyId) { if (LineInfo->TapiPermanentLineId == DeviceId) { return LineInfo; } } else { if (LineInfo->PermanentLineID == DeviceId) { return LineInfo; } } } return NULL; } //********************************************************************************* //* Name: GetLineForSendOperation() //* Author: Ronen Barenboim //* Date: June 03, 1999 //********************************************************************************* //* DESCRIPTION: //* Returns a line to be used for a send operation. //* //* PARAMETERS: //* [IN ] PJOB_QUEUE lpJobQueue //* The recipient job for which the line is intended. //* //* RETURN VALUE: //* On success the function returns a pointer to the LINE_INFO structure of //* the selected line. //* On failure it returns NULL. //********************************************************************************* PLINE_INFO GetLineForSendOperation( PJOB_QUEUE lpJobQueue ) { DEBUG_FUNCTION_NAME(TEXT("GetLineForSendOperation")); Assert(lpJobQueue); return GetTapiLineForFaxOperation( USE_SERVER_DEVICE, JT_SEND, lpJobQueue->RecipientProfile.lptstrFaxNumber ); } //********************************************************************************* //* Name: GetTapiLineForFaxOperation() //* Author: Ronen Barenboim //* Date: June 03, 1999 //********************************************************************************* //* DESCRIPTION: //* Locates an avaliable TAPI device for use in a //* FAX operation. The selection is based on the //* available devices and their assigned priority. //* If DeviceId is USE_SERVER_DEVICE the function will locate a device which //* can be used for the job type specified. It does not revive devices in this //* case. //* //* If DeviceId contains a specific device //* If Handoff is TRUE and the job type is JT_SEND //* The function will return the LINE_INFO for the specified line without //* checking if it is availble or not or configured for send or receive. //* Else //* The function will check first if the specified device match the //* requested job type and then return LINE_INFO. //* If the device is powered off the function will attempt to revive it. //* //* PARAMETERS: //* [IN ] DWORD DeviceId //* The permanent device id (not tapi) of the line. If it is //* USE_SERVER_DEVICE the function will select a line based on the //* line send/receive capabilities, status and priorities. //* //* [IN ] DWORD JobType //* The type of job that is about to be executed on the line. //* can be JT_RECEIVE or JT_SEND. //* //* [IN ] LPWSTR FaxNumber //* For a send operation this is the fax number that is going to be //* used to send the fax. The function uses it to avoid sending //* to faxes to the same number simultaneously. //* //* RETURN VALUE: //* If the function succeeds it returns a pointer to the LINE_INFO //* structure of the selected line. Otherwise it returns NULL. //* If it is NULL the function failed. Call GetLastError() for more info. //********************************************************************************* PLINE_INFO GetTapiLineForFaxOperation( DWORD DeviceId, DWORD JobType, LPWSTR FaxNumber ) { PLIST_ENTRY Next; PLINE_INFO LineInfo; PLINE_INFO SelectedLine = NULL; LPDWORD lpdwDevices = NULL; DEBUG_FUNCTION_NAME(TEXT("GetTapiLineForFaxOperation")); DWORD ec = ERROR_SUCCESS; EnterCriticalSection( &g_CsLine ); if (FaxNumber) { if (FindJobEntryByRecipientNumber(FaxNumber)) { // // We do not allow to outgoing calls to the same number. // LeaveCriticalSection( &g_CsLine ); SetLastError (ERROR_NOT_FOUND); return NULL; } } // // Find out if there is another send job to the same number. // It there is do not select a line and return NULL. // if (DeviceId != USE_SERVER_DEVICE) { Assert (JobType == JT_RECEIVE); Next = g_TapiLinesListHead.Flink; Assert (Next); while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { LineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = LineInfo->ListEntry.Flink; // // The caller specified a specific device to use. Just find it for him. // if (LineInfo->PermanentLineID == DeviceId) { // // Found a device with a matching id. // if (NULL != LineInfo->JobEntry) { // // Device is busy with another job // break; } if ((LineInfo->Flags & FPF_RECEIVE) || // Line is in auto-answer mode or (g_dwManualAnswerDeviceId == LineInfo->PermanentLineID) // this is the manual-answer device ) { // // For receive jobs we assume that the requested device is free since it is // the FSP that tells us when to receive. // we need to mark it as unavailable until the receive operation is completed. // LineInfo->State = 0; // remove the FPS_AVAILABLE bit SelectedLine = LineInfo; break; } if (LineInfo->UnimodemDevice && (LineInfo->Flags & FPF_POWERED_OFF)) { // // If the device is a unimodem device and indicated as powered off // see if we can revive it by opening the line. // if (!OpenTapiLine( LineInfo )) { DebugPrintEx(DEBUG_ERR, TEXT("OpenTapiLine failed for Device [%s] (ec: %ld)"), LineInfo->DeviceName, GetLastError()); LineInfo->State = 0; // remove the FPS_AVAILABLE bit' SelectedLine = LineInfo; } } break; } } } else { // // The user wants us to find a free device for him. This is only valid for send operations // which are not handoff. // Assert( JT_SEND == JobType ); DWORD dwNumDevices, dwCountryCode, dwAreaCode; Assert (FaxNumber); // // Check DialAsEntered case // BOOL bCanonicalAddress = FALSE; BOOL bDialAsEntered = FALSE; ec = IsCanonicalAddress (FaxNumber, &bCanonicalAddress, &dwCountryCode, &dwAreaCode, NULL); if (ERROR_SUCCESS != ec) { DebugPrintEx(DEBUG_ERR, TEXT("IsCanoicalAddress failed with error %ld"), ec); goto exit; } if (TRUE == bCanonicalAddress) { LPLINECOUNTRYLIST lpCountryList = NULL; // // Get the cached all countries list. // if (!(lpCountryList = GetCountryList())) { DebugPrintEx( DEBUG_ERR, TEXT("Can not get all countries cached list")); ec = ERROR_NOT_ENOUGH_MEMORY; goto exit; } if (IsAreaCodeMandatory(lpCountryList, dwCountryCode) == TRUE && ROUTING_RULE_AREA_CODE_ANY == dwAreaCode) { // // The area code is missing - dial as entered // DebugPrintEx(DEBUG_WRN, TEXT("Area code is mandatory for Country code %ld, FaxNumber - %s. The number will be dialed as entered"), dwCountryCode, FaxNumber); bDialAsEntered = TRUE; } } else { // // Not a canonical address - dial as entered // bDialAsEntered = TRUE; } if (FALSE == bDialAsEntered) { EnterCriticalSection( &g_CsConfig ); ec = GetDeviceListByCountryAndAreaCode( dwCountryCode, dwAreaCode, &lpdwDevices, &dwNumDevices); if (ERROR_SUCCESS != ec) { DebugPrintEx(DEBUG_ERR, TEXT("GetDeviceListByCountryAndAreaCode failed with error %ld"), ec); LeaveCriticalSection( &g_CsConfig ); goto exit; } } else { // // Dial As Entered case // // // Bring List of Devices from "All Devices" group // EnterCriticalSection( &g_CsConfig ); PCGROUP pCGroup; pCGroup = g_pGroupsMap->FindGroup (ROUTING_GROUP_ALL_DEVICESW); if (NULL == pCGroup) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("g_pGroupsMap->FindGroup(ROUTING_GROUP_ALL_DEVICESW) failed (ec %ld)"), ec); LeaveCriticalSection( &g_CsConfig ); goto exit; } ec = pCGroup->SerializeDevices (&lpdwDevices, &dwNumDevices); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_ERR, TEXT("pCGroup->SerializeDevices(&lpdwDevices, &dwNumDevices) failed (ec %ld)"), ec); LeaveCriticalSection( &g_CsConfig ); goto exit; } } LeaveCriticalSection( &g_CsConfig ); for (DWORD i = 0; i < dwNumDevices; i++) { Next = g_TapiLinesListHead.Flink; Assert (Next); while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { LineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = LineInfo->ListEntry.Flink; if ( (LineInfo->Flags & FPF_SEND) && lpdwDevices[i] == LineInfo->PermanentLineID) { if ( (LineInfo->Flags & FPF_POWERED_OFF) || (LineInfo->Flags & FPF_RECEIVE) ) { // // The device is marked as powered off. Check if we should try to send using this device // DWORDLONG dwlCurrentTime; DWORDLONG dwlElapsedTime; GetSystemTimeAsFileTime ((FILETIME*)&dwlCurrentTime); Assert (dwlCurrentTime >= LineInfo->LastLineClose); dwlElapsedTime = dwlCurrentTime - LineInfo->LastLineClose; if (dwlElapsedTime < SecToNano(g_dwMaxLineCloseTime)) { // // Not enough time passes since the last LINE_CLOSE. skip this device // continue; } } // // The device is capable of sending and is not marked as FPF_POWERED_OFF. // // // If it is a Tapi device, try to verify it s not busy // if (LineInfo->State == FPS_AVAILABLE && !(LineInfo->JobEntry) && !(LineInfo->Flags & FPF_VIRTUAL)) { if (NULL == LineInfo->hLine) { if (!OpenTapiLine( LineInfo )) { DebugPrintEx(DEBUG_ERR, TEXT("OpenTapiLine failed for Device [%s] (ec: %ld)"), LineInfo->DeviceName, GetLastError()); continue; } } LPLINEDEVSTATUS pLineDevStatus = NULL; BOOL fLineBusy = FALSE; // // check to see if the line is in use // pLineDevStatus = MyLineGetLineDevStatus( LineInfo->hLine ); if (NULL != pLineDevStatus) { if (pLineDevStatus->dwNumOpens > 0 && pLineDevStatus->dwNumActiveCalls > 0) { fLineBusy = TRUE; } MemFree( pLineDevStatus ); } else { // Assume the line is busy DebugPrintEx(DEBUG_ERR, TEXT("MyLineGetLineDevStatus failed for Device [%s] (ec: %ld)"), LineInfo->DeviceName, GetLastError()); fLineBusy = TRUE; } ReleaseTapiLine( LineInfo, NULL ); if (TRUE == fLineBusy) { continue; } } if ((LineInfo->State == FPS_AVAILABLE) && !(LineInfo->JobEntry)) { // // The line is free // LineInfo->State = 0; SelectedLine = LineInfo; } break; // out of while } } if (SelectedLine != NULL) { break; // out of for } } } if (SelectedLine) { DebugPrintEx(DEBUG_MSG, TEXT("Line selected for FAX operation: %d, %d"), SelectedLine->DeviceId, SelectedLine->PermanentLineID ); } Assert (ERROR_SUCCESS == ec); exit: MemFree (lpdwDevices); LeaveCriticalSection( &g_CsLine ); if (ERROR_SUCCESS == ec && NULL == SelectedLine) { ec = ERROR_NOT_FOUND; } SetLastError (ec); return SelectedLine; } BOOL ReleaseTapiLine( PLINE_INFO LineInfo, HCALL hCall ) /*++ Routine Description: Releases the specified TAPI line back into the list as an available device. Closes the line and deallocates the call. (line is not closed for a receive enabled device. Arguments: LineInfo - Pointer to the TAPI line to be released Return Value: TRUE - The line is release. FALSE - The line is not released. --*/ { LONG rVal; HLINE hLine; DEBUG_FUNCTION_NAME(TEXT("ReleaseTapiLine")); Assert(LineInfo); if (!LineInfo) { return FALSE; } EnterCriticalSection( &g_CsLine ); LineInfo->State = FPS_AVAILABLE; LineInfo->RingCount = 0; hLine = LineInfo->hLine; ZeroMemory( &LineInfo->LineMsgOffering, sizeof(LINEMESSAGE) ); if (hCall) { rVal = lineDeallocateCall( hCall ); if (rVal != 0) { DebugPrintEx( DEBUG_ERR, TEXT("lineDeallocateCall() failed, ec=0x%08X, hLine=0x%08X hCall=0x%08X"), rVal, hLine, hCall); } else { if (LineInfo->JobEntry && LineInfo->JobEntry->CallHandle == hCall) { LineInfo->JobEntry->CallHandle = 0; } } } else { DebugPrintEx( DEBUG_WRN, TEXT("ReleaseTapiLine(): cannot deallocate call, NULL call handle")); } // // We actually close the line (by lineClose) only if the line is not // intended for receiving. // if (!(LineInfo->Flags & FPF_RECEIVE) && // Line is not set to auto-receive and LineInfo->hLine && // line is open and LineInfo->PermanentLineID != g_dwManualAnswerDeviceId // this device is not set to manual answer mode ) { // // Attempt to close the line // LONG lRes; LineInfo->hLine = 0; lRes=lineClose( hLine ); if (!lRes) { DebugPrintEx( DEBUG_MSG, TEXT("hLine 0x%08X closed successfuly"), hLine ); } else { DebugPrintEx( DEBUG_ERR, TEXT("Failed to close hLine 0x%08X (ec: 0x%08X)"), hLine, lRes); } } LeaveCriticalSection( &g_CsLine ); return TRUE; } LPLINEDEVSTATUS MyLineGetLineDevStatus( HLINE hLine ) { DWORD LineDevStatusSize; LPLINEDEVSTATUS LineDevStatus = NULL; LONG Rslt = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(_T("lineGetLineDevStatus")); // // allocate the initial linedevstatus structure // LineDevStatusSize = sizeof(LINEDEVSTATUS) + 4096; LineDevStatus = (LPLINEDEVSTATUS) MemAlloc( LineDevStatusSize ); if (!LineDevStatus) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } LineDevStatus->dwTotalSize = LineDevStatusSize; Rslt = lineGetLineDevStatus( hLine, LineDevStatus ); if (Rslt != 0) { DebugPrintEx( DEBUG_ERR, TEXT("lineGetLineDevStatus() failed, ec=0x%08x"), Rslt ); goto exit; } if (LineDevStatus->dwNeededSize > LineDevStatus->dwTotalSize) { // // re-allocate the LineDevStatus structure // LineDevStatusSize = LineDevStatus->dwNeededSize; MemFree( LineDevStatus ); LineDevStatus = (LPLINEDEVSTATUS) MemAlloc( LineDevStatusSize ); if (!LineDevStatus) { Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } Rslt = lineGetLineDevStatus( hLine, LineDevStatus ); if (Rslt != 0) { DebugPrintEx( DEBUG_ERR, TEXT("lineGetLineDevStatus() failed, ec=0x%08x"), Rslt ); goto exit; } } exit: if (Rslt != ERROR_SUCCESS) { MemFree( LineDevStatus ); LineDevStatus = NULL; SetLastError(Rslt); } return LineDevStatus; } LONG MyLineGetTransCaps( LPLINETRANSLATECAPS *LineTransCaps ) { DWORD LineTransCapsSize; LONG Rslt = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(_T("MyLineGetTransCaps")); // // allocate the initial linetranscaps structure // LineTransCapsSize = sizeof(LINETRANSLATECAPS) + 4096; *LineTransCaps = (LPLINETRANSLATECAPS) MemAlloc( LineTransCapsSize ); if (!*LineTransCaps) { DebugPrintEx (DEBUG_ERR, TEXT("MemAlloc() failed, sz=0x%08x"), LineTransCapsSize); Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } (*LineTransCaps)->dwTotalSize = LineTransCapsSize; Rslt = lineGetTranslateCaps( g_hLineApp, MAX_TAPI_API_VER, *LineTransCaps ); if (Rslt != 0) { DebugPrintEx(DEBUG_ERR, TEXT("lineGetTranslateCaps() failed, ec=0x%08x"), Rslt); goto exit; } if ((*LineTransCaps)->dwNeededSize > (*LineTransCaps)->dwTotalSize) { // // re-allocate the LineTransCaps structure // LineTransCapsSize = (*LineTransCaps)->dwNeededSize; MemFree( *LineTransCaps ); *LineTransCaps = (LPLINETRANSLATECAPS) MemAlloc( LineTransCapsSize ); if (!*LineTransCaps) { DebugPrintEx(DEBUG_ERR, TEXT("MemAlloc() failed, sz=0x%08x"), LineTransCapsSize); Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } (*LineTransCaps)->dwTotalSize = LineTransCapsSize; Rslt = lineGetTranslateCaps( g_hLineApp, MAX_TAPI_API_VER, *LineTransCaps ); if (Rslt != 0) { DebugPrintEx(DEBUG_ERR, TEXT("lineGetTranslateCaps() failed, ec=0x%08x"), Rslt); goto exit; } } exit: if (Rslt != ERROR_SUCCESS) { MemFree( *LineTransCaps ); *LineTransCaps = NULL; } return Rslt; } /****************************************************************************** * Name: OpenTapiLine * Author: ******************************************************************************* DESCRIPTION: - Opens the specified TAPI line with the right media modes and ownership. Supports both Unimodem devices and fax boards. - Sets the line so the required lines and address state events will be delivered. PARAMETERS: [IN / OUT ] LineInfo A pointer to a LINE_INFO structure that contains the line information. LINE_INFO.hLine is set to the open line handle if the operation succeeds. RETURN VALUE: TRUE if no error occured. FALSE otherwise. Does not explicitly set LastError. REMARKS: NONE. *******************************************************************************/ BOOL OpenTapiLine( PLINE_INFO LineInfo ) { LONG Rslt = ERROR_SUCCESS; HLINE hLine; DWORD LineStates = 0; DWORD AddressStates = 0; DEBUG_FUNCTION_NAME(_T("OpenTapiLine")); EnterCriticalSection( &g_CsLine ); if (LineInfo->UnimodemDevice) { Rslt = lineOpen( g_hLineApp, LineInfo->DeviceId, &hLine, MAX_TAPI_API_VER, 0, (DWORD_PTR) LineInfo, // Note that the LineInfo pointer is used as CallbackInstance data. This means we will // get the LineInfo pointer each time we get a TAPI message. LINECALLPRIVILEGE_OWNER + LINECALLPRIVILEGE_MONITOR, LINEMEDIAMODE_DATAMODEM | LINEMEDIAMODE_UNKNOWN, NULL ); if (Rslt != ERROR_SUCCESS) { Rslt = lineOpen( g_hLineApp, LineInfo->DeviceId, &hLine, MAX_TAPI_API_VER, 0, (DWORD_PTR) LineInfo, LINECALLPRIVILEGE_OWNER + LINECALLPRIVILEGE_MONITOR, LINEMEDIAMODE_DATAMODEM, NULL ); } } else { Rslt = lineOpen( g_hLineApp, LineInfo->DeviceId, &hLine, MAX_TAPI_API_VER, 0, (DWORD_PTR) LineInfo, LINECALLPRIVILEGE_OWNER + LINECALLPRIVILEGE_MONITOR, LINEMEDIAMODE_G3FAX, NULL ); } if (Rslt != ERROR_SUCCESS) { DebugPrintEx( DEBUG_ERR,TEXT("Device %s FAILED to initialize, ec=%08x"), LineInfo->DeviceName, Rslt ); } else { LineInfo->hLine = hLine; // // set the line status that we need // LineStates |= LineInfo->LineStates & LINEDEVSTATE_OPEN ? LINEDEVSTATE_OPEN : 0; LineStates |= LineInfo->LineStates & LINEDEVSTATE_CLOSE ? LINEDEVSTATE_CLOSE : 0; LineStates |= LineInfo->LineStates & LINEDEVSTATE_RINGING ? LINEDEVSTATE_RINGING : 0; LineStates |= LineInfo->LineStates & LINEDEVSTATE_NUMCALLS ? LINEDEVSTATE_NUMCALLS : 0; LineStates |= LineInfo->LineStates & LINEDEVSTATE_REMOVED ? LINEDEVSTATE_REMOVED : 0; AddressStates = LINEADDRESSSTATE_INUSEZERO | LINEADDRESSSTATE_INUSEONE | LINEADDRESSSTATE_INUSEMANY | LINEADDRESSSTATE_NUMCALLS; Rslt = lineSetStatusMessages( hLine, LineStates, AddressStates ); if (Rslt != 0) { DebugPrintEx(DEBUG_ERR,TEXT("lineSetStatusMessages() failed, [0x%08x:0x%08x], ec=0x%08x"), LineStates, AddressStates, Rslt ); Rslt = ERROR_SUCCESS; } } LeaveCriticalSection( &g_CsLine ); if (ERROR_SUCCESS != Rslt) { // // We set the modem to be FPF_POWERED_OFF to make sure we will not try to constantly resend // on this device. After MAX_LINE_CLOSE_TIME we will retry to send on this device. // LineInfo->hLine = NULL; LineInfo->Flags |= FPF_POWERED_OFF; GetSystemTimeAsFileTime((FILETIME*)&LineInfo->LastLineClose); // // Can not map the TAPI error to a win error so we just return general failure. // We do generate a debug output with the actual error earlier in this code. // SetLastError(ERROR_GEN_FAILURE); return FALSE; } else { LineInfo->Flags &= ~FPF_POWERED_OFF; return TRUE; } } BOOL CALLBACK NewDeviceRoutingMethodEnumerator( PROUTING_METHOD RoutingMethod, DWORD DeviceId ) { BOOL Rslt = FALSE; DEBUG_FUNCTION_NAME(_T("NewDeviceRoutingMethodEnumerator")); __try { Rslt = RoutingMethod->RoutingExtension->FaxRouteDeviceChangeNotification( DeviceId, TRUE ); } __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_ROUTING_EXT, RoutingMethod->RoutingExtension->FriendlyName, GetExceptionCode())) { ASSERT_FALSE; } return Rslt; } BOOL AddNewDevice( DWORD DeviceId, LPLINEDEVCAPS LineDevCaps, BOOL fServerInitialization, PREG_FAX_DEVICES pInputFaxReg ) { BOOL rVal = FALSE; BOOL UnimodemDevice = FALSE; PMDM_DEVSPEC MdmDevSpec = NULL; LPSTR ModemKey = NULL; LPTSTR DeviceName = NULL; REG_SETUP RegSetup = {0}; DWORD dwUniqueLineId = 0; PDEVICE_PROVIDER lpProvider; LPTSTR lptstrTSPName; DWORD ec = ERROR_SUCCESS; BOOL fDeviceAddedToMap = FALSE; DWORD dwDeviceType = FAX_DEVICE_TYPE_OLD; DEBUG_FUNCTION_NAME(TEXT("AddNewDevice")); // // only add devices that support fax // if (! ( ((LineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM) && (UnimodemDevice = IsDeviceModem( LineDevCaps, FAX_MODEM_PROVIDER_NAME ) )) || (LineDevCaps->dwMediaModes & LINEMEDIAMODE_G3FAX) )) { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } if (!(LineDevCaps->dwProviderInfoSize)) { DebugPrintEx( DEBUG_ERR, TEXT("No device provider information")); SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } if (!GetOrigSetupData( LineDevCaps->dwPermanentLineID, &RegSetup )) { DebugPrintEx( DEBUG_ERR, TEXT("GetOrigSetupData failed (ec: %ld)"), GetLastError()); return FALSE; } if (LineDevCaps->dwLineNameSize) { DeviceName = FixupDeviceName( (LPTSTR)((LPBYTE) LineDevCaps + LineDevCaps->dwLineNameOffset) ); if (NULL == DeviceName) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("FixupDeviceName failed (ec: %ld)"), ec); rVal = FALSE; goto exit; } } else { // // The TSP did not provide device name. DO not add the device // ec = ERROR_BAD_FORMAT; DebugPrintEx( DEBUG_ERR, TEXT("Device %d does not have device name, not adding the device"), DeviceId); rVal = FALSE; goto exit; } // // Find the device provider for this device using the TAPI provider name. // lptstrTSPName = (LPTSTR)((LPBYTE) LineDevCaps + LineDevCaps->dwProviderInfoOffset) ; lpProvider = FindDeviceProvider( lptstrTSPName); if (!lpProvider) { DebugPrintEx( DEBUG_ERR, TEXT("Could not find a valid device provider for TAPI device: [%s]. (Looking for TSP : [%s])"), DeviceName, lptstrTSPName ); rVal = FALSE; goto exit; } Assert (FAX_PROVIDER_STATUS_SUCCESS == lpProvider->Status); // try to find this device from service registry and update RegSetup if found if ( pInputFaxReg ) { dwUniqueLineId = FindServiceDeviceByTapiPermanentLineID ( LineDevCaps->dwPermanentLineID, DeviceName, &RegSetup, pInputFaxReg ); } // try to find this device in device cache and update RegSetup if found if ( 0 == dwUniqueLineId ) { BOOL fManualAnswer = FALSE; if (0 != (dwUniqueLineId = FindCacheEntryByTapiPermanentLineID( LineDevCaps->dwPermanentLineID, DeviceName, &RegSetup, &g_dwLastUniqueLineId, &fManualAnswer))) { // // The device was found in the cache // dwDeviceType = FAX_DEVICE_TYPE_CACHED; if (TRUE == fManualAnswer) { // // The device was set to manual answer when moved to the cache // dwDeviceType |= FAX_DEVICE_TYPE_MANUAL_ANSWER; } } } // still 0 so, add this new device to registry if ( 0 == dwUniqueLineId ) { dwDeviceType = FAX_DEVICE_TYPE_NEW; ec = RegAddNewFaxDevice( &g_dwLastUniqueLineId, &dwUniqueLineId, // Create new device. DeviceName, (LPTSTR)((LPBYTE) LineDevCaps + LineDevCaps->dwProviderInfoOffset), lpProvider->szGUID, RegSetup.Csid, RegSetup.Tsid, LineDevCaps->dwPermanentLineID, RegSetup.Flags, RegSetup.Rings); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_WRN, TEXT("RegAddNewFaxDevice() failed for Tapi permanent device id: %ld (ec: %ld)"), LineDevCaps->dwPermanentLineID, ec); rVal = FALSE; goto exit; } } ec = g_pTAPIDevicesIdsMap->AddDevice (LineDevCaps->dwPermanentLineID, dwUniqueLineId); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_WRN, TEXT("g_pTAPIDevicesIdsMap->AddDevice() failed for Tapi permanent device id: %ld (ec: %ld)"), LineDevCaps->dwPermanentLineID, ec); rVal = FALSE; goto exit; } fDeviceAddedToMap = TRUE; ec = InitializeTapiLine( DeviceId, dwUniqueLineId, LineDevCaps, RegSetup.Rings, RegSetup.Flags, RegSetup.Csid, RegSetup.Tsid, RegSetup.lptstrDescription, fServerInitialization, dwDeviceType ); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_WRN, TEXT("InitializeTapiLine failed for Fax unique device id: %ld (ec: %ld)"), dwUniqueLineId, ec); rVal = FALSE; goto exit; } if (FALSE == fServerInitialization) { PLINE_INFO pLineInfo = NULL; // // Close the line if the device is not receive enabled // pLineInfo = GetTapiLineFromDeviceId (dwUniqueLineId, FALSE); if (pLineInfo) { if (!(pLineInfo->Flags & FPF_RECEIVE) && // Device is not set to auto-receive and pLineInfo->hLine && // device is open and pLineInfo->PermanentLineID != g_dwManualAnswerDeviceId // this device is not set to manual answer mode ) { // // Attempt to close the device // HLINE hLine = pLineInfo->hLine; LONG Rslt; pLineInfo->hLine = 0; Rslt = lineClose( hLine ); if (Rslt) { if (LINEERR_INVALLINEHANDLE != Rslt) { DebugPrintEx( DEBUG_ERR, TEXT("lineClose() for line %s [Permanent Id: %010d] has failed. (ec: %ld)"), pLineInfo->DeviceName, pLineInfo->TapiPermanentLineId, Rslt); } else { // // We can get LINEERR_INVALLINEHANDLE if we got LINE_CLOSE // from TAPI. // DebugPrintEx( DEBUG_WRN, TEXT("lineClose() for line %s [Permanent Id: %010d] reported LINEERR_INVALLINEHANDLE. (May be caused by LINE_CLOSE event)"), pLineInfo->DeviceName, pLineInfo->TapiPermanentLineId ); } } } } else { // // We must find the line because InitializeTapiLine() did not fail // ASSERT_FALSE; } if (!g_pGroupsMap->UpdateAllDevicesGroup()) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("COutboundRoutingGroupsMap::UpdateAllDevicesGroup() failed (ec: %ld)"), ec); // // We failed to update group. Remove the line. // if (pLineInfo) { RemoveEntryList (&pLineInfo->ListEntry); // // Update enabled device counter // if (TRUE == IsDeviceEnabled(pLineInfo)) { Assert (g_dwDeviceEnabledCount); g_dwDeviceEnabledCount -= 1; } FreeTapiLine (pLineInfo); g_dwDeviceCount -= 1; } rVal = FALSE; goto exit; } // // Call CreateConfigEvent only when LINE_CREATE event accure during service operation // and not during start up // ec = CreateConfigEvent (FAX_CONFIG_TYPE_DEVICES); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_ERR, TEXT("CreateConfigEvent(FAX_CONFIG_TYPE_DEVICES) failed (ec: %lc)"), ec); } ec = CreateConfigEvent (FAX_CONFIG_TYPE_OUT_GROUPS); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_ERR, TEXT("CreateConfigEvent(FAX_CONFIG_TYPE_OUT_GROUPS) failed (ec: %lc)"), ec); } } rVal = TRUE; exit: if (DeviceName) { MemFree( DeviceName ); } if (FALSE == rVal && TRUE == fDeviceAddedToMap) { DWORD dwRes = g_pTAPIDevicesIdsMap->RemoveDevice (LineDevCaps->dwPermanentLineID); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("Cg_pTAPIDevicesIdsMap->RemoveDevice failed (ec: %lc)"), dwRes); } } FreeOrigSetupData( &RegSetup ); EnumerateRoutingMethods( (PFAXROUTEMETHODENUM)NewDeviceRoutingMethodEnumerator, UlongToPtr(dwUniqueLineId) ); if (FALSE == rVal) { SetLastError(ec); } return rVal; } // AddNewDevice DWORD InitializeTapiLine( DWORD DeviceId, DWORD dwUniqueLineId, LPLINEDEVCAPS LineDevCaps, DWORD Rings, DWORD Flags, LPTSTR Csid, LPTSTR Tsid, LPTSTR lptstrDescription, BOOL fServerInit, DWORD dwDeviceType ) { PLINE_INFO LineInfo = NULL; LONG Rslt = ERROR_SUCCESS; DWORD len; PDEVICE_PROVIDER Provider; BOOL UnimodemDevice; HLINE hLine = 0; LPTSTR ProviderName = NULL; LPTSTR DeviceName = NULL; BOOL NewDevice = TRUE; DWORD LineStates = 0; DWORD AddressStates = 0; LPLINEDEVSTATUS LineDevStatus; DEBUG_FUNCTION_NAME(TEXT("InitializeTapiLine")); Assert(dwUniqueLineId); // // allocate the LINE_INFO structure // LineInfo = (PLINE_INFO) MemAlloc( sizeof(LINE_INFO) ); if (!LineInfo) { Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } ZeroMemory(LineInfo, sizeof(LINE_INFO)); // // get the provider name // len = _tcslen( (LPTSTR)((LPBYTE) LineDevCaps + LineDevCaps->dwProviderInfoOffset) ); ProviderName = (LPTSTR)(MemAlloc( (len + 1) * sizeof(TCHAR) )); if (!ProviderName) { Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } _tcscpy( ProviderName, (LPTSTR)((LPBYTE) LineDevCaps + LineDevCaps->dwProviderInfoOffset) ); // // get the device name // DeviceName = FixupDeviceName( (LPTSTR)((LPBYTE) LineDevCaps + LineDevCaps->dwLineNameOffset) ); if (!DeviceName) { Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } // // verify that the line id is good // if (LineDevCaps->dwPermanentLineID == 0) { DebugPrintEx(DEBUG_ERR, TEXT("TAPI lines must have a non-zero line id [%s]"), DeviceName); Rslt = ERROR_BAD_DEVICE; goto exit; } // // check for a modem device // UnimodemDevice = IsDeviceModem( LineDevCaps, FAX_MODEM_PROVIDER_NAME ); // // assign the device provider // Provider = FindDeviceProvider( ProviderName ); if (!Provider) { DebugPrintEx(DEBUG_ERR, TEXT("Could not find a valid device provider for device: %s"), DeviceName); Rslt = ERROR_BAD_PROVIDER; goto exit; } Assert (FAX_PROVIDER_STATUS_SUCCESS == Provider->Status); // // open the line // if (UnimodemDevice) { Rslt = lineOpen( g_hLineApp, DeviceId, &hLine, MAX_TAPI_API_VER, 0, (DWORD_PTR) LineInfo, LINECALLPRIVILEGE_OWNER + LINECALLPRIVILEGE_MONITOR, LINEMEDIAMODE_DATAMODEM | LINEMEDIAMODE_UNKNOWN, NULL ); if (Rslt != 0) { Rslt = lineOpen( g_hLineApp, DeviceId, &hLine, MAX_TAPI_API_VER, 0, (DWORD_PTR) LineInfo, LINECALLPRIVILEGE_OWNER + LINECALLPRIVILEGE_MONITOR, LINEMEDIAMODE_DATAMODEM, NULL ); } } else { Rslt = lineOpen( g_hLineApp, DeviceId, &hLine, MAX_TAPI_API_VER, 0, (DWORD_PTR) LineInfo, LINECALLPRIVILEGE_OWNER + LINECALLPRIVILEGE_MONITOR, LINEMEDIAMODE_G3FAX, NULL ); } if (Rslt != 0) { DebugPrintEx(DEBUG_ERR, TEXT("Device %s FAILED to initialize, ec=%08x"), DeviceName, Rslt); goto exit; } // // Set hLine in the LINE_INFO structure so it will be freed on failure // LineInfo->hLine = hLine; // // set the line status that we need // LineStates |= LineDevCaps->dwLineStates & LINEDEVSTATE_OPEN ? LINEDEVSTATE_OPEN : 0; LineStates |= LineDevCaps->dwLineStates & LINEDEVSTATE_CLOSE ? LINEDEVSTATE_CLOSE : 0; LineStates |= LineDevCaps->dwLineStates & LINEDEVSTATE_RINGING ? LINEDEVSTATE_RINGING : 0; LineStates |= LineDevCaps->dwLineStates & LINEDEVSTATE_NUMCALLS ? LINEDEVSTATE_NUMCALLS : 0; LineStates |= LineDevCaps->dwLineStates & LINEDEVSTATE_REMOVED ? LINEDEVSTATE_REMOVED : 0; AddressStates = LINEADDRESSSTATE_INUSEZERO | LINEADDRESSSTATE_INUSEONE | LINEADDRESSSTATE_INUSEMANY | LINEADDRESSSTATE_NUMCALLS; Rslt = lineSetStatusMessages( LineInfo->hLine, LineStates, AddressStates ); if (Rslt != 0) { DebugPrintEx(DEBUG_ERR, TEXT("lineSetStatusMessages() failed, [0x%08x:0x%08x], ec=0x%08x"), LineStates, AddressStates, Rslt); if (Rslt == LINEERR_INVALLINEHANDLE) { LineInfo->hLine = 0; } Rslt = 0; } // // now assign the necessary values to the line info struct // LineInfo->Signature = LINE_SIGNATURE; LineInfo->DeviceId = DeviceId; LineInfo->TapiPermanentLineId = LineDevCaps->dwPermanentLineID; LineInfo->Provider = Provider; LineInfo->UnimodemDevice = UnimodemDevice; LineInfo->State = FPS_AVAILABLE; LineInfo->dwReceivingJobsCount = 0; LineInfo->dwSendingJobsCount = 0; LineInfo->LastLineClose = 0; if (DeviceName) { LineInfo->DeviceName = StringDup( DeviceName ); if (!LineInfo->DeviceName) { Rslt = GetLastError (); goto exit; } } else { LineInfo->DeviceName = NULL; } if (Csid) { LineInfo->Csid = StringDup( Csid ); if (!LineInfo->Csid) { Rslt = GetLastError (); goto exit; } } else { LineInfo->Csid = NULL; } if (Tsid) { LineInfo->Tsid = StringDup( Tsid ); if (!LineInfo->Tsid) { Rslt = GetLastError (); goto exit; } } else { LineInfo->Tsid = NULL; } if (lptstrDescription) { LineInfo->lptstrDescription = StringDup( lptstrDescription ); if (!LineInfo->lptstrDescription) { Rslt = GetLastError (); goto exit; } } else { LineInfo->lptstrDescription = NULL; } LineInfo->RingsForAnswer = (LineDevCaps->dwLineStates & LINEDEVSTATE_RINGING) ? Rings : 0; LineInfo->Flags = Flags; LineInfo->RingCount = 0; LineInfo->LineStates = LineDevCaps->dwLineStates; LineInfo->PermanentLineID = dwUniqueLineId; LineInfo->dwDeviceType = dwDeviceType; if (LineInfo->hLine) { // // check to see if the line is in use // LineDevStatus = MyLineGetLineDevStatus( LineInfo->hLine ); if (LineDevStatus) { if (LineDevStatus->dwNumOpens > 0 && LineDevStatus->dwNumActiveCalls > 0) { LineInfo->ModemInUse = TRUE; } MemFree( LineDevStatus ); } } else { // // if we don't have a line handle at this time then the // device must be powered off // DebugPrintEx(DEBUG_ERR, TEXT("Device %s is powered off or disconnected"), DeviceName); LineInfo->Flags |= FPF_POWERED_OFF; // // Since this function is called from TapiInitialize(), we don't have an RPC server up and running yet. // Don't create a FAX_EVENT_TYPE_DEVICE_STATUS event. // } exit: MemFree( DeviceName ); MemFree( ProviderName ); if (Rslt == ERROR_SUCCESS) { InsertTailList( &g_TapiLinesListHead, &LineInfo->ListEntry ); g_dwDeviceCount += 1; if (FALSE == fServerInit) { // // Add cached manual answer device and check device limit // if (0 == g_dwManualAnswerDeviceId && // There is no manual answer device LineInfo->dwDeviceType == (FAX_DEVICE_TYPE_CACHED | FAX_DEVICE_TYPE_MANUAL_ANSWER) && // this is a cached manual answer device !(LineInfo->Flags & FPF_RECEIVE)) // the device is not set to auto receive { // // set this device as manual receive // g_dwManualAnswerDeviceId = LineInfo->PermanentLineID; DWORD dwRes = WriteManualAnswerDeviceId (g_dwManualAnswerDeviceId); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("WriteManualAnswerDeviceId(0) (ec: %lc)"), dwRes); } } if (g_dwDeviceEnabledCount >= g_dwDeviceEnabledLimit) { // // We reached device limit on this SKU. set this device's flags to 0. // DebugPrintEx( DEBUG_WRN, TEXT("Reached device limit on this SKU. reset device flags to 0. Device limit: %ld. Current device: %ld"), g_dwDeviceEnabledLimit, g_dwDeviceEnabledCount); ResetDeviceFlags(LineInfo); } } // // Update enabled device counter // if (TRUE == IsDeviceEnabled(LineInfo)) { g_dwDeviceEnabledCount += 1; } } else { FreeTapiLine( LineInfo ); } return Rslt; } // InitializeTapiLine BOOL IsVirtualDevice( const LINE_INFO *pLineInfo ) { if (!pLineInfo) { return FALSE; } return (pLineInfo->Provider->FaxDevVirtualDeviceCreation != NULL); } VOID UpdateVirtualDeviceSendAndReceiveStatus( PLINE_INFO pLineInfo, BOOL bSend, BOOL bReceive ) /*++ Routine name : UpdateVirtualDeviceSendAndReceiveStatus Routine description: Updates a virtual device with the new send and receive flags Author: Eran Yariv (EranY), Nov, 1999 Arguments: pLineInfo [in] - Pointer to line information bSend [in] - Can the device send faxes? bReceive [in] - Can the device receive faxes? Remarks: This function should be called with g_CsLine held. Return Value: None. --*/ { DEBUG_FUNCTION_NAME(TEXT("UpdateVirtualDeviceSendAndReceiveStatus")); if (!IsVirtualDevice(pLineInfo) || !pLineInfo->Provider->FaxDevCallback) { // // Not a virtual device or does not support FaxDevCallback // return; } __try { pLineInfo->Provider->FaxDevCallback( NULL, pLineInfo->TapiPermanentLineId, LINE_DEVSPECIFIC, 0, bReceive, bSend, 0 ); } __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, pLineInfo->Provider->FriendlyName, GetExceptionCode())) { ASSERT_FALSE; } } // UpdateVirtualDeviceSendAndReceiveStatus VOID UpdateVirtualDevices( VOID ) { PLIST_ENTRY Next; PLINE_INFO LineInfo = NULL; EnterCriticalSection( &g_CsLine ); Next = g_TapiLinesListHead.Flink; if (Next == NULL) { LeaveCriticalSection( &g_CsLine ); return; } while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { LineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = LineInfo->ListEntry.Flink; UpdateVirtualDeviceSendAndReceiveStatus (LineInfo, LineInfo->Flags & FPF_SEND, LineInfo->Flags & FPF_RECEIVE ); } LeaveCriticalSection( &g_CsLine ); } DWORD CreateVirtualDevices( PREG_FAX_SERVICE FaxReg, DWORD dwAPIVersion ) { PLIST_ENTRY Next; PDEVICE_PROVIDER Provider; PLINE_INFO LineInfo = NULL; PREG_DEVICE FaxDevice = NULL; PREG_FAX_DEVICES FaxDevices = NULL; DWORD dwVirtualDeviceCount = 0; REG_SETUP RegSetup={0}; DWORD ec; DEBUG_FUNCTION_NAME(TEXT("CreateVirtualDevices")); Next = g_DeviceProvidersListHead.Flink; if (!Next) { return dwVirtualDeviceCount; } if (!GetOrigSetupData( 0, &RegSetup )) { return dwVirtualDeviceCount; } while ((ULONG_PTR)Next != (ULONG_PTR)&g_DeviceProvidersListHead) { DWORD dwDeviceCount; dwDeviceCount = 0; Provider = CONTAINING_RECORD( Next, DEVICE_PROVIDER, ListEntry ); Next = Provider->ListEntry.Flink; if (Provider->Status != FAX_PROVIDER_STATUS_SUCCESS) { // // This FSP wasn't loaded successfully - skip it // continue; } if (Provider->dwAPIVersion != dwAPIVersion) { // // This FSP doesn't match the required API version - skip it // continue; } if (FSPI_API_VERSION_1 == Provider->dwAPIVersion) { if (Provider->FaxDevVirtualDeviceCreation) { if (!CreateLegacyVirtualDevices(FaxReg, &RegSetup, Provider, &dwDeviceCount)) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("CreateLegacyVirtualDevices failed for provider [%s] (ec: %ld)"), Provider->FriendlyName, ec); goto InitializationFailure; } else { DebugPrintEx( DEBUG_MSG, TEXT("%ld Legacy Virtual devices added by provider [%s]"), dwDeviceCount, Provider->FriendlyName, ec); } } } else { DebugPrintEx( DEBUG_ERR, TEXT("Unsupported API Version (0x%08X) for provider [%s]"), Provider->dwAPIVersion, Provider->FriendlyName); Assert(FALSE); goto InitializationFailure; } dwVirtualDeviceCount+= dwDeviceCount; goto next; InitializationFailure: Provider->Status = FAX_PROVIDER_STATUS_CANT_INIT; FaxLog( FAXLOG_CATEGORY_INIT, FAXLOG_LEVEL_MIN, 1, MSG_VIRTUAL_DEVICE_INIT_FAILED, Provider->FriendlyName ); next: ; } DebugPrintEx(DEBUG_MSG, TEXT("Virtual devices initialized, devices=%d"), g_dwDeviceCount); FreeOrigSetupData( &RegSetup ); return dwVirtualDeviceCount; } //********************************************************************************* //* Name: CreateLegacyVirtualDevices() //* Author: Ronen Barenboim //* Date: May 19, 1999 //********************************************************************************* //* DESCRIPTION: //* Creates the virtual line devices reported by a single FSP and adds them //* to the line list. Also persists the line information in the registry. //* PARAMETERS: //* [IN] PREG_FAX_SERVICE FaxReg //* //* [IN] const REG_SETUP * lpRegSetup //* //* [IN] const DEVICE_PROVIDER * lpcProvider //* A pointer to the provider information. This should be a virtual //* provider. //* [OUT] LPDWORD lpdwDeviceCount //* The number of virtual devices actually added. //* //* RETURN VALUE: //* TRUE //* If the creation succeeded. //* FALSE //* If the creation failed. Call GetLastError() to get extended error //* information. an error of ERROR_INVALID_FUNCTION indicates that //* the FSP creation function failed. //********************************************************************************* BOOL CreateLegacyVirtualDevices( PREG_FAX_SERVICE FaxReg, const REG_SETUP * lpRegSetup, DEVICE_PROVIDER * lpcProvider, LPDWORD lpdwDeviceCount) { DWORD VirtualDeviceCount = 0; WCHAR DevicePrefix[128] = {0}; DWORD DeviceIdPrefix; LPWSTR DeviceName = NULL; DWORD i,j; PLINE_INFO LineInfo = NULL; PREG_DEVICE FaxDevice = NULL; UINT nDevice; PLINE_INFO * lpAddedLines = NULL; DWORD ec = 0; PLIST_ENTRY Next; PLINE_INFO pLineInfo; Assert(lpcProvider); Assert(lpcProvider->FaxDevVirtualDeviceCreation); Assert(lpdwDeviceCount); Assert(FaxReg); Assert(lpRegSetup); DEBUG_FUNCTION_NAME(TEXT("CreateLegacyVirtualDevices")); (*lpdwDeviceCount) = 0; __try { if (!lpcProvider->FaxDevVirtualDeviceCreation( &VirtualDeviceCount, DevicePrefix, &DeviceIdPrefix, g_TapiCompletionPort, FAXDEV_EVENT_KEY )) { ec = ERROR_INVALID_FUNCTION; DebugPrintEx( DEBUG_ERR, TEXT("FaxDevVirtualDeviceCreation failed for provider [%s] (ec: %ld)"), lpcProvider->FriendlyName, GetLastError()); goto InitializationFailure; } } __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, lpcProvider->FriendlyName, GetExceptionCode())) { ASSERT_FALSE; } if (VirtualDeviceCount > 0) { if (VirtualDeviceCount > 256 ) { DebugPrintEx( DEBUG_MSG, TEXT("VirtualDeviceCount returned too many devices (%d)- limit to 256"), VirtualDeviceCount); VirtualDeviceCount = 256; } if ((DeviceIdPrefix == 0) || (DeviceIdPrefix >= DEFAULT_REGVAL_FAX_UNIQUE_DEVICE_ID_BASE)) { // // Provider uses device ids out of allowed range // ec = ERROR_INVALID_FUNCTION; DebugPrintEx( DEBUG_ERR, TEXT("Provider [%s] uses device ids base [%ld] out of allowed range."), lpcProvider->FriendlyName, DeviceIdPrefix); goto InitializationFailure; } // // Check if the range of device IDs does not conflict with an already loaded devices of another provider // Range [1 ... DEFAULT_REGVAL_FAX_UNIQUE_DEVICE_ID_BASE-1] : Reserved for VFSPs. // Since we cannot dictate the range of device ids the VFSPS use, we allocate a space for them // and leave segments allocation to a PM effort here. // Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { pLineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = pLineInfo->ListEntry.Flink; if (pLineInfo->PermanentLineID >= DeviceIdPrefix && pLineInfo->PermanentLineID <= DeviceIdPrefix + VirtualDeviceCount) { // // We have a conflict. log an event and do not load the devices // ec = ERROR_INVALID_FUNCTION; FaxLog( FAXLOG_CATEGORY_INIT, FAXLOG_LEVEL_MIN, 2, MSG_FAX_FSP_CONFLICT, lpcProvider->FriendlyName, pLineInfo->Provider->FriendlyName ); DebugPrintEx( DEBUG_ERR, TEXT("Provider [%s] uses device id [%ld] that conflicts with another FSP [%s]"), lpcProvider->FriendlyName, DeviceIdPrefix, pLineInfo->Provider->FriendlyName ); goto InitializationFailure; } } lpAddedLines = (PLINE_INFO *)MemAlloc(VirtualDeviceCount * sizeof(PLINE_INFO)); if (!lpAddedLines) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocate PLINE_INFO array. (ec: %ld)"), VirtualDeviceCount, GetLastError()); goto InitializationFailure; } memset(lpAddedLines, 0, VirtualDeviceCount * sizeof(PLINE_INFO)); for (i = 0; i < VirtualDeviceCount; i++) { DWORD dwUniqueLineId; // // create the device name // DeviceName = (LPWSTR) MemAlloc( StringSize(DevicePrefix) + 16 ); if (!DeviceName) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("MemAlloc() failed for DeviceName (ec: %ld)"), ec); goto InitializationFailure; } swprintf( DeviceName, L"%s%d", DevicePrefix, i ); // // find the registry information for this device // for (j = 0, FaxDevice = NULL; j < FaxReg->DeviceCount; j++) { if (TRUE == FaxReg->Devices[j].bValidDevice && !_tcscmp(FaxReg->Devices[j].lptstrProviderGuid, lpcProvider->szGUID)) { if (FaxReg->Devices[j].TapiPermanentLineID == DeviceIdPrefix+i) { FaxDevice = &FaxReg->Devices[j]; break; } } } // // if the device is new then add it to the registry // if (!FaxDevice) { // // We set the Fax Device Id to be the VFSP device id - we don't create one on our own // dwUniqueLineId = DeviceIdPrefix + i; RegAddNewFaxDevice( &g_dwLastUniqueLineId, &dwUniqueLineId, DeviceName, lpcProvider->ProviderName, lpcProvider->szGUID, lpRegSetup->Csid, lpRegSetup->Tsid, DeviceIdPrefix + i, (lpRegSetup->Flags | FPF_VIRTUAL), lpRegSetup->Rings ); } else { dwUniqueLineId = FaxDevice->PermanentLineId; Assert(dwUniqueLineId > 0); } // // allocate the LINE_INFO structure // LineInfo = (PLINE_INFO) MemAlloc( sizeof(LINE_INFO) ); if (!LineInfo) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocate LINE_INFO (ec: %ld). DeviceId: %010d DeviceName: %s"), GetLastError(), DeviceIdPrefix + i, DeviceName); goto InitializationFailure; } // // Save a pointer to it so we can free it if we crash ahead // lpAddedLines[*lpdwDeviceCount] = LineInfo; // // now assign the necessary values to the line info struct // LineInfo->Signature = LINE_SIGNATURE; LineInfo->DeviceId = i; LineInfo->TapiPermanentLineId = DeviceIdPrefix + i; LineInfo->PermanentLineID = dwUniqueLineId; Assert(LineInfo->PermanentLineID > 0); LineInfo->hLine = 0; LineInfo->Provider = (PDEVICE_PROVIDER)lpcProvider; LineInfo->DeviceName = DeviceName; // Note: DeviceName is heap allocated and need to be freed LineInfo->UnimodemDevice = FALSE; LineInfo->State = FPS_AVAILABLE; LineInfo->Csid = StringDup( FaxDevice ? FaxDevice->Csid : lpRegSetup->Csid ); LineInfo->Tsid = StringDup( FaxDevice ? FaxDevice->Tsid : lpRegSetup->Tsid ); LineInfo->lptstrDescription = FaxDevice ? StringDup(FaxDevice->lptstrDescription) : NULL; LineInfo->RingsForAnswer = 0; LineInfo->RingCount = 0; LineInfo->LineStates = 0; LineInfo->dwReceivingJobsCount = 0; LineInfo->dwSendingJobsCount = 0; LineInfo->LastLineClose = 0; // We do not use this for virtual devices LineInfo->dwDeviceType = FaxDevice ? FAX_DEVICE_TYPE_OLD : FAX_DEVICE_TYPE_NEW; LineInfo->Flags = FaxDevice ? FaxDevice->Flags : (lpRegSetup->Flags | FPF_VIRTUAL); InsertTailList( &g_TapiLinesListHead, &LineInfo->ListEntry ); (*lpdwDeviceCount)++; // // Update enabled device counter // if (TRUE == IsDeviceEnabled(LineInfo)) { g_dwDeviceEnabledCount += 1; } } } else { ec = ERROR_INVALID_FUNCTION; DebugPrintEx( DEBUG_ERR, TEXT("FaxDevVirtualDeviceCreation() reported 0 devices.")); goto InitializationFailure; } Assert( (*lpdwDeviceCount) == VirtualDeviceCount); Assert (0 == ec); goto Exit; InitializationFailure: Assert (0 != ec); // // Remove the added lines // if (lpAddedLines) { for (nDevice=0 ;nDevice < VirtualDeviceCount; nDevice++) { if (lpAddedLines[nDevice]) { // // Remove the LINE_INFO from the line list // RemoveEntryList(&(lpAddedLines[nDevice]->ListEntry)); // // Update enabled device counter // if (TRUE == IsDeviceEnabled(lpAddedLines[nDevice])) { Assert (g_dwDeviceEnabledCount); g_dwDeviceEnabledCount -= 1; } // // Free the memory occupied by the LINE_INFO // FreeTapiLine(lpAddedLines[nDevice]); } } } (*lpdwDeviceCount) = 0; // If we fail with one device then we fail with all devices. Exit: MemFree(lpAddedLines); if (ec) { SetLastError(ec); } return ( 0 == ec); } DWORD TapiInitialize( PREG_FAX_SERVICE FaxReg ) /*++ Routine Description: This function performs all necessary TAPI initialization. This includes device enumeration, message pump creation, device capabilities caputure, etc. It is required that the device provider initialization is completed before calling this function. Arguments: None. Return Value: Error code. --*/ { LONG Rslt; DWORD i,j; LPLINEDEVCAPS LineDevCaps = NULL; PREG_FAX_DEVICES FaxDevices = NULL; LINEINITIALIZEEXPARAMS LineInitializeExParams; TCHAR FaxSvcName[MAX_PATH*2]={0}; TCHAR Fname[_MAX_FNAME]; TCHAR Ext[_MAX_EXT]; DWORD LocalTapiApiVersion; LINEEXTENSIONID lineExtensionID; DWORD ec = 0; DWORDLONG dwlTimeNow; DWORD dwTapiDevices; DEBUG_FUNCTION_NAME(TEXT("TapiInitialize")); GetSystemTimeAsFileTime((FILETIME *)&dwlTimeNow); if (!LoadAdaptiveFileBuffer()) // Note: allocates AdaptiveFileBuffer (take care to delete it if error occurs later on) { if ( ERROR_FILE_NOT_FOUND == GetLastError() ) { // // We can live without the adaptive file buffer. // DebugPrintEx( DEBUG_WRN, TEXT("AdaptiveFileBuffer (faxadapt.lst) not found.")); ec = 0; } else { // // This is an unexpected error (no memory , file system error) we exit. // ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("LoadAdaptiveFileBuffer() failed (ec: %ld)"), ec); goto Error; } } // // we need to hold onto this cs until tapi is up and ready to serve // EnterCriticalSection( &g_CsLine ); // // initialize tapi // g_TapiCompletionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 1 ); if (!g_TapiCompletionPort) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("CreateIoCompletionPort() failed (ec: %ld)"), ec); LeaveCriticalSection( &g_CsLine ); goto Error; } LineInitializeExParams.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS); LineInitializeExParams.dwNeededSize = 0; LineInitializeExParams.dwUsedSize = 0; LineInitializeExParams.dwOptions = LINEINITIALIZEEXOPTION_USECOMPLETIONPORT; LineInitializeExParams.Handles.hCompletionPort = g_TapiCompletionPort; LineInitializeExParams.dwCompletionKey = TAPI_COMPLETION_KEY; LocalTapiApiVersion = MAX_TAPI_API_VER; Rslt = lineInitializeEx( &g_hLineApp, GetModuleHandle(NULL), NULL, FAX_SERVICE_DISPLAY_NAME, &dwTapiDevices, &LocalTapiApiVersion, &LineInitializeExParams ); if (Rslt != 0) { ec = Rslt; DebugPrintEx( DEBUG_ERR, TEXT("lineInitializeEx() failed devices=%d (ec: %ld)"), dwTapiDevices, ec); LeaveCriticalSection( &g_CsLine ); goto Error; } if (LocalTapiApiVersion < MIN_TAPI_API_VER) { ec = LINEERR_INCOMPATIBLEAPIVERSION; DebugPrintEx( DEBUG_ERR, TEXT("Unsupported TAPI API ver (Ver: %ld)"), LocalTapiApiVersion); LeaveCriticalSection( &g_CsLine ); goto Error; } if (!GetModuleFileName( NULL, FaxSvcName, ARR_SIZE(FaxSvcName))) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("GetModuleFileName for fax service module failed (ec: %ld)"), ec); LeaveCriticalSection( &g_CsLine ); goto Error; } else { _tsplitpath( FaxSvcName, NULL, NULL, Fname, Ext ); _stprintf( FaxSvcName, TEXT("%s%s"), Fname, Ext ); Rslt = lineSetAppPriority( FaxSvcName, LINEMEDIAMODE_UNKNOWN, 0, 0, NULL, 1 ); Rslt = lineSetAppPriority( FaxSvcName, LINEMEDIAMODE_DATAMODEM, 0, 0, NULL, 1 ); if (Rslt != 0) { ec = Rslt; DebugPrintEx( DEBUG_ERR, TEXT("lineSetAppPriority() failed (ec: %ld)"), ec ); LeaveCriticalSection( &g_CsLine ); goto Error; } } // // add any new devices to the registry // FaxDevices = GetFaxDevicesRegistry(); if (!FaxDevices) { DebugPrintEx( DEBUG_ERR, TEXT("GetFaxDevicesRegistry() failed in TapiInitialize. continueing to add devices into registry") ); } for (i = 0; i < dwTapiDevices; i++) { Rslt = lineNegotiateAPIVersion ( g_hLineApp, i, MIN_TAPI_LINE_API_VER, MAX_TAPI_LINE_API_VER, &LocalTapiApiVersion, &lineExtensionID ); if (Rslt == 0) { LineDevCaps = SmartLineGetDevCaps (g_hLineApp, i, LocalTapiApiVersion ); if (LineDevCaps) { if (!AddNewDevice( i, LineDevCaps, TRUE , FaxDevices)) { DebugPrintEx( DEBUG_WRN, TEXT("AddNewDevice() failed for device id: %ld (ec: %ld)"), i, GetLastError()); MemFree( LineDevCaps ); } else { MemFree( LineDevCaps ); } } else { DebugPrintEx( DEBUG_ERR, TEXT("SmartLineGetDevCaps failed for device id: %ld (ec: %ld)"), i, GetLastError()); Assert(FALSE); } } else { DebugPrintEx( DEBUG_WRN, TEXT("lineNegotiateAPIVersion() failed for device id: %ld (ec: %ld)"), i, GetLastError()); } } // // Delete any devices that need deletion // for (j = 0; j < FaxDevices->DeviceCount; j++) { // // skip any devices not created by us (created by FSPs) and virtual devices // if(!FaxDevices->Devices[j].bValidDevice || FaxDevices->Devices[j].Flags & FPF_VIRTUAL) // Cache is not supported for VFSPs { continue; } if(!FaxDevices->Devices[j].DeviceInstalled) { // // update "LastDetected" field on installed devices // MoveDeviceRegIntoDeviceCache( FaxDevices->Devices[j].PermanentLineId, FaxDevices->Devices[j].TapiPermanentLineID, (FaxDevices->Devices[j].PermanentLineId == g_dwManualAnswerDeviceId)); } } // // Cache cleanning // CleanOldDevicesFromDeviceCache(dwlTimeNow); if (!GetCountries()) { DebugPrintEx( DEBUG_WRN, TEXT("Can't init Countries list")); if (!(ec = GetLastError())) ec = ERROR_GEN_FAILURE; LeaveCriticalSection( &g_CsLine ); goto Error; } LeaveCriticalSection( &g_CsLine ); goto Exit; Error: if (g_hLineApp) { Rslt = lineShutdown(g_hLineApp); if (Rslt) { DebugPrintEx( DEBUG_ERR, TEXT("lineShutdown() failed (ec: %ld)"), Rslt); Assert(FALSE); } g_hLineApp = NULL; } if (g_TapiCompletionPort) { if (!CloseHandle( g_TapiCompletionPort )) { DebugPrintEx( DEBUG_ERR, TEXT("CloseHandle( g_TapiCompletionPort ) failed (ec: %ld)"), GetLastError()); Assert(FALSE); } g_TapiCompletionPort = NULL; } MemFree(g_pAdaptiveFileBuffer); g_pAdaptiveFileBuffer = NULL; Exit: FreeFaxDevicesRegistry( FaxDevices ); return ec; } BOOL LoadAdaptiveFileBuffer() { DWORD ec = 0; DWORD i, j; HANDLE AdaptiveFileHandle = INVALID_HANDLE_VALUE; LPTSTR AdaptiveFileName = NULL; DEBUG_FUNCTION_NAME(TEXT("LoadAdaptiveFileBuffer")); // // open faxadapt.lst file to decide on enabling rx // g_pAdaptiveFileBuffer = NULL; AdaptiveFileName = ExpandEnvironmentString( TEXT("%systemroot%\\system32\\faxadapt.lst") ); if (!AdaptiveFileName) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("ExpandEnvironmentString(\"%systemroot%\\system32\\faxadapt.lst\") failed (ec: %ld)"), GetLastError()); goto Error; } AdaptiveFileHandle = SafeCreateFile( AdaptiveFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (AdaptiveFileHandle == INVALID_HANDLE_VALUE ) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Could not open adaptive file [%s] (ec: %ld)"), _tcslwr(AdaptiveFileName), ec); goto Error; } i = GetFileSize( AdaptiveFileHandle, NULL ); if (i != 0xffffffff) { g_pAdaptiveFileBuffer = (LPBYTE)MemAlloc( i + 16 ); if (!g_pAdaptiveFileBuffer) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocated g_pAdaptiveFileBuffer (%ld bytes) (ec: %ld)"), i + 16, ec); goto Error; } if (!ReadFile( AdaptiveFileHandle, g_pAdaptiveFileBuffer, i, &j, NULL ) ) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Could not read adaptive file [%s] (ec: %ld)"), _tcslwr(AdaptiveFileName), ec); goto Error; } else { g_pAdaptiveFileBuffer[j] = 0; // need a string } } Assert (0 == ec); goto Exit; Error: MemFree( g_pAdaptiveFileBuffer ); g_pAdaptiveFileBuffer = NULL; Exit: MemFree( AdaptiveFileName); if (AdaptiveFileHandle != INVALID_HANDLE_VALUE) { CloseHandle( AdaptiveFileHandle ); } if (ec) { SetLastError(ec); } return (0 == ec); } LONG MyLineTranslateAddress( LPCTSTR Address, DWORD DeviceId, LPLINETRANSLATEOUTPUT *TranslateOutput ) { DWORD LineTransOutSize; LONG Rslt = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(_T("MyLineTranslateAddress")); // // allocate the initial linetranscaps structure // LineTransOutSize = sizeof(LINETRANSLATEOUTPUT) + 4096; *TranslateOutput = (LPLINETRANSLATEOUTPUT) MemAlloc( LineTransOutSize ); if (!*TranslateOutput) { DebugPrintEx(DEBUG_ERR, TEXT("MemAlloc() failed, sz=0x%08x"), LineTransOutSize); Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } (*TranslateOutput)->dwTotalSize = LineTransOutSize; Rslt = lineTranslateAddress( g_hLineApp, 0, MAX_TAPI_API_VER, Address, 0, LINETRANSLATEOPTION_CANCELCALLWAITING, *TranslateOutput ); if (Rslt != 0) { DebugPrintEx(DEBUG_ERR, TEXT("lineGetTranslateAddress() failed, ec=0x%08x"), Rslt); goto exit; } if ((*TranslateOutput)->dwNeededSize > (*TranslateOutput)->dwTotalSize) { // // re-allocate the LineTransCaps structure // LineTransOutSize = (*TranslateOutput)->dwNeededSize; MemFree( *TranslateOutput ); *TranslateOutput = (LPLINETRANSLATEOUTPUT) MemAlloc( LineTransOutSize ); if (!*TranslateOutput) { DebugPrintEx(DEBUG_ERR, TEXT("MemAlloc() failed, sz=0x%08x"), LineTransOutSize); Rslt = ERROR_NOT_ENOUGH_MEMORY; goto exit; } (*TranslateOutput)->dwTotalSize = LineTransOutSize; Rslt = lineTranslateAddress( g_hLineApp, 0, MAX_TAPI_API_VER, Address, 0, LINETRANSLATEOPTION_CANCELCALLWAITING, *TranslateOutput ); if (Rslt != 0) { DebugPrintEx(DEBUG_ERR, TEXT("lineGetTranslateAddress() failed, ec=0x%08x"), Rslt); goto exit; } } exit: if (Rslt != ERROR_SUCCESS) { MemFree( *TranslateOutput ); *TranslateOutput = NULL; } return Rslt; } BOOL CreateTapiThread(void) { DWORD ThreadId; DWORD ec = ERROR_SUCCESS; DEBUG_FUNCTION_NAME(TEXT("CreateTapiThread")); g_hTapiWorkerThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) TapiWorkerThread, NULL, 0, &ThreadId ); if (!g_hTapiWorkerThread) { ec = GetLastError(); DebugPrintEx( DEBUG_ERR, TEXT("Could not start TapiWorkerThread (CreateThread)(ec: %ld)"), ec); goto Error; } Assert (ERROR_SUCCESS == ec); goto Exit; Error: Assert (ERROR_SUCCESS != ec); Exit: // // freeServiceGlobals is responsible of closing the threads handle // if (ec) { SetLastError(ec); } return (ERROR_SUCCESS == ec); } DWORD GetDeviceListByCountryAndAreaCode( DWORD dwCountryCode, DWORD dwAreaCode, LPDWORD* lppdwDevices, LPDWORD lpdwNumDevices ) /*++ Routine name : GetDeviceListByCountryAndAreaCode Routine description: Returns an ordered list of devices that are a rule destination. The rule is specified by country and area code. The caller must call MemFree() to deallocate memory. Author: Oded Sacher (OdedS), Dec, 1999 Arguments: dwCountryCode [in ] - Country code dwAreaCode [in ] - Area code lppdwDevices [out ] - Pointer to recieve the device list lpdwNumDevices [out ] - pointer to recieve the number of devices in the list Return Value: Standard win32 error code --*/ { DEBUG_FUNCTION_NAME(TEXT("GetDeviceListByCountryAndAreaCode")); DWORD ec = ERROR_SUCCESS; Assert (lppdwDevices && lpdwNumDevices); CDialingLocation DialingLocation(dwCountryCode, dwAreaCode); // // Search for CountryCode.AreaCode // PCRULE pCRule = g_pRulesMap->FindRule (DialingLocation); if (NULL == pCRule) { ec = GetLastError(); if (FAX_ERR_RULE_NOT_FOUND != ec) { DebugPrintEx( DEBUG_ERR, TEXT("COutboundRulesMap::FindRule failed with error %ld"), ec); goto exit; } // // Search for CountryCode.* // DialingLocation = CDialingLocation(dwCountryCode, ROUTING_RULE_AREA_CODE_ANY); pCRule = g_pRulesMap->FindRule (DialingLocation); if (NULL == pCRule) { ec = GetLastError(); if (FAX_ERR_RULE_NOT_FOUND != ec) { DebugPrintEx( DEBUG_ERR, TEXT("COutboundRulesMap::FindRule failed with error %ld"), ec); goto exit; } // // Search for *.* // DialingLocation = CDialingLocation(ROUTING_RULE_COUNTRY_CODE_ANY, ROUTING_RULE_AREA_CODE_ANY); pCRule = g_pRulesMap->FindRule (DialingLocation); if (NULL == pCRule) { ec = GetLastError(); if (FAX_ERR_RULE_NOT_FOUND != ec) { DebugPrintEx( DEBUG_ERR, TEXT("COutboundRulesMap::FindRule failed with error %ld"), ec); goto exit; } } } } if (NULL == pCRule) { // No rule found!!! DebugPrintEx( DEBUG_MSG, TEXT("No outbound routing rule found")); *lppdwDevices = NULL; *lpdwNumDevices = 0; ec = ERROR_NOT_FOUND; Assert (NULL != pCRule) // Assert (FALSE) goto exit; } else { ec = pCRule->GetDeviceList (lppdwDevices, lpdwNumDevices); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_ERR, TEXT("COutboundRule::GetDeviceList failed with error %ld"), ec); goto exit; } } Assert (ERROR_SUCCESS == ec); exit: return ec; } BOOL IsAreaCodeMandatory( LPLINECOUNTRYLIST lpCountryList, DWORD dwCountryCode ) /*++ Routine name : IsAreaCodeMandatory Routine description: Checks if an area code is mandatory for a specific country Author: Oded Sacher (OdedS), Dec, 1999 Arguments: lpCountryList [in ] - Pointer to LINECOUNTRYLIST list, returned from a call to LineGetCountry dwCountryCode [in ] - The country country code. Return Value: TRUE - The area code is needed. FALSE - The area code is not mandatory. --*/ { DEBUG_FUNCTION_NAME(TEXT("IsAreaCodeMandatory")); LPLINECOUNTRYENTRY lpEntry = NULL; DWORD dwIndex; Assert (lpCountryList); lpEntry = (LPLINECOUNTRYENTRY) // init array of entries ((PBYTE) lpCountryList + lpCountryList->dwCountryListOffset); for (dwIndex=0; dwIndex < lpCountryList->dwNumCountries; dwIndex++) { if (lpEntry[dwIndex].dwCountryCode == dwCountryCode) { // // Matching country code - Check long distance rule. // if (lpEntry[dwIndex].dwLongDistanceRuleSize && lpEntry[dwIndex].dwLongDistanceRuleOffset ) { LPWSTR lpwstrLongDistanceDialingRule = (LPWSTR)((LPBYTE)lpCountryList + lpEntry[dwIndex].dwLongDistanceRuleOffset); if (wcschr(lpwstrLongDistanceDialingRule, TEXT('F')) != NULL) { return TRUE; } return FALSE; } } } return FALSE; } VOID UpdateReceiveEnabledDevicesCount () /*++ Routine name : UpdateReceiveEnabledDevicesCount Routine description: Updates the counter of the number of devices that are enabled to receive faxes Author: Eran Yariv (EranY), Jul, 2000 Arguments: Return Value: None --*/ { PLIST_ENTRY pNext; DWORD dwOldCount; BOOL fManualDeviceFound = FALSE; DEBUG_FUNCTION_NAME(TEXT("UpdateReceiveEnabledDevicesCount")); #if DBG DWORD dwEnabledDevices = 0; DWORD dwDevices = 0; #endif EnterCriticalSection( &g_CsLine ); dwOldCount = g_dwReceiveDevicesCount; g_dwReceiveDevicesCount = 0; pNext = g_TapiLinesListHead.Flink; while ((ULONG_PTR)pNext != (ULONG_PTR)&g_TapiLinesListHead) { PLINE_INFO pLineInfo = CONTAINING_RECORD( pNext, LINE_INFO, ListEntry ); pNext = pLineInfo->ListEntry.Flink; if (g_dwManualAnswerDeviceId == pLineInfo->PermanentLineID) { fManualDeviceFound = TRUE; } if ((pLineInfo->Flags) & FPF_RECEIVE) { if (g_dwManualAnswerDeviceId == pLineInfo->PermanentLineID) { DebugPrintEx(DEBUG_WRN, TEXT("Device %ld is set to auto-receive AND manual-receive. Canceling the manual-receive for it"), g_dwManualAnswerDeviceId); g_dwManualAnswerDeviceId = 0; DWORD dwRes = WriteManualAnswerDeviceId (g_dwManualAnswerDeviceId); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("WriteManualAnswerDeviceId(0) (ec: %lc)"), dwRes); } } g_dwReceiveDevicesCount++; } #if DBG if (TRUE == IsDeviceEnabled(pLineInfo)) { dwEnabledDevices += 1; } dwDevices += 1; #endif } #if DBG Assert (dwEnabledDevices == g_dwDeviceEnabledCount); Assert (dwDevices == g_dwDeviceCount); #endif if (FALSE == fManualDeviceFound && 0 != g_dwManualAnswerDeviceId) { // // There manual answer device id is not valid // g_dwManualAnswerDeviceId = 0; DWORD dwRes = WriteManualAnswerDeviceId (g_dwManualAnswerDeviceId); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("WriteManualAnswerDeviceId(0) (ec: %lc)"), dwRes); } } DebugPrintEx(DEBUG_MSG, TEXT("Number of receive-enabled devices is now %ld"), g_dwReceiveDevicesCount); LeaveCriticalSection( &g_CsLine ); } // UpdateReceiveEnabledDevicesCount BOOL RemoveTapiDevice( DWORD dwDeviceId ) { DWORD ec = ERROR_SUCCESS; BOOL rVal = TRUE; PLINE_INFO pLineInfo = NULL; PLIST_ENTRY Next; DWORD dwPermanentTapiDeviceId; DWORD dwPermanentLineID; BOOL fFound = FALSE; DEBUG_FUNCTION_NAME(TEXT("RemoveTapiDevice")); Next = g_TapiLinesListHead.Flink; Assert (Next); while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { pLineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = pLineInfo->ListEntry.Flink; if (!(pLineInfo->Flags & FPF_VIRTUAL) && // Virtual devices may have the same device id (device index) as the Tapi session id // We do not support removal of VFSP device dwDeviceId == pLineInfo->DeviceId) { dwPermanentTapiDeviceId = pLineInfo->TapiPermanentLineId; dwPermanentLineID = pLineInfo->PermanentLineID; fFound = TRUE; break; } } if (FALSE == fFound) { // // Can be if for some reason the device was not added. // DebugPrintEx( DEBUG_WRN, TEXT("failed to find line for device id: %ld)"), dwDeviceId); SetLastError(ERROR_NOT_FOUND); return FALSE; } RemoveEntryList (&pLineInfo->ListEntry); InsertTailList (&g_RemovedTapiLinesListHead, &pLineInfo->ListEntry); Assert (g_dwDeviceCount); g_dwDeviceCount -= 1; MoveDeviceRegIntoDeviceCache( dwPermanentLineID, dwPermanentTapiDeviceId, (dwPermanentLineID == g_dwManualAnswerDeviceId)); // // Update Enabled devices count // if (TRUE == IsDeviceEnabled(pLineInfo)) { Assert (g_dwDeviceEnabledCount); g_dwDeviceEnabledCount -= 1; } if (dwPermanentLineID == g_dwManualAnswerDeviceId) { g_dwManualAnswerDeviceId = 0; DWORD dwRes = WriteManualAnswerDeviceId (g_dwManualAnswerDeviceId); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("WriteManualAnswerDeviceId(0) (ec: %lc)"), dwRes); } } ec = g_pTAPIDevicesIdsMap->RemoveDevice (dwPermanentTapiDeviceId); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_WRN, TEXT("g_pTAPIDevicesIdsMap->RemoveDevice() failed for Tapi device id: %ld (ec: %ld)"), dwPermanentTapiDeviceId, ec); rVal = FALSE; } // // Update outbound routing // ec = g_pGroupsMap->RemoveDevice(dwPermanentLineID); if (ERROR_SUCCESS != ec) { DebugPrintEx( DEBUG_ERR, TEXT("COutboundRoutingGroupsMap::RemoveDevice() failed (ec: %ld)"), ec); rVal = FALSE; } if (TRUE == rVal) { DWORD dwRes = CreateConfigEvent (FAX_CONFIG_TYPE_DEVICES); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("CreateConfigEvent(FAX_CONFIG_TYPE_DEVICES) (ec: %lc)"), dwRes); } dwRes = CreateConfigEvent (FAX_CONFIG_TYPE_OUT_GROUPS); if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("CreateConfigEvent(FAX_CONFIG_TYPE_OUT_GROUPS) (ec: %lc)"), dwRes); } } else { Assert (ERROR_SUCCESS != ec); SetLastError(ec); } return rVal; } BOOL IsDeviceEnabled( PLINE_INFO pLineInfo ) /*++ Routine name : IsDeviceEnabled Routine description: Checks if a device is send or receive or manual receive enabled Must be called inside G_CsLine Author: Oded Sacher (OdedS), Feb, 2001 Arguments: Return Value: TRUE if enabled. FALSE if not --*/ { Assert (pLineInfo); if ((pLineInfo->Flags & FPF_RECEIVE) || (pLineInfo->Flags & FPF_SEND) || pLineInfo->PermanentLineID == g_dwManualAnswerDeviceId) { // // The device was send/receive/manual receive enabled // return TRUE; } return FALSE; } /*++ Routine name : CleanOldDevicesFromDeviceCache Routine description: The routine scan the device-cache and remove old entries (by DEFAULT_REGVAL_MISSING_DEVICE_LIFETIME constant). Author: Caliv Nir (t-nicali), Apr, 2001 Arguments: dwlTimeNow [in] - current time in UTC ( result of GetSystemTimeAsFileTime ) Return Value: ERROR_SUCCESS - when all devices was checked and cleaned --*/ DWORD CleanOldDevicesFromDeviceCache(DWORDLONG dwlTimeNow) { DWORDLONG dwOldestDate = dwlTimeNow - DEFAULT_REGVAL_MISSING_DEVICE_LIFETIME; // oldest date allowed for cache device HKEY hKeyCache = NULL; DWORDLONG* pDeviceDate; DWORD dwDataSize = sizeof(DWORDLONG); DWORD dwTapiPermanentLineID; DWORD dwKeyNameLen; DWORD dwIndex ; DWORD dwRes = ERROR_SUCCESS; PTSTR pszKeyName= NULL; vector vecCacheEntryForDeletion; DEBUG_FUNCTION_NAME(TEXT("CleanOldDevicesFromDeviceCache")); // open cache registry entry hKeyCache = OpenRegistryKey( HKEY_LOCAL_MACHINE, REGKEY_FAX_DEVICES_CACHE, FALSE, KEY_READ ); if (!hKeyCache) { // // No Device cache is present yet // dwRes = GetLastError(); DebugPrintEx( DEBUG_WRN, TEXT("OpenRegistryKey failed with [%lu] for [%s] . Device cache still wasn't created."), dwRes, REGKEY_FAX_DEVICES_CACHE ); return dwRes; } // get length of longest key name in characrter DWORD dwMaxSubKeyLen; dwRes = RegQueryInfoKey(hKeyCache, NULL, NULL, NULL, NULL, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); if ( ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("RegQueryInfoKey failed with [%lu] for [%s]."), dwRes, REGKEY_FAX_DEVICES_CACHE ); goto Exit; } // Add one for the NULL terminator dwMaxSubKeyLen++; // Allocate buffer for subkey names pszKeyName = (PTSTR) MemAlloc(dwMaxSubKeyLen * sizeof(TCHAR)); if ( NULL == pszKeyName ) { DebugPrintEx( DEBUG_ERR, TEXT("MemAlloc failure") ); dwRes = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } // Store buffer length dwKeyNameLen = dwMaxSubKeyLen; // Start from the begining dwIndex = 0; while ( ERROR_SUCCESS == RegEnumKeyEx(hKeyCache, dwIndex++, pszKeyName, &dwKeyNameLen, NULL, NULL, NULL, NULL) ) { HKEY hKeyDevice; hKeyDevice = OpenRegistryKey( hKeyCache, pszKeyName, FALSE, KEY_READ ); if (!hKeyDevice) { DebugPrintEx( DEBUG_WRN, TEXT("OpenRegistryKey failed for [%s]."), pszKeyName ); goto Next; } // // get caching time // pDeviceDate = (DWORDLONG *)GetRegistryBinary(hKeyDevice, REGVAL_LAST_DETECTED_TIME, &dwDataSize); if ( (NULL == pDeviceDate) || (*pDeviceDate < dwOldestDate) ) { // // mark for deletion old or illegal cache-entry // if ( 1 == _stscanf( pszKeyName, TEXT("%lx"),&dwTapiPermanentLineID ) ) { try { vecCacheEntryForDeletion.push_back(dwTapiPermanentLineID); } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("push back failed throwing an exception: %S"), ex.what() ); SetLastError(ERROR_NOT_ENOUGH_MEMORY); MemFree(pDeviceDate); RegCloseKey (hKeyDevice); goto Exit; } } else { DebugPrintEx( DEBUG_WRN, TEXT("_stscanf failed can't retrive tapi ID skipping this entry.") ); } } MemFree(pDeviceDate); RegCloseKey (hKeyDevice); Next: // restore buffer length dwKeyNameLen = dwMaxSubKeyLen; } try { while (!vecCacheEntryForDeletion.empty()) { dwTapiPermanentLineID = vecCacheEntryForDeletion.back(); DeleteCacheEntry(dwTapiPermanentLineID); vecCacheEntryForDeletion.pop_back(); } } catch (exception &ex) { DebugPrintEx( DEBUG_ERR, TEXT("vector operation failed throwing an exception, abort cleanning; %S"), ex.what() ); } Exit: MemFree(pszKeyName); RegCloseKey (hKeyCache); return dwRes; } DWORD UpdateDevicesFlags( void ) /*++ Routine name : UpdateDevicesFlags Routine description: Updates new devices flags ,so we will not exceed device limit on this SKU Author: Sacher Oded (odeds), May, 2001 Arguments: None Return Value: Win32 error code --*/ { DWORD dwRes = ERROR_SUCCESS; PLIST_ENTRY Next; PLINE_INFO pLineInfo; DEBUG_FUNCTION_NAME(TEXT("UpdateDevicesFlags")); // // loop thru the devices and reset flags of new devices if we exceeded device limit // Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead && g_dwDeviceEnabledCount > g_dwDeviceEnabledLimit) { pLineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = pLineInfo->ListEntry.Flink; if (!(pLineInfo->dwDeviceType & FAX_DEVICE_TYPE_NEW) || FALSE == IsDeviceEnabled(pLineInfo)) { continue; } // // Device is new and enabled. // ResetDeviceFlags(pLineInfo); g_dwDeviceEnabledCount -= 1; } // // loop thru the devices and reset flags of cached devices if we exceeded device limit // Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead && g_dwDeviceEnabledCount > g_dwDeviceEnabledLimit) { pLineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = pLineInfo->ListEntry.Flink; if (!(pLineInfo->dwDeviceType & FAX_DEVICE_TYPE_CACHED) || FALSE == IsDeviceEnabled(pLineInfo)) { continue; } // // Device is cached and enabled. // ResetDeviceFlags(pLineInfo); g_dwDeviceEnabledCount -= 1; } // // loop thru the devices and reset flags of old devices if we exceeded device limit. // Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead && g_dwDeviceEnabledCount > g_dwDeviceEnabledLimit) { pLineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = pLineInfo->ListEntry.Flink; if (!(pLineInfo->dwDeviceType & FAX_DEVICE_TYPE_OLD) || FALSE == IsDeviceEnabled(pLineInfo)) { continue; } // // Device is old and enabled. // ResetDeviceFlags(pLineInfo); g_dwDeviceEnabledCount -= 1; } Assert (g_dwDeviceEnabledCount <= g_dwDeviceEnabledLimit); // // loop thru the devices and close the line handles // for all devices that are NOT set to receive // Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { pLineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = pLineInfo->ListEntry.Flink; if (!(pLineInfo->Flags & FPF_RECEIVE) && // Device is not set to auto-receive and pLineInfo->hLine && // device is open and pLineInfo->PermanentLineID != g_dwManualAnswerDeviceId // this device is not set to manual answer mode ) { // // Attempt to close the device // HLINE hLine = pLineInfo->hLine; pLineInfo->hLine = 0; LONG Rslt = lineClose( hLine ); if (Rslt) { if (LINEERR_INVALLINEHANDLE != Rslt) { DebugPrintEx( DEBUG_ERR, TEXT("lineClose() for line %s [Permanent Id: %010d] has failed. (ec: %ld)"), pLineInfo->DeviceName, pLineInfo->TapiPermanentLineId, Rslt); ASSERT_FALSE; } else { // // We can get LINEERR_INVALLINEHANDLE if we got LINE_CLOSE // from TAPI. // DebugPrintEx( DEBUG_WRN, TEXT("lineClose() for line %s [Permanent Id: %010d] reported LINEERR_INVALLINEHANDLE. (May be caused by LINE_CLOSE event)"), pLineInfo->DeviceName, pLineInfo->TapiPermanentLineId ); } } } } return dwRes; } VOID UpdateManualAnswerDevice( void ) /*++ Routine name : UpdateManualAnswerDevice Routine description: Updates the manual answer device with a cached device Author: Sacher Oded (odeds), July, 2001 Arguments: None Return Value: None --*/ { DEBUG_FUNCTION_NAME(TEXT("UpdateManualAnswerDevice")); // // Call UpdateReceiveEnabledDevicesCount () to make sure the manual answer device is valid // UpdateReceiveEnabledDevicesCount(); // // if we have a valid manual answer device then finish // if (0 == g_dwManualAnswerDeviceId) { // // No valid manual answer device is operational so look if chached devices were manual. // loop through the cached devices and look for the first cached device and set it as a manual answer device // PLIST_ENTRY Next; PLINE_INFO pLineInfo; Next = g_TapiLinesListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&g_TapiLinesListHead) { BOOL fDeviceWasEnabled; DWORD dwRes; pLineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry ); Next = pLineInfo->ListEntry.Flink; // // look for a cached manual answer device that is not set to auto receive // if ( pLineInfo->dwDeviceType != (FAX_DEVICE_TYPE_CACHED | FAX_DEVICE_TYPE_MANUAL_ANSWER) || (pLineInfo->Flags & FPF_RECEIVE)) { continue; } // // We found a device that can be set to manual receive // // // Now it may be that the cached device was not enabled (if for example it was marked as // manual-answer and no send ) so we didn't count it in the Enabled Count devices group. // if so then after setting it as a manual receive we ought to update g_dwDeviceEnabledCount // fDeviceWasEnabled = IsDeviceEnabled(pLineInfo); g_dwManualAnswerDeviceId = pLineInfo->PermanentLineID; dwRes = WriteManualAnswerDeviceId (g_dwManualAnswerDeviceId); // persist in registry if (ERROR_SUCCESS != dwRes) { DebugPrintEx( DEBUG_ERR, TEXT("WriteManualAnswerDeviceId(0) (ec: %lc)"), dwRes); } // // Update enabled devices count // if (FALSE == fDeviceWasEnabled) { // // Another device is now enabled // g_dwDeviceEnabledCount += 1; } // // No need to continue the search, only one "manual receive" device is allowed // break; } } return; }