Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6077 lines
183 KiB

/*++
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 management.
Author:
Wesley Witt (wesw) 24-Jan-1996
Revision History:
--*/
#include "faxsvc.h"
#include "faxreg.h"
#pragma hdrstop
#include <strsafe.h>
#include <efsputil.h>
using namespace std;
// Globals
LIST_ENTRY g_JobListHead; //List of currently running jobs (for which FaxDevStartJob was called).
CFaxCriticalSection g_CsJob;
HANDLE g_StatusCompletionPortHandle;
HINSTANCE g_hResource;
DWORD g_dwFaxSendRetries;
DWORD g_dwFaxSendRetryDelay;
DWORD g_dwFaxDirtyDays;
BOOL g_fFaxUseDeviceTsid;
BOOL g_fFaxUseBranding;
BOOL g_fServerCp;
FAX_TIME g_StartCheapTime;
FAX_TIME g_StopCheapTime;
DWORD g_dwNextJobId;
#define JOB_GROUP_FILE_EXTENSION TEXT("FSP")
static BOOL SendJobReceipt (BOOL bPositive, JOB_QUEUE * lpJobQueue, LPCTSTR lpctstrAttachment);
static BOOL CheckForJobRetry (PJOB_QUEUE lpJobQueue);
static
DWORD
TranslateCanonicalNumber(
LPTSTR lptstrCanonicalFaxNumber,
DWORD dwDeviceID,
LPTSTR lptstrDialableAddress,
DWORD dwDialableAddressCount,
LPTSTR lptstrDisplayableAddress,
DWORD dwDisplayableAddressCount
);
static PJOB_ENTRY
StartLegacySendJob(
PJOB_QUEUE lpJobQueue,
PLINE_INFO lpLineInfo
);
static PJOB_ENTRY CreateJobEntry(PJOB_QUEUE lpJobQueue, LINE_INFO * lpLineInfo, BOOL bTranslateNumber);
BOOL FreeJobEntry(PJOB_ENTRY lpJobEntry , BOOL bDestroy);
static BOOL UpdatePerfCounters(const JOB_QUEUE * lpcJobQueue);
static BOOL
CreateCoverpageTiffFile(
IN short Resolution,
IN const FAX_COVERPAGE_INFOW2 *CoverpageInfo,
IN LPCWSTR lpcwstrExtension,
OUT LPWSTR lpwstrCovTiffFile,
IN DWORD dwCovTiffFileCount
);
static LPWSTR
GetFaxPrinterName(
VOID
);
DWORD BrandFax(LPCTSTR lpctstrFileName, LPCFSPI_BRAND_INFO pcBrandInfo)
{
#define MAX_BRANDING_LEN 115
#define BRANDING_HEIGHT 22 // in scan lines.
//
// We allocate fixed size arrays on the stack to avoid many small allocs on the heap.
//
LPTSTR lptstrBranding = NULL;
DWORD lenBranding =0;
TCHAR szBrandingEnd[MAX_BRANDING_LEN+1];
DWORD lenBrandingEnd = 0;
LPTSTR lptstrCallerNumberPlusCompanyName = NULL;
DWORD lenCallerNumberPlusCompanyName = 0;
DWORD delta =0 ;
DWORD ec = ERROR_SUCCESS;
LPTSTR lptstrDate = NULL;
LPTSTR lptstrTime = NULL;
LPTSTR lptstrDateTime = NULL;
int lenDate =0 ;
int lenTime =0;
LPDWORD MsgPtr[6];
HRESULT hr;
DWORD dwDateTimeLength = 0;
LPTSTR lptstrSenderTsid;
LPTSTR lptstrRecipientPhoneNumber;
LPTSTR lptstrSenderCompany;
DWORD dwSenderTsidLen;
DWORD dwSenderCompanyLen;
DEBUG_FUNCTION_NAME(TEXT("BrandFax"));
Assert(lpctstrFileName);
Assert(pcBrandInfo);
lptstrSenderTsid = pcBrandInfo->lptstrSenderTsid ? pcBrandInfo->lptstrSenderTsid : TEXT("");
lptstrRecipientPhoneNumber = pcBrandInfo->lptstrRecipientPhoneNumber ? pcBrandInfo->lptstrRecipientPhoneNumber : TEXT("");
lptstrSenderCompany = pcBrandInfo->lptstrSenderCompany ? pcBrandInfo->lptstrSenderCompany : TEXT("");
dwSenderTsidLen = lptstrSenderTsid ? _tcslen(lptstrSenderTsid) : 0;
dwSenderCompanyLen = lptstrSenderCompany ? _tcslen(lptstrSenderCompany) : 0;
lenDate = GetY2KCompliantDate(
LOCALE_SYSTEM_DEFAULT,
0,
&pcBrandInfo->tmDateTime,
NULL,
NULL);
if ( ! lenDate )
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("GetY2KCompliantDate() failed (ec: %ld)"),
ec
);
goto Error;
}
lptstrDate = (LPTSTR) MemAlloc(lenDate * sizeof(TCHAR)); // lenDate includes terminating NULL
if (!lptstrDate)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to allocate date buffer of size %ld (ec: %ld)"),
lenDate * sizeof(TCHAR),
ec);
goto Error;
}
if (!GetY2KCompliantDate(
LOCALE_SYSTEM_DEFAULT,
0,
&pcBrandInfo->tmDateTime,
lptstrDate,
lenDate))
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("GetY2KCompliantDate() failed (ec: %ld)"),
ec
);
goto Error;
}
lenTime = FaxTimeFormat( LOCALE_SYSTEM_DEFAULT,
TIME_NOSECONDS,
&pcBrandInfo->tmDateTime,
NULL,
NULL,
0 );
if ( !lenTime )
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("FaxTimeFormat() failed (ec: %ld)"),
ec
);
goto Error;
}
lptstrTime = (LPTSTR) MemAlloc(lenTime * sizeof(TCHAR)); // lenTime includes terminating NULL
if (!lptstrTime)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to allocate time buffer of size %ld (ec: %ld)"),
lenTime * sizeof(TCHAR),
ec);
goto Error;
}
if ( ! FaxTimeFormat(
LOCALE_SYSTEM_DEFAULT,
TIME_NOSECONDS,
&pcBrandInfo->tmDateTime,
NULL, // use locale format
lptstrTime,
lenTime) )
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("FaxTimeFormat() failed (ec: %ld)"),
ec
);
goto Error;
}
//
// Concatenate date and time
//
dwDateTimeLength = lenDate + lenTime; // should be enough, lenDate and lentime both include '\0', and we add only one ' ' between the date and time.
lptstrDateTime = (LPTSTR) MemAlloc (dwDateTimeLength * sizeof(TCHAR));
if (!lptstrDateTime)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to allocate DateTime buffer of size %ld (ec: %ld)"),
dwDateTimeLength,
ec);
goto Error;
}
hr = StringCchPrintf(lptstrDateTime,
dwDateTimeLength,
TEXT("%s %s"),
lptstrDate,
lptstrTime);
if (FAILED(hr))
{
//
// Should never happen, we just allocated large enough buffer.
//
ASSERT_FALSE;
}
//
// Create lpCallerNumberPlusCompanyName
//
if (lptstrSenderCompany)
{
DWORD dwCallerNumberPlusCompanyNameCount = dwSenderTsidLen + dwSenderCompanyLen +2; // we add 2 chars, 1 for '\0' and one for the ' '.
lptstrCallerNumberPlusCompanyName = (LPTSTR) MemAlloc( dwCallerNumberPlusCompanyNameCount * sizeof(TCHAR) );
if (!lptstrCallerNumberPlusCompanyName)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to allocate CallerNumberPlusCompanyName buffer of size %ld (ec: %ld)"),
dwCallerNumberPlusCompanyNameCount,
ec);
goto Error;
}
hr = StringCchPrintf(lptstrCallerNumberPlusCompanyName,
dwCallerNumberPlusCompanyNameCount,
TEXT("%s %s"),
lptstrSenderTsid,
lptstrSenderCompany);
if (FAILED(hr))
{
//
// Should never happen, we just allocated large enough buffer.
//
ASSERT_FALSE;
}
}
else
{
lptstrCallerNumberPlusCompanyName = (LPTSTR)
MemAlloc( (dwSenderTsidLen + 1) * sizeof(TCHAR));
if (!lptstrCallerNumberPlusCompanyName)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to allocate CallerNumberPlusCompanyName buffer of size %ld (ec: %ld)"),
(dwSenderTsidLen + 1) * sizeof(TCHAR),
ec);
goto Error;
}
hr = StringCchCopy(
lptstrCallerNumberPlusCompanyName,
dwSenderTsidLen + 1,
lptstrSenderTsid);
if (FAILED(hr))
{
//
// Should never happen, we just allocated large enough buffer.
//
ASSERT_FALSE;
}
}
//
// Try to create a banner of the following format:
// <szDateTime> FROM: <szCallerNumberPlusCompanyName> TO: <pcBrandInfo->lptstrRecipientPhoneNumber> PAGE: X OF Y
// If it does not fit we will start chopping it off.
//
MsgPtr[0] = (LPDWORD) lptstrDateTime;
MsgPtr[1] = (LPDWORD) lptstrCallerNumberPlusCompanyName;
MsgPtr[2] = (LPDWORD) lptstrRecipientPhoneNumber;
MsgPtr[3] = NULL;
lenBranding = FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
g_hResource,
MSG_BRANDING_FULL,
0,
(LPTSTR)&lptstrBranding,
0,
(va_list *) MsgPtr
);
if ( ! lenBranding )
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("FormatMessage of MSG_BRANDING_FULL failed (ec: %ld)"),
ec);
goto Error;
}
Assert(lptstrBranding);
lenBrandingEnd = FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE ,
g_hResource,
MSG_BRANDING_END,
0,
szBrandingEnd,
sizeof(szBrandingEnd)/sizeof(TCHAR),
NULL
);
if ( !lenBrandingEnd)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("FormatMessage of MSG_BRANDING_END failed (ec: %ld)"),
ec);
goto Error;
}
//
// Make sure we can fit everything.
//
if (lenBranding + lenBrandingEnd + 8 <= MAX_BRANDING_LEN)
{
//
// It fits. Proceed with branding.
//
goto lDoBranding;
}
//
// It did not fit. Try a message of the format:
// <lpDateTime> FROM: <lpCallerNumberPlusCompanyName> PAGE: X OF Y
// This skips the ReceiverNumber. The important part is the CallerNumberPlusCompanyName.
//
MsgPtr[0] = (LPDWORD) lptstrDateTime;
MsgPtr[1] = (LPDWORD) lptstrCallerNumberPlusCompanyName;
MsgPtr[2] = NULL;
//
// Free the previous attempt branding string
//
Assert(lptstrBranding);
LocalFree(lptstrBranding);
lptstrBranding = NULL;
lenBranding = FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
g_hResource,
MSG_BRANDING_SHORT,
0,
(LPTSTR)&lptstrBranding,
0,
(va_list *) MsgPtr
);
if ( !lenBranding )
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("FormatMessage() failed for MSG_BRANDING_SHORT (ec: %ld)"),
ec);
goto Error;
}
Assert(lptstrBranding);
if (lenBranding + lenBrandingEnd + 8 <= MAX_BRANDING_LEN) {
goto lDoBranding;
}
//
// It did not fit.
// We have to truncate the caller number so it fits.
// delta = how many chars of the company name we need to chop off.
//
delta = lenBranding + lenBrandingEnd + 8 - MAX_BRANDING_LEN;
lenCallerNumberPlusCompanyName = _tcslen (lptstrCallerNumberPlusCompanyName);
if (lenCallerNumberPlusCompanyName <= delta) {
DebugPrintEx(
DEBUG_ERR,
TEXT("Can not truncate CallerNumberPlusCompanyName to fit brand limit.")
TEXT(" Delta[%ld] >= lenCallerNumberPlusCompanyName[%ld]"),
delta,
lenCallerNumberPlusCompanyName);
ec = ERROR_BAD_FORMAT;
goto Error;
}
lptstrCallerNumberPlusCompanyName[ lenCallerNumberPlusCompanyName - delta] = TEXT('\0');
MsgPtr[0] = (LPDWORD) lptstrDateTime;
MsgPtr[1] = (LPDWORD) lptstrCallerNumberPlusCompanyName;
MsgPtr[2] = NULL;
//
// Free the previous attempt branding string
//
Assert(lptstrBranding);
LocalFree(lptstrBranding);
lptstrBranding = NULL;
lenBranding = FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
g_hResource,
MSG_BRANDING_SHORT,
0,
(LPTSTR)&lptstrBranding,
0,
(va_list *) MsgPtr
);
if ( !lenBranding )
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("FormatMessage() failed (ec: %ld). MSG_BRANDING_SHORT 2nd attempt"),
ec);
goto Error;
}
Assert(lptstrBranding);
//
// If it did noo fit now then we have a bug.
//
Assert(lenBranding + lenBrandingEnd + 8 <= MAX_BRANDING_LEN);
lDoBranding:
if (!MmrAddBranding(lpctstrFileName, lptstrBranding, szBrandingEnd, BRANDING_HEIGHT) )
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("MmrAddBranding() failed (ec: %ld)")
TEXT(" File: [%s]")
TEXT(" Branding: [%s]")
TEXT(" Branding End: [%s]")
TEXT(" Branding Height: [%d]"),
ec,
lpctstrFileName,
lptstrBranding,
szBrandingEnd,
BRANDING_HEIGHT);
goto Error;
}
Assert( ERROR_SUCCESS == ec);
goto Exit;
Error:
Assert (ERROR_SUCCESS != ec);
Exit:
if (lptstrBranding)
{
LocalFree(lptstrBranding);
lptstrBranding = NULL;
}
MemFree(lptstrDate);
lptstrDate = NULL;
MemFree(lptstrTime);
lptstrTime = NULL;
MemFree(lptstrDateTime);
lptstrDateTime = NULL;
MemFree(lptstrCallerNumberPlusCompanyName);
lptstrCallerNumberPlusCompanyName = NULL;
return ec;
}
HRESULT
WINAPI
FaxBrandDocument(
LPCTSTR lpctsrtFile,
LPCFSPI_BRAND_INFO lpcBrandInfo)
{
DEBUG_FUNCTION_NAME(TEXT("FaxBrandDocument"));
DWORD ec = ERROR_SUCCESS;
if (!lpctsrtFile)
{
DebugPrintEx(DEBUG_ERR,
TEXT("NULL target file name"));
ec = ERROR_INVALID_PARAMETER;
goto Error;
}
if (!lpcBrandInfo)
{
DebugPrintEx(DEBUG_ERR,
TEXT("NULL branding info"));
ec = ERROR_INVALID_PARAMETER;
goto Error;
}
if (lpcBrandInfo->dwSizeOfStruct != sizeof(FSPI_BRAND_INFO))
{
DebugPrintEx(DEBUG_ERR,
TEXT("Bad cover page info parameter, dwSizeOfStruct = %d"),
lpcBrandInfo->dwSizeOfStruct);
ec = ERROR_INVALID_PARAMETER;
goto Error;
}
ec = BrandFax(lpctsrtFile, lpcBrandInfo);
if (ERROR_SUCCESS != ec)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("BrandFax() for file %s has failed (ec: %ld)"),
lpctsrtFile,
ec);
goto Error;
}
Assert (ERROR_SUCCESS == ec);
goto Exit;
Error:
Assert (ERROR_SUCCESS != ec);
Exit:
return HRESULT_FROM_WIN32(ec);
}
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( &g_CsJob );
Next = g_JobListHead.Flink;
if (Next == NULL) {
LeaveCriticalSection( &g_CsJob );
return NULL;
}
while ((ULONG_PTR)Next != (ULONG_PTR)&g_JobListHead) {
JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
if ((ULONG_PTR)JobEntry->InstanceData == (ULONG_PTR)FaxHandle) {
LeaveCriticalSection( &g_CsJob );
return JobEntry;
}
Next = JobEntry->ListEntry.Flink;
}
LeaveCriticalSection( &g_CsJob );
return NULL;
}
BOOL
FindJobByJob(
IN PJOB_ENTRY JobEntryToFind
)
/*++
Routine Description:
This fuction check whether a FAX job exist in g_JobListHead (Job's list)
Arguments:
JobEntryToFind - PJOB_ENTRY from StartJob()
Return Value:
TRUE - if the job was found
FALSE - otherwise
--*/
{
PLIST_ENTRY Next;
PJOB_ENTRY JobEntry;
Assert(JobEntryToFind);
EnterCriticalSection( &g_CsJob );
Next = g_JobListHead.Flink;
if (Next == NULL) {
LeaveCriticalSection( &g_CsJob );
return FALSE;
}
while ((ULONG_PTR)Next != (ULONG_PTR)&g_JobListHead) {
JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
if (JobEntry == JobEntryToFind) {
LeaveCriticalSection( &g_CsJob );
return TRUE;
}
Next = JobEntry->ListEntry.Flink;
}
LeaveCriticalSection( &g_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 = NULL;
BOOL bRes = FALSE;
EnterCriticalSection(&g_CsJob);
JobEntry = FindJob( FaxHandle );
if (JobEntry)
{
if (NULL == JobEntry->CallHandle)
{
JobEntry->CallHandle = CallHandle;
}
bRes = (JobEntry->CallHandle == CallHandle) ? TRUE : FALSE;
}
LeaveCriticalSection(&g_CsJob);
if (FALSE == bRes)
{
SetLastError(ERROR_INVALID_PARAMETER);
}
return bRes;
}
//*********************************************************************************
//* Name: CreateCoverpageTiffFileEx()
//* Author: Ronen Barenboim
//* Date: March 24, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Generates cover page TIFF file from the specified cover page template
//* and new Client API parameters.
//* The function returns the name of the generated file.
//* PARAMETERS:
//* Resolution [IN]
//*
//* dwPageCount [IN]
//*
//* lpcCoverpageEx [IN]
//*
//* lpcRecipient [IN]
//*
//* lpcSender [IN]
//*
//* lpcwstrExtension [IN] - File extension (optional).
//*
//* lpwstrCovTiffFile [OUT]
//* A pointer to Unicode string buffer where the function will place
//* the full path to the generated cover page TIFF file.
//*
//* dwCovTiffFileCount [IN] - size of the buffer pointed by lpwstrCovTiffFile.
//*
//* RETURN VALUE:
//* TRUE
//* If the operation succeeded.
//* FALSE
//* Otherwise. Use GetLastError() to figure out why it failed.
//*
//* REMARKS:
//* The function does not allocate any memory.
//*********************************************************************************
BOOL
CreateCoverpageTiffFileEx(
IN short Resolution,
IN DWORD dwPageCount,
IN LPCFAX_COVERPAGE_INFO_EXW lpcCoverpageEx,
IN LPCFAX_PERSONAL_PROFILEW lpcRecipient,
IN LPCFAX_PERSONAL_PROFILEW lpcSender,
IN LPCWSTR lpcwstrExtension,
OUT LPWSTR lpwstrCovTiffFile,
IN DWORD dwCovTiffFileCount)
{
FAX_COVERPAGE_INFOW2 covLegacy;
BOOL bRes = TRUE;
DEBUG_FUNCTION_NAME(TEXT("CreateCoverpageTiffFileEx"));
Assert(lpcCoverpageEx);
Assert(lpcRecipient);
Assert(lpcSender);
Assert(lpwstrCovTiffFile);
//
// Prepare a legacy FAX_COVERPAGE_INFO from the new cover page info
//
memset(&covLegacy,0,sizeof(covLegacy));
covLegacy.SizeOfStruct=sizeof(covLegacy);
covLegacy.CoverPageName=lpcCoverpageEx->lptstrCoverPageFileName;
covLegacy.UseServerCoverPage=lpcCoverpageEx->bServerBased;
covLegacy.RecCity=lpcRecipient->lptstrCity;
covLegacy.RecCompany=lpcRecipient->lptstrCompany;
covLegacy.RecCountry=lpcRecipient->lptstrCountry;
covLegacy.RecDepartment=lpcRecipient->lptstrDepartment;
covLegacy.RecFaxNumber=lpcRecipient->lptstrFaxNumber;
covLegacy.RecHomePhone=lpcRecipient->lptstrHomePhone;
covLegacy.RecName=lpcRecipient->lptstrName;
covLegacy.RecOfficeLocation=lpcRecipient->lptstrOfficeLocation;
covLegacy.RecOfficePhone=lpcRecipient->lptstrOfficePhone;
covLegacy.RecState=lpcRecipient->lptstrState;
covLegacy.RecStreetAddress=lpcRecipient->lptstrStreetAddress;
covLegacy.RecTitle=lpcRecipient->lptstrTitle;
covLegacy.RecZip=lpcRecipient->lptstrZip;
covLegacy.SdrName=lpcSender->lptstrName;
covLegacy.SdrFaxNumber=lpcSender->lptstrFaxNumber;
covLegacy.SdrCompany=lpcSender->lptstrCompany;
covLegacy.SdrTitle=lpcSender->lptstrTitle;
covLegacy.SdrDepartment=lpcSender->lptstrDepartment;
covLegacy.SdrOfficeLocation=lpcSender->lptstrOfficeLocation;
covLegacy.SdrHomePhone=lpcSender->lptstrHomePhone;
covLegacy.SdrAddress=lpcSender->lptstrStreetAddress;
covLegacy.SdrOfficePhone=lpcSender->lptstrOfficePhone;
covLegacy.SdrEmail=lpcSender->lptstrEmail;
covLegacy.Note=lpcCoverpageEx->lptstrNote;
covLegacy.Subject=lpcCoverpageEx->lptstrSubject;
covLegacy.PageCount=dwPageCount;
//
// Note covLegacy.TimeSent is not set. This field's value is
// generated by FaxPrintCoverPageW().
//
//
// Now call the legacy CreateCoverPageTiffFile() to generate the cover page file
//
if (!CreateCoverpageTiffFile(Resolution, &covLegacy, lpcwstrExtension, lpwstrCovTiffFile, dwCovTiffFileCount))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to generate cover page file for recipient %s@%s. (ec: %ld)"),
lpcRecipient->lptstrName,
lpcRecipient->lptstrFaxNumber,
GetLastError()
);
bRes = FALSE;
}
return bRes;
}
LPWSTR
GetFaxPrinterName(
VOID
)
{
PPRINTER_INFO_2 PrinterInfo;
DWORD i;
DWORD Count;
PrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters( NULL, 2, &Count, 0 );
if (PrinterInfo == NULL)
{
if (ERROR_SUCCESS == GetLastError())
{
//
// No printers are installed
//
SetLastError(ERROR_INVALID_PRINTER_NAME);
}
return NULL;
}
for (i=0; i<Count; i++)
{
if (_wcsicmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0 &&
_wcsicmp( PrinterInfo[i].pPortName, FAX_PORT_NAME ) == 0)
{
LPWSTR p = (LPWSTR) StringDup( PrinterInfo[i].pPrinterName );
MemFree( PrinterInfo );
if (NULL == p )
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return p;
}
}
MemFree( PrinterInfo );
SetLastError (ERROR_INVALID_PRINTER_NAME);
return NULL;
}
VOID
FreeCpFields(
PCOVERPAGEFIELDS pCpFields
)
/*++
Routine Description:
Frees all memory associated with a coverpage field structure.
Arguments:
CpFields - Pointer to a coverpage field structure.
Return Value:
None.
--*/
{
DWORD i;
LPTSTR* lpptstrString;
for (i = 0; i < NUM_INSERTION_TAGS; i++)
{
lpptstrString = (LPTSTR*) ((LPBYTE)(&(pCpFields->RecName)) + (i * sizeof(LPTSTR)));
MemFree (*lpptstrString) ;
}
}
BOOL
FillCoverPageFields(
IN const FAX_COVERPAGE_INFOW2* pFaxCovInfo,
OUT PCOVERPAGEFIELDS pCPFields)
/*++
Author:
Oded Sacher 27-June-2001
Routine Description:
Fills a COVERPAGEFIELDS structure from the content of a FAX_COVERPAGE_INFO structure.
Used to prepare a COVERPAGEFIELDS structure for cover page rendering before rendering cover page.
Arguments:
[IN] pFaxCovInfo - Pointer to a FAX_COVERPAGE_INFO that holds the information to be extracted.
[OUT] pCPFields - Pointer to a COVERPAGEFIELDS structure that gets filled with
the information from FAX_COVERPAGE_INFO.
Return Value:
BOOL
Comments:
The function allocates memory.
Call FreeCoverPageFields to free resources.
--*/
{
DWORD dwDateTimeLen;
DWORD cch;
LPTSTR s;
DWORD ec = 0;
LPCTSTR *src;
LPCTSTR *dst;
DWORD i;
TCHAR szTimeBuffer[MAX_PATH] = {0};
TCHAR szNumberOfPages[12] = {0};
Assert(pFaxCovInfo);
Assert(pCPFields);
memset(pCPFields,0,sizeof(COVERPAGEFIELDS));
pCPFields->ThisStructSize = sizeof(COVERPAGEFIELDS);
pCPFields->RecName = StringDup(pFaxCovInfo->RecName);
pCPFields->RecFaxNumber = StringDup(pFaxCovInfo->RecFaxNumber);
pCPFields->Subject = StringDup(pFaxCovInfo->Subject);
pCPFields->Note = StringDup(pFaxCovInfo->Note);
pCPFields->NumberOfPages = StringDup(_itot( pFaxCovInfo->PageCount, szNumberOfPages, 10 ));
for (i = 0;
i <= ((LPBYTE)&pFaxCovInfo->SdrEmail - (LPBYTE)&pFaxCovInfo->RecCompany)/sizeof(LPCTSTR);
i++)
{
src = (LPCTSTR *) ((LPBYTE)(&pFaxCovInfo->RecCompany) + (i*sizeof(LPCTSTR)));
dst = (LPCTSTR *) ((LPBYTE)(&(pCPFields->RecCompany)) + (i*sizeof(LPCTSTR)));
if (*dst)
{
MemFree ( (LPBYTE) *dst ) ;
}
*dst = (LPCTSTR) StringDup( *src );
}
//
// the time the fax was sent
//
GetLocalTime((LPSYSTEMTIME)&pFaxCovInfo->TimeSent);
//
// dwDataTimeLen is the size of s in characters
//
dwDateTimeLen = ARR_SIZE(szTimeBuffer);
s = szTimeBuffer;
//
// Get date into s
//
GetY2KCompliantDate( LOCALE_USER_DEFAULT, 0, &pFaxCovInfo->TimeSent, s, dwDateTimeLen );
//
// Advance s past the date string and attempt to append time
//
cch = _tcslen( s );
s += cch;
if (++cch < dwDateTimeLen)
{
*s++ = ' ';
//
// DateTimeLen is the decreased by the size of s in characters
//
dwDateTimeLen -= cch;
//
// Get the time here
//
FaxTimeFormat( LOCALE_USER_DEFAULT, 0, &pFaxCovInfo->TimeSent, NULL, s, dwDateTimeLen );
}
pCPFields->TimeSent = StringDup( szTimeBuffer );
return TRUE;
}
//*****************************************************************************
//* Name: CreateCoverpageTiffFile
//* Author:
//*****************************************************************************
//* DESCRIPTION:
//* Renders the specified coverpage into a temp TIFF file and returns the name
//* of the temp TIFF file.
//* PARAMETERS:
//* [IN] IN short Resolution:
//* 196 for 200x200 resolution.
//* 98 for 200x100 resolution.
//* [IN] FAX_COVERPAGE_INFOW *CoverpageInfo:
//* A pointer to a FAX_COVERPAGE_INFOW structure that contains the cover page
//* template information (see SDK help).
//* [IM] LPCWSTR lpcwstrExtension - File extension (".TIF" if NULL)
//*
//* [OUT] LPWSTR lpwstrCovTiffFile:
//* A pointer to a buffer where the function returns the name of the temp file
//* that contains the rendered cover page TIFF file.
//*
//* [IN] DWORD dwCovTiffFileCount:
//* Size in TCHARs of the buffer pointed by lpwstrCovTiffFile.
//* RETURN VALUE:
//* FALSE if the operation failed.
//* TRUE is succeeded.
//* Comments:
//* If the operation failes the function takes care of deleting any temp files.
//*****************************************************************************
BOOL
CreateCoverpageTiffFile(
IN short Resolution,
IN const FAX_COVERPAGE_INFOW2 *CoverpageInfo,
IN LPCWSTR lpcwstrExtension,
OUT LPWSTR lpwstrCovTiffFile,
IN DWORD dwCovTiffFileCount
)
{
WCHAR TempFile[MAX_PATH];
WCHAR wszCpName[MAX_PATH];
LPWSTR FaxPrinter = NULL;
BOOL Rslt = TRUE;
COVDOCINFO covDocInfo;
short Orientation = DMORIENT_PORTRAIT;
DWORD ec = ERROR_SUCCESS;
COVERPAGEFIELDS CpFields = {0};
HRESULT hr;
DEBUG_FUNCTION_NAME(TEXT("CreateCoverpageTiffFile()"));
LPCWSTR lpcwstrFileExt = lpcwstrExtension ? lpcwstrExtension : FAX_TIF_FILE_EXT;
TempFile[0] = L'\0';
//
// Validate the cover page and resolve the full path
//
if (!ValidateCoverpage((LPWSTR)CoverpageInfo->CoverPageName,
NULL,
CoverpageInfo->UseServerCoverPage,
wszCpName,
ARR_SIZE(wszCpName)))
{
ec = GetLastError();
DebugPrintEx(DEBUG_ERR,
TEXT("ValidateCoverpage failed. ec = %ld"),
ec);
Rslt=FALSE;
goto Exit;
}
//
// Collect the cover page fields
//
FillCoverPageFields( CoverpageInfo, &CpFields);
FaxPrinter = GetFaxPrinterName();
if (FaxPrinter == NULL)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("GetFaxPrinterName failed. ec = %ld"),
ec);
Rslt=FALSE;
goto Exit;
}
//
// Get the cover page orientation
//
ec = PrintCoverPage(NULL, NULL, wszCpName, &covDocInfo);
if (ERROR_SUCCESS != ec)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PrintCoverPage for coverpage %s failed (ec: %ld)"),
CoverpageInfo->CoverPageName,
ec);
Rslt=FALSE;
goto Exit;
}
if (!GenerateUniqueFileName( g_wszFaxQueueDir, (LPWSTR)lpcwstrFileExt, TempFile, sizeof(TempFile)/sizeof(WCHAR) ))
{
ec = GetLastError();
DebugPrintEx(DEBUG_ERR,TEXT("Failed to generate unique file name for merged TIFF file (ec: %ld)."), ec);
Rslt=FALSE;
goto Exit;
}
//
// Change the default orientation if needed
//
if (covDocInfo.Orientation == DMORIENT_LANDSCAPE)
{
Orientation = DMORIENT_LANDSCAPE;
}
//
// Render the cover page to a file
//
ec = PrintCoverPageToFile(
wszCpName,
TempFile,
FaxPrinter,
Orientation,
Resolution,
&CpFields);
if (ERROR_SUCCESS != ec)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PrintCoverPageToFile for coverpage %s failed (ec: %ld)"),
CoverpageInfo->CoverPageName,
ec);
Rslt=FALSE;
if (!DeleteFile( TempFile ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DeleteFile for file %s failed (ec: %ld)"),
TempFile,
GetLastError());
}
goto Exit;
}
hr = StringCchCopy(
lpwstrCovTiffFile,
dwCovTiffFileCount,
TempFile);
if (FAILED(hr))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("StringCchCopy for coverpage %s failed (ec: %ld)"),
CoverpageInfo->CoverPageName,
hr);
Rslt=FALSE;
ec = HRESULT_CODE(hr);
if (!DeleteFile( TempFile ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DeleteFile for file %s failed (ec: %ld)"),
TempFile,
GetLastError());
}
goto Exit;
}
Rslt = TRUE;
Exit:
MemFree(FaxPrinter);
FreeCpFields(&CpFields);
if (FALSE == Rslt)
{
ec = (ERROR_SUCCESS != ec) ? ec : ERROR_GEN_FAILURE;
SetLastError(ec);
}
return Rslt;
}
//*****************************************************************************
//* Name: GetBodyTiffResolution
//* Author:
//*****************************************************************************
//* DESCRIPTION:
//* Returns the body tiff file resolution. (200x200 or 200x100)
//* The resolution is determined by the first page only!!
//* PARAMETERS:
//*
//* [IN] LPCWSTR lpcwstrBodyFile - Body tiff file
//*
//* [OUT] short* pResolution:
//* A pointer to a short where the function returns the tiff resolution.
//* TRUE is 200x200. FALSE is 200x100
//* RETURN VALUE:
//* FALSE if the operation failed.
//* TRUE is succeeded.
//* Comments:
//*****************************************************************************
BOOL
GetBodyTiffResolution(
IN LPCWSTR lpcwstrBodyFile,
OUT short* pResolution
)
{
DEBUG_FUNCTION_NAME(TEXT("GetBodyTiffResolution"));
TIFF_INFO TiffInfo;
HANDLE hTiff = NULL;
BOOL RetVal = TRUE;
Assert (lpcwstrBodyFile && pResolution);
//
// open the tiff file
//
hTiff = TiffOpen( lpcwstrBodyFile, &TiffInfo, TRUE, FILLORDER_MSB2LSB );
if (hTiff == NULL)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("TiffOpen() failed. Tiff file: %s"),
lpcwstrBodyFile);
RetVal = FALSE;
goto exit;
}
if (TiffInfo.YResolution != 98 &&
TiffInfo.YResolution != 196)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Invalid Tiff Resolutoin. Tiff file: %s, YRes: %ld."),
lpcwstrBodyFile,
TiffInfo.YResolution);
RetVal = FALSE;
goto exit;
}
*pResolution = TiffInfo.YResolution;
Assert (TRUE == RetVal);
exit:
if (NULL != hTiff)
{
if (!TiffClose(hTiff))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("TiffClose() failed. Tiff file: %s"),
lpcwstrBodyFile);
}
}
return RetVal;
}
//*********************************************************************************
//* Name: CreateTiffFile ()
//* Author: Ronen Barenboim
//* Date: March 24, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Creates the TIFF file for a job queue.
//*
//* The function deals with generating the cover page file and merging it
//* with the body file (if a body exists).
//* It returns the name of the TIFF file it generated. The caller must delete
//* this file when it is no longer needed.
//* PARAMETERS:
//* PJOB_QUEUE lpJob
//* A pointer to a JOB_QUEUE structure that holds the recipient or routing job
//* information.
//* LPCWSTR lpcwstrFileExt - The new file extension (Null will create the default "*.TIF"
//*
//* LPWSTR lpwstrFullPath - Pointer to a buffer to receive the full path to the new file
//*
//* DWORD dwFullPathCount - size in TCHARs of the buffer pointed by lpwstrFullPath.
//*
//* RETURN VALUE:
//* TRUE if successful.
//* FALSE otherwise. Set last erorr on failure
//*********************************************************************************
BOOL
CreateTiffFile (
PJOB_QUEUE lpJob,
LPCWSTR lpcwstrFileExt,
LPWSTR lpwstrFullPath,
DWORD dwFullPathCount
)
{
DEBUG_FUNCTION_NAME(TEXT("CreateTiffFile"));
Assert(lpJob && lpwstrFullPath);
Assert(JT_SEND == lpJob->JobType ||
JT_ROUTING == lpJob->JobType);
PJOB_QUEUE lpParentJob = NULL;
WCHAR szCoverPageTiffFile[MAX_PATH] = {0};
LPCWSTR lpcwstrCoverPageFileName;
LPCWSTR lpcwstrBodyFileName;
short Resolution = 0; // Default resolution
BOOL bRes = FALSE;
HRESULT hr;
if (JT_SEND == lpJob->JobType)
{
lpParentJob = lpJob->lpParentJob;
Assert(lpParentJob);
}
lpcwstrCoverPageFileName = lpParentJob ? lpParentJob->CoverPageEx.lptstrCoverPageFileName : NULL;
lpcwstrBodyFileName = lpParentJob ? lpParentJob->FileName : lpJob->FileName;
if (!lpcwstrCoverPageFileName)
{
//
// No cover page specified.
// The TIFF to send is the body only.
// Copy the body for each recipient
//
Assert(lpcwstrBodyFileName); // must have a body in this case.
LPCWSTR lpcwstrExt = lpcwstrFileExt ? lpcwstrFileExt : FAX_TIF_FILE_EXT;
if (!GenerateUniqueFileName( g_wszFaxQueueDir,
(LPWSTR)lpcwstrExt,
szCoverPageTiffFile,
sizeof(szCoverPageTiffFile)/sizeof(WCHAR) ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("GenerateUniqueFileName() failed (ec: %ld)."),
GetLastError());
goto Exit;
}
if (!CopyFile (lpcwstrBodyFileName, szCoverPageTiffFile, FALSE)) // FALSE - File already exist
{
DebugPrintEx(DEBUG_ERR,
TEXT("CopyFile Failed with %ld "),
GetLastError());
DeleteFile(szCoverPageTiffFile);
goto Exit;
}
hr = StringCchCopy(
lpwstrFullPath,
dwFullPathCount,
szCoverPageTiffFile);
if (FAILED(hr))
{
DebugPrintEx(DEBUG_ERR,
TEXT("StringCchCopy Failed with %ld "),
hr);
DeleteFile(szCoverPageTiffFile);
SetLastError(HRESULT_CODE(hr));
}
else
{
bRes = TRUE;
}
goto Exit;
}
//
// There is a cover page so the tiff is either just the cover page or the cover page
// merged with the body.
//
if (lpParentJob->FileName)
{
if (!GetBodyTiffResolution(lpParentJob->FileName, &Resolution))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("GetBodyTiffResolution() failed (ec: %ld)."),
GetLastError());
goto Exit;
}
}
Assert (Resolution == 0 || Resolution == 98 || Resolution == 196);
//
// First create the cover page (This generates a file and returns its name).
//
if (!CreateCoverpageTiffFileEx(
Resolution,
lpJob->PageCount,
&lpParentJob->CoverPageEx,
&lpJob->RecipientProfile,
&lpParentJob->SenderProfile,
lpcwstrFileExt,
szCoverPageTiffFile,
ARR_SIZE(szCoverPageTiffFile)))
{
DebugPrintEx(DEBUG_ERR,
TEXT("[JobId: %ld] Failed to render cover page template %s"),
lpJob->JobId,
lpParentJob->CoverPageEx.lptstrCoverPageFileName);
goto Exit;
}
if (lpParentJob->FileName)
{
//
// There is a body file specified so merge the body and the cover page into
// the file specified in szCoverPageTiffFile.
//
if (!MergeTiffFiles( szCoverPageTiffFile, lpParentJob->FileName))
{
DebugPrintEx(DEBUG_ERR,
TEXT("[JobId: %ld] Failed to merge cover (%ws) and body (%ws). (ec: %ld)"),
lpJob->JobId,
szCoverPageTiffFile,
lpParentJob->FileName,
GetLastError());
//
// Get rid of the coverpage TIFF we generated.
//
if (!DeleteFile(szCoverPageTiffFile))
{
DebugPrintEx(DEBUG_ERR,
TEXT("[JobId: %ld] Failed to delete cover page TIFF file %ws. (ec: %ld)"),
lpJob->JobId,
szCoverPageTiffFile,
GetLastError());
}
goto Exit;
}
}
hr = StringCchCopy(
lpwstrFullPath,
dwFullPathCount,
szCoverPageTiffFile);
if (FAILED(hr))
{
DebugPrintEx(DEBUG_ERR,
TEXT("StringCchCopy Failed with %ld "),
hr);
DeleteFile(szCoverPageTiffFile);
SetLastError(HRESULT_CODE(hr));
goto Exit;
}
bRes = TRUE;
Exit:
if (FALSE == bRes)
{
//
// Make sure we set last error
//
if (ERROR_SUCCESS == GetLastError())
{
SetLastError (ERROR_GEN_FAILURE);
}
}
return bRes;
} // CreateTiffFile
BOOL
CreateTiffFileForJob (
PJOB_QUEUE lpRecpJob
)
{
DEBUG_FUNCTION_NAME(TEXT("CreateTiffFileForJob"));
WCHAR wszFullPath[MAX_PATH] = {0};
Assert(lpRecpJob);
if (!CreateTiffFile (lpRecpJob, TEXT("FRT"), wszFullPath, ARR_SIZE(wszFullPath)))
{
DebugPrintEx(DEBUG_ERR,
TEXT("CreateTiffFile failed. (ec: %ld)"),
GetLastError());
return FALSE;
}
if (NULL == (lpRecpJob->FileName = StringDup(wszFullPath)))
{
DWORD dwErr = GetLastError();
DebugPrintEx(DEBUG_ERR,
TEXT("StringDup failed. (ec: %ld)"),
dwErr);
if (!DeleteFile(wszFullPath))
{
DebugPrintEx(DEBUG_ERR,
TEXT("[JobId: %ld] Failed to delete TIFF file %ws. (ec: %ld)"),
lpRecpJob->JobId,
wszFullPath,
GetLastError());
}
SetLastError(dwErr);
return FALSE;
}
return TRUE;
}
BOOL
CreateTiffFileForPreview (
PJOB_QUEUE lpRecpJob
)
{
DEBUG_FUNCTION_NAME(TEXT("CreateTiffFileForPreview"));
WCHAR wszFullPath[MAX_PATH] = {0};
Assert(lpRecpJob);
if (lpRecpJob->PreviewFileName)
{
return TRUE;
}
if (!CreateTiffFile (lpRecpJob, TEXT("PRV"), wszFullPath, ARR_SIZE(wszFullPath)))
{
DebugPrintEx(DEBUG_ERR,
TEXT("CreateTiffFile failed. (ec: %ld)"),
GetLastError());
return FALSE;
}
if (NULL == (lpRecpJob->PreviewFileName = StringDup(wszFullPath)))
{
DWORD dwErr = GetLastError();
DebugPrintEx(DEBUG_ERR,
TEXT("StringDup failed. (ec: %ld)"),
dwErr);
if (!DeleteFile(wszFullPath))
{
DebugPrintEx(DEBUG_ERR,
TEXT("[JobId: %ld] Failed to delete TIFF file %ws. (ec: %ld)"),
lpRecpJob->JobId,
wszFullPath,
GetLastError());
}
SetLastError(dwErr);
return FALSE;
}
return TRUE;
}
DWORD
FaxRouteThread(
PJOB_QUEUE lpJobQueueEntry
)
/*++
Routine Description:
This fuction runs asychronously as a separate thread to
route an incoming job.
Arguments:
lpJobQueueEntry - A pointer to the job for which the routing
operation is to be performed.
Return Value:
Always zero.
--*/
{
BOOL Routed = TRUE;
DWORD i;
DWORD dwRes;
DWORD CountFailureInfo = 0;
DEBUG_FUNCTION_NAME(TEXT("FaxRouteThread"));
EnterCriticalSectionJobAndQueue;
CountFailureInfo = lpJobQueueEntry->CountFailureInfo;
LeaveCriticalSectionJobAndQueue;
for (i = 0; i < lpJobQueueEntry->CountFailureInfo; i++)
{
BOOL fRouteSucceed;
fRouteSucceed = FaxRouteRetry( lpJobQueueEntry->FaxRoute, &lpJobQueueEntry->pRouteFailureInfo[i] );
if (FALSE == fRouteSucceed)
{
PROUTING_METHOD pRoutingMethod = FindRoutingMethodByGuid( (lpJobQueueEntry->pRouteFailureInfo[i]).GuidString );
if (pRoutingMethod)
{
WCHAR TmpStr[20] = {0};
HRESULT hr = StringCchPrintf(
TmpStr,
ARR_SIZE(TmpStr),
TEXT("0x%016I64x"),
lpJobQueueEntry->UniqueId);
if (FAILED(hr))
{
//
// Should never happen, we use large enough buffer.
//
ASSERT_FALSE;
}
FaxLog(FAXLOG_CATEGORY_INBOUND,
FAXLOG_LEVEL_MIN,
6,
MSG_FAX_ROUTE_METHOD_FAILED,
TmpStr,
lpJobQueueEntry->FaxRoute->DeviceName,
lpJobQueueEntry->FaxRoute->Tsid,
lpJobQueueEntry->FileName,
pRoutingMethod->RoutingExtension->FriendlyName,
pRoutingMethod->FriendlyName
);
}
}
Routed &= fRouteSucceed;
}
EnterCriticalSectionJobAndQueue;
lpJobQueueEntry->dwLastJobExtendedStatus = 0;
lpJobQueueEntry->ExStatusString[0] = TEXT('\0');
if ( Routed )
{
lpJobQueueEntry->JobStatus = JS_DELETING;
DecreaseJobRefCount (lpJobQueueEntry, TRUE);
}
else
{
//
// We failed to execute the routing method.
// reschedule the job.
//
DWORD dwMaxRetries;
EnterCriticalSection (&g_CsConfig);
dwMaxRetries = g_dwFaxSendRetries;
LeaveCriticalSection (&g_CsConfig);
lpJobQueueEntry->SendRetries++;
if (lpJobQueueEntry->SendRetries <= dwMaxRetries)
{
lpJobQueueEntry->JobStatus = JS_RETRYING;
RescheduleJobQueueEntry( lpJobQueueEntry );
}
else
{
//
// retries exceeded, mark job as expired
//
MarkJobAsExpired(lpJobQueueEntry);
WCHAR TmpStr[20] = {0};
HRESULT hr = StringCchPrintf(
TmpStr,
ARR_SIZE(TmpStr),
TEXT("0x%016I64x"),
lpJobQueueEntry->UniqueId);
if (FAILED(hr))
{
//
// Should never happen, we use large enough buffer.
//
ASSERT_FALSE;
}
FaxLog(FAXLOG_CATEGORY_INBOUND,
FAXLOG_LEVEL_MIN,
3,
MSG_FAX_ROUTE_FAILED,
TmpStr,
lpJobQueueEntry->FaxRoute->DeviceName,
lpJobQueueEntry->FaxRoute->Tsid
);
}
//
// Create Fax EventEx
//
dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
lpJobQueueEntry
);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx( DEBUG_ERR,
_T("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) ")
_T("failed for job id %ld (ec: %lc)"),
lpJobQueueEntry->UniqueId,
dwRes);
}
if (!UpdatePersistentJobStatus(lpJobQueueEntry))
{
DebugPrintEx( DEBUG_ERR,
_T("Failed to update persistent job status to 0x%08x"),
lpJobQueueEntry->JobStatus);
}
}
LeaveCriticalSectionJobAndQueue;
if (!DecreaseServiceThreadsCount())
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
GetLastError());
}
return ERROR_SUCCESS;
}
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; // This structure is passed to FaxDevSend()
BOOL Rslt = FALSE;
BOOL Retrying = FALSE;
BOOL bFakeJobStatus = FALSE;
FSPI_JOB_STATUS FakedJobStatus = {0};
DWORD PageCount = 0;
BOOL bRemoveParentJob = FALSE; // TRUE if at the end of the send the parent job and all
// recipients need to be removed.
PJOB_QUEUE lpJobQueue = NULL ; // Points to the Queue entry attached to the running job.
LPFSPI_JOB_STATUS lpFSPStatus = NULL;
LPFSPI_JOB_STATUS pOrigFaxStatus = NULL;
DWORD dwSttRes;
BOOL bBranding;
DWORD dwJobId;
BOOL bCreateTiffFailed = FALSE;
BOOL fSetSystemIdleTimer = TRUE;
DEBUG_FUNCTION_NAME(TEXT("FaxSendThread"));
Assert (FaxSendItem &&
FaxSendItem->JobEntry &&
FaxSendItem->JobEntry->LineInfo &&
FaxSendItem->JobEntry->LineInfo->Provider);
//
// Don't let the system go to sleep in the middle of the fax transmission.
//
if (NULL == SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS))
{
fSetSystemIdleTimer = FALSE;
DebugPrintEx(DEBUG_ERR,
TEXT("SetThreadExecutionState() failed"));
}
lpJobQueue=FaxSendItem->JobEntry->lpJobQueueEntry;
Assert(lpJobQueue);
//
// Set the information to be sent to FaxDevSend()
// Note:
// The caller number is the sender TSID ! (we have no other indication of the sender phone number)
// This means that the FSP will get the sender TSID which might contain text as well (not just a number)
//
FaxSend.SizeOfStruct = sizeof(FAX_SEND);
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;
//
// Successfully created a new send job on a device. Update counter.
//
(VOID)UpdateDeviceJobsCounter ( FaxSendItem->JobEntry->LineInfo, // Device to update
TRUE, // Sending
1, // Number of new jobs
TRUE); // Enable events
if (!lpJobQueue->FileName)
{
//
// We did not generate a body for this recipient yet. This is the
// time to do so.
//
//
// Set the right body for this job.
// This is either the body specified at the parent or a merge of the body
// with the cover page specified in the parent.
//
DebugPrintEx(
DEBUG_MSG,
TEXT("[JobId: %ld] Generating body for recipient job."),
lpJobQueue->JobId
);
if (!CreateTiffFileForJob(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] CreateTiffFileForJob failed. (ec: %ld)"),
lpJobQueue->JobId,
GetLastError()
);
bCreateTiffFailed = TRUE;
}
}
else
{
//
// We already generated a body for this recipient.
// somthing is wrong
//
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] Using cached body in %s."),
lpJobQueue->JobId,
lpJobQueue->FileName
);
Assert(FALSE);
}
if (bCreateTiffFailed ||
NULL == (FaxSendItem->FileName = StringDup(lpJobQueue->FileName)))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] CreateTiffFileForJob or StringDup failed"),
FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
GetLastError());
//
// Simulate an FSP returning a FS_FATAL_ERROR code.
//
EnterCriticalSection(&g_CsJob);
FreeFSPIJobStatus(&FaxSendItem->JobEntry->FSPIJobStatus, FALSE);
FaxSendItem->JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_FAILED;
FaxSendItem->JobEntry->FSPIJobStatus.dwExtendedStatus = FSPI_ES_FATAL_ERROR;
if (!HandleFailedSendJob(FaxSendItem->JobEntry))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] HandleFailedSendJob() failed (ec: %ld)."),
FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
GetLastError());
}
LeaveCriticalSection(&g_CsJob);
goto Exit;
}
FaxSend.FileName = FaxSendItem->FileName;
//
// Add branding banner (the line at the top of each page) to the fax if necessary.
//
//
// Our service takes care of branding so notify FSP not to brand
//
FaxSend.Branding = FALSE;
EnterCriticalSection (&g_CsConfig);
bBranding = g_fFaxUseBranding;
LeaveCriticalSection (&g_CsConfig);
if (bBranding)
{
FSPI_BRAND_INFO brandInfo;
HRESULT hr;
memset(&brandInfo,0,sizeof(FSPI_BRAND_INFO));
brandInfo.dwSizeOfStruct=sizeof(FSPI_BRAND_INFO);
brandInfo.lptstrRecipientPhoneNumber = FaxSendItem->JobEntry->lpJobQueueEntry->RecipientProfile.lptstrFaxNumber;
brandInfo.lptstrSenderCompany = FaxSendItem->SenderCompany;
brandInfo.lptstrSenderTsid = FaxSendItem->Tsid;
GetLocalTime( &brandInfo.tmDateTime); // can't fail
hr = FaxBrandDocument(FaxSendItem->FileName,&brandInfo);
if (FAILED(hr))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] FaxBrandDocument() failed. (hr: 0x%08X)"),
lpJobQueue->JobId,
hr);
//
// But we go on since it is better to send the fax without the branding
// then lose it altogether.
//
}
}
FaxSendItem->JobEntry->LineInfo->State = FPS_INITIALIZING;
DebugPrintEx(
DEBUG_MSG,
TEXT("[JobId: %ld] Calling FaxDevSend().\n\t File: %s\n\tNumber [%s]\n\thLine = 0x%08X\n\tCallHandle = 0x%08X"),
lpJobQueue->JobId,
FaxSend.FileName,
FaxSendItem->JobEntry->DialablePhoneNumber,
FaxSendItem->JobEntry->LineInfo->hLine,
FaxSend.CallHandle
);
__try
{
//
// Send the fax (This call is blocking)
//
Rslt = FaxSendItem->JobEntry->LineInfo->Provider->FaxDevSend(
(HANDLE) FaxSendItem->JobEntry->InstanceData,
&FaxSend,
FaxSendCallback
);
if (!Rslt)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] FaxDevSend() failed (ec: 0x%0X)"),
lpJobQueue->JobId,
GetLastError());
}
}
__except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, FaxSendItem->JobEntry->LineInfo->Provider->FriendlyName, GetExceptionCode()))
{
ASSERT_FALSE;
}
//
// Get the final status of the job.
//
dwSttRes = GetDevStatus((HANDLE) FaxSendItem->JobEntry->InstanceData,
FaxSendItem->JobEntry->LineInfo,
&lpFSPStatus);
if (ERROR_SUCCESS != dwSttRes)
{
//
// Couldn't retrieve device status.
// Fake one.
//
bFakeJobStatus = TRUE;
DebugPrintEx(DEBUG_ERR,
TEXT("[Job: %ld] GetDevStatus failed - %d"),
FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
dwSttRes);
}
else if ((FSPI_JS_COMPLETED != lpFSPStatus->dwJobStatus) &&
(FSPI_JS_ABORTED != lpFSPStatus->dwJobStatus) &&
(FSPI_JS_FAILED != lpFSPStatus->dwJobStatus) &&
(FSPI_JS_DELETED != lpFSPStatus->dwJobStatus) &&
(FSPI_JS_FAILED_NO_RETRY != lpFSPStatus->dwJobStatus))
{
//
// Status returned is unacceptable - fake one.
//
bFakeJobStatus = TRUE;
DebugPrintEx(DEBUG_WRN,
TEXT("[Job: %ld] GetDevStatus return unacceptable status - %d. Faking the status"),
FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
lpFSPStatus->dwJobStatus);
pOrigFaxStatus = lpFSPStatus;
memcpy (&FakedJobStatus, lpFSPStatus, sizeof (FakedJobStatus));
if (lpFSPStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE)
{
//
// The FSP returned proprietary status.
//
FakedJobStatus.dwExtendedStatus = lpFSPStatus->dwExtendedStatus;
FakedJobStatus.dwExtendedStatusStringId = lpFSPStatus->dwExtendedStatusStringId;
}
lpFSPStatus = NULL;
}
//
// Enter critical section to block out FaxStatusThread
//
EnterCriticalSection( &g_CsJob );
if (bFakeJobStatus)
{
//
// Fake a job status
//
lpFSPStatus = &FakedJobStatus;
FakedJobStatus.dwSizeOfStruct = sizeof (FakedJobStatus);
if (Rslt)
{
//
// Fake success
//
FakedJobStatus.dwJobStatus = FSPI_JS_COMPLETED;
if (0 == FakedJobStatus.dwExtendedStatus)
{
//
// The FSP did not report proprietary status
//
FakedJobStatus.dwExtendedStatus = FSPI_ES_CALL_COMPLETED;
}
}
else
{
//
// Fake failure
//
FakedJobStatus.dwJobStatus = FSPI_JS_FAILED;
if (0 == FakedJobStatus.dwExtendedStatus)
{
//
// The FSP did not report proprietary status
//
FakedJobStatus.dwExtendedStatus = FSPI_ES_FATAL_ERROR;
}
}
}
if (!UpdateJobStatus(FaxSendItem->JobEntry, lpFSPStatus))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] UpdateJobStatus() failed (ec: %ld)."),
FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
GetLastError());
//
// Fake a status (we must have some valid status in job entry)
//
FreeFSPIJobStatus(&FaxSendItem->JobEntry->FSPIJobStatus, FALSE);
if (Rslt)
{
FaxSendItem->JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_COMPLETED;
FaxSendItem->JobEntry->FSPIJobStatus.dwExtendedStatus = FSPI_ES_CALL_COMPLETED;
}
else
{
FaxSendItem->JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_FAILED;
FaxSendItem->JobEntry->FSPIJobStatus.dwExtendedStatus = FSPI_ES_FATAL_ERROR;
}
}
if (!bFakeJobStatus)
{
//
// Note: The FSPI_JOB_STATUS that is returned by GetDevStatus() is
// to be freed as one block.
//
MemFree(lpFSPStatus);
lpFSPStatus = NULL;
}
else
{
//
// This is a faked job status - pointing to a structure on the stack.
//
if (pOrigFaxStatus)
{
//
// The FSP reported some status but we faked it.
// This is a good time to also free it
//
MemFree (pOrigFaxStatus);
pOrigFaxStatus = NULL;
}
}
//
// Block FaxStatusThread from changing this status
//
FaxSendItem->JobEntry->fStopUpdateStatus = TRUE;
LeaveCriticalSection( &g_CsJob );
if (!Rslt)
{
if (!HandleFailedSendJob(FaxSendItem->JobEntry))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] HandleFailedSendJob() failed (ec: %ld)."),
FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
GetLastError());
}
}
else
{
//
// cache the job id since we need id to create the FEI_COMPLETED event
// and when it is generated the job may alrady be gone
//
dwJobId = FaxSendItem->JobEntry->lpJobQueueEntry->JobId;
if (!HandleCompletedSendJob(FaxSendItem->JobEntry))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] HandleCompletedSendJob() failed (ec: %ld)."),
FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
GetLastError());
}
//
// The send job is completed. For W2K backward compatibility we should notify
// FEI_DELETED since the job was allways removed when completed.
//
if (!CreateFaxEvent(0, FEI_DELETED, dwJobId))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (ec: %ld)"),
FEI_DELETED,
dwJobId,
0,
GetLastError());
}
}
Exit:
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 );
//
// Let the system go back to sleep. Set the system idle timer.
//
if (TRUE == fSetSystemIdleTimer)
{
if (NULL == SetThreadExecutionState(ES_CONTINUOUS))
{
DebugPrintEx(DEBUG_ERR,
TEXT("SetThreadExecutionState() failed"));
}
}
if (!DecreaseServiceThreadsCount())
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
GetLastError());
}
return 0;
}
//*********************************************************************************
//* Name: IsSendJobReadyForDeleting()
//* Author: Ronen Barenboim
//* Date: April 19, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Determines if an outgoing job is ready for deleting.
//* A job is ready for deleting when all of the recipients
//* are in the canceled state or or in the completed state.
//* PARAMETERS:
//* [IN] PJOB_QUEUE lpRecipientJob
//*
//* RETURN VALUE:
//* TRUE
//* If the job is ready for deleting.
//* FALSE
//* If the job is not ready for deleting.
//*********************************************************************************
BOOL IsSendJobReadyForDeleting(PJOB_QUEUE lpRecipientJob)
{
DEBUG_FUNCTION_NAME(TEXT("IsSendJobReadyForDeleting"));
Assert (lpRecipientJob);
Assert (lpRecipientJob->JobType == JT_SEND);
PJOB_QUEUE lpParentJob = lpRecipientJob->lpParentJob;
Assert(lpParentJob); // must have a parent job
Assert(lpParentJob->dwRecipientJobsCount>0);
Assert(lpParentJob->dwCompletedRecipientJobsCount +
lpParentJob->dwCanceledRecipientJobsCount +
lpParentJob->dwFailedRecipientJobsCount
<= lpParentJob->dwRecipientJobsCount);
DebugPrintEx(
DEBUG_MSG,
TEXT("[JobId: %ld] [Total Rec = %ld] [Canceled Rec = %ld] [Completed Rec = %ld] [Failed Rec = %ld] [RefCount = %ld]"),
lpParentJob->JobId,
lpParentJob->dwRecipientJobsCount,
lpParentJob->dwCanceledRecipientJobsCount,
lpParentJob->dwCompletedRecipientJobsCount,
lpParentJob->dwFailedRecipientJobsCount,
lpParentJob->RefCount);
if ( (lpParentJob->dwCompletedRecipientJobsCount +
lpParentJob->dwCanceledRecipientJobsCount +
lpParentJob->dwFailedRecipientJobsCount) == lpParentJob->dwRecipientJobsCount )
{
return TRUE;
}
return FALSE;
}
BOOL FreeJobEntry(PJOB_ENTRY lpJobEntry , BOOL bDestroy)
{
DEBUG_FUNCTION_NAME(TEXT("FreeJobEntry"));
Assert(lpJobEntry);
DWORD ec = ERROR_SUCCESS;
DWORD dwJobID = lpJobEntry->lpJobQueueEntry ? lpJobEntry->lpJobQueueEntry->JobId : 0xffffffff; // 0xffffffff for invalid job ID
EnterCriticalSection(&g_CsJob);
//
// Since CreateJobEntry() called OpenTapiLine() for TAPI lines
// we need to close it here.
// Note that the line might alrady be released since ReleaseJob()
// releases the line but does not free the job entry.
//
if (!lpJobEntry->Released)
{
if (lpJobEntry->LineInfo->State != FPS_NOT_FAX_CALL) {
DebugPrintEx( DEBUG_MSG,
TEXT("[Job Id: %ld] Before Releasing tapi line hCall=0x%08X hLine=0x%08X"),
dwJobID,
lpJobEntry->CallHandle,
lpJobEntry->LineInfo->hLine
);
ReleaseTapiLine( lpJobEntry->LineInfo, lpJobEntry->CallHandle );
lpJobEntry->CallHandle = 0;
lpJobEntry->Released = TRUE;
}
}
//
// Remove the job from the running job list
//
RemoveEntryList( &lpJobEntry->ListEntry );
//
// Cut the link between the line and the job
//
EnterCriticalSection( &g_CsLine );
lpJobEntry->LineInfo->JobEntry = NULL;
LeaveCriticalSection( &g_CsLine );
if (!FreeFSPIJobStatus(&lpJobEntry->FSPIJobStatus, FALSE))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[Job Id: %ld] FreeFSPIJobStatus() failed (ec: %ld)"),
dwJobID,
GetLastError);
}
MemFree(lpJobEntry->lpwstrJobTsid);
lpJobEntry->lpwstrJobTsid = NULL;
if (bDestroy)
{
MemFree(lpJobEntry);
}
LeaveCriticalSection(&g_CsJob);
return TRUE;
}
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 = TRUE;
PJOB_INFO_1 JobInfo = NULL;
DEBUG_FUNCTION_NAME(TEXT("End Job"));
Assert(JobEntry);
DWORD dwJobID = JobEntry->lpJobQueueEntry ? JobEntry->lpJobQueueEntry->JobId : 0xffffffff; // 0xffffffff for invalid job ID
EnterCriticalSection( &g_CsJob );
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.
//
DebugPrintEx(DEBUG_WRN,TEXT("EndJob() could not find the Job"), dwJobID);
LeaveCriticalSection( &g_CsJob );
return ERROR_SUCCESS;
}
if (JobEntry->bFSPJobInProgress)
{
//
// If FaxDevEndJob was not yet called for the job then do it now.
// ( The case in which the line is already released occcurs in a
// receive job where we first ReleaseJob() to release the line but
// continue to do the inbound routing and only then call EndJob()).
//
__try
{
DebugPrintEx( DEBUG_MSG,
TEXT("[Job Id: %ld] Legacy FSP job is in progress. Calling FaxDevEndJob()"),
dwJobID);
rVal = JobEntry->LineInfo->Provider->FaxDevEndJob(
(HANDLE) JobEntry->InstanceData
);
if (!rVal)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[Job Id: %ld] FaxDevEndJob() failed"),
dwJobID);
}
else
{
DebugPrintEx( DEBUG_MSG,
TEXT("[Job Id: %ld] FaxDevEndJob() succeeded."),
dwJobID);
JobEntry->bFSPJobInProgress = FALSE;
}
}
__except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, JobEntry->LineInfo->Provider->FriendlyName, GetExceptionCode()))
{
ASSERT_FALSE;
}
}
else
{
DebugPrintEx(
DEBUG_MSG,
TEXT("[Job Id: %ld] FaxDevEndJob() NOT CALLED since legacy FSP job is not in progress."),
dwJobID);
}
if (FreeJobEntry(JobEntry, TRUE))
{
JobEntry = NULL;
}
else
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to free a job entry (%x)."),
JobEntry);
ASSERT_FALSE;
}
//
// 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.
//
LeaveCriticalSection( &g_CsJob );
return rVal;
}
//*********************************************************************************
//* Name: ReleaseJob()
//* Author: Ronen Barenboim
//* Date: April 18, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Calls the FSP to end the specified job (FaxDevEndJob()).
//* Releases the line that was assigned to the job.
//* NOTE: The job itself is NOT DELETED and is NOT remvoed from the running
//* job list !!!
//*
//* PARAMETERS:
//* [IN/OUT] PJOB_ENTRY JobEntry
//* A pointer to the JOB_ENTRY to be ended.
//* RETURN VALUE:
//* REMARKS:
//* If the function is successful then:
//* JobEntry->Released = TRUE
//* JobEntry->hLine = 0
//* JobEntry->CallHandle = 0
//*********************************************************************************
BOOL
ReleaseJob(
IN PJOB_ENTRY JobEntry
)
{
BOOL rVal = TRUE;
DEBUG_FUNCTION_NAME(TEXT("ReleaseJob"));
Assert(JobEntry);
Assert(JobEntry->lpJobQueueEntry);
if (!FindJobByJob( JobEntry )) {
DebugPrintEx(
DEBUG_WRN,
TEXT("[JobId: %ld] was not found in the running job list."),
JobEntry->lpJobQueueEntry->JobId);
return TRUE;
}
EnterCriticalSection( &g_CsJob );
Assert(JobEntry->LineInfo);
Assert(JobEntry->LineInfo->Provider);
Assert(JobEntry->bFSPJobInProgress);
__try
{
rVal = JobEntry->LineInfo->Provider->FaxDevEndJob(
(HANDLE) JobEntry->InstanceData
);
if (!rVal)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] FaxDevEndJob() failed (ec: 0x%0X)"),
JobEntry->lpJobQueueEntry->JobId,
GetLastError());
}
else
{
DebugPrintEx(
DEBUG_MSG,
TEXT("[Job Id: %ld] FaxDevEndJob() succeeded."),
JobEntry->lpJobQueueEntry->JobId);
JobEntry->bFSPJobInProgress = FALSE;
}
}
__except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, JobEntry->LineInfo->Provider->FriendlyName, GetExceptionCode()))
{
ASSERT_FALSE;
}
if (JobEntry->LineInfo->State != FPS_NOT_FAX_CALL)
{
if( !ReleaseTapiLine( JobEntry->LineInfo, JobEntry->CallHandle ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("ReleaseTapiLine() failed "));
}
JobEntry->CallHandle = 0;
}
else
{
//
// FSP_NOT_FAX_CALL indicates a received call that was handed off to RAS.
// In this case we do not want to mark the line as released since it is in
// use by RAS. We will use TAPI evens that indicate the line was released to update
// the line info.
//
DebugPrintEx(
DEBUG_MSG,
TEXT("[JobId: %ld] A call is being handed off to RAS. Line 0x%08X not marked as released."),
JobEntry->lpJobQueueEntry->JobId,
JobEntry->LineInfo->hLine);
}
JobEntry->Released = TRUE;
//
// Cut the link between the line and the job
//
EnterCriticalSection( &g_CsLine );
JobEntry->LineInfo->JobEntry = NULL;
LeaveCriticalSection( &g_CsLine );
LeaveCriticalSection( &g_CsJob );
return rVal;
}
//*********************************************************************************
//* Name: SendDocument()
//* Author: Ronen Barenboim
//* Date: March 21, 1999
//*********************************************************************************
//* DESCRIPTION:
//*
//* PARAMETERS:
//* lpJobEntry
//* A pointer to a JOB_ENTRY structure that was created using StartJob().
//* FileName
//* The path to the TIFF containing the TIFF to send
//*
//* RETURN VALUE:
//*
//*********************************************************************************
DWORD
SendDocument(
PJOB_ENTRY lpJobEntry,
LPTSTR FileName
)
{
PFAX_SEND_ITEM FaxSendItem;
DWORD ThreadId;
HANDLE hThread;
PJOB_QUEUE lpJobQueue;
DWORD nRes;
DWORD ec = ERROR_SUCCESS;
BOOL bUseDeviceTsid;
WCHAR wcZero = L'\0';
STRING_PAIR pairs[8];
DEBUG_FUNCTION_NAME(TEXT("SendDocument"));
Assert(lpJobEntry);
lpJobQueue=lpJobEntry->lpJobQueueEntry;
Assert(lpJobQueue &&
JS_INPROGRESS == lpJobQueue->JobStatus);
FaxSendItem = (PFAX_SEND_ITEM) MemAlloc(sizeof(FAX_SEND_ITEM));
if (!FaxSendItem)
{
ec = ERROR_NOT_ENOUGH_MEMORY;
goto Error;
}
//
// Pack all the thread parameters into a FAX_SEND_ITEM structure.
//
pairs[0].lptstrSrc = lpJobEntry->DialablePhoneNumber; // Use the job entry phone number since it is alrady translated
pairs[0].lpptstrDst = &FaxSendItem->PhoneNumber;
pairs[1].lptstrSrc = lpJobQueue->RecipientProfile.lptstrName;
pairs[1].lpptstrDst = &FaxSendItem->RecipientName;
pairs[2].lptstrSrc = lpJobQueue->SenderProfile.lptstrName;
pairs[2].lpptstrDst = &FaxSendItem->SenderName;
pairs[3].lptstrSrc = lpJobQueue->SenderProfile.lptstrDepartment;
pairs[3].lpptstrDst = &FaxSendItem->SenderDept;
pairs[4].lptstrSrc = lpJobQueue->SenderProfile.lptstrCompany;
pairs[4].lpptstrDst = &FaxSendItem->SenderCompany;
pairs[5].lptstrSrc = lpJobQueue->SenderProfile.lptstrBillingCode;
pairs[5].lpptstrDst = &FaxSendItem->BillingCode;
pairs[6].lptstrSrc = lpJobQueue->JobParamsEx.lptstrDocumentName;
pairs[6].lpptstrDst = &FaxSendItem->DocumentName;
pairs[7].lptstrSrc = NULL;
pairs[7].lpptstrDst = &FaxSendItem->Tsid;
FaxSendItem->JobEntry = lpJobEntry;
FaxSendItem->FileName = NULL; // Set by FaxSendThread
EnterCriticalSection (&g_CsConfig);
bUseDeviceTsid = g_fFaxUseDeviceTsid;
LeaveCriticalSection (&g_CsConfig);
if (!bUseDeviceTsid)
{
// Check Sender Tsid
if ( lpJobQueue->SenderProfile.lptstrTSID &&
(lpJobQueue->SenderProfile.lptstrTSID[0] != wcZero))
{
pairs[7].lptstrSrc = lpJobQueue->SenderProfile.lptstrTSID;
}
else
{
// Use Fax number
if ( lpJobQueue->SenderProfile.lptstrFaxNumber &&
(lpJobQueue->SenderProfile.lptstrFaxNumber[0] != wcZero))
{
pairs[7].lptstrSrc = lpJobQueue->SenderProfile.lptstrFaxNumber;
}
}
}
else
{
// Use device Tsid
pairs[7].lptstrSrc = lpJobEntry->LineInfo->Tsid;
}
nRes=MultiStringDup(pairs, sizeof(pairs)/sizeof(STRING_PAIR));
if (nRes!=0) {
ec=GetLastError();
// MultiStringDup takes care of freeing the memory for the pairs for which the copy succeeded
DebugPrintEx(
DEBUG_ERR,
TEXT("MultiStringDup failed to copy string with index %d. (ec: %ld)"),
nRes-1,
ec);
goto Error;
}
EnterCriticalSection (&g_CsJob);
lpJobEntry->lpwstrJobTsid = StringDup (FaxSendItem->Tsid);
LeaveCriticalSection (&g_CsJob);
if (NULL != FaxSendItem->Tsid && NULL == lpJobEntry->lpwstrJobTsid)
{
ec = GetLastError();
DebugPrintEx(DEBUG_ERR,TEXT("StringDup failed (ec: 0x%0X)"),ec);
goto Error;
}
hThread = CreateThreadAndRefCount(
NULL,
0,
(LPTHREAD_START_ROUTINE) FaxSendThread,
(LPVOID) FaxSendItem,
0,
&ThreadId
);
if (!hThread)
{
ec=GetLastError();
DebugPrintEx(DEBUG_ERR,TEXT("CreateThreadAndRefCount for FaxSendThread failed (ec: 0x%0X)"),ec);
goto Error;
}
else
{
DebugPrintEx(DEBUG_MSG,TEXT("FaxSendThread thread created for job id %d (thread id: 0x%0x)"),lpJobQueue->JobId,ThreadId);
}
CloseHandle( hThread );
Assert (ERROR_SUCCESS == ec);
goto Exit;
Error:
Assert (ERROR_SUCCESS != ec);
if ( FaxSendItem )
{
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 );
}
if (0 == lpJobQueue->dwLastJobExtendedStatus)
{
//
// Job was never really executed - this is a fatal error
//
lpJobQueue->dwLastJobExtendedStatus = FSPI_ES_FATAL_ERROR;
lpJobQueue->ExStatusString[0] = L'\0';
}
if (!MarkJobAsExpired(lpJobQueue))
{
DEBUG_ERR,
TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
lpJobQueue->JobId,
GetLastError();
}
EndJob(lpJobEntry);
lpJobQueue->JobEntry = NULL;
Exit:
return ec;
}
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;
DWORD dwEventId;
DEBUG_FUNCTION_NAME(TEXT("FaxStatusThread"));
while( TRUE )
{
Rval = GetQueuedCompletionStatus(
g_StatusCompletionPortHandle,
&Bytes,
&CompletionKey,
(LPOVERLAPPED*) &FaxStatus,
INFINITE
);
if (!Rval)
{
DebugPrintEx(DEBUG_ERR,TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"), GetLastError() );
continue;
}
if (SERVICE_SHUT_DOWN_KEY == CompletionKey)
{
//
// Service is shutting down
//
DebugPrintEx(
DEBUG_MSG,
TEXT("Service is shutting down"));
//
// Notify all FaxStatusThreads to terminate
//
if (!PostQueuedCompletionStatus( g_StatusCompletionPortHandle,
0,
SERVICE_SHUT_DOWN_KEY,
(LPOVERLAPPED) NULL))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostQueuedCompletionStatus failed (SERVICE_SHUT_DOWN_KEY - FaxStatusThread). (ec: %ld)"),
GetLastError());
}
break;
}
//
// (else we're dealing with a status update from an FSP)
//
BOOL fBadComletionKey = TRUE;
PLINE_INFO pLineInfo = (PLINE_INFO)CompletionKey;
fBadComletionKey = pLineInfo->Signature != LINE_SIGNATURE;
if (fBadComletionKey)
{
DebugPrintEx(DEBUG_WRN,
TEXT("Bad completion key: 0x%08x"),
CompletionKey);
continue;
}
BOOL fBadFaxStatus = TRUE;
fBadFaxStatus = FaxStatus->SizeOfStruct != sizeof(FAX_DEV_STATUS);
if (fBadFaxStatus)
{
DebugPrintEx(DEBUG_WRN,
TEXT("Bad FAX_DEV_STATUS: 0x%08x"),
FaxStatus);
continue;
}
EnterCriticalSection( &g_CsJob );
JobEntry = pLineInfo->JobEntry;
if (!JobEntry)
{
//
// The FSP reported a status on a LineInfo for which the running
// job no longer exists.
//
//
// Free the completion packet memory
//
DebugPrintEx(
DEBUG_WRN,
TEXT("Provider [%s] reported a status packet that was processed after the job entry was already released.\n")
TEXT("StatusId : 0x%08x\n")
TEXT("Line: %s\n")
TEXT("Packet address: %p\n")
TEXT("Heap: %p"),
pLineInfo->Provider->ProviderName,
FaxStatus->StatusId,
pLineInfo->DeviceName,
FaxStatus,
pLineInfo->Provider->HeapHandle);
if (!HeapFree(pLineInfo->Provider->HeapHandle, 0, FaxStatus ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to free orphan device status (ec: %ld)"),
GetLastError());
//
// Nothing else we can do but report it in debug mode
//
}
FaxStatus = NULL;
LeaveCriticalSection( &g_CsJob );
continue;
}
{
DWORD dwJobStatus;
DWORD dwExtendedStatus;
BOOL bPrivateStatusCode;
/*
*****
NTRAID#EdgeBugs-12680-2001/05/14-t-nicali
What if in the meantime another job is executing on the
same line. In this case ->JobEntry will point to ANOTHER job !!!
The solution should be to provide as a completion key the
JobEntry and not the LineInfo !!!
*****
*/
Assert (JobEntry->lpJobQueueEntry);
if (TRUE == JobEntry->fStopUpdateStatus)
{
DebugPrintEx(
DEBUG_WRN,
TEXT("JobId: %ld. fStopUpdateStatus was set. Not updating status %ld"),
JobEntry->lpJobQueueEntry->JobId,
JobEntry->lpJobQueueEntry->JobStatus);
if (!HeapFree(pLineInfo->Provider->HeapHandle, 0, FaxStatus ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to free orphan device status (ec: %ld)"),
GetLastError());
//
// Nothing else we can do but report it in debug mode
//
}
FaxStatus = NULL;
LeaveCriticalSection (&g_CsJob);
continue;
}
//
// Do not update final job states
//
LegacyJobStatusToStatus(
FaxStatus->StatusId,
&dwJobStatus,
&dwExtendedStatus,
&bPrivateStatusCode);
if (FSPI_JS_ABORTED == dwJobStatus ||
FSPI_JS_COMPLETED == dwJobStatus ||
FSPI_JS_FAILED == dwJobStatus ||
FSPI_JS_FAILED_NO_RETRY == dwJobStatus ||
FSPI_JS_DELETED == dwJobStatus )
{
//
// This is a final status update. Final status is updated from FaxSendThread or FaxReceiveThread
//
DebugPrintEx(
DEBUG_WRN,
TEXT("JobId: %ld. Final status code. Not updating status %ld"),
JobEntry->lpJobQueueEntry->JobId,
dwJobStatus);
if (!HeapFree(pLineInfo->Provider->HeapHandle, 0, FaxStatus ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to free orphan device status (ec: %ld)"),
GetLastError());
//
// Nothing else we can do but report it in debug mode
//
}
FaxStatus = NULL;
LeaveCriticalSection (&g_CsJob);
continue;
}
//
// Go ahead with updating the status
//
FreeFSPIJobStatus(&JobEntry->FSPIJobStatus, FALSE);
memset(&JobEntry->FSPIJobStatus, 0, sizeof(FSPI_JOB_STATUS));
JobEntry->FSPIJobStatus.dwSizeOfStruct = sizeof(FSPI_JOB_STATUS);
//
// This is done for backward compatability with W2K Fax API.
// GetJobData() and FAX_GetDeviceStatus() will use this value to return
// the job status for legacy jobs.
//
JobEntry->LineInfo->State = FaxStatus->StatusId;
LegacyJobStatusToStatus(
FaxStatus->StatusId,
&JobEntry->FSPIJobStatus.dwJobStatus,
&JobEntry->FSPIJobStatus.dwExtendedStatus,
&bPrivateStatusCode);
if (bPrivateStatusCode)
{
JobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE;
}
JobEntry->FSPIJobStatus.dwExtendedStatusStringId = FaxStatus->StringId;
JobEntry->FSPIJobStatus.dwPageCount = FaxStatus->PageCount;
JobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_PAGECOUNT;
if (FaxStatus->CSI)
{
JobEntry->FSPIJobStatus.lpwstrRemoteStationId = StringDup( FaxStatus->CSI );
if (!JobEntry->FSPIJobStatus.lpwstrRemoteStationId )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("StringDup( FaxStatus->CSI ) failed (ec: %ld)"),
GetLastError());
}
}
if (FaxStatus->CallerId)
{
JobEntry->FSPIJobStatus.lpwstrCallerId = StringDup( FaxStatus->CallerId );
if (!JobEntry->FSPIJobStatus.lpwstrCallerId )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("StringDup( FaxStatus.CallerId ) failed (ec: %ld)"),
GetLastError());
}
}
if (FaxStatus->RoutingInfo)
{
JobEntry->FSPIJobStatus.lpwstrRoutingInfo = StringDup( FaxStatus->RoutingInfo );
if (!JobEntry->FSPIJobStatus.lpwstrRoutingInfo )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("StringDup( FaxStatus.RoutingInfo ) failed (ec: %ld)"),
GetLastError());
}
}
//
// Get extended status string
//
JobEntry->ExStatusString[0] = L'\0';
if (JobEntry->FSPIJobStatus.dwExtendedStatusStringId != 0)
{
DWORD dwSize;
HINSTANCE hLoadInstance;
Assert (JobEntry->FSPIJobStatus.dwExtendedStatus != 0);
if ( !_tcsicmp(JobEntry->LineInfo->Provider->szGUID,REGVAL_T30_PROVIDER_GUID_STRING) )
{ // special case where the FSP is our FSP (fxst30.dll).
hLoadInstance = g_hResource;
}
else
{
hLoadInstance = JobEntry->LineInfo->Provider->hModule;
}
dwSize = LoadString (hLoadInstance,
JobEntry->FSPIJobStatus.dwExtendedStatusStringId,
JobEntry->ExStatusString,
ARR_SIZE(JobEntry->ExStatusString));
if (dwSize == 0)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to load extended status string (ec: %ld) stringid : %ld, Provider: %s"),
GetLastError(),
JobEntry->FSPIJobStatus.dwExtendedStatusStringId,
JobEntry->LineInfo->Provider->ImageName);
JobEntry->FSPIJobStatus.fAvailableStatusInfo &= ~FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE;
JobEntry->FSPIJobStatus.dwExtendedStatusStringId = 0;
JobEntry->FSPIJobStatus.dwExtendedStatus = 0;
}
}
dwEventId = MapFSPIJobStatusToEventId(&JobEntry->FSPIJobStatus);
//
// Note: W2K Fax did issue notifications with EventId == 0 whenever an
// FSP reported proprietry status code. To keep backward compatability
// we keep up this behaviour although it might be regarded as a bug
//
if ( !CreateFaxEvent( JobEntry->LineInfo->PermanentLineID, dwEventId, JobEntry->lpJobQueueEntry->JobId ) )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateFaxEvent failed. (ec: %ld)"),
GetLastError());
}
EnterCriticalSection (&g_CsQueue);
DWORD dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
JobEntry->lpJobQueueEntry
);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
JobEntry->lpJobQueueEntry->UniqueId,
dwRes);
}
LeaveCriticalSection (&g_CsQueue);
HeapFree( JobEntry->LineInfo->Provider->HeapHandle, 0, FaxStatus );
}
LeaveCriticalSection( &g_CsJob );
}
if (!DecreaseServiceThreadsCount())
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
GetLastError());
}
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.
--*/
{
BOOL bRet;
DEBUG_FUNCTION_NAME(TEXT("InitializeJobManager"));
g_StatusCompletionPortHandle = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,
0,
MAX_STATUS_THREADS
);
if (!g_StatusCompletionPortHandle)
{
DWORD ec = GetLastError();
FaxLog(
FAXLOG_CATEGORY_INIT,
FAXLOG_LEVEL_MIN,
1,
MSG_SERVICE_INIT_FAILED_SYS_RESOURCE,
DWORD2DECIMAL(ec)
);
DebugPrintEx(DEBUG_ERR,TEXT("Failed to create StatusCompletionPort (ec: %ld)"), GetLastError() );
goto Error;
}
bRet = TRUE;
goto Exit;
Error:
bRet = FALSE;
Exit:
return bRet;
}
VOID
SetGlobalsFromRegistry(
PREG_FAX_SERVICE FaxReg
)
{
Assert(FaxReg);
DEBUG_FUNCTION_NAME(TEXT("SetGlobalsFromRegistry"));
EnterCriticalSection (&g_CsConfig);
g_dwFaxSendRetries = FaxReg->Retries;
g_dwFaxSendRetryDelay = (INT) FaxReg->RetryDelay;
g_dwFaxDirtyDays = FaxReg->DirtyDays;
g_dwNextJobId = FaxReg->NextJobNumber;
g_dwQueueState = FaxReg->dwQueueState;
g_fFaxUseDeviceTsid = FaxReg->UseDeviceTsid;
g_fFaxUseBranding = FaxReg->Branding;
g_fServerCp = FaxReg->ServerCp;
g_StartCheapTime = FaxReg->StartCheapTime;
g_StopCheapTime = FaxReg->StopCheapTime;
LeaveCriticalSection (&g_CsConfig);
return;
}
BOOL
FillMsTagInfo(
LPTSTR FaxFileName,
const JOB_QUEUE * lpcJobQueue
)
/*++
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.
--*/
{
BOOL success = FALSE;
MS_TAG_INFO MsTagInfo = {0};
WCHAR wcZero = L'\0';
PJOB_ENTRY lpJobEntry;
LPCFSPI_JOB_STATUS lpcFSPIJobStatus;
DEBUG_FUNCTION_NAME(TEXT("FillMsTagInfo"));
Assert (lpcJobQueue);
Assert (lpcJobQueue->lpParentJob);
lpJobEntry = lpcJobQueue->JobEntry;
Assert(lpJobEntry);
lpcFSPIJobStatus = &lpJobEntry->FSPIJobStatus;
if (lpcJobQueue->RecipientProfile.lptstrName && (lpcJobQueue->RecipientProfile.lptstrName[0] != wcZero) ) {
MsTagInfo.RecipName = lpcJobQueue->RecipientProfile.lptstrName;
}
if (lpcJobQueue->RecipientProfile.lptstrFaxNumber && (lpcJobQueue->RecipientProfile.lptstrFaxNumber[0] != wcZero) ) {
MsTagInfo.RecipNumber = lpcJobQueue->RecipientProfile.lptstrFaxNumber;
}
if (lpcJobQueue->SenderProfile.lptstrName && (lpcJobQueue->SenderProfile.lptstrName[0] != wcZero) ) {
MsTagInfo.SenderName = lpcJobQueue->SenderProfile.lptstrName;
}
if (lpcFSPIJobStatus->lpwstrRoutingInfo && (lpcFSPIJobStatus->lpwstrRoutingInfo[0] != wcZero) ) {
MsTagInfo.Routing = lpcFSPIJobStatus->lpwstrRoutingInfo;
}
if (lpcFSPIJobStatus->lpwstrRemoteStationId && (lpcFSPIJobStatus->lpwstrRemoteStationId[0] != wcZero) ) {
MsTagInfo.Csid = lpcFSPIJobStatus->lpwstrRemoteStationId;
}
if (lpJobEntry->lpwstrJobTsid && (lpJobEntry->lpwstrJobTsid[0] != wcZero) ) {
MsTagInfo.Tsid = lpJobEntry->lpwstrJobTsid;
}
if (!GetRealFaxTimeAsFileTime (lpJobEntry, FAX_TIME_TYPE_START, (FILETIME*)&MsTagInfo.StartTime))
{
MsTagInfo.StartTime = 0;
DebugPrintEx(DEBUG_ERR,TEXT("GetRealFaxTimeAsFileTime (Start time) Failed (ec: %ld)"), GetLastError() );
}
if (!GetRealFaxTimeAsFileTime (lpJobEntry, FAX_TIME_TYPE_END, (FILETIME*)&MsTagInfo.EndTime))
{
MsTagInfo.EndTime = 0;
DebugPrintEx(DEBUG_ERR,TEXT("GetRealFaxTimeAsFileTime (Eend time) Failed (ec: %ld)"), GetLastError() );
}
MsTagInfo.SubmissionTime = lpcJobQueue->lpParentJob->SubmissionTime;
MsTagInfo.OriginalScheduledTime = lpcJobQueue->lpParentJob->OriginalScheduleTime;
MsTagInfo.Type = JT_SEND;
if (lpJobEntry->LineInfo->DeviceName && (lpJobEntry->LineInfo->DeviceName[0] != wcZero) )
{
MsTagInfo.Port = lpJobEntry->LineInfo->DeviceName;
}
MsTagInfo.Pages = lpcJobQueue->PageCount;
MsTagInfo.Retries = lpcJobQueue->SendRetries;
if (lpcJobQueue->RecipientProfile.lptstrCompany && (lpcJobQueue->RecipientProfile.lptstrCompany[0] != wcZero) ) {
MsTagInfo.RecipCompany = lpcJobQueue->RecipientProfile.lptstrCompany;
}
if (lpcJobQueue->RecipientProfile.lptstrStreetAddress && (lpcJobQueue->RecipientProfile.lptstrStreetAddress[0] != wcZero) ) {
MsTagInfo.RecipStreet = lpcJobQueue->RecipientProfile.lptstrStreetAddress;
}
if (lpcJobQueue->RecipientProfile.lptstrCity && (lpcJobQueue->RecipientProfile.lptstrCity[0] != wcZero) ) {
MsTagInfo.RecipCity = lpcJobQueue->RecipientProfile.lptstrCity;
}
if (lpcJobQueue->RecipientProfile.lptstrState && (lpcJobQueue->RecipientProfile.lptstrState[0] != wcZero) ) {
MsTagInfo.RecipState = lpcJobQueue->RecipientProfile.lptstrState;
}
if (lpcJobQueue->RecipientProfile.lptstrZip && (lpcJobQueue->RecipientProfile.lptstrZip[0] != wcZero) ) {
MsTagInfo.RecipZip = lpcJobQueue->RecipientProfile.lptstrZip;
}
if (lpcJobQueue->RecipientProfile.lptstrCountry && (lpcJobQueue->RecipientProfile.lptstrCountry[0] != wcZero) ) {
MsTagInfo.RecipCountry = lpcJobQueue->RecipientProfile.lptstrCountry;
}
if (lpcJobQueue->RecipientProfile.lptstrTitle && (lpcJobQueue->RecipientProfile.lptstrTitle[0] != wcZero) ) {
MsTagInfo.RecipTitle = lpcJobQueue->RecipientProfile.lptstrTitle;
}
if (lpcJobQueue->RecipientProfile.lptstrDepartment && (lpcJobQueue->RecipientProfile.lptstrDepartment[0] != wcZero) ) {
MsTagInfo.RecipDepartment = lpcJobQueue->RecipientProfile.lptstrDepartment;
}
if (lpcJobQueue->RecipientProfile.lptstrOfficeLocation && (lpcJobQueue->RecipientProfile.lptstrOfficeLocation[0] != wcZero) ) {
MsTagInfo.RecipOfficeLocation = lpcJobQueue->RecipientProfile.lptstrOfficeLocation;
}
if (lpcJobQueue->RecipientProfile.lptstrHomePhone && (lpcJobQueue->RecipientProfile.lptstrHomePhone[0] != wcZero) ) {
MsTagInfo.RecipHomePhone = lpcJobQueue->RecipientProfile.lptstrHomePhone;
}
if (lpcJobQueue->RecipientProfile.lptstrOfficePhone && (lpcJobQueue->RecipientProfile.lptstrOfficePhone[0] != wcZero) ) {
MsTagInfo.RecipOfficePhone = lpcJobQueue->RecipientProfile.lptstrOfficePhone;
}
if (lpcJobQueue->RecipientProfile.lptstrEmail && (lpcJobQueue->RecipientProfile.lptstrEmail[0] != wcZero) ) {
MsTagInfo.RecipEMail = lpcJobQueue->RecipientProfile.lptstrEmail;
}
if (lpcJobQueue->SenderProfile.lptstrFaxNumber && (lpcJobQueue->SenderProfile.lptstrFaxNumber[0] != wcZero) ) {
MsTagInfo.SenderNumber = lpcJobQueue->SenderProfile.lptstrFaxNumber;
}
if (lpcJobQueue->SenderProfile.lptstrCompany && (lpcJobQueue->SenderProfile.lptstrCompany[0] != wcZero) ) {
MsTagInfo.SenderCompany = lpcJobQueue->SenderProfile.lptstrCompany;
}
if (lpcJobQueue->SenderProfile.lptstrStreetAddress && (lpcJobQueue->SenderProfile.lptstrStreetAddress[0] != wcZero) ) {
MsTagInfo.SenderStreet = lpcJobQueue->SenderProfile.lptstrStreetAddress;
}
if (lpcJobQueue->SenderProfile.lptstrCity && (lpcJobQueue->SenderProfile.lptstrCity[0] != wcZero) ) {
MsTagInfo.SenderCity = lpcJobQueue->SenderProfile.lptstrCity;
}
if (lpcJobQueue->SenderProfile.lptstrState && (lpcJobQueue->SenderProfile.lptstrState[0] != wcZero) ) {
MsTagInfo.SenderState = lpcJobQueue->SenderProfile.lptstrState;
}
if (lpcJobQueue->SenderProfile.lptstrZip && (lpcJobQueue->SenderProfile.lptstrZip[0] != wcZero) ) {
MsTagInfo.SenderZip = lpcJobQueue->SenderProfile.lptstrZip;
}
if (lpcJobQueue->SenderProfile.lptstrCountry && (lpcJobQueue->SenderProfile.lptstrCountry[0] != wcZero) ) {
MsTagInfo.SenderCountry = lpcJobQueue->SenderProfile.lptstrCountry;
}
if (lpcJobQueue->SenderProfile.lptstrTitle && (lpcJobQueue->SenderProfile.lptstrTitle[0] != wcZero) ) {
MsTagInfo.SenderTitle = lpcJobQueue->SenderProfile.lptstrTitle;
}
if (lpcJobQueue->SenderProfile.lptstrDepartment && (lpcJobQueue->SenderProfile.lptstrDepartment[0] != wcZero) ) {
MsTagInfo.SenderDepartment = lpcJobQueue->SenderProfile.lptstrDepartment;
}
if (lpcJobQueue->SenderProfile.lptstrOfficeLocation && (lpcJobQueue->SenderProfile.lptstrOfficeLocation[0] != wcZero) ) {
MsTagInfo.SenderOfficeLocation = lpcJobQueue->SenderProfile.lptstrOfficeLocation;
}
if (lpcJobQueue->SenderProfile.lptstrHomePhone && (lpcJobQueue->SenderProfile.lptstrHomePhone[0] != wcZero) ) {
MsTagInfo.SenderHomePhone = lpcJobQueue->SenderProfile.lptstrHomePhone;
}
if (lpcJobQueue->SenderProfile.lptstrOfficePhone && (lpcJobQueue->SenderProfile.lptstrOfficePhone[0] != wcZero) ) {
MsTagInfo.SenderOfficePhone = lpcJobQueue->SenderProfile.lptstrOfficePhone;
}
if (lpcJobQueue->SenderProfile.lptstrEmail && (lpcJobQueue->SenderProfile.lptstrEmail[0] != wcZero) ) {
MsTagInfo.SenderEMail = lpcJobQueue->SenderProfile.lptstrEmail;
}
if (lpcJobQueue->SenderProfile.lptstrBillingCode && (lpcJobQueue->SenderProfile.lptstrBillingCode[0] != wcZero) ) {
MsTagInfo.SenderBilling = lpcJobQueue->SenderProfile.lptstrBillingCode;
}
if (lpcJobQueue->JobParamsEx.lptstrDocumentName && (lpcJobQueue->JobParamsEx.lptstrDocumentName[0] != wcZero) ) {
MsTagInfo.Document = lpcJobQueue->JobParamsEx.lptstrDocumentName;
}
if (lpcJobQueue->lpParentJob->CoverPageEx.lptstrSubject && (lpcJobQueue->lpParentJob->CoverPageEx.lptstrSubject[0] != wcZero) ) {
MsTagInfo.Subject = lpcJobQueue->lpParentJob->CoverPageEx.lptstrSubject;
}
if (lpcJobQueue->lpParentJob->UserName && (lpcJobQueue->lpParentJob->UserName[0] != wcZero) ) {
MsTagInfo.SenderUserName = lpcJobQueue->lpParentJob->UserName;
}
if (lpcJobQueue->SenderProfile.lptstrTSID && (lpcJobQueue->SenderProfile.lptstrTSID[0] != wcZero) ) {
MsTagInfo.SenderTsid = lpcJobQueue->SenderProfile.lptstrTSID;
}
MsTagInfo.dwStatus = JS_COMPLETED; // We archive only succesfully sent faxes
MsTagInfo.dwExtendedStatus = lpcFSPIJobStatus->dwExtendedStatus;
if (lpJobEntry->ExStatusString[0] != wcZero) {
MsTagInfo.lptstrExtendedStatus = lpJobEntry->ExStatusString;
}
MsTagInfo.dwlBroadcastId = lpcJobQueue->lpParentJob->UniqueId;
MsTagInfo.Priority = lpcJobQueue->lpParentJob->JobParamsEx.Priority;
success = TiffAddMsTags( FaxFileName, &MsTagInfo, TRUE );
if (!success)
{
DebugPrintEx( DEBUG_ERR,
TEXT("TiffAddMsTags failed, ec = %ld"),
GetLastError ());
}
if(!AddNTFSStorageProperties( FaxFileName, &MsTagInfo , TRUE ))
{
if (ERROR_OPEN_FAILED != GetLastError ())
{
//
// If AddNTFSStorageProperties fails with ERROR_OPEN_FAIL then the archive
// folder is not on an NTFS 5 partition.
// This is ok - NTFS properties are a backup mechanism but not a must
//
DebugPrintEx( DEBUG_ERR,
TEXT("AddNTFSStorageProperties failed, ec = %ld"),
GetLastError ());
success = FALSE;
}
else
{
DebugPrintEx( DEBUG_WRN,
TEXT("AddNTFSStorageProperties failed with ERROR_OPEN_FAIL. Probably not an NTFS 5 partition"));
}
}
return success;
} // FillMsTagInfo
//*********************************************************************************
//* Name: ArchiveOutboundJob()
//* Author: Ronen Barenboim
//* Date: June 03, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Archive a tiff file that has been sent by copying the file to an archive
//* directory. Also adds the MSTags to the new file generated at the
//* archive (not to the source file).
//*
//* PARAMETERS:
//* [IN ] const JOB_QUEUE * lpcJobQueue
//* Pointer to the recipient job which is to be archived.
//*
//* RETURN VALUE:
//* TRUE if the opeation succeeded.
//* FALSE if the operation failed.
//*********************************************************************************
BOOL
ArchiveOutboundJob(
const JOB_QUEUE * lpcJobQueue
)
{
BOOL rVal = FALSE;
WCHAR ArchiveFileName[MAX_PATH] = {0};
LPWSTR lpwszUserSid = NULL;
DWORD ec = ERROR_SUCCESS;
WCHAR wszArchiveFolder[MAX_PATH];
DEBUG_FUNCTION_NAME(TEXT("ArchiveOutboundJob"));
Assert(lpcJobQueue);
//
// be sure that the dir exists
//
EnterCriticalSection (&g_CsConfig);
lstrcpyn ( wszArchiveFolder,
g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].lpcstrFolder,
MAX_PATH);
LeaveCriticalSection (&g_CsConfig);
ec=IsValidFaxFolder(wszArchiveFolder);
if (ERROR_SUCCESS != ec)
{
DebugPrintEx(DEBUG_ERR,
TEXT("IsValidFaxFolder failed for folder : %s (ec=%lu)."),
wszArchiveFolder,
ec);
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
2,
MSG_FAX_ARCHIVE_OUTBOX_FOLDER_ERR,
wszArchiveFolder,
DWORD2DECIMAL(ec)
);
goto Error;
}
//
// get the user sid string
//
if (!ConvertSidToStringSid(lpcJobQueue->lpParentJob->UserSid, &lpwszUserSid))
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("ConvertSidToStringSid() failed (ec: %ld)"),
ec);
goto Error;
}
//
// get the file name
//
if (GenerateUniqueArchiveFileName( wszArchiveFolder,
ArchiveFileName,
ARR_SIZE(ArchiveFileName),
lpcJobQueue->UniqueId,
lpwszUserSid)) {
rVal = TRUE;
}
else
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to generate unique name for archive file at dir [%s] (ec: %ld)"),
wszArchiveFolder,
ec);
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
1,
MSG_FAX_ARCHIVE_CREATE_FILE_FAILED,
DWORD2DECIMAL(ec)
);
goto Error;
}
if (rVal) {
Assert(lpcJobQueue->FileName);
rVal = CopyFile( lpcJobQueue->FileName, ArchiveFileName, FALSE );
if (!rVal)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("CopyFile [%s] to [%s] failed. (ec: %ld)"),
lpcJobQueue->FileName,
ArchiveFileName,
ec);
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
1,
MSG_FAX_ARCHIVE_CREATE_FILE_FAILED,
DWORD2DECIMAL(ec)
);
if (!DeleteFile(ArchiveFileName))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DeleteFile [%s] failed. (ec: %ld)"),
ArchiveFileName,
GetLastError());
}
goto Error;
}
}
if (rVal)
{
DWORD dwRes;
HANDLE hFind;
WIN32_FIND_DATA FindFileData;
if (!FillMsTagInfo( ArchiveFileName,
lpcJobQueue
))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to add MS TIFF tags to archived file %s. (ec: %ld)"),
ArchiveFileName,
dwRes);
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
2,
MSG_FAX_ARCHIVE_NO_TAGS,
ArchiveFileName,
DWORD2HEX(dwRes)
);
}
dwRes = CreateArchiveEvent (lpcJobQueue->UniqueId,
FAX_EVENT_TYPE_OUT_ARCHIVE,
FAX_JOB_EVENT_TYPE_ADDED,
lpcJobQueue->lpParentJob->UserSid);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateConfigEvent(FAX_CONFIG_TYPE_*_ARCHIVE) failed (ec: %lc)"),
dwRes);
}
hFind = FindFirstFile( ArchiveFileName, &FindFileData);
if (INVALID_HANDLE_VALUE == hFind)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("FindFirstFile failed (ec: %lc), File %s"),
GetLastError(),
ArchiveFileName);
}
else
{
// Update the archive size - for quota management
EnterCriticalSection (&g_CsConfig);
if (FAX_ARCHIVE_FOLDER_INVALID_SIZE != g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].dwlArchiveSize)
{
g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].dwlArchiveSize += (MAKELONGLONG(FindFileData.nFileSizeLow ,FindFileData.nFileSizeHigh));
}
LeaveCriticalSection (&g_CsConfig);
Assert (FindFileData.nFileSizeLow);
if (!FindClose(hFind))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("FindClose failed (ec: %lc)"),
GetLastError());
}
}
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MAX,
2,
MSG_FAX_SENT_ARCHIVE_SUCCESS,
lpcJobQueue->FileName,
ArchiveFileName
);
}
Assert( ERROR_SUCCESS == ec);
goto Exit;
Error:
Assert( ERROR_SUCCESS != ec);
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
3,
MSG_FAX_ARCHIVE_FAILED,
lpcJobQueue->FileName,
wszArchiveFolder,
DWORD2HEX(GetLastError())
);
Exit:
if (ERROR_SUCCESS != ec)
{
SetLastError(ec);
}
if (lpwszUserSid != NULL)
{
LocalFree (lpwszUserSid);
}
return (ERROR_SUCCESS == ec);
}
BOOL UpdatePerfCounters(const JOB_QUEUE * lpcJobQueue)
{
SYSTEMTIME SystemTime ;
DWORD Seconds ;
HANDLE FileHandle ;
DWORD Bytes = 0 ; /// Compute #bytes in the file FaxSend.FileName and stick it here!
const JOB_ENTRY * lpcJobEntry;
DEBUG_FUNCTION_NAME(TEXT("UpdatePerfCounters"));
Assert(lpcJobQueue);
lpcJobEntry = lpcJobQueue->JobEntry;
Assert(lpcJobEntry);
FileHandle = SafeCreateFile(
lpcJobEntry->lpJobQueueEntry->FileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(FileHandle != INVALID_HANDLE_VALUE)
{
Bytes = GetFileSize( FileHandle, NULL );
CloseHandle( FileHandle );
}
if (!FileTimeToSystemTime(
(FILETIME*)&lpcJobEntry->ElapsedTime,
&SystemTime
))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("FileTimeToSystemTime failed (ec: %ld)"),
GetLastError());
Assert(FALSE);
memset(&SystemTime,0,sizeof(SYSTEMTIME));
}
Seconds = (DWORD)( SystemTime.wSecond + 60 * ( SystemTime.wMinute + 60 * SystemTime.wHour ));
InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFaxes );
InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->TotalFaxes );
InterlockedExchangeAdd( (PLONG)&g_pFaxPerfCounters->OutboundPages, (LONG)lpcJobEntry->FSPIJobStatus.dwPageCount );
InterlockedExchangeAdd( (PLONG)&g_pFaxPerfCounters->TotalPages, (LONG)lpcJobEntry->FSPIJobStatus.dwPageCount );
EnterCriticalSection( &g_CsPerfCounters );
g_dwOutboundSeconds += Seconds;
g_dwTotalSeconds += Seconds;
g_pFaxPerfCounters->OutboundMinutes = g_dwOutboundSeconds / 60 ;
g_pFaxPerfCounters->TotalMinutes = g_dwTotalSeconds / 60 ;
g_pFaxPerfCounters->OutboundBytes += Bytes;
g_pFaxPerfCounters->TotalBytes += Bytes;
LeaveCriticalSection( &g_CsPerfCounters );
return TRUE;
}
BOOL MarkJobAsExpired(PJOB_QUEUE lpJobQueue)
{
FILETIME CurrentFileTime;
LARGE_INTEGER NewTime;
DWORD dwMaxRetries;
BOOL rVal = TRUE;
DEBUG_FUNCTION_NAME(TEXT("MarkJobAsExpired"));
Assert(lpJobQueue);
Assert( JT_SEND == lpJobQueue->JobType ||
JT_ROUTING == lpJobQueue->JobType );
EnterCriticalSection(&g_CsQueue);
lpJobQueue->JobStatus = JS_RETRIES_EXCEEDED;
EnterCriticalSection (&g_CsConfig);
dwMaxRetries = g_dwFaxSendRetries;
LeaveCriticalSection (&g_CsConfig);
lpJobQueue->SendRetries = dwMaxRetries + 1;
//
// Set the job's ScheduleTime field to the time it totaly failed.
// (current time).
//
GetSystemTimeAsFileTime( &CurrentFileTime ); //Can not fail (Win32 SDK)
NewTime.LowPart = CurrentFileTime.dwLowDateTime;
NewTime.HighPart = CurrentFileTime.dwHighDateTime;
lpJobQueue->ScheduleTime = NewTime.QuadPart;
if (!CommitQueueEntry(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CommitQueueEntry() for recipien job %s has failed. (ec: %ld)"),
lpJobQueue->FileName,
GetLastError());
rVal = FALSE;
}
if (JT_SEND == lpJobQueue->JobType)
{
Assert (lpJobQueue->lpParentJob);
lpJobQueue->lpParentJob->dwFailedRecipientJobsCount+=1;
//
// The parent job keeps the schedule of the last recipient job that failed.
// The job retention policy for the parent will be based on that
// schedule.
lpJobQueue->lpParentJob->ScheduleTime = lpJobQueue->ScheduleTime;
if (!CommitQueueEntry(lpJobQueue->lpParentJob))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CommitQueueEntry() for parent job %s has failed. (ec: %ld)"),
lpJobQueue->lpParentJob->FileName,
GetLastError());
rVal = FALSE;
}
}
LeaveCriticalSection(&g_CsQueue);
return rVal;
}
//*********************************************************************************
//* Name: CreateJobEntry()
//* Author: Ronen Barenboim
//* Date: May 31, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Creates and initializes a new JOB_ENTRY.
//* Opens the line the job is to be executed on (if it is a TAPI line)
//* and creates the attachement between the line and the job.
//* PARAMETERS:
//* [IN/OUT] PJOB_QUEUE lpJobQueue
//* For outgoing jobs this points to the JOB_QUEUE of the outgoing job.
//* for receive job this should be set to NULL.
//* [IN/OUT] LINE_INFO * lpLineInfo
//* A pointer to the LINE_INFO information of the line on which the job
//* is to be executed.
//* [IN ] BOOL bTranslateNumber
//* TRUE if the recipient number needs to be translated into dilable
//* string (needed for legacy FaxDevSend() where the number must be
//* dilable and not canonical).
//*
//* RETURN VALUE:
//*
//*********************************************************************************
PJOB_ENTRY CreateJobEntry(
PJOB_QUEUE lpJobQueue,
LINE_INFO * lpLineInfo,
BOOL bTranslateNumber
)
{
BOOL Failure = TRUE;
PJOB_ENTRY JobEntry = NULL;
DWORD rc = ERROR_SUCCESS;;
DEBUG_FUNCTION_NAME(TEXT("CreateJobEntry"));
Assert(!(lpJobQueue && lpJobQueue->JobType != JT_SEND));
Assert(!(bTranslateNumber && !lpJobQueue));
Assert (lpLineInfo);
JobEntry = (PJOB_ENTRY) MemAlloc( sizeof(JOB_ENTRY) );
if (!JobEntry)
{
rc=GetLastError();
DebugPrintEx(DEBUG_ERR,_T("Failed to allocated memory for JOB_ENTRY."));
goto exit;
}
memset(JobEntry, 0, sizeof(JOB_ENTRY));
if (lpJobQueue)
{
if (! _tcslen(lpJobQueue->tczDialableRecipientFaxNumber))
{
//
// The Fax Number was not compound, make translation as before
//
if (bTranslateNumber)
{
rc = TranslateCanonicalNumber(lpJobQueue->RecipientProfile.lptstrFaxNumber,
lpLineInfo->DeviceId,
JobEntry->DialablePhoneNumber,
ARR_SIZE(JobEntry->DialablePhoneNumber),
JobEntry->DisplayablePhoneNumber,
ARR_SIZE(JobEntry->DisplayablePhoneNumber));
if (ERROR_SUCCESS != rc)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("TranslateCanonicalNumber() faield for number: %s (ec: %ld)"),
lpJobQueue->RecipientProfile.lptstrFaxNumber,
rc);
goto exit;
}
}
else
{
_tcsncpy(JobEntry->DialablePhoneNumber, lpJobQueue->RecipientProfile.lptstrFaxNumber, SIZEOF_PHONENO );
JobEntry->DialablePhoneNumber[SIZEOF_PHONENO - 1] = '\0';
_tcsncpy(JobEntry->DisplayablePhoneNumber, lpJobQueue->RecipientProfile.lptstrFaxNumber, SIZEOF_PHONENO );
JobEntry->DisplayablePhoneNumber[SIZEOF_PHONENO - 1] = '\0';
}
}
else
{
//
// The Fax Number was compound, no translation needed
// Take Dialable from JobQueue and Displayable from Recipient's PersonalProfile's FaxNumber
//
_tcsncpy(JobEntry->DialablePhoneNumber, lpJobQueue->tczDialableRecipientFaxNumber, SIZEOF_PHONENO );
_tcsncpy(JobEntry->DisplayablePhoneNumber, lpJobQueue->RecipientProfile.lptstrFaxNumber, (SIZEOF_PHONENO - 1));
JobEntry->DisplayablePhoneNumber[SIZEOF_PHONENO - 1] = '\0';
}
}
else
{
//
// lpJobQueue is NULL
//
JobEntry->DialablePhoneNumber[0] = L'\0';
JobEntry->DisplayablePhoneNumber[0] = L'\0';
}
JobEntry->CallHandle = 0;
JobEntry->InstanceData = 0;
JobEntry->LineInfo = lpLineInfo;
JobEntry->SendIdx = -1;
JobEntry->Released = FALSE;
JobEntry->lpJobQueueEntry = lpJobQueue;
JobEntry->bFSPJobInProgress = FALSE;
memset(&JobEntry->FSPIJobStatus,0,sizeof(FSPI_JOB_STATUS));
JobEntry->FSPIJobStatus.dwSizeOfStruct = sizeof(FSPI_JOB_STATUS);
JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_UNKNOWN;
GetSystemTimeAsFileTime( (FILETIME*) &JobEntry->StartTime );
EnterCriticalSection (&g_CsLine);
if (!(lpLineInfo->Flags & FPF_VIRTUAL) && (!lpLineInfo->hLine))
{
if (!OpenTapiLine( lpLineInfo ))
{
rc = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("OpenTapiLine failed. (ec: %ld)"),
rc);
LeaveCriticalSection (&g_CsLine);
goto exit;
}
}
//
// Attach the job to the line selected to service it.
//
lpLineInfo->JobEntry = JobEntry;
LeaveCriticalSection (&g_CsLine);
Failure = FALSE;
exit:
if (Failure)
{
// Failure is initialized to TRUE
if (JobEntry)
{
MemFree( JobEntry );
}
JobEntry = NULL;
}
if (ERROR_SUCCESS != rc)
{
SetLastError(rc);
}
return JobEntry;
} // CreateJobEntry
//*********************************************************************************
//* Name: TranslateCanonicalNumber()
//* Author: Ronen Barenboim
//* Date: May 31, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Translates a canonical number to a dilable + displayable number.
//*
//* PARAMETERS:
//* [IN ] LPTSTR lptstrFaxNumber
//* The canonical number to translate.
//*
//* [IN ] DWORD dwDeviceID
//* The device ID.
//*
//* [OUT] LPTSTR lptstrDialableAddress
//* Buffer to receive the dialable translated address.
//*
//* [IN] DWORD dwDialableAddressCount
//* size in TCHARs of the buffer pointed by lptstrDialableAddress
//*
//* [OUT] LPTSTR lptstrDisplayableAddress
//* Buffer to receive the displayable translated address.
//*
//* [IN] DWORD dwDisplayableAddressCount
//* size in TCHARs of the buffer pointed by lptstrDialableAddress
//*
//* RETURN VALUE:
//* Win32 / HRESULT error code
//*********************************************************************************
static
DWORD
TranslateCanonicalNumber(
LPTSTR lptstrCanonicalFaxNumber,
DWORD dwDeviceID,
LPTSTR lptstrDialableAddress,
DWORD dwDialableAddressCount,
LPTSTR lptstrDisplayableAddress,
DWORD dwDisplayableAddressCount
)
{
DWORD ec = ERROR_SUCCESS;
LPLINETRANSLATEOUTPUT LineTranslateOutput = NULL;
DEBUG_FUNCTION_NAME(TEXT("TranslateCanonicalNumber"));
Assert(lptstrCanonicalFaxNumber && lptstrDialableAddress && lptstrDisplayableAddress);
ec = MyLineTranslateAddress( lptstrCanonicalFaxNumber, dwDeviceID, &LineTranslateOutput );
if (ERROR_SUCCESS == ec)
{
LPTSTR lptstrTranslateBuffer;
HRESULT hr;
//
// Copy displayable string
// TAPI returns credit card numbers in the displayable string.
// return the input canonical number as the displayable string.
//
hr = StringCchCopy(
lptstrDisplayableAddress,
dwDisplayableAddressCount,
lptstrCanonicalFaxNumber);
if (FAILED(hr))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("StringCchCopy() failed (ec: %ld)"),
hr);
ec = HRESULT_CODE(hr);
goto Exit;
}
//
// Copy dialable string
//
Assert (LineTranslateOutput->dwDialableStringSize > 0);
lptstrTranslateBuffer=(LPTSTR)((LPBYTE)LineTranslateOutput + LineTranslateOutput->dwDialableStringOffset);
hr = StringCchCopy(
lptstrDialableAddress,
dwDialableAddressCount,
lptstrTranslateBuffer);
if (FAILED(hr))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("StringCchCopy() failed (ec: %ld)"),
hr);
ec = HRESULT_CODE(hr);
goto Exit;
}
}
else
{
// ec is a Tapi ERROR
DebugPrintEx(
DEBUG_ERR,
TEXT("MyLineTranslateAddress() failed for fax number: [%s] (ec: %ld)"),
lptstrCanonicalFaxNumber,
ec);
goto Exit;
}
Assert (ERROR_SUCCESS == ec);
Exit:
MemFree( LineTranslateOutput );
if (ERROR_SUCCESS != ec)
{
SetLastError(ec);
}
return ec;
} // TranslateCanonicalNumber
//*********************************************************************************
//* Name: HandleCompletedSendJob()
//* Author: Ronen Barenboim
//* Date: June 01, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Handles the completion of a recipient job. Called when a recipient job
//* has reaced a JS_COMPLETED state.
//*
//* IMPORTANT- This call can be blocking. Calling thread MUST NOT hold any critical section
//*
//* - Marks the job as completed (JS_COMPLETED).
//* - Archives the sent file if required.
//* - Sends a positive receipt
//* - Removes the parent job if required.
//*
//* PARAMETERS:
//* [IN ] PJOB_ENTRY lpJobEntry
//*
//* RETURN VALUE:
//* TRUE
//* If the operation completed successfully.
//* FALSE
//* If the operation failed. Call GetLastError() for extended errror
//* information.
//*********************************************************************************
BOOL HandleCompletedSendJob(PJOB_ENTRY lpJobEntry)
{
PJOB_QUEUE lpJobQueue = NULL;
DWORD ec = 0;
BOOL fCOMInitiliazed = FALSE;
HRESULT hr;
BOOL bArchiveSentItems;
DWORD dwRes;
DEBUG_FUNCTION_NAME(TEXT("HandleCompletedSendJob)"));
EnterCriticalSection ( &g_CsJob );
EnterCriticalSection (&g_CsConfig);
bArchiveSentItems = g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].bUseArchive;
LeaveCriticalSection (&g_CsConfig);
Assert(lpJobEntry);
lpJobQueue = lpJobEntry->lpJobQueueEntry;
Assert(lpJobQueue);
Assert(JT_SEND == lpJobQueue->JobType);
Assert(FSPI_JS_COMPLETED == lpJobEntry->FSPIJobStatus.dwJobStatus);
//
// Update end time in JOB_ENTRY
//
GetSystemTimeAsFileTime( (FILETIME*) &lpJobEntry->EndTime );
//
// Update elapsed time in JOB_ENTRY
//
Assert (lpJobEntry->EndTime >= lpJobEntry->StartTime);
lpJobEntry->ElapsedTime = lpJobEntry->EndTime - lpJobEntry->StartTime;
//
// We generate a full tiff for each recipient
// so we will have something to put in the send archive.
//
if (!lpJobQueue->FileName)
{
DebugPrintEx(
DEBUG_MSG,
TEXT("[JobId: %ld] Generating body for recipient job."),
lpJobQueue->JobId
);
if (!CreateTiffFileForJob(lpJobQueue))
{
dwRes = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] CreateTiffFileForJob failed. (ec: %ld)"),
lpJobQueue->JobId,
dwRes);
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
1,
MSG_FAX_TIFF_CREATE_FAILED_NO_ARCHIVE,
g_wszFaxQueueDir,
DWORD2DECIMAL(dwRes)
);
}
}
// Needed for Archiving
hr = CoInitialize (NULL);
if (FAILED (hr))
{
WCHAR wszArchiveFolder[MAX_PATH];
EnterCriticalSection (&g_CsConfig);
lstrcpyn ( wszArchiveFolder,
g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].lpcstrFolder,
MAX_PATH);
LeaveCriticalSection (&g_CsConfig);
DebugPrintEx( DEBUG_ERR,
TEXT("CoInitilaize failed, err %ld"),
hr);
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
3,
MSG_FAX_ARCHIVE_FAILED,
lpJobQueue->FileName,
wszArchiveFolder,
DWORD2DECIMAL(hr)
);
}
else
{
fCOMInitiliazed = TRUE;
}
if (lpJobQueue->FileName) //might be null if we failed to generate a TIFF
{
//
// Archive the file (also adds MS Tags to the tiff at the archive directory)
//
if (bArchiveSentItems && fCOMInitiliazed)
{
if (!ArchiveOutboundJob(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("JobId: %ld] ArchiveOutboundJob() failed (ec: %ld)"),
lpJobQueue->JobId,
GetLastError());
//
// The event log entry is generated by the function itself
//
}
}
}
//
// Log the succesful send to the event log
//
EnterCriticalSection (&g_CsOutboundActivityLogging);
if (INVALID_HANDLE_VALUE == g_hOutboxActivityLogFile)
{
DebugPrintEx(DEBUG_ERR,
TEXT("Logging not initialized"));
}
else
{
if (!LogOutboundActivity(lpJobQueue))
{
DebugPrintEx(DEBUG_ERR, TEXT("Logging outbound activity failed"));
}
}
LeaveCriticalSection (&g_CsOutboundActivityLogging);
if (fCOMInitiliazed == TRUE)
{
CoUninitialize ();
}
FaxLogSend(lpJobQueue, FALSE);
//
// Increment counters for Performance Monitor
//
if (g_pFaxPerfCounters)
{
if (!UpdatePerfCounters(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] UpdatePerfCounters() failed. (ec: %ld)"),
lpJobQueue->JobId,
GetLastError());
Assert(FALSE);
}
}
EnterCriticalSection ( &g_CsQueue );
//
// Mark the job as completed (new client API)
//
lpJobQueue->JobStatus = JS_COMPLETED;
//
// Save the last extended status before ending this job
//
lpJobQueue->dwLastJobExtendedStatus = lpJobQueue->JobEntry->FSPIJobStatus.dwExtendedStatus;
hr = StringCchCopy(
lpJobQueue->ExStatusString,
ARR_SIZE(lpJobQueue->ExStatusString),
lpJobQueue->JobEntry->ExStatusString);
if (FAILED(hr))
{
//
// Can never happen, we use large enough buffer.
//
ASSERT_FALSE;
}
if (!UpdatePersistentJobStatus(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to update persistent job status to 0x%08x"),
lpJobQueue->JobStatus);
Assert(FALSE);
}
lpJobQueue->lpParentJob->dwCompletedRecipientJobsCount+=1;
//
// Create Fax EventEx
//
dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS, lpJobQueue );
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
lpJobQueue->UniqueId,
dwRes);
}
//
// We will send the receipt once we are out of all critical sections because this call can be blocking.
// just increase the preview refernce count so the job will not be deleted.
//
IncreaseJobRefCount (lpJobQueue, TRUE); // TRUE - preview
//
// Copy receipt information from JobEntry.
//
lpJobQueue->StartTime = lpJobQueue->JobEntry->StartTime;
lpJobQueue->EndTime = lpJobQueue->JobEntry->EndTime;
//
// EndJob() must be called BEFORE we remove the parent job (and recipients)
//
lpJobQueue->JobEntry->LineInfo->State = FPS_AVAILABLE;
//
// We just completed a send job on the device - update counter.
//
(VOID) UpdateDeviceJobsCounter (lpJobQueue->JobEntry->LineInfo, // Device to update
TRUE, // Sending
-1, // Number of new jobs (-1 = decrease by one)
TRUE); // Enable events
if (!EndJob( lpJobQueue->JobEntry ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("EndJob Failed. (ec: %ld)"),
GetLastError());
}
lpJobQueue->JobEntry = NULL;
DecreaseJobRefCount (lpJobQueue, TRUE); // This will mark it as JS_DELETING if needed
//
// Notify the queue that a device is now available.
//
if (!SetEvent( g_hJobQueueEvent ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to set g_hJobQueueEvent. (ec: %ld)"),
GetLastError());
g_ScanQueueAfterTimeout = TRUE;
}
LeaveCriticalSection ( &g_CsQueue );
LeaveCriticalSection ( &g_CsJob );
//
// Now send the receipt
//
if (!SendJobReceipt (TRUE, lpJobQueue, lpJobQueue->FileName))
{
ec = GetLastError ();
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] SendJobReceipt failed. (ec: %ld)"),
lpJobQueue->JobId,
ec
);
}
EnterCriticalSection (&g_CsQueue);
DecreaseJobRefCount (lpJobQueue, TRUE, TRUE, TRUE); // last TRUE for Preview ref count.
LeaveCriticalSection (&g_CsQueue);
return TRUE;
} // HandleCompletedSendJob
//*********************************************************************************
//* Name: HandleFailedSendJob()
//* Author: Ronen Barenboim
//* Date: June 01, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Handles the post failure operations of a send job.
//*
//* IMPORTANT- This call can be blocking. Calling thread MUST NOT hold any critical section
//*
//* PARAMETERS:
//* [IN ] PJOB_ENTRY lpJobEntry
//* The job that failed. It must be in FSPI_JS_ABORTED or FSPI_JS_FAILED
//* state.
//* RETURN VALUE:
//* TRUE
//* If the operation completed successfully.
//* FALSE
//* If the operation failed. Call GetLastError() for extended errror
//* information.
//*********************************************************************************
BOOL HandleFailedSendJob(PJOB_ENTRY lpJobEntry)
{
PJOB_QUEUE lpJobQueue;
BOOL bRetrying = FALSE;
DEBUG_FUNCTION_NAME(TEXT("HandleFailedSendJob"));
DWORD dwRes;
TCHAR tszJobTiffFile[MAX_PATH] = {0}; // Deleted after receipt is sent
BOOL fAddRetryDelay = TRUE;
EnterCriticalSection ( &g_CsJob );
EnterCriticalSection ( &g_CsQueue );
Assert(lpJobEntry);
lpJobQueue = lpJobEntry->lpJobQueueEntry;
Assert(lpJobQueue);
DebugPrintEx(
DEBUG_MSG,
TEXT("Failed Job: %ld"),
lpJobQueue->JobId);
Assert( FSPI_JS_ABORTED == lpJobEntry->FSPIJobStatus.dwJobStatus ||
FSPI_JS_FAILED == lpJobEntry->FSPIJobStatus.dwJobStatus ||
FSPI_JS_FAILED_NO_RETRY == lpJobEntry->FSPIJobStatus.dwJobStatus ||
FSPI_JS_DELETED == lpJobEntry->FSPIJobStatus.dwJobStatus);
//
// Do not cache rendered tiff files
//
if (lpJobQueue->FileName)
{
//
// We simply store the file name to delete and delete it later
// since we might need it for receipt attachment.
//
_tcsncpy (tszJobTiffFile,
lpJobQueue->FileName,
(sizeof (tszJobTiffFile) / sizeof (tszJobTiffFile[0]))-1);
MemFree (lpJobQueue->FileName);
lpJobQueue->FileName = NULL;
}
//
// Update end time in JOB_ENTRY
//
GetSystemTimeAsFileTime( (FILETIME*) &lpJobEntry->EndTime );
//
// Update elapsed time in JOB_ENTRY
//
Assert (lpJobEntry->EndTime >= lpJobEntry->StartTime);
lpJobEntry->ElapsedTime = lpJobEntry->EndTime - lpJobEntry->StartTime;
if ( FSPI_JS_ABORTED == lpJobEntry->FSPIJobStatus.dwJobStatus)
{
//
// The FSP reported the job was aborted.
//
DebugPrintEx(
DEBUG_MSG,
TEXT("[Job Id: %ld] EFSP reported that job was aborted."),
lpJobQueue->JobId);
//
// Check if the job was aborted by the service (shutting down) or by the user
//
if (FALSE == lpJobEntry->fSystemAbort)
{
//
// The event log about a canceled job will be reported at the end of this if..else block.
//
lpJobEntry->Aborting = 1;
bRetrying = FALSE; // Do not retry on cancel
}
else
{
//
// SystemAbort
// Don't increase the retry count since this is not really a failure.
//
bRetrying = TRUE;
fAddRetryDelay = FALSE;
}
}
else if ( FSPI_JS_FAILED == lpJobEntry->FSPIJobStatus.dwJobStatus)
{
switch (lpJobEntry->FSPIJobStatus.dwExtendedStatus)
{
case FSPI_ES_LINE_UNAVAILABLE:
//
// this is the glare condition. Someone snatched the line before the FSP
// had a chance to grab it.
// We will try again but will not increase the retry count.
//
EnterCriticalSection (&g_CsLine);
//
// Check if the line was busy or closed
//
if (!(lpJobEntry->LineInfo->Flags & FPF_VIRTUAL))
{
//
// Tapi line
//
if (NULL == lpJobEntry->LineInfo->hLine)
{
//
// Tapi worker thread got LINE_CLOSE
//
fAddRetryDelay = FALSE;
}
}
LeaveCriticalSection (&g_CsLine);
bRetrying = TRUE;
if (g_pFaxPerfCounters)
{
//
// Increase the 'Outbound failed connections' counter.
//
InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedConnections );
}
//
// Don't increase the retry count since this is not really a failure.
//
break;
case FSPI_ES_NO_ANSWER:
case FSPI_ES_NO_DIAL_TONE:
case FSPI_ES_DISCONNECTED:
case FSPI_ES_BUSY:
case FSPI_ES_NOT_FAX_CALL:
case FSPI_ES_CALL_DELAYED:
//
// For these error codes we need to retry
//
bRetrying = CheckForJobRetry(lpJobQueue);
if (g_pFaxPerfCounters)
{
//
// Increase the 'Outbound failed connections' counter.
//
InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedConnections );
}
break;
case FSPI_ES_FATAL_ERROR:
//
// For these error codes we need to retry
//
bRetrying = CheckForJobRetry(lpJobQueue);
if (g_pFaxPerfCounters)
{
//
// Increase the 'Outbound failed transmissions' counter.
//
InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedXmit );
}
break;
case FSPI_ES_BAD_ADDRESS:
case FSPI_ES_CALL_BLACKLISTED:
//
// No retry for these error codes
//
bRetrying = FALSE;
if (g_pFaxPerfCounters)
{
//
// Increase the 'Outbound failed connections' counter.
//
InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedConnections );
}
break;
default:
//
// Our default for extension codes
// is to retry.
//
bRetrying = CheckForJobRetry(lpJobQueue);
if (g_pFaxPerfCounters)
{
//
// Increase the 'Outbound failed transmissions' counter.
//
InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedXmit );
}
break;
}
}
else if ( FSPI_JS_FAILED_NO_RETRY == lpJobEntry->FSPIJobStatus.dwJobStatus )
{
//
// The FSP indicated that there is no point in retrying this job.
//
bRetrying = FALSE;
}
else if ( FSPI_JS_DELETED == lpJobEntry->FSPIJobStatus.dwJobStatus )
{
//
// This is the case where the job can not be reestablished
// we treat it as a failure with no retry.
bRetrying = FALSE;
}
if (lpJobEntry->Aborting )
{
//
// An abort operation is in progress for this job.
// No point in retrying.
// Just mark the job as canceled and see if we can remove the parent job yet.
//
DebugPrintEx(
DEBUG_MSG,
TEXT("[JobId: %ld] lpJobEntry->Aborting is ON."));
lpJobQueue->JobStatus = JS_CANCELED;
if (!UpdatePersistentJobStatus(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to update persistent job status to 0x%08x"),
lpJobQueue->JobStatus);
Assert(FALSE);
}
lpJobQueue->lpParentJob->dwCanceledRecipientJobsCount+=1;
bRetrying = FALSE;
}
//
// Save the last extended status before ending this job
//
lpJobQueue->dwLastJobExtendedStatus = lpJobEntry->FSPIJobStatus.dwExtendedStatus;
HRESULT hr = StringCchCopy(
lpJobQueue->ExStatusString,
ARR_SIZE(lpJobQueue->ExStatusString),
lpJobQueue->JobEntry->ExStatusString);
if (FAILED(hr))
{
//
// Can never happen, we use large enough buffer.
//
ASSERT_FALSE;
}
if (!bRetrying && !lpJobEntry->Aborting)
{
//
// If we do not handle an abort request (in this case we do not want
// to count it as a failure since it will be counted as Canceled) and we decided
// not to retry then we need to mark the job as expired.
//
if (0 == lpJobQueue->dwLastJobExtendedStatus)
{
//
// Job was never really executed - this is a fatal error
//
lpJobQueue->dwLastJobExtendedStatus = FSPI_ES_FATAL_ERROR;
lpJobQueue->ExStatusString[0] = L'\0';
}
if (!MarkJobAsExpired(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
lpJobQueue->JobId,
GetLastError());
}
}
if (!bRetrying)
{
//
// Job reached final failure state - send negative receipt
// We will send the receipt once we are out of all critical sections because this call can be blocking.
// just increase the preview refernce count so the job will not be deleted.
//
IncreaseJobRefCount (lpJobQueue, TRUE); // TRUE - preview
//
// Copy receipt information from JobEntry.
//
lpJobQueue->StartTime = lpJobQueue->JobEntry->StartTime;
lpJobQueue->EndTime = lpJobQueue->JobEntry->EndTime;
}
else
{
//
// Job marked for retry. Do not delete it. Reschedule it.
//
DebugPrintEx(
DEBUG_MSG,
TEXT("[JobId: %ld] Set for retry (JS_RETRYING). Retry Count = %ld)"),
lpJobQueue->JobId,
lpJobQueue->SendRetries);
lpJobQueue->JobStatus = JS_RETRYING;
//
// Job entry must be NULLified before leaving the CS.
// This is done below because we still need the Job entry for logging
//
if (TRUE == fAddRetryDelay)
{
//
// Send failure - Reschedule
//
RescheduleJobQueueEntry( lpJobQueue );
}
else
{
//
// FaxDevShutDown() was called, or We lost the line, Do not add retry delay
//
if (!CommitQueueEntry(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CommitQueueEntry() for recipien job %s has failed. (ec: %ld)"),
lpJobQueue->FileName,
GetLastError());
}
}
}
FaxLogSend(
lpJobQueue,
bRetrying);
if (!bRetrying)
{
EnterCriticalSection (&g_CsOutboundActivityLogging);
if (INVALID_HANDLE_VALUE == g_hOutboxActivityLogFile)
{
DebugPrintEx(DEBUG_ERR,
TEXT("Logging not initialized"));
}
else
{
if (!LogOutboundActivity(lpJobQueue))
{
DebugPrintEx(DEBUG_ERR, TEXT("Logging outbound activity failed"));
}
}
LeaveCriticalSection (&g_CsOutboundActivityLogging);
}
//
// Notify clients on status change
//
dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS, lpJobQueue);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
lpJobQueue->UniqueId,
dwRes);
}
//
// EndJob() must be called BEFORE we remove the parent job (and recipients)
//
lpJobEntry->LineInfo->State = FPS_AVAILABLE;
//
// We just completed a send job on the device - update counter.
//
(VOID) UpdateDeviceJobsCounter ( lpJobEntry->LineInfo, // Device to update
TRUE, // Sending
-1, // Number of new jobs (-1 = decrease by one)
TRUE); // Enable events
if (!EndJob( lpJobEntry ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("EndJob Failed. (ec: %ld)"),
GetLastError());
}
lpJobQueue->JobEntry = NULL;
if (JS_CANCELED == lpJobQueue->JobStatus)
{
DWORD dwJobId;
dwJobId = lpJobQueue->JobId;
// Job was canceled - decrease reference count
DecreaseJobRefCount (lpJobQueue, TRUE); // This will mark it as JS_DELETING if needed
//
// We need to send the legacy W2K FEI_DELETING notification.
//
if (!CreateFaxEvent(0, FEI_DELETED, dwJobId))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (ec: %ld)"),
FEI_DELETED,
lpJobQueue->JobId,
0,
GetLastError());
}
}
//
// Notify the queue that a device is now available.
//
if (!SetEvent( g_hJobQueueEvent ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to set g_hJobQueueEvent. (ec: %ld)"),
GetLastError());
g_ScanQueueAfterTimeout = TRUE;
}
LeaveCriticalSection ( &g_CsQueue );
LeaveCriticalSection ( &g_CsJob );
//
// Now, send the receipt
//
if (!bRetrying)
{
//
// Job reached final failure state - send negative receipt
//
if (!SendJobReceipt (FALSE, lpJobQueue, tszJobTiffFile))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] SendJobReceipt failed. (ec: %ld)"),
lpJobQueue->JobId,
GetLastError ());
}
EnterCriticalSection (&g_CsQueue);
DecreaseJobRefCount (lpJobQueue, TRUE, TRUE, TRUE); // last TRUE for Preview ref count.
LeaveCriticalSection (&g_CsQueue);
}
if (lstrlen (tszJobTiffFile))
{
//
// Now we can safely delete the job's TIFF file
//
DebugPrintEx(DEBUG_MSG,
TEXT("Deleting per recipient body file %s"),
tszJobTiffFile);
if (!DeleteFile( tszJobTiffFile ))
{
DebugPrintEx(DEBUG_MSG,
TEXT("Failed to delete per recipient body file %s (ec: %ld)"),
tszJobTiffFile,
GetLastError());
}
}
return TRUE;
} // HandleFailedSendJob
//*********************************************************************************
//* Name: StartReceiveJob()
//* Author: Ronen Barenboim
//* Date: June 02, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Starts a receive job on the specified device.
//* PARAMETERS:
//* [IN ] DWORD DeviceId
//* The permanent line id (not TAPI) of the device on which the fax is
//* to be received.
//*
//* RETURN VALUE:
//*
//*********************************************************************************
PJOB_ENTRY
StartReceiveJob(
DWORD DeviceId
)
{
BOOL Failure = TRUE;
PJOB_ENTRY JobEntry = NULL;
PLINE_INFO LineInfo;
BOOL bRes = FALSE;
DWORD rc = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("StartRecieveJob"));
LineInfo = GetTapiLineForFaxOperation(
DeviceId,
JT_RECEIVE,
NULL
);
if (!LineInfo)
{
//
// Could not find a line to send the fax on.
//
rc = GetLastError();
DebugPrintEx(
DEBUG_WRN,
TEXT("Failed to find a line to send the fax on. (ec: %ld)"),
rc);
goto exit;
}
JobEntry = CreateJobEntry(NULL, LineInfo, FALSE);
if (!JobEntry)
{
rc = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create JobEntry. (ec: %ld)"),
rc);
goto exit;
}
__try
{
//
// Call the FSP associated with the line to start a fax job. Note that at this
// point it is not known if the job is send or receive.
//
bRes = LineInfo->Provider->FaxDevStartJob(
LineInfo->hLine,
LineInfo->DeviceId,
(PHANDLE) &JobEntry->InstanceData, // JOB_ENTRY.InstanceData is where the FSP will place its
// job handle (fax handle).
g_StatusCompletionPortHandle,
(ULONG_PTR) LineInfo ); // Note that the completion key provided to the FSP is the LineInfo
// pointer. When the FSP reports status it uses this key thus allowing
// us to know to which line the status belongs.
}
__except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, LineInfo->Provider->FriendlyName, GetExceptionCode()))
{
ASSERT_FALSE;
}
if (!bRes)
{
rc = GetLastError();
DebugPrintEx(DEBUG_ERR,TEXT("FaxDevStartJob failed (ec: %ld)"),GetLastError());
goto exit;
}
//
// Add the new JOB_ENTRY to the job list.
//
EnterCriticalSection( &g_CsJob );
JobEntry->bFSPJobInProgress = TRUE;
InsertTailList( &g_JobListHead, &JobEntry->ListEntry );
LeaveCriticalSection( &g_CsJob );
Failure = FALSE;
//
// Attach the job to the line selected to service it.
//
LineInfo->JobEntry = JobEntry;
exit:
if (Failure)
{ // Failure is initialized to TRUE
if (LineInfo)
{
ReleaseTapiLine( LineInfo, 0 );
}
if (JobEntry)
{
EndJob(JobEntry);
}
JobEntry = NULL;
}
if (ERROR_SUCCESS != rc)
{
SetLastError(rc);
FaxLog(FAXLOG_CATEGORY_INBOUND,
FAXLOG_LEVEL_MIN,
0,
MSG_FAX_RECEIVE_FAILED);
}
return JobEntry;
}
//*********************************************************************************
//* Name: StartRoutingJob()
//* Author: Mooly Beery (MoolyB)
//* Date: July 20, 2000
//*********************************************************************************
//* DESCRIPTION:
//* Starts a routing operation. Must lock g_CsJob and g_CsQueue.
//* PARAMETERS:
//* [IN/OUT ] PJOB_QUEUE lpJobQueueEntry
//* A pointer to the job for which the routing operation is to be
//* performed.
//*
//* RETURN VALUE:
//* TRUE
//* If the operation succeeded.
//* FALSE
//* If the operation failed. Call GetLastError() to get extended error
//* information.
//*
//*********************************************************************************
BOOL
StartRoutingJob(
PJOB_QUEUE lpJobQueueEntry
)
{
DWORD ec = ERROR_SUCCESS;
HANDLE hThread = NULL;
DWORD ThreadId;
DEBUG_FUNCTION_NAME(TEXT("StartRoutingJob"));
//
// We mark the job as IN_PROGRESS so it can not be deleted or routed simultaneously
//
lpJobQueueEntry->JobStatus = JS_INPROGRESS;
hThread = CreateThreadAndRefCount(
NULL,
0,
(LPTHREAD_START_ROUTINE) FaxRouteThread,
(LPVOID) lpJobQueueEntry,
0,
&ThreadId
);
if (hThread == NULL)
{
ec = GetLastError();
DebugPrintEx( DEBUG_ERR,
_T("CreateThreadAndRefCount for FaxRouteThread failed (ec: 0x%0X)"),
ec);
if (!MarkJobAsExpired(lpJobQueueEntry))
{
DEBUG_ERR,
TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
lpJobQueueEntry->JobId,
GetLastError();
}
SetLastError(ec);
return FALSE;
}
DebugPrintEx( DEBUG_MSG,
_T("FaxRouteThread thread created for job id %d ")
_T("(thread id: 0x%0x)"),
lpJobQueueEntry->JobId,
ThreadId);
CloseHandle( hThread );
//
// Create Fax EventEx
//
DWORD dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
lpJobQueueEntry);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx( DEBUG_ERR,
_T("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) ")
_T("failed for job id %ld (ec: %ld)"),
lpJobQueueEntry->JobId,
dwRes);
}
return TRUE;
}
//*********************************************************************************
//* Name: StartSendJob()
//* Author: Ronen Barenboim
//* Date: June 02, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Starts a send operation on a legacy of Extened FSP device.
//* PARAMETERS:
//* [IN/OUT ] PJOB_QUEUE lpJobQueueEntry
//* A pointer to the recipient job for which the send operation is to be
//* performed. For extended sends this is the Anchor recipient.
//*
//* [IN/OUT] PLINE_INFO lpLineInfo
//* A pointer to the line on which the send operatin is to be performed.
//*
//* RETURN VALUE:
//* TRUE
//* If the operation succeeded.
//* FALSE
//* If the operation failed. Call GetLastError() to get extended error
//* information.
//*
//*********************************************************************************
BOOL
StartSendJob(
PJOB_QUEUE lpJobQueueEntry,
PLINE_INFO lpLineInfo
)
{
DWORD rc = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("StartSendJob"));
Assert(lpJobQueueEntry);
Assert(JT_SEND == lpJobQueueEntry->JobType);
Assert(lpLineInfo);
if (FSPI_API_VERSION_1 == lpLineInfo->Provider->dwAPIVersion)
{
if (!StartLegacySendJob(lpJobQueueEntry,lpLineInfo))
{
rc = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("StartLegacySendJob() failed for JobId: %ld (ec: %ld)"),
lpJobQueueEntry->JobId,
GetLastError());
goto exit;
}
}
else
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Unsupported FSPI version (0x%08X) for line : %s "),
lpLineInfo->Provider->dwAPIVersion,
lpLineInfo->DeviceName);
Assert(FALSE);
goto exit;
}
exit:
if (ERROR_SUCCESS != rc) {
SetLastError(rc);
TCHAR strJobID[20]={0};
//
// Convert Job ID into a string. (the string is 18 TCHARs long !!!)
//
HRESULT hr = StringCchPrintf(
strJobID,
ARR_SIZE(strJobID),
TEXT("0x%016I64x"),
lpJobQueueEntry->UniqueId);
if (FAILED(hr))
{
//
// Should never happen, we use large enough buffer.
//
ASSERT_FALSE;
}
FaxLog(
FAXLOG_CATEGORY_OUTBOUND,
FAXLOG_LEVEL_MIN,
7,
MSG_FAX_SEND_FAILED,
lpJobQueueEntry->SenderProfile.lptstrName,
lpJobQueueEntry->SenderProfile.lptstrBillingCode,
lpJobQueueEntry->SenderProfile.lptstrCompany,
lpJobQueueEntry->SenderProfile.lptstrDepartment,
lpLineInfo->DeviceName,
strJobID,
lpJobQueueEntry->lpParentJob->UserName
);
}
return (0 == rc);
}
//*********************************************************************************
//* Name: StartLegacySendJob()
//* Author: Ronen Barenboim
//* Date: June 02, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Starts the operation of sending a fax on a legacy FSP device.
//* - creates the JOB_ENTRY
//* - calls FaxDevStartJob()
//* - calls SendDocument() to actually send the document
//* - calls EndJob() if anything goes wrong.
//*
//* PARAMETERS:
//* [XXX] PJOB_QUEUE lpJobQueue
//* A pointer to the recipient job for the send operation is to be started.
//* [XXX] PLINE_INFO lpLineInfo
//* A pointer to the LINE_INFO of the line on which the fax is to be sent.
//*
//*
//* RETURN VALUE:
//* TRUE if the operation succeeded.
//* FALSE if it failed. Call GetLastError() to get extended error information.
//*
//*********************************************************************************
PJOB_ENTRY StartLegacySendJob(
PJOB_QUEUE lpJobQueue,
PLINE_INFO lpLineInfo
)
{
PJOB_ENTRY lpJobEntry = NULL;
DWORD rc = 0;
DWORD dwRes;
DEBUG_FUNCTION_NAME(TEXT("StartLegacySendJob"));
Assert(JT_SEND == lpJobQueue->JobType);
Assert(FSPI_API_VERSION_1 == lpLineInfo->Provider->dwAPIVersion);
lpJobEntry = CreateJobEntry(lpJobQueue, lpLineInfo, TRUE);
if (!lpJobEntry)
{
rc = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create JobEntry for JobId: %ld. (ec: %ld)"),
lpJobQueue->JobId,
rc);
goto Error;
}
lpJobQueue->JobStatus = JS_INPROGRESS;
//
// Add the new JOB_ENTRY to the job list.
//
EnterCriticalSection( &g_CsJob );
InsertTailList( &g_JobListHead, &lpJobEntry->ListEntry );
LeaveCriticalSection( &g_CsJob );
//
// Attach the job to the line selected to service it.
//
lpLineInfo->JobEntry = lpJobEntry;
lpJobQueue->JobEntry = lpJobEntry;
__try
{
//
// Call the FSP associated with the line to start a fax job. Note that at this
// point it is not known if the job is send or receive.
//
if (lpLineInfo->Provider->FaxDevStartJob(
lpLineInfo->hLine,
lpLineInfo->DeviceId,
(PHANDLE) &lpJobEntry->InstanceData, // JOB_ENTRY.InstanceData is where the FSP will place its
// job handle (fax handle).
g_StatusCompletionPortHandle,
(ULONG_PTR) lpLineInfo )) // Note that the completion key provided to the FSP is the LineInfo
// pointer. When the FSP reports status it uses this key thus allowing
// us to know to which line the status belongs.
{
DebugPrintEx(
DEBUG_MSG,
TEXT("FaxDevStartJob() Successfuly called for JobId: %ld)"),
lpJobQueue->JobId);
lpJobEntry->bFSPJobInProgress = TRUE;
}
else
{
rc = GetLastError();
DebugPrintEx(DEBUG_ERR,TEXT("FaxDevStartJob() failed (ec: %ld)"),rc);
if (0 == rc)
{
//
// FSP failed to report last error so we set our own.
//
DebugPrintEx(DEBUG_ERR,TEXT("FaxDevStartJob() failed but reported 0 for last error"));
rc = ERROR_GEN_FAILURE;
}
goto Error;
}
}
__except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, lpLineInfo->Provider->FriendlyName, GetExceptionCode()))
{
ASSERT_FALSE;
}
//
// start the send job
//
rc = SendDocument(
lpJobEntry,
lpJobQueue->FileName
);
if (rc)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("SendDocument failed for JobId: %ld (ec: %ld)"),
lpJobQueue->JobId,
rc);
goto Error;
}
Assert (0 == rc);
goto Exit;
Error:
Assert( 0 != rc);
if (lpJobEntry)
{
if (!EndJob(lpJobEntry))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("EndJob() failed for JobId: %ld (ec: %ld)"),
lpJobQueue->JobId,
GetLastError());
}
lpJobEntry = NULL;
lpJobQueue->JobEntry = NULL;
}
else
{
//
// Release the line
//
if (!ReleaseTapiLine(lpLineInfo, NULL))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("ReleaseTapiLine() failed (ec: %ld)"),
GetLastError());
}
}
//
// set the job into the retries exceeded state
//
if (0 == lpJobQueue->dwLastJobExtendedStatus)
{
//
// Job was never really executed - this is a fatal error
//
lpJobQueue->dwLastJobExtendedStatus = FSPI_ES_FATAL_ERROR;
lpJobQueue->ExStatusString[0] = L'\0';
}
if (!MarkJobAsExpired(lpJobQueue))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
lpJobQueue->JobId,
GetLastError());
}
//
// Notify clients on status change
//
dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS, lpJobQueue);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
_T("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
lpJobQueue->UniqueId,
dwRes);
}
Exit:
if (rc)
{
SetLastError(rc);
}
return lpJobEntry;
}
//*********************************************************************************
//* Name: UpdateJobStatus()
//* Author: Ronen Barenboim
//* Date: June 01, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Updated the FSPI job status kept in the job entry.
//* Generates legacy API event and new events as required.
//* PARAMETERS:
//* [OUT] PJOB_ENTRY lpJobEntry
//* The job entry whose FSPI status is to be udpated.
//*
//* [IN] LPCFSPI_JOB_STATUS lpcFSPJobStatus
//* The new FSPI job status.
//*
//* RETURN VALUE:
//* TRUE if the operation succeeded.
//* FALSE if the operation failed. Call GetLastError() to get extended error
//* information.
//* Remarks:
//* The function fress the last FSPI job status held in the job entry
//* (if any).
//*********************************************************************************
BOOL UpdateJobStatus(
PJOB_ENTRY lpJobEntry,
LPCFSPI_JOB_STATUS lpcFSPJobStatus
)
{
DWORD ec = 0;
DWORD dwEventId;
DWORD Size = 0;
HINSTANCE hLoadInstance = NULL;
DEBUG_FUNCTION_NAME(TEXT("UpdateJobStatus"));
Assert(lpJobEntry);
Assert(lpcFSPJobStatus);
Assert (lpJobEntry->lpJobQueueEntry);
EnterCriticalSection( &g_CsJob );
DebugPrintEx(
DEBUG_MSG,
TEXT("dwJobStatus: 0x%08X dwExtendedStatus: 0x%08X"),
lpcFSPJobStatus->dwJobStatus,
lpcFSPJobStatus->dwExtendedStatus
);
if (TRUE == lpJobEntry->fStopUpdateStatus)
{
DebugPrintEx(
DEBUG_WRN,
TEXT("JobId: %ld. fStopUpdateStatus was set. Not updating status"),
lpJobEntry->lpJobQueueEntry->JobId,
lpJobEntry->lpJobQueueEntry->JobStatus);
LeaveCriticalSection (&g_CsJob);
return TRUE;
}
//
// Map the FSPI job status to an FEI_* event (0 if not event matches the status)
//
dwEventId = MapFSPIJobStatusToEventId(lpcFSPJobStatus);
//
// Note: W2K Fax did issue notifications with EventId == 0 whenever an
// FSP reported proprietry status code. To keep backward compatability
// we keep up this behaviour although it might be regarded as a bug
//
if (!CreateFaxEvent( lpJobEntry->LineInfo->PermanentLineID, dwEventId, lpJobEntry->lpJobQueueEntry->JobId ))
{
if ( TRUE == g_bServiceIsDown)
{
DebugPrintEx(
DEBUG_WRN,
TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (Service is going down)"),
dwEventId,
lpJobEntry->lpJobQueueEntry->JobId,
lpJobEntry->LineInfo->PermanentLineID
);
}
else
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (ec: %ld)"),
dwEventId,
lpJobEntry->lpJobQueueEntry->JobId,
lpJobEntry->LineInfo->PermanentLineID,
GetLastError());
Assert(FALSE);
}
}
lpJobEntry->FSPIJobStatus.dwJobStatus = lpcFSPJobStatus->dwJobStatus;
lpJobEntry->FSPIJobStatus.dwExtendedStatus = lpcFSPJobStatus->dwExtendedStatus;
lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId = lpcFSPJobStatus->dwExtendedStatusStringId;
if (lpcFSPJobStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_PAGECOUNT)
{
lpJobEntry->FSPIJobStatus.dwPageCount = lpcFSPJobStatus->dwPageCount;
lpJobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_PAGECOUNT;
}
if (lpcFSPJobStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_TRANSMISSION_START)
{
lpJobEntry->FSPIJobStatus.tmTransmissionStart = lpcFSPJobStatus->tmTransmissionStart;
lpJobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_TRANSMISSION_START;
}
if (lpcFSPJobStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_TRANSMISSION_END)
{
lpJobEntry->FSPIJobStatus.tmTransmissionEnd = lpcFSPJobStatus->tmTransmissionEnd;
lpJobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_TRANSMISSION_END;
}
if (NULL != lpcFSPJobStatus->lpwstrRemoteStationId)
{
if (!ReplaceStringWithCopy(&lpJobEntry->FSPIJobStatus.lpwstrRemoteStationId,
lpcFSPJobStatus->lpwstrRemoteStationId))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("ReplaceStringWithCopy() failed. (ec: %ld)"),
GetLastError());
}
}
if (NULL != lpcFSPJobStatus->lpwstrCallerId)
{
if (!ReplaceStringWithCopy(&lpJobEntry->FSPIJobStatus.lpwstrCallerId,
lpcFSPJobStatus->lpwstrCallerId))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("ReplaceStringWithCopy() failed. (ec: %ld)"),
GetLastError());
}
}
if (NULL != lpcFSPJobStatus->lpwstrRoutingInfo)
{
if (!ReplaceStringWithCopy(&lpJobEntry->FSPIJobStatus.lpwstrRoutingInfo,
lpcFSPJobStatus->lpwstrRoutingInfo))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("ReplaceStringWithCopy() failed. (ec: %ld)"),
GetLastError());
}
}
lpJobEntry->ExStatusString[0] = L'\0';
//
// Get extended status string
//
Assert (lpJobEntry->LineInfo != NULL)
if (lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId != 0)
{
Assert (lpJobEntry->FSPIJobStatus.dwExtendedStatus != 0);
if ( !_tcsicmp(lpJobEntry->LineInfo->Provider->szGUID,REGVAL_T30_PROVIDER_GUID_STRING) )
{ // special case where the FSP is our FSP (fxst30.dll).
hLoadInstance = g_hResource;
}
else
{
hLoadInstance = lpJobEntry->LineInfo->Provider->hModule;
}
Size = LoadString (hLoadInstance,
lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId,
lpJobEntry->ExStatusString,
sizeof(lpJobEntry->ExStatusString)/sizeof(WCHAR));
if (Size == 0)
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to load extended status string (ec: %ld) stringid : %ld, Provider: %s"),
ec,
lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId,
lpJobEntry->LineInfo->Provider->ImageName);
lpJobEntry->FSPIJobStatus.fAvailableStatusInfo &= ~FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE;
lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId = 0;
lpJobEntry->FSPIJobStatus.dwExtendedStatus = 0;
goto Error;
}
}
EnterCriticalSection (&g_CsQueue);
DWORD dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
lpJobEntry->lpJobQueueEntry);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
lpJobEntry->lpJobQueueEntry->UniqueId,
dwRes);
}
LeaveCriticalSection (&g_CsQueue);
Assert (0 == ec);
goto Exit;
Error:
Assert( ec !=0 );
Exit:
LeaveCriticalSection( &g_CsJob );
if (ec)
{
SetLastError(ec);
}
return (0 == ec);
}
//*********************************************************************************
//* Name: CheckForJobRetry
//* Author: Ronen Barenboim
//* Date: June 01, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Checks if a recipient job should be retried.
//* Increments the retry count and marks the job as expired if it passed
//* the retry limit.
//* PARAMETERS:
//* [IN/OUT] PJOB_QUEUE lpJobQueue
//* A pointer to the JOB_QUEUE structure of the recipient job.
//* RETURN VALUE:
//* TRUE if the job is to be retried.
//* FALSE if it is not to be retried.
//*********************************************************************************
BOOL CheckForJobRetry (PJOB_QUEUE lpJobQueue)
{
PJOB_ENTRY lpJobEntry;
DWORD dwMaxRetries;
DEBUG_FUNCTION_NAME(TEXT("CheckForJobRetry"));
Assert(lpJobQueue);
lpJobEntry = lpJobQueue->JobEntry;
Assert(lpJobEntry);
//
// Increase the retry count and check if we exceeded maximum retries.
//
EnterCriticalSection (&g_CsConfig);
dwMaxRetries = g_dwFaxSendRetries;
LeaveCriticalSection (&g_CsConfig);
lpJobQueue->SendRetries++;
if (lpJobQueue->SendRetries <= dwMaxRetries)
{
return TRUE;
}
else
{
//
// retries exceeded report that the job is not to be retried
return FALSE;
}
}
//*********************************************************************************
//* Name: FindJobEntryByRecipientNumber()
//* Author: Ronen Barenboim
//* Date: June 01, 1999
//*********************************************************************************
//* DESCRIPTION:
//* Finds the first running job that is destined to a certain number.
//*
//* PARAMETERS:
//* [IN ] LPTSTR lptstrNumber
//* The number to match. This must be in canonical form.
//*
//* RETURN VALUE:
//* A pointer to the JOB_ENTRY in the g_JobListHead list that is destined to
//* the specified number.
//* If no such job is found the return value is NULL.
//*********************************************************************************
PJOB_ENTRY FindJobEntryByRecipientNumber(LPCWSTR lpcwstrNumber)
{
PLIST_ENTRY lpNext;
PJOB_ENTRY lpJobEntry;
DEBUG_FUNCTION_NAME(TEXT("FindJobEntryByRecipientNumber"));
Assert(lpcwstrNumber);
lpNext = g_JobListHead.Flink;
Assert(lpNext);
while ((ULONG_PTR)lpNext != (ULONG_PTR)&g_JobListHead) {
lpJobEntry = CONTAINING_RECORD( lpNext, JOB_ENTRY, ListEntry );
lpNext = lpJobEntry->ListEntry.Flink;
if (JT_SEND == lpJobEntry->lpJobQueueEntry->JobType)
{
if (!_wcsicmp(lpJobEntry->lpJobQueueEntry->RecipientProfile.lptstrFaxNumber, lpcwstrNumber))
{
return lpJobEntry;
}
}
}
return NULL;
}
BOOL CreateJobQueueThread(void)
{
DWORD ThreadId;
DWORD ec = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("CreateJobQueueThread"));
g_hJobQueueThread = CreateThreadAndRefCount(
NULL,
0,
(LPTHREAD_START_ROUTINE) JobQueueThread,
NULL,
0,
&ThreadId
);
if (NULL == g_hJobQueueThread)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create JobQueueThread (ec: %ld)."),
GetLastError());
goto Error;
}
Assert( ERROR_SUCCESS == ec);
goto Exit;
Error:
Assert (ERROR_SUCCESS != ec);
//
// We don't close the already created threads. (They are terminated on process exit).
//
Exit:
if (ERROR_SUCCESS != ec)
{
SetLastError(ec);
}
return (ERROR_SUCCESS == ec);
}
BOOL CreateStatusThreads(void)
{
int i;
DWORD ThreadId;
DWORD ec = ERROR_SUCCESS;
HANDLE hStatusThreads[MAX_STATUS_THREADS];
DEBUG_FUNCTION_NAME(TEXT("CreateStatusThreads"));
memset(hStatusThreads, 0, sizeof(HANDLE)*MAX_STATUS_THREADS);
for (i=0; i<MAX_STATUS_THREADS; i++) {
hStatusThreads[i] = CreateThreadAndRefCount(
NULL,
0,
(LPTHREAD_START_ROUTINE) FaxStatusThread,
NULL,
0,
&ThreadId
);
if (!hStatusThreads[i]) {
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to create status thread %d (CreateThreadAndRefCount)(ec=0x%08x)."),
i,
ec);
goto Error;
}
}
Assert (ERROR_SUCCESS == ec);
goto Exit;
Error:
Assert (ERROR_SUCCESS != ec);
Exit:
//
// Close the thread handles we no longer need them
//
for (i=0; i<MAX_STATUS_THREADS; i++)
{
if(NULL == hStatusThreads[i])
{
continue;
}
if (!CloseHandle(hStatusThreads[i]))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to close thread handle at index %ld [handle = 0x%08X] (ec=0x%08x)."),
i,
hStatusThreads[i],
GetLastError());
}
}
if (ec)
{
SetLastError(ec);
}
return (ERROR_SUCCESS == ec);
}
static
BOOL
SendJobReceipt (
BOOL bPositive,
JOB_QUEUE * lpJobQueue,
LPCTSTR lpctstrAttachment
)
/*++
Routine name : SendJobReceipt
Routine description:
Determines if a receipts should be send and calls SendReceipt accordingly
Author:
Eran Yariv (EranY), Feb, 2000
Arguments:
bPositive [in] - Did current job ended successfully?
lpJobQueue [in] - Pointer to recipient job that just ended
lpctstrAttachment [in] - Job TIFF file to attach (in case of single recipient job only)
Return Value:
TRUE if successful, FALSE otherwise.
In case of failure, call GetLastError() to obtain error code.
--*/
{
BOOL bSingleJobReceipt = FALSE;
DEBUG_FUNCTION_NAME(TEXT("SendJobReceipt)"));
if (lpJobQueue->lpParentJob->dwRecipientJobsCount > 1)
{
//
// Broadcast case
//
if (lpJobQueue->JobParamsEx.dwReceiptDeliveryType & DRT_GRP_PARENT)
{
//
// Broadcast receipt grouping is requested
//
if (IsSendJobReadyForDeleting (lpJobQueue))
{
//
// This is the last job in the broadcast, it's time to send a broadcast receipt
//
//
// As receipt sending is async, there still might be a chance that more than one recipient jobs will reach this point
// We must verify that only one receipt is sent per broadcast job
//
EnterCriticalSection (&g_CsQueue);
if (FALSE == lpJobQueue->lpParentJob->fReceiptSent)
{
PJOB_QUEUE pParentJob = lpJobQueue->lpParentJob;
BOOL bPositiveBroadcast =
(pParentJob->dwCompletedRecipientJobsCount == pParentJob->dwRecipientJobsCount) ?
TRUE : FALSE;
//
// set the flag so we will not send duplicate receipts for broadcast
//
lpJobQueue->lpParentJob->fReceiptSent = TRUE;
//
// Leave g_CsQueue so we will not block the service
//
LeaveCriticalSection (&g_CsQueue);
if (!SendReceipt(bPositiveBroadcast,
TRUE,
pParentJob,
pParentJob->FileName))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[Job Id: %ld] Failed to send broadcast receipt. (ec: %ld)"),
lpJobQueue->JobId,
GetLastError());
return FALSE;
}
}
else
{
//
// More than one job reached this point when the broadcast jo was ready for deleting.
// Only on receipt is sent
//
LeaveCriticalSection (&g_CsQueue);
}
}
else
{
//
// More jobs are still not finished, do not send receipt
//
}
}
else
{
//
// This is a recipient part of a broadcast but the user was
// asking for a receipt for every recipient.
//
bSingleJobReceipt = TRUE;
}
}
else
{
//
// This is not a broadcast case
//
bSingleJobReceipt = TRUE;
}
if (bSingleJobReceipt)
{
//
// Send receipt for this job only
//
if (!SendReceipt(bPositive, FALSE, lpJobQueue, lpctstrAttachment))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("[Job Id: %ld] Failed to send POSITIVE receipt. (ec: %ld)"),
lpJobQueue->JobId,
GetLastError());
return FALSE;
}
}
return TRUE;
} // SendJobReceipt
VOID
UpdateDeviceJobsCounter (
PLINE_INFO pLine,
BOOL bSend,
int iInc,
BOOL bNotify
)
/*++
Routine name : UpdateDeviceJobsCounter
Routine description:
Updates the send or receive jobs counter of a device
Author:
Eran Yariv (EranY), Jul, 2000
Arguments:
pLine [in] - Device pointer
bSend [in] - Send counter (FALSE = Receive counter)
iInc [in] - Increase jobs count (negative means decrease)
decrease [in] - Allow events (FAX_EVENT_TYPE_DEVICE_STATUS)
Return Value:
None.
--*/
{
DWORD dwOldCount;
DWORD dwNewCount;
DEBUG_FUNCTION_NAME(TEXT("UpdateDeviceJobsCounter)"));
Assert (pLine);
if (!iInc)
{
//
// No change
//
ASSERT_FALSE;
return;
}
EnterCriticalSection (&g_CsLine);
dwOldCount = bSend ? pLine->dwSendingJobsCount : pLine->dwReceivingJobsCount;
if (0 > iInc)
{
//
// Decrease case
//
if ((int)dwOldCount + iInc < 0)
{
//
// Weird - should never happen
//
ASSERT_FALSE;
iInc = -(int)dwOldCount;
}
}
dwNewCount = (DWORD)((int)dwOldCount + iInc);
if (bSend)
{
pLine->dwSendingJobsCount = dwNewCount;
}
else
{
pLine->dwReceivingJobsCount = dwNewCount;
}
LeaveCriticalSection (&g_CsLine);
if (bNotify && ((0 == dwNewCount) || (0 == dwOldCount)))
{
//
// State change
//
DWORD ec = CreateDeviceEvent (pLine, FALSE);
if (ERROR_SUCCESS != ec)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CreateDeviceEvent() (ec: %lc)"),
ec);
}
}
} // UpdateDeviceJobsCounter