|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
job.c
Abstract:
This module implements the job creation and deletion. Also included in the file are the queue management functions and thread managegement.
Author:
Wesley Witt (wesw) 24-Jan-1996
Revision History:
--*/
#include "faxsvc.h"
#pragma hdrstop
LIST_ENTRY JobListHead; CRITICAL_SECTION CsJob; HANDLE StatusCompletionPortHandle; DWORD FaxSendRetries; DWORD FaxSendRetryDelay; DWORD FaxDirtyDays; BOOL FaxUseDeviceTsid; BOOL FaxUseBranding; BOOL ServerCp; FAX_TIME StartCheapTime; FAX_TIME StopCheapTime; BOOL ArchiveOutgoingFaxes; LPTSTR ArchiveDirectory; DWORD NextJobId; BOOL ForceReceive; DWORD TerminationDelay;
extern HANDLE hServiceEndEvent; // signal this after letting clients know fax service is ending
PJOB_ENTRY FindJob( IN HANDLE FaxHandle )
/*++
Routine Description:
This fuction locates a FAX job by matching the FAX handle value.
Arguments:
FaxHandle - FAX handle returned from startjob
Return Value:
NULL for failure. Valid pointer to a JOB_ENTRY on success.
--*/
{ PLIST_ENTRY Next; PJOB_ENTRY JobEntry;
EnterCriticalSection( &CsJob );
Next = JobListHead.Flink; if (Next == NULL) { LeaveCriticalSection( &CsJob ); return NULL; }
while ((ULONG_PTR)Next != (ULONG_PTR)&JobListHead) {
JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
if (JobEntry->InstanceData == (ULONG_PTR) FaxHandle) {
LeaveCriticalSection( &CsJob ); return JobEntry;
}
Next = JobEntry->ListEntry.Flink;
}
LeaveCriticalSection( &CsJob ); return NULL; }
BOOL FindJobByJob( IN PJOB_ENTRY JobEntryToFind )
/*++
Routine Description:
This fuction locates a FAX job by matching the FAX handle value.
Arguments:
FaxHandle - FAX handle returned from startjob
Return Value:
NULL for failure. Valid pointer to a JOB_ENTRY on success.
--*/
{ PLIST_ENTRY Next; PJOB_ENTRY JobEntry;
EnterCriticalSection( &CsJob );
Next = JobListHead.Flink; if (Next == NULL) { LeaveCriticalSection( &CsJob ); return FALSE; }
while ((ULONG_PTR)Next != (ULONG_PTR)&JobListHead) {
JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
if (JobEntry == JobEntryToFind) {
LeaveCriticalSection( &CsJob ); return TRUE;
}
Next = JobEntry->ListEntry.Flink;
}
LeaveCriticalSection( &CsJob ); return FALSE; }
BOOL FaxSendCallback( IN HANDLE FaxHandle, IN HCALL CallHandle, IN DWORD Reserved1, IN DWORD Reserved2 )
/*++
Routine Description:
This fuction is called asychronously by a FAX device provider after a call is established. The sole purpose of the callback is to communicate the call handle from the device provider to the FAX service.
Arguments:
FaxHandle - FAX handle returned from startjob CallHandle - Call handle for newly initiated call Reserved1 - Always zero. Reserved2 - Always zero.
Return Value:
TRUE for success, FAX operation continues. FALSE for failure, FAX operation is terminated.
--*/
{ PJOB_ENTRY JobEntry;
JobEntry = FindJob( FaxHandle ); if (!JobEntry) {
return FALSE;
}
JobEntry->CallHandle = CallHandle;
return TRUE;
}
DWORD FaxSendThread( PFAX_SEND_ITEM FaxSendItem )
/*++
Routine Description:
This fuction runs asychronously as a separate thread to send a FAX document. There is one send thread per outstanding FAX send operation. The thread ends when the document is either successfuly sent or the operation is aborted.
Arguments:
FaxSendItem - pointer to a FAX send item packet that describes the requested FAX send operation.
Return Value:
Always zero.
--*/
{ FAX_SEND FaxSend; PFAX_DEV_STATUS FaxStatus = NULL; DWORD StatusSize; BOOL Rslt; DWORD BytesNeeded; BOOL Retrying = FALSE; BOOL Archived; TCHAR PageCountStr[64]; TCHAR TimeStr[128]; LPDWORD MsgPtr[6]; TCHAR MsgStr[2048]; DWORD MsgCount; FILETIME LocalTime; TCHAR lpDate[50]; int lenDate; TCHAR lpTime[50]; int lenTime; TCHAR lpDateTime[104];
TCHAR lpCallerNumberPlusCompanyName[200]; DWORD lenCallerNumberPlusCompanyName; DWORD delta;
BOOL HandoffJob;
TCHAR lpBranding[400]; DWORD lenBranding; TCHAR lpBrandingEnd[50]; DWORD lenBrandingEnd; DWORD BrandingMaxLen = 115; INT BrandingHeight = 22; // in scan lines.
DWORD PageCount = 0;
//
// 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) { DebugPrint(( TEXT("FaxSendThread exiting because it could not allocate memory") )); return ERROR_NOT_ENOUGH_MEMORY; }
SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
FaxSend.SizeOfStruct = sizeof(FAX_SEND); FaxSend.FileName = FaxSendItem->FileName; FaxSend.CallerName = FaxSendItem->SenderName; FaxSend.CallerNumber = FaxSendItem->Tsid; FaxSend.ReceiverName = FaxSendItem->RecipientName; FaxSend.ReceiverNumber = FaxSendItem->PhoneNumber; FaxSend.CallHandle = 0; // filled in later via TapiStatusThread, if appropriate
FaxSend.Reserved[0] = 0; FaxSend.Reserved[1] = 0; FaxSend.Reserved[2] = 0;
FaxSendItem->JobQueue->JobStatus = JS_INPROGRESS; FaxSendItem->JobEntry->DocumentName = StringDup( FaxSendItem->DocumentName ); HandoffJob = FaxSendItem->JobEntry->HandoffJob;
//
// Replace the original MMR file by one with the Branding on every page.
//
if (FaxUseBranding && FaxSendItem->JobQueue->SendRetries == 0) {
if (FaxSend.CallerNumber == NULL) { DebugPrint(( TEXT("FaxSendThread() CallerNumber==0 NO BRANDING job\n") )); goto lPostBranding; }
if (FaxSend.ReceiverNumber == NULL) { DebugPrint(( TEXT("FaxSendThread() ReceiverNumber==0 NO BRANDING job\n") )); goto lPostBranding; }
if ( ! (lenDate = GetDateFormat( LOCALE_SYSTEM_DEFAULT, DATE_SHORTDATE, NULL, // use system date
NULL, // use locale format
lpDate, sizeof(lpDate)) ) ) {
DebugPrint(( TEXT("FaxSendThread() GetDateFormat failed NO BRANDING job\n") )); goto lPostBranding; }
if ( ! (lenTime = GetTimeFormat( LOCALE_SYSTEM_DEFAULT, TIME_NOSECONDS, NULL, // use system time
NULL, // use locale format
lpTime, sizeof(lpTime)) ) ) {
DebugPrint(( TEXT("FaxSendThread() GetTimeFormat failed NO BRANDING job\n") )); goto lPostBranding; }
_stprintf( lpDateTime, TEXT("%s %s"), lpDate, lpTime);
//
// Create lpCallerNumberPlusCompanyName
//
if (FaxSendItem->SenderCompany) { _stprintf( lpCallerNumberPlusCompanyName, TEXT("%s %s"), FaxSend.CallerNumber, FaxSendItem->SenderCompany); } else { _stprintf( lpCallerNumberPlusCompanyName, TEXT("%s"), FaxSend.CallerNumber ); }
MsgPtr[0] = (LPDWORD) lpDateTime; MsgPtr[1] = (LPDWORD) lpCallerNumberPlusCompanyName; MsgPtr[2] = (LPDWORD) FaxSend.ReceiverNumber; MsgPtr[3] = NULL;
if ( ! ( lenBranding = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, MSG_BRANDING_FULL, 0, lpBranding, sizeof(lpBranding), (va_list *) MsgPtr ) ) ) { DebugPrint(( TEXT("FaxSendThread() MSG_BRANDING_OF failed NO BRANDING job\n") )); goto lPostBranding; }
if ( ! ( lenBrandingEnd = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, NULL, MSG_BRANDING_END, 0, lpBrandingEnd, sizeof(lpBrandingEnd), NULL ) ) ) { DebugPrint(( TEXT("FaxSendThread() MSG_BRANDING_OF failed NO BRANDING job\n") )); goto lPostBranding; }
//
// Make sure we can fit everything.
//
if (lenBranding + lenBrandingEnd + 8 <= BrandingMaxLen) { goto lDoBranding; }
//
// Lets try to skip ReceiverNumber. The important part - is the CallerNumberPlusCompanyName.
//
MsgPtr[0] = (LPDWORD) lpDateTime; MsgPtr[1] = (LPDWORD) lpCallerNumberPlusCompanyName; MsgPtr[2] = NULL;
if ( ! ( lenBranding = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, MSG_BRANDING_SHORT, 0, lpBranding, sizeof(lpBranding), (va_list *) MsgPtr ) ) ) { DebugPrint(( TEXT("FaxSendThread() MSG_BRANDING_SHORT failed NO BRANDING job\n") )); goto lPostBranding; }
if (lenBranding + lenBrandingEnd + 8 <= BrandingMaxLen) { goto lDoBranding; }
//
// We need to truncate CallerNumberPlusCompanyName and re-format the message.
//
delta = lenBranding + lenBrandingEnd + 8 - BrandingMaxLen;
lenCallerNumberPlusCompanyName = _tcslen (lpCallerNumberPlusCompanyName); if (lenCallerNumberPlusCompanyName <= delta) { DebugPrint(( TEXT("FaxSendThread() DELTA logical error NO BRANDING job\n") )); goto lPostBranding; }
lpCallerNumberPlusCompanyName[ lenCallerNumberPlusCompanyName - delta] = TEXT('\0');
MsgPtr[0] = (LPDWORD) lpDateTime; MsgPtr[1] = (LPDWORD) lpCallerNumberPlusCompanyName; MsgPtr[2] = NULL;
if ( ! ( lenBranding = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, MSG_BRANDING_SHORT, 0, lpBranding, sizeof(lpBranding), (va_list *) MsgPtr ) ) ) { DebugPrint(( TEXT("FaxSendThread() 2nd MSG_BRANDING_SHORT failed NO BRANDING job\n") )); goto lPostBranding; }
if (lenBranding + lenBrandingEnd + 8 > BrandingMaxLen) { DebugPrint(( TEXT("FaxSendThread() DELTA 2 logical error NO BRANDING job\n") )); goto lPostBranding; }
lDoBranding:
__try {
if (! MmrAddBranding( FaxSend.FileName, lpBranding, lpBrandingEnd, BrandingHeight) ) { DebugPrint(( TEXT("FaxSendThread() could not ADD Branding\n") )); }
} __except (EXCEPTION_EXECUTE_HANDLER) {
DebugPrint(( TEXT("MmrAddBranding() failed: 0x%08x"), GetExceptionCode() ));
}
}
lPostBranding:
if (!HandoffJob) { FaxSendItem->JobEntry->LineInfo->State = FPS_INITIALIZING; } else { //
// We need to wait for TapiWorkerThread to get an existing CallHandle and put it in the lineinfo structure
//
WaitForSingleObject(FaxSendItem->JobEntry->hCallHandleEvent,INFINITE);
if (!FaxSendItem->JobEntry->LineInfo->HandoffCallHandle) { //
// somehow the call handoff failed, we can't send the fax
//
FaxSendItem->JobEntry->LineInfo->State = FPS_ABORTING; __try {
Rslt = FaxSendItem->JobEntry->LineInfo->Provider->FaxDevAbortOperation( (HANDLE) FaxSendItem->JobEntry->InstanceData); } __except (EXCEPTION_EXECUTE_HANDLER) { FaxSendItem->JobEntry->ErrorCode = GetExceptionCode(); }
} else { //
// Set the call handle, we're ready to send the fax
//
FaxSend.CallHandle = FaxSendItem->JobEntry->LineInfo->HandoffCallHandle; FaxSendItem->JobEntry->LineInfo->State = FPS_INITIALIZING; } }
DebugPrint((TEXT("Started FAX send - File [%s] - Number [%s]"), FaxSend.FileName, FaxSendItem->JobEntry->PhoneNumber ));
__try {
Rslt = FaxSendItem->JobEntry->LineInfo->Provider->FaxDevSend( (HANDLE) FaxSendItem->JobEntry->InstanceData, &FaxSend, FaxSendCallback );
} __except (EXCEPTION_EXECUTE_HANDLER) {
FaxSendItem->JobEntry->ErrorCode = GetExceptionCode();
}
__try {
FaxStatus->SizeOfStruct = sizeof(FAX_DEV_STATUS);
FaxSendItem->JobEntry->LineInfo->Provider->FaxDevReportStatus( (HANDLE) FaxSendItem->JobEntry->InstanceData, FaxStatus, StatusSize, &BytesNeeded );
} __except (EXCEPTION_EXECUTE_HANDLER) {
DebugPrint(( TEXT("FaxDevReportStatus() failed: 0x%08x"), GetExceptionCode() ));
}
DebugPrint(( TEXT("Send status: 0x%08x, string: 0x%08x File %s"), FaxStatus->StatusId, FaxStatus->StringId, FaxSend.FileName ));
//
// enter critical section to block out FaxStatusThread
//
EnterCriticalSection( &CsJob );
GetSystemTimeAsFileTime( (FILETIME*) &FaxSendItem->JobEntry->EndTime ); FaxSendItem->JobEntry->ElapsedTime = FaxSendItem->JobEntry->EndTime - FaxSendItem->JobEntry->StartTime; PageCount = FaxStatus->PageCount;
if (!Rslt) {
switch (FaxStatus->StatusId) { case FS_LINE_UNAVAILABLE: //
// this is the glare condition
//
if (PerfCounters) { InterlockedIncrement( (PLONG)&PerfCounters->OutboundFailedXmit ); }
Retrying = TRUE; break;
case FS_NO_ANSWER: case FS_NO_DIAL_TONE: case FS_DISCONNECTED: case FS_BUSY: case FS_NOT_FAX_CALL: case FS_FATAL_ERROR:
if (PerfCounters){ InterlockedIncrement( (PLONG)&PerfCounters->OutboundFailedConnections ); }
FaxSendItem->JobQueue->SendRetries++;
if (FaxSendItem->JobQueue->SendRetries <= FaxSendRetries) { Retrying = TRUE; } else { //
// retries exceeded, mark job as expired
//
FILETIME CurrentFileTime; LARGE_INTEGER NewTime;
FaxSendItem->JobQueue->JobStatus = JS_RETRIES_EXCEEDED ;
GetSystemTimeAsFileTime( &CurrentFileTime ); NewTime.LowPart = CurrentFileTime.dwLowDateTime; NewTime.HighPart = CurrentFileTime.dwHighDateTime;
FaxSendItem->JobQueue->ScheduleTime = NewTime.QuadPart; }
FaxLogSend( FaxSendItem, Rslt, FaxStatus, Retrying );
break ;
case FS_USER_ABORT:
FaxLogSend( FaxSendItem, Rslt, FaxStatus, FALSE ); break ;
default: if (PerfCounters){ InterlockedIncrement( (PLONG)&PerfCounters->OutboundFailedXmit ); } }
//
// clean up the job queue entry
//
EnterCriticalSection ( &CsQueue ); FaxSendItem->JobQueue->RefCount -= 1;
//
// don't retry a handoff job
//
if ( FaxSendItem->JobQueue->JobEntry && (FaxSendItem->JobQueue->JobEntry->HandoffJob || FaxSendItem->JobQueue->JobEntry->Aborting) ) { RemoveJobQueueEntry( FaxSendItem->JobQueue ); FaxSendItem->JobQueue = NULL;
} else if (Retrying) {
FaxSendItem->JobQueue->JobStatus = JS_RETRYING; FaxSendItem->JobQueue->JobEntry = NULL;
RescheduleJobQueueEntry( FaxSendItem->JobQueue ); } LeaveCriticalSection ( &CsQueue );
//
// send the negative delivery report
//
if (!Retrying && ((FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX && FaxSendItem->JobEntry->DeliveryReportProfile) || (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL))) { SYSTEMTIME SystemTime;
FileTimeToLocalFileTime( (FILETIME*) &FaxSendItem->JobEntry->StartTime, &LocalTime ); FileTimeToSystemTime( &LocalTime, &SystemTime );
GetTimeFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, &SystemTime, NULL, TimeStr, sizeof(TimeStr) );
MsgPtr[0] = (LPDWORD) FaxSendItem->SenderName; MsgPtr[1] = (LPDWORD) FaxSendItem->RecipientName; MsgPtr[2] = (LPDWORD) FaxSendItem->JobEntry->PhoneNumber; MsgPtr[3] = (LPDWORD) TimeStr; MsgPtr[4] = (LPDWORD) FaxSendItem->JobEntry->LineInfo->DeviceName; MsgPtr[5] = (LPDWORD) GetString( FaxStatus->StatusId );
MsgCount = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, MSG_NDR, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), MsgStr, sizeof(MsgStr), (va_list *) MsgPtr );
if (FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX) { StoreMapiMessage( FaxSendItem->JobEntry->DeliveryReportProfile, GetString( IDS_SERVER_NAME ), GetString( IDS_NDR_SUBJECT ), MsgStr, FaxSend.FileName, GetString( IDS_NDR_FILENAME ), IMPORTANCE_HIGH, NULL, &BytesNeeded ); } else if (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL && InboundProfileInfo) { MailMapiMessage( InboundProfileInfo, FaxSendItem->JobEntry->DeliveryReportAddress, GetString( IDS_NDR_SUBJECT ), MsgStr, FaxSend.FileName, GetString( IDS_NDR_FILENAME ), IMPORTANCE_HIGH, &BytesNeeded ); } }
} else {
//
// add MS tiff tags to the sent fax
// the tiff file is a temp file, so we add the tags to the source file
//
AddTiffTags(FaxSend.FileName, FaxSendItem->JobEntry->StartTime, FaxStatus, &FaxSend );
//
// if the send was successful, archive the file
//
Archived = ArchivePrintJob( FaxSend.FileName );
FaxLogSend( FaxSendItem, Rslt, FaxStatus, TRUE );
//
// Increment counters for Performance Monitor
//
if (PerfCounters){ SYSTEMTIME SystemTime ; DWORD Seconds ; HANDLE FileHandle ; DWORD Bytes = 0 ; /// Compute #bytes in the file FaxSend.FileName and stick it here!
FileHandle = CreateFile( FaxSend.FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if(FileHandle != INVALID_HANDLE_VALUE){ Bytes = GetFileSize( FileHandle, NULL ); CloseHandle( FileHandle ); } FileTimeToSystemTime( (FILETIME*)&FaxSendItem->JobEntry->ElapsedTime, &SystemTime ); Seconds = (DWORD)( SystemTime.wSecond + 60 * ( SystemTime.wMinute + 60 * SystemTime.wHour )); InterlockedIncrement( (PLONG)&PerfCounters->OutboundFaxes ); InterlockedIncrement( (PLONG)&PerfCounters->TotalFaxes ); InterlockedExchangeAdd( (PLONG)&PerfCounters->OutboundPages, (LONG)FaxStatus->PageCount ); InterlockedExchangeAdd( (PLONG)&PerfCounters->TotalPages, (LONG)FaxStatus->PageCount );
EnterCriticalSection( &CsPerfCounters );
OutboundSeconds += Seconds; TotalSeconds += Seconds; PerfCounters->OutboundMinutes = OutboundSeconds / 60 ; PerfCounters->TotalMinutes = TotalSeconds / 60 ; PerfCounters->OutboundBytes += Bytes; PerfCounters->TotalBytes += Bytes;
LeaveCriticalSection( &CsPerfCounters ); }
//
// send the positive delivery report
//
if ((FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX && FaxSendItem->JobEntry->DeliveryReportProfile) || (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL)) { SYSTEMTIME SystemTime;
_ltot( (LONG) PageCount, PageCountStr, 10 );
FileTimeToLocalFileTime( (FILETIME*) &FaxSendItem->JobEntry->StartTime, &LocalTime ); FileTimeToSystemTime( &LocalTime, &SystemTime );
GetTimeFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, &SystemTime, NULL, TimeStr, sizeof(TimeStr) );
MsgPtr[0] = (LPDWORD) FaxSendItem->SenderName; MsgPtr[1] = (LPDWORD) FaxSendItem->RecipientName; MsgPtr[2] = (LPDWORD) FaxSendItem->JobEntry->PhoneNumber; MsgPtr[3] = (LPDWORD) PageCountStr; MsgPtr[4] = (LPDWORD) TimeStr; MsgPtr[5] = (LPDWORD) FaxSendItem->JobEntry->LineInfo->DeviceName; MsgPtr[6] = NULL;
MsgCount = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, MSG_DR, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), MsgStr, sizeof(MsgStr), (va_list *) MsgPtr );
if (FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX) { StoreMapiMessage( FaxSendItem->JobEntry->DeliveryReportProfile, GetString( IDS_SERVICE_NAME ), GetString( IDS_DR_SUBJECT ), MsgStr, FaxSend.FileName, GetString( IDS_DR_FILENAME ), IMPORTANCE_NORMAL, NULL, &BytesNeeded ); } else if (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL && InboundProfileInfo) { MailMapiMessage( InboundProfileInfo, FaxSendItem->JobEntry->DeliveryReportAddress, GetString( IDS_DR_SUBJECT ), MsgStr, FaxSend.FileName, GetString( IDS_DR_FILENAME ), IMPORTANCE_NORMAL, &BytesNeeded ); } }
//
// remove this queue entry from the queue list
//
EnterCriticalSection ( &CsQueue ); FaxSendItem->JobQueue->RefCount -= 1; RemoveJobQueueEntry( FaxSendItem->JobQueue ); FaxSendItem->JobQueue = NULL; LeaveCriticalSection ( &CsQueue ); }
//
// do any special work for a broadcast job
//
if (FaxSendItem->JobEntry->BroadcastJob) { DeleteFile( FaxSendItem->FileName ); }
FaxSendItem->JobEntry->ErrorCode = FaxStatus->StatusId; FaxSendItem->JobEntry->RefCount -= 1; FaxSendItem->JobEntry->LineInfo->State = FPS_AVAILABLE;
if (FaxSendItem->JobEntry->RefCount == 0 && FaxSendItem->JobEntry->hEventEnd) { SetEvent( FaxSendItem->JobEntry->hEventEnd ); EndJob( FaxSendItem->JobEntry );
EnterCriticalSection ( &CsQueue ); // JobQueue may already be NULL for an aborted job
if (FaxSendItem->JobQueue) { if (!Retrying && (FaxSendItem->JobQueue->JobStatus != JS_RETRIES_EXCEEDED)) { FaxSendItem->JobQueue->JobStatus = JS_DELETING; } FaxSendItem->JobQueue->JobEntry = NULL; } LeaveCriticalSection ( &CsQueue );
}
LeaveCriticalSection( &CsJob );
if (!Retrying && (!ArchiveOutgoingFaxes || Archived)) { DeleteFile( FaxSend.FileName ); }
MemFree( FaxSendItem->FileName ); MemFree( FaxSendItem->PhoneNumber ); MemFree( FaxSendItem->Tsid ); MemFree( FaxSendItem->RecipientName ); MemFree( FaxSendItem->SenderName ); MemFree( FaxSendItem->SenderDept ); MemFree( FaxSendItem->SenderCompany ); MemFree( FaxSendItem->BillingCode ); MemFree( FaxSendItem->DocumentName ); MemFree( FaxSendItem ); MemFree( FaxStatus ); ReleaseSemaphore( JobQueueSemaphore, 1, NULL );
SetThreadExecutionState(ES_CONTINUOUS);
return 0; }
PJOB_ENTRY StartJob( DWORD DeviceId, DWORD JobType, LPWSTR FaxNumber )
/*++
Routine Description:
This fuction calls the device provider's StartJob function.
Arguments:
DeviceId - Device Id to start the job on, or USE_SERVER_DEVICE. JobType - type of job FaxNumber - phone number for outbound jobs
Return Value:
Pointer to a JOB_ENTRY, or NULL for failure.
--*/
{ BOOL Failure = TRUE; PJOB_ENTRY JobEntry = NULL; PLINE_INFO LineInfo;
JobEntry = (PJOB_ENTRY) MemAlloc( sizeof(JOB_ENTRY) ); if (!JobEntry) { goto exit; }
if (FaxNumber) { //
// get a cannonical phone number
//
LPLINETRANSLATEOUTPUT LineTranslateOutput = NULL;
if (MyLineTranslateAddress( FaxNumber, 0, &LineTranslateOutput ) == 0) { wcsncpy( JobEntry->PhoneNumber, (LPWSTR) ((LPBYTE)LineTranslateOutput + LineTranslateOutput->dwDisplayableStringOffset), SIZEOF_PHONENO );
MemFree( LineTranslateOutput ); } else { wcsncpy( JobEntry->PhoneNumber, FaxNumber, SIZEOF_PHONENO ); } }
//
// assume send job without use_server_device is a handoff job
//
if (JobType == JT_SEND && DeviceId != USE_SERVER_DEVICE) { LineInfo = GetTapiLineForFaxOperation( DeviceId, JobType, JobEntry->PhoneNumber, TRUE ); } else { LineInfo = GetTapiLineForFaxOperation( DeviceId, JobType, JobEntry->PhoneNumber, FALSE ); } if (!LineInfo) { goto exit; }
JobEntry->JobType = JT_UNKNOWN; JobEntry->CallHandle = 0; JobEntry->InstanceData = 0; JobEntry->ErrorCode = 0; JobEntry->LineInfo = LineInfo; JobEntry->SendIdx = -1; JobEntry->Released = FALSE; JobEntry->HandoffJob = (JobType == JT_SEND && DeviceId != USE_SERVER_DEVICE); JobEntry->hEventEnd = CreateEvent( NULL, FALSE, FALSE, NULL ); JobEntry->hCallHandleEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
GetSystemTimeAsFileTime( (FILETIME*) &JobEntry->StartTime );
__try {
if ((!(LineInfo->Flags & FPF_VIRTUAL)) && (!LineInfo->hLine) && (!OpenTapiLine( LineInfo ))) { DebugPrint(( TEXT("Could not get an open tapi line, StartJob() failed") )); goto exit; }
if (LineInfo->Provider->FaxDevStartJob( LineInfo->hLine, LineInfo->DeviceId, (PHANDLE) &JobEntry->InstanceData, StatusCompletionPortHandle, (ULONG_PTR) LineInfo )) {
EnterCriticalSection( &CsJob ); InsertTailList( &JobListHead, &JobEntry->ListEntry ); LeaveCriticalSection( &CsJob ); Failure = FALSE;
} else {
DebugPrint((TEXT("FaxDevStartJob failed")));
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
}
LineInfo->JobEntry = JobEntry;
exit: if (Failure) { if (LineInfo) { ReleaseTapiLine( LineInfo, LineInfo->JobEntry ? LineInfo->JobEntry->CallHandle : 0 ); } if (JobEntry) { CloseHandle (JobEntry->hEventEnd); CloseHandle (JobEntry->hCallHandleEvent); MemFree( JobEntry ); } JobEntry = NULL; }
return JobEntry; }
BOOL EndJob( IN PJOB_ENTRY JobEntry )
/*++
Routine Description:
This fuction calls the device provider's EndJob function.
Arguments:
None.
Return Value:
Error code.
--*/
{ BOOL rVal; PJOB_INFO_1 JobInfo = NULL;
if (!FindJobByJob( JobEntry )) {
//
// if we get here then it means we hit a race
// condition where the FaxSendThread called EndJob
// at the same time that a client app did.
//
return ERROR_SUCCESS; }
if (JobEntry->RefCount) {
HANDLE hEventEnd; DWORD Result;
EnterCriticalSection( &CsJob );
hEventEnd = JobEntry->hEventEnd;
LeaveCriticalSection( &CsJob );
while (TRUE) {
Result = WaitForSingleObject( hEventEnd, 1000 );
// if the wait timed out and FAX_SendDocument() has been called
// (SendIdx != -1), then check for a job status change
if (Result != WAIT_TIMEOUT) { //
// if the event has been signaled or deleted, then return
//
break; } }
return ERROR_SUCCESS;
}
EnterCriticalSection( &CsJob );
if (!JobEntry->Released) { __try {
rVal = JobEntry->LineInfo->Provider->FaxDevEndJob( (HANDLE) JobEntry->InstanceData ); if (!rVal) { DebugPrint(( TEXT("FaxDevEndJob() failed") )); }
} __except (EXCEPTION_EXECUTE_HANDLER) {
DebugPrint(( TEXT("FaxDevEndJob() crashed, ec=0x%08x"), GetExceptionCode() ));
} }
if (!JobEntry->Released) { if (JobEntry->LineInfo->State != FPS_NOT_FAX_CALL) { ReleaseTapiLine( JobEntry->LineInfo, JobEntry->CallHandle ); JobEntry->CallHandle = 0; } }
RemoveEntryList( &JobEntry->ListEntry );
EnterCriticalSection( &CsLine ); JobEntry->LineInfo->JobEntry = NULL; LeaveCriticalSection( &CsLine );
CloseHandle( JobEntry->hEventEnd ); CloseHandle( JobEntry->hCallHandleEvent );
MemFree( (LPBYTE) JobEntry->JobParam.RecipientNumber ); MemFree( (LPBYTE) JobEntry->JobParam.RecipientName ); MemFree( (LPBYTE) JobEntry->JobParam.Tsid ); MemFree( (LPBYTE) JobEntry->JobParam.SenderName ); MemFree( (LPBYTE) JobEntry->JobParam.SenderCompany ); MemFree( (LPBYTE) JobEntry->JobParam.SenderDept ); MemFree( (LPBYTE) JobEntry->JobParam.BillingCode );
MemFree( JobEntry->FaxStatus.CSI ); MemFree( JobEntry->FaxStatus.CallerId ); MemFree( JobEntry->FaxStatus.RoutingInfo );
MemFree( JobEntry->DeliveryReportAddress ); MemFree( JobEntry->DocumentName ); MemFree( JobEntry->UserName );
//
// There could have been a request to change the port status while we were handling this job.
// We allow the caller to modify a few of these requests to succeed, like the ring count for instance.
// While we still have the job critical section, let's make sure that we commit any requested changes to the
// registry. This should be a fairly quick operation.
//
CommitDeviceChanges();
LeaveCriticalSection( &CsJob );
MemFree( JobEntry );
return rVal; }
BOOL ReleaseJob( IN PJOB_ENTRY JobEntry ) { BOOL rVal;
if (!FindJobByJob( JobEntry )) { return ERROR_SUCCESS; }
EnterCriticalSection( &CsJob );
__try {
rVal = JobEntry->LineInfo->Provider->FaxDevEndJob( (HANDLE) JobEntry->InstanceData ); if (!rVal) { DebugPrint(( TEXT("FaxDevEndJob() failed") )); }
} __except (EXCEPTION_EXECUTE_HANDLER) {
DebugPrint(( TEXT("FaxDevEndJob() crashed, ec=0x%08x"), GetExceptionCode() ));
}
if (JobEntry->LineInfo->State != FPS_NOT_FAX_CALL) { ReleaseTapiLine( JobEntry->LineInfo, JobEntry->CallHandle ); JobEntry->CallHandle = 0; }
JobEntry->Released = TRUE;
LeaveCriticalSection( &CsJob );
return TRUE; }
DWORD SendDocument( PJOB_ENTRY JobEntry, LPTSTR FileName, PFAX_JOB_PARAM JobParam, PJOB_QUEUE JobQueue )
/*++
Routine Description:
This fuction queues a new item that requests a FAX document be sent.
Arguments:
JobEntry - Pointer to a JOB_ENTRY created by StartJob. FileName - File name containing the TIFF data JobParam - Pointer to FAX_JOB_PARAM struct Return Value:
Error code.
--*/
{ PFAX_SEND_ITEM FaxSendItem; DWORD ThreadId; HANDLE hThread;
if (JobEntry->RefCount) {
//
// only one operation per job
//
return ERROR_IO_PENDING;
}
FaxSendItem = (PFAX_SEND_ITEM) MemAlloc(sizeof(FAX_SEND_ITEM)); if (!FaxSendItem) { return ERROR_NOT_ENOUGH_MEMORY; }
FaxSendItem->JobEntry = JobEntry; FaxSendItem->FileName = StringDup( FileName ); FaxSendItem->PhoneNumber = StringDup( JobParam->RecipientNumber ); if (JobParam->Tsid == NULL || JobParam->Tsid[0] == 0 || FaxUseDeviceTsid) { FaxSendItem->Tsid = StringDup( JobEntry->LineInfo->Tsid ); } else { FaxSendItem->Tsid = StringDup( JobParam->Tsid ); } FaxSendItem->RecipientName = StringDup( JobParam->RecipientName ); FaxSendItem->SenderName = StringDup( JobParam->SenderName ); FaxSendItem->SenderDept = StringDup( JobParam->SenderDept ); FaxSendItem->SenderCompany = StringDup( JobParam->SenderCompany ); FaxSendItem->BillingCode = StringDup( JobParam->BillingCode ); FaxSendItem->DocumentName = StringDup( JobParam->DocumentName );
FaxSendItem->JobQueue = JobQueue; JobQueue->RefCount += 1; JobEntry->RefCount += 1;
hThread = CreateThread( NULL, 1024*100, (LPTHREAD_START_ROUTINE) FaxSendThread, (LPVOID) FaxSendItem, 0, &ThreadId );
if (!hThread) { MemFree( FaxSendItem->FileName ); MemFree( FaxSendItem->PhoneNumber ); MemFree( FaxSendItem->Tsid ); MemFree( FaxSendItem->RecipientName ); MemFree( FaxSendItem->SenderName ); MemFree( FaxSendItem->SenderDept ); MemFree( FaxSendItem->SenderCompany ); MemFree( FaxSendItem->BillingCode ); MemFree( FaxSendItem ); CloseHandle( hThread ); return GetLastError(); }
CloseHandle( hThread );
return ERROR_SUCCESS; }
DWORD FaxStatusThread( LPVOID UnUsed )
/*++
Routine Description:
This fuction runs asychronously as a separate thread to query the status of all outstanding fax jobs. The status is updated in the JOB_ENTRY structure and the print job is updated with a explanitory string.
Arguments:
UnUsed - UnUsed pointer
Return Value:
Always zero.
--*/
{ PJOB_ENTRY JobEntry; PFAX_DEV_STATUS FaxStatus; BOOL Rval; DWORD Bytes; ULONG_PTR CompletionKey; INT PageCount;
while( TRUE ) {
Rval = GetQueuedCompletionStatus( StatusCompletionPortHandle, &Bytes, &CompletionKey, (LPOVERLAPPED*) &FaxStatus, INFINITE ); if (!Rval) { DebugPrint(( TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"), GetLastError() )); continue; }
if (CompletionKey == EVENT_COMPLETION_KEY) { //
// Let each registered client know about the fax event
//
PLIST_ENTRY Next; PFAX_CLIENT_DATA ClientData; PFAX_EVENT FaxEvent = (PFAX_EVENT) FaxStatus;
EnterCriticalSection( &CsClients );
Next = ClientsListHead.Flink; if (Next) { while ((ULONG_PTR)Next != (ULONG_PTR)&ClientsListHead) { DWORD i; BOOL fMessageSent = FALSE;
ClientData = CONTAINING_RECORD( Next, FAX_CLIENT_DATA, ListEntry ); DebugPrint(( TEXT("%d: Current : %08x\t Handle : %08x\t Next : %08x Head: %08x : \n"), GetTickCount(), (ULONG_PTR)Next, ClientData->hWnd? (ULONG_PTR) ClientData->hWnd : (ULONG_PTR) ClientData->FaxClientHandle, (ULONG_PTR)ClientData->ListEntry.Flink, (ULONG_PTR)&ClientsListHead )); Next = ClientData->ListEntry.Flink;
//
// only send the started message once to each client
//
if ((FaxEvent->EventId == FEI_FAXSVC_STARTED) && ClientData->StartedMsg) { fMessageSent = TRUE; goto next_client; } if (ClientData->hWnd) {
fMessageSent = PostClientMessage(ClientData,FaxEvent); ClientData->StartedMsg = (FaxEvent->EventId == FEI_FAXSVC_STARTED) ? TRUE : ClientData->StartedMsg; goto next_client;
}
if (!ClientData->FaxClientHandle) { for(i = 0; i < 10; i++){ __try {
Rval = FAX_OpenConnection( ClientData->FaxHandle, ClientData->Context, &ClientData->FaxClientHandle ); if (Rval) { DebugPrint(( TEXT("FAX_OpenConnection() failed, ec=0x%08x"), Rval )); continue; } else { break; } } __except (EXCEPTION_EXECUTE_HANDLER) { DebugPrint(( TEXT("FAX_OpenConnection() crashed: 0x%08x"), GetExceptionCode() )); }
Sleep( 1000 );
} }
//
// if we don't have a handle at this point, forget it
//
if (!ClientData->FaxClientHandle) { goto next_client; }
for(i = 0; i < 10; i++){ __try { Rval = FAX_ClientEventQueue( ClientData->FaxClientHandle, *FaxEvent ); if (Rval) { DebugPrint(( TEXT("FAX_ClientEventQueue() failed, ec=0x%08x"), Rval )); continue; } else { fMessageSent = TRUE; ClientData->StartedMsg = (FaxEvent->EventId == FEI_FAXSVC_STARTED) ? TRUE : ClientData->StartedMsg; break; } } __except (EXCEPTION_EXECUTE_HANDLER) { DebugPrint(( TEXT("FAX_ClientEventQueue() crashed: 0x%08x"), GetExceptionCode() )); }
Sleep( 1000 );
}
next_client: if (!fMessageSent) { //
// stale list entry, remove the client from our list.
//
if (ClientData->hWnd && ClientData->hClientToken) { CloseHandle( ClientData->hClientToken ); MemFree( (LPBYTE) ClientData->WindowStation ); MemFree( (LPBYTE) ClientData->Desktop ); } RemoveEntryList( &ClientData->ListEntry ); MemFree( ClientData ); } } }
LeaveCriticalSection( &CsClients );
//
// signal event if fax service ended, so we can terminate the process
//
if (FaxEvent->EventId == FEI_FAXSVC_ENDED && hServiceEndEvent != INVALID_HANDLE_VALUE) { SetEvent( hServiceEndEvent ) ; }
MemFree( FaxEvent ); FaxStatus = NULL;
continue; }
//
// (else we're dealing with a status update from an FSP)
//
EnterCriticalSection( &CsJob ); JobEntry = ((PLINE_INFO) CompletionKey)->JobEntry; if (!JobEntry) { //
// this code path exposes a memory leak.
// the completion packed is not freed if this
// path is taken. the problem is that the
// memory cannot be freed if we don't have
// access to the job structure.
//
LeaveCriticalSection( &CsJob );
DebugPrint(( TEXT("FaxStatusThread - NULL JobEntry got StatusId 0x%08x"), FaxStatus->StatusId ));
continue; }
JobEntry->LineInfo->State = FaxStatus->StatusId; CreateFaxEvent( JobEntry->LineInfo->PermanentLineID, MapStatusIdToEventId( FaxStatus->StatusId ), JobEntry->JobId );
PageCount = FaxStatus->PageCount ? FaxStatus->PageCount : -1;
MemFree( JobEntry->FaxStatus.CSI ); MemFree( JobEntry->FaxStatus.CallerId ); MemFree( JobEntry->FaxStatus.RoutingInfo );
JobEntry->FaxStatus.SizeOfStruct = FaxStatus->SizeOfStruct; JobEntry->FaxStatus.StatusId = FaxStatus->StatusId; JobEntry->FaxStatus.StringId = FaxStatus->StringId; JobEntry->FaxStatus.PageCount = FaxStatus->PageCount; JobEntry->FaxStatus.CSI = StringDup( FaxStatus->CSI ); JobEntry->FaxStatus.CallerId = StringDup( FaxStatus->CallerId ); JobEntry->FaxStatus.RoutingInfo = StringDup( FaxStatus->RoutingInfo ); JobEntry->FaxStatus.Reserved[0] = 0; JobEntry->FaxStatus.Reserved[1] = 0; JobEntry->FaxStatus.Reserved[2] = 0;
HeapFree( JobEntry->LineInfo->Provider->HeapHandle, 0, FaxStatus );
LeaveCriticalSection( &CsJob ); }
return 0; }
BOOL InitializeJobManager( PREG_FAX_SERVICE FaxReg )
/*++
Routine Description:
This fuction initializes the thread pool and FAX service queues.
Arguments:
ThreadHint - Number of threads to create in the initial pool.
Return Value:
Thread return value.
--*/
{ HANDLE hThread; DWORD ThreadId; DWORD i;
InitializeListHead( &JobListHead ); InitializeCriticalSection( &CsJob );
InitializeListHead( &QueueListHead ); InitializeCriticalSection( &CsQueue );
SetRetryValues( FaxReg );
if (GetFileAttributes( FaxReceiveDir ) == 0xffffffff) { MakeDirectory( FaxReceiveDir ); }
if (GetFileAttributes( FaxQueueDir ) == 0xffffffff) { MakeDirectory( FaxQueueDir ); }
StatusCompletionPortHandle = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, MAX_STATUS_THREADS ); if (!StatusCompletionPortHandle) { DebugPrint(( TEXT("CreateIoCompletionPort() failed, ec=0x%08x"), GetLastError() )); return FALSE; }
hThread = CreateThread( NULL, 1024*100, (LPTHREAD_START_ROUTINE) JobQueueThread, NULL, 0, &ThreadId ); if (!hThread) { return FALSE; }
CloseHandle( hThread );
for (i=0; i<MAX_STATUS_THREADS; i++) { hThread = CreateThread( NULL, 1024*100, (LPTHREAD_START_ROUTINE) FaxStatusThread, NULL, 0, &ThreadId );
if (!hThread) { return FALSE; }
CloseHandle( hThread ); }
return TRUE; }
VOID SetRetryValues( PREG_FAX_SERVICE FaxReg ) { FaxSendRetries = FaxReg->Retries; FaxSendRetryDelay = (INT) FaxReg->RetryDelay; FaxDirtyDays = FaxReg->DirtyDays; QueuePaused = FaxReg->QueuePaused; NextJobId = FaxReg->NextJobNumber; ForceReceive = FaxReg->ForceReceive; TerminationDelay = FaxReg->TerminationDelay == 0 ? 30 : FaxReg->TerminationDelay; FaxUseDeviceTsid = FaxReg->UseDeviceTsid; FaxUseBranding = FaxReg->Branding; ServerCp = FaxReg->ServerCp; StartCheapTime = FaxReg->StartCheapTime; StopCheapTime = FaxReg->StopCheapTime; ArchiveOutgoingFaxes = FaxReg->ArchiveOutgoingFaxes; ArchiveDirectory = StringDup( FaxReg->ArchiveDirectory ); }
LPTSTR ExtractFaxTag( LPTSTR pTagKeyword, LPTSTR pTaggedStr, INT *pcch )
/*++
Routine Description:
Find the value of for the specified tag in a tagged string.
Arguments:
pTagKeyword - specifies the interested tag keyword pTaggedStr - points to the tagged string to be searched pcch - returns the length of the specified tag value (if found)
Return Value:
Points to the value for the specified tag. NULL if the specified tag is not found
NOTE:
Tagged strings have the following form: <tag>value<tag>value
The format of tags is defined as: <$FAXTAG$ tag-name>
There is exactly one space between the tag keyword and the tag name. Characters in a tag are case-sensitive.
--*/
{ LPTSTR pValue;
if (pValue = _tcsstr(pTaggedStr, pTagKeyword)) {
pValue += _tcslen(pTagKeyword);
if (pTaggedStr = _tcsstr(pValue, FAXTAG_PREFIX)) *pcch = (INT)(pTaggedStr - pValue); else *pcch = _tcslen(pValue); }
return pValue; }
BOOL AddTiffTags( LPTSTR FaxFileName, DWORDLONG SendTime, PFAX_DEV_STATUS FaxStatus, PFAX_SEND FaxSend )
/*++
Routine Description:
Add Ms Tiff Tags to a sent fax. Wraps TiffAddMsTags...
Arguments:
FaxFileName - Name of the file to archive SendTime - time the fax was sent FaxStatus - job status FaxSend - FAX_SEND structure for sent fax, includes CSID.
Return Value:
TRUE - The tags were added. FALSE - The tags were not added.
--*/ { MS_TAG_INFO MsTagInfo; WCHAR wcZero = L'\0';
MsTagInfo.RecipName = NULL; if (FaxSend->ReceiverName && (FaxSend->ReceiverName[0] != wcZero) ) { MsTagInfo.RecipName = FaxSend->ReceiverName; }
MsTagInfo.RecipNumber = NULL; if (FaxSend->ReceiverNumber && (FaxSend->ReceiverNumber[0] != wcZero) ) { MsTagInfo.RecipNumber = FaxSend->ReceiverNumber; }
MsTagInfo.SenderName = NULL; if (FaxSend->CallerName && (FaxSend->CallerName[0] != wcZero) ) { MsTagInfo.SenderName = FaxSend->CallerName; }
MsTagInfo.Routing = NULL; if (FaxStatus->RoutingInfo && (FaxStatus->RoutingInfo[0] != wcZero) ) { MsTagInfo.Routing = FaxStatus->RoutingInfo; }
MsTagInfo.CallerId = NULL; if (FaxStatus->CallerId && (FaxStatus->CallerId[0] != wcZero) ) { MsTagInfo.CallerId = FaxStatus->CallerId; }
MsTagInfo.Csid = NULL; if (FaxStatus->CSI && (FaxStatus->CSI[0] != wcZero) ) { MsTagInfo.Csid = FaxStatus->CSI; }
MsTagInfo.Tsid = NULL; if (FaxSend->CallerNumber && (FaxSend->CallerNumber[0] != wcZero) ) { MsTagInfo.Tsid = FaxSend->CallerNumber; }
MsTagInfo.FaxTime = SendTime;
return TiffAddMsTags( FaxFileName, &MsTagInfo );
}
BOOL ArchivePrintJob( LPTSTR FaxFileName )
/*++
Routine Description:
Archive a tiff file that has been sent by copying the file to an archive directory.
Arguments:
FaxFileName - Name of the file to archive
Return Value:
TRUE - The copy was made. FALSE - The copy was not made.
--*/ { BOOL rVal = FALSE; WCHAR ArchiveFileName[MAX_PATH];
if (!ArchiveOutgoingFaxes) { return FALSE; }
//
// be sure that the dir exists
//
MakeDirectory( ArchiveDirectory );
//
// get the file name
//
if (GenerateUniqueFileName( ArchiveDirectory, NULL, ArchiveFileName, sizeof(ArchiveFileName)/sizeof(WCHAR)) != 0) { rVal = TRUE; }
if (rVal) {
rVal = CopyFile( FaxFileName, ArchiveFileName, FALSE );
} if (rVal) { FaxLog( FAXLOG_CATEGORY_OUTBOUND, FAXLOG_LEVEL_MAX, 2, MSG_FAX_ARCHIVE_SUCCESS, FaxFileName, ArchiveFileName ); } else { FaxLog( FAXLOG_CATEGORY_OUTBOUND, FAXLOG_LEVEL_MIN, 3, MSG_FAX_ARCHIVE_FAILED, FaxFileName, ArchiveFileName, GetLastErrorText(GetLastError()) ); }
return rVal;
}
PVOID MyGetJob( HANDLE hPrinter, DWORD level, DWORD jobId )
/*++
Routine Description:
Wrapper function for spooler API GetJob
Arguments:
hPrinter - Handle to the printer object level - Level of JOB_INFO structure interested jobId - Specifies the job ID
Return Value:
Pointer to a JOB_INFO structure, NULL if there is an error
--*/
{ PBYTE pJobInfo = NULL; DWORD cbNeeded;
if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) && GetLastError() == ERROR_INSUFFICIENT_BUFFER && (pJobInfo = MemAlloc(cbNeeded)) && GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded)) { return pJobInfo; }
MemFree(pJobInfo); return NULL; }
|