Source code of Windows XP (NT5)
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.

2035 lines
52 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. All rights reserved
  4. Abstract:
  5. This module provides functionality for ADs within spooler
  6. Author:
  7. Steve Wilson (NT) Nov 1997
  8. Revision History:
  9. --*/
  10. #include <precomp.h>
  11. #pragma hdrstop
  12. #define LOG_EVENT_ERROR_BUFFER_SIZE 11
  13. #define MAX_CN 63 // DS limits common names to 63 non-null chars
  14. #define UNIQUE_NUMBER_SIZE 10
  15. #define MIN_CN (UNIQUE_NUMBER_SIZE + 3 + 1) // CN= + room for unique number if needed, plus NULL
  16. extern BOOL gdwLogDsEvents;
  17. extern "C" HANDLE ghDsUpdateThread;
  18. extern "C" DWORD gdwDsUpdateThreadId;
  19. // Policy values
  20. WCHAR *szPublishPrinters = L"PublishPrinters";
  21. WCHAR *szPruneDownlevel = L"PruneDownlevel";
  22. WCHAR *szPruningInterval = L"PruningInterval";
  23. WCHAR *szPruningRetries = L"PruningRetries";
  24. WCHAR *szPruningPriority = L"PruningPriority";
  25. WCHAR *szVerifyPublishedState = L"VerifyPublishedState";
  26. WCHAR *szImmortal = L"Immortal";
  27. WCHAR *szServerThreadPolicy = L"ServerThread";
  28. WCHAR *szPruningRetryLog = L"PruningRetryLog";
  29. extern "C" BOOL (*pfnOpenPrinter)(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS);
  30. extern "C" BOOL (*pfnClosePrinter)(HANDLE);
  31. extern "C" LONG (*pfnDocumentProperties)(HWND, HANDLE, LPWSTR, PDEVMODE, PDEVMODE, DWORD);
  32. DWORD dwLastPruningPriority = DEFAULT_PRUNING_PRIORITY;
  33. HRESULT
  34. GetPrintQueue(
  35. HANDLE hPrinter,
  36. IADs **ppADs
  37. )
  38. /*++
  39. Function Description:
  40. This function returns the ADs PrintQueue object of the supplied printer handle.
  41. Parameters:
  42. hPrinter - printer handle
  43. ppADs - return pointer to the PrintQueue object. Caller frees via ppADs->Release().
  44. Return Values:
  45. HRESULT
  46. --*/
  47. {
  48. HRESULT hr;
  49. PSPOOL pSpool = (PSPOOL) hPrinter;
  50. PINIPRINTER pIniPrinter = pSpool->pIniPrinter;
  51. IDispatch *pDispatch = NULL;
  52. IADsContainer *pADsContainer = NULL;
  53. WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
  54. // Create the Print-Queue object
  55. // Get the container
  56. hr = GetPrintQueueContainer(hPrinter, &pADsContainer, ppADs);
  57. BAIL_ON_FAILURE(hr);
  58. if (!*ppADs) { // PrintQueue object does not exist, so create it
  59. hr = pADsContainer->Create( SPLDS_PRINTER_CLASS,
  60. pIniPrinter->pszCN,
  61. &pDispatch);
  62. if (FAILED(hr)) {
  63. wsprintf(ErrorBuffer, L"%0x", hr);
  64. SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
  65. gdwLogDsEvents & LOG_ERROR,
  66. MSG_CANT_CREATE_PRINTQUEUE,
  67. FALSE,
  68. pIniPrinter->pszDN,
  69. ErrorBuffer,
  70. NULL );
  71. DBGMSG(DBG_WARNING,("Can't Create PrintQueue: %ws, %ws\n", pIniPrinter->pszDN, ErrorBuffer));
  72. BAIL_ON_FAILURE(hr);
  73. }
  74. hr = pDispatch->QueryInterface(IID_IADs, (void **) ppADs);
  75. BAIL_ON_FAILURE(hr);
  76. }
  77. error:
  78. if (pADsContainer)
  79. pADsContainer->Release();
  80. if (pDispatch)
  81. pDispatch->Release();
  82. return hr;
  83. }
  84. HRESULT
  85. GetPublishPoint(
  86. HANDLE hPrinter
  87. )
  88. /*++
  89. Function Description:
  90. This function gets the publish point by setting pIniPrinter->pszDN and pIniPrinter->pszCN
  91. Parameters:
  92. hPrinter - printer handle
  93. Return Values:
  94. HRESULT
  95. --*/
  96. {
  97. HRESULT hr;
  98. PSPOOL pSpool = (PSPOOL) hPrinter;
  99. PINIPRINTER pIniPrinter = pSpool->pIniPrinter;
  100. // We should only be here if we couldn't use the existing DN & CN, so free old ones
  101. FreeSplStr(pIniPrinter->pszCN);
  102. pIniPrinter->pszCN = NULL;
  103. FreeSplStr(pIniPrinter->pszDN);
  104. pIniPrinter->pszDN = NULL;
  105. // If Published, update DN from GUID
  106. if (pIniPrinter->pszObjectGUID) {
  107. hr = GetPublishPointFromGUID( hPrinter,
  108. pIniPrinter->pszObjectGUID,
  109. &pIniPrinter->pszDN,
  110. &pIniPrinter->pszCN,
  111. TRUE);
  112. //
  113. // If the object is actually deleted, the error is ERROR_FILE_NOT_FOUND
  114. // If the object is a tombstone, the error is ERROR_DS_NO_SUCH_OBJECT
  115. //
  116. if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND ||
  117. HRESULT_CODE(hr) == ERROR_DS_NO_SUCH_OBJECT) {
  118. if (ghDsUpdateThread && gdwDsUpdateThreadId == GetCurrentThreadId()) {
  119. // We are in the background thread
  120. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  121. } else {
  122. pIniPrinter->DsKeyUpdateForeground = DS_KEY_REPUBLISH;
  123. }
  124. }
  125. BAIL_ON_FAILURE(hr);
  126. } else {
  127. // Generate default publish point & common name
  128. hr = GetDefaultPublishPoint(hPrinter, &pIniPrinter->pszDN);
  129. BAIL_ON_FAILURE(hr);
  130. // Printer name might change, so make a copy here
  131. EnterSplSem();
  132. PWSTR pszPrinterName = AllocSplStr(pIniPrinter->pName);
  133. LeaveSplSem();
  134. if (pszPrinterName) {
  135. hr = GetCommonName( hPrinter,
  136. pIniPrinter->pIniSpooler->pMachineName,
  137. pszPrinterName,
  138. pIniPrinter->pszDN,
  139. &pIniPrinter->pszCN);
  140. FreeSplStr(pszPrinterName);
  141. } else {
  142. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  143. }
  144. BAIL_ON_FAILURE(hr);
  145. }
  146. error:
  147. return hr;
  148. }
  149. HRESULT
  150. GetPublishPointFromGUID(
  151. HANDLE hPrinter,
  152. PWSTR pszObjectGUID,
  153. PWSTR *ppszDN,
  154. PWSTR *ppszCN,
  155. BOOL bGetDNAndCN
  156. )
  157. /*++
  158. Function Description:
  159. This function returns the publish point of a specified GUID
  160. Parameters:
  161. hPrinter - printer handle
  162. ppszObjectGUID - objectGUID of object for which we want to find the publish point
  163. ppszDN - ADsPath of container containing object. Caller frees via FreeSplMem().
  164. ppszCN - Common Name of object. Caller frees via FreeSplMem().
  165. bGetDNAndCN - if TRUE, DN of Container & CN of object are returned. If FALSE, DN is path to object
  166. Return Values:
  167. HRESULT
  168. --*/
  169. {
  170. PSPOOL pSpool = (PSPOOL) hPrinter;
  171. DWORD dwRet, nBytes, nChars;
  172. BOOL bRet;
  173. PWSTR pNames[2];
  174. DS_NAME_RESULT *pDNR = NULL;
  175. DOMAIN_CONTROLLER_INFO *pDCI = NULL;
  176. HANDLE hDS = NULL;
  177. PWSTR psz;
  178. WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
  179. HRESULT hr = S_OK;
  180. dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
  181. if (dwRet != ERROR_SUCCESS) {
  182. wsprintf(ErrorBuffer, L"%0x", dwRet);
  183. if (pSpool) {
  184. SplLogEvent( pSpool->pIniSpooler,
  185. gdwLogDsEvents & LOG_ERROR,
  186. MSG_CANT_GET_DNS_DOMAIN_NAME,
  187. FALSE,
  188. ErrorBuffer,
  189. NULL );
  190. }
  191. DBGMSG(DBG_WARNING,("Can't get DNS Domain Name: %ws\n", ErrorBuffer));
  192. goto error;
  193. }
  194. // Get Publish Point
  195. if (ppszDN) {
  196. pNames[0] = pszObjectGUID;
  197. pNames[1] = NULL;
  198. if (!(DsCrackNames(
  199. hDS,
  200. DS_NAME_NO_FLAGS,
  201. DS_UNKNOWN_NAME,
  202. DS_FQDN_1779_NAME,
  203. 1,
  204. &pNames[0],
  205. &pDNR) == ERROR_SUCCESS)) {
  206. dwRet = GetLastError();
  207. wsprintf(ErrorBuffer, L"%0x", dwRet);
  208. if (pSpool) {
  209. SplLogEvent( pSpool->pIniSpooler,
  210. gdwLogDsEvents & LOG_WARNING,
  211. MSG_CANT_CRACK_GUID,
  212. FALSE,
  213. pDCI->DomainName,
  214. ErrorBuffer,
  215. NULL );
  216. DBGMSG(DBG_WARNING,("Can't crack GUID: %ws, %ws\n", pDCI->DomainName, ErrorBuffer));
  217. }
  218. dwRet = ERROR_FILE_NOT_FOUND;
  219. goto error;
  220. }
  221. if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
  222. dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
  223. wsprintf(ErrorBuffer, L"%0x", dwRet);
  224. if (pSpool) {
  225. SplLogEvent( pSpool->pIniSpooler,
  226. gdwLogDsEvents & LOG_WARNING,
  227. MSG_CANT_CRACK_GUID,
  228. FALSE,
  229. pDCI->DomainName,
  230. ErrorBuffer,
  231. NULL );
  232. }
  233. DBGMSG(DBG_WARNING,("Can't crack GUID: %ws %ws\n", pDCI->DomainName, ErrorBuffer));
  234. dwRet = ERROR_FILE_NOT_FOUND;
  235. goto error;
  236. }
  237. if (bGetDNAndCN) {
  238. // Separate DN into CN & PublishPoint
  239. // pDNR has form: CN=CommonName,DN...
  240. hr = FQDN2CNDN(pDCI->DomainControllerName + 2, pDNR->rItems[0].pName, ppszCN, ppszDN);
  241. BAIL_ON_FAILURE(hr);
  242. } else {
  243. hr = BuildLDAPPath(pDCI->DomainControllerName + 2, pDNR->rItems[0].pName, ppszDN);
  244. BAIL_ON_FAILURE(hr);
  245. }
  246. }
  247. error:
  248. if (pDNR)
  249. DsFreeNameResult(pDNR);
  250. if (hDS)
  251. DsUnBind(&hDS);
  252. if (pDCI)
  253. NetApiBufferFree(pDCI);
  254. if (dwRet != ERROR_SUCCESS) {
  255. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
  256. }
  257. if (FAILED(hr)) {
  258. if (ppszCN) {
  259. FreeSplMem(*ppszCN);
  260. *ppszCN = NULL;
  261. }
  262. if (ppszDN) {
  263. FreeSplMem(*ppszDN);
  264. *ppszDN = NULL;
  265. }
  266. }
  267. return hr;
  268. }
  269. HRESULT
  270. FQDN2CNDN(
  271. PWSTR pszDCName,
  272. PWSTR pszFQDN,
  273. PWSTR *ppszCN,
  274. PWSTR *ppszDN
  275. )
  276. {
  277. IADs *pADs = NULL;
  278. PWSTR pszCN = NULL;
  279. PWSTR pszDN = NULL;
  280. PWSTR pszLDAPPath = NULL;
  281. HRESULT hr;
  282. // Get LDAP path to object
  283. hr = BuildLDAPPath(pszDCName, pszFQDN, &pszLDAPPath);
  284. BAIL_ON_FAILURE(hr);
  285. // Get DN
  286. hr = ADsGetObject(pszLDAPPath, IID_IADs, (void **) &pADs);
  287. BAIL_ON_FAILURE(hr);
  288. hr = pADs->get_Parent(&pszDN);
  289. BAIL_ON_FAILURE(hr);
  290. if (!(*ppszDN = AllocSplStr(pszDN))) {
  291. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  292. BAIL_ON_FAILURE(hr);
  293. }
  294. // Get CN
  295. hr = pADs->get_Name(&pszCN);
  296. BAIL_ON_FAILURE(hr);
  297. if (!(*ppszCN = AllocSplStr(pszCN))) {
  298. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  299. BAIL_ON_FAILURE(hr);
  300. }
  301. error:
  302. if (pADs)
  303. pADs->Release();
  304. if (pszCN)
  305. SysFreeString(pszCN);
  306. if (pszDN)
  307. SysFreeString(pszDN);
  308. FreeSplStr(pszLDAPPath);
  309. if (FAILED(hr)) {
  310. FreeSplStr(*ppszCN);
  311. FreeSplStr(*ppszDN);
  312. }
  313. return hr;
  314. }
  315. HRESULT
  316. BuildLDAPPath(
  317. PWSTR pszDC,
  318. PWSTR pszFQDN,
  319. PWSTR *ppszLDAPPath
  320. )
  321. {
  322. PWSTR pszEscapedFQDN = NULL;
  323. DWORD nBytes;
  324. HRESULT hr = S_OK;
  325. //
  326. // pszFQDN is assumed to contain escaped DN_SPECIAL_CHARS characters, but
  327. // ADSI has additional special characters that need to be escaped before using
  328. // the LDAP path in ADSI calls.
  329. //
  330. pszEscapedFQDN = CreateEscapedString(pszFQDN, ADSI_SPECIAL_CHARS);
  331. if (!pszEscapedFQDN) {
  332. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  333. BAIL_ON_FAILURE(hr);
  334. }
  335. // LDAP:// + pDCName + / + pName + 1
  336. nBytes = (wcslen(pszDC) + wcslen(pszEscapedFQDN) + 9)*sizeof(WCHAR);
  337. if (!(*ppszLDAPPath = (PWSTR) AllocSplMem(nBytes))) {
  338. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  339. BAIL_ON_FAILURE(hr);
  340. }
  341. wsprintf(*ppszLDAPPath, L"LDAP://%ws/%ws", pszDC, pszEscapedFQDN);
  342. error:
  343. FreeSplStr(pszEscapedFQDN);
  344. return hr;
  345. }
  346. HRESULT
  347. GetPrintQueueContainer(
  348. HANDLE hPrinter,
  349. IADsContainer **ppADsContainer,
  350. IADs **ppADs
  351. )
  352. /*++
  353. Function Description:
  354. This function returns the container and, if it exists, the PrintQueue object pointer
  355. corresponding to the supplied printer handle
  356. Parameters:
  357. hPrinter - printer handle
  358. ppADsContainer - return Container ADsPath. Caller frees via ppADsContainer->Release().
  359. ppADs - return PrintQueue object ADsPath. Caller frees via ppADs->Release().
  360. Return Values:
  361. If successful, returns the printqueue container and, if found, the printqueue dispatch.
  362. If there is no printqueue, the dispatch is set to NULL and the default container is returned.
  363. --*/
  364. {
  365. HRESULT hr;
  366. PSPOOL pSpool = (PSPOOL) hPrinter;
  367. PINIPRINTER pIniPrinter = pSpool->pIniPrinter;
  368. WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
  369. DWORD nBytes;
  370. PWSTR pszObjectGUID = NULL;
  371. IDispatch *pPrintQDispatch = NULL;
  372. LPWSTR pszPrinterObjectGUID = pIniPrinter->pszObjectGUID;
  373. LPWSTR pszPrinterDN = pIniPrinter->pszDN;
  374. LPWSTR pszPrinterCN = pIniPrinter->pszCN;
  375. *ppADsContainer = NULL;
  376. *ppADs = NULL;
  377. //
  378. // Try quick search for object using known properties.
  379. //
  380. // We are outside Spooler CS and pIniPrinter->pszObjectGUID,
  381. // pIniPrinter->pszDN, pIniPrinter->pszCN might get changed by
  382. // the DS background thread. In the case they are set to NULL,
  383. // we'll take an AV. So, even if they change or are set to NULL,
  384. // we'll use whatever we have when we entered the function.
  385. // The worst that can happen is to find a DS printQueue
  386. // object that doesn't last till we use it, which is fine. This
  387. // is already assumed. Or, to not find a DS printQueue that will
  388. // be created just after we query the DS. This is also fine.
  389. //
  390. if (pszPrinterObjectGUID &&
  391. pszPrinterDN &&
  392. pszPrinterCN) {
  393. // Try to get the container using existing DN
  394. hr = ADsGetObject( pszPrinterDN,
  395. IID_IADsContainer,
  396. (void **) ppADsContainer
  397. );
  398. if (SUCCEEDED(hr)) {
  399. // Verify that printqueue exists in this container
  400. hr = (*ppADsContainer)->GetObject( SPLDS_PRINTER_CLASS,
  401. pszPrinterCN,
  402. &pPrintQDispatch);
  403. // Verify that the found printQueue object has the same GUID
  404. if (SUCCEEDED(hr) && pPrintQDispatch) {
  405. hr = pPrintQDispatch->QueryInterface(IID_IADs, (void **) ppADs);
  406. BAIL_ON_FAILURE(hr);
  407. hr = GetGUID(*ppADs, &pszObjectGUID);
  408. BAIL_ON_FAILURE(hr);
  409. if (wcscmp(pszObjectGUID, pszPrinterObjectGUID))
  410. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
  411. } else {
  412. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
  413. }
  414. }
  415. } else {
  416. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
  417. }
  418. // Couldn't find container or printQueue, so find by GUID or get default container
  419. if (FAILED(hr)) {
  420. // The following items may have been allocated above and need to be freed
  421. // here since we're going to reallocate them
  422. if (pPrintQDispatch) {
  423. pPrintQDispatch->Release();
  424. pPrintQDispatch = NULL;
  425. }
  426. if (*ppADsContainer) {
  427. (*ppADsContainer)->Release();
  428. *ppADsContainer = NULL;
  429. }
  430. if (*ppADs) {
  431. (*ppADs)->Release();
  432. *ppADs = NULL;
  433. }
  434. // find or create pszDN and pszCN
  435. hr = GetPublishPoint(hPrinter);
  436. BAIL_ON_FAILURE(hr);
  437. SPLASSERT(pIniPrinter->pszDN);
  438. hr = ADsGetObject( pIniPrinter->pszDN,
  439. IID_IADsContainer,
  440. (void **) ppADsContainer
  441. );
  442. if (FAILED(hr)) {
  443. wsprintf(ErrorBuffer, L"%0x", hr);
  444. DBGMSG(DBG_WARNING,("Can't get Container: %ws, %ws\n", pIniPrinter->pszDN, ErrorBuffer));
  445. SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
  446. gdwLogDsEvents & LOG_ERROR,
  447. MSG_CANT_GET_CONTAINER,
  448. FALSE,
  449. pIniPrinter->pszDN,
  450. ErrorBuffer,
  451. NULL );
  452. BAIL_ON_FAILURE(hr);
  453. }
  454. // Try to get the printqueue, but don't error out if we can't
  455. (*ppADsContainer)->GetObject( SPLDS_PRINTER_CLASS,
  456. pIniPrinter->pszCN,
  457. &pPrintQDispatch);
  458. if (pPrintQDispatch)
  459. pPrintQDispatch->QueryInterface(IID_IADs, (void **) ppADs);
  460. }
  461. error:
  462. if (pPrintQDispatch)
  463. pPrintQDispatch->Release();
  464. FreeSplStr(pszObjectGUID);
  465. return hr;
  466. }
  467. HRESULT
  468. GetCommonName(
  469. HANDLE hPrinter,
  470. PWSTR pszServerName,
  471. PWSTR pszPrinterName,
  472. PWSTR pszDN,
  473. PWSTR *ppszCommonName
  474. )
  475. /*++
  476. Function Description:
  477. This function returns a standard format Common Name of the PrintQueue object
  478. generated from the supplied Server and Printer names
  479. Parameters:
  480. hPrinter - printer handle
  481. pszServerName - name of a server
  482. pszPrinterName - name of a printer on the server
  483. pszDN - container DN
  484. ppszCommonName - return CommonName. Caller frees via FreeSplMem().
  485. Return Values:
  486. HRESULT
  487. --*/
  488. {
  489. DWORD nBytes;
  490. PWSTR psz;
  491. PWSTR pszPrinterNameStart = pszPrinterName;
  492. // "CN=Server-Printer"
  493. nBytes = (wcslen(pszPrinterName) + wcslen(pszServerName) + 5)*sizeof(WCHAR);
  494. // We need to also make room for a unique number if there is a conflict
  495. if (nBytes < MIN_CN)
  496. nBytes = MIN_CN;
  497. if (!(*ppszCommonName = psz = (PWSTR) AllocSplMem(nBytes))) {
  498. return MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  499. }
  500. // CN=
  501. wcscpy(psz, L"CN=");
  502. // Server
  503. for(psz += 3, pszServerName += 2 ; *pszServerName ; ++psz, ++pszServerName) {
  504. *psz = wcschr(DN_SPECIAL_CHARS, *pszServerName) ? TEXT('_') : *pszServerName;
  505. }
  506. *psz = L'-';
  507. // Printer
  508. for(++psz; *pszPrinterName ; ++psz, ++pszPrinterName) {
  509. *psz = wcschr(DN_SPECIAL_CHARS, *pszPrinterName) ? TEXT('_') : *pszPrinterName;
  510. }
  511. // NULL
  512. *psz = *pszPrinterName;
  513. // DS only allows 64 characters in CN attribute, so shorten this if needed
  514. if (wcslen(*ppszCommonName) > MAX_CN - 1) {
  515. (*ppszCommonName)[MAX_CN] = NULL;
  516. }
  517. // Generate a non-conflicting name in this container
  518. GetUniqueCN(pszDN, ppszCommonName, pszPrinterNameStart);
  519. return S_OK;
  520. }
  521. VOID
  522. GetUniqueCN(
  523. PWSTR pszDN,
  524. PWSTR *ppszCommonName,
  525. PWSTR pszPrinterName
  526. )
  527. {
  528. // Check if an object with this CN already exists in this container
  529. // Get Container
  530. IADsContainer *pADsContainer = NULL;
  531. UINT32 CN; // This is a "random" number we append to the common name to make it unique
  532. CN = (UINT32) (ULONG_PTR) &CN; // Initialize with some random number
  533. HRESULT hr = ADsGetObject(pszDN, IID_IADsContainer, (void **) &pADsContainer);
  534. if (SUCCEEDED(hr)) {
  535. BOOL bTryAgain;
  536. do {
  537. IDispatch *pPrintQDispatch = NULL;
  538. bTryAgain = FALSE;
  539. // Get PrintQueue, if it exists
  540. pADsContainer->GetObject( SPLDS_PRINTER_CLASS,
  541. *ppszCommonName,
  542. &pPrintQDispatch);
  543. if (pPrintQDispatch) {
  544. IADs *pADs = NULL;
  545. hr = pPrintQDispatch->QueryInterface(IID_IADs, (void **) &pADs);
  546. if (SUCCEEDED(hr)) {
  547. // Generate a unique Common Name.
  548. UINT32 cchCommonName = wcslen(*ppszCommonName);
  549. PWSTR pszDigits;
  550. if (cchCommonName >= MIN_CN) {
  551. pszDigits = *ppszCommonName + cchCommonName - UNIQUE_NUMBER_SIZE;
  552. } else {
  553. pszDigits = *ppszCommonName + 3; // CN=
  554. }
  555. bTryAgain = TRUE;
  556. wsprintf(pszDigits, L"%010d", ++CN);
  557. pADs->Release();
  558. }
  559. pPrintQDispatch->Release();
  560. }
  561. } while(bTryAgain);
  562. pADsContainer->Release();
  563. }
  564. }
  565. HRESULT
  566. GetDefaultPublishPoint(
  567. HANDLE hPrinter,
  568. PWSTR *ppszDN
  569. )
  570. /*++
  571. Function Description:
  572. This function returns the default publish point (Container) of a PrintQueue
  573. based on the supplied printer handle.
  574. Parameters:
  575. hPrinter - printer handle
  576. ppszDN - return default PrintQueue container. Caller frees via FreeSplMem()
  577. Return Values:
  578. HRESULT
  579. --*/
  580. {
  581. WCHAR szServerName[MAX_COMPUTERNAME_LENGTH + 1];
  582. WCHAR szName[MAX_PATH + 1];
  583. DWORD nSize;
  584. HANDLE hDS = NULL;
  585. DWORD dwRet = ERROR_SUCCESS;
  586. BOOL bRet;
  587. PWSTR pNames[2];
  588. DS_NAME_RESULT *pDNR = NULL;
  589. DOMAIN_CONTROLLER_INFO *pDCI = NULL;
  590. PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
  591. WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
  592. HRESULT hr = S_OK;
  593. // Get Computer name
  594. nSize = MAX_COMPUTERNAME_LENGTH + 1;
  595. if (!(bRet = GetComputerName(szServerName, &nSize))) {
  596. dwRet = GetLastError();
  597. goto error;
  598. }
  599. // Get Domain name
  600. dwRet = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole);
  601. if (dwRet) {
  602. wsprintf(ErrorBuffer, L"%0x", dwRet);
  603. DBGMSG(DBG_WARNING,("Can't get Primary Domain Info: %ws\n", ErrorBuffer));
  604. SplLogEvent(((PSPOOL) hPrinter)->pIniSpooler,
  605. gdwLogDsEvents & LOG_ERROR,
  606. MSG_CANT_GET_PRIMARY_DOMAIN_INFO,
  607. FALSE,
  608. ErrorBuffer,
  609. NULL );
  610. goto error;
  611. }
  612. // Domain\Server
  613. wsprintf(szName, L"%ws\\%ws$", pDsRole->DomainNameFlat, szServerName);
  614. pNames[0] = szName;
  615. pNames[1] = NULL;
  616. dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
  617. if (dwRet != ERROR_SUCCESS) {
  618. wsprintf(ErrorBuffer, L"%0x", dwRet);
  619. DBGMSG(DBG_WARNING,("Can't get DNS Domain Name: %ws\n", ErrorBuffer));
  620. SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
  621. gdwLogDsEvents & LOG_ERROR,
  622. MSG_CANT_GET_DNS_DOMAIN_NAME,
  623. FALSE,
  624. ErrorBuffer,
  625. NULL );
  626. goto error;
  627. }
  628. // Get Publish Point
  629. if (ppszDN) {
  630. if (!(DsCrackNames(
  631. hDS,
  632. DS_NAME_NO_FLAGS,
  633. DS_UNKNOWN_NAME,
  634. DS_FQDN_1779_NAME,
  635. 1,
  636. &pNames[0],
  637. &pDNR) == ERROR_SUCCESS)) {
  638. dwRet = GetLastError();
  639. wsprintf(ErrorBuffer, L"%0x", dwRet);
  640. DBGMSG(DBG_WARNING,("Can't Crack Name: %ws, %ws\n", pDCI->DomainName, ErrorBuffer));
  641. SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
  642. gdwLogDsEvents & LOG_ERROR,
  643. MSG_CANT_CRACK,
  644. FALSE,
  645. pDCI->DomainName,
  646. ErrorBuffer,
  647. NULL );
  648. goto error;
  649. }
  650. if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
  651. dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
  652. wsprintf(ErrorBuffer, L"%0x", dwRet);
  653. DBGMSG(DBG_WARNING,("Can't Crack Name: %ws, %ws\n", pDCI->DomainName, ErrorBuffer));
  654. SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
  655. gdwLogDsEvents & LOG_ERROR,
  656. MSG_CANT_CRACK,
  657. FALSE,
  658. pDCI->DomainName,
  659. ErrorBuffer,
  660. NULL );
  661. goto error;
  662. }
  663. // LDAP:// + pDCName + / + pName + 1
  664. hr = BuildLDAPPath(pDCI->DomainControllerName + 2, pDNR->rItems[0].pName, ppszDN);
  665. BAIL_ON_FAILURE(hr);
  666. }
  667. error:
  668. if (pDNR)
  669. DsFreeNameResult(pDNR);
  670. if (hDS)
  671. DsUnBind(&hDS);
  672. if (pDCI)
  673. NetApiBufferFree(pDCI);
  674. if (pDsRole)
  675. DsRoleFreeMemory((PVOID) pDsRole);
  676. // If dwRet has no facility, then make into HRESULT
  677. if (dwRet != ERROR_SUCCESS) {
  678. if (HRESULT_CODE(dwRet) == dwRet) {
  679. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
  680. } else {
  681. hr = dwRet;
  682. }
  683. }
  684. // Finally, make absolutely sure we are failing if *ppszDN is NULL
  685. if (hr == S_OK && (!ppszDN || !*ppszDN)) {
  686. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
  687. }
  688. return hr;
  689. }
  690. // Utility routine to report if a printer is color or monochrome
  691. BOOL
  692. ThisIsAColorPrinter(
  693. LPCTSTR lpstrName
  694. )
  695. /*++
  696. Function Description:
  697. This function determines whether or not a printer supports color printing
  698. Parameters:
  699. lpstrName - Printer name
  700. Return Values:
  701. If printer supports color, return is TRUE. Otherwise, return value is FALSE
  702. --*/
  703. {
  704. HANDLE hPrinter = NULL;
  705. LPTSTR lpstrMe = const_cast <LPTSTR> (lpstrName);
  706. BOOL bReturn = FALSE;
  707. LPDEVMODE lpdm = NULL;
  708. long lcbNeeded;
  709. if (!(*pfnOpenPrinter)(lpstrMe, &hPrinter, NULL)) {
  710. DBGMSG(DBG_WARNING, ("Unable to open printer '%ws'- error %d\n", lpstrName,
  711. GetLastError()));
  712. goto error;
  713. }
  714. // First, use DocumentProperties to find the correct DEVMODE size- we
  715. // must use the DEVMODE to force color on, in case the user's defaults
  716. // have turned it off...
  717. lcbNeeded = (*pfnDocumentProperties)(NULL, hPrinter, lpstrMe, NULL, NULL, 0);
  718. if (lcbNeeded <= 0) {
  719. DBGMSG( DBG_WARNING,
  720. ("Document Properties (get size) for '%ws' returned %d\n",
  721. lpstrName,lcbNeeded));
  722. goto error;
  723. }
  724. lpdm = (LPDEVMODE) AllocSplMem(lcbNeeded);
  725. if (lpdm) {
  726. lpdm->dmSize = sizeof(DEVMODE);
  727. lpdm->dmFields = DM_COLOR;
  728. lpdm->dmColor = DMCOLOR_COLOR;
  729. if (IDOK == (*pfnDocumentProperties)(NULL, hPrinter, lpstrMe, lpdm, lpdm, DM_IN_BUFFER | DM_OUT_BUFFER)) {
  730. // Finally, we can create the DC!
  731. HDC hdcThis = CreateDC(NULL, lpstrName, NULL, lpdm);
  732. if (hdcThis) {
  733. bReturn = 2 < (unsigned) GetDeviceCaps(hdcThis, NUMCOLORS);
  734. DeleteDC(hdcThis);
  735. }
  736. }
  737. else
  738. DBGMSG(DBG_WARNING, ("DocumentProperties (retrieve) on '%s' failed- error %d\n",
  739. lpstrName, GetLastError()));
  740. }
  741. else
  742. DBGMSG(DBG_WARNING, ("ThisIsAColorPrinter(%s) failed to get %d bytes\n",
  743. lpstrName, lcbNeeded));
  744. error:
  745. FreeSplMem(lpdm);
  746. if (hPrinter)
  747. (*pfnClosePrinter)(hPrinter);
  748. return bReturn;
  749. }
  750. HRESULT
  751. GetGUID(
  752. IADs *pADs,
  753. PWSTR *ppszObjectGUID
  754. )
  755. /*++
  756. Function Description:
  757. This function returns the ObjectGUID of a given ADs object.
  758. ppszObjectGUID must be freed by caller using FreeSplStr().
  759. Parameters:
  760. pADs - input ADs object pointer
  761. ppszObjectGUID - object GUID of pADs
  762. Return Values:
  763. HRESULT
  764. --*/
  765. {
  766. HRESULT hr;
  767. LPOLESTR pszObjectGUID = NULL;
  768. IID ObjectIID;
  769. hr = pADs->GetInfo();
  770. if (SUCCEEDED(hr)) {
  771. hr = get_UI1Array_Property(pADs, L"ObjectGUID", &ObjectIID);
  772. if (SUCCEEDED(hr)) {
  773. hr = StringFromIID(ObjectIID, &pszObjectGUID);
  774. if (SUCCEEDED(hr)) {
  775. if (!(*ppszObjectGUID = AllocSplStr(pszObjectGUID)))
  776. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  777. }
  778. CoTaskMemFree(pszObjectGUID);
  779. }
  780. }
  781. return hr;
  782. }
  783. BOOL
  784. PrinterPublishProhibited()
  785. {
  786. return !GetDwPolicy(szPublishPrinters, DEFAULT_PRINT_PUBLISH_POLICY);
  787. }
  788. DWORD
  789. VerifyPublishedStatePolicy()
  790. {
  791. return GetDwPolicy(szVerifyPublishedState, DEFAULT_VERIFY_PUBLISHED_STATE);
  792. }
  793. DWORD
  794. PruneDownlevel()
  795. {
  796. return GetDwPolicy(szPruneDownlevel, DEFAULT_PRUNE_DOWNLEVEL);
  797. }
  798. DWORD
  799. PruningInterval(
  800. )
  801. {
  802. return GetDwPolicy(szPruningInterval, DEFAULT_PRUNING_INTERVAL);
  803. }
  804. DWORD
  805. ImmortalPolicy(
  806. )
  807. {
  808. return GetDwPolicy(szImmortal, DEFAULT_IMMORTAL);
  809. }
  810. VOID
  811. ServerThreadPolicy(
  812. BOOL bHaveDs
  813. )
  814. {
  815. DWORD dwPolicy;
  816. dwPolicy = GetDwPolicy(szServerThreadPolicy, SERVER_THREAD_UNCONFIGURED);
  817. if (dwPolicy == SERVER_THREAD_UNCONFIGURED) {
  818. ServerThreadRunning = !(bHaveDs ? SERVER_THREAD_OFF : SERVER_THREAD_ON);
  819. } else {
  820. ServerThreadRunning = !dwPolicy;
  821. }
  822. CreateServerThread();
  823. }
  824. DWORD
  825. PruningRetries(
  826. )
  827. {
  828. DWORD dwPruningRetries;
  829. dwPruningRetries = GetDwPolicy(szPruningRetries, DEFAULT_PRUNING_RETRIES);
  830. if (dwPruningRetries > MAX_PRUNING_RETRIES)
  831. dwPruningRetries = MAX_PRUNING_RETRIES;
  832. return dwPruningRetries;
  833. }
  834. DWORD
  835. PruningRetryLog(
  836. )
  837. {
  838. DWORD dwPruningRetryLog;
  839. dwPruningRetryLog = GetDwPolicy(szPruningRetryLog, DEFAULT_PRUNING_RETRY_LOG);
  840. return dwPruningRetryLog;
  841. }
  842. VOID
  843. SetPruningPriority(
  844. )
  845. {
  846. DWORD dwPriority = GetDwPolicy(szPruningPriority, DEFAULT_PRUNING_PRIORITY);
  847. if (dwPriority != dwLastPruningPriority) {
  848. if (dwPriority == THREAD_PRIORITY_LOWEST ||
  849. dwPriority == THREAD_PRIORITY_BELOW_NORMAL ||
  850. dwPriority == THREAD_PRIORITY_NORMAL ||
  851. dwPriority == THREAD_PRIORITY_ABOVE_NORMAL ||
  852. dwPriority == THREAD_PRIORITY_HIGHEST) {
  853. SetThreadPriority(GetCurrentThread(), DEFAULT_PRUNING_PRIORITY);
  854. } else {
  855. SetThreadPriority(GetCurrentThread(), dwPriority);
  856. }
  857. dwLastPruningPriority = dwPriority;
  858. }
  859. }
  860. BOOL
  861. ThisMachineIsADC(
  862. )
  863. {
  864. NT_PRODUCT_TYPE NtProductType;
  865. RtlGetNtProductType(&NtProductType);
  866. return NtProductType == NtProductLanManNt;
  867. }
  868. DWORD
  869. GetDomainRoot(
  870. PWSTR *ppszDomainRoot
  871. )
  872. /*++
  873. Function Description:
  874. This function returns the ADsPath of the root of the current domain
  875. Parameters:
  876. ppszDomainRoot - pointer to buffer receiving string pointer of domain root ADsPath string
  877. free ppszDomainRoot with a call to FreeSplMem
  878. Return Values:
  879. DWORD
  880. --*/
  881. {
  882. DWORD dwRet = ERROR_SUCCESS;
  883. PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
  884. DS_NAME_RESULT *pDNR = NULL;
  885. DOMAIN_CONTROLLER_INFO *pDCI = NULL;
  886. HANDLE hDS = NULL;
  887. WCHAR szName[MAX_PATH + 1];
  888. PWSTR pNames[2];
  889. PWSTR pszDomainRoot;
  890. DWORD cb;
  891. if (!ppszDomainRoot) {
  892. dwRet = ERROR_INVALID_PARAMETER;
  893. goto error;
  894. }
  895. // Get Domain name
  896. dwRet = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole);
  897. if (dwRet)
  898. goto error;
  899. wsprintf(szName, L"%ws\\", pDsRole->DomainNameFlat);
  900. pNames[0] = szName;
  901. pNames[1] = NULL;
  902. dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
  903. if (dwRet != ERROR_SUCCESS) {
  904. goto error;
  905. }
  906. if (!(DsCrackNames(
  907. hDS,
  908. DS_NAME_NO_FLAGS,
  909. DS_UNKNOWN_NAME,
  910. DS_FQDN_1779_NAME,
  911. 1,
  912. &pNames[0],
  913. &pDNR) == ERROR_SUCCESS)) {
  914. dwRet = GetLastError();
  915. goto error;
  916. }
  917. if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
  918. dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
  919. goto error;
  920. }
  921. // LDAP:// + pDCName + 1
  922. cb = (wcslen(pDNR->rItems[0].pName) + 8)*sizeof(WCHAR);
  923. if (!(*ppszDomainRoot = (PWSTR) AllocSplMem(cb))) {
  924. dwRet = GetLastError();
  925. goto error;
  926. }
  927. wsprintf(*ppszDomainRoot, L"LDAP://%ws", pDNR->rItems[0].pName);
  928. error:
  929. if (pDNR)
  930. DsFreeNameResult(pDNR);
  931. if (hDS)
  932. DsUnBind(&hDS);
  933. if (pDCI)
  934. NetApiBufferFree(pDCI);
  935. if (pDsRole)
  936. DsRoleFreeMemory((PVOID) pDsRole);
  937. return dwRet;
  938. }
  939. PWSTR
  940. CreateSearchString(
  941. PWSTR pszIn
  942. )
  943. {
  944. PWSTR psz, pszSS;
  945. PWSTR pszSearchString = NULL;
  946. DWORD cb;
  947. /* Replace \ with \5c */
  948. /* Count chars & pad */
  949. for (cb = 0, psz = pszIn ; *psz ; ++psz, ++cb) {
  950. if (*psz == L'\\')
  951. cb += 2;
  952. }
  953. cb = (cb + 1)*sizeof *psz;
  954. if (pszSearchString = (PWSTR) GlobalAlloc(GMEM_FIXED, cb)) {
  955. for(psz = pszIn, pszSS = pszSearchString ; *psz ; ++psz, ++pszSS) {
  956. *pszSS = *psz;
  957. if (*psz == L'\\') {
  958. *++pszSS = L'5';
  959. *++pszSS = L'c';
  960. }
  961. }
  962. *pszSS = L'\0';
  963. }
  964. return pszSearchString;
  965. }
  966. BOOL
  967. ServerOnSite(
  968. PWSTR *ppszMySites,
  969. ULONG cMySites,
  970. PWSTR pszServer
  971. )
  972. /*++
  973. Function Description:
  974. This function returns TRUE if pszServer is on one of the ppszMySites and pszServer exists
  975. Parameters:
  976. ppszMySites - input sites
  977. pszServer - input server name
  978. Return Values:
  979. BOOL - TRUE if server exists on site, FALSE otherwise or on error
  980. --*/
  981. {
  982. PSOCKET_ADDRESS pSocketAddresses = NULL;
  983. PWSTR *ppszSiteNames = NULL;
  984. DWORD nAddresses;
  985. DWORD dwRet, i;
  986. ULONG j;
  987. WORD wVersion;
  988. WSADATA WSAData;
  989. BOOL bServerOnSite = FALSE;
  990. wVersion = MAKEWORD(1, 1);
  991. if (WSAStartup(wVersion, &WSAData) == ERROR_SUCCESS) {
  992. // Find out if Server is on Site
  993. GetSocketAddressesFromMachineName(pszServer, &pSocketAddresses, &nAddresses);
  994. if (nAddresses == 0) {
  995. bServerOnSite = TRUE; // Claim server is on site if we can't find it
  996. } else {
  997. dwRet = DsAddressToSiteNames( (PCWSTR) NULL,
  998. nAddresses,
  999. pSocketAddresses,
  1000. &ppszSiteNames);
  1001. if (dwRet == NO_ERROR) {
  1002. for(i = 0 ; i < nAddresses ; ++i) {
  1003. for(j = 0 ; j < cMySites ; ++j) {
  1004. if (ppszSiteNames[i] && ppszMySites[j] && !wcscmp(ppszSiteNames[i], ppszMySites[j])) {
  1005. bServerOnSite = TRUE;
  1006. break;
  1007. }
  1008. }
  1009. }
  1010. }
  1011. }
  1012. if (ppszSiteNames)
  1013. NetApiBufferFree(ppszSiteNames);
  1014. if (pSocketAddresses)
  1015. FreeSplSockets(pSocketAddresses, nAddresses);
  1016. WSACleanup();
  1017. }
  1018. return bServerOnSite;
  1019. }
  1020. VOID
  1021. FreeSplSockets(
  1022. PSOCKET_ADDRESS pSocketAddress,
  1023. DWORD nAddresses
  1024. )
  1025. {
  1026. DWORD i;
  1027. PSOCKET_ADDRESS pSocket;
  1028. for(i = 0, pSocket = pSocketAddress ; i < nAddresses ; ++i, ++pSocket)
  1029. FreeSplMem(pSocket->lpSockaddr);
  1030. FreeSplMem(pSocketAddress);
  1031. }
  1032. VOID
  1033. AllocSplSockets(
  1034. struct hostent *pHostEnt,
  1035. PSOCKET_ADDRESS *ppSocketAddress,
  1036. DWORD *nSocketAddresses
  1037. )
  1038. {
  1039. DWORD i;
  1040. PSOCKET_ADDRESS pSocket;
  1041. for ( *nSocketAddresses = 0 ; pHostEnt->h_addr_list[*nSocketAddresses] ; ++(*nSocketAddresses))
  1042. ;
  1043. // Allocate SOCKET_ADDRESS array
  1044. *ppSocketAddress = (PSOCKET_ADDRESS) AllocSplMem(*nSocketAddresses*sizeof(SOCKET_ADDRESS));
  1045. if (!*ppSocketAddress)
  1046. *nSocketAddresses = 0;
  1047. // Allocate Sockaddr element for each SOCKET_ADDRESS
  1048. // If we fail partway through, just use partial list
  1049. for (i = 0, pSocket = *ppSocketAddress ; i < *nSocketAddresses ; ++i, ++pSocket) {
  1050. if (!(pSocket->lpSockaddr = (struct sockaddr *) AllocSplMem(sizeof(struct sockaddr_in)))) {
  1051. *nSocketAddresses = i;
  1052. break;
  1053. }
  1054. if (pHostEnt->h_addrtype == AF_INET) {
  1055. ((struct sockaddr_in *) pSocket->lpSockaddr)->sin_family = AF_INET;
  1056. ((struct sockaddr_in *) pSocket->lpSockaddr)->sin_addr = *(struct in_addr *) pHostEnt->h_addr_list[i];
  1057. pSocket->iSockaddrLength = sizeof(struct sockaddr_in);
  1058. } else {
  1059. DBGMSG(DBG_WARNING,("AllocSplSockets: addrtype != AF_INET: %d\n", pHostEnt->h_addrtype));
  1060. }
  1061. }
  1062. }
  1063. VOID
  1064. GetSocketAddressesFromMachineName(
  1065. PWSTR pszMachineName, // \\Machine
  1066. PSOCKET_ADDRESS *ppSocketAddress,
  1067. DWORD *nSocketAddresses
  1068. )
  1069. /*++
  1070. Routine Description:
  1071. This routine builds list of names other than the machine name that
  1072. can be used to call spooler APIs.
  1073. --*/
  1074. {
  1075. struct hostent *HostEnt;
  1076. PSTR pszAnsiMachineName = NULL;
  1077. DWORD iWsaError;
  1078. *nSocketAddresses = 0;
  1079. *ppSocketAddress = 0;
  1080. if (pszAnsiMachineName = (PSTR) AllocSplStr(pszMachineName + 2)) {
  1081. if (UnicodeToAnsiString((PWSTR) pszAnsiMachineName, pszAnsiMachineName, NULL_TERMINATED)) {
  1082. if (HostEnt = gethostbyname(pszAnsiMachineName)) {
  1083. AllocSplSockets(HostEnt, ppSocketAddress, nSocketAddresses);
  1084. } else {
  1085. iWsaError = WSAGetLastError();
  1086. DBGMSG(DBG_WARNING, ("gethostbyname failed in DsPrune: %d\n", iWsaError));
  1087. }
  1088. }
  1089. }
  1090. FreeSplMem(pszAnsiMachineName);
  1091. }
  1092. DWORD
  1093. UNC2Server(
  1094. PCWSTR pszUNC,
  1095. PWSTR *ppszServer
  1096. )
  1097. {
  1098. PWSTR psz;
  1099. DWORD cb;
  1100. DWORD nChars;
  1101. if (!pszUNC || pszUNC[0] != L'\\' || pszUNC[1] != L'\\')
  1102. return ERROR_INVALID_PARAMETER;
  1103. if(!(psz = wcschr(pszUNC + 2, L'\\')))
  1104. return ERROR_INVALID_PARAMETER;
  1105. cb = (DWORD) ((ULONG_PTR) psz - (ULONG_PTR) pszUNC + sizeof *psz);
  1106. if (!(*ppszServer = (PWSTR) AllocSplMem(cb)))
  1107. return GetLastError();
  1108. nChars = (DWORD) (psz - pszUNC);
  1109. wcsncpy(*ppszServer, pszUNC, nChars);
  1110. (*ppszServer)[nChars] = L'\0';
  1111. return ERROR_SUCCESS;
  1112. }
  1113. BOOL
  1114. ServerExists(
  1115. PWSTR pszServerName
  1116. )
  1117. {
  1118. NET_API_STATUS Status;
  1119. SERVER_INFO_100 *pServer;
  1120. BOOL bServerExists;
  1121. Status = NetServerGetInfo(pszServerName, 100, (PBYTE *) &pServer);
  1122. bServerExists = !Status;
  1123. Status = NetApiBufferFree(pServer);
  1124. return bServerExists;
  1125. }
  1126. HRESULT
  1127. UnpublishByGUID(
  1128. PINIPRINTER pIniPrinter
  1129. )
  1130. {
  1131. HRESULT hr;
  1132. SplOutSem();
  1133. if (!pIniPrinter->pszObjectGUID) {
  1134. pIniPrinter->DsKeyUpdate = 0;
  1135. pIniPrinter->DsKeyUpdateForeground = 0;
  1136. hr = S_OK;
  1137. } else {
  1138. PWSTR pszDN = NULL;
  1139. PWSTR pszCN = NULL;
  1140. hr = GetPublishPointFromGUID(NULL, pIniPrinter->pszObjectGUID, &pszDN, &pszCN, TRUE);
  1141. DBGMSG(DBG_EXEC,
  1142. ("UnpublishByGUID: GUID %ws\n", pIniPrinter->pszObjectGUID));
  1143. if (SUCCEEDED(hr)) {
  1144. DBGMSG(DBG_EXEC,
  1145. ("UnpublishByGUID: DN %ws CN %ws\n",
  1146. pszDN,
  1147. pszCN));
  1148. IADsContainer *pADsContainer = NULL;
  1149. // Get the container
  1150. hr = ADsGetObject( pszDN,
  1151. IID_IADsContainer,
  1152. (void **) &pADsContainer
  1153. );
  1154. if (SUCCEEDED(hr)) {
  1155. hr = pADsContainer->Delete(SPLDS_PRINTER_CLASS, pszCN);
  1156. pADsContainer->Release();
  1157. }
  1158. // If the container or the object is gone, succeed
  1159. if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT) ||
  1160. HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND ||
  1161. HRESULT_CODE(hr) == ERROR_PATH_NOT_FOUND)
  1162. hr = S_OK;
  1163. }
  1164. FreeSplStr(pszDN);
  1165. FreeSplStr(pszCN);
  1166. }
  1167. // free GUID if object is deleted
  1168. if (SUCCEEDED(hr)) {
  1169. pIniPrinter->DsKeyUpdate = 0;
  1170. pIniPrinter->DsKeyUpdateForeground = 0;
  1171. FreeSplStr(pIniPrinter->pszObjectGUID);
  1172. pIniPrinter->pszObjectGUID = NULL;
  1173. FreeSplStr(pIniPrinter->pszCN);
  1174. pIniPrinter->pszCN = NULL;
  1175. FreeSplStr(pIniPrinter->pszDN);
  1176. pIniPrinter->pszDN = NULL;
  1177. EnterSplSem();
  1178. if (pIniPrinter->bDsPendingDeletion) {
  1179. pIniPrinter->bDsPendingDeletion = 0;
  1180. DECPRINTERREF(pIniPrinter);
  1181. }
  1182. LeaveSplSem();
  1183. DBGMSG(DBG_EXEC, ("UnpublishByGUID Succeeded\n"));
  1184. } else {
  1185. pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH;
  1186. DBGMSG(DBG_EXEC, ("UnpublishByGUID Failed\n"));
  1187. }
  1188. SplOutSem();
  1189. return hr;
  1190. }
  1191. HRESULT
  1192. GetDNSMachineName(
  1193. PWSTR pszShortMachineName,
  1194. PWSTR *ppszMachineName
  1195. )
  1196. {
  1197. struct hostent *pHostEnt;
  1198. DWORD dwRet = ERROR_SUCCESS;
  1199. HRESULT hr = S_OK;
  1200. PSTR pszAnsiMachineName = NULL;
  1201. WORD wVersion;
  1202. WSADATA WSAData;
  1203. wVersion = MAKEWORD(1, 1);
  1204. dwRet = WSAStartup(wVersion, &WSAData);
  1205. if (dwRet == ERROR_SUCCESS) {
  1206. if (!pszShortMachineName || !*pszShortMachineName) {
  1207. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INVALID_PARAMETER);
  1208. BAIL_ON_FAILURE(hr);
  1209. }
  1210. if (!(pszAnsiMachineName = (PSTR) AllocSplStr(pszShortMachineName))) {
  1211. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1212. BAIL_ON_FAILURE(hr);
  1213. }
  1214. if (!UnicodeToAnsiString((PWSTR) pszAnsiMachineName, pszAnsiMachineName, NULL_TERMINATED)) {
  1215. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1216. BAIL_ON_FAILURE(hr);
  1217. }
  1218. if (!(pHostEnt = gethostbyname(pszAnsiMachineName))) {
  1219. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, WSAGetLastError());
  1220. BAIL_ON_FAILURE(hr);
  1221. }
  1222. if (!(*ppszMachineName = AnsiToUnicodeStringWithAlloc(pHostEnt->h_name))) {
  1223. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1224. BAIL_ON_FAILURE(hr);
  1225. }
  1226. } else {
  1227. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
  1228. }
  1229. error:
  1230. if (dwRet == ERROR_SUCCESS)
  1231. {
  1232. WSACleanup();
  1233. }
  1234. FreeSplMem(pszAnsiMachineName);
  1235. return hr;
  1236. }
  1237. HRESULT
  1238. GetClusterUser(
  1239. IADs **ppADs
  1240. )
  1241. {
  1242. HRESULT hr;
  1243. WCHAR szUserName[MAX_PATH + 8]; // Allow for LDAP://
  1244. PWSTR pszUserName = szUserName;
  1245. DWORD cchUserName = MAX_PATH + 1;
  1246. BOOL bRet;
  1247. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  1248. if (FAILED(hr))
  1249. return hr;
  1250. // Get cluster container's name, which must be the current user name
  1251. wcscpy(pszUserName, L"LDAP://");
  1252. if (!GetUserNameEx(NameFullyQualifiedDN, pszUserName + 7, &cchUserName)) {
  1253. if (cchUserName > MAX_PATH + 1) {
  1254. pszUserName = (PWSTR) AllocSplMem((cchUserName + 7)*sizeof(WCHAR));
  1255. if (!pszUserName) {
  1256. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1257. goto error;
  1258. }
  1259. wcscpy(pszUserName, L"LDAP://");
  1260. if (!GetUserNameEx(NameFullyQualifiedDN, pszUserName + 7, &cchUserName)) {
  1261. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1262. goto error;
  1263. }
  1264. } else {
  1265. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1266. goto error;
  1267. }
  1268. }
  1269. // Get the object
  1270. hr = ADsGetObject( pszUserName,
  1271. IID_IADs,
  1272. (void **) ppADs
  1273. );
  1274. BAIL_ON_FAILURE(hr);
  1275. error:
  1276. if (pszUserName != szUserName)
  1277. FreeSplStr(pszUserName);
  1278. CoUninitialize();
  1279. return hr;
  1280. }
  1281. HRESULT
  1282. FQDN2Whatever(
  1283. PWSTR pszIn,
  1284. PWSTR *ppszOut,
  1285. DS_NAME_FORMAT NameFormat
  1286. )
  1287. {
  1288. DWORD dwRet = ERROR_SUCCESS;
  1289. DS_NAME_RESULT *pDNR = NULL;
  1290. DOMAIN_CONTROLLER_INFO *pDCI = NULL;
  1291. HANDLE hDS = NULL;
  1292. PWSTR pNames[2];
  1293. PWSTR psz;
  1294. HRESULT hr = S_OK;
  1295. *ppszOut = NULL;
  1296. // Get the DC name
  1297. dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
  1298. if (dwRet != ERROR_SUCCESS)
  1299. goto error;
  1300. // Translate the name
  1301. if (wcslen(pszIn) < 8) {
  1302. dwRet = ERROR_INVALID_PARAMETER;
  1303. goto error;
  1304. }
  1305. psz = wcschr(pszIn + 7, L'/');
  1306. if (!psz) {
  1307. dwRet = ERROR_INVALID_PARAMETER;
  1308. goto error;
  1309. }
  1310. pNames[0] = ++psz; // Input string is LDAP://ntdev.microsoft.com/CN=... Strip off the LDAP://.../ portion
  1311. pNames[1] = NULL;
  1312. if (!(DsCrackNames(
  1313. hDS,
  1314. DS_NAME_NO_FLAGS,
  1315. DS_FQDN_1779_NAME,
  1316. NameFormat,
  1317. 1,
  1318. &pNames[0],
  1319. &pDNR) == ERROR_SUCCESS)) {
  1320. dwRet = GetLastError();
  1321. goto error;
  1322. }
  1323. if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
  1324. dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
  1325. goto error;
  1326. }
  1327. *ppszOut = AllocSplStr(pDNR->rItems[0].pName);
  1328. error:
  1329. if (pDNR)
  1330. DsFreeNameResult(pDNR);
  1331. if (hDS)
  1332. DsUnBind(&hDS);
  1333. if (pDCI)
  1334. NetApiBufferFree(pDCI);
  1335. return dwRet == ERROR_SUCCESS ? S_OK : MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
  1336. }
  1337. DWORD
  1338. InitializeDSClusterInfo(
  1339. PINISPOOLER pIniSpooler,
  1340. HANDLE *phToken
  1341. )
  1342. {
  1343. HRESULT hr = S_OK;
  1344. DWORD dwError = ERROR_SUCCESS;
  1345. IADs *pADs = NULL;
  1346. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  1347. if (FAILED(hr)) {
  1348. return HRESULT_CODE(hr);
  1349. }
  1350. // Impersonate the client
  1351. if (!ImpersonatePrinterClient(*phToken)) {
  1352. dwError = GetLastError();
  1353. DBGMSG(DBG_WARNING,("InitializeDSClusterInfo FAILED: %d\n", dwError));
  1354. CoUninitialize();
  1355. return dwError;
  1356. }
  1357. // Get a copy of the client token
  1358. dwError = NtOpenThreadToken(NtCurrentThread(), TOKEN_IMPERSONATE, TRUE, &pIniSpooler->hClusterToken);
  1359. if (dwError != STATUS_SUCCESS) {
  1360. DBGMSG(DBG_WARNING,("InitializeDSClusterInfo DuplicateToken FAILED: %d\n", dwError));
  1361. pIniSpooler->hClusterToken = INVALID_HANDLE_VALUE;
  1362. goto error;
  1363. }
  1364. // Get the Cluster User Object
  1365. hr = GetClusterUser(&pADs);
  1366. // Get the GUID to the Cluster User Object
  1367. if (SUCCEEDED(hr))
  1368. hr = GetGUID(pADs, &pIniSpooler->pszClusterGUID);
  1369. error:
  1370. *phToken = RevertToPrinterSelf();
  1371. if (FAILED(hr)) {
  1372. dwError = HRESULT_CODE(hr);
  1373. FreeSplStr(pIniSpooler->pszClusterGUID);
  1374. pIniSpooler->pszClusterGUID = NULL;
  1375. }
  1376. if (pADs)
  1377. pADs->Release();
  1378. CoUninitialize();
  1379. return dwError;
  1380. }
  1381. BOOL
  1382. CheckPublishedPrinters(
  1383. )
  1384. {
  1385. PINIPRINTER pIniPrinter;
  1386. BOOL bHavePublishedPrinters = FALSE;
  1387. SplInSem();
  1388. if (VerifyPublishedStatePolicy() == INFINITE)
  1389. return FALSE;
  1390. RunForEachSpooler(&bHavePublishedPrinters, CheckPublishedSpooler);
  1391. return bHavePublishedPrinters;
  1392. }
  1393. BOOL
  1394. CheckPublishedSpooler(
  1395. HANDLE h,
  1396. PINISPOOLER pIniSpooler
  1397. )
  1398. {
  1399. PBOOL pbHavePublishedPrinters = (PBOOL)h;
  1400. PINIPRINTER pIniPrinter;
  1401. if (!(pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL))
  1402. return TRUE;
  1403. for (pIniPrinter = pIniSpooler->pIniPrinter ; pIniPrinter ; pIniPrinter = pIniPrinter->pNext) {
  1404. if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
  1405. // Refresh: verify that we're really published
  1406. // Note that if there isn't any new info to publish and we're published,
  1407. // we won't do any SetInfo so the overhead is minimal
  1408. pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH;
  1409. *pbHavePublishedPrinters = TRUE;
  1410. } else if (pIniPrinter->pszObjectGUID) {
  1411. // The only way we can get here is if someone is unpublishing the printer,
  1412. // so we don't really need to set the background DsKeyUpdate state. Doing
  1413. // so maintains symmetry with above statement and InitializeDS
  1414. pIniPrinter->DsKeyUpdate |= DS_KEY_UNPUBLISH;
  1415. *pbHavePublishedPrinters = TRUE;
  1416. }
  1417. }
  1418. return TRUE;
  1419. }
  1420. PWSTR
  1421. CreateEscapedString(
  1422. PCWSTR pszIn,
  1423. PCWSTR pszSpecialChars
  1424. )
  1425. {
  1426. PWSTR psz, pszO;
  1427. PWSTR pszOut = NULL;
  1428. DWORD cb;
  1429. if (!pszIn || !pszSpecialChars) {
  1430. SetLastError(ERROR_INVALID_NAME);
  1431. return NULL;
  1432. }
  1433. // Count special characters
  1434. for (cb = 0, psz = (PWSTR) pszIn ; psz = wcspbrk(psz, pszSpecialChars) ; ++cb, ++psz)
  1435. ;
  1436. // Add in length of input string
  1437. cb = (wcslen(pszIn) + cb + 1)*sizeof *pszIn;
  1438. // Allocate output buffer and precede special DS chars with '\'
  1439. if (pszOut = (PWSTR) AllocSplMem(cb)) {
  1440. for(psz = (PWSTR) pszIn, pszO = pszOut ; *psz ; ++psz, ++pszO) {
  1441. if (wcschr(pszSpecialChars, *psz)) {
  1442. *pszO++ = L'\\';
  1443. }
  1444. *pszO = *psz;
  1445. }
  1446. *pszO = L'\0';
  1447. }
  1448. return pszOut;
  1449. }
  1450. PWSTR
  1451. DevCapStrings2MultiSz(
  1452. PWSTR pszDevCapString,
  1453. DWORD nDevCapStrings,
  1454. DWORD dwDevCapStringLength,
  1455. DWORD *pcbBytes
  1456. )
  1457. {
  1458. DWORD i, cbBytes, cbSize;
  1459. PWSTR pszMultiSz = NULL;
  1460. PWSTR pStr;
  1461. if (!pszDevCapString || !pcbBytes)
  1462. return NULL;
  1463. *pcbBytes = 0;
  1464. //
  1465. // Devcap buffers may not be NULL terminated
  1466. //
  1467. cbBytes = (nDevCapStrings*(dwDevCapStringLength + 1) + 1)*sizeof(WCHAR);
  1468. //
  1469. // Allocate and copy
  1470. //
  1471. if (pszMultiSz = (PWSTR) AllocSplMem(cbBytes)) {
  1472. for(i = 0, pStr = pszMultiSz, cbBytes = 0 ; i < nDevCapStrings ; ++i, pStr += cbSize, cbBytes +=cbSize ) {
  1473. wcsncpy(pStr, pszDevCapString + i*dwDevCapStringLength, dwDevCapStringLength);
  1474. cbSize = *pStr ? wcslen(pStr) + 1 : 0;
  1475. }
  1476. *pStr = L'\0';
  1477. *pcbBytes = (cbBytes + 1) * sizeof(WCHAR);
  1478. }
  1479. return pszMultiSz;
  1480. }
  1481. DWORD
  1482. Bind2DS(
  1483. HANDLE *phDS,
  1484. DOMAIN_CONTROLLER_INFO **ppDCI,
  1485. ULONG Flags
  1486. )
  1487. {
  1488. DWORD dwRet;
  1489. dwRet = DsGetDcName(NULL, NULL, NULL, NULL, Flags, ppDCI);
  1490. if (dwRet == ERROR_SUCCESS) {
  1491. if ((*ppDCI)->Flags & DS_DS_FLAG) {
  1492. dwRet = DsBind (NULL, (*ppDCI)->DomainName, phDS);
  1493. if (dwRet != ERROR_SUCCESS) {
  1494. NetApiBufferFree(*ppDCI);
  1495. *ppDCI = NULL;
  1496. if (!(Flags & DS_FORCE_REDISCOVERY)) {
  1497. dwRet = Bind2DS(phDS, ppDCI, DS_FORCE_REDISCOVERY | Flags);
  1498. }
  1499. }
  1500. } else {
  1501. NetApiBufferFree(*ppDCI);
  1502. *ppDCI = NULL;
  1503. dwRet = ERROR_CANT_ACCESS_DOMAIN_INFO;
  1504. }
  1505. }
  1506. return dwRet;
  1507. }
  1508. DWORD
  1509. DsCrackNamesStatus2Win32Error(
  1510. DWORD dwStatus
  1511. )
  1512. {
  1513. switch (dwStatus) {
  1514. case DS_NAME_ERROR_RESOLVING:
  1515. return ERROR_DS_NAME_ERROR_RESOLVING;
  1516. case DS_NAME_ERROR_NOT_FOUND:
  1517. return ERROR_DS_NAME_ERROR_NOT_FOUND;
  1518. case DS_NAME_ERROR_NOT_UNIQUE:
  1519. return ERROR_DS_NAME_ERROR_NOT_UNIQUE;
  1520. case DS_NAME_ERROR_NO_MAPPING:
  1521. return ERROR_DS_NAME_ERROR_NO_MAPPING;
  1522. case DS_NAME_ERROR_DOMAIN_ONLY:
  1523. return ERROR_DS_NAME_ERROR_DOMAIN_ONLY;
  1524. case DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING:
  1525. return ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING;
  1526. }
  1527. return ERROR_FILE_NOT_FOUND;
  1528. }
  1529. DWORD
  1530. GetDSSleepInterval (
  1531. HANDLE h
  1532. )
  1533. {
  1534. DWORD dwVerifyPublishedStateInterval;
  1535. PDSUPDATEDATA pData = (PDSUPDATEDATA)h;
  1536. DWORD dwTimeToSleep = 30 * ONE_MINUTE;
  1537. //
  1538. // 30 min is the minimum interval that can be set with the policy editor.
  1539. // If someone enables the policy while we are sleeping, then we need to wake up
  1540. // to pick the settings. This doesn't apply if the DS doesn't respond.
  1541. //
  1542. if (pData && pData->bSleep) {
  1543. //
  1544. // If the updating is failing, Data.bSleep is set to TRUE.
  1545. // This happens when the DS is down.
  1546. //
  1547. dwTimeToSleep = pData->dwSleepTime;
  1548. //
  1549. // Sleep interval is doubled to a maximum of 2 hours.
  1550. // We still want to attempt publishing every 2 hours. We also attempt if
  1551. // a "publish" action is taken(the even will be signaled).
  1552. //
  1553. //
  1554. pData->dwSleepTime = pData->dwSleepTime * 2 > 2 * ONE_HOUR ?
  1555. 2 * ONE_HOUR :
  1556. pData->dwSleepTime * 2;
  1557. } else {
  1558. dwTimeToSleep = INFINITE;
  1559. }
  1560. return dwTimeToSleep;
  1561. }