/*++ Copyright (c) 1991-1993 Microsoft Corporation Module Name: PrtQ.c Abstract: This module implements the print queue APIs to remote machines using the Remote Admin Protocol. In addition to the usual downlevel usage, this protocol is also used for the NT-to-NT print APIs. Author: John Rogers (JohnRo) 16-May-1991 Environment: Portable to any flat, 32-bit environment. (Uses Win32 typedefs.) Requires ANSI C extensions: slash-slash comments, long external names. Notes: All of the RxPrint APIs are wide-character APIs, regardless of whether or not UNICODE is defined. This allows the net/dosprint/dosprint.c code to use the winspool APIs (which are currently ANSI APIs, despite their prototypes using LPTSTR in some places). Revision History: 16-May-1991 JohnRo Initial version. 17-Jun-1991 JohnRo Added RxPrintQ{Continue,Pause,Purge}. Added module header. 15-Jul-1991 JohnRo Added RxPrintQ{Add,Del,Enum,SetInfo}. 16-Jul-1991 JohnRo Estimate bytes needed for print APIs. 17-Jul-1991 JohnRo Extracted RxpDebug.h from Rxp.h. 21-Nov-1991 JohnRo Removed NT dependencies to reduce recompiles. 07-Jun-1992 JohnRo RAID 10324: net print vs. UNICODE. RAID 7243: Avoid 64KB requests (Winball servers only HAVE 64KB!) Use PREFIX_ equates. 11-May-1993 JohnRo RAID 9942: workaround Windows For Workgroups (WFW) bug in DosPrintQEnum, when level=0 and multiple queues exist. 18-May-1993 JohnRo RAID 10222: DosPrintQGetInfoW underestimates number of bytes needed. --*/ #ifndef UNICODE #error "RxPrint APIs assume RxRemoteApi uses wide characters." #endif // These must be included first: #include // IN, LPWSTR, etc. #include // NET_API_STATUS, etc. // These may be included in any order: #include // API_ equates. #include // NERR_ and ERROR_ equates. #include // NetpIsUncComputerNameValid(), etc. #include // NetpAssert(). #include // NetpSetOptionalArg(). #include // PREFIX_ equates. #include // REM16_, REM32_, REMSmb_, field index equates. #include // RxRemoteApi(). #include // RxpEstimatedBytesNeeded(). MAX_TRANSACT_ equates. #include // IF_DEBUG(). #include // My prototypes, some PRQ_ equates. #include // NetpPrintQStructureInfo(). DBGSTATIC NET_API_STATUS RxpGetPrintQInfoDescs( IN DWORD InfoLevel, IN BOOL AddOrSet, OUT LPDESC * DataDesc16 OPTIONAL, OUT LPDESC * DataDesc32 OPTIONAL, OUT LPDESC * DataDescSmb OPTIONAL, OUT LPDESC * AuxDesc16 OPTIONAL, OUT LPDESC * AuxDesc32 OPTIONAL, OUT LPDESC * AuxDescSmb OPTIONAL ) { switch (InfoLevel) { case 0 : if (AddOrSet) { return (ERROR_INVALID_LEVEL); } NetpSetOptionalArg(AuxDesc16, NULL); NetpSetOptionalArg(AuxDesc32, NULL); NetpSetOptionalArg(AuxDescSmb, NULL); NetpSetOptionalArg(DataDesc16, REM16_printQ_0); NetpSetOptionalArg(DataDesc32, REM32_printQ_0); NetpSetOptionalArg(DataDescSmb, REMSmb_printQ_0); return (NERR_Success); case 1 : NetpSetOptionalArg(AuxDesc16, NULL); NetpSetOptionalArg(AuxDesc32, NULL); NetpSetOptionalArg(AuxDescSmb, NULL); NetpSetOptionalArg(DataDesc16, REM16_printQ_1); NetpSetOptionalArg(DataDesc32, REM32_printQ_1); NetpSetOptionalArg(DataDescSmb, REMSmb_printQ_1); return (NERR_Success); case 2 : if (AddOrSet) { return (ERROR_INVALID_LEVEL); } NetpSetOptionalArg(AuxDesc16, REM16_print_job_1); NetpSetOptionalArg(AuxDesc32, REM32_print_job_1); NetpSetOptionalArg(AuxDescSmb, REMSmb_print_job_1); NetpSetOptionalArg(DataDesc16, REM16_printQ_2); NetpSetOptionalArg(DataDesc32, REM32_printQ_2); NetpSetOptionalArg(DataDescSmb, REMSmb_printQ_2); return (NERR_Success); case 3 : NetpSetOptionalArg(AuxDesc16, NULL); NetpSetOptionalArg(AuxDesc32, NULL); NetpSetOptionalArg(AuxDescSmb, NULL); NetpSetOptionalArg(DataDesc16, REM16_printQ_3); NetpSetOptionalArg(DataDesc32, REM32_printQ_3); NetpSetOptionalArg(DataDescSmb, REMSmb_printQ_3); return (NERR_Success); case 4 : if (AddOrSet) { return (ERROR_INVALID_LEVEL); } NetpSetOptionalArg(AuxDesc16, REM16_print_job_2); NetpSetOptionalArg(AuxDesc32, REM32_print_job_2); NetpSetOptionalArg(AuxDescSmb, REMSmb_print_job_2); NetpSetOptionalArg(DataDesc16, REM16_printQ_4); NetpSetOptionalArg(DataDesc32, REM32_printQ_4); NetpSetOptionalArg(DataDescSmb, REMSmb_printQ_4); return (NERR_Success); case 5 : if (AddOrSet) { return (ERROR_INVALID_LEVEL); } NetpSetOptionalArg(AuxDesc16, NULL); NetpSetOptionalArg(AuxDesc32, NULL); NetpSetOptionalArg(AuxDescSmb, NULL); NetpSetOptionalArg(DataDesc16, REM16_printQ_5); NetpSetOptionalArg(DataDesc32, REM32_printQ_5); NetpSetOptionalArg(DataDescSmb, REMSmb_printQ_5); return (NERR_Success); default : return (ERROR_INVALID_LEVEL); } /* NOTREACHED */ } // RxpGetPrintQInfoDescs SPLERR SPLENTRY RxPrintQAdd( IN LPWSTR UncServerName, IN DWORD Level, IN LPBYTE Buffer, IN DWORD BufferSize ) { LPDESC AuxDesc16, AuxDesc32, AuxDescSmb; LPDESC DataDesc16, DataDesc32, DataDescSmb; NET_API_STATUS Status; Status = RxpGetPrintQInfoDescs( Level, TRUE, // this is an add or set API. & DataDesc16, & DataDesc32, & DataDescSmb, & AuxDesc16, & AuxDesc32, & AuxDescSmb); if (Status != NERR_Success) { return (Status); } return (RxRemoteApi( API_WPrintQAdd, // API number (LPTSTR) UncServerName, REMSmb_DosPrintQAdd_P, // parm desc DataDesc16, DataDesc32, DataDescSmb, AuxDesc16, AuxDesc32, AuxDescSmb, FALSE, // not a null session API // rest of API's arguments, in 32-bit LM2.x format: Level, Buffer, BufferSize)); } // RxPrintQAdd SPLERR SPLENTRY RxPrintQContinue( IN LPWSTR UncServerName, IN LPWSTR QueueName ) { return (RxRemoteApi( API_WPrintQContinue, // API number (LPTSTR) UncServerName, REMSmb_DosPrintQContinue_P, // parm desc NULL, // no data desc 16 NULL, // no data desc 32 NULL, // no data desc SMB NULL, // no aux desc 16 NULL, // no aux desc 32 NULL, // no aux desc SMB FALSE, // not a null session API // rest of API's arguments, in 32-bit LM2.x format: QueueName)); } // RxPrintQContinue SPLERR SPLENTRY RxPrintQDel( IN LPWSTR UncServerName, IN LPWSTR QueueName ) { return (RxRemoteApi( API_WPrintQDel, // API number (LPTSTR) UncServerName, REMSmb_DosPrintQDel_P, // parm desc NULL, // no data desc 16 NULL, // no data desc 32 NULL, // no data desc SMB NULL, // no aux desc 16 NULL, // no aux desc 32 NULL, // no aux desc SMB FALSE, // not a null session API // rest of API's arguments, in 32-bit LM2.x format: QueueName)); } // RxPrintQDel SPLERR SPLENTRY RxPrintQEnum( IN LPWSTR UncServerName, IN DWORD Level, OUT LPBYTE Buffer, IN DWORD BufferSize, OUT LPDWORD EntriesRead, OUT LPDWORD EntriesAvail ) { DWORD ActualEntries = 0; LPDESC AuxDesc16, AuxDesc32, AuxDescSmb; LPDESC DataDesc16, DataDesc32, DataDescSmb; DWORD SafeEntries; LPVOID SafeLevelBuffer = NULL; NET_API_STATUS Status; Status = RxpGetPrintQInfoDescs( Level, FALSE, // this is not an add or set API. & DataDesc16, & DataDesc32, & DataDescSmb, & AuxDesc16, & AuxDesc32, & AuxDescSmb); if (Status != NERR_Success) { return (Status); } // // Assume normal call, on assumption that we won't run into WFW 3.1 // Status = RxRemoteApi( API_WPrintQEnum, // API number (LPTSTR) UncServerName, REMSmb_DosPrintQEnum_P, // parm desc DataDesc16, DataDesc32, DataDescSmb, AuxDesc16, AuxDesc32, AuxDescSmb, 0, // flags: not a null session API // rest of API's arguments, in 32-bit LM2.x format: Level, Buffer, BufferSize, &ActualEntries, EntriesAvail); // // Check for a nasty WFW 3.1 bug: for level 0, with multiple queues, // one or more queue names are garbage. (I've seen garbage "zzzzWWzzl" // come back, in particular.) If we might have gotten this, // then retry with a safe info level. Since level 0 is just short queue // names, and level 1 is a superset of that, we can make one from the other. // if ( (!RxpFatalErrorCode(Status)) && (Level == 0) && (ActualEntries > 1 ) ) { LPCTSTR OutEntry = (LPVOID) Buffer; DWORD SafeAvail; PPRQINFO SafeEntry; const DWORD SafeLevel = 1; DWORD SafeLevelEntrySize; DWORD SafeLevelBufferSize; RetryTheApi: // // Avoid pointer faults below if caller's buffer isn't large enough. // if ( (ActualEntries * (LM20_QNLEN+1) * sizeof(TCHAR)) > BufferSize ) { Status = NERR_BufTooSmall; goto Cleanup; } // // Compute area needed for safe array, and allocate it. // (VOID) NetpPrintQStructureInfo( SafeLevel, PARMNUM_ALL, TRUE, // Yes, we need native size FALSE, // not add or setinfo API sizeof(TCHAR), // size of chars wanted NULL, // don't need DataDesc16 OPTIONAL, NULL, // don't need DataDesc32 OPTIONAL, NULL, // don't need DataDescSmb OPTIONAL, NULL, // don't need AuxDesc16 OPTIONAL, NULL, // don't need AuxDesc32 OPTIONAL, NULL, // don't need AuxDescSmb OPTIONAL, &SafeLevelEntrySize, // need max size NULL, // don't need size of fixed part NULL ); // don't need size of string area NetpAssert( SafeLevelEntrySize > 0 ); SafeLevelBufferSize = ActualEntries * SafeLevelEntrySize; NetpAssert( SafeLevelBufferSize > 0 ); SafeLevelBuffer = NetpMemoryAllocate( SafeLevelBufferSize ); if (SafeLevelBuffer == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // Do recursive call, to get queues at safe info level. // Status = RxPrintQEnum( UncServerName, SafeLevel, // level we want SafeLevelBuffer, // buffer to fill in SafeLevelBufferSize, &SafeEntries, &SafeAvail ); if ( Status == ERROR_MORE_DATA) { if (SafeLevelBuffer != NULL) { NetpMemoryFree( SafeLevelBuffer ); } SafeLevelBuffer = NULL; if (SafeAvail > ActualEntries) { // Retry if queue was added. ActualEntries = SafeAvail; goto RetryTheApi; } else { // Not enough information to know what to do differently. NetpKdPrint(( PREFIX_NETAPI "RxPrintQEnum: WFW workaround failed, error more data " "but should have been enough!\n" )); Status = NERR_InternalError; goto Cleanup; } } if ( RxpFatalErrorCode( Status ) ) { NetpKdPrint(( PREFIX_NETAPI "RxPrintQEnum: WFW workaround failed, API status=" FORMAT_API_STATUS ".\n", Status )); goto Cleanup; } if (SafeEntries==0) { // Deleted them all of a sudden? OK, I guess. ActualEntries = 0; goto Cleanup; } // // Convert safe info level to desired info level. // ActualEntries = 0; SafeEntry = (LPVOID) SafeLevelBuffer; do { LPCTSTR SafeQueueName; SafeQueueName = SafeEntry->szName; NetpAssert( SafeQueueName != NULL ); if ( (*SafeQueueName) == TCHAR_EOS) { NetpKdPrint(( PREFIX_NETAPI "RxPrintQEnum: WFW workaround failed, got empty " "queue name anyway.\n" )); Status = NERR_InternalError; goto Cleanup; } if ( STRLEN( SafeQueueName ) < LM20_QNLEN ) { if (NetpIsPrintQueueNameValid( SafeQueueName ) ) { // Hey, it's short and valid. Copy it over. (VOID) STRCPY( (LPTSTR) OutEntry, // dest (LPTSTR) SafeQueueName ); // src OutEntry += (LM20_QNLEN+1); ++ActualEntries; } else { NetpKdPrint(( PREFIX_NETAPI "RxPrintQEnum: WFW workaround failed, got bad " "queue name anyway.\n" )); Status = NERR_InternalError; goto Cleanup; } } --SafeEntries; ++SafeEntry; } while (SafeEntries > 0); } Cleanup: NetpSetOptionalArg( EntriesRead, ActualEntries ); if (SafeLevelBuffer != NULL) { NetpMemoryFree( SafeLevelBuffer ); } return (Status); } // RxPrintQEnum SPLERR SPLENTRY RxPrintQGetInfo( IN LPWSTR UncServerName, IN LPWSTR QueueName, IN DWORD Level, OUT LPBYTE Buffer, IN DWORD BufferSize, OUT LPDWORD BytesNeeded // estimated (probably too large). ) { LPDESC AuxDesc16, AuxDesc32, AuxDescSmb; LPDESC DataDesc16, DataDesc32, DataDescSmb; DWORD BytesNeeded16; DWORD EdittedBufferSize; NET_API_STATUS Status; NetpAssert(UncServerName != NULL); NetpAssert(*UncServerName != '\0'); Status = RxpGetPrintQInfoDescs( Level, FALSE, // not an add or set API. & DataDesc16, & DataDesc32, & DataDescSmb, & AuxDesc16, & AuxDesc32, & AuxDescSmb); if (Status != NERR_Success) { return (Status); } NetpAssert( DataDesc16 != NULL ); NetpAssert( DataDesc32 != NULL ); NetpAssert( DataDescSmb != NULL ); if (BufferSize <= MAX_TRANSACT_RET_DATA_SIZE) { EdittedBufferSize = BufferSize; } else { EdittedBufferSize = MAX_TRANSACT_RET_DATA_SIZE; } Status = RxRemoteApi( API_WPrintQGetInfo, (LPTSTR) UncServerName, REMSmb_DosPrintQGetInfo_P, DataDesc16, DataDesc32, DataDescSmb, AuxDesc16, AuxDesc32, AuxDescSmb, FALSE, // not a null session API. // rest of API's arguments in LM 2.x format: QueueName, Level, Buffer, EdittedBufferSize, & BytesNeeded16); // downlevel buffer size needed. // If ERROR_MORE_DATA, convert BytesNeeded to native num. if ( (Status == ERROR_MORE_DATA) || (Status == NERR_BufTooSmall) ) { *BytesNeeded = RxpEstimateBytesNeeded(BytesNeeded16); } else { *BytesNeeded = BufferSize; } IF_DEBUG(PRTQ) { NetpKdPrint(( PREFIX_NETAPI "RxPrintQGetInfo: returned, status is " FORMAT_API_STATUS "\n", Status)); } // Return results of RxRemoteApi call. return (Status); } // RxPrintQGetInfo SPLERR SPLENTRY RxPrintQPause( IN LPWSTR UncServerName, IN LPWSTR QueueName ) { return (RxRemoteApi( API_WPrintQPause, // API number (LPTSTR) UncServerName, REMSmb_DosPrintQPause_P, // parm desc NULL, // no data desc 16 NULL, // no data desc 32 NULL, // no data desc SMB NULL, // no aux desc 16 NULL, // no aux desc 32 NULL, // no aux desc SMB FALSE, // not a null session API // rest of API's arguments, in 32-bit LM2.x format: QueueName)); } // RxPrintQPause SPLERR SPLENTRY RxPrintQPurge( IN LPWSTR UncServerName, IN LPWSTR QueueName ) { return (RxRemoteApi( API_WPrintQPurge, // API number (LPTSTR) UncServerName, REMSmb_DosPrintQPurge_P, // parm desc NULL, // no data desc 16 NULL, // no data desc 32 NULL, // no data desc SMB NULL, // no aux desc 16 NULL, // no aux desc 32 NULL, // no aux desc SMB FALSE, // not a null session API // rest of API's arguments, in 32-bit LM2.x format: QueueName)); } // RxPrintQPurge SPLERR SPLENTRY RxPrintQSetInfo( IN LPWSTR UncServerName, IN LPWSTR QueueName, IN DWORD Level, IN LPBYTE Buffer, IN DWORD BufferSize, IN DWORD ParmNum ) { LPDESC AuxDesc16, AuxDesc32, AuxDescSmb; LPDESC DataDesc16, DataDesc32, DataDescSmb; NET_API_STATUS Status; NetpAssert(UncServerName != NULL); NetpAssert(*UncServerName != '\0'); Status = RxpGetPrintQInfoDescs( Level, TRUE, // This is a setinfo API. & DataDesc16, & DataDesc32, & DataDescSmb, & AuxDesc16, & AuxDesc32, & AuxDescSmb); if (Status != NERR_Success) { return (Status); } if (ParmNum == PARMNUM_ALL) { Status = RxRemoteApi( API_WPrintQSetInfo, // API number (LPTSTR) UncServerName, REMSmb_DosPrintQSetInfo_P, // parm desc DataDesc16, DataDesc32, DataDescSmb, AuxDesc16, AuxDesc32, AuxDescSmb, FALSE, // not a null session API // rest of API's arguments, in 32-bit LM 2.x format: QueueName, Level, Buffer, BufferSize, ParmNum); } else { DWORD FieldIndex; // Compute field index from parmnum and level. NetpAssert( (Level==1) || (Level==3) ); // Already verified. switch (ParmNum) { case PRQ_PRIORITY_PARMNUM : if (Level==1) { FieldIndex = PRQ_PRIORITY_LVL1_FIELDINDEX; } else { FieldIndex = PRQ_PRIORITY_LVL3_FIELDINDEX; } break; case PRQ_STARTTIME_PARMNUM : if (Level==1) { FieldIndex = PRQ_STARTTIME_LVL1_FIELDINDEX; } else { FieldIndex = PRQ_STARTTIME_LVL3_FIELDINDEX; } break; case PRQ_UNTILTIME_PARMNUM : if (Level==1) { FieldIndex = PRQ_UNTILTIME_LVL1_FIELDINDEX; } else { FieldIndex = PRQ_UNTILTIME_LVL3_FIELDINDEX; } break; case PRQ_SEPARATOR_PARMNUM : if (Level==1) { FieldIndex = PRQ_SEPARATOR_LVL1_FIELDINDEX; } else { FieldIndex = PRQ_SEPARATOR_LVL3_FIELDINDEX; } break; case PRQ_PROCESSOR_PARMNUM : if (Level==1) { FieldIndex = PRQ_PROCESSOR_LVL1_FIELDINDEX; } else { FieldIndex = PRQ_PROCESSOR_LVL3_FIELDINDEX; } break; case PRQ_DESTINATIONS_PARMNUM : if (Level==1) { FieldIndex = PRQ_DESTINATIONS_LVL1_FIELDINDEX; } else { return (ERROR_INVALID_LEVEL); } break; case PRQ_PARMS_PARMNUM : if (Level==1) { FieldIndex = PRQ_PARMS_LVL1_FIELDINDEX; } else { FieldIndex = PRQ_PARMS_LVL3_FIELDINDEX; } break; case PRQ_COMMENT_PARMNUM : if (Level==1) { FieldIndex = PRQ_COMMENT_LVL1_FIELDINDEX; } else { FieldIndex = PRQ_COMMENT_LVL3_FIELDINDEX; } break; case PRQ_PRINTERS_PARMNUM : if (Level==1) { return (ERROR_INVALID_LEVEL); } else { FieldIndex = PRQ_PRINTERS_LVL3_FIELDINDEX; } break; case PRQ_DRIVERDATA_PARMNUM : // Can't set driver data from NT /* FALLTHROUGH */ default : IF_DEBUG(PRTQ) { NetpKdPrint(( PREFIX_NETAPI "RxPrintQSetInfo: invalid (bad parmnum).\n" )); } return (ERROR_INVALID_PARAMETER); } Status = RxpSetField ( API_WPrintQSetInfo, // API number UncServerName, "z", // object's desc QueueName, // object to set REMSmb_DosPrintQSetInfo_P, // parm desc DataDesc16, DataDesc32, DataDescSmb, (LPVOID) Buffer, // native info buffer ParmNum, // parm num to send FieldIndex, // field index Level); } return (Status); } // RxPrintQSetInfo