2419 lines
53 KiB
C
2419 lines
53 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
print.c
|
|
|
|
Abstract:
|
|
|
|
This module handles the FAX receive case.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 24-April-1996
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "faxsvc.h"
|
|
#pragma hdrstop
|
|
|
|
#define INTERNAL 1
|
|
#include "common.h"
|
|
|
|
|
|
PFAX_PRINTER_INFO FaxPrinterInfo;
|
|
DWORD FaxPrinters;
|
|
PHANDLE FaxPrinterNotifyHandles;
|
|
DWORD HandleCount;
|
|
HANDLE SpoolerProcessHandle;
|
|
DWORD SpoolerProcessIdx;
|
|
DWORD ReservedHandles;
|
|
HANDLE DirtyTimerHandle = INVALID_HANDLE_VALUE;
|
|
DWORD DirtyTimerIdx;
|
|
HANDLE ModemTimerHandle = INVALID_HANDLE_VALUE;
|
|
DWORD ModemTimerIdx;
|
|
|
|
extern DWORD FaxDirtyDays;
|
|
extern HANDLE FaxServerEvent;
|
|
|
|
LPTSTR PrintPlatforms[] =
|
|
{
|
|
TEXT("Windows NT x86"),
|
|
TEXT("Windows NT R4000"),
|
|
TEXT("Windows NT Alpha_AXP"),
|
|
TEXT("Windows NT PowerPC")
|
|
};
|
|
|
|
|
|
WORD PrinterFieldType1[] =
|
|
{
|
|
JOB_NOTIFY_FIELD_STATUS
|
|
};
|
|
|
|
WORD PrinterFieldType2[] =
|
|
{
|
|
PRINTER_NOTIFY_FIELD_PRINTER_NAME
|
|
};
|
|
|
|
|
|
PRINTER_NOTIFY_OPTIONS_TYPE PrinterNotifyOptionsType[] =
|
|
{
|
|
{
|
|
JOB_NOTIFY_TYPE,
|
|
0,
|
|
0,
|
|
0,
|
|
sizeof(PrinterFieldType1) / sizeof(WORD),
|
|
PrinterFieldType1
|
|
},
|
|
{
|
|
PRINTER_NOTIFY_TYPE,
|
|
0,
|
|
0,
|
|
0,
|
|
sizeof(PrinterFieldType2) / sizeof(WORD),
|
|
PrinterFieldType2
|
|
}
|
|
};
|
|
|
|
|
|
PRINTER_NOTIFY_OPTIONS PrinterNotifyOptions =
|
|
{
|
|
2,
|
|
0,
|
|
sizeof(PrinterNotifyOptionsType) / sizeof(PRINTER_NOTIFY_OPTIONS_TYPE),
|
|
PrinterNotifyOptionsType
|
|
};
|
|
|
|
BOOL
|
|
AddPortExW(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pBuffer,
|
|
LPWSTR pMonitorName
|
|
);
|
|
|
|
VOID
|
|
CleanDirtyQueues(
|
|
VOID
|
|
);
|
|
|
|
PVOID
|
|
MyEnumPrinters(
|
|
LPTSTR pServerName,
|
|
DWORD level,
|
|
PDWORD pcPrinters
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for spooler API EnumPrinters
|
|
|
|
Arguments:
|
|
|
|
pServerName - Specifies the name of the print server
|
|
level - Level of PRINTER_INFO_x structure
|
|
pcPrinters - Returns the number of printers enumerated
|
|
|
|
Return Value:
|
|
|
|
Pointer to an array of PRINTER_INFO_x structures
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PBYTE pPrinterInfo = NULL;
|
|
DWORD cb;
|
|
|
|
if (! EnumPrinters(PRINTER_ENUM_LOCAL,
|
|
pServerName,
|
|
level,
|
|
NULL,
|
|
0,
|
|
&cb,
|
|
pcPrinters) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pPrinterInfo = MemAlloc(cb)) &&
|
|
EnumPrinters(PRINTER_ENUM_LOCAL,
|
|
pServerName,
|
|
level,
|
|
pPrinterInfo,
|
|
cb,
|
|
&cb,
|
|
pcPrinters))
|
|
{
|
|
return pPrinterInfo;
|
|
}
|
|
|
|
MemFree(pPrinterInfo);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PVOID
|
|
MyEnumPorts(
|
|
LPTSTR pServerName,
|
|
DWORD level,
|
|
PDWORD pcPorts
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for spooler API EnumPrinters
|
|
|
|
Arguments:
|
|
|
|
pServerName - Specifies the name of the print server
|
|
level - Level of PRINTER_INFO_x structure
|
|
pcPrinters - Returns the number of printers enumerated
|
|
|
|
Return Value:
|
|
|
|
Pointer to an array of PRINTER_INFO_x structures
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PBYTE pPortInfo = NULL;
|
|
DWORD cb;
|
|
|
|
if (! EnumPorts( NULL, level, NULL, 0, &cb, pcPorts ) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pPortInfo = MemAlloc(cb)) &&
|
|
EnumPorts( NULL, level, pPortInfo, cb, &cb, pcPorts ))
|
|
{
|
|
return pPortInfo;
|
|
}
|
|
|
|
MemFree( pPortInfo );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PVOID
|
|
MyGetJob(
|
|
HANDLE hPrinter,
|
|
DWORD level,
|
|
DWORD jobId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for spooler API GetJob
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Handle to the printer object
|
|
level - Level of JOB_INFO structure interested
|
|
jobId - Specifies the job ID
|
|
|
|
Return Value:
|
|
|
|
Pointer to a JOB_INFO structure, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PBYTE pJobInfo = NULL;
|
|
DWORD cbNeeded;
|
|
|
|
if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pJobInfo = MemAlloc(cbNeeded)) &&
|
|
GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded))
|
|
{
|
|
return pJobInfo;
|
|
}
|
|
|
|
MemFree(pJobInfo);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetPrinterDataDWord(
|
|
HANDLE hPrinter,
|
|
PWSTR pRegKey,
|
|
DWORD defaultValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve a DWORD value under PrinterData registry key
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Specifies the printer in question
|
|
pRegKey - Specifies the name of registry value
|
|
defaultValue - Specifies the default value to be used if no data exists in registry
|
|
|
|
Return Value:
|
|
|
|
Current value for the requested registry key
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD value, type, cb;
|
|
|
|
if (GetPrinterData(hPrinter,
|
|
pRegKey,
|
|
&type,
|
|
(PBYTE) &value,
|
|
sizeof(value),
|
|
&cb) == ERROR_SUCCESS)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
|
|
LPTSTR
|
|
GetPrinterDataStr(
|
|
HANDLE hPrinter,
|
|
LPTSTR pRegKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a string value from the PrinterData registry key
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Identifies the printer object
|
|
pRegKey - Specifies the name of registry value
|
|
|
|
Return Value:
|
|
|
|
pBuffer
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD type, cb;
|
|
PVOID pBuffer = NULL;
|
|
|
|
//
|
|
// We should really pass NULL for pData parameter here. But to workaround
|
|
// a bug in the spooler API GetPrinterData, we must pass in a valid pointer here.
|
|
//
|
|
|
|
if (GetPrinterData( hPrinter, pRegKey, &type, (PBYTE) &type, 0, &cb ) == ERROR_MORE_DATA &&
|
|
(pBuffer = MemAlloc( cb )) &&
|
|
GetPrinterData( hPrinter, pRegKey, &type, pBuffer, cb, &cb ) == ERROR_SUCCESS &&
|
|
(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ))
|
|
{
|
|
return pBuffer;
|
|
}
|
|
|
|
DebugPrint(( TEXT("Couldn't get printer data string %ws: %d\n"), pRegKey, GetLastError() ));
|
|
MemFree( pBuffer );
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DeletePortInternal(
|
|
HANDLE hPrinter,
|
|
LPTSTR PortName
|
|
)
|
|
{
|
|
BOOL Rval = TRUE;
|
|
BOOL PortFound = FALSE;
|
|
DWORD i;
|
|
LPPRINTER_INFO_2 PrinterInfo = NULL;
|
|
LPTSTR p;
|
|
LPTSTR s2;
|
|
LPTSTR s;
|
|
|
|
|
|
if ((!GetPrinter( hPrinter, 2, NULL, 0, &i )) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
|
|
DebugPrint(( TEXT("GetPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
PrinterInfo = (LPPRINTER_INFO_2) MemAlloc( i );
|
|
if (!PrinterInfo) {
|
|
DebugPrint(( TEXT("MemAlloc() failed, size=%d"), i ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
if (!GetPrinter( hPrinter, 2, (LPBYTE) PrinterInfo, i, &i )) {
|
|
DebugPrint(( TEXT("GetPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
p = PrinterInfo->pPortName;
|
|
while (p && *p) {
|
|
s = _tcschr( p, TEXT(',') );
|
|
if (s) {
|
|
s2 = s;
|
|
*s = 0;
|
|
} else {
|
|
s2 = NULL;
|
|
}
|
|
if (_tcscmp( p, PortName ) == 0) {
|
|
PortFound = TRUE;
|
|
if (s2) {
|
|
_tcscpy( p, s2+1 );
|
|
} else {
|
|
*p = 0;
|
|
break;
|
|
}
|
|
} else {
|
|
p += _tcslen(p);
|
|
if (s2) {
|
|
*s2 = TEXT(',');
|
|
p += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PortFound) {
|
|
if (!SetPrinter( hPrinter, 2, (LPBYTE) PrinterInfo, 0 )) {
|
|
DebugPrint(( TEXT("SetPrinter() failed, ec=%d"), GetLastError() ));
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
MemFree( PrinterInfo );
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DeletePrinterPort(
|
|
LPTSTR PortName
|
|
)
|
|
{
|
|
BOOL Rval = TRUE;
|
|
DWORD i;
|
|
PPRINTER_INFO_2 PrinterInfo = NULL;
|
|
DWORD PrinterCount;
|
|
PRINTER_DEFAULTS PrinterDefaults;
|
|
HANDLE hPrinter;
|
|
|
|
|
|
PrinterInfo = MyEnumPrinters( NULL, 2, &PrinterCount );
|
|
if (!PrinterInfo) {
|
|
DebugPrint(( TEXT("MyEnumPrinters() failed, ec=%d"), GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// first remove the port name from the list
|
|
// associated with each fax printer. this is
|
|
// necessary because the DeletePort() api will
|
|
// not work if the port is associated with a
|
|
// printer
|
|
//
|
|
|
|
for (i=0; i<PrinterCount; i++) {
|
|
if (_tcsicmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0) {
|
|
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
|
|
|
|
if (!OpenPrinter( PrinterInfo[i].pPrinterName, &hPrinter, &PrinterDefaults )) {
|
|
DebugPrint(( TEXT("OpenPrinter() failed, ec=%d"), GetLastError() ));
|
|
continue;
|
|
}
|
|
|
|
if (!DeletePortInternal( hPrinter, PortName )) {
|
|
Rval = FALSE;
|
|
}
|
|
|
|
ClosePrinter( hPrinter );
|
|
}
|
|
}
|
|
|
|
MemFree( PrinterInfo );
|
|
|
|
//
|
|
// next, if the port was dis-associated from all
|
|
// printers, then ask the spooler to delete it.
|
|
//
|
|
|
|
if (Rval) {
|
|
DeletePort( NULL, NULL, PortName );
|
|
}
|
|
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsValidPort(
|
|
PPORT_INFO_2 PortInfo,
|
|
DWORD PortCount,
|
|
LPTSTR PortName
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i=0; i<PortCount; i++) {
|
|
if (_tcscmp( PortName, PortInfo[i].pPortName ) == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddPrinterPort(
|
|
LPTSTR PortName
|
|
)
|
|
{
|
|
BOOL Rval = TRUE;
|
|
DWORD i;
|
|
PORT_INFO_1W PortInfo;
|
|
PPRINTER_INFO_2 PrinterInfo = NULL;
|
|
PPRINTER_INFO_2 ThisPrinterInfo = NULL;
|
|
DWORD PrinterCount;
|
|
LPTSTR p,s;
|
|
PPORT_INFO_2 PortInfo2 = NULL;
|
|
DWORD PortCount;
|
|
LPTSTR NewPort = NULL;
|
|
PRINTER_DEFAULTS PrinterDefaults;
|
|
HANDLE hPrinter;
|
|
DWORD Bytes;
|
|
|
|
|
|
PortInfo.pName = PortName;
|
|
|
|
Rval = AddPortExW(
|
|
NULL,
|
|
1,
|
|
(LPBYTE) &PortInfo,
|
|
FAX_MONITOR_NAME
|
|
);
|
|
if (!Rval) {
|
|
DebugPrint(( TEXT("AddPortExW() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
PortInfo2 = (PPORT_INFO_2) MyEnumPorts( NULL, 2, &PortCount );
|
|
if (!PortInfo2) {
|
|
DebugPrint(( TEXT("MyEnumPorts() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
PrinterInfo = MyEnumPrinters( NULL, 2, &PrinterCount );
|
|
if (!PrinterInfo) {
|
|
DebugPrint(( TEXT("MyEnumPrinters() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
for (i=0; i<PrinterCount; i++) {
|
|
|
|
if (_tcsicmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0) {
|
|
|
|
MemFree( NewPort );
|
|
|
|
NewPort = MemAlloc( StringSize( PrinterInfo[i].pPortName ) + StringSize( PortName ) + 4 );
|
|
if (!NewPort) {
|
|
DebugPrint(( TEXT("Could not allocate memory for NewPort") ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
_tcscpy( NewPort, PortName );
|
|
p = PrinterInfo[i].pPortName;
|
|
|
|
while( p && *p ) {
|
|
s = _tcschr( p, TEXT(',') );
|
|
if (s) {
|
|
*s = 0;
|
|
}
|
|
if (IsValidPort( PortInfo2, PortCount, p )) {
|
|
if (*NewPort) {
|
|
_tcscat( NewPort, TEXT(",") );
|
|
}
|
|
_tcscat( NewPort, p );
|
|
}
|
|
if (s) {
|
|
*s = TEXT(',');
|
|
p = s + 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
|
|
|
|
if (!OpenPrinter( PrinterInfo[i].pPrinterName, &hPrinter, &PrinterDefaults )) {
|
|
DebugPrint(( TEXT("OpenPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
if ((!GetPrinter( hPrinter, 2, NULL, 0, &Bytes )) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
|
|
DebugPrint(( TEXT("GetPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
MemFree( ThisPrinterInfo );
|
|
|
|
ThisPrinterInfo = (LPPRINTER_INFO_2) MemAlloc( Bytes );
|
|
if (!ThisPrinterInfo) {
|
|
DebugPrint(( TEXT("MemAlloc() failed, size=%d"), Bytes ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
if (!GetPrinter( hPrinter, 2, (LPBYTE) ThisPrinterInfo, Bytes, &Bytes )) {
|
|
DebugPrint(( TEXT("GetPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
ThisPrinterInfo->pPortName = NewPort;
|
|
|
|
if (!SetPrinter( hPrinter, 2, (LPBYTE) ThisPrinterInfo, 0 )) {
|
|
DebugPrint(( TEXT("SetPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
ClosePrinter( hPrinter );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
MemFree( ThisPrinterInfo );
|
|
MemFree( PrinterInfo );
|
|
MemFree( PortInfo2 );
|
|
MemFree( NewPort );
|
|
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateNullPrintJobs(
|
|
PJOB_ENTRY JobEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a NULL print job on each FAX printer in the
|
|
system. This is necessary to that status information
|
|
is displayed for incoming fax jobs.
|
|
|
|
|
|
Arguments:
|
|
|
|
JobEntry - Pointer to a FAX job entry.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The print jobs are all created.
|
|
FALSE - Some or all of the print jobs were not created.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
DOC_INFO_1 DocInfo;
|
|
BOOL Rval = TRUE;
|
|
PRINTER_DEFAULTS PrinterDefaults;
|
|
|
|
|
|
|
|
//
|
|
// loop thru the printers and create a job on each one
|
|
//
|
|
|
|
EnterCriticalSection( &CsJob );
|
|
|
|
for (i=0; i<FaxPrinters; i++) {
|
|
|
|
//
|
|
// create the print job
|
|
//
|
|
|
|
DocInfo.pDocName = GetString( IDS_SERVER_NAME );
|
|
DocInfo.pOutputFile = NULL;
|
|
DocInfo.pDatatype = 0;
|
|
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
|
|
|
|
if (!OpenPrinter( FaxPrinterInfo[i].PrinterName, &JobEntry->hPrinter[i], &PrinterDefaults )) {
|
|
DebugPrint(( TEXT("OpenPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
JobEntry->PrintJobIds[i] = StartDocPrinter(
|
|
JobEntry->hPrinter[i],
|
|
1,
|
|
(LPBYTE) &DocInfo
|
|
);
|
|
if (JobEntry->PrintJobIds[i]) {
|
|
|
|
DebugPrint((TEXT("Started receive print JobId %d"), JobEntry->PrintJobIds[i]));
|
|
|
|
//
|
|
// pause the job so nothing really happens
|
|
//
|
|
|
|
if (!SetJob( FaxPrinterInfo[i].hPrinter, JobEntry->PrintJobIds[i], 0, NULL, JOB_CONTROL_PAUSE )) {
|
|
DebugPrint(( TEXT("SetJob() failed, ec=%d"), GetLastError() ));
|
|
|
|
}
|
|
|
|
//
|
|
// set the initial status string
|
|
//
|
|
|
|
SetPrintJobStatus(
|
|
JobEntry->hPrinter[i],
|
|
JobEntry->PrintJobIds[i],
|
|
FPS_INITIALIZING,
|
|
NULL,
|
|
-1
|
|
);
|
|
} else {
|
|
|
|
DebugPrint(( TEXT("StartDocPrinter() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
LeaveCriticalSection( &CsJob );
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DeleteNullPrintJobs(
|
|
PFAX_PRINTER_INFO RecvFaxPrinterInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the NULL print jobs for all FAX printers
|
|
on the system.
|
|
|
|
Arguments:
|
|
|
|
RecvFaxPrinterInfo - Pointer to array of structs holding printer handles to close.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The print jobs are all deleted.
|
|
FALSE - Some or all of the print jobs were not deleted.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
BOOL Rval = TRUE;
|
|
DWORD JobId;
|
|
HANDLE hPrinter;
|
|
|
|
|
|
EnterCriticalSection( &CsJob );
|
|
|
|
for (i=0; i<FaxPrinters; i++) {
|
|
|
|
JobId = RecvFaxPrinterInfo[i].PrintJobId;
|
|
hPrinter = RecvFaxPrinterInfo[i].hPrinter;
|
|
|
|
if (!SetJob(
|
|
hPrinter,
|
|
JobId,
|
|
0,
|
|
NULL,
|
|
JOB_CONTROL_CANCEL
|
|
)) {
|
|
|
|
DebugPrint(( TEXT("SetJob() failed, ec=%d"), GetLastError() ));
|
|
Rval = FALSE;
|
|
|
|
}
|
|
|
|
DebugPrint((TEXT("Ended receive print JobId %d"), JobId));
|
|
|
|
if (!EndDocPrinter( hPrinter )) {
|
|
|
|
DebugPrint(( TEXT("EndDocPrinter() failed, ec=%d"), GetLastError() ));
|
|
|
|
}
|
|
|
|
ClosePrinter( hPrinter );
|
|
|
|
}
|
|
|
|
LeaveCriticalSection( &CsJob );
|
|
|
|
return Rval;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
EnableSpoolerPort(
|
|
LPTSTR PortName,
|
|
BOOL Enable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enables or disables a spooler port.
|
|
|
|
Arguments:
|
|
|
|
PortName - Name of the port to be changes
|
|
Enable - TRUE = enable, FAlSE = disable
|
|
|
|
Return Value:
|
|
|
|
TRUE - The port status is changed.
|
|
FALSE - The port status is not changed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PORT_INFO_3 PortInfo;
|
|
|
|
|
|
//
|
|
// change the spooler's port status
|
|
//
|
|
|
|
PortInfo.dwStatus = 0;
|
|
PortInfo.pszStatus = NULL,
|
|
PortInfo.dwSeverity = Enable ? PORT_STATUS_TYPE_INFO : PORT_STATUS_TYPE_ERROR;
|
|
|
|
if (!SetPort( NULL, PortName, 3, (LPBYTE) &PortInfo )) {
|
|
DebugPrint(( TEXT("SetPort() failed, ec=%d"), GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
SetPrintJobCompleted(
|
|
HANDLE hPrinter,
|
|
DWORD PrintJobId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Causes the spooler to complete a print job.
|
|
The result is the print job is removed from the
|
|
print queue.
|
|
|
|
Arguments:
|
|
|
|
PrinterName - Name of the printer that owns the job
|
|
PrintJobId - Id of the job to be restarted
|
|
|
|
Return Value:
|
|
|
|
TRUE - The print job is completed.
|
|
FALSE - The print job is not completed.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!PrintJobId) {
|
|
DebugPrint(( TEXT("SetPrintJobCompleted() failed: 0x%d"), PrintJobId ));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DebugPrint((TEXT("Setting job status for job %d - JOB_CONTROL_SENT_TO_PRINTER"), PrintJobId));
|
|
if (!SetJob(
|
|
hPrinter,
|
|
PrintJobId,
|
|
0,
|
|
NULL,
|
|
JOB_CONTROL_SENT_TO_PRINTER
|
|
)) {
|
|
|
|
DebugPrint(( TEXT("SetJob() failed: 0x%08x"), GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
SetPrintJobPaused(
|
|
HANDLE hPrinter,
|
|
DWORD PrintJobId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Causes the spooler to pause a print job.
|
|
|
|
Arguments:
|
|
|
|
PrinterName - Name of the printer that owns the job
|
|
PrintJobId - Id of the job to be restarted
|
|
|
|
Return Value:
|
|
|
|
TRUE - The print job is paused.
|
|
FALSE - The print job is not paused.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if (!PrintJobId) {
|
|
DebugPrint(( TEXT("SetPrintJobPaused() failed: 0x%d"), PrintJobId ));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DebugPrint((TEXT("Setting job status for job %d - JOB_CONTROL_PAUSE"), PrintJobId));
|
|
if (!SetJob(
|
|
hPrinter,
|
|
PrintJobId,
|
|
0,
|
|
NULL,
|
|
JOB_CONTROL_PAUSE
|
|
)) {
|
|
|
|
DebugPrint(( TEXT("SetJob() failed: 0x%08x"), GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ArchivePrintJob(
|
|
HANDLE hPrinter,
|
|
LPTSTR FaxFileName,
|
|
DWORDLONG SendTime,
|
|
PFAX_DEV_STATUS FaxStatus,
|
|
PFAX_SEND FaxSend
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Archive a tiff file that has been sent by copying the file to an archive
|
|
directory.
|
|
|
|
Arguments:
|
|
|
|
FaxFileName - Name of the file to archive
|
|
|
|
Return Value:
|
|
|
|
TRUE - The copy was made.
|
|
FALSE - The copy was not made.
|
|
|
|
--*/
|
|
{
|
|
BOOL rVal = FALSE;
|
|
DWORD ByteCount;
|
|
LPTSTR ArchiveDirStr = NULL;
|
|
LPTSTR ArchiveDir = NULL;
|
|
LPTSTR ArchiveFileName = NULL;
|
|
MS_TAG_INFO MsTagInfo;
|
|
WCHAR wcZero = L'\0';
|
|
|
|
|
|
|
|
if (!GetPrinterDataDWord( hPrinter, PRNDATA_ARCHIVEFLAG, 0 )) {
|
|
return TRUE;
|
|
}
|
|
|
|
ArchiveDirStr = GetPrinterDataStr( hPrinter, PRNDATA_ARCHIVEDIR );
|
|
if (!ArchiveDirStr) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// get the dir name
|
|
//
|
|
|
|
ByteCount = ExpandEnvironmentStrings( ArchiveDirStr, ArchiveDir, 0 );
|
|
|
|
ArchiveDir = MemAlloc( ByteCount * sizeof(TCHAR) );
|
|
if (!ArchiveDir) {
|
|
goto exit;
|
|
}
|
|
ExpandEnvironmentStrings( ArchiveDirStr, ArchiveDir, ByteCount );
|
|
|
|
//
|
|
// be sure that the dir exists
|
|
//
|
|
|
|
MakeDirectory( ArchiveDir );
|
|
|
|
//
|
|
// get the file name
|
|
//
|
|
|
|
ByteCount = (ByteCount + 20) * sizeof(TCHAR);
|
|
ArchiveFileName = MemAlloc( ByteCount );
|
|
if (!ArchiveFileName) {
|
|
goto exit;
|
|
}
|
|
|
|
rVal = GenerateUniqueFileName( ArchiveDir, ArchiveFileName, ByteCount );
|
|
|
|
if (rVal) {
|
|
|
|
rVal = CopyFile( FaxFileName, ArchiveFileName, FALSE );
|
|
|
|
//
|
|
// add the microsoft fax tags to the file
|
|
// this is necessary ONLY when we archive the
|
|
// file when doing a send. if we are not
|
|
// archiving the file then it is deleted, so
|
|
// adding the tags is not necessary.
|
|
//
|
|
|
|
if (rVal) {
|
|
|
|
MsTagInfo.RecipName = NULL;
|
|
if (FaxSend->ReceiverName && (FaxSend->ReceiverName[0] != wcZero) ) {
|
|
MsTagInfo.RecipName = FaxSend->ReceiverName;
|
|
}
|
|
|
|
MsTagInfo.RecipNumber = NULL;
|
|
if (FaxSend->ReceiverNumber && (FaxSend->ReceiverNumber[0] != wcZero) ) {
|
|
MsTagInfo.RecipNumber = FaxSend->ReceiverNumber;
|
|
}
|
|
|
|
MsTagInfo.SenderName = NULL;
|
|
if (FaxSend->CallerName && (FaxSend->CallerName[0] != wcZero) ) {
|
|
MsTagInfo.SenderName = FaxSend->CallerName;
|
|
}
|
|
|
|
MsTagInfo.Routing = NULL;
|
|
if (FaxStatus->RoutingInfo && (FaxStatus->RoutingInfo[0] != wcZero) ) {
|
|
MsTagInfo.Routing = FaxStatus->RoutingInfo;
|
|
}
|
|
|
|
MsTagInfo.CallerId = NULL;
|
|
if (FaxStatus->CallerId && (FaxStatus->CallerId[0] != wcZero) ) {
|
|
MsTagInfo.CallerId = FaxStatus->CallerId;
|
|
}
|
|
|
|
MsTagInfo.Csid = NULL;
|
|
if (FaxStatus->CSI && (FaxStatus->CSI[0] != wcZero) ) {
|
|
MsTagInfo.Csid = FaxStatus->CSI;
|
|
}
|
|
|
|
MsTagInfo.Tsid = NULL;
|
|
if (FaxSend->CallerNumber && (FaxSend->CallerNumber[0] != wcZero) ) {
|
|
MsTagInfo.Tsid = FaxSend->CallerNumber;
|
|
}
|
|
|
|
MsTagInfo.FaxTime = SendTime;
|
|
|
|
TiffAddMsTags( ArchiveFileName, &MsTagInfo );
|
|
}
|
|
}
|
|
|
|
if (rVal) {
|
|
FaxLog(
|
|
FAXLOG_CATEGORY_OUTBOUND,
|
|
FAXLOG_LEVEL_MAX,
|
|
2,
|
|
MSG_FAX_ARCHIVE_SUCCESS,
|
|
FaxFileName,
|
|
ArchiveFileName
|
|
);
|
|
} else {
|
|
FaxLog(
|
|
FAXLOG_CATEGORY_OUTBOUND,
|
|
FAXLOG_LEVEL_MIN,
|
|
3,
|
|
MSG_FAX_ARCHIVE_FAILED,
|
|
FaxFileName,
|
|
ArchiveFileName,
|
|
GetLastErrorText(GetLastError())
|
|
);
|
|
}
|
|
|
|
exit:
|
|
MemFree( ArchiveDirStr );
|
|
MemFree( ArchiveDir );
|
|
MemFree( ArchiveFileName );
|
|
|
|
return rVal;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RestartPrintJob(
|
|
HANDLE hPrinter,
|
|
DWORD PrintJobId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Causes a print job to be restarted.
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Handle to printer that owns the job
|
|
PrintJobId - Id of the job to be restarted
|
|
|
|
Return Value:
|
|
|
|
TRUE - The print job is restarted
|
|
FALSE - The print job is not restarted
|
|
|
|
--*/
|
|
|
|
{
|
|
PJOB_INFO_2 pJobInfo;
|
|
SYSTEMTIME SystemTime;
|
|
DWORD Minutes;
|
|
|
|
if (!PrintJobId) {
|
|
return FALSE;
|
|
}
|
|
|
|
DebugPrint((TEXT("Setting job status for job %d - JOB_CONTROL_RESTART"), PrintJobId));
|
|
|
|
pJobInfo = (PJOB_INFO_2) MyGetJob( hPrinter, 2, PrintJobId );
|
|
|
|
if (pJobInfo == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
GetSystemTime(&SystemTime);
|
|
|
|
// wait a couple of minutes to restart the job
|
|
|
|
Minutes = SystemTime.wHour * MINUTES_PER_HOUR + SystemTime.wMinute;
|
|
Minutes += 2;
|
|
Minutes %= MINUTES_PER_DAY;
|
|
|
|
pJobInfo->StartTime = Minutes;
|
|
|
|
if (!SetJob(
|
|
hPrinter,
|
|
PrintJobId,
|
|
2,
|
|
(LPBYTE) pJobInfo,
|
|
JOB_CONTROL_RESTART
|
|
)) {
|
|
|
|
DebugPrint(( TEXT("SetJob() failed: 0x%08x"), GetLastError() ));
|
|
MemFree( pJobInfo );
|
|
return FALSE;
|
|
}
|
|
MemFree( pJobInfo );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetPrintJobStatus(
|
|
HANDLE hPrinter,
|
|
DWORD PrintJobId,
|
|
DWORD Status,
|
|
LPTSTR PhoneNumber,
|
|
INT PageCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the status string for a print job.
|
|
|
|
Arguments:
|
|
|
|
PrinterName - Name of the printer that owns the job
|
|
PrintJobId - Id of the job to be restarted
|
|
Status - Status is
|
|
|
|
Return Value:
|
|
|
|
TRUE - The status is changed.
|
|
FALSE - The status is NOT changed.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPJOB_INFO_1 JobInfo = NULL;
|
|
LPTSTR StatusString = NULL;
|
|
BOOL Rval = FALSE;
|
|
DWORD BytesNeeded;
|
|
DWORD Size;
|
|
|
|
|
|
if ((!GetJob(
|
|
hPrinter,
|
|
PrintJobId,
|
|
1,
|
|
NULL,
|
|
0,
|
|
&BytesNeeded )) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
DebugPrint(( TEXT("SetPrintJobStatus GetJob(0) JobId %d failed: 0x%08x"), PrintJobId, GetLastError() ));
|
|
goto exit;
|
|
|
|
}
|
|
|
|
Size = BytesNeeded;
|
|
BytesNeeded += 256;
|
|
|
|
JobInfo = (LPJOB_INFO_1) MemAlloc( BytesNeeded );
|
|
if (!JobInfo) {
|
|
DebugPrint(( TEXT("MemAlloc() failed: 0x%08x"), BytesNeeded ));
|
|
goto exit;
|
|
}
|
|
|
|
if (!GetJob(
|
|
hPrinter,
|
|
PrintJobId,
|
|
1,
|
|
(LPBYTE) JobInfo,
|
|
Size,
|
|
&Size
|
|
)) {
|
|
DebugPrint(( TEXT("SetPrintJobStatus GetJob(1) JobId %d failed: 0x%08x"), PrintJobId, GetLastError() ));
|
|
goto exit;
|
|
|
|
}
|
|
|
|
StatusString = GetString( Status );
|
|
if (StatusString) {
|
|
JobInfo->pStatus = (LPTSTR) ((LPBYTE)JobInfo + Size);
|
|
if (Status == FS_DIALING || Status == FS_TRANSMITTING) {
|
|
_stprintf( JobInfo->pStatus, StatusString, PhoneNumber );
|
|
} else {
|
|
_tcscpy( JobInfo->pStatus, StatusString );
|
|
}
|
|
}
|
|
|
|
if (PageCount != -1) {
|
|
JobInfo->PagesPrinted = (DWORD) PageCount;
|
|
}
|
|
|
|
DebugPrint((TEXT("Setting job status for job %d - %s"), PrintJobId, StatusString));
|
|
|
|
if (!SetJob(
|
|
hPrinter,
|
|
PrintJobId,
|
|
1,
|
|
(LPBYTE) JobInfo,
|
|
0
|
|
)) {
|
|
|
|
DebugPrint(( TEXT("SetJob() failed: 0x%08x"), GetLastError() ));
|
|
goto exit;
|
|
}
|
|
|
|
Rval = TRUE;
|
|
|
|
exit:
|
|
if (JobInfo) {
|
|
MemFree( JobInfo );
|
|
}
|
|
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsPrinterFaxPrinter(
|
|
LPTSTR PrinterName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if a printer is a fax printer.
|
|
|
|
Arguments:
|
|
|
|
PrinterName - Name of the printer
|
|
|
|
Return Value:
|
|
|
|
TRUE for success.
|
|
FALSE for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE hPrinter = NULL;
|
|
PRINTER_DEFAULTS PrinterDefaults;
|
|
SYSTEM_INFO SystemInfo;
|
|
DWORD Size;
|
|
DWORD Rval = FALSE;
|
|
LPDRIVER_INFO_2 DriverInfo = NULL;
|
|
|
|
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = PRINTER_READ;
|
|
|
|
if (!OpenPrinter( PrinterName, &hPrinter, &PrinterDefaults )) {
|
|
|
|
DebugPrint(( TEXT("OpenPrinter(%d) failed, ec=%d"), __LINE__, GetLastError() ));
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
GetSystemInfo( &SystemInfo );
|
|
|
|
Size = 4096;
|
|
|
|
DriverInfo = (LPDRIVER_INFO_2) MemAlloc( Size );
|
|
if (!DriverInfo) {
|
|
DebugPrint(( TEXT("Memory allocation failed, size=%d"), Size ));
|
|
goto exit;
|
|
}
|
|
|
|
Rval = GetPrinterDriver(
|
|
hPrinter,
|
|
PrintPlatforms[SystemInfo.wProcessorArchitecture],
|
|
2,
|
|
(LPBYTE) DriverInfo,
|
|
Size,
|
|
&Size
|
|
);
|
|
if (!Rval) {
|
|
DebugPrint(( TEXT("GetPrinterDriver() failed, ec=%d"), GetLastError() ));
|
|
goto exit;
|
|
}
|
|
|
|
if (_tcscmp( DriverInfo->pName, FAX_DRIVER_NAME ) == 0) {
|
|
Rval = TRUE;
|
|
} else {
|
|
Rval = FALSE;
|
|
}
|
|
|
|
exit:
|
|
|
|
MemFree( DriverInfo );
|
|
ClosePrinter( hPrinter );
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RefreshPrinterInfo(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates the necessary data structures
|
|
to track the fax printers on the server. The data
|
|
structures are then populated with the necessary data.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE for success.
|
|
FALSE for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD PrinterCount;
|
|
PPRINTER_INFO_2 PrinterInfo;
|
|
DWORD i;
|
|
DWORD j;
|
|
HANDLE hPrinter;
|
|
HANDLE hNotify;
|
|
PRINTER_DEFAULTS PrinterDefaults;
|
|
BOOL Rval = FALSE;
|
|
|
|
|
|
|
|
EnterCriticalSection( &CsJob );
|
|
|
|
//
|
|
// close all handles and release all memory
|
|
//
|
|
|
|
if (FaxPrinterInfo) {
|
|
for (i=0; i<FaxPrinters+1; i++) {
|
|
FindClosePrinterChangeNotification( FaxPrinterInfo[i].hNotify );
|
|
ClosePrinter( FaxPrinterInfo[i].hPrinter );
|
|
MemFree( FaxPrinterInfo[i].PrinterName );
|
|
}
|
|
}
|
|
|
|
MemFree( FaxPrinterInfo );
|
|
MemFree( FaxPrinterNotifyHandles );
|
|
FaxPrinterInfo = NULL;
|
|
FaxPrinterNotifyHandles = NULL;
|
|
|
|
FaxPrinters = 0;
|
|
HandleCount = 0;
|
|
|
|
//
|
|
// enumerate all of the printers on this server
|
|
//
|
|
|
|
PrinterInfo = MyEnumPrinters( NULL, 2, &PrinterCount );
|
|
if (!PrinterInfo) {
|
|
PrinterCount = 0;
|
|
}
|
|
|
|
//
|
|
// count the fax printers
|
|
//
|
|
|
|
for (i=0; i<PrinterCount; i++) {
|
|
|
|
//
|
|
// is this a fax printer??
|
|
//
|
|
|
|
if (_tcsicmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0) {
|
|
FaxPrinters += 1;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// allocate the fax printer info structures
|
|
//
|
|
|
|
FaxPrinterInfo = (PFAX_PRINTER_INFO) MemAlloc( (FaxPrinters+1) * sizeof(FAX_PRINTER_INFO) );
|
|
if (!FaxPrinterInfo) {
|
|
DebugPrint(( TEXT("Memory allocation failed\n") ));
|
|
goto exit;
|
|
}
|
|
|
|
ZeroMemory( FaxPrinterInfo, (FaxPrinters+1) * sizeof(FAX_PRINTER_INFO) );
|
|
|
|
//
|
|
// get a server handle
|
|
//
|
|
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = SERVER_ALL_ACCESS;
|
|
|
|
if (!OpenPrinter( NULL, &hPrinter, &PrinterDefaults )) {
|
|
DebugPrint(( TEXT("OpenPrinter() failed, ec=%d"), GetLastError() ));
|
|
goto exit;
|
|
}
|
|
|
|
hNotify = FindFirstPrinterChangeNotification(
|
|
hPrinter,
|
|
PRINTER_CHANGE_ADD_PRINTER | PRINTER_CHANGE_DELETE_PRINTER,
|
|
0,
|
|
&PrinterNotifyOptions
|
|
);
|
|
if (hNotify == INVALID_HANDLE_VALUE) {
|
|
ClosePrinter ( hPrinter );
|
|
DebugPrint(( TEXT("FindFirstPrinterChangeNotification() failed, ec=%d"), GetLastError() ));
|
|
goto exit;
|
|
}
|
|
|
|
FaxPrinterInfo[FaxPrinters].hPrinter = hPrinter;
|
|
FaxPrinterInfo[FaxPrinters].hNotify = hNotify;
|
|
FaxPrinterInfo[FaxPrinters].PrinterName = NULL;
|
|
|
|
//
|
|
// set the notification function for all fax printers
|
|
//
|
|
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
|
|
|
|
for (i=0,j=0; i<PrinterCount; i++) {
|
|
|
|
//
|
|
// is this a fax printer??
|
|
//
|
|
|
|
if (_tcsicmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0) {
|
|
|
|
if (!OpenPrinter( PrinterInfo[i].pPrinterName, &hPrinter, &PrinterDefaults )) {
|
|
DebugPrint(( TEXT("OpenPrinter() failed, ec=%d"), GetLastError() ));
|
|
goto exit;
|
|
}
|
|
|
|
hNotify = FindFirstPrinterChangeNotification(
|
|
hPrinter,
|
|
PRINTER_CHANGE_DELETE_JOB,
|
|
0,
|
|
&PrinterNotifyOptions
|
|
);
|
|
if (hNotify == INVALID_HANDLE_VALUE) {
|
|
DebugPrint(( TEXT("FindFirstPrinterChangeNotification() failed, ec=%d"), GetLastError() ));
|
|
goto exit;
|
|
}
|
|
|
|
FaxPrinterInfo[j].hPrinter = hPrinter;
|
|
FaxPrinterInfo[j].hNotify = hNotify;
|
|
FaxPrinterInfo[j].PrinterName = StringDup( PrinterInfo[i].pPrinterName );
|
|
|
|
j += 1;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
ReservedHandles = (SpoolerProcessHandle == NULL) ? 3 : 4;
|
|
|
|
HandleCount = FaxPrinters + ReservedHandles;
|
|
|
|
FaxPrinterNotifyHandles = (PHANDLE) MemAlloc( HandleCount * sizeof(HANDLE) );
|
|
|
|
if (!FaxPrinterNotifyHandles) {
|
|
DebugPrint(( TEXT("Memory allocation failed\n") ));
|
|
return FALSE;
|
|
}
|
|
|
|
for (i=0; i<HandleCount-ReservedHandles+1; i++) {
|
|
FaxPrinterNotifyHandles[i] = FaxPrinterInfo[i].hNotify;
|
|
}
|
|
|
|
if (SpoolerProcessHandle) {
|
|
SpoolerProcessIdx = i;
|
|
FaxPrinterNotifyHandles[i++] = SpoolerProcessHandle;
|
|
}
|
|
|
|
//
|
|
// initialize the dirty days queue cleaner timer
|
|
//
|
|
|
|
if (DirtyTimerHandle == INVALID_HANDLE_VALUE) {
|
|
|
|
DirtyTimerIdx = i;
|
|
DirtyTimerHandle = CreateWaitableTimer( NULL, FALSE, NULL );
|
|
|
|
if (DirtyTimerHandle == INVALID_HANDLE_VALUE) {
|
|
DebugPrint(( TEXT("CreateWaitableTimer failed ec=%d"), GetLastError() ));
|
|
} else {
|
|
LARGE_INTEGER DueTime;
|
|
LONG lPeriod = MILLISECONDS_PER_SECOND * SECONDS_PER_HOUR; // once per hour
|
|
|
|
DueTime.QuadPart = 0;
|
|
|
|
if( !SetWaitableTimer( DirtyTimerHandle, &DueTime, lPeriod, NULL, NULL, FALSE ) ) {
|
|
DebugPrint(( TEXT("SetWaitableTimer failed ec=%d"), GetLastError() ));
|
|
}
|
|
|
|
FaxPrinterNotifyHandles[i] = DirtyTimerHandle;
|
|
|
|
}
|
|
} else {
|
|
|
|
DirtyTimerIdx = i;
|
|
FaxPrinterNotifyHandles[i] = DirtyTimerHandle;
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
//
|
|
// initialize the modem delayed initialization timer
|
|
//
|
|
|
|
if (ModemTimerHandle == INVALID_HANDLE_VALUE) {
|
|
|
|
ModemTimerIdx = i;
|
|
ModemTimerHandle = CreateWaitableTimer( NULL, FALSE, NULL );
|
|
|
|
if (ModemTimerHandle == INVALID_HANDLE_VALUE) {
|
|
DebugPrint(( TEXT("CreateWaitableTimer failed ec=%d"), GetLastError() ));
|
|
} else {
|
|
LARGE_INTEGER DueTime;
|
|
LONG lPeriod = MILLISECONDS_PER_SECOND * 15;
|
|
|
|
DueTime.QuadPart = 0;
|
|
|
|
if( !SetWaitableTimer( ModemTimerHandle, &DueTime, lPeriod, NULL, NULL, FALSE ) ) {
|
|
DebugPrint(( TEXT("SetWaitableTimer failed ec=%d"), GetLastError() ));
|
|
}
|
|
|
|
FaxPrinterNotifyHandles[i] = ModemTimerHandle;
|
|
|
|
}
|
|
} else {
|
|
|
|
ModemTimerIdx = i;
|
|
FaxPrinterNotifyHandles[i] = ModemTimerHandle;
|
|
|
|
}
|
|
|
|
Rval = TRUE;
|
|
|
|
exit:
|
|
LeaveCriticalSection( &CsJob );
|
|
MemFree( PrinterInfo );
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
HandleJobChange(
|
|
PFAX_PRINTER_INFO FaxPrinterInfo,
|
|
DWORD JobStatus,
|
|
DWORD JobId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles a print job change. We only care
|
|
about job deletions. When a user deletes a fax print job
|
|
we must call the device provider's abort function so
|
|
that the fax operation can be terminated.
|
|
|
|
Arguments:
|
|
|
|
FaxPrinterInfo - Printer info structure for the printer that owns this job
|
|
JobStatus - The new status of the job
|
|
JobId - The print job id
|
|
|
|
Return Value:
|
|
|
|
TRUE for success.
|
|
FALSE for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PJOB_ENTRY JobEntry;
|
|
|
|
|
|
EnterCriticalSection( &CsJob );
|
|
|
|
//
|
|
// is the job being deleted?
|
|
//
|
|
|
|
if (!(JobStatus & JOB_STATUS_DELETING)) {
|
|
LeaveCriticalSection( &CsJob );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// get the fax job
|
|
//
|
|
|
|
JobEntry = FindJobByPrintJob( JobId );
|
|
|
|
if (JobEntry == NULL || JobEntry->Aborting) {
|
|
//
|
|
// either the job does not exist or it is already aborting
|
|
//
|
|
|
|
LeaveCriticalSection( &CsJob );
|
|
return FALSE;
|
|
}
|
|
|
|
SetPrintJobStatus(
|
|
FaxPrinterInfo->hPrinter,
|
|
JobId,
|
|
FPS_ABORTING,
|
|
NULL,
|
|
-1
|
|
);
|
|
|
|
//
|
|
// call the device provider's abort function
|
|
//
|
|
|
|
__try {
|
|
|
|
JobEntry->LineInfo->Provider->FaxDevAbortOperation(
|
|
(HANDLE) JobEntry->InstanceData
|
|
);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
JobEntry->ErrorCode = GetExceptionCode();
|
|
|
|
}
|
|
|
|
JobEntry->Aborting = TRUE;
|
|
|
|
LeaveCriticalSection( &CsJob );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
GetSpoolerProcessHandle(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gets a handles to the spooler's
|
|
process object. It does this by enumerating the
|
|
task list on the system and then looks for a
|
|
process called "spoolss.exe". This task's process
|
|
identifier is used to open a process handle.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NULL - Could not get the spooler's process handle
|
|
HANDLE - The spooler's process handle
|
|
|
|
--*/
|
|
|
|
{
|
|
#define MAX_TASKS 256
|
|
DWORD TaskCount;
|
|
PTASK_LIST TaskList = NULL;
|
|
DWORD SpoolerPid = 0;
|
|
DWORD i;
|
|
HANDLE SpoolerProcessHandle = NULL;
|
|
|
|
|
|
TaskList = (PTASK_LIST) MemAlloc( MAX_TASKS * sizeof(TASK_LIST) );
|
|
if (!TaskList) {
|
|
goto exit;
|
|
}
|
|
|
|
TaskCount = GetTaskList( TaskList, MAX_TASKS );
|
|
if (!TaskCount) {
|
|
goto exit;
|
|
}
|
|
|
|
for (i=0; i<TaskCount; i++) {
|
|
if (_stricmp( TaskList[i].ProcessName, "spoolss.exe" ) == 0) {
|
|
SpoolerPid = TaskList[i].dwProcessId;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == TaskCount) {
|
|
goto exit;
|
|
}
|
|
|
|
if (SpoolerProcessHandle) {
|
|
CloseHandle( SpoolerProcessHandle );
|
|
}
|
|
|
|
SpoolerProcessHandle = OpenProcess( SYNCHRONIZE, FALSE, SpoolerPid );
|
|
|
|
exit:
|
|
MemFree( TaskList );
|
|
return SpoolerProcessHandle;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WaitForSpoolerToStart(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function waits for the spooler service to start.
|
|
It calls the service controller and queries the status
|
|
of the spooler every 2 seconds (polled).
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rVal = 0;
|
|
SC_HANDLE hSvcMgr = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
SERVICE_STATUS Status;
|
|
|
|
|
|
hSvcMgr = OpenSCManager(
|
|
NULL,
|
|
NULL,
|
|
SC_MANAGER_ALL_ACCESS
|
|
);
|
|
if (!hSvcMgr) {
|
|
rVal = GetLastError();
|
|
DebugPrint(( TEXT("could not open service manager: error code = %u"), rVal ));
|
|
goto exit;
|
|
}
|
|
|
|
hService = OpenService(
|
|
hSvcMgr,
|
|
TEXT("Spooler"),
|
|
SERVICE_ALL_ACCESS
|
|
);
|
|
if (!hService) {
|
|
rVal = GetLastError();
|
|
DebugPrint((
|
|
TEXT("could not open the Spooler service: error code = %u"),
|
|
rVal
|
|
));
|
|
goto exit;
|
|
}
|
|
|
|
if (!QueryServiceStatus( hService, &Status )) {
|
|
rVal = GetLastError();
|
|
DebugPrint((
|
|
TEXT("could not query status for the Spooler service: error code = %u"),
|
|
rVal
|
|
));
|
|
goto exit;
|
|
}
|
|
|
|
while (Status.dwCurrentState != SERVICE_RUNNING) {
|
|
|
|
Sleep( 1000 * 2 );
|
|
|
|
if (!QueryServiceStatus( hService, &Status )) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (Status.dwCurrentState != SERVICE_RUNNING) {
|
|
rVal = GetLastError();
|
|
DebugPrint((
|
|
TEXT("could not start the Spooler service: error code = %u"),
|
|
rVal
|
|
));
|
|
goto exit;
|
|
}
|
|
|
|
rVal = ERROR_SUCCESS;
|
|
|
|
//
|
|
// get the spooler's process handle
|
|
//
|
|
|
|
SpoolerProcessHandle = GetSpoolerProcessHandle();
|
|
|
|
exit:
|
|
|
|
CloseServiceHandle( hService );
|
|
CloseServiceHandle( hSvcMgr );
|
|
|
|
return rVal;
|
|
}
|
|
|
|
|
|
DWORD
|
|
PrintStatusThread(
|
|
LPVOID NotUsed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function runs as a thread to process print
|
|
status changes. Each fax printer sends status changes
|
|
in the form of events to this thread. When it is
|
|
determined that a job is being deleted, the abort
|
|
function for the device provider is called to
|
|
terminate the fax send operation.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD FailCount = 0;
|
|
PPRINTER_NOTIFY_INFO PrinterNotifyInfo;
|
|
DWORD WaitObject;
|
|
DWORD Change;
|
|
HANDLE CleanQueueHandle = NULL;
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// wat for a job notification change
|
|
//
|
|
|
|
if (!HandleCount) {
|
|
if (WaitForSpoolerToStart() != ERROR_SUCCESS) {
|
|
FaxLog(
|
|
FAXLOG_CATEGORY_UNKNOWN,
|
|
FAXLOG_LEVEL_MIN,
|
|
0,
|
|
MSG_PRINTER_FAILURE
|
|
);
|
|
ReportServiceStatus( SERVICE_STOPPED, 0, 0 );
|
|
ExitProcess(0);
|
|
}
|
|
if (!RefreshPrinterInfo()) {
|
|
FailCount += 1;
|
|
Sleep( 1000 );
|
|
if (FailCount == 20) {
|
|
FaxLog(
|
|
FAXLOG_CATEGORY_UNKNOWN,
|
|
FAXLOG_LEVEL_MIN,
|
|
0,
|
|
MSG_PRINTER_FAILURE
|
|
);
|
|
ReportServiceStatus( SERVICE_STOPPED, 0, 0 );
|
|
ExitProcess(0);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
WaitObject = WaitForMultipleObjects(
|
|
HandleCount,
|
|
FaxPrinterNotifyHandles,
|
|
FALSE,
|
|
INFINITE
|
|
);
|
|
|
|
if (WaitObject == WAIT_FAILED || (WaitObject >= HandleCount && WaitObject < MAXIMUM_WAIT_OBJECTS)) {
|
|
|
|
//
|
|
// there was some problem in receiving the event
|
|
//
|
|
|
|
DebugPrint(( TEXT("WaitForMultipleObjects() failed, ec=%d"), GetLastError() ));
|
|
continue;
|
|
}
|
|
|
|
if (WaitObject == SpoolerProcessIdx) {
|
|
|
|
//
|
|
// the spooler just ended
|
|
//
|
|
|
|
SpoolerProcessHandle = 0;
|
|
SpoolerProcessIdx = 0;
|
|
|
|
WaitForSpoolerToStart();
|
|
|
|
RefreshPrinterInfo();
|
|
|
|
continue;
|
|
}
|
|
|
|
if (WaitObject == DirtyTimerIdx) {
|
|
DWORD ThreadId;
|
|
DWORD WaitObject;
|
|
|
|
//
|
|
// if the thread is still running, don't create another one
|
|
//
|
|
|
|
if (CleanQueueHandle != NULL) {
|
|
|
|
WaitObject = WaitForSingleObject( CleanQueueHandle, 0 );
|
|
|
|
if (WaitObject == WAIT_TIMEOUT) {
|
|
continue;
|
|
}
|
|
|
|
CloseHandle( CleanQueueHandle );
|
|
}
|
|
|
|
CleanQueueHandle = CreateThread(
|
|
NULL,
|
|
1024*100,
|
|
(LPTHREAD_START_ROUTINE) CleanDirtyQueues,
|
|
NULL,
|
|
0,
|
|
&ThreadId
|
|
);
|
|
|
|
if (CleanQueueHandle == NULL) {
|
|
DebugPrint(( TEXT("Cannot create CleanDirtyQueues thread") ));
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (WaitObject == ModemTimerIdx) {
|
|
|
|
PLIST_ENTRY Next;
|
|
PLINE_INFO LineInfo;
|
|
|
|
EnterCriticalSection( &CsLine );
|
|
|
|
Next = TapiLinesListHead.Flink;
|
|
if (Next) {
|
|
while ((ULONG)Next != (ULONG)&TapiLinesListHead) {
|
|
|
|
LineInfo = CONTAINING_RECORD( Next, LINE_INFO, ListEntry );
|
|
Next = LineInfo->ListEntry.Flink;
|
|
|
|
if (LineInfo->UnimodemDevice && (LineInfo->Flags & FPF_POWERED_OFF) &&
|
|
(LineInfo->Flags & FPF_RECEIVE_OK)) {
|
|
|
|
//
|
|
// put a popup on the currently active desktop
|
|
// we only allow 1 popup per device at a time
|
|
// and we only present the popup twice
|
|
//
|
|
|
|
if (!LineInfo->ModemInUse &&
|
|
LineInfo->ModemPopupActive &&
|
|
LineInfo->ModemPopUps < MAX_MODEM_POPUPS)
|
|
{
|
|
|
|
LineInfo->ModemPopupActive = 0;
|
|
LineInfo->ModemPopUps += 1;
|
|
|
|
ServiceMessageBox(
|
|
GetString( IDS_POWERED_OFF_MODEM ),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND,
|
|
TRUE,
|
|
&LineInfo->ModemPopupActive
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// see if we can revive the device
|
|
//
|
|
|
|
if (OpenTapiLine( LineInfo )) {
|
|
|
|
LPLINEDEVSTATUS LineDevStatus;
|
|
|
|
//
|
|
// check to see if the line is in use
|
|
//
|
|
|
|
LineDevStatus = MyLineGetLineDevStatus( LineInfo->hLine );
|
|
if (LineDevStatus) {
|
|
if (LineDevStatus->dwNumOpens > 0 && LineDevStatus->dwNumActiveCalls > 0) {
|
|
LineInfo->ModemInUse = TRUE;
|
|
} else {
|
|
LineInfo->ModemInUse = FALSE;
|
|
}
|
|
MemFree( LineDevStatus );
|
|
}
|
|
|
|
if (!LineInfo->ModemInUse) {
|
|
|
|
DebugPrint(( TEXT("Device %s is now powered on, connected, and ready for use"), LineInfo->DeviceName ));
|
|
|
|
LineInfo->Flags &= ~FPF_POWERED_OFF;
|
|
LineInfo->Flags &= ~FPF_RECEIVE_OK;
|
|
LineInfo->Flags |= FPF_RECEIVE;
|
|
|
|
LineInfo->State = FPS_AVAILABLE;
|
|
|
|
CreateFaxEvent( LineInfo->PermanentLineID, FEI_MODEM_POWERED_ON );
|
|
}
|
|
}
|
|
|
|
if (LineInfo->Flags & FPF_POWERED_OFF) {
|
|
DebugPrint(( TEXT("Could not revive device [%s]"), LineInfo->DeviceName ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection( &CsLine );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// get the status information from the spooler
|
|
//
|
|
|
|
if (!FindNextPrinterChangeNotification( FaxPrinterNotifyHandles[WaitObject], &Change, NULL, &PrinterNotifyInfo )) {
|
|
DebugPrint(( TEXT("FindNextPrinterChangeNotification() failed, ec=%d"), GetLastError() ));
|
|
continue;
|
|
}
|
|
|
|
if (Change == PRINTER_CHANGE_ADD_PRINTER || Change == PRINTER_CHANGE_DELETE_PRINTER) {
|
|
|
|
//
|
|
// get the current printer info
|
|
//
|
|
|
|
RefreshPrinterInfo();
|
|
|
|
} else if (PrinterNotifyInfo && PrinterNotifyInfo->aData[0].Field == JOB_NOTIFY_FIELD_STATUS) {
|
|
|
|
HandleJobChange(
|
|
&FaxPrinterInfo[WaitObject],
|
|
PrinterNotifyInfo->aData[0].NotifyData.adwData[0],
|
|
PrinterNotifyInfo->aData[0].Id
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// free the spooler allocated memory
|
|
//
|
|
|
|
FreePrinterNotifyInfo( PrinterNotifyInfo );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
DisallowFaxSharing(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE hPrinterServer;
|
|
PRINTER_DEFAULTS PrinterDefaults;
|
|
TCHAR String[128];
|
|
LONG Rslt;
|
|
DWORD Size;
|
|
|
|
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = SERVER_ACCESS_ADMINISTER;
|
|
|
|
if (!OpenPrinter( NULL, &hPrinterServer, &PrinterDefaults )) {
|
|
DebugPrint(( TEXT("OpenPrinter() failed, ec=%d"), GetLastError() ));
|
|
return;
|
|
}
|
|
|
|
_tcscpy( String, FAX_DRIVER_NAME );
|
|
Size = StringSize( String );
|
|
|
|
Rslt = SetPrinterData(
|
|
hPrinterServer,
|
|
SPLREG_NO_REMOTE_PRINTER_DRIVERS,
|
|
REG_SZ,
|
|
(LPBYTE) String,
|
|
Size
|
|
);
|
|
if ((Rslt != ERROR_SUCCESS) && (Rslt != ERROR_SUCCESS_RESTART_REQUIRED)) {
|
|
DebugPrint(( TEXT("SetPrinterData() failed, ec=%d"), Rslt ));
|
|
}
|
|
|
|
ClosePrinter( hPrinterServer );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
InitializePrinting(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the printing thread. The thread
|
|
is used to handle the case where the user that started a
|
|
fax send wants to delete the print job.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE for success.
|
|
FALSE for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD ThreadId;
|
|
HANDLE hThread;
|
|
|
|
|
|
//
|
|
// this shouldn't be necessary, but someone might
|
|
// figure out how to subvert our security
|
|
//
|
|
|
|
if (InstallType & FAX_INSTALL_WORKSTATION) {
|
|
DisallowFaxSharing();
|
|
}
|
|
|
|
//
|
|
// get the spooler's process handle
|
|
//
|
|
|
|
SpoolerProcessHandle = GetSpoolerProcessHandle();
|
|
|
|
//
|
|
// get the current printer info
|
|
//
|
|
|
|
RefreshPrinterInfo();
|
|
|
|
//
|
|
// start the thread that will do the actual
|
|
// status processing
|
|
//
|
|
|
|
hThread = CreateThread(
|
|
NULL,
|
|
1024*100,
|
|
(LPTHREAD_START_ROUTINE) PrintStatusThread,
|
|
NULL,
|
|
0,
|
|
&ThreadId
|
|
);
|
|
|
|
if (!hThread) {
|
|
return GetLastError();
|
|
}
|
|
|
|
CloseHandle( hThread );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
CleanDirtyQueues(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is invoked periodically by PrintStatusThread to clean the fax printer queues of
|
|
print jobs that have failed to be sent and have been in the queue longer than FaxDirtyDays.
|
|
|
|
This function also attempts to route inbound faxes that have failed previous routing attempts.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
#if 0
|
|
DWORD i;
|
|
DWORD j;
|
|
PJOB_INFO_2 JobInfo;
|
|
BYTE JobBuffer[4096];
|
|
BOOL Result;
|
|
DWORD cbData;
|
|
DWORD cJobs;
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER SubmitTime;
|
|
DWORD cBytes;
|
|
DWORD Retries;
|
|
LPTSTR RetryTag;
|
|
LPTSTR RouteTag;
|
|
DWORD WaitObject;
|
|
|
|
// wait for the server to come up completely
|
|
|
|
WaitObject = WaitForSingleObject( FaxServerEvent, INFINITE );
|
|
|
|
DebugPrint(( TEXT("Cleaning print queues") ));
|
|
|
|
GetSystemTimeAsFileTime( (FILETIME *) &CurrentTime );
|
|
|
|
// enumerate all of the print jobs in all of the fax printers
|
|
|
|
for (i = 0; i < FaxPrinters; i++) {
|
|
|
|
for (j = 0; TRUE ; j++) {
|
|
|
|
Result = EnumJobs(
|
|
FaxPrinterInfo[i].hPrinter,
|
|
j,
|
|
1,
|
|
2,
|
|
JobBuffer,
|
|
sizeof(JobBuffer),
|
|
&cbData,
|
|
&cJobs
|
|
);
|
|
|
|
JobInfo = (PJOB_INFO_2) JobBuffer;
|
|
|
|
if (!Result || cJobs == 0) {
|
|
break;
|
|
}
|
|
|
|
// only consider the jobs that are paused
|
|
|
|
if (!(JobInfo->Status & JOB_STATUS_PAUSED) || JobInfo->pParameters == NULL) {
|
|
continue;
|
|
}
|
|
|
|
// if it is a send retry that has maxed out, delete the job
|
|
|
|
RetryTag = ExtractFaxTag(FAXTAG_SEND_RETRY, JobInfo->pParameters, &cBytes);
|
|
|
|
if (RetryTag) {
|
|
|
|
Retries = _ttoi( RetryTag );
|
|
|
|
if (Retries == 0) {
|
|
|
|
SystemTimeToFileTime( &JobInfo->Submitted, (FILETIME *) &SubmitTime);
|
|
|
|
if (SubmitTime.QuadPart + (FaxDirtyDays * FILETIMETICKS_PER_DAY) < CurrentTime.QuadPart) {
|
|
|
|
while (SetPrintJobCompleted( FaxPrinterInfo[i].hPrinter, JobInfo->JobId ))
|
|
;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// if it is an inbound routing failure, try to route it again
|
|
|
|
RouteTag = ExtractFaxTag(FAXTAG_ROUTE_FILE, JobInfo->pParameters, &cBytes);
|
|
|
|
if (RouteTag) {
|
|
|
|
PROUTE_INFO RouteInfo;
|
|
|
|
RouteTag++; // skip over the space
|
|
|
|
RouteInfo = LoadRouteInfo( RouteTag );
|
|
|
|
if (RouteInfo != NULL) {
|
|
|
|
__try {
|
|
|
|
FaxRoute(
|
|
&RouteInfo->FaxReceive,
|
|
&RouteInfo->LineInfo,
|
|
&RouteInfo->FaxStatus,
|
|
NULL,
|
|
0,
|
|
RouteInfo->ElapsedTime
|
|
);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
// if the file is corrupt and causes an exception, delete it and cancel the print job
|
|
|
|
DebugPrint(( TEXT("Exception processing routing information file") ));
|
|
|
|
DeleteFile( RouteTag );
|
|
|
|
}
|
|
|
|
MemFree( RouteInfo );
|
|
|
|
if (GetFileAttributes( RouteTag ) == 0xffffffff) {
|
|
|
|
if(!SetJob(
|
|
FaxPrinterInfo[i].hPrinter,
|
|
JobInfo->JobId,
|
|
0,
|
|
NULL,
|
|
JOB_CONTROL_CANCEL
|
|
)) {
|
|
DebugPrint(( TEXT("CleanDirtyQueues - SetJob failed - ec %d"), GetLastError() ));
|
|
}
|
|
} else {
|
|
|
|
SetPrintJobStatus(
|
|
FaxPrinterInfo[i].hPrinter,
|
|
JobInfo->JobId,
|
|
FPS_ROUTERETRY,
|
|
NULL,
|
|
-1
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|