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