/*++ Copyright (c) 1996 Microsoft Corporation Module Name: receive.c Abstract: This module handles the FAX receive case. Author: Wesley Witt (wesw) 6-Mar-1996 Revision History: --*/ #include "faxsvc.h" #pragma hdrstop DWORD FaxReceiveThread( PFAX_RECEIVE_ITEM FaxReceiveItem ) /*++ Routine Description: This function process a FAX send operation. This runs asynchronously as a separate thread. There is one thread for each outstanding FAX operation. Arguments: FaxReceiveItem - FAX receive packet Return Value: Error code. --*/ { DWORD rVal = ERROR_SUCCESS; PJOB_ENTRY JobEntry; DWORD JobId; PLINE_INFO LineInfo; PFAX_RECEIVE FaxReceive = NULL; PFAX_DEV_STATUS FaxStatus = NULL; DWORD ReceiveSize; DWORD StatusSize; BOOL Result; DWORD BytesNeeded; DWORDLONG ElapsedTime = 0; DWORDLONG ReceiveTime = 0; BOOL DoFaxRoute = FALSE; DWORD Attrib; DWORD RecoveredPages,TotalPages; MS_TAG_INFO MsTagInfo; BOOL fReceiveNoFile = FALSE; BOOL ReceiveFailed = FALSE; PJOB_QUEUE JobQueue = NULL; BOOL DeviceCanSend; __try { LineInfo = FaxReceiveItem->LineInfo; JobEntry = FaxReceiveItem->JobEntry; JobQueue = AddJobQueueEntry( JT_RECEIVE, FaxReceiveItem->FileName, NULL, NULL, FALSE, JobEntry ); if (!JobQueue) { return ERROR_NOT_ENOUGH_MEMORY; } JobId = JobQueue->JobId; DeviceCanSend = ((LineInfo->Flags & FPF_SEND) == FPF_SEND); // // allocate memory for the receive packet // this is a variable size packet based // on the size of the strings contained // withing the packet. // ReceiveSize = sizeof(FAX_RECEIVE) + FAXDEVRECEIVE_SIZE; FaxReceive = MemAlloc( ReceiveSize ); if (!FaxReceive) { RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; return ERROR_NOT_ENOUGH_MEMORY; } // // allocate memory for the status packet // this is a variable size packet based // on the size of the strings contained // withing the packet. // StatusSize = sizeof(FAX_DEV_STATUS) + FAXDEVREPORTSTATUS_SIZE; FaxStatus = (PFAX_DEV_STATUS) MemAlloc( StatusSize ); if (!FaxStatus) { RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; MemFree( JobQueue ); return ERROR_NOT_ENOUGH_MEMORY; } SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS); // // setup the status packet // FaxStatus->SizeOfStruct = StatusSize; // // setup the receive packet // FaxReceive->SizeOfStruct = ReceiveSize; // // copy filename into place // FaxReceive->FileName = (LPTSTR) ((LPBYTE)FaxReceive + sizeof(FAX_RECEIVE)); _tcscpy( FaxReceive->FileName, FaxReceiveItem->FileName ); FaxReceive->ReceiverName = NULL; // // copy number into place right after filename // FaxReceive->ReceiverNumber = (LPTSTR) ( (LPBYTE)FaxReceive->FileName + sizeof(TCHAR)*(_tcslen(FaxReceive->FileName) + 1)); _tcscpy( FaxReceive->ReceiverNumber, LineInfo->Csid ); FaxReceive->Reserved[0] = 0; FaxReceive->Reserved[1] = 0; FaxReceive->Reserved[2] = 0; FaxReceive->Reserved[3] = 0; Attrib = GetFileAttributes( FaxReceiveDir ); if (Attrib == 0xffffffff) { MakeDirectory( FaxReceiveDir ); } Attrib = GetFileAttributes( FaxReceiveDir ); if (Attrib == 0xffffffff) { FaxLog( FAXLOG_CATEGORY_INBOUND, FAXLOG_LEVEL_MAX, 1, MSG_FAX_RECEIVE_NODIR, FaxReceiveDir ); } Attrib = GetFileAttributes( FaxReceive->FileName ); if (Attrib == 0xffffffff) { FaxLog( FAXLOG_CATEGORY_INBOUND, FAXLOG_LEVEL_MIN, 1, MSG_FAX_RECEIVE_NOFILE, FaxReceive->FileName ); fReceiveNoFile = TRUE; DebugPrint(( TEXT("FaxReceive - %s does not exist"), FaxReceive->FileName )); } else { DebugPrint(( TEXT("Starting FAX receive into %s"), FaxReceive->FileName )); } // // do the actual receive // __try { Result = LineInfo->Provider->FaxDevReceive( (HANDLE) JobEntry->InstanceData, FaxReceiveItem->hCall, FaxReceive ); } __except (EXCEPTION_EXECUTE_HANDLER) { Result = FALSE; DebugPrint(( TEXT("FaxDevReceive() failed: 0x%08x"), GetExceptionCode() )); ReceiveFailed = TRUE; } __try { LineInfo->Provider->FaxDevReportStatus( (HANDLE) JobEntry->InstanceData, FaxStatus, StatusSize, &BytesNeeded ); } __except (EXCEPTION_EXECUTE_HANDLER) { DebugPrint(( TEXT("FaxDevReportStatus() failed: 0x%08x"), GetExceptionCode() )); } if (!Result) { DebugPrint(( TEXT("FAX receive failed: 0x%08x"), FaxStatus->StatusId )); ReceiveFailed = TRUE; if (FaxStatus->StatusId == FS_NOT_FAX_CALL) { if (HandoffCallToRas( LineInfo, FaxReceiveItem->hCall )) { FaxReceiveItem->hCall = 0; LineInfo->State = FPS_NOT_FAX_CALL; DeviceCanSend = FALSE; } RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; DeleteFile( FaxReceive->FileName ); } if ( (FaxStatus->StatusId == FS_FATAL_ERROR) && (! fReceiveNoFile) ) { // // try to recover one or more pages of the received fax // if (!TiffRecoverGoodPages(FaxReceive->FileName,&RecoveredPages,&TotalPages) ) { // // couldn't recover any pages, just log an error and delete the received fax. // rxerr: FaxLog( FAXLOG_CATEGORY_INBOUND, FAXLOG_LEVEL_MIN, 0, MSG_FAX_RECEIVE_FAILED ); //DeleteFile( FaxReceive->FileName ); } else { // // recovered some pages, log a message and add to job queue // TCHAR RecoverCountStrBuf[64]; TCHAR TotalCountStrBuf[64]; TCHAR TimeStr[128]; LPTSTR ToStr; TCHAR RecoverFileName[MAX_PATH]; GenerateUniqueFileName( FaxReceiveDir, TEXT("tif"), RecoverFileName, MAX_PATH ); if (!CopyFile(FaxReceive->FileName,RecoverFileName,FALSE)) { goto rxerr; } FormatElapsedTimeStr( (FILETIME*)&JobEntry->ElapsedTime, TimeStr, sizeof(TimeStr) ); _ltot((LONG) RecoveredPages, RecoverCountStrBuf, 10); _ltot((LONG) TotalPages, TotalCountStrBuf, 10); if (FaxStatus->RoutingInfo == NULL || FaxStatus->RoutingInfo[0] == 0) { ToStr = FaxReceive->ReceiverNumber; } else { ToStr = FaxStatus->RoutingInfo; } FaxLog( FAXLOG_CATEGORY_INBOUND, FAXLOG_LEVEL_MIN, 8, MSG_FAX_RECEIVE_FAIL_RECOVER, RecoverFileName, FaxStatus->CSI, FaxStatus->CallerId, ToStr, RecoverCountStrBuf, TotalCountStrBuf, TimeStr, JobEntry->LineInfo->DeviceName ); AddJobQueueEntry( JT_FAIL_RECEIVE, RecoverFileName, NULL, NULL, FALSE, NULL ); } RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; } if (FaxStatus->StatusId == FS_USER_ABORT) { FaxLog( FAXLOG_CATEGORY_INBOUND, FAXLOG_LEVEL_MED, 0, MSG_FAX_RECEIVE_USER_ABORT ); RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; //DeleteFile( FaxReceive->FileName); } } else { __try { GetSystemTimeAsFileTime( (FILETIME*) &JobEntry->EndTime ); ReceiveTime = JobEntry->StartTime; JobEntry->ElapsedTime = JobEntry->EndTime - JobEntry->StartTime; if (!TiffPostProcessFast( FaxReceive->FileName, NULL )) { DebugPrint(( TEXT("failed to post process the TIFF file") )); DebugPrint(( TEXT("FAX receive %d failed"), JobId )); ReceiveFailed = TRUE; FaxLog( FAXLOG_CATEGORY_INBOUND, FAXLOG_LEVEL_MIN, 0, MSG_FAX_RECEIVE_FAILED ); RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; } else { TCHAR PageCountStrBuf[64]; TCHAR TimeStr[128]; LPTSTR ToStr; DebugPrint(( TEXT("FAX receive %d succeeded"), JobId )); FormatElapsedTimeStr( (FILETIME*)&JobEntry->ElapsedTime, TimeStr, sizeof(TimeStr) ); _ltot((LONG) FaxStatus->PageCount, PageCountStrBuf, 10); if (FaxStatus->RoutingInfo == NULL || FaxStatus->RoutingInfo[0] == 0) { ToStr = FaxReceive->ReceiverNumber; } else { ToStr = FaxStatus->RoutingInfo; } FaxLog( FAXLOG_CATEGORY_INBOUND, FAXLOG_LEVEL_MED, 7, MSG_FAX_RECEIVE_SUCCESS, FaxReceive->FileName, FaxStatus->CSI, FaxStatus->CallerId, ToStr, PageCountStrBuf, TimeStr, JobEntry->LineInfo->DeviceName ); ElapsedTime = JobEntry->ElapsedTime; DoFaxRoute = TRUE; } } __except (EXCEPTION_EXECUTE_HANDLER) { DebugPrint(( TEXT("failed to post process the TIFF file, ec=%x"), GetExceptionCode() )); ReceiveFailed = TRUE; RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; } } } __except (EXCEPTION_EXECUTE_HANDLER) { DebugPrint(( TEXT("FAX receive failed due to exception in device provider, ec=0x%08x"), GetExceptionCode() )); ReceiveFailed = TRUE; RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; } if (PerfCounters && ReceiveFailed && LineInfo->State != FPS_NOT_FAX_CALL) { InterlockedIncrement( (PLONG)&PerfCounters->InboundFailedReceive ); } // // end the job // JobEntry->RefCount -= 1; if (JobEntry->RefCount == 0 && JobEntry->hEventEnd) { SetEvent( JobEntry->hEventEnd ); } ReleaseJob( JobEntry ); // // add the microsoft fax tags to the file // this is necessary ONLY when we route the // file when doing a receive. if we are not // routing the file then it is deleted, so // adding the tags is not necessary. // MsTagInfo.RecipName = FaxReceive->ReceiverName; MsTagInfo.RecipNumber = FaxReceive->ReceiverNumber; MsTagInfo.SenderName = NULL; MsTagInfo.Routing = FaxStatus->RoutingInfo; MsTagInfo.CallerId = FaxStatus->CallerId; MsTagInfo.Csid = FaxReceive->ReceiverNumber; MsTagInfo.Tsid = FaxStatus->CSI; MsTagInfo.FaxTime = ReceiveTime; TiffAddMsTags( FaxReceive->FileName, &MsTagInfo ); // // route the newly received fax // if (DoFaxRoute) { if (PerfCounters){ SYSTEMTIME SystemTime ; DWORD Seconds ; HANDLE hFileHandle; DWORD Bytes = 0 ; InterlockedIncrement( (LPLONG) &PerfCounters->InboundFaxes ) ; InterlockedIncrement( (LPLONG) &PerfCounters->TotalFaxes ) ; FileTimeToSystemTime( (FILETIME*)&ElapsedTime, &SystemTime ); Seconds = (DWORD)( SystemTime.wSecond + 60 * ( SystemTime.wMinute + 60 * SystemTime.wHour )); InterlockedExchangeAdd( (PLONG)&PerfCounters->InboundPages, (LONG)FaxStatus->PageCount ); InterlockedExchangeAdd( (PLONG)&PerfCounters->TotalPages, (LONG)FaxStatus->PageCount ); hFileHandle = CreateFile( FaxReceive->FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( hFileHandle != INVALID_HANDLE_VALUE ){ Bytes = GetFileSize( hFileHandle, NULL ); CloseHandle( hFileHandle ); } EnterCriticalSection( &CsPerfCounters ); InboundSeconds += Seconds; TotalSeconds += Seconds; PerfCounters->InboundMinutes = InboundSeconds/60 ; PerfCounters->TotalMinutes = TotalSeconds/60; PerfCounters->InboundBytes += Bytes; PerfCounters->TotalBytes += Bytes; LeaveCriticalSection( &CsPerfCounters ); } __try { BOOL RouteSucceeded; PROUTE_FAILURE_INFO RouteFailureInfo; DWORD CountFailureInfo; PFAX_ROUTE Route = MemAlloc( sizeof(FAX_ROUTE) ); if (Route == NULL) { __leave; } // // now setup the fax routing data structure // Route->SizeOfStruct = sizeof(FAX_ROUTE); Route->JobId = JobId; Route->ElapsedTime = ElapsedTime; Route->ReceiveTime = ReceiveTime; Route->PageCount = FaxStatus->PageCount; Route->Csid = StringDup( FaxReceive->ReceiverNumber ); Route->Tsid = StringDup( FaxStatus->CSI ); Route->CallerId = StringDup( FaxStatus->CallerId ); Route->ReceiverName = StringDup( FaxReceive->ReceiverName ); Route->ReceiverNumber = StringDup( FaxReceive->ReceiverNumber ); Route->DeviceName = LineInfo->DeviceName; Route->DeviceId = LineInfo->PermanentLineID; Route->RoutingInfo = StringDup( FaxStatus->RoutingInfo ); JobQueue->FaxRoute = Route; JobQueue->FaxRoute = Route; RouteSucceeded = FaxRoute( JobQueue, FaxReceive->FileName, Route, &RouteFailureInfo, &CountFailureInfo ); if (!RouteSucceeded) { INT i; TCHAR QueueFileName[MAX_PATH]; EnterCriticalSection( &CsQueue ); // // wrap the critical section stuff in try block so we always release CsQueue // __try { JobQueue->CountFailureInfo = CountFailureInfo; for (i = 0; i < (INT) CountFailureInfo; i++) { JobQueue->RouteFailureInfo[i] = RouteFailureInfo[i]; } GenerateUniqueFileName( FaxQueueDir, TEXT("fqe"), QueueFileName, sizeof(QueueFileName)/sizeof(WCHAR) ); JobQueue->QueueFileName = StringDup( QueueFileName ); JobQueue->JobType = JT_ROUTING; JobQueue->JobStatus = JS_RETRYING; RescheduleJobQueueEntry( JobQueue ); } __except (EXCEPTION_EXECUTE_HANDLER) { DebugPrint(( TEXT("FaxRoute() crashed, ec=0x%08x"), GetExceptionCode() )); } LeaveCriticalSection( &CsQueue ); } else { RemoveJobQueueEntry( JobQueue ); JobQueue = NULL; } } __except (EXCEPTION_EXECUTE_HANDLER) { DebugPrint(( TEXT("FaxRoute() crashed, ec=0x%08x"), GetExceptionCode() )); } } EnterCriticalSection( &CsQueue ); if (JobQueue && JobQueue->JobEntry && (JobQueue->JobType != JT_ROUTING)) { JobQueue->JobStatus = JS_DELETING; JobQueue->JobEntry = NULL; } LeaveCriticalSection( &CsQueue ); EndJob( JobEntry ); // // clean up and exit // MemFree( FaxReceiveItem->FileName ); MemFree( FaxReceiveItem ); MemFree( FaxReceive ); MemFree( FaxStatus ); // // signal our queue if we now have a send capable device available. // (also false if we're did a RAS handoff, since the device is still in use // if (DeviceCanSend) { ReleaseSemaphore( JobQueueSemaphore, 1, NULL ); } SetThreadExecutionState(ES_CONTINUOUS); return rVal; } DWORD StartFaxReceive( PJOB_ENTRY JobEntry, HCALL hCall, PLINE_INFO LineInfo, LPTSTR FileName, DWORD FileNameSize ) /*++ Routine Description: This function start a FAX receive operation by creating a thread that calls the appropriate device provider. Arguments: hCall - Call handle dwMessage - Reason for the callback dwInstance - LINE_INFO pointer dwParam1 - Callback parameter #1 dwParam2 - Callback parameter #2 dwParam3 - Callback parameter #3 Return Value: Error code. --*/ { PFAX_RECEIVE_ITEM FaxReceiveItem = NULL; DWORD rVal = ERROR_SUCCESS; HANDLE hThread; DWORD ThreadId; // // generate a filename for the received fax // GenerateUniqueFileName( FaxReceiveDir, TEXT("tif"), FileName, FileNameSize ); // // allocate the fax receive structure // FaxReceiveItem = MemAlloc( sizeof(FAX_RECEIVE_ITEM) ); if (!FaxReceiveItem) { rVal = ERROR_NOT_ENOUGH_MEMORY; goto exit; } // // setup the fax receive values // FaxReceiveItem->hCall = hCall; FaxReceiveItem->LineInfo = LineInfo; FaxReceiveItem->JobEntry = JobEntry; FaxReceiveItem->FileName = StringDup( FileName ); JobEntry->JobType = JT_RECEIVE; JobEntry->CallHandle = hCall; JobEntry->RefCount += 1; LineInfo->State = FPS_INITIALIZING; // // start the receive operation // hThread = CreateThread( NULL, 1024*100, (LPTHREAD_START_ROUTINE) FaxReceiveThread, (LPVOID) FaxReceiveItem, 0, &ThreadId ); if (!hThread) { MemFree( FaxReceiveItem ); rVal = GetLastError(); } else { CloseHandle( hThread ); } exit: return rVal; }