mirror of https://github.com/tongzx/nt5src
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.
2161 lines
49 KiB
2161 lines
49 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
faxmon.c
|
|
|
|
Abstract:
|
|
|
|
Implementation of the following print monitor entry points:
|
|
InitializePrintMonitor
|
|
OpenPort
|
|
ClosePort
|
|
StartDocPort
|
|
EndDocPort
|
|
WritePort
|
|
ReadPort
|
|
|
|
Environment:
|
|
|
|
Windows NT fax print monitor
|
|
|
|
Revision History:
|
|
|
|
05/09/96 -davidx-
|
|
Remove caching of ports from the monitor.
|
|
|
|
02/22/96 -davidx-
|
|
Created it.
|
|
|
|
mm/dd/yy -author-
|
|
description
|
|
|
|
--*/
|
|
|
|
#include "faxmon.h"
|
|
#include "tiff.h"
|
|
#include "faxreg.h"
|
|
#include <splapip.h>
|
|
|
|
//
|
|
// Determine whether we're at the beginning of a TIFF file
|
|
//
|
|
|
|
#define ValidTiffFileHeader(p) \
|
|
(((LPSTR) (p))[0] == 'I' && ((LPSTR) (p))[1] == 'I' && \
|
|
((PBYTE) (p))[2] == 42 && ((PBYTE) (p))[3] == 0)
|
|
|
|
//
|
|
// Read a DWORD value from an unaligned address
|
|
//
|
|
|
|
#define ReadUnalignedDWord(p) *((DWORD UNALIGNED *) (p))
|
|
|
|
//
|
|
// Write a DWORD value to an unaligned address
|
|
//
|
|
|
|
#define WriteUnalignedDWord(p, value) (*((DWORD UNALIGNED *) (p)) = (value))
|
|
|
|
//
|
|
// Fax monitor name string
|
|
//
|
|
|
|
TCHAR faxMonitorName[CCHDEVICENAME] = TEXT("Windows NT Fax Monitor");
|
|
|
|
//
|
|
// DLL instance handle
|
|
//
|
|
|
|
HANDLE ghInstance = NULL;
|
|
|
|
//
|
|
// Retry parameters when failing to connect to the fax service
|
|
// default = infinite retry with 5 seconds interval
|
|
//
|
|
|
|
DWORD connectRetryCount = 0;
|
|
DWORD connectRetryInterval = 5;
|
|
|
|
|
|
|
|
BOOL
|
|
DllEntryPoint(
|
|
HANDLE hModule,
|
|
ULONG ulReason,
|
|
PCONTEXT pContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
DLL initialization procedure.
|
|
|
|
Arguments:
|
|
|
|
hModule - DLL instance handle
|
|
ulReason - Reason for the call
|
|
pContext - Pointer to context (not used by us)
|
|
|
|
Return Value:
|
|
|
|
TRUE if DLL is initialized successfully, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (ulReason) {
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
ghInstance = hModule;
|
|
LoadString(ghInstance, IDS_FAX_MONITOR_NAME, faxMonitorName, CCHDEVICENAME);
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
LPMONITOREX
|
|
InitializePrintMonitor(
|
|
LPTSTR pRegistryRoot
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the print monitor
|
|
|
|
Arguments:
|
|
|
|
pRegistryRoot = Points to a string that specifies the registry root for the monitor
|
|
|
|
Return Value:
|
|
|
|
Pointer to a MONITOREX structure which contains function pointers
|
|
to other print monitor entry points. NULL if there is an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
static MONITOREX faxmonFuncs = {
|
|
|
|
sizeof(MONITOR),
|
|
{
|
|
FaxMonEnumPorts,
|
|
FaxMonOpenPort,
|
|
NULL, // OpenPortEx
|
|
FaxMonStartDocPort,
|
|
FaxMonWritePort,
|
|
FaxMonReadPort,
|
|
FaxMonEndDocPort,
|
|
FaxMonClosePort,
|
|
FaxMonAddPort,
|
|
FaxMonAddPortEx,
|
|
FaxMonConfigurePort,
|
|
FaxMonDeletePort,
|
|
NULL, // GetPrinterDataFromPort
|
|
NULL, // SetPortTimeOuts
|
|
}
|
|
};
|
|
|
|
|
|
Trace("InitializePrintMonitor");
|
|
|
|
//
|
|
// Get fax service connection retry parameters from the registry
|
|
//
|
|
|
|
if (pRegistryRoot) {
|
|
|
|
HKEY hRegKey;
|
|
LONG status;
|
|
|
|
status = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
pRegistryRoot,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&hRegKey,
|
|
NULL);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
|
|
connectRetryCount =
|
|
GetRegistryDWord(hRegKey, REGVAL_CONNECT_RETRY_COUNT, connectRetryCount);
|
|
|
|
connectRetryInterval =
|
|
GetRegistryDWord(hRegKey, REGVAL_CONNECT_RETRY_INTERVAL, connectRetryInterval);
|
|
|
|
RegCloseKey(hRegKey);
|
|
}
|
|
}
|
|
|
|
return &faxmonFuncs;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonOpenPort(
|
|
LPTSTR pPortName,
|
|
PHANDLE pHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Provides a port for a newly connected printer
|
|
|
|
Arguments:
|
|
|
|
pName - Points to a string that specifies the port name
|
|
pHandle - Returns a handle to the port
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PFAXPORT pFaxPort = NULL;
|
|
|
|
|
|
Trace("OpenPort");
|
|
Assert(pHandle != NULL && pPortName != NULL);
|
|
|
|
//
|
|
// Get information about the specified port
|
|
//
|
|
|
|
if ((pFaxPort = MemAllocZ(sizeof(FAXPORT))) &&
|
|
(pPortName = DuplicateString(FAX_PORT_NAME)))
|
|
{
|
|
pFaxPort->startSig = pFaxPort->endSig = pFaxPort;
|
|
pFaxPort->pName = pPortName;
|
|
pFaxPort->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
} else {
|
|
|
|
MemFree(pFaxPort);
|
|
pFaxPort = NULL;
|
|
}
|
|
|
|
*pHandle = (HANDLE) pFaxPort;
|
|
return (*pHandle != NULL);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FreeFaxJobInfo(
|
|
PFAXPORT pFaxPort
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free up memory used for maintaining information about the current job
|
|
|
|
Arguments:
|
|
|
|
pFaxPort - Points to a fax port structure
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Close and delete the temporary file if necessary
|
|
//
|
|
|
|
if (pFaxPort->hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
CloseHandle(pFaxPort->hFile);
|
|
pFaxPort->hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (pFaxPort->pFilename) {
|
|
|
|
DeleteFile(pFaxPort->pFilename);
|
|
MemFree(pFaxPort->pFilename);
|
|
pFaxPort->pFilename = NULL;
|
|
}
|
|
|
|
if (pFaxPort->hPrinter) {
|
|
|
|
ClosePrinter(pFaxPort->hPrinter);
|
|
pFaxPort->hPrinter = NULL;
|
|
}
|
|
|
|
MemFree(pFaxPort->pPrinterName);
|
|
pFaxPort->pPrinterName = NULL;
|
|
|
|
MemFree(pFaxPort->pParameters);
|
|
pFaxPort->pParameters = NULL;
|
|
ZeroMemory(&pFaxPort->jobParam, sizeof(pFaxPort->jobParam));
|
|
|
|
//
|
|
// Disconnect from the fax service if necessary
|
|
//
|
|
|
|
if (pFaxPort->hFaxSvc) {
|
|
|
|
if (! pFaxPort->pFaxClose(pFaxPort->hFaxSvc)) {
|
|
Error(("FaxClose failed: %d\n", GetLastError()));
|
|
}
|
|
|
|
FreeLibrary( pFaxPort->hWinFax );
|
|
|
|
pFaxPort->hFaxSvc = NULL;
|
|
pFaxPort->pFaxConnectFaxServerW = NULL;
|
|
pFaxPort->pFaxClose = NULL;
|
|
pFaxPort->pFaxSendDocumentW = NULL;
|
|
pFaxPort->pFaxAccessCheck = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonClosePort(
|
|
HANDLE hPort
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the port specified by hPort when there are no printers connected to it
|
|
|
|
Arguments:
|
|
|
|
hPort - Specifies the handle of the port to be close
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PFAXPORT pFaxPort = (PFAXPORT) hPort;
|
|
|
|
Trace("ClosePort");
|
|
|
|
//
|
|
// Make sure we have a valid handle
|
|
//
|
|
|
|
if (! ValidFaxPort(pFaxPort)) {
|
|
|
|
Error(("Trying to close an invalid fax port handle\n"));
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Free up memory used for maintaining information about the current job
|
|
//
|
|
|
|
FreeFaxJobInfo(pFaxPort);
|
|
|
|
MemFree(pFaxPort->pName);
|
|
MemFree(pFaxPort);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
LPTSTR
|
|
CreateTempFaxFile(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a temporary file in the system spool directory for storing fax data
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
Pointer to the name of the newly created temporary file
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
//TCHAR spoolDir[MAX_PATH];
|
|
//HANDLE hServer;
|
|
LPTSTR pFilename;
|
|
|
|
TCHAR TempDir[MAX_PATH];
|
|
TCHAR FileName[MAX_PATH];
|
|
|
|
//
|
|
// Allocate a memory buffer for holding the temporary filename
|
|
//
|
|
|
|
if (pFilename = MemAlloc(sizeof(TCHAR) * MAX_PATH)) {
|
|
|
|
if (!GetTempPath(sizeof(TempDir)/sizeof(TCHAR),TempDir)||
|
|
!GetTempFileName(TempDir, TEXT("fax"), 0, FileName))
|
|
{
|
|
MemFree(pFilename);
|
|
pFilename = NULL;
|
|
} else {
|
|
lstrcpy(pFilename,FileName);
|
|
}
|
|
|
|
}
|
|
|
|
if (! pFilename)
|
|
Error(("Failed to create temporary file in the spool directory\n"));
|
|
|
|
return pFilename;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
OpenTempFaxFile(
|
|
PFAXPORT pFaxPort,
|
|
BOOL doAppend
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open a handle to the current fax job file associated with a port
|
|
|
|
Arguments:
|
|
|
|
pFaxPort - Points to a fax port structure
|
|
doAppend - Specifies whether to discard existing data in the file or
|
|
append new data to it
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD creationFlags;
|
|
|
|
Assert(pFaxPort->pFilename && pFaxPort->hFile == INVALID_HANDLE_VALUE);
|
|
Verbose(("Temporary fax job file: %ws\n", pFaxPort->pFilename));
|
|
|
|
//
|
|
// Open the file for reading and writing
|
|
//
|
|
|
|
creationFlags = doAppend ? OPEN_ALWAYS : (OPEN_ALWAYS | TRUNCATE_EXISTING);
|
|
|
|
pFaxPort->hFile = CreateFile(pFaxPort->pFilename,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
creationFlags,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
//
|
|
// If we're appending, then move the file pointer to end of file
|
|
//
|
|
|
|
if (doAppend && pFaxPort->hFile != INVALID_HANDLE_VALUE &&
|
|
SetFilePointer(pFaxPort->hFile, 0, NULL, FILE_END) == 0xffffffff)
|
|
{
|
|
Error(("SetFilePointer failed: %d\n", GetLastError()));
|
|
|
|
CloseHandle(pFaxPort->hFile);
|
|
pFaxPort->hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return (pFaxPort->hFile != INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
|
|
|
|
LPCTSTR
|
|
ExtractFaxTag(
|
|
LPCTSTR pTagKeyword,
|
|
LPCTSTR pTaggedStr,
|
|
INT *pcch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the value of for the specified tag in a tagged string.
|
|
|
|
Arguments:
|
|
|
|
pTagKeyword - specifies the interested tag keyword
|
|
pTaggedStr - points to the tagged string to be searched
|
|
pcch - returns the length of the specified tag value (if found)
|
|
|
|
Return Value:
|
|
|
|
Points to the value for the specified tag.
|
|
NULL if the specified tag is not found
|
|
|
|
NOTE:
|
|
|
|
Tagged strings have the following form:
|
|
<tag>value<tag>value
|
|
|
|
The format of tags is defined as:
|
|
<$FAXTAG$ tag-name>
|
|
|
|
There is exactly one space between the tag keyword and the tag name.
|
|
Characters in a tag are case-sensitive.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPCTSTR pValue;
|
|
|
|
if (pValue = _tcsstr(pTaggedStr, pTagKeyword)) {
|
|
|
|
pValue += _tcslen(pTagKeyword);
|
|
|
|
if (pTaggedStr = _tcsstr(pValue, FAXTAG_PREFIX))
|
|
*pcch = (INT)(pTaggedStr - pValue);
|
|
else
|
|
*pcch = _tcslen(pValue);
|
|
}
|
|
|
|
return pValue;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
GetJobInfo(
|
|
PFAXPORT pFaxPort,
|
|
DWORD jobId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve recipient information from the devmode associated with the job
|
|
|
|
Arguments:
|
|
|
|
pFaxPort - Points to a fax port structure
|
|
jobId - Specifies the current job ID
|
|
|
|
Return Value:
|
|
|
|
TRUE if the job parameters are successfully retrieved or
|
|
the fax job is from a downlevel fax client.
|
|
|
|
FALSE if there is an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
JOB_INFO_2 *pJobInfo2;
|
|
LPCTSTR pParameters = NULL;
|
|
|
|
//
|
|
// Retrieve the parameter string associated with the specified job.
|
|
// If there is no job parameter or the parameter contains the tag
|
|
// <$FAXTAG$ DOWNLEVEL>, then we assume the job comes from downlevel client.
|
|
//
|
|
|
|
ZeroMemory(&pFaxPort->jobParam, sizeof(pFaxPort->jobParam));
|
|
pFaxPort->jobParam.SizeOfStruct = sizeof( FAX_JOB_PARAM );
|
|
|
|
if ((pJobInfo2 = MyGetJob(pFaxPort->hPrinter, 2, jobId)) == NULL ||
|
|
(pParameters = pJobInfo2->pParameters) == NULL ||
|
|
_tcsstr(pParameters, FAXTAG_DOWNLEVEL_CLIENT) != NULL)
|
|
{
|
|
MemFree(pJobInfo2);
|
|
return TRUE;
|
|
}
|
|
|
|
if ((pFaxPort->pParameters = DuplicateString(pParameters)) != NULL) {
|
|
|
|
//
|
|
// Tags used to pass information about fax jobs
|
|
//
|
|
|
|
static LPCTSTR faxtagNames[NUM_JOBPARAM_TAGS] = {
|
|
|
|
FAXTAG_RECIPIENT_NUMBER,
|
|
FAXTAG_RECIPIENT_NAME,
|
|
FAXTAG_TSID,
|
|
FAXTAG_SENDER_NAME,
|
|
FAXTAG_SENDER_COMPANY,
|
|
FAXTAG_SENDER_DEPT,
|
|
FAXTAG_BILLING_CODE,
|
|
FAXTAG_WHEN_TO_SEND,
|
|
FAXTAG_SEND_AT_TIME,
|
|
FAXTAG_PROFILE_NAME,
|
|
FAXTAG_EMAIL_NAME
|
|
};
|
|
|
|
LPTSTR WhenToSend = NULL;
|
|
LPTSTR SendAtTime = NULL;
|
|
LPTSTR DeliveryReportType = NULL;
|
|
|
|
LPTSTR *fieldStr[NUM_JOBPARAM_TAGS] = {
|
|
|
|
(LPTSTR *)&pFaxPort->jobParam.RecipientNumber,
|
|
(LPTSTR *)&pFaxPort->jobParam.RecipientName,
|
|
(LPTSTR *)&pFaxPort->jobParam.Tsid,
|
|
(LPTSTR *)&pFaxPort->jobParam.SenderName,
|
|
(LPTSTR *)&pFaxPort->jobParam.SenderCompany,
|
|
(LPTSTR *)&pFaxPort->jobParam.SenderDept,
|
|
(LPTSTR *)&pFaxPort->jobParam.BillingCode,
|
|
&WhenToSend,
|
|
&SendAtTime,
|
|
(LPTSTR *)&pFaxPort->jobParam.DeliveryReportAddress,
|
|
&DeliveryReportType
|
|
};
|
|
|
|
INT fieldLen[NUM_JOBPARAM_TAGS];
|
|
INT count;
|
|
|
|
pParameters = pFaxPort->pParameters;
|
|
Verbose(("JOB_INFO_2.pParameter = %ws\n", pParameters));
|
|
|
|
//
|
|
// Extract individual fields out of the tagged string
|
|
//
|
|
|
|
for (count=0; count < NUM_JOBPARAM_TAGS; count++) {
|
|
|
|
*fieldStr[count] = (LPTSTR)ExtractFaxTag(faxtagNames[count],
|
|
pParameters,
|
|
&fieldLen[count]);
|
|
}
|
|
|
|
//
|
|
// Null-terminate each field
|
|
//
|
|
|
|
for (count=0; count < NUM_JOBPARAM_TAGS; count++) {
|
|
if (*fieldStr[count]) {
|
|
(*fieldStr[count])[fieldLen[count]] = NUL;
|
|
}
|
|
}
|
|
|
|
if (WhenToSend) {
|
|
if (_tcsicmp( WhenToSend, TEXT("cheap") ) == 0) {
|
|
pFaxPort->jobParam.ScheduleAction = JSA_DISCOUNT_PERIOD;
|
|
} else if (_tcsicmp( WhenToSend, TEXT("at") ) == 0) {
|
|
pFaxPort->jobParam.ScheduleAction = JSA_SPECIFIC_TIME;
|
|
}
|
|
}
|
|
|
|
if (SendAtTime) {
|
|
if (_tcslen(SendAtTime) == 5 && SendAtTime[2] == L':' &&
|
|
_istdigit(SendAtTime[0]) && _istdigit(SendAtTime[1]) &&
|
|
_istdigit(SendAtTime[3]) && _istdigit(SendAtTime[4]))
|
|
{
|
|
DWORDLONG FileTime;
|
|
SYSTEMTIME LocalTime;
|
|
INT Minutes;
|
|
INT SendMinutes;
|
|
|
|
SendAtTime[2] = 0;
|
|
|
|
//
|
|
// Calculate the number of minutes from now to send and add that to the current time.
|
|
//
|
|
|
|
GetLocalTime( &LocalTime );
|
|
SystemTimeToFileTime( &LocalTime, (LPFILETIME) &FileTime );
|
|
|
|
SendMinutes = min(23,_ttoi( &SendAtTime[0] )) * 60 + min(59,_ttoi( &SendAtTime[3] ));
|
|
|
|
Minutes = LocalTime.wHour * 60 + LocalTime.wMinute;
|
|
|
|
Minutes = SendMinutes - Minutes;
|
|
|
|
|
|
// Account for passing midnight
|
|
//
|
|
if (Minutes < 0) {
|
|
Minutes += 24 * 60;
|
|
}
|
|
|
|
FileTime += (DWORDLONG)(Minutes * 60I64 * 1000I64 * 1000I64 * 10I64);
|
|
|
|
FileTimeToSystemTime((LPFILETIME) &FileTime, &pFaxPort->jobParam.ScheduleTime );
|
|
|
|
SendAtTime[2] = L':';
|
|
|
|
}
|
|
}
|
|
|
|
if (DeliveryReportType) {
|
|
if (_tcsicmp( DeliveryReportType, TEXT("email") ) == 0) {
|
|
pFaxPort->jobParam.DeliveryReportType = DRT_EMAIL;
|
|
} else if (_tcsicmp( DeliveryReportType, TEXT("inbox") ) == 0) {
|
|
pFaxPort->jobParam.DeliveryReportType = DRT_INBOX;
|
|
}
|
|
}
|
|
|
|
if (pFaxPort->jobParam.RecipientNumber == NULL) {
|
|
|
|
Error(("Missing recipient phone number.\n"));
|
|
SetJob(pFaxPort->hPrinter, jobId, 0, NULL, JOB_CONTROL_PAUSE);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
|
|
MemFree(pJobInfo2);
|
|
|
|
return (pFaxPort->jobParam.RecipientNumber != NULL);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonStartDocPort(
|
|
HANDLE hPort,
|
|
LPTSTR pPrinterName,
|
|
DWORD JobId,
|
|
DWORD Level,
|
|
LPBYTE pDocInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Spooler calls this function to start a new print job on the port
|
|
|
|
Arguments:
|
|
|
|
hPort - Identifies the port
|
|
pPrinterName - Specifies the name of the printer to which the job is being sent
|
|
JobId - Identifies the job being sent by the spooler
|
|
Level - Specifies the DOC_INFO_x level
|
|
pDocInfo - Points to the document information
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PFAXPORT pFaxPort = (PFAXPORT) hPort;
|
|
|
|
|
|
Verbose(("Entering StartDocPort: %d ...\n", JobId));
|
|
|
|
//
|
|
// Make sure we have a valid handle
|
|
//
|
|
|
|
if (! ValidFaxPort(pFaxPort)) {
|
|
|
|
Error(("StartDocPort is given an invalid fax port handle\n"));
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if we're at the beginning of a series of chained jobs
|
|
//
|
|
|
|
if (! pFaxPort->hFaxSvc) {
|
|
|
|
PJOB_INFO_1 pJobInfo1;
|
|
PORT_INFO_3 portInfo3;
|
|
HANDLE hPrinter = NULL;
|
|
BOOL offline = FALSE;
|
|
DWORD count = connectRetryCount;
|
|
DWORD jobStatus = 0;
|
|
|
|
Assert(pFaxPort->pPrinterName == NULL &&
|
|
pFaxPort->hPrinter == NULL &&
|
|
pFaxPort->pParameters == NULL &&
|
|
pFaxPort->pFilename == NULL &&
|
|
pFaxPort->hFile == INVALID_HANDLE_VALUE);
|
|
|
|
//
|
|
// load the winfax dll
|
|
//
|
|
|
|
pFaxPort->hWinFax = LoadLibrary( L"winfax.dll" );
|
|
if (pFaxPort->hWinFax == NULL) {
|
|
Error(("LoadLibrary failed loading winfax.dll\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// get the function addresses
|
|
//
|
|
|
|
pFaxPort->pFaxConnectFaxServerW = (PFAXCONNECTFAXSERVERW) GetProcAddress( pFaxPort->hWinFax, "FaxConnectFaxServerW" );
|
|
pFaxPort->pFaxClose = (PFAXCLOSE) GetProcAddress( pFaxPort->hWinFax, "FaxClose" );
|
|
pFaxPort->pFaxSendDocumentW = (PFAXSENDDOCUMENTW) GetProcAddress( pFaxPort->hWinFax, "FaxSendDocumentW" );
|
|
pFaxPort->pFaxAccessCheck = (PFAXACCESSCHECK) GetProcAddress( pFaxPort->hWinFax, "FaxAccessCheck" );
|
|
|
|
if (pFaxPort->pFaxConnectFaxServerW == NULL ||
|
|
pFaxPort->pFaxClose == NULL ||
|
|
pFaxPort->pFaxSendDocumentW == NULL ||
|
|
pFaxPort->pFaxAccessCheck == NULL) {
|
|
Error(("GetProcAddress failed loading winfax.dll\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Connect to the fax service and obtain a session handle
|
|
//
|
|
|
|
while (! pFaxPort->pFaxConnectFaxServerW(NULL, &pFaxPort->hFaxSvc)) {
|
|
|
|
Error(("FaxConnectFaxServer failed: %d\n", GetLastError()));
|
|
pFaxPort->hFaxSvc = NULL;
|
|
|
|
if (! offline) {
|
|
|
|
portInfo3.dwStatus = PORT_STATUS_OFFLINE;
|
|
portInfo3.pszStatus = NULL;
|
|
portInfo3.dwSeverity = PORT_STATUS_TYPE_WARNING;
|
|
|
|
if (! SetPort(NULL, pFaxPort->pName, 3, (LPBYTE) &portInfo3))
|
|
Error(("SetPort failed: %d\n", GetLastError()));
|
|
}
|
|
|
|
offline = TRUE;
|
|
Sleep(connectRetryInterval * 1000);
|
|
|
|
//
|
|
// Check if the job has been deleted or restarted
|
|
//
|
|
|
|
if (!hPrinter && !OpenPrinter(pPrinterName, &hPrinter, NULL)) {
|
|
|
|
Error(("OpenPrinter failed: %d\n", GetLastError()));
|
|
hPrinter = NULL;
|
|
|
|
} else if (pJobInfo1 = MyGetJob(hPrinter, 1, JobId)) {
|
|
|
|
jobStatus = pJobInfo1->Status;
|
|
|
|
}
|
|
|
|
MemFree(pJobInfo1);
|
|
|
|
if (--count == 0 || (jobStatus & (JOB_STATUS_DELETING|
|
|
JOB_STATUS_DELETED|
|
|
JOB_STATUS_RESTART)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the offline status on the port
|
|
//
|
|
|
|
if (offline) {
|
|
|
|
portInfo3.dwStatus = 0;
|
|
portInfo3.pszStatus = NULL;
|
|
portInfo3.dwSeverity = PORT_STATUS_TYPE_INFO;
|
|
|
|
if (! SetPort(NULL, pFaxPort->pName, 3, (LPBYTE) &portInfo3)) {
|
|
Error(("SetPort failed: %d\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
if (hPrinter) {
|
|
ClosePrinter(hPrinter);
|
|
}
|
|
|
|
if (pFaxPort->hFaxSvc) {
|
|
if (!pFaxPort->pFaxAccessCheck(pFaxPort->hFaxSvc, FAX_JOB_SUBMIT) ) {
|
|
FreeFaxJobInfo(pFaxPort);
|
|
Error(("FaxAccessCheck failed : %d\n", GetLastError() ));
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
// HANDLE hToken;
|
|
|
|
//
|
|
// The monitor runs in the context of the current job's owner.
|
|
// In order to create temporary files in the spool directory,
|
|
// we need to revert to the spooler context first.
|
|
//
|
|
|
|
/* if (! (hToken = RevertToPrinterSelf()))
|
|
Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
|
|
*/
|
|
//
|
|
// Remember the printer name because we'll need it at EndDocPort time.
|
|
// Get a temporary filename and open it for reading and writing.
|
|
// Remember other job related information
|
|
//
|
|
|
|
if (! (pFaxPort->pPrinterName = DuplicateString(pPrinterName)) ||
|
|
! OpenPrinter(pPrinterName, &pFaxPort->hPrinter, NULL) ||
|
|
! GetJobInfo(pFaxPort, JobId) ||
|
|
! (pFaxPort->pFilename = CreateTempFaxFile()) ||
|
|
! OpenTempFaxFile(pFaxPort, FALSE))
|
|
{
|
|
FreeFaxJobInfo(pFaxPort);
|
|
} else
|
|
pFaxPort->jobId = pFaxPort->nextJobId = JobId;
|
|
|
|
//
|
|
// Switch back to the original context if necessary
|
|
//
|
|
|
|
/* if (hToken && !ImpersonatePrinterClient(hToken))
|
|
Error(("ImpersonatePrinterClient failed: %d\n", GetLastError())); */
|
|
}
|
|
|
|
} else {
|
|
|
|
Assert(pFaxPort->jobId == JobId);
|
|
}
|
|
|
|
return (pFaxPort->hFaxSvc != NULL);
|
|
}
|
|
|
|
|
|
|
|
INT
|
|
FixUpFaxFile(
|
|
PFAXPORT pFaxPort
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fixed up the saved print job data into a well-formed TIFF file
|
|
|
|
Arguments:
|
|
|
|
pFaxPort - Points to a fax port structure
|
|
|
|
Return Value:
|
|
|
|
error code FAXERR_*
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD fileSize;
|
|
PBYTE pFileEnd, pFileHdr;
|
|
PBYTE pFileView = NULL;
|
|
HANDLE hFileMap = NULL;
|
|
INT result = FAXERR_BAD_TIFF;
|
|
|
|
//
|
|
// Get the size of print job file
|
|
//
|
|
|
|
FlushFileBuffers(pFaxPort->hFile);
|
|
|
|
if ((fileSize = GetFileSize(pFaxPort->hFile, NULL)) == 0)
|
|
return FAXERR_IGNORE;
|
|
|
|
if (fileSize == 0xffffffff)
|
|
return FAXERR_FATAL;
|
|
|
|
__try {
|
|
|
|
//
|
|
// Map the fax job file into memory
|
|
//
|
|
|
|
if ((hFileMap = CreateFileMapping(pFaxPort->hFile, NULL, PAGE_READWRITE, 0, 0, NULL)) &&
|
|
(pFileHdr = pFileView = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, fileSize)) &&
|
|
ValidTiffFileHeader(pFileHdr))
|
|
{
|
|
DWORD ifdOffset, maxOffset, fileOffset;
|
|
PBYTE pIfdOffset;
|
|
|
|
//
|
|
// A fax print job may contain multiple TIFF files. Each each iteration
|
|
// of the outer loop below deals with a single embedded TIFF file.
|
|
//
|
|
|
|
pFileEnd = pFileHdr + fileSize;
|
|
ifdOffset = ReadUnalignedDWord(pFileHdr + sizeof(DWORD));
|
|
|
|
do {
|
|
|
|
Verbose(("Reading embedded TIFF file ...\n"));
|
|
maxOffset = 0;
|
|
fileOffset = (DWORD)(pFileHdr - pFileView);
|
|
|
|
//
|
|
// Each iteration of the following loops processes one IFD
|
|
// from an embedded TIFF file.
|
|
//
|
|
|
|
do {
|
|
|
|
PTIFF_IFD pIfd;
|
|
PTIFF_TAG pIfdEntry;
|
|
INT ifdCount;
|
|
DWORD size, index, stripCount = 0;
|
|
PDWORD pStripOffsets = NULL;
|
|
|
|
pIfd = (PTIFF_IFD) (pFileHdr + ifdOffset);
|
|
Assert( (PBYTE) pIfd < pFileEnd);
|
|
if ((PBYTE) pIfd >= pFileEnd) {
|
|
result = FAXERR_FATAL;
|
|
__leave;
|
|
}
|
|
ifdOffset += sizeof(WORD) + pIfd->wEntries * sizeof(TIFF_TAG);
|
|
pIfdOffset = pFileHdr + ifdOffset;
|
|
|
|
Assert(pIfdOffset < pFileEnd);
|
|
if (pIfdOffset >= pFileEnd) {
|
|
result = FAXERR_FATAL;
|
|
__leave;
|
|
}
|
|
|
|
if ((ifdOffset + sizeof(DWORD)) > maxOffset)
|
|
maxOffset = ifdOffset + sizeof(DWORD);
|
|
|
|
//
|
|
// We should add the file offset to any non-zero IFD offset
|
|
//
|
|
|
|
if ((ifdOffset = ReadUnalignedDWord(pIfdOffset)) != 0)
|
|
WriteUnalignedDWord(pIfdOffset, ifdOffset + fileOffset);
|
|
|
|
//
|
|
// Now go through each IFD entry and calculate the largest offset
|
|
//
|
|
|
|
pIfdEntry = (PTIFF_TAG) ((PBYTE) pIfd + sizeof(WORD));
|
|
ifdCount = pIfd->wEntries;
|
|
|
|
Verbose((" Reading IFD: %d entries ...\n", ifdCount));
|
|
|
|
while (ifdCount-- > 0) {
|
|
|
|
//
|
|
// Figure the size of various TIFF data types
|
|
//
|
|
|
|
switch (pIfdEntry->DataType) {
|
|
|
|
case TIFF_ASCII:
|
|
case TIFF_BYTE:
|
|
case TIFF_SBYTE:
|
|
case TIFF_UNDEFINED:
|
|
|
|
size = 1;
|
|
break;
|
|
|
|
case TIFF_SHORT:
|
|
case TIFF_SSHORT:
|
|
|
|
size = 2;
|
|
break;
|
|
|
|
case TIFF_LONG:
|
|
case TIFF_SLONG:
|
|
case TIFF_FLOAT:
|
|
|
|
size = 4;
|
|
break;
|
|
|
|
case TIFF_RATIONAL:
|
|
case TIFF_SRATIONAL:
|
|
case TIFF_DOUBLE:
|
|
|
|
size = 8;
|
|
break;
|
|
|
|
default:
|
|
|
|
Warning(("Unknown TIFF data type: %d\n", pIfdEntry->DataType));
|
|
size = 1;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Look for StripOffsets and StripByteCounts tags
|
|
//
|
|
|
|
if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS ||
|
|
pIfdEntry->TagId == TIFFTAG_STRIPBYTECOUNTS)
|
|
{
|
|
DWORD n = pIfdEntry->DataCount;
|
|
|
|
if ((pIfdEntry->DataType == TIFF_LONG) &&
|
|
(stripCount == 0 || stripCount == n) &&
|
|
(pStripOffsets || (pStripOffsets = MemAllocZ(sizeof(DWORD)*n))))
|
|
{
|
|
if ((stripCount = n) == 1) {
|
|
|
|
pStripOffsets[0] += pIfdEntry->DataOffset;
|
|
|
|
if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS)
|
|
pIfdEntry->DataOffset += fileOffset;
|
|
|
|
} else {
|
|
|
|
DWORD UNALIGNED *p;
|
|
|
|
Verbose(("Multiple strips per page: %d\n", n));
|
|
|
|
p = (DWORD UNALIGNED *) (pFileHdr + pIfdEntry->DataOffset);
|
|
|
|
for (index=0; index < stripCount; index++) {
|
|
|
|
n = *p;
|
|
pStripOffsets[index] += n;
|
|
|
|
if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS)
|
|
*p = n + fileOffset;
|
|
|
|
p = (DWORD UNALIGNED *) ((LPBYTE) p + sizeof(DWORD));
|
|
}
|
|
}
|
|
|
|
} else
|
|
Error(("Bad StripOffsets/StripByteCounts tag\n"));
|
|
}
|
|
|
|
//
|
|
// For a composite value, IFDENTRY.value is an offset
|
|
//
|
|
|
|
if (size * pIfdEntry->DataCount > sizeof(DWORD)) {
|
|
|
|
if (pIfdEntry->DataOffset > maxOffset)
|
|
maxOffset = pIfdEntry->DataOffset;
|
|
|
|
pIfdEntry->DataOffset += fileOffset;
|
|
}
|
|
|
|
pIfdEntry++;
|
|
}
|
|
|
|
//
|
|
// Make sure to skip the image data when search for the next file
|
|
//
|
|
|
|
if (pStripOffsets) {
|
|
|
|
for (index=0; index < stripCount; index++) {
|
|
|
|
if (pStripOffsets[index] > maxOffset)
|
|
maxOffset = pStripOffsets[index];
|
|
}
|
|
|
|
MemFree(pStripOffsets);
|
|
}
|
|
|
|
} while (ifdOffset);
|
|
|
|
//
|
|
// Search for the beginning of next TIFF file
|
|
//
|
|
|
|
pFileHdr += maxOffset;
|
|
|
|
while (pFileHdr < pFileEnd) {
|
|
|
|
if (ValidTiffFileHeader(pFileHdr)) {
|
|
|
|
//
|
|
// Modify the offset in the last IFD
|
|
//
|
|
|
|
ifdOffset = ReadUnalignedDWord(pFileHdr + sizeof(DWORD));
|
|
WriteUnalignedDWord(pIfdOffset, ifdOffset + (DWORD)(pFileHdr - pFileView));
|
|
break;
|
|
}
|
|
|
|
pFileHdr++;
|
|
}
|
|
|
|
} while (pFileHdr < pFileEnd);
|
|
|
|
result = FAXERR_NONE;
|
|
}
|
|
|
|
} __finally {
|
|
|
|
//
|
|
// Perform necessary cleanup before returning to caller
|
|
//
|
|
|
|
if (pFileView)
|
|
UnmapViewOfFile(pFileView);
|
|
|
|
if (hFileMap)
|
|
CloseHandle(hFileMap);
|
|
|
|
CloseHandle(pFaxPort->hFile);
|
|
pFaxPort->hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
INT
|
|
CheckJobRestart(
|
|
PFAXPORT pFaxPort
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if the job has been restarted.
|
|
If not, get the ID of the next job in the chain.
|
|
|
|
Arguments:
|
|
|
|
pFaxPort - Points to a fax port structure
|
|
|
|
Return Value:
|
|
|
|
FAXERR_RESTART or FAXERR_NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
JOB_INFO_3 *pJobInfo3;
|
|
JOB_INFO_2 *pJobInfo2;
|
|
INT status = FAXERR_NONE;
|
|
|
|
//
|
|
// If not, get the ID of the next job in the chain.
|
|
//
|
|
|
|
Verbose(("Job chain: id = %d\n", pFaxPort->nextJobId));
|
|
|
|
if (pJobInfo3 = MyGetJob(pFaxPort->hPrinter, 3, pFaxPort->jobId)) {
|
|
|
|
pFaxPort->nextJobId = pJobInfo3->NextJobId;
|
|
MemFree(pJobInfo3);
|
|
|
|
} else
|
|
pFaxPort->nextJobId = 0;
|
|
|
|
//
|
|
// Determine whether the job has been restarted or deleted
|
|
//
|
|
|
|
if (pJobInfo2 = MyGetJob(pFaxPort->hPrinter, 2, pFaxPort->jobId)) {
|
|
|
|
if (pJobInfo2->Status & (JOB_STATUS_RESTART | JOB_STATUS_DELETING))
|
|
status = FAXERR_RESTART;
|
|
|
|
MemFree(pJobInfo2);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FaxMonEndDocPort(
|
|
HANDLE hPort
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Spooler calls this function at the end of a print job
|
|
|
|
Arguments:
|
|
|
|
hPort - Identifies the port
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PFAXPORT pFaxPort = (PFAXPORT) hPort;
|
|
INT status;
|
|
LPTSTR pAtSign, pNewRecipName = NULL;
|
|
//HANDLE hToken;
|
|
DWORD FaxJobId;
|
|
BOOL Rslt;
|
|
JOB_INFO_2 *pJobInfo2;
|
|
|
|
|
|
Trace("EndDocPort");
|
|
|
|
//
|
|
// Make sure we have a valid handle
|
|
//
|
|
|
|
if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) {
|
|
|
|
Error(("EndDocPort is given an invalid fax port handle\n"));
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if the job has been restarted. If not, get the ID of
|
|
// the next job in the chain.
|
|
//
|
|
|
|
if ((status = CheckJobRestart(pFaxPort)) != FAXERR_NONE)
|
|
goto ExitEndDocPort;
|
|
|
|
//
|
|
// Check if we're at the end of a job chain
|
|
//
|
|
|
|
if (pFaxPort->nextJobId != 0 && pFaxPort->pParameters != NULL) {
|
|
|
|
SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The monitor runs in the context of the current job's owner.
|
|
// In order to create temporary files in the spool directory,
|
|
// we need to revert to the spooler context first.
|
|
//
|
|
|
|
/* if (! (hToken = RevertToPrinterSelf()))
|
|
Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
|
|
*/
|
|
//
|
|
// Check if we're dealing with fax jobs from win31 or win95 clients
|
|
//
|
|
|
|
if ((pFaxPort->pParameters == NULL) &&
|
|
(status = ProcessDownlevelFaxJob(pFaxPort)) != FAXERR_NONE)
|
|
{
|
|
goto ExitEndDocPort;
|
|
}
|
|
|
|
//
|
|
// Fix up the temporary fax data into a properly formatted TIFF file.
|
|
//
|
|
|
|
if ((status = FixUpFaxFile(pFaxPort)) != FAXERR_NONE) {
|
|
goto ExitEndDocPort;
|
|
}
|
|
|
|
//
|
|
// Call the fax service to send the TIFF file
|
|
//
|
|
|
|
#if DBG
|
|
|
|
if (_debugLevel > 0) {
|
|
|
|
DbgPrint("Send document to fax service:\n");
|
|
DbgPrint(" Printer Name: %ws\n", pFaxPort->pPrinterName);
|
|
DbgPrint(" Job ID: %d\n", pFaxPort->jobId);
|
|
DbgPrint(" File Name: %ws\n", pFaxPort->pFilename);
|
|
DbgPrint(" Recipient Number: %ws\n", pFaxPort->jobParam.RecipientNumber);
|
|
DbgPrint(" Recipient Name: %ws\n", pFaxPort->jobParam.RecipientName);
|
|
DbgPrint(" TSID: %ws\n", pFaxPort->jobParam.Tsid);
|
|
DbgPrint(" Sender Name: %ws\n", pFaxPort->jobParam.SenderName);
|
|
DbgPrint(" Sender Company: %ws\n", pFaxPort->jobParam.SenderCompany);
|
|
DbgPrint(" Sender Dept: %ws\n", pFaxPort->jobParam.SenderDept);
|
|
DbgPrint(" Billing Code: %ws\n", pFaxPort->jobParam.BillingCode);
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// fixup the fax address
|
|
//
|
|
|
|
if (pAtSign = _tcschr(pFaxPort->jobParam.RecipientNumber, TEXT('@'))) {
|
|
|
|
*pAtSign++ = NUL;
|
|
|
|
if (pFaxPort->jobParam.RecipientName == NULL)
|
|
pNewRecipName = (LPTSTR) pFaxPort->jobParam.RecipientName = (LPTSTR)DuplicateString(pFaxPort->jobParam.RecipientNumber);
|
|
|
|
_tcscpy((LPTSTR)pFaxPort->jobParam.RecipientNumber, pAtSign);
|
|
}
|
|
|
|
//
|
|
// send the fax
|
|
//
|
|
|
|
pJobInfo2 = MyGetJob( pFaxPort->hPrinter, 2, pFaxPort->jobId );
|
|
if (pJobInfo2) {
|
|
pFaxPort->jobParam.DocumentName = pJobInfo2->pDocument;
|
|
} else {
|
|
pFaxPort->jobParam.DocumentName = NULL;
|
|
}
|
|
|
|
/* if (hToken && !ImpersonatePrinterClient(hToken)) {
|
|
Error(("ImpersonatePrinterClient failed: %d\n", GetLastError()));
|
|
} */
|
|
|
|
pFaxPort->jobParam.Reserved[0] = 0xffffffff;
|
|
pFaxPort->jobParam.Reserved[1] = pFaxPort->jobId;
|
|
|
|
Rslt = pFaxPort->pFaxSendDocumentW( pFaxPort->hFaxSvc, pFaxPort->pFilename, &pFaxPort->jobParam, NULL, &FaxJobId );
|
|
|
|
/* if (! (hToken = RevertToPrinterSelf())) {
|
|
Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
|
|
} */
|
|
|
|
if (pJobInfo2) {
|
|
MemFree( pJobInfo2 );
|
|
pFaxPort->jobParam.DocumentName = NULL;
|
|
}
|
|
|
|
if (Rslt) {
|
|
status = FAXERR_NONE;
|
|
SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
|
|
DeleteFile( pFaxPort->pFilename );
|
|
} else {
|
|
status = GetLastError();
|
|
Error(("FaxSendDocumentForSpooler failed: %d\n", GetLastError()));
|
|
}
|
|
|
|
ExitEndDocPort:
|
|
|
|
if (status == FAXERR_NONE) {
|
|
|
|
//
|
|
// If the job was successfully sent to the fax service, then
|
|
// the service will delete the temporary file when it's done
|
|
// with it. So we don't need to delete it here.
|
|
//
|
|
|
|
MemFree(pFaxPort->pFilename);
|
|
pFaxPort->pFilename = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the job wasn't successfully sent to the fax service,
|
|
// inform the spooler that there is an error on the job.
|
|
//
|
|
// Or if the print job has no data, simply ignore it.
|
|
//
|
|
|
|
switch (status) {
|
|
|
|
case FAXERR_RESTART:
|
|
|
|
Warning(("Job restarted or deleted: id = %d\n", pFaxPort->jobId));
|
|
|
|
case FAXERR_IGNORE:
|
|
|
|
SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
|
|
break;
|
|
|
|
default:
|
|
|
|
Error(("Error sending fax job: id = %d\n", pFaxPort->jobId));
|
|
|
|
SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_PAUSE);
|
|
SetJobStatus(pFaxPort->hPrinter, pFaxPort->jobId, status);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pNewRecipName) {
|
|
|
|
MemFree(pNewRecipName);
|
|
pFaxPort->jobParam.RecipientName = NULL;
|
|
}
|
|
|
|
FreeFaxJobInfo(pFaxPort);
|
|
|
|
//
|
|
// Switch back to the original context if necessary
|
|
//
|
|
|
|
/* if (hToken && !ImpersonatePrinterClient(hToken))
|
|
Error(("ImpersonatePrinterClient failed: %d\n", GetLastError()));*/
|
|
|
|
return (status < FAXERR_SPECIAL);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonWritePort(
|
|
HANDLE hPort,
|
|
LPBYTE pBuffer,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbWritten
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes data to a port
|
|
|
|
Arguments:
|
|
|
|
hPort - Identifies the port
|
|
pBuffer - Points to a buffer that contains data to be written to the port
|
|
cbBuf - Specifies the size in bytes of the buffer
|
|
pcbWritten - Returns the count of bytes successfully written to the port
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PFAXPORT pFaxPort = (PFAXPORT) hPort;
|
|
|
|
//
|
|
// Make sure we have a valid handle
|
|
//
|
|
|
|
if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) {
|
|
|
|
Error(("WritePort is given an invalid fax port handle\n"));
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
Assert(pFaxPort->hFile != INVALID_HANDLE_VALUE);
|
|
return WriteFile(pFaxPort->hFile, pBuffer, cbBuf, pcbWritten, NULL);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonReadPort(
|
|
HANDLE hPort,
|
|
LPBYTE pBuffer,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbRead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads data from the port
|
|
|
|
Arguments:
|
|
|
|
hPort - Identifies the port
|
|
pBuffer - Points to a buffer where data read from the printer can be written
|
|
cbBuf - Specifies the size in bytes of the buffer pointed to by pBuffer
|
|
pcbRead - Returns the number of bytes successfully read from the port
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
Trace("ReadPort");
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonEnumPorts(
|
|
LPTSTR pServerName,
|
|
DWORD Level,
|
|
LPBYTE pPorts,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerates the ports available on the specified server
|
|
|
|
Arguments:
|
|
|
|
pServerName - Specifies the name of the server whose ports are to be enumerated
|
|
dwLevel - Specifies the version of the structure to which pPorts points
|
|
pPorts - Points to an array of PORT_INFO_1 structures where data describing
|
|
the available ports will be writteno
|
|
cbBuf - Specifies the size in bytes of the buffer to which pPorts points
|
|
pcbNeeded - Returns the required buffer size identified by pPorts
|
|
pReturned - Returns the number of PORT_INFO_1 structures returned
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
#define MAX_DESC_LEN 64
|
|
|
|
{
|
|
TCHAR portDescStr[MAX_DESC_LEN];
|
|
INT descStrSize, faxmonNameSize;
|
|
DWORD cbNeeded;
|
|
BOOL status = TRUE;
|
|
PORT_INFO_1 *pPortInfo1 = (PORT_INFO_1 *) pPorts;
|
|
PORT_INFO_2 *pPortInfo2 = (PORT_INFO_2 *) pPorts;
|
|
INT strSize;
|
|
|
|
|
|
Trace("EnumPorts");
|
|
|
|
if (pcbNeeded == NULL || pReturned == NULL || (pPorts == NULL && cbBuf != 0)) {
|
|
|
|
Error(("Invalid input parameters\n"));
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Load the fax port description string
|
|
//
|
|
|
|
if (! LoadString(ghInstance, IDS_FAX_PORT_DESC, portDescStr, MAX_DESC_LEN))
|
|
portDescStr[0] = NUL;
|
|
|
|
descStrSize = SizeOfString(portDescStr);
|
|
faxmonNameSize = SizeOfString(faxMonitorName);
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
|
|
cbNeeded = sizeof(PORT_INFO_1) + SizeOfString(FAX_PORT_NAME);
|
|
break;
|
|
|
|
case 2:
|
|
|
|
cbNeeded = sizeof(PORT_INFO_2) + descStrSize + faxmonNameSize + SizeOfString(FAX_PORT_NAME);
|
|
break;
|
|
}
|
|
|
|
*pReturned = 1;
|
|
*pcbNeeded = cbNeeded;
|
|
|
|
if (cbNeeded > cbBuf) {
|
|
|
|
//
|
|
// Caller didn't provide a big enough buffer
|
|
//
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
status = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Strings must be packed at the end of the caller provided buffer.
|
|
// Otherwise, spooler will mess up.
|
|
//
|
|
|
|
pPorts += cbBuf;
|
|
|
|
//
|
|
// Copy the requested port information to the caller provided buffer
|
|
//
|
|
|
|
strSize = SizeOfString(FAX_PORT_NAME);
|
|
pPorts -= strSize;
|
|
CopyMemory(pPorts, FAX_PORT_NAME, strSize);
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
|
|
pPortInfo1->pName = (LPTSTR) pPorts;
|
|
Verbose(("Port info 1: %ws\n", pPortInfo1->pName));
|
|
|
|
pPortInfo1++;
|
|
break;
|
|
|
|
case 2:
|
|
|
|
pPortInfo2->pPortName = (LPTSTR) pPorts;
|
|
|
|
//
|
|
// Copy the fax monitor name string
|
|
//
|
|
|
|
pPorts -= faxmonNameSize;
|
|
pPortInfo2->pMonitorName = (LPTSTR) pPorts;
|
|
CopyMemory(pPorts, faxMonitorName, faxmonNameSize);
|
|
|
|
//
|
|
// Copy the fax port description string
|
|
//
|
|
|
|
pPorts -= descStrSize;
|
|
pPortInfo2->pDescription = (LPTSTR) pPorts;
|
|
CopyMemory(pPorts, portDescStr, descStrSize);
|
|
|
|
pPortInfo2->fPortType = PORT_TYPE_WRITE;
|
|
pPortInfo2->Reserved = 0;
|
|
|
|
Verbose(("Port info 2: %ws, %ws, %ws\n",
|
|
pPortInfo2->pPortName,
|
|
pPortInfo2->pMonitorName,
|
|
pPortInfo2->pDescription));
|
|
|
|
pPortInfo2++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DisplayErrorNotImplemented(
|
|
HWND hwnd,
|
|
INT titleId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display an error dialog to tell the user that he cannot manage
|
|
fax devices in the Printers folder.
|
|
|
|
Arguments:
|
|
|
|
hwnd - Specifies the parent window for the message box
|
|
titleId - Message box title string resource ID
|
|
|
|
Return Value:
|
|
|
|
FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR title[128];
|
|
TCHAR message[256];
|
|
|
|
LoadString(ghInstance, titleId, title, 128);
|
|
LoadString(ghInstance, IDS_CONFIG_ERROR, message, 256);
|
|
MessageBox(hwnd, message, title, MB_OK|MB_ICONERROR);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonAddPort(
|
|
LPTSTR pServerName,
|
|
HWND hwnd,
|
|
LPTSTR pMonitorName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds the name of a port to the list of supported ports
|
|
|
|
Arguments:
|
|
|
|
pServerName - Specifies the name of the server to which the port is to be added
|
|
hwnd - Identifies the parent window of the AddPort dialog box
|
|
pMonitorName - Specifies the monitor associated with the port
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
Trace("AddPort");
|
|
|
|
return DisplayErrorNotImplemented(hwnd, IDS_ADD_PORT);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonAddPortEx(
|
|
LPTSTR pServerName,
|
|
DWORD level,
|
|
LPBYTE pBuffer,
|
|
LPTSTR pMonitorName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds the name of a port to the list of supported ports
|
|
|
|
Arguments:
|
|
|
|
pServerName - Specifies the name of the server to which the port is to be added
|
|
hwnd - Identifies the parent window of the AddPort dialog box
|
|
pMonitorName - Specifies the monitor associated with the port
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
Trace("AddPortEx");
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonDeletePort(
|
|
LPTSTR pServerName,
|
|
HWND hwnd,
|
|
LPTSTR pPortName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the specified port from the list of supported ports
|
|
|
|
Arguments:
|
|
|
|
pServerName - Specifies the name of the server from which the port is to be removed
|
|
hwnd - Identifies the parent window of the port-deletion dialog box
|
|
pPortName - Specifies the name of the port to be deleted
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
Trace("DeletePort");
|
|
return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxMonConfigurePort(
|
|
LPWSTR pServerName,
|
|
HWND hwnd,
|
|
LPWSTR pPortName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display a dialog box to allow user to configure the specified port
|
|
|
|
Arguments:
|
|
|
|
pServerName - Specifies the name of the server on which the given port exists
|
|
hwnd - Identifies the parent window of the port-configuration dialog
|
|
pPortName - Specifies the name of the port to be configured
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
Trace("ConfigurePort");
|
|
|
|
return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT);
|
|
}
|
|
|
|
|
|
|
|
LPTSTR
|
|
DuplicateString(
|
|
LPCTSTR pSrcStr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make a duplicate of the given character string
|
|
|
|
Arguments:
|
|
|
|
pSrcStr - Specifies the string to be duplicated
|
|
|
|
Return Value:
|
|
|
|
Pointer to the duplicated string, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR pDestStr;
|
|
INT strSize;
|
|
|
|
if (pSrcStr != NULL) {
|
|
|
|
strSize = SizeOfString(pSrcStr);
|
|
|
|
if (pDestStr = MemAlloc(strSize))
|
|
CopyMemory(pDestStr, pSrcStr, strSize);
|
|
else
|
|
Error(("Memory allocation failed\n"));
|
|
|
|
} else
|
|
pDestStr = NULL;
|
|
|
|
return pDestStr;
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
MyGetJob(
|
|
HANDLE hPrinter,
|
|
DWORD level,
|
|
DWORD jobId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for spooler API GetJob
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Handle to the printer object
|
|
level - Level of JOB_INFO structure interested
|
|
jobId - Specifies the job ID
|
|
|
|
Return Value:
|
|
|
|
Pointer to a JOB_INFO structure, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PBYTE pJobInfo = NULL;
|
|
DWORD cbNeeded;
|
|
|
|
if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pJobInfo = MemAlloc(cbNeeded)) &&
|
|
GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded))
|
|
{
|
|
return pJobInfo;
|
|
}
|
|
|
|
Error(("GetJob failed: %d\n", GetLastError()));
|
|
MemFree(pJobInfo);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SetJobStatus(
|
|
HANDLE hPrinter,
|
|
DWORD jobId,
|
|
INT statusStrId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the status information of a print job
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Specifies the printer on which the job is printed
|
|
jobId - Specifies the job identifier
|
|
statusStrID - Specifies the status string resource ID
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
#define MAX_MESSAGE_LEN 256
|
|
|
|
{
|
|
JOB_INFO_1 *pJobInfo1;
|
|
BOOL result = FALSE;
|
|
TCHAR message[MAX_MESSAGE_LEN];
|
|
|
|
//
|
|
// Get the current job information
|
|
//
|
|
|
|
if (pJobInfo1 = MyGetJob(hPrinter, 1, jobId)) {
|
|
|
|
//
|
|
// Update the status field
|
|
//
|
|
|
|
if (LoadString(ghInstance, statusStrId, message, MAX_MESSAGE_LEN))
|
|
pJobInfo1->pStatus = message;
|
|
else {
|
|
|
|
pJobInfo1->pStatus = NULL;
|
|
pJobInfo1->Status = JOB_STATUS_ERROR;
|
|
}
|
|
|
|
pJobInfo1->Position = JOB_POSITION_UNSPECIFIED;
|
|
|
|
if (! (result = SetJob(hPrinter, jobId, 1, (PBYTE) pJobInfo1, 0)))
|
|
Error(("SetJob failed: %d\n", GetLastError()));
|
|
|
|
MemFree(pJobInfo1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
GetRegistryDWord(
|
|
HKEY hRegKey,
|
|
LPTSTR pValueName,
|
|
DWORD defaultValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve a DWORD value from the registry
|
|
|
|
Arguments:
|
|
|
|
hRegKey - Handle to the user info registry key
|
|
pValueName - Specifies the name of the string value in registry
|
|
defaultValue - Specifies the default value to be used in case of an error
|
|
|
|
Return Value:
|
|
|
|
Requested DWORD value from the user info registry key
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD size, type, value;
|
|
|
|
//
|
|
// Retrieve the country code value from the registry.
|
|
// Use the default value if none exists.
|
|
//
|
|
|
|
size = sizeof(value);
|
|
|
|
if (RegQueryValueEx(hRegKey, pValueName, NULL, &type, (PBYTE) &value, &size) != ERROR_SUCCESS ||
|
|
type != REG_DWORD)
|
|
{
|
|
value = defaultValue;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Variable for controlling the amount of debug messages generated
|
|
//
|
|
|
|
INT _debugLevel = 1;
|
|
|
|
|
|
LPCSTR
|
|
StripDirPrefixA(
|
|
LPCSTR pFilename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Strip the directory prefix off a filename
|
|
|
|
Arguments:
|
|
|
|
pFilename - Pointer to filename string
|
|
|
|
Return Value:
|
|
|
|
Pointer to the last component of a filename (without directory prefix)
|
|
|
|
--*/
|
|
|
|
{
|
|
LPCSTR pstr;
|
|
|
|
if (pstr = strrchr(pFilename, PATH_SEPARATOR))
|
|
return pstr + 1;
|
|
|
|
return pFilename;
|
|
}
|
|
|
|
#endif
|