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.
 
 
 
 
 
 

2036 lines
52 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
All rights reserved
Abstract:
This module provides functionality for ADs within spooler
Author:
Steve Wilson (NT) Nov 1997
Revision History:
--*/
#include <precomp.h>
#pragma hdrstop
#define LOG_EVENT_ERROR_BUFFER_SIZE 11
#define MAX_CN 63 // DS limits common names to 63 non-null chars
#define UNIQUE_NUMBER_SIZE 10
#define MIN_CN (UNIQUE_NUMBER_SIZE + 3 + 1) // CN= + room for unique number if needed, plus NULL
extern BOOL gdwLogDsEvents;
extern "C" HANDLE ghDsUpdateThread;
extern "C" DWORD gdwDsUpdateThreadId;
// Policy values
WCHAR *szPublishPrinters = L"PublishPrinters";
WCHAR *szPruneDownlevel = L"PruneDownlevel";
WCHAR *szPruningInterval = L"PruningInterval";
WCHAR *szPruningRetries = L"PruningRetries";
WCHAR *szPruningPriority = L"PruningPriority";
WCHAR *szVerifyPublishedState = L"VerifyPublishedState";
WCHAR *szImmortal = L"Immortal";
WCHAR *szServerThreadPolicy = L"ServerThread";
WCHAR *szPruningRetryLog = L"PruningRetryLog";
extern "C" BOOL (*pfnOpenPrinter)(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS);
extern "C" BOOL (*pfnClosePrinter)(HANDLE);
extern "C" LONG (*pfnDocumentProperties)(HWND, HANDLE, LPWSTR, PDEVMODE, PDEVMODE, DWORD);
DWORD dwLastPruningPriority = DEFAULT_PRUNING_PRIORITY;
HRESULT
GetPrintQueue(
HANDLE hPrinter,
IADs **ppADs
)
/*++
Function Description:
This function returns the ADs PrintQueue object of the supplied printer handle.
Parameters:
hPrinter - printer handle
ppADs - return pointer to the PrintQueue object. Caller frees via ppADs->Release().
Return Values:
HRESULT
--*/
{
HRESULT hr;
PSPOOL pSpool = (PSPOOL) hPrinter;
PINIPRINTER pIniPrinter = pSpool->pIniPrinter;
IDispatch *pDispatch = NULL;
IADsContainer *pADsContainer = NULL;
WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
// Create the Print-Queue object
// Get the container
hr = GetPrintQueueContainer(hPrinter, &pADsContainer, ppADs);
BAIL_ON_FAILURE(hr);
if (!*ppADs) { // PrintQueue object does not exist, so create it
hr = pADsContainer->Create( SPLDS_PRINTER_CLASS,
pIniPrinter->pszCN,
&pDispatch);
if (FAILED(hr)) {
wsprintf(ErrorBuffer, L"%0x", hr);
SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
gdwLogDsEvents & LOG_ERROR,
MSG_CANT_CREATE_PRINTQUEUE,
FALSE,
pIniPrinter->pszDN,
ErrorBuffer,
NULL );
DBGMSG(DBG_WARNING,("Can't Create PrintQueue: %ws, %ws\n", pIniPrinter->pszDN, ErrorBuffer));
BAIL_ON_FAILURE(hr);
}
hr = pDispatch->QueryInterface(IID_IADs, (void **) ppADs);
BAIL_ON_FAILURE(hr);
}
error:
if (pADsContainer)
pADsContainer->Release();
if (pDispatch)
pDispatch->Release();
return hr;
}
HRESULT
GetPublishPoint(
HANDLE hPrinter
)
/*++
Function Description:
This function gets the publish point by setting pIniPrinter->pszDN and pIniPrinter->pszCN
Parameters:
hPrinter - printer handle
Return Values:
HRESULT
--*/
{
HRESULT hr;
PSPOOL pSpool = (PSPOOL) hPrinter;
PINIPRINTER pIniPrinter = pSpool->pIniPrinter;
// We should only be here if we couldn't use the existing DN & CN, so free old ones
FreeSplStr(pIniPrinter->pszCN);
pIniPrinter->pszCN = NULL;
FreeSplStr(pIniPrinter->pszDN);
pIniPrinter->pszDN = NULL;
// If Published, update DN from GUID
if (pIniPrinter->pszObjectGUID) {
hr = GetPublishPointFromGUID( hPrinter,
pIniPrinter->pszObjectGUID,
&pIniPrinter->pszDN,
&pIniPrinter->pszCN,
TRUE);
//
// If the object is actually deleted, the error is ERROR_FILE_NOT_FOUND
// If the object is a tombstone, the error is ERROR_DS_NO_SUCH_OBJECT
//
if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND ||
HRESULT_CODE(hr) == ERROR_DS_NO_SUCH_OBJECT) {
if (ghDsUpdateThread && gdwDsUpdateThreadId == GetCurrentThreadId()) {
// We are in the background thread
pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
} else {
pIniPrinter->DsKeyUpdateForeground = DS_KEY_REPUBLISH;
}
}
BAIL_ON_FAILURE(hr);
} else {
// Generate default publish point & common name
hr = GetDefaultPublishPoint(hPrinter, &pIniPrinter->pszDN);
BAIL_ON_FAILURE(hr);
// Printer name might change, so make a copy here
EnterSplSem();
PWSTR pszPrinterName = AllocSplStr(pIniPrinter->pName);
LeaveSplSem();
if (pszPrinterName) {
hr = GetCommonName( hPrinter,
pIniPrinter->pIniSpooler->pMachineName,
pszPrinterName,
pIniPrinter->pszDN,
&pIniPrinter->pszCN);
FreeSplStr(pszPrinterName);
} else {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
}
BAIL_ON_FAILURE(hr);
}
error:
return hr;
}
HRESULT
GetPublishPointFromGUID(
HANDLE hPrinter,
PWSTR pszObjectGUID,
PWSTR *ppszDN,
PWSTR *ppszCN,
BOOL bGetDNAndCN
)
/*++
Function Description:
This function returns the publish point of a specified GUID
Parameters:
hPrinter - printer handle
ppszObjectGUID - objectGUID of object for which we want to find the publish point
ppszDN - ADsPath of container containing object. Caller frees via FreeSplMem().
ppszCN - Common Name of object. Caller frees via FreeSplMem().
bGetDNAndCN - if TRUE, DN of Container & CN of object are returned. If FALSE, DN is path to object
Return Values:
HRESULT
--*/
{
PSPOOL pSpool = (PSPOOL) hPrinter;
DWORD dwRet, nBytes, nChars;
BOOL bRet;
PWSTR pNames[2];
DS_NAME_RESULT *pDNR = NULL;
DOMAIN_CONTROLLER_INFO *pDCI = NULL;
HANDLE hDS = NULL;
PWSTR psz;
WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
HRESULT hr = S_OK;
dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
if (dwRet != ERROR_SUCCESS) {
wsprintf(ErrorBuffer, L"%0x", dwRet);
if (pSpool) {
SplLogEvent( pSpool->pIniSpooler,
gdwLogDsEvents & LOG_ERROR,
MSG_CANT_GET_DNS_DOMAIN_NAME,
FALSE,
ErrorBuffer,
NULL );
}
DBGMSG(DBG_WARNING,("Can't get DNS Domain Name: %ws\n", ErrorBuffer));
goto error;
}
// Get Publish Point
if (ppszDN) {
pNames[0] = pszObjectGUID;
pNames[1] = NULL;
if (!(DsCrackNames(
hDS,
DS_NAME_NO_FLAGS,
DS_UNKNOWN_NAME,
DS_FQDN_1779_NAME,
1,
&pNames[0],
&pDNR) == ERROR_SUCCESS)) {
dwRet = GetLastError();
wsprintf(ErrorBuffer, L"%0x", dwRet);
if (pSpool) {
SplLogEvent( pSpool->pIniSpooler,
gdwLogDsEvents & LOG_WARNING,
MSG_CANT_CRACK_GUID,
FALSE,
pDCI->DomainName,
ErrorBuffer,
NULL );
DBGMSG(DBG_WARNING,("Can't crack GUID: %ws, %ws\n", pDCI->DomainName, ErrorBuffer));
}
dwRet = ERROR_FILE_NOT_FOUND;
goto error;
}
if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
wsprintf(ErrorBuffer, L"%0x", dwRet);
if (pSpool) {
SplLogEvent( pSpool->pIniSpooler,
gdwLogDsEvents & LOG_WARNING,
MSG_CANT_CRACK_GUID,
FALSE,
pDCI->DomainName,
ErrorBuffer,
NULL );
}
DBGMSG(DBG_WARNING,("Can't crack GUID: %ws %ws\n", pDCI->DomainName, ErrorBuffer));
dwRet = ERROR_FILE_NOT_FOUND;
goto error;
}
if (bGetDNAndCN) {
// Separate DN into CN & PublishPoint
// pDNR has form: CN=CommonName,DN...
hr = FQDN2CNDN(pDCI->DomainControllerName + 2, pDNR->rItems[0].pName, ppszCN, ppszDN);
BAIL_ON_FAILURE(hr);
} else {
hr = BuildLDAPPath(pDCI->DomainControllerName + 2, pDNR->rItems[0].pName, ppszDN);
BAIL_ON_FAILURE(hr);
}
}
error:
if (pDNR)
DsFreeNameResult(pDNR);
if (hDS)
DsUnBind(&hDS);
if (pDCI)
NetApiBufferFree(pDCI);
if (dwRet != ERROR_SUCCESS) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
}
if (FAILED(hr)) {
if (ppszCN) {
FreeSplMem(*ppszCN);
*ppszCN = NULL;
}
if (ppszDN) {
FreeSplMem(*ppszDN);
*ppszDN = NULL;
}
}
return hr;
}
HRESULT
FQDN2CNDN(
PWSTR pszDCName,
PWSTR pszFQDN,
PWSTR *ppszCN,
PWSTR *ppszDN
)
{
IADs *pADs = NULL;
PWSTR pszCN = NULL;
PWSTR pszDN = NULL;
PWSTR pszLDAPPath = NULL;
HRESULT hr;
// Get LDAP path to object
hr = BuildLDAPPath(pszDCName, pszFQDN, &pszLDAPPath);
BAIL_ON_FAILURE(hr);
// Get DN
hr = ADsGetObject(pszLDAPPath, IID_IADs, (void **) &pADs);
BAIL_ON_FAILURE(hr);
hr = pADs->get_Parent(&pszDN);
BAIL_ON_FAILURE(hr);
if (!(*ppszDN = AllocSplStr(pszDN))) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
BAIL_ON_FAILURE(hr);
}
// Get CN
hr = pADs->get_Name(&pszCN);
BAIL_ON_FAILURE(hr);
if (!(*ppszCN = AllocSplStr(pszCN))) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
BAIL_ON_FAILURE(hr);
}
error:
if (pADs)
pADs->Release();
if (pszCN)
SysFreeString(pszCN);
if (pszDN)
SysFreeString(pszDN);
FreeSplStr(pszLDAPPath);
if (FAILED(hr)) {
FreeSplStr(*ppszCN);
FreeSplStr(*ppszDN);
}
return hr;
}
HRESULT
BuildLDAPPath(
PWSTR pszDC,
PWSTR pszFQDN,
PWSTR *ppszLDAPPath
)
{
PWSTR pszEscapedFQDN = NULL;
DWORD nBytes;
HRESULT hr = S_OK;
//
// pszFQDN is assumed to contain escaped DN_SPECIAL_CHARS characters, but
// ADSI has additional special characters that need to be escaped before using
// the LDAP path in ADSI calls.
//
pszEscapedFQDN = CreateEscapedString(pszFQDN, ADSI_SPECIAL_CHARS);
if (!pszEscapedFQDN) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
BAIL_ON_FAILURE(hr);
}
// LDAP:// + pDCName + / + pName + 1
nBytes = (wcslen(pszDC) + wcslen(pszEscapedFQDN) + 9)*sizeof(WCHAR);
if (!(*ppszLDAPPath = (PWSTR) AllocSplMem(nBytes))) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
BAIL_ON_FAILURE(hr);
}
wsprintf(*ppszLDAPPath, L"LDAP://%ws/%ws", pszDC, pszEscapedFQDN);
error:
FreeSplStr(pszEscapedFQDN);
return hr;
}
HRESULT
GetPrintQueueContainer(
HANDLE hPrinter,
IADsContainer **ppADsContainer,
IADs **ppADs
)
/*++
Function Description:
This function returns the container and, if it exists, the PrintQueue object pointer
corresponding to the supplied printer handle
Parameters:
hPrinter - printer handle
ppADsContainer - return Container ADsPath. Caller frees via ppADsContainer->Release().
ppADs - return PrintQueue object ADsPath. Caller frees via ppADs->Release().
Return Values:
If successful, returns the printqueue container and, if found, the printqueue dispatch.
If there is no printqueue, the dispatch is set to NULL and the default container is returned.
--*/
{
HRESULT hr;
PSPOOL pSpool = (PSPOOL) hPrinter;
PINIPRINTER pIniPrinter = pSpool->pIniPrinter;
WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
DWORD nBytes;
PWSTR pszObjectGUID = NULL;
IDispatch *pPrintQDispatch = NULL;
LPWSTR pszPrinterObjectGUID = pIniPrinter->pszObjectGUID;
LPWSTR pszPrinterDN = pIniPrinter->pszDN;
LPWSTR pszPrinterCN = pIniPrinter->pszCN;
*ppADsContainer = NULL;
*ppADs = NULL;
//
// Try quick search for object using known properties.
//
// We are outside Spooler CS and pIniPrinter->pszObjectGUID,
// pIniPrinter->pszDN, pIniPrinter->pszCN might get changed by
// the DS background thread. In the case they are set to NULL,
// we'll take an AV. So, even if they change or are set to NULL,
// we'll use whatever we have when we entered the function.
// The worst that can happen is to find a DS printQueue
// object that doesn't last till we use it, which is fine. This
// is already assumed. Or, to not find a DS printQueue that will
// be created just after we query the DS. This is also fine.
//
if (pszPrinterObjectGUID &&
pszPrinterDN &&
pszPrinterCN) {
// Try to get the container using existing DN
hr = ADsGetObject( pszPrinterDN,
IID_IADsContainer,
(void **) ppADsContainer
);
if (SUCCEEDED(hr)) {
// Verify that printqueue exists in this container
hr = (*ppADsContainer)->GetObject( SPLDS_PRINTER_CLASS,
pszPrinterCN,
&pPrintQDispatch);
// Verify that the found printQueue object has the same GUID
if (SUCCEEDED(hr) && pPrintQDispatch) {
hr = pPrintQDispatch->QueryInterface(IID_IADs, (void **) ppADs);
BAIL_ON_FAILURE(hr);
hr = GetGUID(*ppADs, &pszObjectGUID);
BAIL_ON_FAILURE(hr);
if (wcscmp(pszObjectGUID, pszPrinterObjectGUID))
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
} else {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
}
}
} else {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
}
// Couldn't find container or printQueue, so find by GUID or get default container
if (FAILED(hr)) {
// The following items may have been allocated above and need to be freed
// here since we're going to reallocate them
if (pPrintQDispatch) {
pPrintQDispatch->Release();
pPrintQDispatch = NULL;
}
if (*ppADsContainer) {
(*ppADsContainer)->Release();
*ppADsContainer = NULL;
}
if (*ppADs) {
(*ppADs)->Release();
*ppADs = NULL;
}
// find or create pszDN and pszCN
hr = GetPublishPoint(hPrinter);
BAIL_ON_FAILURE(hr);
SPLASSERT(pIniPrinter->pszDN);
hr = ADsGetObject( pIniPrinter->pszDN,
IID_IADsContainer,
(void **) ppADsContainer
);
if (FAILED(hr)) {
wsprintf(ErrorBuffer, L"%0x", hr);
DBGMSG(DBG_WARNING,("Can't get Container: %ws, %ws\n", pIniPrinter->pszDN, ErrorBuffer));
SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
gdwLogDsEvents & LOG_ERROR,
MSG_CANT_GET_CONTAINER,
FALSE,
pIniPrinter->pszDN,
ErrorBuffer,
NULL );
BAIL_ON_FAILURE(hr);
}
// Try to get the printqueue, but don't error out if we can't
(*ppADsContainer)->GetObject( SPLDS_PRINTER_CLASS,
pIniPrinter->pszCN,
&pPrintQDispatch);
if (pPrintQDispatch)
pPrintQDispatch->QueryInterface(IID_IADs, (void **) ppADs);
}
error:
if (pPrintQDispatch)
pPrintQDispatch->Release();
FreeSplStr(pszObjectGUID);
return hr;
}
HRESULT
GetCommonName(
HANDLE hPrinter,
PWSTR pszServerName,
PWSTR pszPrinterName,
PWSTR pszDN,
PWSTR *ppszCommonName
)
/*++
Function Description:
This function returns a standard format Common Name of the PrintQueue object
generated from the supplied Server and Printer names
Parameters:
hPrinter - printer handle
pszServerName - name of a server
pszPrinterName - name of a printer on the server
pszDN - container DN
ppszCommonName - return CommonName. Caller frees via FreeSplMem().
Return Values:
HRESULT
--*/
{
DWORD nBytes;
PWSTR psz;
PWSTR pszPrinterNameStart = pszPrinterName;
// "CN=Server-Printer"
nBytes = (wcslen(pszPrinterName) + wcslen(pszServerName) + 5)*sizeof(WCHAR);
// We need to also make room for a unique number if there is a conflict
if (nBytes < MIN_CN)
nBytes = MIN_CN;
if (!(*ppszCommonName = psz = (PWSTR) AllocSplMem(nBytes))) {
return MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
}
// CN=
wcscpy(psz, L"CN=");
// Server
for(psz += 3, pszServerName += 2 ; *pszServerName ; ++psz, ++pszServerName) {
*psz = wcschr(DN_SPECIAL_CHARS, *pszServerName) ? TEXT('_') : *pszServerName;
}
*psz = L'-';
// Printer
for(++psz; *pszPrinterName ; ++psz, ++pszPrinterName) {
*psz = wcschr(DN_SPECIAL_CHARS, *pszPrinterName) ? TEXT('_') : *pszPrinterName;
}
// NULL
*psz = *pszPrinterName;
// DS only allows 64 characters in CN attribute, so shorten this if needed
if (wcslen(*ppszCommonName) > MAX_CN - 1) {
(*ppszCommonName)[MAX_CN] = NULL;
}
// Generate a non-conflicting name in this container
GetUniqueCN(pszDN, ppszCommonName, pszPrinterNameStart);
return S_OK;
}
VOID
GetUniqueCN(
PWSTR pszDN,
PWSTR *ppszCommonName,
PWSTR pszPrinterName
)
{
// Check if an object with this CN already exists in this container
// Get Container
IADsContainer *pADsContainer = NULL;
UINT32 CN; // This is a "random" number we append to the common name to make it unique
CN = (UINT32) (ULONG_PTR) &CN; // Initialize with some random number
HRESULT hr = ADsGetObject(pszDN, IID_IADsContainer, (void **) &pADsContainer);
if (SUCCEEDED(hr)) {
BOOL bTryAgain;
do {
IDispatch *pPrintQDispatch = NULL;
bTryAgain = FALSE;
// Get PrintQueue, if it exists
pADsContainer->GetObject( SPLDS_PRINTER_CLASS,
*ppszCommonName,
&pPrintQDispatch);
if (pPrintQDispatch) {
IADs *pADs = NULL;
hr = pPrintQDispatch->QueryInterface(IID_IADs, (void **) &pADs);
if (SUCCEEDED(hr)) {
// Generate a unique Common Name.
UINT32 cchCommonName = wcslen(*ppszCommonName);
PWSTR pszDigits;
if (cchCommonName >= MIN_CN) {
pszDigits = *ppszCommonName + cchCommonName - UNIQUE_NUMBER_SIZE;
} else {
pszDigits = *ppszCommonName + 3; // CN=
}
bTryAgain = TRUE;
wsprintf(pszDigits, L"%010d", ++CN);
pADs->Release();
}
pPrintQDispatch->Release();
}
} while(bTryAgain);
pADsContainer->Release();
}
}
HRESULT
GetDefaultPublishPoint(
HANDLE hPrinter,
PWSTR *ppszDN
)
/*++
Function Description:
This function returns the default publish point (Container) of a PrintQueue
based on the supplied printer handle.
Parameters:
hPrinter - printer handle
ppszDN - return default PrintQueue container. Caller frees via FreeSplMem()
Return Values:
HRESULT
--*/
{
WCHAR szServerName[MAX_COMPUTERNAME_LENGTH + 1];
WCHAR szName[MAX_PATH + 1];
DWORD nSize;
HANDLE hDS = NULL;
DWORD dwRet = ERROR_SUCCESS;
BOOL bRet;
PWSTR pNames[2];
DS_NAME_RESULT *pDNR = NULL;
DOMAIN_CONTROLLER_INFO *pDCI = NULL;
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE];
HRESULT hr = S_OK;
// Get Computer name
nSize = MAX_COMPUTERNAME_LENGTH + 1;
if (!(bRet = GetComputerName(szServerName, &nSize))) {
dwRet = GetLastError();
goto error;
}
// Get Domain name
dwRet = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole);
if (dwRet) {
wsprintf(ErrorBuffer, L"%0x", dwRet);
DBGMSG(DBG_WARNING,("Can't get Primary Domain Info: %ws\n", ErrorBuffer));
SplLogEvent(((PSPOOL) hPrinter)->pIniSpooler,
gdwLogDsEvents & LOG_ERROR,
MSG_CANT_GET_PRIMARY_DOMAIN_INFO,
FALSE,
ErrorBuffer,
NULL );
goto error;
}
// Domain\Server
wsprintf(szName, L"%ws\\%ws$", pDsRole->DomainNameFlat, szServerName);
pNames[0] = szName;
pNames[1] = NULL;
dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
if (dwRet != ERROR_SUCCESS) {
wsprintf(ErrorBuffer, L"%0x", dwRet);
DBGMSG(DBG_WARNING,("Can't get DNS Domain Name: %ws\n", ErrorBuffer));
SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
gdwLogDsEvents & LOG_ERROR,
MSG_CANT_GET_DNS_DOMAIN_NAME,
FALSE,
ErrorBuffer,
NULL );
goto error;
}
// Get Publish Point
if (ppszDN) {
if (!(DsCrackNames(
hDS,
DS_NAME_NO_FLAGS,
DS_UNKNOWN_NAME,
DS_FQDN_1779_NAME,
1,
&pNames[0],
&pDNR) == ERROR_SUCCESS)) {
dwRet = GetLastError();
wsprintf(ErrorBuffer, L"%0x", dwRet);
DBGMSG(DBG_WARNING,("Can't Crack Name: %ws, %ws\n", pDCI->DomainName, ErrorBuffer));
SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
gdwLogDsEvents & LOG_ERROR,
MSG_CANT_CRACK,
FALSE,
pDCI->DomainName,
ErrorBuffer,
NULL );
goto error;
}
if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
wsprintf(ErrorBuffer, L"%0x", dwRet);
DBGMSG(DBG_WARNING,("Can't Crack Name: %ws, %ws\n", pDCI->DomainName, ErrorBuffer));
SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler,
gdwLogDsEvents & LOG_ERROR,
MSG_CANT_CRACK,
FALSE,
pDCI->DomainName,
ErrorBuffer,
NULL );
goto error;
}
// LDAP:// + pDCName + / + pName + 1
hr = BuildLDAPPath(pDCI->DomainControllerName + 2, pDNR->rItems[0].pName, ppszDN);
BAIL_ON_FAILURE(hr);
}
error:
if (pDNR)
DsFreeNameResult(pDNR);
if (hDS)
DsUnBind(&hDS);
if (pDCI)
NetApiBufferFree(pDCI);
if (pDsRole)
DsRoleFreeMemory((PVOID) pDsRole);
// If dwRet has no facility, then make into HRESULT
if (dwRet != ERROR_SUCCESS) {
if (HRESULT_CODE(dwRet) == dwRet) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
} else {
hr = dwRet;
}
}
// Finally, make absolutely sure we are failing if *ppszDN is NULL
if (hr == S_OK && (!ppszDN || !*ppszDN)) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND);
}
return hr;
}
// Utility routine to report if a printer is color or monochrome
BOOL
ThisIsAColorPrinter(
LPCTSTR lpstrName
)
/*++
Function Description:
This function determines whether or not a printer supports color printing
Parameters:
lpstrName - Printer name
Return Values:
If printer supports color, return is TRUE. Otherwise, return value is FALSE
--*/
{
HANDLE hPrinter = NULL;
LPTSTR lpstrMe = const_cast <LPTSTR> (lpstrName);
BOOL bReturn = FALSE;
LPDEVMODE lpdm = NULL;
long lcbNeeded;
if (!(*pfnOpenPrinter)(lpstrMe, &hPrinter, NULL)) {
DBGMSG(DBG_WARNING, ("Unable to open printer '%ws'- error %d\n", lpstrName,
GetLastError()));
goto error;
}
// First, use DocumentProperties to find the correct DEVMODE size- we
// must use the DEVMODE to force color on, in case the user's defaults
// have turned it off...
lcbNeeded = (*pfnDocumentProperties)(NULL, hPrinter, lpstrMe, NULL, NULL, 0);
if (lcbNeeded <= 0) {
DBGMSG( DBG_WARNING,
("Document Properties (get size) for '%ws' returned %d\n",
lpstrName,lcbNeeded));
goto error;
}
lpdm = (LPDEVMODE) AllocSplMem(lcbNeeded);
if (lpdm) {
lpdm->dmSize = sizeof(DEVMODE);
lpdm->dmFields = DM_COLOR;
lpdm->dmColor = DMCOLOR_COLOR;
if (IDOK == (*pfnDocumentProperties)(NULL, hPrinter, lpstrMe, lpdm, lpdm, DM_IN_BUFFER | DM_OUT_BUFFER)) {
// Finally, we can create the DC!
HDC hdcThis = CreateDC(NULL, lpstrName, NULL, lpdm);
if (hdcThis) {
bReturn = 2 < (unsigned) GetDeviceCaps(hdcThis, NUMCOLORS);
DeleteDC(hdcThis);
}
}
else
DBGMSG(DBG_WARNING, ("DocumentProperties (retrieve) on '%s' failed- error %d\n",
lpstrName, GetLastError()));
}
else
DBGMSG(DBG_WARNING, ("ThisIsAColorPrinter(%s) failed to get %d bytes\n",
lpstrName, lcbNeeded));
error:
FreeSplMem(lpdm);
if (hPrinter)
(*pfnClosePrinter)(hPrinter);
return bReturn;
}
HRESULT
GetGUID(
IADs *pADs,
PWSTR *ppszObjectGUID
)
/*++
Function Description:
This function returns the ObjectGUID of a given ADs object.
ppszObjectGUID must be freed by caller using FreeSplStr().
Parameters:
pADs - input ADs object pointer
ppszObjectGUID - object GUID of pADs
Return Values:
HRESULT
--*/
{
HRESULT hr;
LPOLESTR pszObjectGUID = NULL;
IID ObjectIID;
hr = pADs->GetInfo();
if (SUCCEEDED(hr)) {
hr = get_UI1Array_Property(pADs, L"ObjectGUID", &ObjectIID);
if (SUCCEEDED(hr)) {
hr = StringFromIID(ObjectIID, &pszObjectGUID);
if (SUCCEEDED(hr)) {
if (!(*ppszObjectGUID = AllocSplStr(pszObjectGUID)))
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
}
CoTaskMemFree(pszObjectGUID);
}
}
return hr;
}
BOOL
PrinterPublishProhibited()
{
return !GetDwPolicy(szPublishPrinters, DEFAULT_PRINT_PUBLISH_POLICY);
}
DWORD
VerifyPublishedStatePolicy()
{
return GetDwPolicy(szVerifyPublishedState, DEFAULT_VERIFY_PUBLISHED_STATE);
}
DWORD
PruneDownlevel()
{
return GetDwPolicy(szPruneDownlevel, DEFAULT_PRUNE_DOWNLEVEL);
}
DWORD
PruningInterval(
)
{
return GetDwPolicy(szPruningInterval, DEFAULT_PRUNING_INTERVAL);
}
DWORD
ImmortalPolicy(
)
{
return GetDwPolicy(szImmortal, DEFAULT_IMMORTAL);
}
VOID
ServerThreadPolicy(
BOOL bHaveDs
)
{
DWORD dwPolicy;
dwPolicy = GetDwPolicy(szServerThreadPolicy, SERVER_THREAD_UNCONFIGURED);
if (dwPolicy == SERVER_THREAD_UNCONFIGURED) {
ServerThreadRunning = !(bHaveDs ? SERVER_THREAD_OFF : SERVER_THREAD_ON);
} else {
ServerThreadRunning = !dwPolicy;
}
CreateServerThread();
}
DWORD
PruningRetries(
)
{
DWORD dwPruningRetries;
dwPruningRetries = GetDwPolicy(szPruningRetries, DEFAULT_PRUNING_RETRIES);
if (dwPruningRetries > MAX_PRUNING_RETRIES)
dwPruningRetries = MAX_PRUNING_RETRIES;
return dwPruningRetries;
}
DWORD
PruningRetryLog(
)
{
DWORD dwPruningRetryLog;
dwPruningRetryLog = GetDwPolicy(szPruningRetryLog, DEFAULT_PRUNING_RETRY_LOG);
return dwPruningRetryLog;
}
VOID
SetPruningPriority(
)
{
DWORD dwPriority = GetDwPolicy(szPruningPriority, DEFAULT_PRUNING_PRIORITY);
if (dwPriority != dwLastPruningPriority) {
if (dwPriority == THREAD_PRIORITY_LOWEST ||
dwPriority == THREAD_PRIORITY_BELOW_NORMAL ||
dwPriority == THREAD_PRIORITY_NORMAL ||
dwPriority == THREAD_PRIORITY_ABOVE_NORMAL ||
dwPriority == THREAD_PRIORITY_HIGHEST) {
SetThreadPriority(GetCurrentThread(), DEFAULT_PRUNING_PRIORITY);
} else {
SetThreadPriority(GetCurrentThread(), dwPriority);
}
dwLastPruningPriority = dwPriority;
}
}
BOOL
ThisMachineIsADC(
)
{
NT_PRODUCT_TYPE NtProductType;
RtlGetNtProductType(&NtProductType);
return NtProductType == NtProductLanManNt;
}
DWORD
GetDomainRoot(
PWSTR *ppszDomainRoot
)
/*++
Function Description:
This function returns the ADsPath of the root of the current domain
Parameters:
ppszDomainRoot - pointer to buffer receiving string pointer of domain root ADsPath string
free ppszDomainRoot with a call to FreeSplMem
Return Values:
DWORD
--*/
{
DWORD dwRet = ERROR_SUCCESS;
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
DS_NAME_RESULT *pDNR = NULL;
DOMAIN_CONTROLLER_INFO *pDCI = NULL;
HANDLE hDS = NULL;
WCHAR szName[MAX_PATH + 1];
PWSTR pNames[2];
PWSTR pszDomainRoot;
DWORD cb;
if (!ppszDomainRoot) {
dwRet = ERROR_INVALID_PARAMETER;
goto error;
}
// Get Domain name
dwRet = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole);
if (dwRet)
goto error;
wsprintf(szName, L"%ws\\", pDsRole->DomainNameFlat);
pNames[0] = szName;
pNames[1] = NULL;
dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
if (dwRet != ERROR_SUCCESS) {
goto error;
}
if (!(DsCrackNames(
hDS,
DS_NAME_NO_FLAGS,
DS_UNKNOWN_NAME,
DS_FQDN_1779_NAME,
1,
&pNames[0],
&pDNR) == ERROR_SUCCESS)) {
dwRet = GetLastError();
goto error;
}
if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
goto error;
}
// LDAP:// + pDCName + 1
cb = (wcslen(pDNR->rItems[0].pName) + 8)*sizeof(WCHAR);
if (!(*ppszDomainRoot = (PWSTR) AllocSplMem(cb))) {
dwRet = GetLastError();
goto error;
}
wsprintf(*ppszDomainRoot, L"LDAP://%ws", pDNR->rItems[0].pName);
error:
if (pDNR)
DsFreeNameResult(pDNR);
if (hDS)
DsUnBind(&hDS);
if (pDCI)
NetApiBufferFree(pDCI);
if (pDsRole)
DsRoleFreeMemory((PVOID) pDsRole);
return dwRet;
}
PWSTR
CreateSearchString(
PWSTR pszIn
)
{
PWSTR psz, pszSS;
PWSTR pszSearchString = NULL;
DWORD cb;
/* Replace \ with \5c */
/* Count chars & pad */
for (cb = 0, psz = pszIn ; *psz ; ++psz, ++cb) {
if (*psz == L'\\')
cb += 2;
}
cb = (cb + 1)*sizeof *psz;
if (pszSearchString = (PWSTR) GlobalAlloc(GMEM_FIXED, cb)) {
for(psz = pszIn, pszSS = pszSearchString ; *psz ; ++psz, ++pszSS) {
*pszSS = *psz;
if (*psz == L'\\') {
*++pszSS = L'5';
*++pszSS = L'c';
}
}
*pszSS = L'\0';
}
return pszSearchString;
}
BOOL
ServerOnSite(
PWSTR *ppszMySites,
ULONG cMySites,
PWSTR pszServer
)
/*++
Function Description:
This function returns TRUE if pszServer is on one of the ppszMySites and pszServer exists
Parameters:
ppszMySites - input sites
pszServer - input server name
Return Values:
BOOL - TRUE if server exists on site, FALSE otherwise or on error
--*/
{
PSOCKET_ADDRESS pSocketAddresses = NULL;
PWSTR *ppszSiteNames = NULL;
DWORD nAddresses;
DWORD dwRet, i;
ULONG j;
WORD wVersion;
WSADATA WSAData;
BOOL bServerOnSite = FALSE;
wVersion = MAKEWORD(1, 1);
if (WSAStartup(wVersion, &WSAData) == ERROR_SUCCESS) {
// Find out if Server is on Site
GetSocketAddressesFromMachineName(pszServer, &pSocketAddresses, &nAddresses);
if (nAddresses == 0) {
bServerOnSite = TRUE; // Claim server is on site if we can't find it
} else {
dwRet = DsAddressToSiteNames( (PCWSTR) NULL,
nAddresses,
pSocketAddresses,
&ppszSiteNames);
if (dwRet == NO_ERROR) {
for(i = 0 ; i < nAddresses ; ++i) {
for(j = 0 ; j < cMySites ; ++j) {
if (ppszSiteNames[i] && ppszMySites[j] && !wcscmp(ppszSiteNames[i], ppszMySites[j])) {
bServerOnSite = TRUE;
break;
}
}
}
}
}
if (ppszSiteNames)
NetApiBufferFree(ppszSiteNames);
if (pSocketAddresses)
FreeSplSockets(pSocketAddresses, nAddresses);
WSACleanup();
}
return bServerOnSite;
}
VOID
FreeSplSockets(
PSOCKET_ADDRESS pSocketAddress,
DWORD nAddresses
)
{
DWORD i;
PSOCKET_ADDRESS pSocket;
for(i = 0, pSocket = pSocketAddress ; i < nAddresses ; ++i, ++pSocket)
FreeSplMem(pSocket->lpSockaddr);
FreeSplMem(pSocketAddress);
}
VOID
AllocSplSockets(
struct hostent *pHostEnt,
PSOCKET_ADDRESS *ppSocketAddress,
DWORD *nSocketAddresses
)
{
DWORD i;
PSOCKET_ADDRESS pSocket;
for ( *nSocketAddresses = 0 ; pHostEnt->h_addr_list[*nSocketAddresses] ; ++(*nSocketAddresses))
;
// Allocate SOCKET_ADDRESS array
*ppSocketAddress = (PSOCKET_ADDRESS) AllocSplMem(*nSocketAddresses*sizeof(SOCKET_ADDRESS));
if (!*ppSocketAddress)
*nSocketAddresses = 0;
// Allocate Sockaddr element for each SOCKET_ADDRESS
// If we fail partway through, just use partial list
for (i = 0, pSocket = *ppSocketAddress ; i < *nSocketAddresses ; ++i, ++pSocket) {
if (!(pSocket->lpSockaddr = (struct sockaddr *) AllocSplMem(sizeof(struct sockaddr_in)))) {
*nSocketAddresses = i;
break;
}
if (pHostEnt->h_addrtype == AF_INET) {
((struct sockaddr_in *) pSocket->lpSockaddr)->sin_family = AF_INET;
((struct sockaddr_in *) pSocket->lpSockaddr)->sin_addr = *(struct in_addr *) pHostEnt->h_addr_list[i];
pSocket->iSockaddrLength = sizeof(struct sockaddr_in);
} else {
DBGMSG(DBG_WARNING,("AllocSplSockets: addrtype != AF_INET: %d\n", pHostEnt->h_addrtype));
}
}
}
VOID
GetSocketAddressesFromMachineName(
PWSTR pszMachineName, // \\Machine
PSOCKET_ADDRESS *ppSocketAddress,
DWORD *nSocketAddresses
)
/*++
Routine Description:
This routine builds list of names other than the machine name that
can be used to call spooler APIs.
--*/
{
struct hostent *HostEnt;
PSTR pszAnsiMachineName = NULL;
DWORD iWsaError;
*nSocketAddresses = 0;
*ppSocketAddress = 0;
if (pszAnsiMachineName = (PSTR) AllocSplStr(pszMachineName + 2)) {
if (UnicodeToAnsiString((PWSTR) pszAnsiMachineName, pszAnsiMachineName, NULL_TERMINATED)) {
if (HostEnt = gethostbyname(pszAnsiMachineName)) {
AllocSplSockets(HostEnt, ppSocketAddress, nSocketAddresses);
} else {
iWsaError = WSAGetLastError();
DBGMSG(DBG_WARNING, ("gethostbyname failed in DsPrune: %d\n", iWsaError));
}
}
}
FreeSplMem(pszAnsiMachineName);
}
DWORD
UNC2Server(
PCWSTR pszUNC,
PWSTR *ppszServer
)
{
PWSTR psz;
DWORD cb;
DWORD nChars;
if (!pszUNC || pszUNC[0] != L'\\' || pszUNC[1] != L'\\')
return ERROR_INVALID_PARAMETER;
if(!(psz = wcschr(pszUNC + 2, L'\\')))
return ERROR_INVALID_PARAMETER;
cb = (DWORD) ((ULONG_PTR) psz - (ULONG_PTR) pszUNC + sizeof *psz);
if (!(*ppszServer = (PWSTR) AllocSplMem(cb)))
return GetLastError();
nChars = (DWORD) (psz - pszUNC);
wcsncpy(*ppszServer, pszUNC, nChars);
(*ppszServer)[nChars] = L'\0';
return ERROR_SUCCESS;
}
BOOL
ServerExists(
PWSTR pszServerName
)
{
NET_API_STATUS Status;
SERVER_INFO_100 *pServer;
BOOL bServerExists;
Status = NetServerGetInfo(pszServerName, 100, (PBYTE *) &pServer);
bServerExists = !Status;
Status = NetApiBufferFree(pServer);
return bServerExists;
}
HRESULT
UnpublishByGUID(
PINIPRINTER pIniPrinter
)
{
HRESULT hr;
SplOutSem();
if (!pIniPrinter->pszObjectGUID) {
pIniPrinter->DsKeyUpdate = 0;
pIniPrinter->DsKeyUpdateForeground = 0;
hr = S_OK;
} else {
PWSTR pszDN = NULL;
PWSTR pszCN = NULL;
hr = GetPublishPointFromGUID(NULL, pIniPrinter->pszObjectGUID, &pszDN, &pszCN, TRUE);
DBGMSG(DBG_EXEC,
("UnpublishByGUID: GUID %ws\n", pIniPrinter->pszObjectGUID));
if (SUCCEEDED(hr)) {
DBGMSG(DBG_EXEC,
("UnpublishByGUID: DN %ws CN %ws\n",
pszDN,
pszCN));
IADsContainer *pADsContainer = NULL;
// Get the container
hr = ADsGetObject( pszDN,
IID_IADsContainer,
(void **) &pADsContainer
);
if (SUCCEEDED(hr)) {
hr = pADsContainer->Delete(SPLDS_PRINTER_CLASS, pszCN);
pADsContainer->Release();
}
// If the container or the object is gone, succeed
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT) ||
HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND ||
HRESULT_CODE(hr) == ERROR_PATH_NOT_FOUND)
hr = S_OK;
}
FreeSplStr(pszDN);
FreeSplStr(pszCN);
}
// free GUID if object is deleted
if (SUCCEEDED(hr)) {
pIniPrinter->DsKeyUpdate = 0;
pIniPrinter->DsKeyUpdateForeground = 0;
FreeSplStr(pIniPrinter->pszObjectGUID);
pIniPrinter->pszObjectGUID = NULL;
FreeSplStr(pIniPrinter->pszCN);
pIniPrinter->pszCN = NULL;
FreeSplStr(pIniPrinter->pszDN);
pIniPrinter->pszDN = NULL;
EnterSplSem();
if (pIniPrinter->bDsPendingDeletion) {
pIniPrinter->bDsPendingDeletion = 0;
DECPRINTERREF(pIniPrinter);
}
LeaveSplSem();
DBGMSG(DBG_EXEC, ("UnpublishByGUID Succeeded\n"));
} else {
pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH;
DBGMSG(DBG_EXEC, ("UnpublishByGUID Failed\n"));
}
SplOutSem();
return hr;
}
HRESULT
GetDNSMachineName(
PWSTR pszShortMachineName,
PWSTR *ppszMachineName
)
{
struct hostent *pHostEnt;
DWORD dwRet = ERROR_SUCCESS;
HRESULT hr = S_OK;
PSTR pszAnsiMachineName = NULL;
WORD wVersion;
WSADATA WSAData;
wVersion = MAKEWORD(1, 1);
dwRet = WSAStartup(wVersion, &WSAData);
if (dwRet == ERROR_SUCCESS) {
if (!pszShortMachineName || !*pszShortMachineName) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INVALID_PARAMETER);
BAIL_ON_FAILURE(hr);
}
if (!(pszAnsiMachineName = (PSTR) AllocSplStr(pszShortMachineName))) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
BAIL_ON_FAILURE(hr);
}
if (!UnicodeToAnsiString((PWSTR) pszAnsiMachineName, pszAnsiMachineName, NULL_TERMINATED)) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
BAIL_ON_FAILURE(hr);
}
if (!(pHostEnt = gethostbyname(pszAnsiMachineName))) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, WSAGetLastError());
BAIL_ON_FAILURE(hr);
}
if (!(*ppszMachineName = AnsiToUnicodeStringWithAlloc(pHostEnt->h_name))) {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
BAIL_ON_FAILURE(hr);
}
} else {
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
}
error:
if (dwRet == ERROR_SUCCESS)
{
WSACleanup();
}
FreeSplMem(pszAnsiMachineName);
return hr;
}
HRESULT
GetClusterUser(
IADs **ppADs
)
{
HRESULT hr;
WCHAR szUserName[MAX_PATH + 8]; // Allow for LDAP://
PWSTR pszUserName = szUserName;
DWORD cchUserName = MAX_PATH + 1;
BOOL bRet;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
return hr;
// Get cluster container's name, which must be the current user name
wcscpy(pszUserName, L"LDAP://");
if (!GetUserNameEx(NameFullyQualifiedDN, pszUserName + 7, &cchUserName)) {
if (cchUserName > MAX_PATH + 1) {
pszUserName = (PWSTR) AllocSplMem((cchUserName + 7)*sizeof(WCHAR));
if (!pszUserName) {
hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
goto error;
}
wcscpy(pszUserName, L"LDAP://");
if (!GetUserNameEx(NameFullyQualifiedDN, pszUserName + 7, &cchUserName)) {
hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
goto error;
}
} else {
hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
goto error;
}
}
// Get the object
hr = ADsGetObject( pszUserName,
IID_IADs,
(void **) ppADs
);
BAIL_ON_FAILURE(hr);
error:
if (pszUserName != szUserName)
FreeSplStr(pszUserName);
CoUninitialize();
return hr;
}
HRESULT
FQDN2Whatever(
PWSTR pszIn,
PWSTR *ppszOut,
DS_NAME_FORMAT NameFormat
)
{
DWORD dwRet = ERROR_SUCCESS;
DS_NAME_RESULT *pDNR = NULL;
DOMAIN_CONTROLLER_INFO *pDCI = NULL;
HANDLE hDS = NULL;
PWSTR pNames[2];
PWSTR psz;
HRESULT hr = S_OK;
*ppszOut = NULL;
// Get the DC name
dwRet = Bind2DS(&hDS, &pDCI, DS_DIRECTORY_SERVICE_PREFERRED);
if (dwRet != ERROR_SUCCESS)
goto error;
// Translate the name
if (wcslen(pszIn) < 8) {
dwRet = ERROR_INVALID_PARAMETER;
goto error;
}
psz = wcschr(pszIn + 7, L'/');
if (!psz) {
dwRet = ERROR_INVALID_PARAMETER;
goto error;
}
pNames[0] = ++psz; // Input string is LDAP://ntdev.microsoft.com/CN=... Strip off the LDAP://.../ portion
pNames[1] = NULL;
if (!(DsCrackNames(
hDS,
DS_NAME_NO_FLAGS,
DS_FQDN_1779_NAME,
NameFormat,
1,
&pNames[0],
&pDNR) == ERROR_SUCCESS)) {
dwRet = GetLastError();
goto error;
}
if (pDNR->rItems[0].status != DS_NAME_NO_ERROR) {
dwRet = DsCrackNamesStatus2Win32Error(pDNR->rItems[0].status);
goto error;
}
*ppszOut = AllocSplStr(pDNR->rItems[0].pName);
error:
if (pDNR)
DsFreeNameResult(pDNR);
if (hDS)
DsUnBind(&hDS);
if (pDCI)
NetApiBufferFree(pDCI);
return dwRet == ERROR_SUCCESS ? S_OK : MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
}
DWORD
InitializeDSClusterInfo(
PINISPOOLER pIniSpooler,
HANDLE *phToken
)
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
IADs *pADs = NULL;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr)) {
return HRESULT_CODE(hr);
}
// Impersonate the client
if (!ImpersonatePrinterClient(*phToken)) {
dwError = GetLastError();
DBGMSG(DBG_WARNING,("InitializeDSClusterInfo FAILED: %d\n", dwError));
CoUninitialize();
return dwError;
}
// Get a copy of the client token
dwError = NtOpenThreadToken(NtCurrentThread(), TOKEN_IMPERSONATE, TRUE, &pIniSpooler->hClusterToken);
if (dwError != STATUS_SUCCESS) {
DBGMSG(DBG_WARNING,("InitializeDSClusterInfo DuplicateToken FAILED: %d\n", dwError));
pIniSpooler->hClusterToken = INVALID_HANDLE_VALUE;
goto error;
}
// Get the Cluster User Object
hr = GetClusterUser(&pADs);
// Get the GUID to the Cluster User Object
if (SUCCEEDED(hr))
hr = GetGUID(pADs, &pIniSpooler->pszClusterGUID);
error:
*phToken = RevertToPrinterSelf();
if (FAILED(hr)) {
dwError = HRESULT_CODE(hr);
FreeSplStr(pIniSpooler->pszClusterGUID);
pIniSpooler->pszClusterGUID = NULL;
}
if (pADs)
pADs->Release();
CoUninitialize();
return dwError;
}
BOOL
CheckPublishedPrinters(
)
{
PINIPRINTER pIniPrinter;
BOOL bHavePublishedPrinters = FALSE;
SplInSem();
if (VerifyPublishedStatePolicy() == INFINITE)
return FALSE;
RunForEachSpooler(&bHavePublishedPrinters, CheckPublishedSpooler);
return bHavePublishedPrinters;
}
BOOL
CheckPublishedSpooler(
HANDLE h,
PINISPOOLER pIniSpooler
)
{
PBOOL pbHavePublishedPrinters = (PBOOL)h;
PINIPRINTER pIniPrinter;
if (!(pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL))
return TRUE;
for (pIniPrinter = pIniSpooler->pIniPrinter ; pIniPrinter ; pIniPrinter = pIniPrinter->pNext) {
if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
// Refresh: verify that we're really published
// Note that if there isn't any new info to publish and we're published,
// we won't do any SetInfo so the overhead is minimal
pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH;
*pbHavePublishedPrinters = TRUE;
} else if (pIniPrinter->pszObjectGUID) {
// The only way we can get here is if someone is unpublishing the printer,
// so we don't really need to set the background DsKeyUpdate state. Doing
// so maintains symmetry with above statement and InitializeDS
pIniPrinter->DsKeyUpdate |= DS_KEY_UNPUBLISH;
*pbHavePublishedPrinters = TRUE;
}
}
return TRUE;
}
PWSTR
CreateEscapedString(
PCWSTR pszIn,
PCWSTR pszSpecialChars
)
{
PWSTR psz, pszO;
PWSTR pszOut = NULL;
DWORD cb;
if (!pszIn || !pszSpecialChars) {
SetLastError(ERROR_INVALID_NAME);
return NULL;
}
// Count special characters
for (cb = 0, psz = (PWSTR) pszIn ; psz = wcspbrk(psz, pszSpecialChars) ; ++cb, ++psz)
;
// Add in length of input string
cb = (wcslen(pszIn) + cb + 1)*sizeof *pszIn;
// Allocate output buffer and precede special DS chars with '\'
if (pszOut = (PWSTR) AllocSplMem(cb)) {
for(psz = (PWSTR) pszIn, pszO = pszOut ; *psz ; ++psz, ++pszO) {
if (wcschr(pszSpecialChars, *psz)) {
*pszO++ = L'\\';
}
*pszO = *psz;
}
*pszO = L'\0';
}
return pszOut;
}
PWSTR
DevCapStrings2MultiSz(
PWSTR pszDevCapString,
DWORD nDevCapStrings,
DWORD dwDevCapStringLength,
DWORD *pcbBytes
)
{
DWORD i, cbBytes, cbSize;
PWSTR pszMultiSz = NULL;
PWSTR pStr;
if (!pszDevCapString || !pcbBytes)
return NULL;
*pcbBytes = 0;
//
// Devcap buffers may not be NULL terminated
//
cbBytes = (nDevCapStrings*(dwDevCapStringLength + 1) + 1)*sizeof(WCHAR);
//
// Allocate and copy
//
if (pszMultiSz = (PWSTR) AllocSplMem(cbBytes)) {
for(i = 0, pStr = pszMultiSz, cbBytes = 0 ; i < nDevCapStrings ; ++i, pStr += cbSize, cbBytes +=cbSize ) {
wcsncpy(pStr, pszDevCapString + i*dwDevCapStringLength, dwDevCapStringLength);
cbSize = *pStr ? wcslen(pStr) + 1 : 0;
}
*pStr = L'\0';
*pcbBytes = (cbBytes + 1) * sizeof(WCHAR);
}
return pszMultiSz;
}
DWORD
Bind2DS(
HANDLE *phDS,
DOMAIN_CONTROLLER_INFO **ppDCI,
ULONG Flags
)
{
DWORD dwRet;
dwRet = DsGetDcName(NULL, NULL, NULL, NULL, Flags, ppDCI);
if (dwRet == ERROR_SUCCESS) {
if ((*ppDCI)->Flags & DS_DS_FLAG) {
dwRet = DsBind (NULL, (*ppDCI)->DomainName, phDS);
if (dwRet != ERROR_SUCCESS) {
NetApiBufferFree(*ppDCI);
*ppDCI = NULL;
if (!(Flags & DS_FORCE_REDISCOVERY)) {
dwRet = Bind2DS(phDS, ppDCI, DS_FORCE_REDISCOVERY | Flags);
}
}
} else {
NetApiBufferFree(*ppDCI);
*ppDCI = NULL;
dwRet = ERROR_CANT_ACCESS_DOMAIN_INFO;
}
}
return dwRet;
}
DWORD
DsCrackNamesStatus2Win32Error(
DWORD dwStatus
)
{
switch (dwStatus) {
case DS_NAME_ERROR_RESOLVING:
return ERROR_DS_NAME_ERROR_RESOLVING;
case DS_NAME_ERROR_NOT_FOUND:
return ERROR_DS_NAME_ERROR_NOT_FOUND;
case DS_NAME_ERROR_NOT_UNIQUE:
return ERROR_DS_NAME_ERROR_NOT_UNIQUE;
case DS_NAME_ERROR_NO_MAPPING:
return ERROR_DS_NAME_ERROR_NO_MAPPING;
case DS_NAME_ERROR_DOMAIN_ONLY:
return ERROR_DS_NAME_ERROR_DOMAIN_ONLY;
case DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING:
return ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING;
}
return ERROR_FILE_NOT_FOUND;
}
DWORD
GetDSSleepInterval (
HANDLE h
)
{
DWORD dwVerifyPublishedStateInterval;
PDSUPDATEDATA pData = (PDSUPDATEDATA)h;
DWORD dwTimeToSleep = 30 * ONE_MINUTE;
//
// 30 min is the minimum interval that can be set with the policy editor.
// If someone enables the policy while we are sleeping, then we need to wake up
// to pick the settings. This doesn't apply if the DS doesn't respond.
//
if (pData && pData->bSleep) {
//
// If the updating is failing, Data.bSleep is set to TRUE.
// This happens when the DS is down.
//
dwTimeToSleep = pData->dwSleepTime;
//
// Sleep interval is doubled to a maximum of 2 hours.
// We still want to attempt publishing every 2 hours. We also attempt if
// a "publish" action is taken(the even will be signaled).
//
//
pData->dwSleepTime = pData->dwSleepTime * 2 > 2 * ONE_HOUR ?
2 * ONE_HOUR :
pData->dwSleepTime * 2;
} else {
dwTimeToSleep = INFINITE;
}
return dwTimeToSleep;
}