windows-nt/Source/XPSP1/NT/printscan/fax/service/server/print.c
2020-09-26 16:20:57 +08:00

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
}