/*++ Copyright (c) 1990-2003 Microsoft Corporation All rights reserved Module Name: winspool.c Abstract: Implements the spooler supported apis for printing. // @@BEGIN_DDKSPLIT Author: Environment: User Mode -Win32 Revision History: // @@END_DDKSPLIT --*/ #include "precomp.h" #pragma hdrstop WCHAR szNULL[] = L""; WCHAR szLcmDeviceNameHeader[] = L"\\Device\\NamedPipe\\Spooler\\"; WCHAR szWindows[] = L"windows"; WCHAR szINIKey_TransmissionRetryTimeout[] = L"TransmissionRetryTimeout"; // // Timeouts for serial printing // #define WRITE_TOTAL_TIMEOUT 3000 // 3 seconds #define READ_TOTAL_TIMEOUT 5000 // 5 seconds #define READ_INTERVAL_TIMEOUT 200 // 0.2 second BOOL DeletePortNode( PINILOCALMON pIniLocalMon, PINIPORT pIniPort ) { PINIPORT pPort, pPrevPort; for( pPort = pIniLocalMon->pIniPort; pPort && pPort != pIniPort; pPort = pPort->pNext){ pPrevPort = pPort; } if (pPort) { // found the port if (pPort == pIniLocalMon->pIniPort) { pIniLocalMon->pIniPort = pPort->pNext; } else { pPrevPort->pNext = pPort->pNext; } FreeSplMem(pPort); return TRUE; } else // port not found return FALSE; } BOOL RemoveDosDeviceDefinition( PINIPORT pIniPort ) /*++ Routine Description: Removes the NONSPOOLED.. dos device definition created by localmon Arguments: pIniPort : Pointer to the INIPORT Return Value: TRUE on success, FALSE on error --*/ { WCHAR TempDosDeviceName[MAX_PATH]; if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName), L"NONSPOOLED_", pIniPort->pName, NULL )) return FALSE; LcmRemoveColon(TempDosDeviceName); return DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL); } // @@BEGIN_DDKSPLIT DWORD HandleLptQueryRemove( LPVOID pData ) { DWORD dwRet = NO_ERROR; PINIPORT pIniPort = (PINIPORT)pData; SPLASSERT(pIniPort && pIniPort->signature == IPO_SIGNATURE && pIniPort->hNotify != NULL ); LcmEnterSplSem(); // // Fix is not multi-thread safe now // if ( pIniPort->Status & PP_STARTDOC ) { dwRet = ERROR_BUSY; goto Done; } // InitializeCriticalSection(pIniPort->&CritSection); CloseHandle(pIniPort->hFile); SplUnregisterForDeviceEvents(pIniPort->hNotify); pIniPort->hNotify = NULL; pIniPort->hFile = INVALID_HANDLE_VALUE; Done: LcmLeaveSplSem(); return dwRet; } // @@END_DDKSPLIT BOOL ValidateDosDevicePort( PINIPORT pIniPort ) /*++ Routine Description: Checks if the given port corresponds to a dos device. For a dos device port the following is done: -- Dos device definition for the NONSPOOLED.. is created -- CreateFile is done on the NONSPOOLED.. port Arguments: pIniPort : Pointer to the INIPORT Return Value: TRUE on all validations passing, FALSE otherwise Side effect: For dos devices : a. CreateFile is called on the NONSPOOLED.. name b. PP_DOSDEVPORT flag is set c. pIniPort->pDeviceName is set to the first string found on QueryDosDefition this could be used to see if the definition changed (ex. when user did a net use lpt1 \\server\printer the connection is effective only when the user is logged in) d. PP_COMM_PORT is set for real LPT/COM port (ie. GetCommTimeouts worked, not a net use lpt1 case) --*/ { DCB dcb; COMMTIMEOUTS cto; WCHAR TempDosDeviceName[MAX_PATH]; HANDLE hToken = NULL; WCHAR DeviceNames[MAX_PATH]; WCHAR DosDeviceName[MAX_PATH]; WCHAR NewNtDeviceName[MAX_PATH]; WCHAR *pDeviceNames=DeviceNames; BOOL bRet = FALSE; LPWSTR pDeviceName = NULL; hToken = RevertToPrinterSelf(); if (!hToken) goto Done; if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF(DosDeviceName), pIniPort->pName, NULL )) goto Done; LcmRemoveColon(DosDeviceName); // // If the port is not a dos device port nothing to do -- return success // if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames)) ) { bRet = TRUE; goto Done; } pDeviceName = AllocSplStr(pDeviceNames); if ( !pDeviceName ) goto Done; if( ERROR_SUCCESS != StrNCatBuffW( NewNtDeviceName, COUNTOF(NewNtDeviceName), szLcmDeviceNameHeader, pIniPort->pName, NULL )) goto Done; LcmRemoveColon(NewNtDeviceName); // // Search for the first non-matching name in pDeviceNames list. // while ( lstrcmpi(pDeviceNames, NewNtDeviceName) == 0 ) { pDeviceNames+=wcslen(pDeviceNames)+1; } if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName), L"NONSPOOLED_", pIniPort->pName, NULL )) goto Done; LcmRemoveColon(TempDosDeviceName); // // Delete any existing definition for TempDosDeviceName. This ensures that // there exists only one definition for the nonspooled_port device name. // DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL); DefineDosDevice(DDD_RAW_TARGET_PATH, TempDosDeviceName, pDeviceNames); ImpersonatePrinterClient(hToken); hToken = NULL; if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName), L"\\\\.\\NONSPOOLED_", pIniPort->pName, NULL )) goto Done; LcmRemoveColon(TempDosDeviceName); pIniPort->hFile = CreateFile(TempDosDeviceName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); // // If CreateFile fails remove redirection and fail the call // if ( pIniPort->hFile == INVALID_HANDLE_VALUE ) { (VOID)RemoveDosDeviceDefinition(pIniPort); goto Done; } pIniPort->Status |= PP_DOSDEVPORT; SetEndOfFile(pIniPort->hFile); if ( IS_COM_PORT (pIniPort->pName) ) { if ( GetCommState(pIniPort->hFile, &dcb) ) { GetCommTimeouts(pIniPort->hFile, &cto); GetIniCommValues (pIniPort->pName, &dcb, &cto); SetCommState (pIniPort->hFile, &dcb); cto.WriteTotalTimeoutConstant = WRITE_TOTAL_TIMEOUT; cto.WriteTotalTimeoutMultiplier = 0; cto.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT; cto.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT; SetCommTimeouts(pIniPort->hFile, &cto); pIniPort->Status |= PP_COMM_PORT; } else { DBGMSG(DBG_WARNING, ("ERROR: Failed GetCommState pIniPort->hFile %x\n",pIniPort->hFile) ); } } else if ( IS_LPT_PORT (pIniPort->pName) ) { if ( GetCommTimeouts(pIniPort->hFile, &cto) ) { cto.WriteTotalTimeoutConstant = GetProfileInt(szWindows, szINIKey_TransmissionRetryTimeout, 45 ); cto.WriteTotalTimeoutConstant*=1000; SetCommTimeouts(pIniPort->hFile, &cto); // @@BEGIN_DDKSPLIT hToken = RevertToPrinterSelf(); pIniPort->hNotify = SplRegisterForDeviceEvents( pIniPort->hFile, (LPVOID)pIniPort, HandleLptQueryRemove); ImpersonatePrinterClient(hToken); hToken = NULL; // @@END_DDKSPLIT pIniPort->Status |= PP_COMM_PORT; } else { DBGMSG(DBG_WARNING, ("ERROR: Failed GetCommTimeouts pIniPort->hFile %x\n",pIniPort->hFile) ); } } FreeSplStr( pIniPort->pDeviceName ); pIniPort->pDeviceName = pDeviceName; bRet = TRUE; Done: if (hToken) ImpersonatePrinterClient(hToken); if ( !bRet && pDeviceName ) FreeSplStr(pDeviceName); return bRet; } BOOL FixupDosDeviceDefinition( PINIPORT pIniPort ) /*++ Routine Description: Called before every StartDocPort for a DOSDEVPORT. The routine will check if the dos device defintion has changed (if a user logged and his connection is remembered). Also for a connection case the CreateFile is called since that needs to be done per job Arguments: pIniPort : Pointer to the INIPORT Return Value: TRUE on all validations passing, FALSE otherwise --*/ { WCHAR DeviceNames[MAX_PATH]; WCHAR DosDeviceName[MAX_PATH]; HANDLE hToken; // // If the port is not a real LPT port we open it per job // @@BEGIN_DDKSPLIT // Also parallel ports could be closed on QUERYREMOVE if user undocks // then it will be opened on next job's StartDocPort // @@END_DDKSPLIT // if ( !(pIniPort->Status & PP_COMM_PORT) || pIniPort->hFile == INVALID_HANDLE_VALUE ) return ValidateDosDevicePort(pIniPort); if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF (DosDeviceName), pIniPort->pName, NULL )) return FALSE; LcmRemoveColon(DosDeviceName); hToken = RevertToPrinterSelf(); if (!hToken) { return FALSE; } if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames) ) ) { ImpersonatePrinterClient(hToken); return FALSE; } // // If strings are same then definition has not changed // if ( !lstrcmpi(DeviceNames, pIniPort->pDeviceName) ) { ImpersonatePrinterClient(hToken); return TRUE; } (VOID)RemoveDosDeviceDefinition(pIniPort); CloseHandle(pIniPort->hFile); pIniPort->hFile = INVALID_HANDLE_VALUE; // @@BEGIN_DDKSPLIT if ( pIniPort->hNotify ) { SplUnregisterForDeviceEvents(pIniPort->hNotify); pIniPort->hNotify = NULL; } // @@END_DDKSPLIT pIniPort->Status &= ~(PP_COMM_PORT | PP_DOSDEVPORT); FreeSplStr(pIniPort->pDeviceName); pIniPort->pDeviceName = NULL; ImpersonatePrinterClient(hToken); return ValidateDosDevicePort(pIniPort); }