mirror of https://github.com/tongzx/nt5src
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
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;
|
|
}
|