You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
637 lines
16 KiB
637 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1990-1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
addjob.c
|
|
|
|
|
|
Abstract:
|
|
|
|
This module provides all the public exported APIs relating to Printer
|
|
and Job management for the Local Print Providor. This module contains
|
|
LocalSpl's implementation of the following spooler apis
|
|
|
|
LocalAddJob
|
|
LocalScheduleJob
|
|
|
|
|
|
Author:
|
|
|
|
Dave Snipp (DaveSn) 15-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
Rewritten both apis -- Krishna Ganugapati (KrishnaG) 5-Apr-1994
|
|
RapidPrint -- Matthew A Felton (mattfe) June 1994
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#include "jobid.h"
|
|
#include "winsprlp.h"
|
|
#include "filepool.hxx"
|
|
|
|
VOID
|
|
AddJobEntry(
|
|
PINIPRINTER pIniPrinter,
|
|
PINIJOB pIniJob
|
|
);
|
|
|
|
|
|
BOOL
|
|
LocalAddJob(
|
|
HANDLE hPrinter,
|
|
DWORD Level,
|
|
LPBYTE pData,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded
|
|
)
|
|
{
|
|
PINIPRINTER pIniPrinter;
|
|
PINIJOB pIniJob;
|
|
PSPOOL pSpool=(PSPOOL)hPrinter;
|
|
DWORD cb;
|
|
WCHAR szFileName[MAX_PATH];
|
|
LPBYTE pEnd;
|
|
DWORD LastError=0;
|
|
LPADDJOB_INFO_1 pAddJob = (LPADDJOB_INFO_1)pData;
|
|
DWORD NextId;
|
|
BOOL bRemote = FALSE;
|
|
DOC_INFO_1 DocInfo1;
|
|
BOOL bRet;
|
|
DWORD dwStatus = 0;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
LPWSTR pMachineName = NULL;
|
|
LPWSTR pszSpoolFile = NULL;
|
|
PMAPPED_JOB pMappedJob = NULL;
|
|
SIZE_T FileNameLength = 0;
|
|
|
|
SplOutSem();
|
|
|
|
switch( Level ){
|
|
case 1:
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
pMachineName = (LPWSTR)( ((PBYTE)pData) +
|
|
(ULONG_PTR)((PADDJOB_INFO_2W)pData)->pData );
|
|
|
|
//
|
|
// Validate string.
|
|
//
|
|
if( pMachineName > (LPWSTR)( ((PBYTE)pData)+cbBuf )){
|
|
SetLastError( ERROR_INVALID_LEVEL );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Ensure NULL termination.
|
|
//
|
|
*(PWCHAR)(((ULONG_PTR)(pData + cbBuf - sizeof( WCHAR ))&~1)) = 0;
|
|
break;
|
|
|
|
default:
|
|
SetLastError( ERROR_INVALID_LEVEL );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// memset docinfo
|
|
//
|
|
|
|
memset((LPBYTE)&DocInfo1, 0, sizeof(DOC_INFO_1));
|
|
|
|
//
|
|
// Figure out whether the job is a remote or local job
|
|
//
|
|
{
|
|
HRESULT hRes = CheckLocalCall();
|
|
|
|
if (hRes == S_FALSE)
|
|
{
|
|
bRemote = TRUE;
|
|
}
|
|
else if (hRes != S_OK)
|
|
{
|
|
SetLastError(SCODE_CODE(hRes));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the name of the user
|
|
//
|
|
|
|
if (bRemote) {
|
|
DocInfo1.pDocName = szRemoteDoc;
|
|
} else{
|
|
DocInfo1.pDocName = szLocalDoc;
|
|
}
|
|
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// We should not be calling addjob on a Job Handle.
|
|
//
|
|
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER | PRINTER_HANDLE_JOB )) {
|
|
LeaveSplSem();
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// We're interested if this is a remote call (not if it was opened
|
|
// via \\server\remote). The server process does this.
|
|
//
|
|
if (pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) {
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return(FALSE);
|
|
}
|
|
|
|
if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) {
|
|
if (pSpool->pIniPort->Status & PP_MONITOR) {
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return(FALSE);
|
|
} else {
|
|
|
|
//
|
|
// If we had level == 2 (passing in the computer name), then
|
|
// convert back down to level 1 for old print providers.
|
|
// We don't need to fix up the structure since level 1 and 2
|
|
// are identical; it's just that level 2 is an in-out buffer.
|
|
//
|
|
//
|
|
if (Level == 2 || Level == 3) {
|
|
Level = 1;
|
|
}
|
|
|
|
//
|
|
// This is the "Local Printer masquerading as a Remote Printer"
|
|
//
|
|
LeaveSplSem();
|
|
bRet = AddJob(pSpool->hPort, Level, pData, cbBuf, pcbNeeded);
|
|
|
|
if(bRet)
|
|
{
|
|
EnterSplSem();
|
|
pSpool->Status |= SPOOL_STATUS_ADDJOB;
|
|
LeaveSplSem();
|
|
}
|
|
|
|
return(bRet);
|
|
}
|
|
}
|
|
|
|
pIniPrinter = pSpool->pIniPrinter;
|
|
|
|
SPLASSERT(pIniPrinter);
|
|
|
|
if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT) {
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INVALID_ACCESS);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Disallow EMF if PRINTER_ATTRIBUTE_RAW_ONLY is set.
|
|
//
|
|
if( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_RAW_ONLY ){
|
|
|
|
LPWSTR pszDatatype = pSpool->pDatatype ?
|
|
pSpool->pDatatype :
|
|
pIniPrinter->pDatatype;
|
|
|
|
if( !ValidRawDatatype( pszDatatype )){
|
|
LeaveSplSem();
|
|
SetLastError( ERROR_INVALID_DATATYPE );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
NextId = GetNextId( pIniPrinter->pIniSpooler->hJobIdMap );
|
|
|
|
GetFullNameFromId(pIniPrinter, NextId, TRUE, szFileName, COUNTOF(szFileName), pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL);
|
|
cb = wcslen(szFileName)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
sizeof(ADDJOB_INFO_1);
|
|
|
|
*pcbNeeded = cb;
|
|
if (cb > cbBuf) {
|
|
|
|
// Freeup the JobId.
|
|
vMarkOff( pIniPrinter->pIniSpooler->hJobIdMap, NextId);
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// WMI Trace Event
|
|
//
|
|
LeaveSplSem();
|
|
|
|
LogWmiTraceEvent(NextId, EVENT_TRACE_TYPE_SPL_SPOOLJOB, NULL);
|
|
|
|
EnterSplSem();
|
|
|
|
SplInSem();
|
|
|
|
|
|
dwStatus = JOB_SPOOLING | JOB_TYPE_ADDJOB;
|
|
if (Level == 2 || Level ==3) {
|
|
dwStatus |= JOB_DOWNLEVEL;
|
|
}
|
|
if ((pIniJob = CreateJobEntry(pSpool,
|
|
1,
|
|
(LPBYTE)&DocInfo1,
|
|
NextId,
|
|
bRemote,
|
|
dwStatus,
|
|
pMachineName)) == NULL) {
|
|
|
|
//
|
|
// Free up the JobId.
|
|
//
|
|
vMarkOff( pIniPrinter->pIniSpooler->hJobIdMap, NextId);
|
|
DBGMSG(DBG_WARNING,("Error: CreateJobEntry failed in LocalAddJob\n"));
|
|
LeaveSplSem();
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Level 3 is called only by RDR/SRV. For details see LocalScheduleJob
|
|
//
|
|
pIniJob->AddJobLevel = Level;
|
|
|
|
pIniPrinter->cSpooling++;
|
|
if (pIniPrinter->cSpooling > pIniPrinter->cMaxSpooling) {
|
|
pIniPrinter->cMaxSpooling = pIniPrinter->cSpooling;
|
|
}
|
|
|
|
AddJobEntry(pIniPrinter, pIniJob);
|
|
|
|
pEnd = (LPBYTE)pAddJob+cbBuf;
|
|
|
|
FileNameLength = (wcslen(szFileName) + 1)*sizeof(WCHAR);
|
|
|
|
pEnd -= FileNameLength;
|
|
WORD_ALIGN_DOWN(pEnd);
|
|
|
|
//
|
|
// This is OK because we have already checked that the buffer is long enough
|
|
// to contain this string. Completely reworking this function now is too risky.
|
|
//
|
|
StringCchCopy((LPWSTR)pEnd, FileNameLength, szFileName);
|
|
pAddJob->Path = (LPWSTR)pEnd;
|
|
pAddJob->JobId = pIniJob->JobId;
|
|
|
|
//
|
|
// Now we want to add the job into the spools list of current jobs.
|
|
// This is so that the spool file can be deleted correctly at the end
|
|
// of the job, even if we have aborted.
|
|
//
|
|
pMappedJob = AllocSplMem(sizeof(MAPPED_JOB));
|
|
|
|
pszSpoolFile = AllocSplMem(MAX_PATH * sizeof( WCHAR ));
|
|
|
|
if (pMappedJob && pszSpoolFile)
|
|
{
|
|
BOOL bDuplicate = FALSE;
|
|
DWORD TempJobId = pIniJob->JobId;
|
|
PMAPPED_JOB pTempMappedJob;
|
|
|
|
StringCchCopy(pszSpoolFile, MAX_PATH, szFileName);
|
|
|
|
//
|
|
// Run through the list and make sure we have no duplicates.
|
|
// It is not at all obvious why this would ever be the case.
|
|
//
|
|
for (pTempMappedJob = pSpool->pMappedJob;
|
|
pTempMappedJob;
|
|
pTempMappedJob = pTempMappedJob->pNext) {
|
|
|
|
if (pTempMappedJob->JobId == TempJobId) {
|
|
|
|
//
|
|
// Set the mapped job to record that it was added with AddJob.
|
|
//
|
|
pTempMappedJob->fStatus |= kMappedJobAddJob;
|
|
|
|
bDuplicate = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No duplicates, add this job to the linked list.
|
|
//
|
|
if (!bDuplicate) {
|
|
|
|
pMappedJob->pszSpoolFile = pszSpoolFile;
|
|
pMappedJob->fStatus = kMappedJobAddJob;
|
|
pMappedJob->JobId = TempJobId;
|
|
pMappedJob->pNext = pSpool->pMappedJob;
|
|
pSpool->pMappedJob = pMappedJob;
|
|
|
|
} else {
|
|
|
|
FreeSplMem(pszSpoolFile);
|
|
FreeSplMem(pMappedJob);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
FreeSplMem(pMappedJob);
|
|
FreeSplMem(pszSpoolFile);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Storing pIniJob in pSpool is bogus since you can call AddJob multiple
|
|
// times. We should have a linked list here. If the client calls AddJob
|
|
// two times then closes the handle (no ScheduleJob), then only the last
|
|
// job is rundown and eliminated.
|
|
//
|
|
// This bug has been here since 3.1, and probably isn't worth fixing.
|
|
//
|
|
pSpool->pIniJob = pIniJob;
|
|
pSpool->Status |= SPOOL_STATUS_ADDJOB;
|
|
|
|
SetPrinterChange(pSpool->pIniPrinter,
|
|
pIniJob,
|
|
NVAddJob,
|
|
PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER,
|
|
pSpool->pIniSpooler );
|
|
|
|
//
|
|
// If necessary Start Downlevel Size Detection thread
|
|
//
|
|
|
|
CheckSizeDetectionThread();
|
|
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
LocalScheduleJob(
|
|
HANDLE hPrinter,
|
|
DWORD JobId)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
PSPOOL pSpool=(PSPOOL)hPrinter;
|
|
WCHAR szFileName[MAX_PATH];
|
|
PINIJOB pIniJob;
|
|
DWORD Position;
|
|
DWORD LastError = FALSE;
|
|
HANDLE hPort;
|
|
BOOL bRet;
|
|
NOTIFYVECTOR NotifyVector;
|
|
WIN32_FILE_ATTRIBUTE_DATA FileAttributeData;
|
|
PMAPPED_JOB pMappedJob = NULL;
|
|
|
|
COPYNV(NotifyVector, NVJobStatus);
|
|
|
|
//
|
|
// WMI Trace Event.
|
|
//
|
|
LogWmiTraceEvent(JobId, EVENT_TRACE_TYPE_SPL_TRACKTHREAD, NULL);
|
|
|
|
SplOutSem();
|
|
EnterSplSem();
|
|
|
|
//
|
|
// We should not be calling schedulejob on a Job Handle.
|
|
//
|
|
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER | PRINTER_HANDLE_JOB )) {
|
|
LeaveSplSem();
|
|
return (FALSE);
|
|
}
|
|
|
|
if (pSpool->Status & SPOOL_STATUS_STARTDOC) {
|
|
SetLastError(ERROR_SPL_NO_ADDJOB);
|
|
LeaveSplSem();
|
|
return(FALSE);
|
|
}
|
|
|
|
if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) {
|
|
if (pSpool->pIniPort->Status & PP_MONITOR) {
|
|
SetLastError(ERROR_INVALID_ACCESS);
|
|
LeaveSplSem();
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// This is the "Local Printer masquerading as the Network Printer"
|
|
//
|
|
hPort = pSpool->hPort;
|
|
LeaveSplSem();
|
|
bRet = ScheduleJob(hPort, JobId);
|
|
return(bRet);
|
|
}
|
|
|
|
if ((pIniJob = FindJob(pSpool->pIniPrinter, JobId, &Position)) == NULL) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
LeaveSplSem();
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
if (pIniJob->Status & JOB_SCHEDULE_JOB) {
|
|
|
|
DBGMSG(DBG_WARNING, ("ScheduleJob: job 0x%x (id = %d) already scheduled\n",
|
|
pIniJob, pIniJob->JobId));
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(pIniJob->Status & JOB_TYPE_ADDJOB)) {
|
|
|
|
DBGMSG(DBG_WARNING, ("ScheduleJob: job 0x%x (id = %d) no addjob\n",
|
|
pIniJob, pIniJob->JobId));
|
|
|
|
SetLastError(ERROR_SPL_NO_ADDJOB);
|
|
LeaveSplSem();
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Check to see whether this job was added with AddJob in the past on this
|
|
// handle, if it was, then we can go ahead and schedule it. If it was not,
|
|
// then we fail with Access denied.
|
|
//
|
|
for(pMappedJob = pSpool->pMappedJob; pMappedJob; pMappedJob = pMappedJob->pNext) {
|
|
|
|
//
|
|
// If we found the job on the same handle, clear the Addjob bit.
|
|
//
|
|
if (pMappedJob->JobId == JobId) {
|
|
pMappedJob->fStatus &= ~kMappedJobAddJob;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pMappedJob) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
InterlockedOr((LONG*)&(pIniJob->Status), JOB_SCHEDULE_JOB);
|
|
|
|
if (pIniJob->Status & JOB_SPOOLING) {
|
|
InterlockedAnd((LONG*)&(pIniJob->Status), ~JOB_SPOOLING);
|
|
pIniJob->pIniPrinter->cSpooling--;
|
|
}
|
|
|
|
if ( pIniJob->Status & JOB_TIMEOUT ) {
|
|
InterlockedAnd((LONG*)&(pIniJob->Status), ~(JOB_TIMEOUT | JOB_ABANDON));
|
|
FreeSplStr(pIniJob->pStatus);
|
|
pIniJob->pStatus = NULL;
|
|
}
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// Despooling whilst spooling requires us to wake the writing
|
|
// thread if it is waiting.
|
|
//
|
|
if ( pIniJob->WaitForWrite != NULL )
|
|
SetEvent(pIniJob->WaitForWrite);
|
|
|
|
//
|
|
// Release any thread waiting on SeekPrinter for this job.
|
|
//
|
|
SeekPrinterSetEvent(pIniJob, NULL, TRUE);
|
|
|
|
SPLASSERT(pIniJob->cRef != 0);
|
|
|
|
DECJOBREF(pIniJob);
|
|
|
|
DBGMSG(DBG_TRACE, ("ScheduleJob:cRef = %d\n", pIniJob->cRef));
|
|
|
|
//
|
|
// FP Change
|
|
// For File pools, we know the Filename of the spool file, so
|
|
// we can just copy it in.
|
|
//
|
|
if ( pIniJob->pszSplFileName )
|
|
{
|
|
StringCchCopy(szFileName, COUNTOF(szFileName), pIniJob->pszSplFileName);
|
|
}
|
|
else
|
|
{
|
|
GetFullNameFromId(pSpool->pIniPrinter, pIniJob->JobId, TRUE, szFileName, COUNTOF(szFileName), FALSE);
|
|
}
|
|
|
|
bRet = GetFileAttributesEx(szFileName,
|
|
GetFileExInfoStandard,
|
|
&FileAttributeData);
|
|
|
|
//
|
|
// According to MSDN: The ScheduleJob function checks for a valid spool file.
|
|
// If there is an invalid spool file, or if it is empty, ScheduleJob deletes
|
|
// both the spool file and the corresponding print job entry in the print spooler.
|
|
//
|
|
// The RDR/SRV will call AddJob even if the caller of CreateFile did noy request
|
|
// WRITE access. This will cause us at add a job, but nobody will ever write to
|
|
// the spooler file. In this case, we delete the job. For this reason we have
|
|
// level 3 for AddJob. Level 3 is meant to be used only by RDR/SRV.
|
|
//
|
|
if (!bRet ||
|
|
!(FileAttributeData.nFileSizeLow || FileAttributeData.nFileSizeHigh) && pIniJob->AddJobLevel == 3) {
|
|
|
|
DBGMSG(DBG_WARNING, ("Could not GetFileAttributesEx %ws in ScheduleJob or file size is 0\n", szFileName));
|
|
|
|
DeleteJob(pIniJob, BROADCAST);
|
|
pSpool->pIniJob = NULL;
|
|
pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
|
|
LeaveSplSem();
|
|
|
|
//
|
|
// If we deleted the job because the spool file was empty and the job came via RDR/SRV
|
|
// In this case we return success.
|
|
//
|
|
if (bRet)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// We delete the job because the spool is not found
|
|
//
|
|
SetLastError(ERROR_SPOOL_FILE_NOT_FOUND);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Do not accept spool files larger than 4GB
|
|
//
|
|
if (FileAttributeData.nFileSizeHigh && !ValidRawDatatype(pIniJob->pDatatype))
|
|
{
|
|
DeleteJob(pIniJob, BROADCAST);
|
|
pSpool->pIniJob = NULL;
|
|
pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
|
|
LeaveSplSem();
|
|
|
|
SetLastError(ERROR_ARITHMETIC_OVERFLOW);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If size changed, we must update our size
|
|
// and potentially notify people.
|
|
//
|
|
if (pIniJob->Size != FileAttributeData.nFileSizeLow) {
|
|
ADDNV(NotifyVector, NVSpoolJob);
|
|
pIniJob->Size = FileAttributeData.nFileSizeLow;
|
|
}
|
|
|
|
WriteShadowJob(pIniJob, FALSE);
|
|
|
|
if (pIniJob->Status & JOB_PENDING_DELETION) {
|
|
|
|
DBGMSG(DBG_TRACE, ("LocalScheduleJob: Deleting Job because its pending deletion\n"));
|
|
DeleteJob(pIniJob, BROADCAST);
|
|
|
|
} else {
|
|
|
|
CHECK_SCHEDULER();
|
|
|
|
SetPrinterChange(pIniJob->pIniPrinter,
|
|
pIniJob,
|
|
NotifyVector,
|
|
PRINTER_CHANGE_SET_JOB,
|
|
pIniJob->pIniPrinter->pIniSpooler );
|
|
}
|
|
|
|
pSpool->pIniJob = NULL;
|
|
|
|
pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
|
|
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
return(TRUE);
|
|
}
|
|
|