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.

2080 lines
55 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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCbPrintf(*ppszLDAPPath, nBytes, 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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCbCopy(psz, nBytes, 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. StringCchPrintf(pszDigits, (cchCommonName + 1) - (pszDigits - *ppszCommonName), 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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCchPrintf(szName, COUNTOF(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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCchPrintf(ErrorBuffer, COUNTOF(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. StringCchPrintf(ErrorBuffer, COUNTOF(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. GetSID(
  752. IADs *pADs,
  753. PWSTR *ppszObjectSID
  754. )
  755. /*++
  756. Function Description:
  757. This function returns the ObjectSID of a given ADs user object.
  758. ppszObjectGUID must be freed by caller using FreeSplStr().
  759. Parameters:
  760. pADs - input ADs object pointer
  761. ppszObjectSID - objectSID of pADs
  762. Return Values:
  763. HRESULT
  764. --*/
  765. {
  766. HRESULT hr;
  767. LPWSTR pszObjectSID = NULL;
  768. SID ObjectSID;
  769. hr = pADs->GetInfo();
  770. if (SUCCEEDED(hr)) {
  771. hr = get_SID_Property(pADs, L"ObjectSID", &pszObjectSID);
  772. if (SUCCEEDED(hr)) {
  773. if (!(*ppszObjectSID = AllocSplStr(pszObjectSID))) {
  774. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  775. }
  776. LocalFree(pszObjectSID);
  777. }
  778. }
  779. return hr;
  780. }
  781. HRESULT
  782. GetGUID(
  783. IADs *pADs,
  784. PWSTR *ppszObjectGUID
  785. )
  786. /*++
  787. Function Description:
  788. This function returns the ObjectGUID of a given ADs object.
  789. ppszObjectGUID must be freed by caller using FreeSplStr().
  790. Parameters:
  791. pADs - input ADs object pointer
  792. ppszObjectGUID - object GUID of pADs
  793. Return Values:
  794. HRESULT
  795. --*/
  796. {
  797. HRESULT hr;
  798. LPOLESTR pszObjectGUID = NULL;
  799. IID ObjectIID;
  800. hr = pADs->GetInfo();
  801. if (SUCCEEDED(hr)) {
  802. hr = get_UI1Array_Property(pADs, L"ObjectGUID", &ObjectIID);
  803. if (SUCCEEDED(hr)) {
  804. hr = StringFromIID(ObjectIID, &pszObjectGUID);
  805. if (SUCCEEDED(hr)) {
  806. if (!(*ppszObjectGUID = AllocSplStr(pszObjectGUID)))
  807. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  808. }
  809. CoTaskMemFree(pszObjectGUID);
  810. }
  811. }
  812. return hr;
  813. }
  814. BOOL
  815. PrinterPublishProhibited()
  816. {
  817. return !GetSpoolerNumericPolicy(szPublishPrinters, DEFAULT_PRINT_PUBLISH_POLICY);
  818. }
  819. DWORD
  820. VerifyPublishedStatePolicy()
  821. {
  822. return GetSpoolerNumericPolicy(szVerifyPublishedState, DEFAULT_VERIFY_PUBLISHED_STATE);
  823. }
  824. DWORD
  825. PruneDownlevel()
  826. {
  827. return GetSpoolerNumericPolicy(szPruneDownlevel, DEFAULT_PRUNE_DOWNLEVEL);
  828. }
  829. DWORD
  830. PruningInterval(
  831. )
  832. {
  833. return GetSpoolerNumericPolicy(szPruningInterval, DEFAULT_PRUNING_INTERVAL);
  834. }
  835. DWORD
  836. ImmortalPolicy(
  837. )
  838. {
  839. return GetSpoolerNumericPolicy(szImmortal, DEFAULT_IMMORTAL);
  840. }
  841. VOID
  842. ServerThreadPolicy(
  843. BOOL bHaveDs
  844. )
  845. {
  846. DWORD dwPolicy;
  847. dwPolicy = GetSpoolerNumericPolicy(szServerThreadPolicy, SERVER_THREAD_UNCONFIGURED);
  848. if (dwPolicy == SERVER_THREAD_UNCONFIGURED) {
  849. ServerThreadRunning = !(bHaveDs ? SERVER_THREAD_OFF : SERVER_THREAD_ON);
  850. } else {
  851. ServerThreadRunning = !dwPolicy;
  852. }
  853. CreateServerThread();
  854. }
  855. DWORD
  856. PruningRetries(
  857. )
  858. {
  859. DWORD dwPruningRetries;
  860. dwPruningRetries = GetSpoolerNumericPolicy(szPruningRetries, DEFAULT_PRUNING_RETRIES);
  861. if (dwPruningRetries > MAX_PRUNING_RETRIES)
  862. dwPruningRetries = MAX_PRUNING_RETRIES;
  863. return dwPruningRetries;
  864. }
  865. DWORD
  866. PruningRetryLog(
  867. )
  868. {
  869. DWORD dwPruningRetryLog;
  870. dwPruningRetryLog = GetSpoolerNumericPolicy(szPruningRetryLog, DEFAULT_PRUNING_RETRY_LOG);
  871. return dwPruningRetryLog;
  872. }
  873. VOID
  874. SetPruningPriority(
  875. )
  876. {
  877. DWORD dwPriority = GetSpoolerNumericPolicy(szPruningPriority, DEFAULT_PRUNING_PRIORITY);
  878. if (dwPriority != dwLastPruningPriority) {
  879. if (dwPriority == THREAD_PRIORITY_LOWEST ||
  880. dwPriority == THREAD_PRIORITY_BELOW_NORMAL ||
  881. dwPriority == THREAD_PRIORITY_NORMAL ||
  882. dwPriority == THREAD_PRIORITY_ABOVE_NORMAL ||
  883. dwPriority == THREAD_PRIORITY_HIGHEST) {
  884. SetThreadPriority(GetCurrentThread(), DEFAULT_PRUNING_PRIORITY);
  885. } else {
  886. SetThreadPriority(GetCurrentThread(), dwPriority);
  887. }
  888. dwLastPruningPriority = dwPriority;
  889. }
  890. }
  891. BOOL
  892. ThisMachineIsADC(
  893. )
  894. {
  895. NT_PRODUCT_TYPE NtProductType;
  896. RtlGetNtProductType(&NtProductType);
  897. return NtProductType == NtProductLanManNt;
  898. }
  899. DWORD
  900. GetDomainRoot(
  901. PWSTR *ppszDomainRoot
  902. )
  903. /*++
  904. Function Description:
  905. This function returns the ADsPath of the root of the current domain
  906. Parameters:
  907. ppszDomainRoot - pointer to buffer receiving string pointer of domain root ADsPath string
  908. free ppszDomainRoot with a call to FreeSplMem
  909. Return Values:
  910. DWORD
  911. --*/
  912. {
  913. DWORD dwRet = ERROR_SUCCESS;
  914. PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
  915. DS_NAME_RESULT *pDNR = NULL;
  916. DOMAIN_CONTROLLER_INFO *pDCI = NULL;
  917. HANDLE hDS = NULL;
  918. WCHAR szName[MAX_PATH + 1];
  919. PWSTR pNames[2];
  920. PWSTR pszDomainRoot;
  921. DWORD cb;
  922. if (!ppszDomainRoot) {
  923. dwRet = ERROR_INVALID_PARAMETER;
  924. goto error;
  925. }
  926. // Get Domain name
  927. dwRet = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole);
  928. if (dwRet)
  929. goto error;
  930. StringCchPrintf(szName, COUNTOF(szName), L"%ws\\", pDsRole->DomainNameFlat);
  931. pNames[0] = szName;
  932. pNames[1] = NULL;
  933. dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
  934. if (dwRet != ERROR_SUCCESS) {
  935. goto error;
  936. }
  937. if (!(DsCrackNames(
  938. hDS,
  939. DS_NAME_NO_FLAGS,
  940. DS_UNKNOWN_NAME,
  941. DS_FQDN_1779_NAME,
  942. 1,
  943. &pNames[0],
  944. &pDNR) == ERROR_SUCCESS)) {
  945. dwRet = GetLastError();
  946. goto error;
  947. }
  948. if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
  949. dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
  950. goto error;
  951. }
  952. // LDAP:// + pDCName + 1
  953. cb = (wcslen(pDNR->rItems[0].pName) + 8)*sizeof(WCHAR);
  954. if (!(*ppszDomainRoot = (PWSTR) AllocSplMem(cb))) {
  955. dwRet = GetLastError();
  956. goto error;
  957. }
  958. StringCbPrintf(*ppszDomainRoot, cb, L"LDAP://%ws", pDNR->rItems[0].pName);
  959. error:
  960. if (pDNR)
  961. DsFreeNameResult(pDNR);
  962. if (hDS)
  963. DsUnBind(&hDS);
  964. if (pDCI)
  965. NetApiBufferFree(pDCI);
  966. if (pDsRole)
  967. DsRoleFreeMemory((PVOID) pDsRole);
  968. return dwRet;
  969. }
  970. PWSTR
  971. CreateSearchString(
  972. PWSTR pszIn
  973. )
  974. {
  975. PWSTR psz, pszSS;
  976. PWSTR pszSearchString = NULL;
  977. DWORD cb;
  978. /* Replace \ with \5c */
  979. /* Count chars & pad */
  980. for (cb = 0, psz = pszIn ; *psz ; ++psz, ++cb) {
  981. if (*psz == L'\\')
  982. cb += 2;
  983. }
  984. cb = (cb + 1)*sizeof *psz;
  985. if (pszSearchString = (PWSTR) GlobalAlloc(GMEM_FIXED, cb)) {
  986. for(psz = pszIn, pszSS = pszSearchString ; *psz ; ++psz, ++pszSS) {
  987. *pszSS = *psz;
  988. if (*psz == L'\\') {
  989. *++pszSS = L'5';
  990. *++pszSS = L'c';
  991. }
  992. }
  993. *pszSS = L'\0';
  994. }
  995. return pszSearchString;
  996. }
  997. BOOL
  998. ServerOnSite(
  999. PWSTR *ppszMySites,
  1000. ULONG cMySites,
  1001. PWSTR pszServer
  1002. )
  1003. /*++
  1004. Function Description:
  1005. This function returns TRUE if pszServer is on one of the ppszMySites and pszServer exists
  1006. Parameters:
  1007. ppszMySites - input sites
  1008. pszServer - input server name
  1009. Return Values:
  1010. BOOL - TRUE if server exists on site, FALSE otherwise or on error
  1011. --*/
  1012. {
  1013. PSOCKET_ADDRESS pSocketAddresses = NULL;
  1014. PWSTR *ppszSiteNames = NULL;
  1015. DWORD nAddresses;
  1016. DWORD dwRet, i;
  1017. ULONG j;
  1018. WORD wVersion;
  1019. WSADATA WSAData;
  1020. BOOL bServerOnSite = FALSE;
  1021. wVersion = MAKEWORD(1, 1);
  1022. if (WSAStartup(wVersion, &WSAData) == ERROR_SUCCESS) {
  1023. // Find out if Server is on Site
  1024. GetSocketAddressesFromMachineName(pszServer, &pSocketAddresses, &nAddresses);
  1025. if (nAddresses == 0) {
  1026. bServerOnSite = TRUE; // Claim server is on site if we can't find it
  1027. } else {
  1028. dwRet = DsAddressToSiteNames( (PCWSTR) NULL,
  1029. nAddresses,
  1030. pSocketAddresses,
  1031. &ppszSiteNames);
  1032. if (dwRet == NO_ERROR) {
  1033. for(i = 0 ; i < nAddresses ; ++i) {
  1034. for(j = 0 ; j < cMySites ; ++j) {
  1035. if (ppszSiteNames[i] && ppszMySites[j] && !wcscmp(ppszSiteNames[i], ppszMySites[j])) {
  1036. bServerOnSite = TRUE;
  1037. break;
  1038. }
  1039. }
  1040. }
  1041. }
  1042. }
  1043. if (ppszSiteNames)
  1044. NetApiBufferFree(ppszSiteNames);
  1045. if (pSocketAddresses)
  1046. FreeSplSockets(pSocketAddresses, nAddresses);
  1047. WSACleanup();
  1048. }
  1049. return bServerOnSite;
  1050. }
  1051. VOID
  1052. FreeSplSockets(
  1053. PSOCKET_ADDRESS pSocketAddress,
  1054. DWORD nAddresses
  1055. )
  1056. {
  1057. DWORD i;
  1058. PSOCKET_ADDRESS pSocket;
  1059. for(i = 0, pSocket = pSocketAddress ; i < nAddresses ; ++i, ++pSocket)
  1060. FreeSplMem(pSocket->lpSockaddr);
  1061. FreeSplMem(pSocketAddress);
  1062. }
  1063. VOID
  1064. AllocSplSockets(
  1065. struct hostent *pHostEnt,
  1066. PSOCKET_ADDRESS *ppSocketAddress,
  1067. DWORD *nSocketAddresses
  1068. )
  1069. {
  1070. DWORD i;
  1071. PSOCKET_ADDRESS pSocket;
  1072. for ( *nSocketAddresses = 0 ; pHostEnt->h_addr_list[*nSocketAddresses] ; ++(*nSocketAddresses))
  1073. ;
  1074. // Allocate SOCKET_ADDRESS array
  1075. *ppSocketAddress = (PSOCKET_ADDRESS) AllocSplMem(*nSocketAddresses*sizeof(SOCKET_ADDRESS));
  1076. if (!*ppSocketAddress)
  1077. *nSocketAddresses = 0;
  1078. // Allocate Sockaddr element for each SOCKET_ADDRESS
  1079. // If we fail partway through, just use partial list
  1080. for (i = 0, pSocket = *ppSocketAddress ; i < *nSocketAddresses ; ++i, ++pSocket) {
  1081. if (!(pSocket->lpSockaddr = (struct sockaddr *) AllocSplMem(sizeof(struct sockaddr_in)))) {
  1082. *nSocketAddresses = i;
  1083. break;
  1084. }
  1085. if (pHostEnt->h_addrtype == AF_INET) {
  1086. ((struct sockaddr_in *) pSocket->lpSockaddr)->sin_family = AF_INET;
  1087. ((struct sockaddr_in *) pSocket->lpSockaddr)->sin_addr = *(struct in_addr *) pHostEnt->h_addr_list[i];
  1088. pSocket->iSockaddrLength = sizeof(struct sockaddr_in);
  1089. } else {
  1090. DBGMSG(DBG_WARNING,("AllocSplSockets: addrtype != AF_INET: %d\n", pHostEnt->h_addrtype));
  1091. }
  1092. }
  1093. }
  1094. VOID
  1095. GetSocketAddressesFromMachineName(
  1096. PWSTR pszMachineName, // \\Machine
  1097. PSOCKET_ADDRESS *ppSocketAddress,
  1098. DWORD *nSocketAddresses
  1099. )
  1100. /*++
  1101. Routine Description:
  1102. This routine builds list of names other than the machine name that
  1103. can be used to call spooler APIs.
  1104. --*/
  1105. {
  1106. struct hostent *HostEnt;
  1107. PSTR pszAnsiMachineName = NULL;
  1108. DWORD iWsaError;
  1109. *nSocketAddresses = 0;
  1110. *ppSocketAddress = 0;
  1111. if (SUCCEEDED(UnicodeToAnsiString(pszMachineName + 2, &pszAnsiMachineName))) {
  1112. if (HostEnt = gethostbyname(pszAnsiMachineName)) {
  1113. AllocSplSockets(HostEnt, ppSocketAddress, nSocketAddresses);
  1114. } else {
  1115. iWsaError = WSAGetLastError();
  1116. DBGMSG(DBG_WARNING, ("gethostbyname failed in DsPrune: %d\n", iWsaError));
  1117. }
  1118. }
  1119. FreeSplMem(pszAnsiMachineName);
  1120. }
  1121. DWORD
  1122. UNC2Server(
  1123. PCWSTR pszUNC,
  1124. PWSTR *ppszServer
  1125. )
  1126. {
  1127. PWSTR psz;
  1128. DWORD cb;
  1129. DWORD nChars;
  1130. if (!pszUNC || pszUNC[0] != L'\\' || pszUNC[1] != L'\\')
  1131. return ERROR_INVALID_PARAMETER;
  1132. if(!(psz = wcschr(pszUNC + 2, L'\\')))
  1133. return ERROR_INVALID_PARAMETER;
  1134. cb = (DWORD) ((ULONG_PTR) psz - (ULONG_PTR) pszUNC + sizeof *psz);
  1135. if (!(*ppszServer = (PWSTR) AllocSplMem(cb)))
  1136. return GetLastError();
  1137. nChars = (DWORD) (psz - pszUNC);
  1138. wcsncpy(*ppszServer, pszUNC, nChars);
  1139. (*ppszServer)[nChars] = L'\0';
  1140. return ERROR_SUCCESS;
  1141. }
  1142. BOOL
  1143. ServerExists(
  1144. PWSTR pszServerName
  1145. )
  1146. {
  1147. NET_API_STATUS Status;
  1148. SERVER_INFO_100 *pServer;
  1149. BOOL bServerExists;
  1150. Status = NetServerGetInfo(pszServerName, 100, (PBYTE *) &pServer);
  1151. bServerExists = !Status;
  1152. Status = NetApiBufferFree(pServer);
  1153. return bServerExists;
  1154. }
  1155. HRESULT
  1156. UnpublishByGUID(
  1157. PINIPRINTER pIniPrinter
  1158. )
  1159. {
  1160. HRESULT hr;
  1161. SplOutSem();
  1162. if (!pIniPrinter->pszObjectGUID) {
  1163. pIniPrinter->DsKeyUpdate = 0;
  1164. pIniPrinter->DsKeyUpdateForeground = 0;
  1165. hr = S_OK;
  1166. } else {
  1167. PWSTR pszDN = NULL;
  1168. PWSTR pszCN = NULL;
  1169. hr = GetPublishPointFromGUID(NULL, pIniPrinter->pszObjectGUID, &pszDN, &pszCN, TRUE);
  1170. DBGMSG(DBG_EXEC,
  1171. ("UnpublishByGUID: GUID %ws\n", pIniPrinter->pszObjectGUID));
  1172. if (SUCCEEDED(hr)) {
  1173. DBGMSG(DBG_EXEC,
  1174. ("UnpublishByGUID: DN %ws CN %ws\n",
  1175. pszDN,
  1176. pszCN));
  1177. IADsContainer *pADsContainer = NULL;
  1178. // Get the container
  1179. hr = ADsGetObject( pszDN,
  1180. IID_IADsContainer,
  1181. (void **) &pADsContainer
  1182. );
  1183. if (SUCCEEDED(hr)) {
  1184. hr = pADsContainer->Delete(SPLDS_PRINTER_CLASS, pszCN);
  1185. pADsContainer->Release();
  1186. }
  1187. // If the container or the object is gone, succeed
  1188. if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT) ||
  1189. HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND ||
  1190. HRESULT_CODE(hr) == ERROR_PATH_NOT_FOUND)
  1191. hr = S_OK;
  1192. }
  1193. FreeSplStr(pszDN);
  1194. FreeSplStr(pszCN);
  1195. }
  1196. // free GUID if object is deleted
  1197. if (SUCCEEDED(hr)) {
  1198. pIniPrinter->DsKeyUpdate = 0;
  1199. pIniPrinter->DsKeyUpdateForeground = 0;
  1200. FreeSplStr(pIniPrinter->pszObjectGUID);
  1201. pIniPrinter->pszObjectGUID = NULL;
  1202. FreeSplStr(pIniPrinter->pszCN);
  1203. pIniPrinter->pszCN = NULL;
  1204. FreeSplStr(pIniPrinter->pszDN);
  1205. pIniPrinter->pszDN = NULL;
  1206. EnterSplSem();
  1207. if (pIniPrinter->bDsPendingDeletion) {
  1208. pIniPrinter->bDsPendingDeletion = 0;
  1209. DECPRINTERREF(pIniPrinter);
  1210. }
  1211. LeaveSplSem();
  1212. DBGMSG(DBG_EXEC, ("UnpublishByGUID Succeeded\n"));
  1213. } else {
  1214. pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH;
  1215. DBGMSG(DBG_EXEC, ("UnpublishByGUID Failed\n"));
  1216. }
  1217. SplOutSem();
  1218. return hr;
  1219. }
  1220. HRESULT
  1221. GetDNSMachineName(
  1222. PWSTR pszShortMachineName,
  1223. PWSTR *ppszMachineName
  1224. )
  1225. {
  1226. struct hostent *pHostEnt;
  1227. DWORD dwRet = ERROR_SUCCESS;
  1228. HRESULT hr = S_OK;
  1229. PSTR pszAnsiMachineName = NULL;
  1230. WORD wVersion;
  1231. WSADATA WSAData;
  1232. wVersion = MAKEWORD(1, 1);
  1233. dwRet = WSAStartup(wVersion, &WSAData);
  1234. if (dwRet == ERROR_SUCCESS) {
  1235. if (!pszShortMachineName || !*pszShortMachineName) {
  1236. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INVALID_PARAMETER);
  1237. BAIL_ON_FAILURE(hr);
  1238. }
  1239. hr = UnicodeToAnsiString(pszShortMachineName, &pszAnsiMachineName);
  1240. BAIL_ON_FAILURE(hr);
  1241. if (!(pHostEnt = gethostbyname(pszAnsiMachineName))) {
  1242. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, WSAGetLastError());
  1243. BAIL_ON_FAILURE(hr);
  1244. }
  1245. if (!(*ppszMachineName = AnsiToUnicodeStringWithAlloc(pHostEnt->h_name))) {
  1246. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1247. BAIL_ON_FAILURE(hr);
  1248. }
  1249. } else {
  1250. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
  1251. }
  1252. error:
  1253. if (dwRet == ERROR_SUCCESS)
  1254. {
  1255. WSACleanup();
  1256. }
  1257. FreeSplMem(pszAnsiMachineName);
  1258. return hr;
  1259. }
  1260. HRESULT
  1261. GetClusterUser(
  1262. IADs **ppADs
  1263. )
  1264. {
  1265. HRESULT hr;
  1266. WCHAR szUserName[MAX_PATH + 8]; // Allow for LDAP://
  1267. PWSTR pszUserName = szUserName;
  1268. DWORD cchUserName = MAX_PATH + 1;
  1269. BOOL bRet;
  1270. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  1271. if (FAILED(hr))
  1272. return hr;
  1273. // Get cluster container's name, which must be the current user name
  1274. StringCchCopy(pszUserName, COUNTOF(szUserName), L"LDAP://");
  1275. if (!GetUserNameEx(NameFullyQualifiedDN, pszUserName + 7, &cchUserName)) {
  1276. if (cchUserName > MAX_PATH + 1) {
  1277. pszUserName = (PWSTR) AllocSplMem((cchUserName + 7)*sizeof(WCHAR));
  1278. if (!pszUserName) {
  1279. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1280. goto error;
  1281. }
  1282. StringCchPrintf(pszUserName, cchUserName + 7, L"LDAP://");
  1283. if (!GetUserNameEx(NameFullyQualifiedDN, pszUserName + 7, &cchUserName)) {
  1284. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1285. goto error;
  1286. }
  1287. } else {
  1288. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
  1289. goto error;
  1290. }
  1291. }
  1292. // Get the object
  1293. hr = ADsGetObject( pszUserName,
  1294. IID_IADs,
  1295. (void **) ppADs
  1296. );
  1297. BAIL_ON_FAILURE(hr);
  1298. error:
  1299. if (pszUserName != szUserName)
  1300. FreeSplStr(pszUserName);
  1301. CoUninitialize();
  1302. return hr;
  1303. }
  1304. HRESULT
  1305. FQDN2Whatever(
  1306. PWSTR pszIn,
  1307. PWSTR *ppszOut,
  1308. DS_NAME_FORMAT NameFormat
  1309. )
  1310. {
  1311. DWORD dwRet = ERROR_SUCCESS;
  1312. DS_NAME_RESULT *pDNR = NULL;
  1313. DOMAIN_CONTROLLER_INFO *pDCI = NULL;
  1314. HANDLE hDS = NULL;
  1315. PWSTR pNames[2];
  1316. PWSTR psz;
  1317. HRESULT hr = S_OK;
  1318. *ppszOut = NULL;
  1319. // Get the DC name
  1320. dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
  1321. if (dwRet != ERROR_SUCCESS)
  1322. goto error;
  1323. // Translate the name
  1324. if (wcslen(pszIn) < 8) {
  1325. dwRet = ERROR_INVALID_PARAMETER;
  1326. goto error;
  1327. }
  1328. psz = wcschr(pszIn + 7, L'/');
  1329. if (!psz) {
  1330. dwRet = ERROR_INVALID_PARAMETER;
  1331. goto error;
  1332. }
  1333. pNames[0] = ++psz; // Input string is LDAP://ntdev.microsoft.com/CN=... Strip off the LDAP://.../ portion
  1334. pNames[1] = NULL;
  1335. if (!(DsCrackNames(
  1336. hDS,
  1337. DS_NAME_NO_FLAGS,
  1338. DS_FQDN_1779_NAME,
  1339. NameFormat,
  1340. 1,
  1341. &pNames[0],
  1342. &pDNR) == ERROR_SUCCESS)) {
  1343. dwRet = GetLastError();
  1344. goto error;
  1345. }
  1346. if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
  1347. dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
  1348. goto error;
  1349. }
  1350. *ppszOut = AllocSplStr(pDNR->rItems[0].pName);
  1351. error:
  1352. if (pDNR)
  1353. DsFreeNameResult(pDNR);
  1354. if (hDS)
  1355. DsUnBind(&hDS);
  1356. if (pDCI)
  1357. NetApiBufferFree(pDCI);
  1358. return dwRet == ERROR_SUCCESS ? S_OK : MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
  1359. }
  1360. DWORD
  1361. InitializeDSClusterInfo(
  1362. PINISPOOLER pIniSpooler,
  1363. HANDLE *phToken
  1364. )
  1365. {
  1366. HRESULT hr = S_OK;
  1367. DWORD dwError = ERROR_SUCCESS;
  1368. IADs *pADs = NULL;
  1369. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  1370. if (FAILED(hr)) {
  1371. return HRESULT_CODE(hr);
  1372. }
  1373. // Impersonate the client
  1374. if (!ImpersonatePrinterClient(*phToken)) {
  1375. dwError = GetLastError();
  1376. DBGMSG(DBG_WARNING,("InitializeDSClusterInfo FAILED: %d\n", dwError));
  1377. CoUninitialize();
  1378. return dwError;
  1379. }
  1380. // Get a copy of the client token
  1381. dwError = NtOpenThreadToken(NtCurrentThread(), TOKEN_IMPERSONATE, TRUE, &pIniSpooler->hClusterToken);
  1382. if (dwError != STATUS_SUCCESS) {
  1383. DBGMSG(DBG_WARNING,("InitializeDSClusterInfo DuplicateToken FAILED: %d\n", dwError));
  1384. pIniSpooler->hClusterToken = INVALID_HANDLE_VALUE;
  1385. goto error;
  1386. }
  1387. // Get the Cluster User Object
  1388. if (SUCCEEDED(hr = GetClusterUser(&pADs))) {
  1389. hr = GetSID(pADs, &pIniSpooler->pszClusterSID);
  1390. }
  1391. error:
  1392. *phToken = RevertToPrinterSelf();
  1393. if (FAILED(hr)) {
  1394. dwError = HRESULT_CODE(hr);
  1395. FreeSplStr(pIniSpooler->pszClusterSID);
  1396. pIniSpooler->pszClusterSID = NULL;
  1397. }
  1398. if (pADs)
  1399. pADs->Release();
  1400. CoUninitialize();
  1401. return dwError;
  1402. }
  1403. BOOL
  1404. CheckPublishedPrinters(
  1405. )
  1406. {
  1407. PINIPRINTER pIniPrinter;
  1408. BOOL bHavePublishedPrinters = FALSE;
  1409. SplInSem();
  1410. if (VerifyPublishedStatePolicy() == INFINITE)
  1411. return FALSE;
  1412. RunForEachSpooler(&bHavePublishedPrinters, CheckPublishedSpooler);
  1413. return bHavePublishedPrinters;
  1414. }
  1415. BOOL
  1416. CheckPublishedSpooler(
  1417. HANDLE h,
  1418. PINISPOOLER pIniSpooler
  1419. )
  1420. {
  1421. PBOOL pbHavePublishedPrinters = (PBOOL)h;
  1422. PINIPRINTER pIniPrinter;
  1423. if (!(pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL))
  1424. return TRUE;
  1425. for (pIniPrinter = pIniSpooler->pIniPrinter ; pIniPrinter ; pIniPrinter = pIniPrinter->pNext) {
  1426. if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
  1427. // Refresh: verify that we're really published
  1428. // Note that if there isn't any new info to publish and we're published,
  1429. // we won't do any SetInfo so the overhead is minimal
  1430. pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH;
  1431. *pbHavePublishedPrinters = TRUE;
  1432. } else if (pIniPrinter->pszObjectGUID) {
  1433. // The only way we can get here is if someone is unpublishing the printer,
  1434. // so we don't really need to set the background DsKeyUpdate state. Doing
  1435. // so maintains symmetry with above statement and InitializeDS
  1436. pIniPrinter->DsKeyUpdate |= DS_KEY_UNPUBLISH;
  1437. *pbHavePublishedPrinters = TRUE;
  1438. }
  1439. }
  1440. return TRUE;
  1441. }
  1442. PWSTR
  1443. CreateEscapedString(
  1444. PCWSTR pszIn,
  1445. PCWSTR pszSpecialChars
  1446. )
  1447. {
  1448. PWSTR psz, pszO;
  1449. PWSTR pszOut = NULL;
  1450. DWORD cb;
  1451. if (!pszIn || !pszSpecialChars) {
  1452. SetLastError(ERROR_INVALID_NAME);
  1453. return NULL;
  1454. }
  1455. // Count special characters
  1456. for (cb = 0, psz = (PWSTR) pszIn ; psz = wcspbrk(psz, pszSpecialChars) ; ++cb, ++psz)
  1457. ;
  1458. // Add in length of input string
  1459. cb = (wcslen(pszIn) + cb + 1)*sizeof *pszIn;
  1460. // Allocate output buffer and precede special DS chars with '\'
  1461. if (pszOut = (PWSTR) AllocSplMem(cb)) {
  1462. for(psz = (PWSTR) pszIn, pszO = pszOut ; *psz ; ++psz, ++pszO) {
  1463. if (wcschr(pszSpecialChars, *psz)) {
  1464. *pszO++ = L'\\';
  1465. }
  1466. *pszO = *psz;
  1467. }
  1468. *pszO = L'\0';
  1469. }
  1470. return pszOut;
  1471. }
  1472. PWSTR
  1473. DevCapStrings2MultiSz(
  1474. PWSTR pszDevCapString,
  1475. DWORD nDevCapStrings,
  1476. DWORD dwDevCapStringLength,
  1477. DWORD *pcbBytes
  1478. )
  1479. {
  1480. DWORD i, cbBytes, cbSize;
  1481. PWSTR pszMultiSz = NULL;
  1482. PWSTR pStr;
  1483. if (!pszDevCapString || !pcbBytes)
  1484. return NULL;
  1485. *pcbBytes = 0;
  1486. //
  1487. // Devcap buffers may not be NULL terminated
  1488. //
  1489. cbBytes = (nDevCapStrings*(dwDevCapStringLength + 1) + 1)*sizeof(WCHAR);
  1490. //
  1491. // Allocate and copy
  1492. //
  1493. if (pszMultiSz = (PWSTR) AllocSplMem(cbBytes)) {
  1494. for(i = 0, pStr = pszMultiSz, cbBytes = 0 ; i < nDevCapStrings ; ++i, pStr += cbSize, cbBytes +=cbSize ) {
  1495. wcsncpy(pStr, pszDevCapString + i*dwDevCapStringLength, dwDevCapStringLength);
  1496. cbSize = *pStr ? wcslen(pStr) + 1 : 0;
  1497. }
  1498. *pStr = L'\0';
  1499. *pcbBytes = (cbBytes + 1) * sizeof(WCHAR);
  1500. }
  1501. return pszMultiSz;
  1502. }
  1503. DWORD
  1504. Bind2DS(
  1505. HANDLE *phDS,
  1506. DOMAIN_CONTROLLER_INFO **ppDCI,
  1507. ULONG Flags
  1508. )
  1509. {
  1510. DWORD dwRet;
  1511. dwRet = DsGetDcName(NULL, NULL, NULL, NULL, Flags, ppDCI);
  1512. if (dwRet == ERROR_SUCCESS) {
  1513. if ((*ppDCI)->Flags & DS_DS_FLAG) {
  1514. dwRet = DsBind (NULL, (*ppDCI)->DomainName, phDS);
  1515. if (dwRet != ERROR_SUCCESS) {
  1516. NetApiBufferFree(*ppDCI);
  1517. *ppDCI = NULL;
  1518. if (!(Flags & DS_FORCE_REDISCOVERY)) {
  1519. dwRet = Bind2DS(phDS, ppDCI, DS_FORCE_REDISCOVERY | Flags);
  1520. }
  1521. }
  1522. } else {
  1523. NetApiBufferFree(*ppDCI);
  1524. *ppDCI = NULL;
  1525. dwRet = ERROR_CANT_ACCESS_DOMAIN_INFO;
  1526. }
  1527. }
  1528. return dwRet;
  1529. }
  1530. DWORD
  1531. DsCrackNamesStatus2Win32Error(
  1532. DWORD dwStatus
  1533. )
  1534. {
  1535. switch (dwStatus) {
  1536. case DS_NAME_ERROR_RESOLVING:
  1537. return ERROR_DS_NAME_ERROR_RESOLVING;
  1538. case DS_NAME_ERROR_NOT_FOUND:
  1539. return ERROR_DS_NAME_ERROR_NOT_FOUND;
  1540. case DS_NAME_ERROR_NOT_UNIQUE:
  1541. return ERROR_DS_NAME_ERROR_NOT_UNIQUE;
  1542. case DS_NAME_ERROR_NO_MAPPING:
  1543. return ERROR_DS_NAME_ERROR_NO_MAPPING;
  1544. case DS_NAME_ERROR_DOMAIN_ONLY:
  1545. return ERROR_DS_NAME_ERROR_DOMAIN_ONLY;
  1546. case DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING:
  1547. return ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING;
  1548. }
  1549. return ERROR_FILE_NOT_FOUND;
  1550. }
  1551. DWORD
  1552. GetDSSleepInterval (
  1553. HANDLE h
  1554. )
  1555. {
  1556. DWORD dwVerifyPublishedStateInterval;
  1557. PDSUPDATEDATA pData = (PDSUPDATEDATA)h;
  1558. DWORD dwTimeToSleep = 30 * ONE_MINUTE;
  1559. //
  1560. // 30 min is the minimum interval that can be set with the policy editor.
  1561. // If someone enables the policy while we are sleeping, then we need to wake up
  1562. // to pick the settings. This doesn't apply if the DS doesn't respond.
  1563. //
  1564. if (pData && pData->bSleep) {
  1565. //
  1566. // If the updating is failing, Data.bSleep is set to TRUE.
  1567. // This happens when the DS is down.
  1568. //
  1569. dwTimeToSleep = pData->dwSleepTime;
  1570. //
  1571. // Sleep interval is doubled to a maximum of 2 hours.
  1572. // We still want to attempt publishing every 2 hours. We also attempt if
  1573. // a "publish" action is taken(the even will be signaled).
  1574. //
  1575. //
  1576. pData->dwSleepTime = pData->dwSleepTime * 2 > 2 * ONE_HOUR ?
  1577. 2 * ONE_HOUR :
  1578. pData->dwSleepTime * 2;
  1579. } else {
  1580. dwTimeToSleep = VerifyPublishedStatePolicy();
  1581. if (dwTimeToSleep != INFINITE)
  1582. {
  1583. dwTimeToSleep *= ONE_MINUTE;
  1584. }
  1585. }
  1586. return dwTimeToSleep;
  1587. }