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.

541 lines
15 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. rdppnutl.c
  5. Abstract:
  6. User-Mode RDP Module Containing Redirected Printer-Related Utilities
  7. Author:
  8. TadB
  9. Revision History:
  10. --*/
  11. #include <TSrv.h>
  12. #include <winspool.h>
  13. #include "rdppnutl.h"
  14. #include "regapi.h"
  15. #include <wchar.h>
  16. //////////////////////////////////////////////////////////////
  17. //
  18. // Defines and Macros
  19. //
  20. //
  21. // Spooler Service Name
  22. //
  23. #define SPOOLER L"Spooler"
  24. //
  25. // Is a character numeric?
  26. //
  27. #define ISNUM(c) ((c>='0')&&(c<='9'))
  28. //////////////////////////////////////////////////////////////
  29. //
  30. // Globals to this Module
  31. //
  32. // Number of seconds to wait for the spooler to finish initializing.
  33. DWORD SpoolerServiceTimeout = 45;
  34. //////////////////////////////////////////////////////////////
  35. //
  36. // Internal Prototypes
  37. //
  38. // Actually performs the printer deletion.
  39. void DeleteTSPrinters(
  40. IN PRINTER_INFO_5 *pPrinterInfo,
  41. IN DWORD count
  42. );
  43. // Load registry settings for this module.
  44. void LoadRDPPNUTLRegistrySettings();
  45. // Waits until the spooler finishes initializing or until a timeout period
  46. // elapses.
  47. DWORD WaitForSpoolerToStart();
  48. DWORD
  49. RDPPNUTL_RemoveAllTSPrinters()
  50. /*++
  51. Routine Description:
  52. Removes all TS-Redirected Printer Queues
  53. Arguments:
  54. Return Value:
  55. Returns ERROR_SUCCESS on success. Error status, otherwise.
  56. --*/
  57. {
  58. PRINTER_INFO_5 *pPrinterInfo = NULL;
  59. DWORD cbBuf = 0;
  60. DWORD cReturnedStructs = 0;
  61. DWORD tsPrintQueueFlags;
  62. NTSTATUS status;
  63. PBYTE buf = NULL;
  64. OSVERSIONINFOEX versionInfo;
  65. unsigned char stackBuf[4 * 1024]; // Initial EnumPrinter buffer size to
  66. // avoid two round-trip RPC's, if possible.
  67. //
  68. // This code should only run on server. For Pro/Personal, we can't run because it
  69. // affects boot performance. For Pro, we clean up queues in winlogon anyway.
  70. //
  71. versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  72. if (!GetVersionEx((LPOSVERSIONINFO)&versionInfo)) {
  73. status = GetLastError();
  74. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: GetVersionEx failed. Error: %08X.\n",
  75. status));
  76. TS_ASSERT(FALSE);
  77. return status;
  78. }
  79. if (versionInfo.wProductType == VER_NT_WORKSTATION) {
  80. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Skipping cleanup because not server\n"));
  81. return ERROR_SUCCESS;
  82. }
  83. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: RDPPNUTL_RemoveAllTSPrinters entry\n"));
  84. //
  85. // Load registry settings for this module.
  86. //
  87. LoadRDPPNUTLRegistrySettings();
  88. //
  89. // Wait until the spooler has finished initializing.
  90. //
  91. status = WaitForSpoolerToStart();
  92. if (status != ERROR_SUCCESS) {
  93. TRACE((
  94. DEBUG_TSHRSRV_DEBUG,
  95. "RDPPNUTL: RDPPNUTL_RemoveAllTSPrinters exiting because spooler failed to start.\n"
  96. ));
  97. return status;
  98. }
  99. //
  100. // Try to enumerate printers using the stack buffer, first, to avoid two
  101. // round-trip RPC's to the spooler, if possible.
  102. //
  103. if (!EnumPrinters(
  104. PRINTER_ENUM_LOCAL, // Flags
  105. NULL, // Name
  106. 5, // Print Info Type
  107. stackBuf, // buffer
  108. sizeof(stackBuf), // Size of buffer
  109. &cbBuf, // Required
  110. &cReturnedStructs)) {
  111. status = GetLastError();
  112. //
  113. // See if we need to allocate more room for the printer information.
  114. //
  115. if (status == ERROR_INSUFFICIENT_BUFFER) {
  116. buf = TSHeapAlloc(HEAP_ZERO_MEMORY,
  117. cbBuf,
  118. TS_HTAG_TSS_PRINTERINFO2);
  119. if (buf == NULL) {
  120. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: ALLOCMEM failed. Error: %08X.\n",
  121. GetLastError()));
  122. status = ERROR_OUTOFMEMORY;
  123. }
  124. else {
  125. pPrinterInfo = (PRINTER_INFO_5 *)buf;
  126. status = ERROR_SUCCESS;
  127. }
  128. //
  129. // Enumerate printers.
  130. //
  131. if (status == ERROR_SUCCESS) {
  132. if (!EnumPrinters(
  133. PRINTER_ENUM_LOCAL,
  134. NULL,
  135. 5,
  136. (PBYTE)pPrinterInfo,
  137. cbBuf,
  138. &cbBuf,
  139. &cReturnedStructs)) {
  140. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: EnumPrinters failed. Error: %08X.\n",
  141. GetLastError()));
  142. status = GetLastError();
  143. }
  144. else {
  145. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Second EnumPrinters succeeded.\n"));
  146. }
  147. }
  148. }
  149. else {
  150. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: EnumPrinters failed. Error: %08X.\n",
  151. GetLastError()));
  152. }
  153. }
  154. else {
  155. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: First EnumPrinters succeeded.\n"));
  156. status = ERROR_SUCCESS;
  157. pPrinterInfo = (PRINTER_INFO_5 *)stackBuf;
  158. }
  159. //
  160. // Delete all the TS printers. We allow ERROR_INSUFFICIENT_BUFFER here because
  161. // a second invokation of EnumPrinters may have missed a few last-minute
  162. // printer additions.
  163. //
  164. if ((status == ERROR_SUCCESS) || (status == ERROR_INSUFFICIENT_BUFFER)) {
  165. DeleteTSPrinters(pPrinterInfo, cReturnedStructs);
  166. status = ERROR_SUCCESS;
  167. }
  168. //
  169. // Release the printer info buffer.
  170. //
  171. if (buf != NULL) {
  172. TSHeapFree(buf);
  173. }
  174. TRACE((DEBUG_TSHRSRV_DEBUG,"TShrSRV: RDPPNUTL_RemoveAllTSPrinters exit\n"));
  175. return status;
  176. }
  177. void
  178. DeleteTSPrinters(
  179. IN PRINTER_INFO_5 *pPrinterInfo,
  180. IN DWORD count
  181. )
  182. /*++
  183. Routine Description:
  184. Actually performs the printer deletion.
  185. Arguments:
  186. pPrinterInfo - All printer queues on the system.
  187. count - Number of printers in pPrinterInfo
  188. Return Value:
  189. NA
  190. --*/
  191. {
  192. DWORD i;
  193. DWORD regValueDataType;
  194. DWORD sessionID;
  195. HANDLE hPrinter = NULL;
  196. DWORD bufSize;
  197. PRINTER_DEFAULTS defaults = {NULL, NULL, PRINTER_ALL_ACCESS};
  198. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: DeleteTSPrinters entry\n"));
  199. for (i=0; i<count; i++) {
  200. if (pPrinterInfo[i].pPrinterName) {
  201. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Checking %ws for TS printer status.\n",
  202. pPrinterInfo[i].pPrinterName));
  203. //
  204. // Is this a TS printer?
  205. //
  206. if (pPrinterInfo[i].pPortName &&
  207. (pPrinterInfo[i].pPortName[0] == 'T') &&
  208. (pPrinterInfo[i].pPortName[1] == 'S') &&
  209. ISNUM(pPrinterInfo[i].pPortName[2])) {
  210. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: %ws is a TS printer.\n",
  211. pPrinterInfo[i].pPrinterName));
  212. }
  213. else {
  214. continue;
  215. }
  216. //
  217. // Purge and delete the printer.
  218. //
  219. if (OpenPrinter(pPrinterInfo[i].pPrinterName, &hPrinter, &defaults)) {
  220. if (!SetPrinter(hPrinter, 0, NULL, PRINTER_CONTROL_PURGE) ||
  221. !DeletePrinter(hPrinter)) {
  222. TRACE((DEBUG_TSHRSRV_WARN,"RDPPNUTL: Error deleting printer %ws.\n",
  223. pPrinterInfo[i].pPrinterName));
  224. }
  225. else {
  226. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Successfully deleted %ws.\n",
  227. pPrinterInfo[i].pPrinterName));
  228. }
  229. ClosePrinter(hPrinter);
  230. }
  231. else {
  232. TRACE((DEBUG_TSHRSRV_ERROR,
  233. "RDPPNUTL: OpenPrinter failed for %ws. Error: %08X.\n",
  234. pPrinterInfo[i].pPrinterName,
  235. GetLastError())
  236. );
  237. }
  238. }
  239. else {
  240. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Printer %ld is NULL\n", i));
  241. }
  242. }
  243. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: DeleteTSPrinters exit\n"));
  244. }
  245. void
  246. LoadRDPPNUTLRegistrySettings()
  247. /*++
  248. Routine Description:
  249. Load registry settings for this module.
  250. Arguments:
  251. Return Value:
  252. NA
  253. --*/
  254. {
  255. HKEY regKey;
  256. DWORD dwResult;
  257. DWORD type;
  258. DWORD sz;
  259. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Loading registry settings.\n"));
  260. //
  261. // Open the registry key.
  262. //
  263. dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVICERDR_REG_NAME,
  264. 0, KEY_READ, &regKey);
  265. if (dwResult == ERROR_SUCCESS) {
  266. //
  267. // Read the "wait for spooler" timeout value.
  268. //
  269. sz = sizeof(SpoolerServiceTimeout);
  270. dwResult = RegQueryValueEx(
  271. regKey,
  272. DEVICERDR_WAITFORSPOOLTIMEOUT,
  273. NULL,
  274. &type,
  275. (PBYTE)&SpoolerServiceTimeout,
  276. &sz
  277. );
  278. if (dwResult != ERROR_SUCCESS){
  279. TRACE((DEBUG_TSHRSRV_WARN,
  280. "RDPPNUTL: Failed to read spooler timeout value.: %08X.\n",
  281. dwResult));
  282. }
  283. else {
  284. TRACE((DEBUG_TSHRSRV_WARN,
  285. "RDPPNUTL: Spooler timeout value is %ld.\n",
  286. SpoolerServiceTimeout));
  287. }
  288. //
  289. // Close the reg key.
  290. //
  291. RegCloseKey(regKey);
  292. }
  293. else {
  294. TRACE((DEBUG_TSHRSRV_ERROR,
  295. "RDPPNUTL: Failed to open registry key: %08X.\n",
  296. dwResult));
  297. }
  298. }
  299. DWORD
  300. WaitForSpoolerToStart()
  301. /*++
  302. Routine Description:
  303. Waits until the spooler finishes initializing or until a timeout period
  304. elapses.
  305. Arguments:
  306. Return Value:
  307. Returns ERROR_SUCCESS if the spooler successfully initialized. Otherwise,
  308. an error code is returned.
  309. --*/
  310. {
  311. SC_HANDLE scManager = NULL;
  312. SC_HANDLE hService = NULL;
  313. DWORD result = ERROR_SUCCESS;
  314. SERVICE_STATUS serviceStatus;
  315. DWORD i;
  316. QUERY_SERVICE_CONFIG *pServiceConfig = NULL;
  317. DWORD bufSize;
  318. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Enter WaitForSpoolerToStart.\n"));
  319. //
  320. // Open the service control manager.
  321. //
  322. scManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
  323. if (scManager == NULL) {
  324. result = GetLastError();
  325. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: OpenSCManager failed with %08X.\n",
  326. result));
  327. goto CleanUpAndExit;
  328. }
  329. //
  330. // Open the spooler service.
  331. //
  332. hService = OpenService(scManager, SPOOLER, SERVICE_ALL_ACCESS);
  333. if (hService == NULL) {
  334. result = GetLastError();
  335. TRACE((DEBUG_TSHRSRV_ERROR,
  336. "RDPPNUTL: OpenService on spooler failed with %08X.\n",
  337. result));
  338. goto CleanUpAndExit;
  339. }
  340. //
  341. // If the spooler is currently running, that is all we need to know.
  342. //
  343. if (!QueryServiceStatus(hService, &serviceStatus)) {
  344. result = GetLastError();
  345. TRACE((DEBUG_TSHRSRV_ERROR,
  346. "RDPPNUTL: QueryServiceStatus on spooler failed with %08X.\n",
  347. result));
  348. goto CleanUpAndExit;
  349. }
  350. else if (serviceStatus.dwCurrentState == SERVICE_RUNNING) {
  351. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Spooler is running.\n"));
  352. result = ERROR_SUCCESS;
  353. goto CleanUpAndExit;
  354. }
  355. //
  356. // Size the spooler service query configuration buffer. This API should
  357. // fail with ERROR_INSUFFICIENT_BUFFER, so we can get the size of the
  358. // buffer before we call the function with real parameters.
  359. //
  360. if (!QueryServiceConfig(hService, NULL, 0, &bufSize) &&
  361. (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
  362. pServiceConfig = (QUERY_SERVICE_CONFIG *)TSHeapAlloc(
  363. HEAP_ZERO_MEMORY,
  364. bufSize,
  365. TS_HTAG_TSS_SPOOLERINFO
  366. );
  367. if (pServiceConfig == NULL) {
  368. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: ALLOCMEM failed. Error: %08X.\n", GetLastError()));
  369. result = ERROR_OUTOFMEMORY;
  370. goto CleanUpAndExit;
  371. }
  372. }
  373. else {
  374. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: QueryServiceConfig unexpected return.\n"));
  375. result = E_UNEXPECTED;
  376. goto CleanUpAndExit;
  377. }
  378. //
  379. // Get the spooler's configuration information.
  380. //
  381. if (!QueryServiceConfig(hService, pServiceConfig, bufSize, &bufSize)) {
  382. TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: QueryServiceConfig failed: %08X.\n",
  383. GetLastError()));
  384. result = GetLastError();
  385. goto CleanUpAndExit;
  386. }
  387. //
  388. // If the spooler is not automatically configured to start on demand or
  389. // automatically on system start then that is all we need to know.
  390. //
  391. if (pServiceConfig->dwStartType != SERVICE_AUTO_START) {
  392. TRACE((DEBUG_TSHRSRV_WARN,"RDPPNUTL: Spooler not configured to start.\n"));
  393. result = E_FAIL;
  394. goto CleanUpAndExit;
  395. }
  396. //
  397. // Poll the service status until we timeout or until the spooler
  398. // starts.
  399. //
  400. for (i=0; (i<SpoolerServiceTimeout) &&
  401. (serviceStatus.dwCurrentState != SERVICE_RUNNING); i++) {
  402. //
  403. // Sleep for a sec.
  404. //
  405. Sleep(1000);
  406. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Spooler is still initializing.\n"));
  407. //
  408. // Try again.
  409. //
  410. if (!QueryServiceStatus(hService, &serviceStatus)) {
  411. result = GetLastError();
  412. TRACE((DEBUG_TSHRSRV_ERROR,
  413. "RDPPNUTL: QueryServiceStatus on spooler failed with %08X.\n",
  414. result));
  415. goto CleanUpAndExit;
  416. }
  417. }
  418. //
  419. // Sucess if the spooler is now running.
  420. //
  421. if (serviceStatus.dwCurrentState == SERVICE_RUNNING) {
  422. result = ERROR_SUCCESS;
  423. }
  424. else {
  425. TRACE((DEBUG_TSHRSRV_WARN,
  426. "RDPPNUTL: Spooler is not running after a timeout or error.\n")
  427. );
  428. result = E_FAIL;
  429. }
  430. CleanUpAndExit:
  431. if (pServiceConfig != NULL) {
  432. TSHeapFree(pServiceConfig);
  433. }
  434. if (scManager != NULL) {
  435. CloseServiceHandle(scManager);
  436. }
  437. if (hService != NULL) {
  438. CloseServiceHandle(hService);
  439. }
  440. TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Exit WaitForSpoolerToStart.\n"));
  441. return result;
  442. }