1897 lines
51 KiB
C
1897 lines
51 KiB
C
/*++
|
|
|
|
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;
|
|
}
|