|
|
/*++
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
|