Leaked source code of windows server 2003
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.

407 lines
11 KiB

  1. /*++
  2. Copyright (c) 1990-2003 Microsoft Corporation
  3. All rights reserved
  4. Module Name:
  5. winspool.c
  6. Abstract:
  7. Implements the spooler supported apis for printing.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Environment:
  11. User Mode -Win32
  12. Revision History:
  13. // @@END_DDKSPLIT
  14. --*/
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. WCHAR szNULL[] = L"";
  18. WCHAR szLcmDeviceNameHeader[] = L"\\Device\\NamedPipe\\Spooler\\";
  19. WCHAR szWindows[] = L"windows";
  20. WCHAR szINIKey_TransmissionRetryTimeout[] = L"TransmissionRetryTimeout";
  21. //
  22. // Timeouts for serial printing
  23. //
  24. #define WRITE_TOTAL_TIMEOUT 3000 // 3 seconds
  25. #define READ_TOTAL_TIMEOUT 5000 // 5 seconds
  26. #define READ_INTERVAL_TIMEOUT 200 // 0.2 second
  27. BOOL
  28. DeletePortNode(
  29. PINILOCALMON pIniLocalMon,
  30. PINIPORT pIniPort
  31. )
  32. {
  33. PINIPORT pPort, pPrevPort;
  34. for( pPort = pIniLocalMon->pIniPort;
  35. pPort && pPort != pIniPort;
  36. pPort = pPort->pNext){
  37. pPrevPort = pPort;
  38. }
  39. if (pPort) { // found the port
  40. if (pPort == pIniLocalMon->pIniPort) {
  41. pIniLocalMon->pIniPort = pPort->pNext;
  42. } else {
  43. pPrevPort->pNext = pPort->pNext;
  44. }
  45. FreeSplMem(pPort);
  46. return TRUE;
  47. }
  48. else // port not found
  49. return FALSE;
  50. }
  51. BOOL
  52. RemoveDosDeviceDefinition(
  53. PINIPORT pIniPort
  54. )
  55. /*++
  56. Routine Description:
  57. Removes the NONSPOOLED.. dos device definition created by localmon
  58. Arguments:
  59. pIniPort : Pointer to the INIPORT
  60. Return Value:
  61. TRUE on success, FALSE on error
  62. --*/
  63. {
  64. WCHAR TempDosDeviceName[MAX_PATH];
  65. if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
  66. L"NONSPOOLED_", pIniPort->pName, NULL ))
  67. return FALSE;
  68. LcmRemoveColon(TempDosDeviceName);
  69. return DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
  70. }
  71. // @@BEGIN_DDKSPLIT
  72. DWORD
  73. HandleLptQueryRemove(
  74. LPVOID pData
  75. )
  76. {
  77. DWORD dwRet = NO_ERROR;
  78. PINIPORT pIniPort = (PINIPORT)pData;
  79. SPLASSERT(pIniPort && pIniPort->signature == IPO_SIGNATURE
  80. && pIniPort->hNotify != NULL );
  81. LcmEnterSplSem();
  82. //
  83. // Fix is not multi-thread safe now
  84. //
  85. if ( pIniPort->Status & PP_STARTDOC ) {
  86. dwRet = ERROR_BUSY;
  87. goto Done;
  88. }
  89. // InitializeCriticalSection(pIniPort->&CritSection);
  90. CloseHandle(pIniPort->hFile);
  91. SplUnregisterForDeviceEvents(pIniPort->hNotify);
  92. pIniPort->hNotify = NULL;
  93. pIniPort->hFile = INVALID_HANDLE_VALUE;
  94. Done:
  95. LcmLeaveSplSem();
  96. return dwRet;
  97. }
  98. // @@END_DDKSPLIT
  99. BOOL
  100. ValidateDosDevicePort(
  101. PINIPORT pIniPort
  102. )
  103. /*++
  104. Routine Description:
  105. Checks if the given port corresponds to a dos device.
  106. For a dos device port the following is done:
  107. -- Dos device definition for the NONSPOOLED.. is created
  108. -- CreateFile is done on the NONSPOOLED.. port
  109. Arguments:
  110. pIniPort : Pointer to the INIPORT
  111. Return Value:
  112. TRUE on all validations passing, FALSE otherwise
  113. Side effect:
  114. For dos devices :
  115. a. CreateFile is called on the NONSPOOLED.. name
  116. b. PP_DOSDEVPORT flag is set
  117. c. pIniPort->pDeviceName is set to the first string found on
  118. QueryDosDefition this could be used to see if the definition changed
  119. (ex. when user did a net use lpt1 \\server\printer the connection
  120. is effective only when the user is logged in)
  121. d. PP_COMM_PORT is set for real LPT/COM port
  122. (ie. GetCommTimeouts worked, not a net use lpt1 case)
  123. --*/
  124. {
  125. DCB dcb;
  126. COMMTIMEOUTS cto;
  127. WCHAR TempDosDeviceName[MAX_PATH];
  128. HANDLE hToken = NULL;
  129. WCHAR DeviceNames[MAX_PATH];
  130. WCHAR DosDeviceName[MAX_PATH];
  131. WCHAR NewNtDeviceName[MAX_PATH];
  132. WCHAR *pDeviceNames=DeviceNames;
  133. BOOL bRet = FALSE;
  134. LPWSTR pDeviceName = NULL;
  135. hToken = RevertToPrinterSelf();
  136. if (!hToken)
  137. goto Done;
  138. if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF(DosDeviceName),
  139. pIniPort->pName, NULL ))
  140. goto Done;
  141. LcmRemoveColon(DosDeviceName);
  142. //
  143. // If the port is not a dos device port nothing to do -- return success
  144. //
  145. if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames)) ) {
  146. bRet = TRUE;
  147. goto Done;
  148. }
  149. pDeviceName = AllocSplStr(pDeviceNames);
  150. if ( !pDeviceName )
  151. goto Done;
  152. if( ERROR_SUCCESS != StrNCatBuffW( NewNtDeviceName, COUNTOF(NewNtDeviceName),
  153. szLcmDeviceNameHeader, pIniPort->pName, NULL ))
  154. goto Done;
  155. LcmRemoveColon(NewNtDeviceName);
  156. //
  157. // Search for the first non-matching name in pDeviceNames list.
  158. //
  159. while ( lstrcmpi(pDeviceNames, NewNtDeviceName) == 0 ) {
  160. pDeviceNames+=wcslen(pDeviceNames)+1;
  161. }
  162. if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
  163. L"NONSPOOLED_", pIniPort->pName, NULL ))
  164. goto Done;
  165. LcmRemoveColon(TempDosDeviceName);
  166. //
  167. // Delete any existing definition for TempDosDeviceName. This ensures that
  168. // there exists only one definition for the nonspooled_port device name.
  169. //
  170. DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
  171. DefineDosDevice(DDD_RAW_TARGET_PATH, TempDosDeviceName, pDeviceNames);
  172. ImpersonatePrinterClient(hToken);
  173. hToken = NULL;
  174. if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
  175. L"\\\\.\\NONSPOOLED_", pIniPort->pName, NULL ))
  176. goto Done;
  177. LcmRemoveColon(TempDosDeviceName);
  178. pIniPort->hFile = CreateFile(TempDosDeviceName,
  179. GENERIC_READ | GENERIC_WRITE,
  180. FILE_SHARE_READ,
  181. NULL,
  182. OPEN_ALWAYS,
  183. FILE_ATTRIBUTE_NORMAL |
  184. FILE_FLAG_SEQUENTIAL_SCAN,
  185. NULL);
  186. //
  187. // If CreateFile fails remove redirection and fail the call
  188. //
  189. if ( pIniPort->hFile == INVALID_HANDLE_VALUE ) {
  190. (VOID)RemoveDosDeviceDefinition(pIniPort);
  191. goto Done;
  192. }
  193. pIniPort->Status |= PP_DOSDEVPORT;
  194. SetEndOfFile(pIniPort->hFile);
  195. if ( IS_COM_PORT (pIniPort->pName) ) {
  196. if ( GetCommState(pIniPort->hFile, &dcb) ) {
  197. GetCommTimeouts(pIniPort->hFile, &cto);
  198. GetIniCommValues (pIniPort->pName, &dcb, &cto);
  199. SetCommState (pIniPort->hFile, &dcb);
  200. cto.WriteTotalTimeoutConstant = WRITE_TOTAL_TIMEOUT;
  201. cto.WriteTotalTimeoutMultiplier = 0;
  202. cto.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT;
  203. cto.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT;
  204. SetCommTimeouts(pIniPort->hFile, &cto);
  205. pIniPort->Status |= PP_COMM_PORT;
  206. } else {
  207. DBGMSG(DBG_WARNING,
  208. ("ERROR: Failed GetCommState pIniPort->hFile %x\n",pIniPort->hFile) );
  209. }
  210. } else if ( IS_LPT_PORT (pIniPort->pName) ) {
  211. if ( GetCommTimeouts(pIniPort->hFile, &cto) ) {
  212. cto.WriteTotalTimeoutConstant =
  213. GetProfileInt(szWindows,
  214. szINIKey_TransmissionRetryTimeout,
  215. 45 );
  216. cto.WriteTotalTimeoutConstant*=1000;
  217. SetCommTimeouts(pIniPort->hFile, &cto);
  218. // @@BEGIN_DDKSPLIT
  219. hToken = RevertToPrinterSelf();
  220. pIniPort->hNotify = SplRegisterForDeviceEvents(
  221. pIniPort->hFile,
  222. (LPVOID)pIniPort,
  223. HandleLptQueryRemove);
  224. ImpersonatePrinterClient(hToken);
  225. hToken = NULL;
  226. // @@END_DDKSPLIT
  227. pIniPort->Status |= PP_COMM_PORT;
  228. } else {
  229. DBGMSG(DBG_WARNING,
  230. ("ERROR: Failed GetCommTimeouts pIniPort->hFile %x\n",pIniPort->hFile) );
  231. }
  232. }
  233. FreeSplStr( pIniPort->pDeviceName );
  234. pIniPort->pDeviceName = pDeviceName;
  235. bRet = TRUE;
  236. Done:
  237. if (hToken)
  238. ImpersonatePrinterClient(hToken);
  239. if ( !bRet && pDeviceName )
  240. FreeSplStr(pDeviceName);
  241. return bRet;
  242. }
  243. BOOL
  244. FixupDosDeviceDefinition(
  245. PINIPORT pIniPort
  246. )
  247. /*++
  248. Routine Description:
  249. Called before every StartDocPort for a DOSDEVPORT. The routine will check if
  250. the dos device defintion has changed (if a user logged and his connection
  251. is remembered). Also for a connection case the CreateFile is called since
  252. that needs to be done per job
  253. Arguments:
  254. pIniPort : Pointer to the INIPORT
  255. Return Value:
  256. TRUE on all validations passing, FALSE otherwise
  257. --*/
  258. {
  259. WCHAR DeviceNames[MAX_PATH];
  260. WCHAR DosDeviceName[MAX_PATH];
  261. HANDLE hToken;
  262. //
  263. // If the port is not a real LPT port we open it per job
  264. // @@BEGIN_DDKSPLIT
  265. // Also parallel ports could be closed on QUERYREMOVE if user undocks
  266. // then it will be opened on next job's StartDocPort
  267. // @@END_DDKSPLIT
  268. //
  269. if ( !(pIniPort->Status & PP_COMM_PORT) ||
  270. pIniPort->hFile == INVALID_HANDLE_VALUE )
  271. return ValidateDosDevicePort(pIniPort);
  272. if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF (DosDeviceName),
  273. pIniPort->pName, NULL ))
  274. return FALSE;
  275. LcmRemoveColon(DosDeviceName);
  276. hToken = RevertToPrinterSelf();
  277. if (!hToken) {
  278. return FALSE;
  279. }
  280. if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames) ) ) {
  281. ImpersonatePrinterClient(hToken);
  282. return FALSE;
  283. }
  284. //
  285. // If strings are same then definition has not changed
  286. //
  287. if ( !lstrcmpi(DeviceNames, pIniPort->pDeviceName) )
  288. {
  289. ImpersonatePrinterClient(hToken);
  290. return TRUE;
  291. }
  292. (VOID)RemoveDosDeviceDefinition(pIniPort);
  293. CloseHandle(pIniPort->hFile);
  294. pIniPort->hFile = INVALID_HANDLE_VALUE;
  295. // @@BEGIN_DDKSPLIT
  296. if ( pIniPort->hNotify ) {
  297. SplUnregisterForDeviceEvents(pIniPort->hNotify);
  298. pIniPort->hNotify = NULL;
  299. }
  300. // @@END_DDKSPLIT
  301. pIniPort->Status &= ~(PP_COMM_PORT | PP_DOSDEVPORT);
  302. FreeSplStr(pIniPort->pDeviceName);
  303. pIniPort->pDeviceName = NULL;
  304. ImpersonatePrinterClient(hToken);
  305. return ValidateDosDevicePort(pIniPort);
  306. }