/*++ Copyright (c) 1991-1993 Microsoft Corporation Module Name: DosPrtW.c Abstract: This module provides the UNICODE mapping layer from the old DosPrint APIs to the new all singing all dancing beautiful Print APIs. (The ANSI mapping layer is in DosPrint.c) Author: Dave Snipp (DaveSn) 26-Apr-1991 Revision History: 09-Jul-1992 JohnRo Created this file (from DaveSn's DosPrint.c) for RAID 10324: net print vs. UNICODE. 05-Oct-1992 JohnRo RAID 3556: DosPrintQGetInfo(from downlevel) level 3, rc=124. (4&5 too.) RAID 3580: lmsvcs.exe: access violation from OS/2 DosPrintJobGetInfo. RAID 8333: view printer queues hangs DOS LM enh client. Make sure data type in job level 1 is null terminated. Fixed job submitted times. Fixed error code if GlobalAlloc fails. Fixed memory leak in DosPrintQGetInfoW. Fixed DosPrintQEnumW level 5 array bug. Fixed DosPrintJobEnumW levels 2 and 3. 25-Nov-1992 JohnRo RAID 1661: downlevel to NT DosPrintDestEnum not supported. Added code to track down empty queue name. Quiet normal debug output. Avoid const vs. volatile compiler warnings. Avoid other new compiler warnings. 08-Feb-1993 JohnRo RAID 10164: Data misalignment error during XsDosPrintQGetInfo(). 22-Mar-1993 JohnRo RAID 2974: NET PRINT says NT printer is held when it isn't. 11-May-1993 JohnRo RAID 9942: fix queue name in job info level 3. 14-May-1993 JohnRo RAID 9961: DosPrintDestEnum returns NO_ERROR to downlevel but pcReturned=0; should return NERR_DestNotFound. Fixed data type returned from PrjInfoFixedSizeW(). 18-May-1993 JohnRo DosPrintQGetInfoW underestimates number of bytes needed. Use NetpKdPrint() where possible. Made changes suggested by PC-LINT. 04-Jun-1993 JohnRo RAID 10222: DosPrintQEnumW returns ERROR_INVALID_USER_BUFFER when queue is empty. Made changes suggested by PC-LINT 5.0 08-Jul-1993 JohnRo RAID 15509: GetJob() API sometimes returned TRUE, even on error case. Also added some >64KB checks. Added some assert checks... 13-Jul-1993 JohnRo Intermittent empty print queue (was buggy after some MyEnumJobs calls). 29-Mar-1995 AlbertT Support for pause/resume/purge printer queue added. SetJobInfo 1 comment field (translated into document name) support added so that chicago clients can set the doc name. --*/ #ifndef UNICODE #error "RxPrint APIs assume RxRemoteApi uses wide characters." #endif #define NOMINMAX #define NOSERVICE // Avoid vs. conflicts. #include //#include #include #include #include #ifdef _WINSPOOL_ #error "Include of winspool.h moved, make sure it doesn't get UNICODE." #endif #undef UNICODE #undef TEXT #define TEXT(quote) quote #include #undef TEXT #define TEXT(quote) __TEXT(quote) #define UNICODE #ifndef _WINSPOOL_ #error "Oops, winspool.h changed, make sure this code is still OK." #endif #include #include // CommandALocalJob(), etc. #include // NetApiBufferFree(), etc. #include // NO_ERROR, NERR_, and ERROR_ equates. #include // LPSHARE_INFO_1, STYPE_ equates, etc. #include // PREFIX_ equates. #include // offsetof(). #include // NetpSystemTimeToGmtTime(). #include // WCSSIZE(), NetpNCopy{type}To{type}. #include // wsclen(), wcscpy(), etc. #include "myspool.h" #define STR_CONV_SIZE(psz) ( (strlen(psz)+1) * sizeof(WCHAR) ) // NULL_STR_CONV_SIZE: Compute size needed for converted string, which // is possibly a null pointer but downlevel really wants ptr to null char. #define NULL_STR_CONV_SIZE(psz) ( (psz) ? STR_CONV_SIZE(psz) : sizeof(WCHAR) ) #define ARRAY_END ((DWORD) -1) #define MAX_WORD ( (WORD) (~0) ) #define MY_PROTOCOL_LIMIT_ERROR ERROR_NOT_ENOUGH_MEMORY #define WIN95_DRIVER_SHARE "\\print$\\WIN40\\0" VOID NetpSetJobCountForQueue( IN DWORD QueueLevel, IN OUT LPVOID Queue, IN BOOL HasUnicodeStrings, IN DWORD JobCount ); DBGSTATIC LPWSTR PackAnsiStringsToW( LPSTR *pSource, LPBYTE pDest, CONST DWORD *DestOffsets, LPWSTR pEnd ) { // Make sure our end pointer is WCHAR aligned or we'll fault later ROUND_DOWN_POINTER( pEnd, ALIGN_WCHAR ); while (*DestOffsets != ARRAY_END) { if (*pSource) { pEnd-=(strlen(*pSource) + 1); // Copy the string and convert chars while we're at it. NetpCopyStrToWStr(pEnd, *pSource); *(LPWSTR *)(pDest+*DestOffsets) = pEnd; } else { --pEnd; // need 1 char for this. *pEnd = L'\0'; *(LPWSTR *)(pDest+*DestOffsets) = pEnd; } pSource++; DestOffsets++; } return pEnd; } DBGSTATIC DWORD PrjInfoFixedSizeW( IN DWORD Level // assumed valid ) { switch (Level) { case 0: return sizeof(WORD); // job number. case 1: return (sizeof(PRJINFOW)); case 2: return (sizeof(PRJINFO2W)); case 3: return (sizeof(PRJINFO3W)); default: NetpAssert( FALSE ); return (0); } /*NOTREACHED*/ } DBGSTATIC DWORD GetPrjInfoSizeW( IN DWORD Level, IN LPJOB_INFO_2 pJob, IN LPCWSTR QueueNameW ) { NetpAssert( pJob != NULL ); switch (Level) { case 0: return sizeof(WORD); // job number. case 1: return sizeof(PRJINFOW) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pParameters) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pStatus) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ); // fake pszComment case 2: return sizeof(PRJINFO2W) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pUserName) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ) + // fake pszComment NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ); case 3: NetpAssert( QueueNameW != NULL ); return sizeof(PRJINFO3W) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pUserName) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ) + // fake pszComment NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pNotifyName) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDatatype) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pParameters) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pStatus) ) + WCSSIZE( QueueNameW ) + // pszQueue NULL_STR_CONV_SIZE( (LPSTR) (pJob->pPrintProcessor) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pParameters) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDriverName) ) + NULL_STR_CONV_SIZE( (LPSTR) (pJob->pPrinterName) ); default: NetpKdPrint(( PREFIX_DOSPRINT "GetPrjInfoSizeW: invalid level!\n" )); return 0; } /*NOTREACHED*/ } // Print job info string table (for level 1). DBGSTATIC CONST DWORD PrjInfo1StringsW[]={ offsetof(PRJINFOW, pszParms), offsetof(PRJINFOW, pszStatus), offsetof(PRJINFOW, pszComment), ARRAY_END}; // Print job info string table (for level 2). DBGSTATIC CONST DWORD PrjInfo2StringsW[]={ offsetof(PRJINFO2W, pszUserName), offsetof(PRJINFO2W, pszComment), offsetof(PRJINFO2W, pszDocument), (DWORD) -1}; // Print job info string table (for items which level 3 has on top of level 2). DBGSTATIC CONST DWORD PrjInfo3StringsW[]={ offsetof(PRJINFO3W, pszNotifyName), offsetof(PRJINFO3W, pszDataType), offsetof(PRJINFO3W, pszParms), offsetof(PRJINFO3W, pszStatus), offsetof(PRJINFO3W, pszQProcName), offsetof(PRJINFO3W, pszQProcParms), offsetof(PRJINFO3W, pszDriverName), offsetof(PRJINFO3W, pszPrinterName), (DWORD) -1}; DBGSTATIC LPWSTR CopyJobToPrjInfoW( IN DWORD Level, IN LPJOB_INFO_2 pJob, IN LPCWSTR QueueNameW, OUT PBYTE pBuffer, IN OUT LPWSTR pEnd ) { LPSTR *pSourceStrings; NET_API_STATUS rc; NetpAssert( pBuffer != NULL ); NetpAssert( pEnd != NULL ); NetpAssert( pJob != NULL ); switch (Level) { case 0: { PWORD pJobIds = (PWORD) pBuffer; *pJobIds = (WORD)pJob->JobId; } break; case 1: { LPSTR SourceStrings[sizeof(PrjInfo1StringsW)/sizeof(DWORD)]; PPRJINFOW pPrjInfo = (LPVOID) pBuffer; pSourceStrings=SourceStrings; *pSourceStrings++ = (LPSTR) (pJob->pParameters); *pSourceStrings++ = (LPSTR) (pJob->pStatus); *pSourceStrings++ = (LPSTR) (pJob->pDocument); // fake pszComment pEnd = PackAnsiStringsToW( SourceStrings, (LPBYTE) (LPVOID) pPrjInfo, PrjInfo1StringsW, pEnd); pPrjInfo->uJobId = (WORD)pJob->JobId; if (pJob->pUserName) (VOID) NetpNCopyStrToWStr( (LPWSTR) (pPrjInfo->szUserName), (LPSTR) (pJob->pUserName), LM20_UNLEN+1); else pPrjInfo->szUserName[0] = L'\0'; if (pJob->pNotifyName) (VOID) NetpNCopyStrToWStr( (LPWSTR) (pPrjInfo->szNotifyName), (LPSTR) (pJob->pNotifyName), LM20_CNLEN+1); else pPrjInfo->szNotifyName[0] = L'\0'; if (pJob->pDatatype) { (VOID) NetpNCopyStrToWStr( (LPWSTR) (pPrjInfo->szDataType), (LPSTR) (pJob->pDatatype), DTLEN+1); pPrjInfo->szDataType[DTLEN] = L'\0'; } else { pPrjInfo->szDataType[0] = L'\0'; } pPrjInfo->uPosition = (WORD)pJob->Position; pPrjInfo->fsStatus = PrjStatusFromJobStatus( pJob->Status ); rc = NetpSystemTimeToGmtTime( &pJob->Submitted, &pPrjInfo->ulSubmitted ); NetpAssert( rc == NO_ERROR ); pPrjInfo->ulSize = pJob->Size; } break; case 2: /*FALLTHROUGH*/ case 3: { PPRJINFO2W pPrjInfo = (LPVOID) pBuffer; LPSTR SourceStrings[sizeof(PrjInfo2StringsW)/sizeof(DWORD)]; pSourceStrings=SourceStrings; *pSourceStrings++ = (LPSTR) (pJob->pUserName); *pSourceStrings++ = (LPSTR) (pJob->pDocument); // fake pszComment *pSourceStrings++ = (LPSTR) (pJob->pDocument); pEnd = PackAnsiStringsToW( SourceStrings, (LPBYTE) (LPVOID) pPrjInfo, PrjInfo2StringsW, pEnd); pPrjInfo->uJobId = (WORD)pJob->JobId; pPrjInfo->uPriority = (WORD)pJob->Priority; pPrjInfo->uPosition = (WORD)pJob->Position; pPrjInfo->fsStatus = PrjStatusFromJobStatus( pJob->Status ); rc = NetpSystemTimeToGmtTime( &pJob->Submitted, &pPrjInfo->ulSubmitted ); NetpAssert( rc == NO_ERROR ); pPrjInfo->ulSize = pJob->Size; } if (Level == 3) { PPRJINFO3W pPrjInfo = (LPVOID) pBuffer; LPSTR SourceStrings[sizeof(PrjInfo3StringsW)/sizeof(DWORD)]; // // Copy queue name first, as it is already right char set. // NetpAssert( QueueNameW != NULL ); pEnd-=(wcslen(QueueNameW) + 1); (VOID) wcscpy(pEnd, QueueNameW); pPrjInfo->pszQueue = pEnd; // // Copy and convert other strings. // pSourceStrings=SourceStrings; *pSourceStrings++ = (LPSTR) (pJob->pNotifyName); *pSourceStrings++ = (LPSTR) (pJob->pDatatype); *pSourceStrings++ = (LPSTR) (pJob->pParameters); *pSourceStrings++ = (LPSTR) (pJob->pStatus); *pSourceStrings++ = (LPSTR) (pJob->pPrintProcessor); *pSourceStrings++ = (LPSTR) (pJob->pParameters); *pSourceStrings++ = (LPSTR) (pJob->pDriverName); *pSourceStrings++ = (LPSTR) (pJob->pPrinterName); pEnd = PackAnsiStringsToW( SourceStrings, (LPBYTE) (LPVOID) pPrjInfo, PrjInfo3StringsW, pEnd); pPrjInfo->pDriverData = NULL; } break; default: NetpKdPrint(( PREFIX_DOSPRINT "CopyJobToPrjInfoW: invalid level!\n" )); } return pEnd; } DBGSTATIC DWORD GetPrqInfoSizeW( IN DWORD Level, IN LPCWSTR QueueNameW, IN LPPRINTER_INFO_2 pPrinter ) { NetpAssert( QueueNameW != NULL ); NetpAssert( (*QueueNameW) != L'\0' ); switch (Level) { case 0: return ( (LM20_QNLEN+1) * sizeof(WCHAR) ); case 1: /*FALLTHROUGH*/ case 2: return sizeof(PRQINFOW) + NULL_STR_CONV_SIZE( pPrinter->pSepFile ) + NULL_STR_CONV_SIZE( pPrinter->pPrintProcessor ) + NULL_STR_CONV_SIZE( pPrinter->pPortName ) + NULL_STR_CONV_SIZE( pPrinter->pParameters ) + NULL_STR_CONV_SIZE( pPrinter->pComment ); case 3: /*FALLTHROUGH*/ case 4: NetpAssert( QueueNameW != NULL ); return sizeof(PRQINFO3W) + WCSSIZE( QueueNameW ) + // pszName NULL_STR_CONV_SIZE( pPrinter->pSepFile ) + NULL_STR_CONV_SIZE( pPrinter->pPrintProcessor ) + NULL_STR_CONV_SIZE( pPrinter->pParameters ) + NULL_STR_CONV_SIZE( pPrinter->pComment ) + NULL_STR_CONV_SIZE( pPrinter->pPortName ) + NULL_STR_CONV_SIZE( pPrinter->pDriverName ); case 5: NetpAssert( QueueNameW != NULL ); return sizeof(LPWSTR) + WCSSIZE( QueueNameW ); // pszName default: NetpKdPrint(( PREFIX_DOSPRINT "GetPrqInfoSizeW: invalid level!\n" )); } return 0; } DBGSTATIC DWORD GetDrvInfoSizeW( IN DWORD Level, IN LPDRIVER_INFO_3A pDriverInfo3, IN LPCSTR pUNCSharePath, OUT LPDWORD pdwDependentFileCount ) { LPSTR psz; DWORD dwSize; switch (Level) { case 52: dwSize = sizeof(PRQINFO52W) + NULL_STR_CONV_SIZE(pDriverInfo3->pName) + NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pDriverPath)) + NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pDataFile)) + NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pConfigFile)) + NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pHelpFile)) + NULL_STR_CONV_SIZE(pDriverInfo3->pDefaultDataType) + NULL_STR_CONV_SIZE(pDriverInfo3->pMonitorName) + NULL_STR_CONV_SIZE(pUNCSharePath); *pdwDependentFileCount = 0; for ( psz = pDriverInfo3->pDependentFiles; psz && *psz ; psz += strlen(psz) + 1 ) { dwSize += NULL_STR_CONV_SIZE(GetDependentFileNameA(psz)); (*pdwDependentFileCount)++; } // // For the '\0's // dwSize += (MAX_DEPENDENT_FILES-*pdwDependentFileCount)*sizeof(WCHAR); return dwSize; default: NetpKdPrint(( PREFIX_DOSPRINT "GetDrvInfoSizeW: invalid level!\n" )); } return 0; } DBGSTATIC DWORD PrqInfoFixedSizeW( IN DWORD Level // assumed valid ) { switch (Level) { case 0: return ( (LM20_QNLEN+1) * sizeof(WCHAR) ); case 1: /*FALLTHROUGH*/ case 2: return (sizeof(PRQINFOW)); case 3: /*FALLTHROUGH*/ case 4: return (sizeof(PRQINFO3W)); case 5: return (sizeof(LPWSTR)); default: NetpAssert( FALSE ); // Level should be valid! return (0); } /*NOTREACHED*/ } // String table for Q levels 1,2 DBGSTATIC CONST DWORD PrqInfo1StringsW[]={ offsetof(PRQINFOW, pszSepFile), offsetof(PRQINFOW, pszPrProc), offsetof(PRQINFOW, pszDestinations), offsetof(PRQINFOW, pszParms), offsetof(PRQINFOW, pszComment), ARRAY_END}; // String table for Q levels 3,4. DBGSTATIC CONST DWORD PrqInfo3StringsW[]={ offsetof(PRQINFO3W, pszSepFile), offsetof(PRQINFO3W, pszPrProc), offsetof(PRQINFO3W, pszParms), offsetof(PRQINFO3W, pszComment), offsetof(PRQINFO3W, pszPrinters), offsetof(PRQINFO3W, pszDriverName), (DWORD) -1}; // Print driver info3 string table (for level 52) DBGSTATIC CONST DWORD PrqInfo52StringsW[]={ offsetof(PRQINFO52W, pszModelName), offsetof(PRQINFO52W, pszDriverName), offsetof(PRQINFO52W, pszDataFileName), offsetof(PRQINFO52W, pszMonitorName), offsetof(PRQINFO52W, pszDriverPath), offsetof(PRQINFO52W, pszDefaultDataType), offsetof(PRQINFO52W, pszHelpFile), offsetof(PRQINFO52W, pszConfigFile), offsetof(PRQINFO52W, pszDependentNames[0]), offsetof(PRQINFO52W, pszDependentNames[1]), offsetof(PRQINFO52W, pszDependentNames[2]), offsetof(PRQINFO52W, pszDependentNames[3]), offsetof(PRQINFO52W, pszDependentNames[4]), offsetof(PRQINFO52W, pszDependentNames[5]), offsetof(PRQINFO52W, pszDependentNames[6]), offsetof(PRQINFO52W, pszDependentNames[7]), offsetof(PRQINFO52W, pszDependentNames[8]), offsetof(PRQINFO52W, pszDependentNames[9]), offsetof(PRQINFO52W, pszDependentNames[10]), offsetof(PRQINFO52W, pszDependentNames[11]), offsetof(PRQINFO52W, pszDependentNames[12]), offsetof(PRQINFO52W, pszDependentNames[13]), offsetof(PRQINFO52W, pszDependentNames[14]), offsetof(PRQINFO52W, pszDependentNames[15]), offsetof(PRQINFO52W, pszDependentNames[16]), offsetof(PRQINFO52W, pszDependentNames[17]), offsetof(PRQINFO52W, pszDependentNames[18]), offsetof(PRQINFO52W, pszDependentNames[19]), offsetof(PRQINFO52W, pszDependentNames[20]), offsetof(PRQINFO52W, pszDependentNames[21]), offsetof(PRQINFO52W, pszDependentNames[22]), offsetof(PRQINFO52W, pszDependentNames[23]), offsetof(PRQINFO52W, pszDependentNames[24]), offsetof(PRQINFO52W, pszDependentNames[25]), offsetof(PRQINFO52W, pszDependentNames[26]), offsetof(PRQINFO52W, pszDependentNames[27]), offsetof(PRQINFO52W, pszDependentNames[28]), offsetof(PRQINFO52W, pszDependentNames[29]), offsetof(PRQINFO52W, pszDependentNames[30]), offsetof(PRQINFO52W, pszDependentNames[31]), offsetof(PRQINFO52W, pszDependentNames[32]), offsetof(PRQINFO52W, pszDependentNames[33]), offsetof(PRQINFO52W, pszDependentNames[34]), offsetof(PRQINFO52W, pszDependentNames[35]), offsetof(PRQINFO52W, pszDependentNames[36]), offsetof(PRQINFO52W, pszDependentNames[37]), offsetof(PRQINFO52W, pszDependentNames[38]), offsetof(PRQINFO52W, pszDependentNames[39]), offsetof(PRQINFO52W, pszDependentNames[40]), offsetof(PRQINFO52W, pszDependentNames[41]), offsetof(PRQINFO52W, pszDependentNames[42]), offsetof(PRQINFO52W, pszDependentNames[43]), offsetof(PRQINFO52W, pszDependentNames[44]), offsetof(PRQINFO52W, pszDependentNames[45]), offsetof(PRQINFO52W, pszDependentNames[46]), offsetof(PRQINFO52W, pszDependentNames[47]), offsetof(PRQINFO52W, pszDependentNames[48]), offsetof(PRQINFO52W, pszDependentNames[49]), offsetof(PRQINFO52W, pszDependentNames[50]), offsetof(PRQINFO52W, pszDependentNames[51]), offsetof(PRQINFO52W, pszDependentNames[52]), offsetof(PRQINFO52W, pszDependentNames[53]), offsetof(PRQINFO52W, pszDependentNames[54]), offsetof(PRQINFO52W, pszDependentNames[55]), offsetof(PRQINFO52W, pszDependentNames[56]), offsetof(PRQINFO52W, pszDependentNames[57]), offsetof(PRQINFO52W, pszDependentNames[58]), offsetof(PRQINFO52W, pszDependentNames[59]), offsetof(PRQINFO52W, pszDependentNames[60]), offsetof(PRQINFO52W, pszDependentNames[61]), offsetof(PRQINFO52W, pszDependentNames[62]), offsetof(PRQINFO52W, pszDependentNames[63]), (DWORD) -1}; DBGSTATIC LPWSTR CopyPrinterToPrqInfoW( IN LPPRINTER_INFO_2 pPrinter, IN DWORD Level, OUT LPBYTE pBuffer, IN LPCWSTR QueueNameW, OUT LPWSTR pEnd ) { LPSTR *pSourceStrings; NetpAssert( pEnd != NULL ); NetpAssert( QueueNameW != NULL ); NetpAssert( (*QueueNameW) != L'\0' ); switch (Level) { case 0: (VOID) wcsncpy( (LPWSTR) (LPVOID) pBuffer, QueueNameW, LM20_QNLEN); break; case 1: /*FALLTHROUGH*/ case 2: { LPSTR SourceStrings[sizeof(PrqInfo1StringsW)/sizeof(DWORD)]; PPRQINFOW pPrqInfo = (LPVOID) pBuffer; pSourceStrings=SourceStrings; *pSourceStrings++ = pPrinter->pSepFile; *pSourceStrings++ = pPrinter->pPrintProcessor; *pSourceStrings++ = pPrinter->pPortName; *pSourceStrings++ = pPrinter->pParameters; *pSourceStrings++ = pPrinter->pComment; pEnd = PackAnsiStringsToW( SourceStrings, (LPBYTE) (LPVOID) pPrqInfo, PrqInfo1StringsW, pEnd); NetpAssert( QueueNameW != NULL ); (VOID) wcsncpy( pPrqInfo->szName, // dest QueueNameW, // src LM20_QNLEN); // char count pPrqInfo->szName[LM20_QNLEN] = (USHORT)0; pPrqInfo->uPriority = (WORD)pPrinter->Priority; pPrqInfo->uStartTime = (WORD)pPrinter->StartTime; pPrqInfo->uUntilTime = (WORD)pPrinter->UntilTime; pPrqInfo->fsStatus = PrqStatusFromPrinterStatus( pPrinter->Status ); pPrqInfo->cJobs = (WORD)pPrinter->cJobs; } break; case 3: /*FALLTHROUGH*/ case 4: { LPSTR SourceStrings[sizeof(PrqInfo3StringsW)/sizeof(DWORD)]; PPRQINFO3W pPrqInfo = (LPVOID) pBuffer; // // Copy queue name first, as it is already right char set. // NetpAssert( QueueNameW != NULL ); pEnd-=(wcslen(QueueNameW) + 1); (VOID) wcscpy(pEnd, QueueNameW); pPrqInfo->pszName = pEnd; // // Copy and convert other strings. // pSourceStrings=SourceStrings; *pSourceStrings++ = pPrinter->pSepFile; *pSourceStrings++ = pPrinter->pPrintProcessor; *pSourceStrings++ = pPrinter->pParameters; *pSourceStrings++ = pPrinter->pComment; *pSourceStrings++ = pPrinter->pPortName; // pszPrinters *pSourceStrings++ = pPrinter->pDriverName; pEnd = PackAnsiStringsToW( SourceStrings, (LPBYTE) (LPVOID) pPrqInfo, PrqInfo3StringsW, pEnd); pPrqInfo->uPriority = (WORD)pPrinter->Priority; pPrqInfo->uStartTime = (WORD)pPrinter->StartTime; pPrqInfo->uUntilTime = (WORD)pPrinter->UntilTime; pPrqInfo->fsStatus = PrqStatusFromPrinterStatus( pPrinter->Status ); pPrqInfo->cJobs = (WORD)pPrinter->cJobs; pPrqInfo->pDriverData = NULL; // Note: if level is 4, caller will add array of jobs after this. break; } case 5: NetpAssert( QueueNameW != NULL ); pEnd -= (wcslen( QueueNameW ) + 1); * (LPWSTR *) pBuffer = pEnd; (VOID) wcscpy( pEnd, // dest QueueNameW ); // src break; default: NetpKdPrint(( PREFIX_DOSPRINT "CopyPrinterToPrqInfoW: invalid level!\n" )); } return pEnd; } DBGSTATIC LPWSTR CopyDriverToPrqInfoW( IN LPDRIVER_INFO_3A pDriver3, IN DWORD dwDependentFileCount, IN LPSTR pUNCSharePath, IN DWORD Level, OUT LPBYTE pBuffer, OUT LPWSTR pEnd ) { LPSTR *pSourceStrings; LPSTR psz; NetpAssert( pEnd != NULL ); NetpAssert(MAX_DEPENDENT_FILES == 64); switch (Level) { case 52: { PPRQINFO52W pPrqInfo = (LPVOID) pBuffer; LPSTR SourceStrings[sizeof(PrqInfo52StringsW)/sizeof(DWORD)]; ZeroMemory((LPBYTE)SourceStrings, sizeof(SourceStrings)); pSourceStrings=SourceStrings; *pSourceStrings++ = pDriver3->pName; *pSourceStrings++ = GetFileNameA(pDriver3->pDriverPath); *pSourceStrings++ = GetFileNameA(pDriver3->pDataFile); *pSourceStrings++ = GetFileNameA(pDriver3->pMonitorName); *pSourceStrings++ = pUNCSharePath; *pSourceStrings++ = GetFileNameA(pDriver3->pDefaultDataType); *pSourceStrings++ = GetFileNameA(pDriver3->pHelpFile); *pSourceStrings++ = GetFileNameA(pDriver3->pConfigFile); for ( psz = pDriver3->pDependentFiles ; psz && *psz ; psz += strlen(psz) + 1 ) { *pSourceStrings++ = GetDependentFileNameA(psz); } pEnd = PackAnsiStringsToW( SourceStrings, (LPBYTE) (LPVOID)pPrqInfo, PrqInfo52StringsW, pEnd); pPrqInfo->uVersion = (WORD)pDriver3->cVersion; pPrqInfo->cDependentNames = (WORD)dwDependentFileCount; } break; default: NetpKdPrint(( PREFIX_DOSPRINT "CopyPrinterToPrqInfoW: invalid level!\n" )); } return pEnd; } DBGSTATIC NET_API_STATUS ComputeSpaceNeededForJobs( IN LPCWSTR QueueNameW, IN DWORD QLevel, IN HANDLE PrinterHandle, OUT LPDWORD pcbNeeded ) { NET_API_STATUS ApiStatus; DWORD cJobs; DWORD cbJobs; DWORD cbNeeded = 0; DWORD JobLevel; LPJOB_INFO_2 pJob = NULL; LPJOB_INFO_2 pJobs = NULL; NetpAssert( (QLevel==2) || (QLevel==4) ); NetpAssert( QueueNameW != NULL ); if (QLevel==2) { JobLevel = 1; } else { JobLevel = 2; } if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, NULL, 0, &cbJobs, &cJobs)) { ApiStatus = (NET_API_STATUS) GetLastError(); if (ApiStatus == ERROR_INSUFFICIENT_BUFFER) { pJobs = (LPVOID) GlobalAlloc(GMEM_FIXED, cbJobs); if (pJobs == NULL) { ApiStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } } else { NetpKdPrint(( PREFIX_DOSPRINT "ComputeSpaceNeededForJobs: got error " FORMAT_API_STATUS " from MyEnumJobs(first).\n", ApiStatus )); goto Cleanup; } } if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, (LPBYTE)pJobs, cbJobs, &cbJobs, &cJobs)) { ApiStatus = (NET_API_STATUS) GetLastError(); NetpAssert( ApiStatus != ERROR_INSUFFICIENT_BUFFER ); NetpKdPrint(( PREFIX_DOSPRINT "ComputeSpaceNeededForJobs: got error " FORMAT_API_STATUS " from MyEnumJobs(second)\n", ApiStatus )); goto Cleanup; } if (cJobs == 0) { cbNeeded = 0; ApiStatus = NO_ERROR; goto Cleanup; } if (pJobs == NULL) { NetpKdPrint(( PREFIX_DOSPRINT "ComputeSpaceNeededForJobs: never allocated array!\n" )); ApiStatus = NERR_InternalError; goto Cleanup; } pJob=pJobs; while (cJobs--) { cbNeeded+=GetPrjInfoSizeW(JobLevel, pJob++, QueueNameW); } *pcbNeeded=(WORD)cbNeeded; // final byte count for this queue's jobs. ApiStatus = NO_ERROR; Cleanup: if (pJobs != NULL) { (VOID) GlobalFree(pJobs); } *pcbNeeded = cbNeeded; // final byte count for this queue's jobs. return (ApiStatus); } // ComputeSpaceNeededForJobs DBGSTATIC NET_API_STATUS AppendJobsToPrqW( IN LPCWSTR QueueNameW, IN DWORD QLevel, IN HANDLE PrinterHandle, OUT LPBYTE pbBuf, IN DWORD cbBuf, IN LPVOID pEnd, OUT LPVOID * pNewEnd, OUT LPDWORD pcbNeeded, OUT LPDWORD pcReturned, IN BOOL AllowPartialData ) { DWORD cJobs; DWORD cbJobs; DWORD cbNeeded = 0; DWORD cbPrj; DWORD JobLevel; DWORD rc; DWORD JobSize; DWORD BytesLeft; DWORD JobsStored; LPJOB_INFO_2 pJob = NULL; LPJOB_INFO_2 pJobs = NULL; NetpAssert( (QLevel==2) || (QLevel==4) ); NetpAssert( QueueNameW != NULL ); if (QLevel==2) { cbPrj = sizeof(PRJINFOW); JobLevel = 1; } else { cbPrj = sizeof(PRJINFO2W); JobLevel = 2; } if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, NULL, 0, &cbJobs, pcReturned)) { rc = GetLastError(); if (rc == ERROR_INSUFFICIENT_BUFFER) { pJobs = (LPVOID) GlobalAlloc(GMEM_FIXED, cbJobs); if (pJobs == NULL) { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } } else { NetpKdPrint(( PREFIX_DOSPRINT "AppendJobsToPrqW: got error " FORMAT_API_STATUS " from MyEnumJobs(first)\n", rc )); goto Cleanup; } } if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, (LPBYTE)pJobs, cbJobs, &cbJobs, pcReturned)) { rc = GetLastError(); NetpAssert( rc != ERROR_INSUFFICIENT_BUFFER ); NetpKdPrint(( PREFIX_DOSPRINT "AppendJobsToPrqW: got error " FORMAT_API_STATUS " from MyEnumJobs(second)\n", rc )); goto Cleanup; } if (*pcReturned == 0) { cbNeeded = 0; rc = NO_ERROR; goto Cleanup; } if (pJobs == NULL) { NetpKdPrint(( PREFIX_DOSPRINT "AppendJobsToPrqW: never allocated array!\n" )); rc = NERR_InternalError; goto Cleanup; } cJobs = *pcReturned; pJob=pJobs; while (cJobs--) cbNeeded+=GetPrjInfoSizeW(JobLevel, pJob++, QueueNameW); *pcbNeeded = cbNeeded; // final byte count for this queue's jobs. if (cbNeeded <= cbBuf) { cJobs = *pcReturned; pJob=pJobs; while (cJobs--) { pEnd = CopyJobToPrjInfoW(JobLevel, pJob++, QueueNameW, pbBuf, pEnd); pbBuf += cbPrj; // Note: Wasn't DWORD aligned } rc = NO_ERROR; } else { // // See if the user wants to receive as much data as we can fit. // if( AllowPartialData == TRUE ) { cJobs = *pcReturned; pJob = pJobs; JobsStored = 0; BytesLeft = cbBuf; while( cJobs-- ) { JobSize = GetPrjInfoSizeW( JobLevel, pJob, QueueNameW ); if( JobSize <= BytesLeft ) { // // This job will fit. Add it in. // pEnd = CopyJobToPrjInfoW( JobLevel, pJob++, QueueNameW, pbBuf, pEnd ); pbBuf += cbPrj; // Note: Wasn't DWORD aligned BytesLeft -= JobSize; JobsStored++; } else { // // The buffer is full. // break; } } if( JobsStored != 0 ) { // // Return what we were able to store. // *pcReturned = JobsStored; rc = NO_ERROR; } else { rc = NERR_BufTooSmall; } } else { rc = NERR_BufTooSmall; } } Cleanup: if (pJobs != NULL) { (VOID) GlobalFree(pJobs); } *pcbNeeded = cbNeeded; // final byte count for this queue's jobs. if (pNewEnd != NULL) { *pNewEnd = pEnd; } return (rc); } SPLERR SPLENTRY DosPrintQGetInfoW( LPWSTR pszServer, LPWSTR pszQueueName, WORD uLevel, PBYTE pbBuf, WORD cbBuf, PUSHORT pcbNeeded ) { DWORD cJobsReturned; LPWSTR pEnd; DWORD rc; HANDLE hPrinter = INVALID_HANDLE_VALUE; LPPRINTER_INFO_2 pPrinter = NULL; LPDRIVER_INFO_3A pDriver = NULL; CHAR szDriverDir[MAX_PATH]; DWORD cbNeeded = 0, dwDependentFileCount; DWORD cbNeededForJobs; if (pszServer && *pszServer) { rc = RxPrintQGetInfo(pszServer, pszQueueName, uLevel, pbBuf, cbBuf, &cbNeeded); if (cbNeeded > MAX_WORD) { rc = MY_PROTOCOL_LIMIT_ERROR; goto Cleanup; } *pcbNeeded = (USHORT)cbNeeded; goto Cleanup; } *pcbNeeded = 0; // in case an error occurs. if ( !NetpIsPrintQLevelValid( uLevel, FALSE ) ) { rc = ERROR_INVALID_LEVEL; goto Cleanup; } if ( (pszQueueName==NULL) || ((*pszQueueName)==L'\0') ) { rc = ERROR_INVALID_PARAMETER; goto Cleanup; } if ( !MyOpenPrinterW( pszQueueName, &hPrinter, NULL) ) { rc = GetLastError(); if ( rc == ERROR_INVALID_PRINTER_NAME ) rc = NERR_QNotFound; goto Cleanup; } // // Level 52 is meant for point and print from a Windows 95 clients // can't use with other clients since no environment info is passed // if ( uLevel == 52 ) { cbNeeded = sizeof(szDriverDir)-2; szDriverDir[0] = szDriverDir[1] = '\\'; if ( !GetComputerNameA(szDriverDir+2, &cbNeeded) ) { rc = GetLastError(); goto Cleanup; } if ( strlen(szDriverDir) + strlen(WIN95_DRIVER_SHARE) + 1 > sizeof(szDriverDir) ) { rc = ERROR_NOT_ENOUGH_MEMORY; NetpAssert( rc != NO_ERROR ); // Always break goto Cleanup; } strcat(szDriverDir, WIN95_DRIVER_SHARE); (VOID)MyGetPrinterDriver(hPrinter, WIN95_ENVIRONMENT, 3, NULL, 0, &cbNeeded); rc = GetLastError(); if ( rc != ERROR_INSUFFICIENT_BUFFER ) goto Cleanup; pDriver = (LPVOID) GlobalAlloc(GMEM_FIXED, cbNeeded); if ( !pDriver ) { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } if ( !MyGetPrinterDriver(hPrinter, WIN95_ENVIRONMENT, 3, (LPVOID)pDriver, cbNeeded, &cbNeeded) ) { rc = GetLastError(); goto Cleanup; } cbNeeded=GetDrvInfoSizeW(uLevel, pDriver, szDriverDir, &dwDependentFileCount); if ( dwDependentFileCount > MAX_DEPENDENT_FILES ) { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } } else { if (!MyGetPrinter(hPrinter, 2, NULL, 0, &cbNeeded)) { rc = GetLastError(); if (rc == ERROR_INSUFFICIENT_BUFFER) { pPrinter = (LPVOID) GlobalAlloc(GMEM_FIXED, cbNeeded); if (pPrinter == NULL) { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } } else { goto Cleanup; } } if (!MyGetPrinter(hPrinter, 2, (LPBYTE)pPrinter, cbNeeded, &cbNeeded)) { rc = GetLastError(); goto Cleanup; } // How much for just the queue structure and its strings? cbNeeded=GetPrqInfoSizeW(uLevel, pszQueueName, pPrinter); } if (cbNeeded > MAX_WORD) { rc = MY_PROTOCOL_LIMIT_ERROR; goto Cleanup; } *pcbNeeded = (WORD)cbNeeded; // Tell caller the size (so far). // // Build the queue structure itself. // if (cbNeeded <= (DWORD) cbBuf) { if ( uLevel == 52 ) { ZeroMemory(pbBuf, cbNeeded); pEnd = CopyDriverToPrqInfoW(pDriver, dwDependentFileCount, szDriverDir, uLevel, pbBuf, (LPWSTR) (pbBuf+cbBuf) ); } else { pEnd = CopyPrinterToPrqInfoW(pPrinter, uLevel, pbBuf, pszQueueName, (LPWSTR) (pbBuf+cbBuf) ); } } else { // // Too small. Well, need to find total size before we can tell caller. // if ( (uLevel==2) || (uLevel==4) ) { rc = ComputeSpaceNeededForJobs( pszQueueName, uLevel, // Q info level hPrinter, & cbNeededForJobs ); if (rc != NO_ERROR) { goto Cleanup; } cbNeeded += cbNeededForJobs; } if (cbNeeded > MAX_WORD) { rc = MY_PROTOCOL_LIMIT_ERROR; goto Cleanup; } rc = NERR_BufTooSmall; goto Cleanup; } // // Append jobs if necessary. // if ( (uLevel==2) || (uLevel==4) ) { DWORD cbPrq = PrqInfoFixedSizeW( uLevel ); rc = AppendJobsToPrqW( pszQueueName, uLevel, // Q info level hPrinter, pbBuf + cbPrq, // put first job here cbBuf - cbNeeded, // bytes avail for jobs pEnd, // str area NULL, // don't need new pEnd & cbNeededForJobs, & cJobsReturned, cbBuf == MAX_WORD ? TRUE : FALSE ); // If the buffer is at its max, get what we can. if( cbNeeded + cbNeededForJobs > MAX_WORD ) { *pcbNeeded = MAX_WORD; } else { *pcbNeeded = (USHORT) (cbNeeded + cbNeededForJobs); } // // Update job count in queue structure, as it may be out of date. // NetpSetJobCountForQueue( uLevel, // queue info level pbBuf, // queue structure to update TRUE, // yes, UNICODE strings cJobsReturned ); // actual job count if (rc != NO_ERROR) { goto Cleanup; } } rc = NO_ERROR; Cleanup: if (hPrinter != INVALID_HANDLE_VALUE) { (VOID) MyClosePrinter( hPrinter ); } if (pPrinter) { (VOID) GlobalFree( pPrinter ); } if (pDriver) { (VOID) GlobalFree( pDriver ); } return rc; } SPLERR SPLENTRY DosPrintJobGetInfoW( LPWSTR pszServer, BOOL bRemote, WORD uJobId, WORD uLevel, PBYTE pbBuf, WORD cbBuf, PUSHORT pcbNeeded ) { DWORD cb; HANDLE hPrinter = INVALID_HANDLE_VALUE; LPSTR QueueNameA = NULL; LPWSTR QueueNameW = NULL; LPJOB_INFO_2 pJob = NULL; LPWSTR pEnd; DWORD rc; DWORD cbNeeded = 0; if (bRemote) { rc = RxPrintJobGetInfo(pszServer, uJobId, uLevel, pbBuf, cbBuf, &cbNeeded); *pcbNeeded = (USHORT)cbNeeded; return rc; } *pcbNeeded = 0; // in case an error occurs. if ( !NetpIsPrintJobLevelValid( uLevel, FALSE ) ) { rc = ERROR_INVALID_LEVEL; goto Cleanup; } // // The 3.51 spooler has been changed to accept Get/SetJobs on the // local server handle. We will still do security checks against // the Job's security descriptor. This also avoids the costly // FindLocalJob() call. // if (!MyOpenPrinterW( pszServer, &hPrinter, NULL)) { rc = GetLastError(); NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: " "MyOpenPrinter( NULL, &hPrinter, NULL ) failed" FORMAT_API_STATUS "\n", rc )); hPrinter = INVALID_HANDLE_VALUE; goto Cleanup; } NetpAssert( hPrinter != INVALID_HANDLE_VALUE ); // // Note: this should really call MyGetJobW, since it looks // like the code later thunks from ansi back to unicode. // if (!MyGetJobA(hPrinter, uJobId, 2, NULL, 0, &cb)) { rc=GetLastError(); NetpAssert( rc != NO_ERROR ); if (rc == ERROR_INSUFFICIENT_BUFFER) { pJob = (LPVOID) GlobalAlloc(GMEM_FIXED, cb); if (pJob == NULL) { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } if ( !MyGetJobA(hPrinter, uJobId, 2, (LPBYTE)pJob, cb, &cb) ) { rc=GetLastError(); NetpAssert( rc != NO_ERROR ); goto Cleanup; } } else { if (rc == ERROR_INVALID_PARAMETER) { rc = NERR_JobNotFound; } goto Cleanup; // Job deleted? Not enough mem? } } if (pJob == NULL) { NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobGetInfoW: " "*** STILL INVALID RESULT FROM MyGetJob, pJob IS NULL!\n" )); rc = NERR_InternalError; goto Cleanup; } NetpAssert( pJob != NULL ); NetpAssert( pJob->pPrinterName != NULL ); QueueNameA = FindQueueNameInPrinterNameA( (pJob->pPrinterName) ); NetpAssert( QueueNameA != NULL ); QueueNameW = NetpAllocWStrFromStr( QueueNameA ); if (QueueNameW == NULL) { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } NetpAssert( QueueNameW != NULL ); cb=GetPrjInfoSizeW(uLevel, pJob, QueueNameW); *pcbNeeded=(WORD)cb; if (cb > (DWORD) cbBuf) { rc = NERR_BufTooSmall; goto Cleanup; } pEnd = (LPVOID) (pbBuf+cbBuf); (VOID) CopyJobToPrjInfoW(uLevel, pJob, QueueNameW, pbBuf, pEnd); rc = NO_ERROR; Cleanup: if (hPrinter != INVALID_HANDLE_VALUE) { (VOID) MyClosePrinter( hPrinter ); } if (pJob != NULL) { (VOID) GlobalFree( pJob ); } if (QueueNameW != NULL) { (VOID) NetApiBufferFree( QueueNameW ); } return (rc); } SPLERR SPLENTRY DosPrintJobDelW( LPWSTR pszServer, BOOL bRemote, WORD uJobId ) { if (bRemote) return RxPrintJobDel(pszServer, uJobId); return (CommandALocalJobA(NULL, pszServer, NULL, uJobId, 0, NULL, JOB_CONTROL_CANCEL ) ); } SPLERR SPLENTRY DosPrintJobContinueW( LPWSTR pszServer, BOOL bRemote, WORD uJobId ) { if (bRemote) return RxPrintJobContinue(pszServer, uJobId); return (CommandALocalJobA(NULL, pszServer, NULL, uJobId, 0, NULL, JOB_CONTROL_RESUME ) ); } SPLERR SPLENTRY DosPrintJobPauseW( LPWSTR pszServer, BOOL bRemote, WORD uJobId ) { if (bRemote) return RxPrintJobPause(pszServer, uJobId); return (CommandALocalJobA(NULL, pszServer, NULL, uJobId, 0, NULL, JOB_CONTROL_PAUSE ) ); } SPLERR SPLENTRY DosPrintJobEnumW( LPWSTR pszServer, LPWSTR pszQueueName, WORD uLevel, PBYTE pbBuf, WORD cbBuf, PWORD pcReturned, PWORD pcTotal ) { DWORD cbPrinter; LPJOB_INFO_2 pJob = NULL; LPJOB_INFO_2 pJobs; DWORD cb, cbJobs, cReturned, cJobs; HANDLE hPrinter = INVALID_HANDLE_VALUE; LPWSTR pEnd; DWORD rc; DWORD cTotal; if (pszServer && *pszServer) { rc = RxPrintJobEnum(pszServer, pszQueueName, uLevel, pbBuf, cbBuf, &cReturned, &cTotal); *pcReturned = (WORD)cReturned; *pcTotal = (WORD)cTotal; goto Cleanup; } *pcReturned=0; *pcTotal = 0; if ( !NetpIsPrintJobLevelValid( uLevel, FALSE ) ) { rc = ERROR_INVALID_LEVEL; goto Cleanup; } if (!MyOpenPrinterW( pszQueueName, &hPrinter, NULL)) { rc = GetLastError(); goto Cleanup; } NetpAssert( hPrinter != INVALID_HANDLE_VALUE ); if (!MyEnumJobs(hPrinter, 0, (DWORD) -1, 2, NULL, 0, &cbJobs, &cReturned)) { rc = GetLastError(); NetpAssert( rc != NO_ERROR ); if (rc == ERROR_INSUFFICIENT_BUFFER) { if (pJob = (LPVOID) GlobalAlloc(GMEM_FIXED, cbJobs)) { if (!MyEnumJobs(hPrinter, 0, (DWORD) -1, 2, (LPBYTE)pJob, cbJobs, &cbJobs, &cReturned)) { rc = GetLastError(); NetpAssert( rc != NO_ERROR ); NetpAssert( rc != ERROR_INSUFFICIENT_BUFFER ); NetpKdPrint(( PREFIX_DOSPRINT "DosPrintJobEnumW: got error " FORMAT_API_STATUS " from MyEnumJobs(first)\n", rc )); goto Cleanup; } } else { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } } else { NetpKdPrint(( PREFIX_DOSPRINT "DosPrintJobEnumW: got error " FORMAT_API_STATUS " from MyEnumJobs(first)\n", rc )); goto Cleanup; } } if (cReturned == 0) { *pcReturned = 0; *pcTotal = 0; rc = NO_ERROR; goto Cleanup; } if (pJob == NULL) { NetpKdPrint(( PREFIX_DOSPRINT "DosPrintJobEnumW: never allocated array!\n" )); rc = NERR_InternalError; goto Cleanup; } *pcTotal = (WORD)cReturned; cb=0; cJobs=cReturned; pJobs=pJob; while (cJobs--) cb+=GetPrjInfoSizeW(uLevel, pJobs++, pszQueueName); if (cb <= (DWORD) cbBuf) { DWORD cbFixedPortion = PrjInfoFixedSizeW( uLevel ); NetpAssert( cbFixedPortion != 0 ); // level already checked! pEnd = (LPWSTR)(pbBuf+cbBuf); cJobs=cReturned; pJobs=pJob; while (cJobs--) { pEnd = CopyJobToPrjInfoW(uLevel, pJobs++, pszQueueName, pbBuf, pEnd); pbBuf += cbFixedPortion; } *pcReturned = (WORD)cReturned; rc = NO_ERROR; } else { rc = NERR_BufTooSmall; goto Cleanup; } Cleanup: if (hPrinter != INVALID_HANDLE_VALUE) { (VOID) MyClosePrinter( hPrinter ); } if (pJob != NULL) { (VOID) GlobalFree( pJob ); } return rc; } SPLERR SPLENTRY DosPrintDestEnumW( IN LPWSTR pszServer OPTIONAL, IN WORD uLevel, OUT PBYTE pbBuf, IN WORD cbBuf, OUT PUSHORT pcReturned, OUT PUSHORT pcTotal ) { DWORD cReturned=0, cTotal=0, rc; if (pszServer && *pszServer) { rc = RxPrintDestEnum(pszServer, uLevel, pbBuf, cbBuf, &cReturned, &cTotal); *pcReturned = (USHORT)cReturned; *pcTotal = (USHORT)cTotal; return rc; } // Stub for local dest enum - no entries, dest not found. *pcReturned = 0; *pcTotal = 0; return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintDestControlW( LPWSTR pszServer, LPWSTR pszDevName, WORD uControl ) { if (pszServer && *pszServer) return RxPrintDestControl(pszServer, pszDevName, uControl); return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintDestGetInfoW( LPWSTR pszServer, LPWSTR pszName, WORD uLevel, PBYTE pbBuf, WORD cbBuf, PUSHORT pcbNeeded ) { DWORD cbNeeded = 0, rc; if (pszServer && *pszServer) { rc = RxPrintDestGetInfo(pszServer, pszName, uLevel, pbBuf, cbBuf, &cbNeeded); *pcbNeeded = (USHORT)cbNeeded; return rc; } return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintDestAddW( LPWSTR pszServer, WORD uLevel, PBYTE pbBuf, WORD cbBuf ) { if (pszServer && *pszServer) return RxPrintDestAdd(pszServer, uLevel, pbBuf, cbBuf); return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintDestSetInfoW( LPWSTR pszServer, LPWSTR pszName, WORD uLevel, PBYTE pbBuf, WORD cbBuf, WORD uParmNum ) { if (pszServer && *pszServer) return RxPrintDestSetInfo(pszServer, pszName, uLevel, pbBuf, cbBuf, uParmNum); return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintDestDelW( LPWSTR pszServer, LPWSTR pszPrinterName ) { if (pszServer && *pszServer) return RxPrintDestDel(pszServer, pszPrinterName); return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintQEnumW( LPWSTR pszServer, WORD uLevel, PBYTE pbBuf, WORD cbBuf, PUSHORT pcReturned, PUSHORT pcTotal ) { DWORD cJobsReturned; DWORD Total, cbNeeded, rc; HANDLE hPrinter = INVALID_HANDLE_VALUE; DWORD i; DWORD JobFixedEntrySize = 0; DWORD JobLevel; LPSHARE_INFO_1 pShareInfo = NULL; DWORD cbPrinter; LPPRINTER_INFO_2 pPrinter = NULL; BOOL BufferTooSmall=FALSE; DWORD cReturned = 0; DWORD cTotal = 0; #if DBG LPVOID OutputBufferStart = pbBuf; #endif LPVOID pEnd; DWORD SharesRead; if ( !NetpIsPrintQLevelValid( uLevel, FALSE ) ) { rc = ERROR_INVALID_LEVEL; goto Cleanup; } if (pszServer && *pszServer) { rc = RxPrintQEnum(pszServer, uLevel, pbBuf, cbBuf, &cReturned, &cTotal); *pcReturned = (USHORT)cReturned; *pcTotal = (USHORT)cTotal; goto Cleanup; } *pcReturned = 0; *pcTotal = 0; rc=NetShareEnum( NULL, 1, (LPBYTE *)(LPVOID)&pShareInfo, MAX_PREFERRED_LENGTH, &SharesRead, &Total, NULL); if (rc != NO_ERROR) { NetpKdPrint((PREFIX_DOSPRINT "DosPrintQEnumW: NetShareEnum returned " FORMAT_API_STATUS "\n", rc)); goto Cleanup; } pEnd = (pbBuf + cbBuf); if (uLevel==2) { JobLevel = 1; JobFixedEntrySize = PrjInfoFixedSizeW( JobLevel ); } else if (uLevel == 4) { JobLevel = 2; JobFixedEntrySize = PrjInfoFixedSizeW( JobLevel ); } for (i=0; i LM20_QNLEN) { continue; } if ( !MyOpenPrinterW(pShareInfo[i].shi1_netname, &hPrinter, NULL)) { rc = (NET_API_STATUS) GetLastError(); NetpKdPrint(( PREFIX_DOSPRINT "DosPrintQEnumW: MyOpenPrinter failed, status " FORMAT_API_STATUS ".\n", rc )); NetpAssert( rc != NO_ERROR ); goto Cleanup; } NetpAssert( hPrinter != INVALID_HANDLE_VALUE ); if (!MyGetPrinter(hPrinter, 2, NULL, 0, &cbPrinter)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { rc = (NET_API_STATUS) GetLastError(); NetpKdPrint(( PREFIX_DOSPRINT "DosPrintQEnumW: MyGetPrinter(first) failed, status " FORMAT_API_STATUS ".\n", rc )); NetpAssert( rc != NO_ERROR ); goto Cleanup; } } NetpAssert( cbPrinter != 0 ); pPrinter = (LPVOID) GlobalAlloc(GMEM_FIXED, cbPrinter); if (pPrinter == NULL) { rc = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } if ( !MyGetPrinter(hPrinter, 2, (LPBYTE)pPrinter, cbPrinter, &cbPrinter)) { rc = (NET_API_STATUS) GetLastError(); NetpKdPrint(( PREFIX_DOSPRINT "DosPrintQEnumW: MyGetPrinter(second) failed, status " FORMAT_API_STATUS ".\n", rc )); NetpAssert( rc != NO_ERROR ); goto Cleanup; } cbNeeded=GetPrqInfoSizeW(uLevel, pShareInfo[i].shi1_netname, // Q nam pPrinter); NetpAssert( cbNeeded > 0 ); NetpAssert( cbNeeded <= (DWORD) MAX_WORD ); if ( (!BufferTooSmall) && ((DWORD)cbBuf >= cbNeeded) ) { LPVOID pbQueue = pbBuf; // // Handle queue structure itself. // pEnd = CopyPrinterToPrqInfoW(pPrinter, uLevel, pbBuf, pShareInfo[i].shi1_netname, pEnd); pbBuf += PrqInfoFixedSizeW( uLevel ); cbBuf -= (WORD) cbNeeded; // // Append job structures if needed. // if ( (uLevel==2) || (uLevel==4) ) { // info level includes jobs NetpAssert( pbBuf < (LPBYTE) pEnd ); rc = AppendJobsToPrqW( pShareInfo[i].shi1_netname, uLevel, // Q info level hPrinter, pbBuf, // first job here cbBuf, // bytes avail pEnd, // str area & pEnd, // set new end ptr & cbNeeded, & cJobsReturned, FALSE ); // Only accept all the data. if (rc == NERR_BufTooSmall) { BufferTooSmall = TRUE; // continue, as we need pcTotal... } else if (rc != NO_ERROR) { goto Cleanup; } else { // Must be NO_ERROR. NetpAssert( cbNeeded <= (DWORD) MAX_WORD ); NetpAssert( pbBuf < (LPBYTE) pEnd ); NetpAssert( JobFixedEntrySize !=0 ); pbBuf += (JobFixedEntrySize * cJobsReturned); cbBuf -= (WORD) cbNeeded; (*pcReturned)++; // Correct possible out of date // job count in queue structure. NetpSetJobCountForQueue( uLevel, pbQueue, TRUE, // yes, UNICODE strs cJobsReturned ); } } else { // info level does not include jobs (*pcReturned)++; } } else { // not enough mem for Q struct BufferTooSmall = TRUE; // Continue, as we want to compute pcTotal for subsequent queues. } (*pcTotal)++; NetpAssert( pPrinter != NULL ); (VOID) GlobalFree(pPrinter); pPrinter = NULL; NetpAssert( hPrinter != INVALID_HANDLE_VALUE ); (VOID) MyClosePrinter(hPrinter); hPrinter = INVALID_HANDLE_VALUE; } // for each share Cleanup: if (hPrinter != INVALID_HANDLE_VALUE) { (VOID) MyClosePrinter( hPrinter ); } if (pPrinter != NULL) { (VOID) GlobalFree( pPrinter ); } if (pShareInfo != NULL) { (VOID) NetApiBufferFree(pShareInfo); } if (BufferTooSmall) { rc = NERR_BufTooSmall; } return (rc); } SPLERR SPLENTRY DosPrintQSetInfoW( LPWSTR pszServer, LPWSTR pszQueueName, WORD uLevel, PBYTE pbBuf, WORD cbBuf, WORD uParmNum ) { if (pszServer && *pszServer) return RxPrintQSetInfo(pszServer, pszQueueName, uLevel, pbBuf, cbBuf, uParmNum); return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintQPauseW( LPWSTR pszServer, LPWSTR pszQueueName ) { if (pszServer && *pszServer) return RxPrintQPause(pszServer, pszQueueName); return (CommandALocalPrinterW( pszQueueName, PRINTER_CONTROL_PAUSE ) ); } SPLERR SPLENTRY DosPrintQContinueW( LPWSTR pszServer, LPWSTR pszQueueName ) { if (pszServer && *pszServer) return RxPrintQContinue(pszServer, pszQueueName); return (CommandALocalPrinterW( pszQueueName, PRINTER_CONTROL_RESUME ) ); } SPLERR SPLENTRY DosPrintQPurgeW( LPWSTR pszServer, LPWSTR pszQueueName ) { if (pszServer && *pszServer) return RxPrintQPurge(pszServer, pszQueueName); return (CommandALocalPrinterW( pszQueueName, PRINTER_CONTROL_PURGE ) ); } SPLERR SPLENTRY DosPrintQAddW( LPWSTR pszServer, WORD uLevel, PBYTE pbBuf, WORD cbBuf ) { if (pszServer && *pszServer) return RxPrintQAdd(pszServer, uLevel, pbBuf, cbBuf); return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintQDelW( LPWSTR pszServer, LPWSTR pszQueueName ) { if (pszServer && *pszServer) return RxPrintQDel(pszServer, pszQueueName); return ERROR_NOT_SUPPORTED; } SPLERR SPLENTRY DosPrintJobSetInfoW( LPWSTR pszServer, BOOL bRemote, WORD uJobId, WORD uLevel, PBYTE pbBuf, WORD cbBuf, WORD uParmNum ) { if (bRemote) return RxPrintJobSetInfo(pszServer, uJobId, uLevel, pbBuf, cbBuf, uParmNum); // // Hack for Chicago: support Level 1, ParmNum 0xb so that jobs // are set with the comment field. // if (uLevel == 1 && uParmNum == PRJ_COMMENT_PARMNUM) { HANDLE hPrinter = INVALID_HANDLE_VALUE; CHAR szDocument[MAX_PATH]; PJOB_INFO_1 pJob = NULL; DWORD cbJob; SPLERR rc; // // Allocate maximum size of JOB_INFO_1A. Later, this // should be moved into the spooler's header file. // cbJob = sizeof(JOB_INFO_1) + 6 * MAX_PATH; pJob = (PJOB_INFO_1) GlobalAlloc(GMEM_FIXED, cbJob); if (pJob == NULL) { rc = GetLastError(); goto Cleanup; } // // The 3.51 spooler has been changed to accept Get/SetJobs on the // local server handle. We will still do security checks against // the Job's security descriptor. This also avoids the costly // FindLocalJob() call. // if (!MyOpenPrinterW( pszServer, &hPrinter, NULL)) { rc = GetLastError(); NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: " "MyOpenPrinter( NULL, &hPrinter, NULL ) failed" FORMAT_API_STATUS "\n", rc )); hPrinter = INVALID_HANDLE_VALUE; goto Cleanup; } NetpAssert( hPrinter != INVALID_HANDLE_VALUE ); // // We need to get a copy of the old job info. Later, the // spooler should be changed to allow "don't change" values. // if (!MyGetJobA( hPrinter, uJobId, 1, (PBYTE)pJob, cbJob, &cbJob )) { rc = GetLastError(); NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: " "MyGetJob failed" FORMAT_API_STATUS "\n", rc )); goto Cleanup; } // // Put in new document name. // NetpNCopyWStrToStr( szDocument, (LPWSTR)pbBuf, sizeof( szDocument ) / sizeof( szDocument[0] )); pJob->pDocument = szDocument; // // Don't try and change the position, since this requires // admin access (and isn't necessary). // pJob->Position = JOB_POSITION_UNSPECIFIED; rc = CommandALocalJobA( hPrinter, NULL, NULL, uJobId, 1, (PBYTE)pJob, 0 ); if (rc) { NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: " "CommandALocalJobA failed " FORMAT_API_STATUS "\n", rc )); } Cleanup: if (pJob) { GlobalFree( pJob ); } if (hPrinter != INVALID_HANDLE_VALUE) { MyClosePrinter( hPrinter ); } return rc; } return ERROR_NOT_SUPPORTED; } VOID NetpSetJobCountForQueue( IN DWORD QueueLevel, IN OUT LPVOID Queue, IN BOOL HasUnicodeStrings, IN DWORD JobCount ) { NetpAssert( NetpIsPrintQLevelValid( QueueLevel, FALSE ) ); NetpAssert( Queue != NULL ); if (QueueLevel == 2) { if (HasUnicodeStrings) { PPRQINFOW pq = Queue; pq->cJobs = (WORD) JobCount; } else { PPRQINFOA pq = Queue; pq->cJobs = (WORD) JobCount; } } else if (QueueLevel == 4) { if (HasUnicodeStrings) { PPRQINFO3W pq = Queue; pq->cJobs = (WORD) JobCount; } else { PPRQINFO3A pq = Queue; pq->cJobs = (WORD) JobCount; } } else { NetpAssert( FALSE ); // Should never get here! } } // NetpSetJobCountForQueue