/*++ Copyright (c) 1996 Microsoft Corporation Abstract: This module provides functionality for ADs within spooler Author: Steve Wilson (NT) December 1996 Revision History: --*/ #include #pragma hdrstop #include "ds.hxx" #define LOG_EVENT_ERROR_BUFFER_SIZE 11 #define PPM_FACTOR 48 #define LOTS_OF_FORMS 300 // This is a little more than twice the number of built-in forms extern BOOL gbInDomain; extern BOOL gdwLogDsEvents; extern "C" HANDLE ghDsUpdateThread; extern "C" DWORD gdwDsUpdateThreadId; extern "C" BOOL (*pfnOpenPrinter)(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS); extern "C" BOOL (*pfnClosePrinter)(HANDLE); extern "C" DWORD SetPrinterDs( HANDLE hPrinter, DWORD dwAction, BOOL bSynchronous ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; HRESULT hr; HANDLE hToken = NULL; PWSTR pszObjectGUID, pszCN, pszDN; DWORD DsKeyUpdate, Attributes; BOOL DoChange = FALSE; NOTIFYVECTOR NotifyVector; SplInSem(); if (!gbInDomain) return ERROR_DS_UNAVAILABLE; // Don't allow masquerading printer publishing if (pSpool->pIniPort && !(pSpool->pIniPort->Status & PP_MONITOR)) return ERROR_INVALID_PARAMETER; hToken = RevertToPrinterSelf(); // All DS accesses are done by LocalSystem account // If any of these change we'll update the registry entry DsKeyUpdate = pIniPrinter->DsKeyUpdate; pszObjectGUID = pIniPrinter->pszObjectGUID; pszCN = pIniPrinter->pszCN; pszDN = pIniPrinter->pszDN; Attributes = pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED; // Attribute states desired state, not current state switch (dwAction) { case DSPRINT_UPDATE: if (!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED)) { hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND); } else if (bSynchronous) { // We are in the background thread. if (pIniPrinter->DsKeyUpdate) { INCPRINTERREF(pIniPrinter); LeaveSplSem(); hr = DsPrinterPublish(hPrinter); EnterSplSem(); DECPRINTERREF(pIniPrinter); } else { hr = ERROR_SUCCESS; } } else { // Here we are in the foreground thread. if (pIniPrinter->DsKeyUpdateForeground) { pIniPrinter->Attributes |= PRINTER_ATTRIBUTE_PUBLISHED; pIniPrinter->dwAction = DSPRINT_PUBLISH; hr = ERROR_IO_PENDING; } else { hr = ERROR_SUCCESS; } } break; case DSPRINT_PUBLISH: if (!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) && PrinterPublishProhibited()) { // There is a policy against publishing printers from this machine. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, ERROR_ACCESS_DENIED); } else { if (bSynchronous) { INCPRINTERREF(pIniPrinter); LeaveSplSem(); hr = DsPrinterPublish(hPrinter); EnterSplSem(); DECPRINTERREF(pIniPrinter); } else { // This is a Pending Unpublish state if (!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) && pIniPrinter->pszObjectGUID) pIniPrinter->dwAction = DSPRINT_REPUBLISH; else pIniPrinter->dwAction = DSPRINT_PUBLISH; pIniPrinter->Attributes |= PRINTER_ATTRIBUTE_PUBLISHED; hr = ERROR_IO_PENDING; } } break; case DSPRINT_REPUBLISH: if (PrinterPublishProhibited()) { // There is a policy against publishing printers from this machine. hr = MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, ERROR_ACCESS_DENIED); } else { // Synchronous mode is from background thread and it should only call Publish/Unpublish if (bSynchronous) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INVALID_PARAMETER); SPLASSERT(FALSE); } else { pIniPrinter->dwAction = DSPRINT_REPUBLISH; pIniPrinter->Attributes |= PRINTER_ATTRIBUTE_PUBLISHED; hr = ERROR_IO_PENDING; } } break; case DSPRINT_UNPUBLISH: if (bSynchronous) { INCPRINTERREF(pIniPrinter); LeaveSplSem(); hr = DsPrinterUnpublish(hPrinter); EnterSplSem(); DECPRINTERREF(pIniPrinter); } else { pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_PUBLISHED; pIniPrinter->dwAction = DSPRINT_UNPUBLISH; hr = ERROR_IO_PENDING; } break; default: hr = ERROR_INVALID_PARAMETER; break; } // Update Registry and set notifications if (pszCN != pIniPrinter->pszCN || pszDN != pIniPrinter->pszDN || pszObjectGUID != pIniPrinter->pszObjectGUID || DsKeyUpdate != pIniPrinter->DsKeyUpdate || Attributes != (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED)) { ZERONV(NotifyVector); if (pszObjectGUID != pIniPrinter->pszObjectGUID) { NotifyVector[PRINTER_NOTIFY_TYPE] |= BIT(I_PRINTER_OBJECT_GUID); DoChange = TRUE; } if (Attributes != (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED)) { NotifyVector[PRINTER_NOTIFY_TYPE] |= BIT(I_PRINTER_ATTRIBUTES); DoChange = TRUE; } if (DoChange) { UpdatePrinterIni(pIniPrinter, UPDATE_CHANGEID); SetPrinterChange(pIniPrinter, NULL, NotifyVector, PRINTER_CHANGE_SET_PRINTER, pIniPrinter->pIniSpooler); } else if (DsKeyUpdate != pIniPrinter->DsKeyUpdate) { UpdatePrinterIni(pIniPrinter, UPDATE_DS_ONLY); } } SplInSem(); if (hr == ERROR_IO_PENDING && !bSynchronous) SpawnDsUpdate(1); if (hToken) ImpersonatePrinterClient(hToken); return (DWORD) hr; } HRESULT DsPrinterPublish( HANDLE hPrinter ) { HRESULT hr; PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE]; BOOL bUpdating = !!pIniPrinter->pszObjectGUID; DWORD dwDsKeyUpdate; SplOutSem(); #if DBG EnterSplSem(); DBGMSG( DBG_EXEC, ("DsPrinterPublish: %ws\n", pIniPrinter->pName)); LeaveSplSem(); #endif // On first publish update all keys and tell the driver to write its non-devcap properties if (!bUpdating) { // We execute this on the background thread and hence donot need any // to be in the critical section. pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH | DS_KEY_SPOOLER | DS_KEY_DRIVER | DS_KEY_USER; } // Update DS properties dwDsKeyUpdate = pIniPrinter->DsKeyUpdate & (DS_KEY_SPOOLER | DS_KEY_DRIVER | DS_KEY_USER); hr = DsPrinterUpdate(hPrinter); BAIL_ON_FAILURE(hr); DBGMSG( DBG_EXEC, ("PublishDsUpdate: Printer Updated\n" ) ); error: if (SUCCEEDED(hr)) { // Only write a success event if something changed if (dwDsKeyUpdate != pIniPrinter->DsKeyUpdate) { SplLogEvent( pIniPrinter->pIniSpooler, gdwLogDsEvents & LOG_INFO, bUpdating ? MSG_PRINTER_UPDATED : MSG_PRINTER_PUBLISHED, FALSE, pIniPrinter->pszCN, pIniPrinter->pszDN, NULL ); } } else if (pIniPrinter->pszCN && pIniPrinter->pszDN) { wsprintf(ErrorBuffer, L"%x", hr); SplLogEvent( pIniPrinter->pIniSpooler, gdwLogDsEvents & LOG_ERROR, MSG_PRINTER_NOT_PUBLISHED, FALSE, pIniPrinter->pszCN, pIniPrinter->pszDN, ErrorBuffer, NULL ); } if (pIniPrinter->DsKeyUpdate) hr = ERROR_IO_PENDING; return hr; } HRESULT DsPrinterUpdate( HANDLE hPrinter ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; HRESULT hr = S_OK; DWORD dwResult; BOOL bImpersonating = FALSE; IADs *pADs = NULL; SplOutSem(); if(!(pIniPrinter->DsKeyUpdate & (DS_KEY_SPOOLER | DS_KEY_DRIVER | DS_KEY_USER))) { pIniPrinter->DsKeyUpdate = 0; } // If we aren't truly published yet, be sure to publish mandatory properties first! if (!pIniPrinter->pszObjectGUID) { // // Fail if we're on a cluster but couldn't get the Cluster GUID // The Cluster GUID is required later in AddClusterAce // if ((pIniPrinter->pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG) && !pIniPrinter->pIniSpooler->pszClusterGUID) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_CLUSTER_NO_SECURITY_CONTEXT); BAIL_ON_FAILURE(hr); } // Get or Create printQueue object hr = GetPrintQueue(hPrinter, &pADs); BAIL_ON_FAILURE(hr); hr = PublishMandatoryProperties(hPrinter, pADs); BAIL_ON_FAILURE(hr); } else { // If we are a Cluster, impersonate the Cluster User if (pIniPrinter->pIniSpooler->hClusterToken != INVALID_HANDLE_VALUE) { // Impersonate the client if (!ImpersonatePrinterClient(pIniPrinter->pIniSpooler->hClusterToken)) { dwResult = GetLastError(); DBGMSG(DBG_WARNING,("DsPrinterPublish FAILED: %d\n", dwResult)); hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); BAIL_ON_FAILURE(hr); } bImpersonating = TRUE; } // Get or Create printQueue object hr = GetPrintQueue(hPrinter, &pADs); BAIL_ON_FAILURE(hr); } // Update User - updates from Registry // // CopyRegistry2Ds for DS_KEY_USER values must // be called in the first place since there could be duplicate values // that might overwrite properties contained by either // DS_KEY_SPOOLER or DS_KEY_DRIVER. // Ignore the return value since publishing of DS_KEY_USER values // is not critical // if (pIniPrinter->DsKeyUpdate & DS_KEY_USER) { CopyRegistry2Ds(hPrinter, DS_KEY_USER, pADs); } // Update Spooler - updates from Registry if (pIniPrinter->DsKeyUpdate & DS_KEY_SPOOLER) { hr = CopyRegistry2Ds(hPrinter, DS_KEY_SPOOLER, pADs); BAIL_ON_FAILURE(hr); } // Update Driver - updates from Registry if (pIniPrinter->DsKeyUpdate & DS_KEY_DRIVER) { hr = CopyRegistry2Ds(hPrinter, DS_KEY_DRIVER, pADs); // Ignore missing key if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND) hr = S_OK; BAIL_ON_FAILURE(hr); } error: if (pADs) pADs->Release(); if (bImpersonating) pIniPrinter->pIniSpooler->hClusterToken = RevertToPrinterSelf(); return hr; } HRESULT DsDeletePQObject( HANDLE hPrinter ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; IADsContainer *pADsContainer = NULL; IADs *pADs = NULL; HRESULT hr = E_FAIL; WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE]; BOOL bImpersonating = FALSE; DWORD dwError; // // This routine is called when AddClusterAce failed. Even if we faild deleteing the object, // we really want to clean up the pIniPrinter structure so that we prevent the case where // the object stays forever in pending un/publishing. // That's because the other cluster node fails to delete/update it // since the printQueue object doesn't have the cluster user ace added. Pruner also fails to delete it // since the PrintQueue's GUID matches the pIniPrinter's GUID. // SplOutSem(); hr = GetPrintQueueContainer(hPrinter, &pADsContainer, &pADs); BAIL_ON_FAILURE(hr); // Delete Printer Object hr = pADsContainer->Delete(SPLDS_PRINTER_CLASS, pIniPrinter->pszCN); DBGMSG(DBG_EXEC,("DsPrinterUnpublish FAILED: %x, %ws\n", hr, pIniPrinter->pszCN)); BAIL_ON_FAILURE(hr); error: if (pADs) pADs->Release(); if (pADsContainer) pADsContainer->Release(); pIniPrinter->DsKeyUpdate = 0; FreeSplStr(pIniPrinter->pszObjectGUID); pIniPrinter->pszObjectGUID = NULL; FreeSplStr(pIniPrinter->pszCN); pIniPrinter->pszCN = NULL; FreeSplStr(pIniPrinter->pszDN); pIniPrinter->pszDN = NULL; return hr; } HRESULT DsPrinterUnpublish( HANDLE hPrinter ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; IADsContainer *pADsContainer = NULL; IADs *pADs = NULL; HRESULT hr = E_FAIL; WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE]; BOOL bImpersonating = FALSE; DWORD dwError; SplOutSem(); // If we are a Cluster, impersonate the Cluster User if (pIniPrinter->pIniSpooler->hClusterToken != INVALID_HANDLE_VALUE) { // Impersonate the client if (!ImpersonatePrinterClient(pIniPrinter->pIniSpooler->hClusterToken)) { dwError = GetLastError(); DBGMSG(DBG_WARNING,("DsPrinterUnpublish FAILED: %d\n", dwError)); hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwError); goto error; } bImpersonating = TRUE; } hr = GetPrintQueueContainer(hPrinter, &pADsContainer, &pADs); BAIL_ON_FAILURE(hr); // Delete Printer Object hr = pADsContainer->Delete(SPLDS_PRINTER_CLASS, pIniPrinter->pszCN); DBGMSG(DBG_EXEC,("DsPrinterUnpublish FAILED: %x, %ws\n", hr, pIniPrinter->pszCN)); BAIL_ON_FAILURE(hr); error: if (bImpersonating) pIniPrinter->pIniSpooler->hClusterToken = RevertToPrinterSelf(); 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; SplLogEvent( pIniPrinter->pIniSpooler, gdwLogDsEvents & LOG_INFO, MSG_MISSING_PRINTER_UNPUBLISHED, FALSE, pIniPrinter->pName, NULL ); } else if (SUCCEEDED(hr)) { SplLogEvent( pIniPrinter->pIniSpooler, gdwLogDsEvents & LOG_INFO, MSG_PRINTER_UNPUBLISHED, FALSE, pIniPrinter->pszCN, pIniPrinter->pszDN, NULL ); } else if(pIniPrinter->pszCN && pIniPrinter->pszDN) { wsprintf(ErrorBuffer, L"%x", hr); SplLogEvent( pIniPrinter->pIniSpooler, gdwLogDsEvents & LOG_ERROR, MSG_CANT_DELETE_PRINTQUEUE, FALSE, pIniPrinter->pszCN, pIniPrinter->pszDN, ErrorBuffer, NULL ); } if (SUCCEEDED(hr)) { pIniPrinter->DsKeyUpdate = 0; FreeSplStr(pIniPrinter->pszObjectGUID); pIniPrinter->pszObjectGUID = NULL; FreeSplStr(pIniPrinter->pszCN); pIniPrinter->pszCN = NULL; FreeSplStr(pIniPrinter->pszDN); pIniPrinter->pszDN = NULL; } else { pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH; } if (pADs) pADs->Release(); if (pADsContainer) pADsContainer->Release(); if (pIniPrinter->DsKeyUpdate) hr = ERROR_IO_PENDING; return hr; } LPCWSTR MapDSFlag2DSKey( DWORD Flag ) { DWORD idx; LPCWSTR pKey = NULL; struct DSEntry { DWORD Flag; LPCWSTR pKey; }; static DSEntry DSKeys [] = { {DS_KEY_SPOOLER , SPLDS_SPOOLER_KEY}, {DS_KEY_DRIVER , SPLDS_DRIVER_KEY}, {DS_KEY_USER , SPLDS_USER_KEY}, {0 , NULL}, }; for (idx = 0; DSKeys[idx].pKey; idx++) { if(DSKeys[idx].Flag & Flag) { pKey = DSKeys[idx].pKey; } } return pKey; } HRESULT CopyRegistry2Ds( HANDLE hPrinter, DWORD Flag, IADs *pADs ) { HRESULT hr = ERROR_SUCCESS; DWORD i; DWORD dwLDAPError; DWORD cbEnumValues = 0; PPRINTER_ENUM_VALUES pEnumValues = NULL; DWORD nEnumValues; DWORD dwResult; WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE]; BSTR bstrADsPath = NULL; PINIPRINTER pIniPrinter = ((PSPOOL)hPrinter)->pIniPrinter; LPCWSTR pKey = MapDSFlag2DSKey(Flag); #if DBG EnterSplSem(); DBGMSG(DBG_EXEC, ("Mass Publish %ws", ((PSPOOL)hPrinter)->pIniPrinter->pName)); LeaveSplSem(); #endif // Enumerate and Publish Key dwResult = SplEnumPrinterDataEx( hPrinter, pKey, (LPBYTE) pEnumValues, cbEnumValues, &cbEnumValues, &nEnumValues ); if (dwResult != ERROR_MORE_DATA) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); if( HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND && Flag != DS_KEY_SPOOLER) { goto IgnoreError; } else { goto error; } } if (!(pEnumValues = (PPRINTER_ENUM_VALUES) AllocSplMem(cbEnumValues))) { DBGMSG(DBG_EXEC,("CopyRegistry2Ds EnumPrinterDataEx FAILED: %d\n", GetLastError())); hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError()); BAIL_ON_FAILURE(hr); } dwResult = SplEnumPrinterDataEx( hPrinter, pKey, (LPBYTE) pEnumValues, cbEnumValues, &cbEnumValues, &nEnumValues ); if (dwResult != ERROR_SUCCESS) { DBGMSG(DBG_EXEC,("CopyRegistry2Ds 2nd EnumPrinterDataEx FAILED: %d\n", GetLastError())); hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); if( HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND && Flag != DS_KEY_SPOOLER) { goto IgnoreError; } else { goto error; } } // Mass publish for (i = 0 ; i < nEnumValues ; ++i) { hr = PublishDsData( pADs, pEnumValues[i].pValueName, pEnumValues[i].dwType, (PBYTE) pEnumValues[i].pData); // Don't bail out on failure to put a specific property if (FAILED(hr)) { if (pEnumValues[i].pValueName) { DBGMSG(DBG_EXEC, ("Put property failed: %x, %ws\n", hr, pEnumValues[i].pValueName)); } else { DBGMSG(DBG_EXEC, ("Put property failed: %x\n", hr)); } } else { DBGMSG(DBG_EXEC, ("Put %ws succeeded\n", pEnumValues[i].pValueName)); } } hr = pADs->SetInfo(); // Mass publishing failed, now try Setting on every Put if (SUCCEEDED(hr)) { DBGMSG( DBG_EXEC, ("Mass Publishing Succeeded for %ws\n", pKey) ); } else { if (HRESULT_CODE(hr) == ERROR_EXTENDED_ERROR) ADsGetLastError(&dwLDAPError, NULL, 0, NULL, 0); else dwLDAPError = hr; DBGMSG( DBG_EXEC, ("Mass Publishing FAILED for %ws: %x\n", pKey, dwLDAPError) ); // Now we have to try SetInfo/GetInfo on every Put. // If the DS lacks a spooler property, then the spooler will never // be able to publish any properties. Also, we'll fail if duplicate // strings exist in REG_MULTISZ attributes. // Maybe it is better to publish what we can, // but this requires calling SetInfo() for every property, which defeats the cache. // Alternatively, we could try doing the single SetInfo once and if that fails, resort // to the SetInfo on every Put. // Additionally, when SetInfo fails it is necessary to call GetInfo on that property // in order to clear the cache's update flag for the property. When SetInfo fails // it does not clear the update flag: the update flag is only cleared when SetInfo // succeeds. Not calling GetInfo will result in SetInfo() errors on all subsequent // attempts to publish a property. // Refresh the cache hr = pADs->GetInfo(); BAIL_ON_FAILURE(hr); for (i = 0 ; i < nEnumValues ; ++i) { hr = PublishDsData( pADs, pEnumValues[i].pValueName, pEnumValues[i].dwType, (PBYTE) pEnumValues[i].pData); // Don't bail out on failure to put a specific property if (FAILED(hr)) { if (pEnumValues[i].pValueName) { DBGMSG(DBG_EXEC, ("Put property failed: %x, %ws\n", hr, pEnumValues[i].pValueName)); } else { DBGMSG(DBG_EXEC, ("Put property failed: %x\n", hr)); } wsprintf(ErrorBuffer, L"%x", hr); hr = pADs->get_ADsPath(&bstrADsPath); if (SUCCEEDED(hr)) { SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler, gdwLogDsEvents & LOG_WARNING, MSG_CANT_PUBLISH_PROPERTY, FALSE, pEnumValues[i].pValueName ? pEnumValues[i].pValueName : L"NULLName", bstrADsPath, ErrorBuffer, NULL ); SysFreeString(bstrADsPath); } } else { DBGMSG(DBG_EXEC, ("Put2 %ws succeeded\n", pEnumValues[i].pValueName)); } hr = pADs->SetInfo(); if (FAILED(hr)) { if (HRESULT_CODE(hr) == ERROR_EXTENDED_ERROR) ADsGetLastError(&dwLDAPError, NULL, 0, NULL, 0); if (pEnumValues[i].dwType == REG_SZ) DBGMSG(DBG_EXEC, ("PUBLISH FAILED: %ws, \"%ws\", %x\n", pEnumValues[i].pValueName, (LPWSTR) pEnumValues[i].pData, dwLDAPError)); else DBGMSG(DBG_EXEC, ("PUBLISH FAILED: %ws, %x\n", pEnumValues[i].pValueName, dwLDAPError)); wsprintf(ErrorBuffer, L"%x", hr); hr = pADs->get_ADsPath(&bstrADsPath); if (SUCCEEDED(hr)) { SplLogEvent( ((PSPOOL) hPrinter)->pIniSpooler, gdwLogDsEvents & LOG_WARNING, MSG_CANT_PUBLISH_PROPERTY, FALSE, pEnumValues[i].pValueName ? pEnumValues[i].pValueName : L"NULLName", bstrADsPath, ErrorBuffer, NULL ); SysFreeString(bstrADsPath); } // reset cache update flag // If this fails, there's nothing more that can be done except throw our hands up // in despair. If this fails, no spooler properties will ever be published. hr = pADs->GetInfo(); BAIL_ON_FAILURE(hr); } else { DBGMSG( DBG_EXEC, ("Published: %ws\n", pEnumValues[i].pValueName) ); } } } IgnoreError: EnterSplSem(); pIniPrinter->DsKeyUpdate &= ~Flag; if(!(pIniPrinter->DsKeyUpdate & (DS_KEY_SPOOLER | DS_KEY_DRIVER | DS_KEY_USER))) { pIniPrinter->DsKeyUpdate = 0; } LeaveSplSem(); error: FreeSplMem(pEnumValues); return hr; } HRESULT PublishDsData( IADs *pADs, LPWSTR pValue, DWORD dwType, PBYTE pData ) { HRESULT hr; BOOL bCreated = FALSE; switch (dwType) { case REG_SZ: hr = put_BSTR_Property(pADs, pValue, (LPWSTR) pData); break; case REG_MULTI_SZ: hr = put_MULTISZ_Property(pADs, pValue, (LPWSTR) pData); break; case REG_DWORD: hr = put_DWORD_Property(pADs, pValue, (DWORD *) pData); break; case REG_BINARY: hr = put_BOOL_Property(pADs, pValue, (BOOL *) pData); break; } return hr; } HRESULT PublishMandatoryProperties( HANDLE hPrinter, IADs *pADs ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; HRESULT hr, hrAce; WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE]; #if DBG EnterSplSem(); DBGMSG(DBG_EXEC, ("PublishMandatoryProperties: %ws\n", pIniPrinter->pName)); LeaveSplSem(); #endif hr = SetMandatoryProperties(hPrinter, pADs); DBGMSG(DBG_EXEC, ("PublishMandatoryProperties: SMP result %d\n", hr)); BAIL_ON_FAILURE(hr); // New Schema has correct SD by default, so no need to call PutDSSD // hr = PutDSSD(pIniPrinter, pADs); hr = pADs->SetInfo(); if (FAILED(hr)) { DBGMSG(DBG_EXEC, ("PublishMandatoryProperties: SetInfo failed %d\n", hr)); wsprintf(ErrorBuffer, L"%x", hr); SplLogEvent( pSpool->pIniSpooler, gdwLogDsEvents & LOG_ERROR, MSG_CANT_PUBLISH_MANDATORY_PROPERTIES, FALSE, pIniPrinter->pszCN, pIniPrinter->pszDN, ErrorBuffer, NULL ); // If SetInfo returns ERROR_BUSY it means the object already exists. // We should have avoided this conflict when we created the CN because // we check for conflicts and generate a random name. Nonetheless, an // object could have appeared between the time we generated the CN and this SetInfo, // so failing here will let us try again and we'll generate a new name if we clear the // current one. if (HRESULT_CODE(hr) == ERROR_BUSY) { FreeSplMem(pIniPrinter->pszCN); pIniPrinter->pszCN = NULL; FreeSplMem(pIniPrinter->pszDN); pIniPrinter->pszDN = NULL; } BAIL_ON_FAILURE(hr); } // Get & Set ACE if we're a cluster hrAce = AddClusterAce(pSpool, pADs); // Get & store GUID hr = GetGUID(pADs, &pIniPrinter->pszObjectGUID); // // Keep the first failure, if present // if (FAILED(hrAce)) { hr = hrAce; wsprintf(ErrorBuffer, L"%x", hrAce); SplLogEvent( pSpool->pIniSpooler, gdwLogDsEvents & LOG_ERROR, MSG_CANT_ADD_CLUSTER_ACE, FALSE, pIniPrinter->pszCN, pIniPrinter->pszDN, ErrorBuffer, NULL ); DsDeletePQObject(hPrinter); DBGMSG(DBG_EXEC, ("PublishMandatoryProperties: AddClusterAce failed %d\n", hr)); BAIL_ON_FAILURE(hr); } // // Unpublish if we can't add the cluster ace or get the GUID // If we can't get the GUID, unpublishing will fail, but internal flags // will be set correctly and pruner will delete the orphan // if (FAILED(hr)){ DsPrinterUnpublish(hPrinter); DBGMSG(DBG_EXEC, ("PublishMandatoryProperties: GetGuid failed %d\n", hr)); BAIL_ON_FAILURE(hr); } else { DBGMSG(DBG_EXEC, ("PublishMandatoryProperties: GetGuid success %ws\n", pIniPrinter->pszObjectGUID)); } error: if (FAILED(hr)) { pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_PUBLISHED; } return hr; } HRESULT SetMandatoryProperties( HANDLE hPrinter, IADs *pADs ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; WCHAR szBuffer[MAX_UNC_PRINTER_NAME + 1]; DWORD dwResult; DWORD dwTemp; HRESULT hr; PWSTR pszServerName = NULL; // Get FQDN of this machine hr = GetDNSMachineName(pIniPrinter->pIniSpooler->pMachineName + 2, &pszServerName); BAIL_ON_FAILURE(hr); // UNC Printer Name // Build the UNC Printer Path wcscpy(szBuffer, L"\\\\"); wcscat(szBuffer, pszServerName); wcscat(szBuffer, TEXT("\\")); wcscat(szBuffer, pIniPrinter->pName); dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_UNC_NAME, REG_SZ, (PBYTE) szBuffer, (wcslen(szBuffer) + 1)*sizeof *szBuffer); if (dwResult != ERROR_SUCCESS) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); BAIL_ON_FAILURE(hr); } if (pADs) { hr = PublishDsData( pADs, SPLDS_UNC_NAME, REG_SZ, (PBYTE) szBuffer); BAIL_ON_FAILURE(hr); } // versionNumber dwTemp = DS_PRINTQUEUE_VERSION_WIN2000; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_VERSION_NUMBER, REG_DWORD, (PBYTE) &dwTemp, sizeof dwTemp); if (dwResult != ERROR_SUCCESS) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); BAIL_ON_FAILURE(hr); } if (pADs) { hr = PublishDsData( pADs, SPLDS_VERSION_NUMBER, REG_DWORD, (PBYTE) &dwTemp); BAIL_ON_FAILURE(hr); } // ServerName (without \\) dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_SERVER_NAME, REG_SZ, (PBYTE) pszServerName, (wcslen(pszServerName) + 1)*sizeof(WCHAR)); if (dwResult != ERROR_SUCCESS) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); BAIL_ON_FAILURE(hr); } if (pADs) { hr = PublishDsData( pADs, SPLDS_SERVER_NAME, REG_SZ, (PBYTE) pszServerName); BAIL_ON_FAILURE(hr); } // ShortServerName (without \\) dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_SHORT_SERVER_NAME, REG_SZ, (PBYTE) (pIniPrinter->pIniSpooler->pMachineName + 2), (wcslen(pIniPrinter->pIniSpooler->pMachineName + 2) + 1)*sizeof(WCHAR)); if (dwResult != ERROR_SUCCESS) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); BAIL_ON_FAILURE(hr); } if (pADs) { hr = PublishDsData( pADs, SPLDS_SHORT_SERVER_NAME, REG_SZ, (PBYTE) pIniPrinter->pIniSpooler->pMachineName + 2); BAIL_ON_FAILURE(hr); } // printerName dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINTER_NAME, REG_SZ, (PBYTE) pIniPrinter->pName, pIniPrinter->pName ? (wcslen(pIniPrinter->pName) + 1)*sizeof *pIniPrinter->pName : 0); if (dwResult != ERROR_SUCCESS) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); BAIL_ON_FAILURE(hr); } if (pADs) { hr = PublishDsData( pADs, SPLDS_PRINTER_NAME, REG_SZ, (PBYTE) pIniPrinter->pName); BAIL_ON_FAILURE(hr); } error: FreeSplStr(pszServerName); return hr; } // UpdateDsSpoolerKey - writes IniPrinter to registry VOID UpdateDsSpoolerKey( HANDLE hPrinter, DWORD dwVector ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; DWORD i, cbBytes, dwTemp; LPWSTR pString = NULL, pStr; DWORD dwResult = ERROR_SUCCESS; BOOL bSet = FALSE; BYTE Byte; PWSTR pszUrl = NULL; SplInSem(); // *** PRINTER_INFO_2 properties *** // Description if (dwVector & BIT(I_PRINTER_COMMENT)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_DESCRIPTION, REG_SZ, (PBYTE) pIniPrinter->pComment, pIniPrinter->pComment ? (wcslen(pIniPrinter->pComment) + 1)*sizeof *pIniPrinter->pComment : 0); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: Description, %x\n", dwResult) ); #endif } // Driver-Name if (dwVector & BIT(I_PRINTER_DRIVER_NAME)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_DRIVER_NAME, REG_SZ, (PBYTE) pIniPrinter->pIniDriver->pName, pIniPrinter->pIniDriver->pName ? (wcslen(pIniPrinter->pIniDriver->pName) + 1)*sizeof *pIniPrinter->pIniDriver->pName : 0); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: DriverName, %x\n", dwResult) ); #endif } // Location if (dwVector & BIT(I_PRINTER_LOCATION)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_LOCATION, REG_SZ, (PBYTE) pIniPrinter->pLocation, pIniPrinter->pLocation ? (wcslen(pIniPrinter->pLocation) + 1)*sizeof *pIniPrinter->pLocation : 0); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: Location, %x\n", dwResult) ); #endif } // portName if (dwVector & BIT(I_PRINTER_PORT_NAME)) { for(i = cbBytes = 0 ; i < pIniPrinter->cPorts ; ++i) cbBytes += (wcslen(pIniPrinter->ppIniPorts[i]->pName) + 1)*sizeof(WCHAR); cbBytes += sizeof(WCHAR); // final NULL of MULTI_SZ if (!(pString = (LPWSTR) AllocSplMem(cbBytes))) { dwResult = GetLastError(); goto error; } for(i = 0, pStr = pString ; i < pIniPrinter->cPorts ; ++i, pStr += wcslen(pStr) + 1) wcscpy(pStr, pIniPrinter->ppIniPorts[i]->pName); dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PORT_NAME, REG_MULTI_SZ, (PBYTE) pString, cbBytes); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: PortName, %x\n", dwResult) ); #endif } // startTime if (dwVector & BIT(I_PRINTER_START_TIME)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINT_START_TIME, REG_DWORD, (PBYTE) &pIniPrinter->StartTime, sizeof pIniPrinter->StartTime); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: StartTime, %x\n", dwResult) ); #endif } // endTime if (dwVector & BIT(I_PRINTER_UNTIL_TIME)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINT_END_TIME, REG_DWORD, (PBYTE) &pIniPrinter->UntilTime, sizeof pIniPrinter->UntilTime); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: EndTime, %x\n", dwResult) ); #endif } // printerName if (dwVector & BIT(I_PRINTER_PRINTER_NAME)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINTER_NAME, REG_SZ, (PBYTE) pIniPrinter->pName, pIniPrinter->pName ? (wcslen(pIniPrinter->pName) + 1)*sizeof *pIniPrinter->pName : 0); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: PrinterName, %x\n", dwResult) ); #endif } // keepPrintedJobs if (dwVector & BIT(I_PRINTER_ATTRIBUTES)) { Byte = (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS) ? 1 : 0; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINT_KEEP_PRINTED_JOBS, REG_BINARY, &Byte, sizeof Byte); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: KeepPrintedJobs, %x\n", dwResult) ); #endif } // printSeparatorFile if (dwVector & BIT(I_PRINTER_SEPFILE)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINT_SEPARATOR_FILE, REG_SZ, (PBYTE) pIniPrinter->pSepFile, pIniPrinter->pSepFile ? (wcslen(pIniPrinter->pSepFile) + 1)*sizeof *pIniPrinter->pSepFile : 0); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: SeparatorFile, %x\n", dwResult) ); #endif } // printShareName if (dwVector & BIT(I_PRINTER_SHARE_NAME)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINT_SHARE_NAME, REG_SZ, (PBYTE) pIniPrinter->pShareName, pIniPrinter->pShareName ? (wcslen(pIniPrinter->pShareName) + 1)*sizeof *pIniPrinter->pShareName : 0); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: ShareName, %x\n", dwResult) ); #endif } // printSpooling if (dwVector & BIT(I_PRINTER_ATTRIBUTES)) { if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT) { pStr = L"PrintDirect"; } else if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST) { pStr = L"PrintAfterSpooled"; } else { pStr = L"PrintWhileSpooling"; } dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRINT_SPOOLING, REG_SZ, (PBYTE) pStr, (wcslen(pStr) + 1)*sizeof *pStr); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: PrintSpooling, %x\n", dwResult) ); #endif } // priority if (dwVector & BIT(I_PRINTER_PRIORITY)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_PRIORITY, REG_DWORD, (PBYTE) &pIniPrinter->Priority, sizeof pIniPrinter->Priority); bSet = TRUE; #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: Priority, %x\n", dwResult) ); #endif } // Non-Info2 properties if (bSet) { SetMandatoryProperties(hPrinter, NULL); // URL if (pszUrl = GetPrinterUrl(pSpool)) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_URL, REG_SZ, (PBYTE) pszUrl, (wcslen(pszUrl) + 1)*sizeof *pszUrl); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: URL, %x\n", dwResult) ); #endif } // Immortal dwResult = ImmortalPolicy(); dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_FLAGS, REG_DWORD, (PBYTE) &dwResult, sizeof(dwResult)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsSpoolerKey: Immortal, %x\n", dwResult) ); #endif } error: FreeSplMem(pszUrl); FreeSplMem(pString); } VOID UpdateDsDriverKey( HANDLE hPrinter ) { PSPOOL pSpool = (PSPOOL) hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; DWORD i, cbBytes; WCHAR szBuffer[33]; // Used by ltow LPWSTR pString, pStr; BOOL bResult; DWORD dwResult; BYTE Byte; LPWSTR pOutput = NULL, pTemp = NULL, pTemp1 = NULL; DWORD cOutputBytes, cTempBytes; POINTS point; DWORD dwServerMajorVersion, dwServerMinorVersion; DWORD cbNeeded; HANDLE hModule = FALSE; PDEVCAP pDevCap; PSPLDEVCAP pSplDevCaps; HANDLE hDevCapPrinter = NULL; WCHAR pPrinterName[MAX_UNC_PRINTER_NAME]; WCHAR pBuf[100]; BOOL bInSplSem = TRUE; DWORD dwTemp, dwPrintRate, dwPrintRateUnit, dwPrintPPM; // *** DeviceCapability properties *** SplInSem(); DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: %ws\n", pIniPrinter->pName)); pOutput = (LPWSTR) AllocSplMem(cOutputBytes = 200); if (!pOutput) goto error; pTemp = (LPWSTR) AllocSplMem(cTempBytes = 200); if (!pTemp) goto error; // Get & Load Driver PINIENVIRONMENT pIniEnvironment; pIniEnvironment = FindEnvironment(szEnvironment, pSpool->pIniSpooler); if (pIniEnvironment) { WCHAR szConfigFile[INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1]; PINIVERSION pIniVersion; pIniVersion = FindVersionForDriver(pIniEnvironment, pIniPrinter->pIniDriver); if (!pIniVersion) goto error; if( !(i = GetDriverVersionDirectory(szConfigFile, (DWORD)(COUNTOF(szConfigFile) - wcslen(pIniPrinter->pIniDriver->pConfigFile) - 1), pSpool->pIniSpooler, pIniEnvironment, pIniVersion, pIniPrinter->pIniDriver, NULL)) ) { goto error; } szConfigFile[i++] = L'\\'; wcscpy(szConfigFile + i, pIniPrinter->pIniDriver->pConfigFile); if (!(hModule = LoadLibrary(szConfigFile))) { goto error; } if (!(pDevCap = reinterpret_cast(GetProcAddress(hModule, "DrvDeviceCapabilities")))) { goto error; } pSplDevCaps = reinterpret_cast(GetProcAddress(hModule, (LPCSTR) MAKELPARAM(254, 0))); } else { goto error; } DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: driver found\n")); INCPRINTERREF(pIniPrinter); // We need to use UNC format so we go to the right pIniSpooler. // For instance, we won't find the printer if it's in the cluster pIniSpooler and we don't use // the virtual cluster name (\\server\printer). wsprintf(pPrinterName, L"%s\\%s", pIniPrinter->pIniSpooler->pMachineName, pIniPrinter->pName); LeaveSplSem(); bInSplSem = FALSE; if (!(*pfnOpenPrinter)(pPrinterName, &hDevCapPrinter, NULL)) { dwResult = GetLastError(); DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: OpenPrinter failed %d\n", dwResult)); goto error; } // printBinNames if (!DevCapMultiSz( hPrinter, hDevCapPrinter, pDevCap, NULL, pPrinterName, DC_BINNAMES, 24, SPLDS_PRINT_BIN_NAMES)) { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_BINNAMES failed %d\n", GetLastError())); } // printCollate (awaiting DC_COLLATE) _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_COLLATE, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_COLLATE, REG_BINARY, (PBYTE) &dwResult, sizeof(BYTE)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: Collate, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_COLLATE failed %d\n", GetLastError())); } // printColor _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_COLORDEVICE, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult == GDI_ERROR) { // Try alternative method dwResult = ThisIsAColorPrinter(pIniPrinter->pName); } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_COLORDEVICE failed %d\n", GetLastError())); } dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_COLOR, REG_BINARY, (PBYTE) &dwResult, sizeof(BYTE)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: Color, %x\n", dwResult) ); #endif // printDuplexSupported _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_DUPLEX, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { dwResult = !!dwResult; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_DUPLEX_SUPPORTED, REG_BINARY, (PBYTE) &dwResult, sizeof(BYTE)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: Duplex, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_DUPLEX failed %d\n", GetLastError())); } // printStaplingSupported _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_STAPLE, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_STAPLING_SUPPORTED, REG_BINARY, (PBYTE) &dwResult, sizeof(BYTE)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: Duplex, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_STAPLE failed %d\n", GetLastError())); } // printMaxXExtent & printMaxYExtent _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_MAXEXTENT, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { *((DWORD *) &point) = dwResult; dwTemp = (DWORD) point.x; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_MAX_X_EXTENT, REG_DWORD, (PBYTE) &dwTemp, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: MaxXExtent, %x\n", dwResult) ); #endif dwTemp = (DWORD) point.y; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_MAX_Y_EXTENT, REG_DWORD, (PBYTE) &dwTemp, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: MaxYExtent, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_MAXEXTENT failed %d\n", GetLastError())); } // printMinXExtent & printMinYExtent _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_MINEXTENT, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { *((DWORD *) &point) = dwResult; dwTemp = (DWORD) point.x; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_MIN_X_EXTENT, REG_DWORD, (PBYTE) &dwTemp, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: MinXExtent, %x\n", dwResult) ); #endif dwTemp = (DWORD) point.y; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_MIN_Y_EXTENT, REG_DWORD, (PBYTE) &dwTemp, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: MinYExtent, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_MINEXTENT failed %d\n", GetLastError())); } // printMediaSupported - Not part of printQueue, but is in Schema if (!DevCapMultiSz( hPrinter, hDevCapPrinter, pDevCap, pSplDevCaps, pPrinterName, DC_PAPERNAMES, 64, SPLDS_PRINT_MEDIA_SUPPORTED)) { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_PAPERNAMES failed %d\n", GetLastError())); } // printMediaReady if (!DevCapMultiSz( hPrinter, hDevCapPrinter, pDevCap, pSplDevCaps, pPrinterName, DC_MEDIAREADY, 64, SPLDS_PRINT_MEDIA_READY)) { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_MEDIAREADY failed %d\n", GetLastError())); } // printNumberUp _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_NUP, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_NUMBER_UP, REG_DWORD, (PBYTE) &dwResult, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: NumberUp, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_NUP failed %d\n", GetLastError())); } // printMemory _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_PRINTERMEM, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_MEMORY, REG_DWORD, (PBYTE) &dwResult, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: printMemory, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_PRINTERMEM failed %d\n", GetLastError())); } // printOrientationsSupported _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_ORIENTATION, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { if (dwResult == 90 || dwResult == 270) { wcscpy(pBuf, L"PORTRAIT"); wcscpy(pStr = pBuf + wcslen(pBuf) + 1, L"LANDSCAPE"); } else { wcscpy(pStr = pBuf, L"PORTRAIT"); } pStr += wcslen(pStr) + 1; *pStr++ = L'\0'; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_ORIENTATIONS_SUPPORTED, REG_MULTI_SZ, (PBYTE) pBuf, (DWORD) ((ULONG_PTR) pStr - (ULONG_PTR) pBuf)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: Orientations, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_ORIENTATION failed %d\n", GetLastError())); } // printMaxResolutionSupported _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_ENUMRESOLUTIONS, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { if (cOutputBytes < dwResult*2*sizeof(DWORD)) { if(!(pTemp1 = (LPWSTR) ReallocSplMem(pOutput, 0, cOutputBytes = dwResult*2*sizeof(DWORD)))) goto error; pOutput = pTemp1; } _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_ENUMRESOLUTIONS, pOutput, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR && dwResult > 0) { // Find the maximum resolution: we have dwResult*2 resolutions to check _try { for(i = dwTemp = 0 ; i < dwResult*2 ; ++i) { if (((DWORD *) pOutput)[i] > dwTemp) dwTemp = ((DWORD *) pOutput)[i]; } dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_MAX_RESOLUTION_SUPPORTED, REG_DWORD, (PBYTE) &dwTemp, sizeof(DWORD)); } _except(1) { SetLastError(dwResult = GetExceptionCode()); } #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: Resolution, %x\n", dwResult) ); #endif } } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_ENUMRESOLUTIONS failed %d\n", GetLastError())); } // printLanguage if (!DevCapMultiSz( hPrinter, hDevCapPrinter, pDevCap, NULL, pPrinterName, DC_PERSONALITY, 32, SPLDS_PRINT_LANGUAGE)) { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_PERSONALITY failed %d\n", GetLastError())); } // printRate // NOTE: If PrintRate is 0, no value is published _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_PRINTRATE, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } dwPrintRate = dwResult ? dwResult : GDI_ERROR; if (dwPrintRate != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_RATE, REG_DWORD, (PBYTE) &dwPrintRate, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: PrintRate, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_PRINTRATE failed %d\n", GetLastError())); } // printRateUnit _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_PRINTRATEUNIT, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } dwPrintRateUnit = dwResult; // // If the capability isn't supported, set printRateUnit to empty string. // switch (dwPrintRateUnit) { case PRINTRATEUNIT_PPM: pStr = L"PagesPerMinute"; break; case PRINTRATEUNIT_CPS: pStr = L"CharactersPerSecond"; break; case PRINTRATEUNIT_LPM: pStr = L"LinesPerMinute"; break; case PRINTRATEUNIT_IPM: pStr = L"InchesPerMinute"; break; default: pStr = L""; break; } if (pStr) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_RATE_UNIT, REG_SZ, (PBYTE) pStr, (wcslen(pStr) + 1)*sizeof *pStr); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: PrintRateUnit, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_PRINTRATEUNIT no unit %d\n", dwPrintRateUnit )); } // printPagesPerMinute // DevCap returns 0 if there is no entry in GPD _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_PRINTRATEPPM, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult == GDI_ERROR) dwResult = 0; dwPrintPPM = dwResult; // If dwPrintPPM == 0, then calculate PPM from PrintRate if (dwPrintPPM == 0) { if (dwPrintRate == GDI_ERROR) { dwPrintPPM = GDI_ERROR; } else { switch (dwPrintRateUnit) { case PRINTRATEUNIT_PPM: dwPrintPPM = dwPrintRate; break; case PRINTRATEUNIT_CPS: case PRINTRATEUNIT_LPM: dwPrintPPM = dwPrintRate/PPM_FACTOR; if (dwPrintPPM == 0) dwPrintPPM = 1; // min PPM is 1 break; default: DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: PRINTRATEUNIT not found %d\n", dwPrintRateUnit)); dwPrintPPM = GDI_ERROR; break; } } } if (dwPrintPPM != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_PRINT_PAGES_PER_MINUTE, REG_DWORD, (PBYTE) &dwPrintPPM, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG(DBG_WARNING, ("UpdateDsDriverKey: PrintPagesPerMinute, %x\n", dwResult)); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: PPM failed %d\n", GetLastError())); } // printDriverVersion _try { dwResult = (*pDevCap)( hDevCapPrinter, pPrinterName, DC_VERSION, NULL, NULL); } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, SPLDS_DRIVER_VERSION, REG_DWORD, (PBYTE) &dwResult, sizeof(DWORD)); #if DBG if (dwResult != ERROR_SUCCESS) DBGMSG( DBG_WARNING, ("UpdateDsDriverKey: Driver Version, %x\n", dwResult) ); #endif } else { DBGMSG( DBG_EXEC, ("UpdateDsDriverKey: DC_VERSION failed %d\n", GetLastError())); } error: if (hDevCapPrinter) (*pfnClosePrinter)(hDevCapPrinter); if (!bInSplSem) { EnterSplSem(); DECPRINTERREF(pIniPrinter); } if (hModule) FreeLibrary(hModule); FreeSplMem(pOutput); FreeSplMem(pTemp); } BOOL DevCapMultiSz( HANDLE hPrinter, HANDLE hDevCapPrinter, PDEVCAP pDevCap, PSPLDEVCAP pSplDevCap, PWSTR pszPrinterName, WORD fwCapability, DWORD dwElementBytes, PWSTR pszRegValue ) /*++ Function Description: This function writes a multisz devcap string to the DsDriverKey registry Parameters: hPrinter - printer handle hDevCapPrinter - devcap handle pDevCap - devcap function pointer pszPrinterName - name of the printer fwCapability - devcap capability entry dwElementBytes - length of each string element in the array returned by devcaps pszRegValue - name of the registry value to which the multisz string will be written Return Values: BOOL - TRUE if successful, FALSE if not. Call GetLastError to retrieve failure error. --*/ { DWORD dwResult, cbBytes; PWSTR pszDevCapBuffer = NULL; PWSTR pszRegData = NULL; _try { dwResult = GDI_ERROR; if (pSplDevCap) { dwResult = (*pSplDevCap)( hDevCapPrinter, pszPrinterName, fwCapability, NULL, 0, NULL); } if (dwResult == GDI_ERROR) { dwResult = (*pDevCap)( hDevCapPrinter, pszPrinterName, fwCapability, NULL, NULL); } if (dwResult != GDI_ERROR) { // // DeviceCapabilities doesn't take a buffer size parameter, so if you get // printer properties on a hundred or so printers at the same time, you will // occasionally hit the case where win32 cache is deleting & adding forms in // RefreshFormsCache and DC_PAPERNAMES calls EnumForms and gets different // results. The first call may return 3 forms and second returns 20. So we // allocate a big buffer here so third party drivers don't AV. For unidrv // we have a different interface that accepts a buffer size. // if ((fwCapability == DC_PAPERNAMES || fwCapability == DC_MEDIAREADY) && dwResult < LOTS_OF_FORMS) { cbBytes = LOTS_OF_FORMS*dwElementBytes*sizeof(WCHAR); } else { cbBytes = dwResult*dwElementBytes*sizeof(WCHAR); } pszDevCapBuffer = (PWSTR) AllocSplMem(cbBytes); if (pszDevCapBuffer) { dwResult = GDI_ERROR; if (pSplDevCap) { dwResult = (*pSplDevCap)( hDevCapPrinter, pszPrinterName, fwCapability, pszDevCapBuffer, cbBytes/sizeof(WCHAR), NULL); } if (dwResult == GDI_ERROR) { dwResult = (*pDevCap)( hDevCapPrinter, pszPrinterName, fwCapability, pszDevCapBuffer, NULL); } if (dwResult != GDI_ERROR) { if (!(pszRegData = DevCapStrings2MultiSz(pszDevCapBuffer, dwResult, dwElementBytes, &cbBytes))) { dwResult = GDI_ERROR; } } } else { dwResult = GDI_ERROR; } } } _except(1) { SetLastError(GetExceptionCode()); dwResult = GDI_ERROR; } if (dwResult != GDI_ERROR) { dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, pszRegValue, REG_MULTI_SZ, (PBYTE) pszRegData, cbBytes); if (dwResult != ERROR_SUCCESS) { SetLastError(dwResult); dwResult = GDI_ERROR; } } else { WCHAR szzNull[2]; szzNull[0] = szzNull[1] = '\0'; dwResult = SplSetPrinterDataEx( hPrinter, SPLDS_DRIVER_KEY, pszRegValue, REG_MULTI_SZ, (PBYTE) szzNull, 2 * sizeof(WCHAR)); } FreeSplStr(pszDevCapBuffer); FreeSplStr(pszRegData); return dwResult != GDI_ERROR; } // RecreateDsKey: Clears existing published properties and recreates & republishes Registry key extern "C" DWORD RecreateDsKey( HANDLE hPrinter, PWSTR pszKey ) { PSPOOL pSpool = (PSPOOL)hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; SplOutSem(); // 1) Clear all published Properties under Key ClearDsKey(hPrinter, pszKey); // 2) Delete Key EnterSplSem(); SplDeletePrinterKey(hPrinter, pszKey); // 3) Recreate Key if (!wcscmp(pszKey, SPLDS_DRIVER_KEY)) { UpdateDsDriverKey(hPrinter); } else if (!wcscmp(pszKey, SPLDS_SPOOLER_KEY)) { UpdateDsSpoolerKey(hPrinter, 0xffffffff); } // 4) Republish Key SetPrinterDs(hPrinter, DSPRINT_UPDATE, FALSE); LeaveSplSem(); SplOutSem(); return ERROR_SUCCESS; } // ClearDsKey: clears all properties in specified key HRESULT ClearDsKey( HANDLE hPrinter, PWSTR pszKey ) { HRESULT hr = ERROR_SUCCESS; DWORD i; DWORD cbEnumValues = 0; PPRINTER_ENUM_VALUES pEnumValues = NULL; DWORD nEnumValues; DWORD dwResult; PSPOOL pSpool = (PSPOOL)hPrinter; PINIPRINTER pIniPrinter = pSpool->pIniPrinter; IADs *pADs = NULL; HANDLE hToken = NULL; VARIANT var; SplOutSem(); // // If we're not published, there's no DS key to clear, so just return success // if (!pIniPrinter->pszObjectGUID) return S_OK; hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hr)) return hr; hToken = RevertToPrinterSelf(); // All DS accesses are done by LocalSystem account // 1) Enumerate all the Values under key // Enumerate and Publish Key dwResult = SplEnumPrinterDataEx( hPrinter, pszKey, NULL, 0, &cbEnumValues, &nEnumValues ); if (dwResult != ERROR_MORE_DATA) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); goto error; } if (!(pEnumValues = (PPRINTER_ENUM_VALUES) AllocSplMem(cbEnumValues))) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError()); BAIL_ON_FAILURE(hr); } dwResult = SplEnumPrinterDataEx( hPrinter, pszKey, (LPBYTE) pEnumValues, cbEnumValues, &cbEnumValues, &nEnumValues ); if (dwResult != ERROR_SUCCESS) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwResult); goto error; } // Get or Create printQueue object hr = GetPrintQueue(hPrinter, &pADs); BAIL_ON_FAILURE(hr); // 2) Clear Published Properties VariantInit(&var); for (i = 0 ; i < nEnumValues ; ++i) { hr = pADs->PutEx( ADS_PROPERTY_CLEAR, pEnumValues[i].pValueName, var ); #if DBG if (FAILED(hr)) DBGMSG(DBG_EXEC, ("Failed to clear property: %ws\n", pEnumValues[i].pValueName)); #endif } hr = pADs->SetInfo(); BAIL_ON_FAILURE(hr); error: FreeSplMem(pEnumValues); if (hToken) ImpersonatePrinterClient(hToken); if (pADs) { pADs->Release(); } CoUninitialize(); return hr; } HRESULT PutDSSD( PINIPRINTER pIniPrinter, IADs *pADs ) { IADsSecurityDescriptor *pSD = NULL; IADsAccessControlList *pACL = NULL; IDispatch *pSDDispatch = NULL; IDispatch *pACLDispatch = NULL; HRESULT hr, hr1; DWORD cb, cbDomain; DOMAIN_CONTROLLER_INFO *pDCI = NULL; PWSTR pszTrustee = NULL; PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL; DWORD dwRet; WCHAR ErrorBuffer[LOG_EVENT_ERROR_BUFFER_SIZE]; BSTR bstrADsPath = NULL; // Get SecurityDescriptor // *** Create ACE hr = CoCreateInstance( CLSID_SecurityDescriptor, NULL, CLSCTX_INPROC_SERVER, IID_IADsSecurityDescriptor, (void **) &pSD); BAIL_ON_FAILURE(hr); hr = pSD->QueryInterface(IID_IDispatch, (void **) &pSDDispatch); BAIL_ON_FAILURE(hr); // Create DACL hr = CoCreateInstance( CLSID_AccessControlList, NULL, CLSCTX_INPROC_SERVER, IID_IADsAccessControlList, (void **) &pACL); BAIL_ON_FAILURE(hr); hr = pACL->QueryInterface(IID_IDispatch, (void **) &pACLDispatch); BAIL_ON_FAILURE(hr); // Get Domain name dwRet = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole); if (dwRet) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet); BAIL_ON_FAILURE(hr); } // *** Create ACEs *** // NTPRINTING\SWILSONTEST$ cbDomain = wcslen(pDsRole->DomainNameFlat); cb = cbDomain + wcslen(pIniPrinter->pIniSpooler->pMachineName + 2) + 3; // add \, $, NULL cb *= sizeof(WCHAR); if (!(pszTrustee = (PWSTR) AllocSplMem(cb))) { hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError()); BAIL_ON_FAILURE(hr); } wsprintf(pszTrustee, L"%ws\\%ws$", pDsRole->DomainNameFlat, pIniPrinter->pIniSpooler->pMachineName + 2); hr = CreateAce(pACL, (BSTR) pszTrustee, GENERIC_ALL); BAIL_ON_FAILURE(hr); hr = CreateAce(pACL, (BSTR) L"Domain Admins", GENERIC_ALL); BAIL_ON_FAILURE(hr); hr = CreateAce(pACL, (BSTR) L"Authenticated Users", GENERIC_READ); BAIL_ON_FAILURE(hr); hr = CreateAce(pACL, (BSTR) L"System", GENERIC_ALL); BAIL_ON_FAILURE(hr); // Write it out hr = pACL->put_AclRevision(ACL_REVISION4); BAIL_ON_FAILURE(hr); hr = pSD->put_DiscretionaryAcl(pACLDispatch); BAIL_ON_FAILURE(hr); hr = put_Dispatch_Property(pADs, L"nTSecurityDescriptor", pSDDispatch); BAIL_ON_FAILURE(hr); // If SetInfo returns ERROR_BUSY it means the object already exists // If the object exists, delete it and create a new one hr = pADs->SetInfo(); if (HRESULT_CODE(hr) == ERROR_BUSY) pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; BAIL_ON_FAILURE(hr); DBGMSG(DBG_EXEC, ("PutDSSD: Successfully added ACL\n")); error: FreeSplMem(pszTrustee); if (pSDDispatch) pSDDispatch->Release(); if (pACLDispatch) pACLDispatch->Release(); if (pSD) pSD->Release(); if (pACL) pACL->Release(); if (pDCI) NetApiBufferFree(pDCI); if (pDsRole) DsRoleFreeMemory((PVOID) pDsRole); if (FAILED(hr)) { DBGMSG(DBG_EXEC, ("PutDSSD: Failed to add ACL: %x\n\n", hr)); wsprintf(ErrorBuffer, L"%x", hr); hr1 = pADs->get_ADsPath(&bstrADsPath); if (SUCCEEDED(hr1)) { SplLogEvent( pIniPrinter->pIniSpooler, gdwLogDsEvents & LOG_ERROR, MSG_CANT_WRITE_ACL, FALSE, bstrADsPath, ErrorBuffer, NULL ); SysFreeString(bstrADsPath); } } return hr; } HRESULT CreateAce( IADsAccessControlList *pACL, BSTR pszTrustee, DWORD dwAccessMask ) { IADsAccessControlEntry *pACE = NULL; IDispatch *pACEDispatch = NULL; HRESULT hr; // *** Create ACE hr = CoCreateInstance( CLSID_AccessControlEntry, NULL, CLSCTX_INPROC_SERVER, IID_IADsAccessControlEntry, (void **) &pACE); BAIL_ON_FAILURE(hr); hr = pACE->put_AccessMask(dwAccessMask); BAIL_ON_FAILURE(hr); hr = pACE->put_AceType(ACCESS_ALLOWED_ACE_TYPE); BAIL_ON_FAILURE(hr); hr = pACE->put_AceFlags(0); BAIL_ON_FAILURE(hr); hr = pACE->put_Trustee(pszTrustee); BAIL_ON_FAILURE(hr); hr = pACE->QueryInterface(IID_IDispatch, (void **) &pACEDispatch); BAIL_ON_FAILURE(hr); hr = pACL->AddAce(pACEDispatch); BAIL_ON_FAILURE(hr); error: if (pACEDispatch) pACEDispatch->Release(); if (pACE) pACE->Release(); return hr; } HRESULT AddClusterAce( PSPOOL pSpool, IADs *pADsPrintQueue ) { PINIPRINTER pIniPrinter = pSpool->pIniPrinter; IDispatch *pSDPrintQueueDispatch = NULL; IADsSecurityDescriptor *pSDPrintQueue = NULL; IDispatch *pACLPrintQueueDispatch = NULL; IADsAccessControlList *pACLPrintQueue = NULL; PWSTR pszClusterDN = NULL; PWSTR pszAccount = NULL; HRESULT hr; // If we don't have a GUID, then we're not a cluster and we don't need to add the ACE if (!pIniPrinter->pIniSpooler->pszClusterGUID) return S_OK; // Get the Cluster Object DN hr = GetPublishPointFromGUID(NULL, pIniPrinter->pIniSpooler->pszClusterGUID, &pszClusterDN, NULL, FALSE); BAIL_ON_FAILURE(hr); // Get the PrintQueue Security Descriptor hr = get_Dispatch_Property(pADsPrintQueue, L"nTSecurityDescriptor", &pSDPrintQueueDispatch); BAIL_ON_FAILURE(hr); hr = pSDPrintQueueDispatch->QueryInterface(IID_IADsSecurityDescriptor, (void **) &pSDPrintQueue); BAIL_ON_FAILURE(hr); // Get DACL from the Security Descriptor hr = pSDPrintQueue->get_DiscretionaryAcl(&pACLPrintQueueDispatch); BAIL_ON_FAILURE(hr); hr = pACLPrintQueueDispatch->QueryInterface(IID_IADsAccessControlList, (void **) &pACLPrintQueue); BAIL_ON_FAILURE(hr); // Create the new ACE & Add it to the ACL // Cluster account is a user account, so get current user and stick it in this ACE hr = FQDN2Whatever(pszClusterDN, &pszAccount, DS_NT4_ACCOUNT_NAME); BAIL_ON_FAILURE(hr); hr = CreateAce(pACLPrintQueue, (BSTR) pszAccount, GENERIC_ALL); BAIL_ON_FAILURE(hr); // Write the ACL back to the Security Descriptor hr = pSDPrintQueue->put_DiscretionaryAcl(pACLPrintQueueDispatch); BAIL_ON_FAILURE(hr); // Write the Security Descriptor back to the object hr = put_Dispatch_Property(pADsPrintQueue, L"nTSecurityDescriptor", pSDPrintQueueDispatch); BAIL_ON_FAILURE(hr); hr = pADsPrintQueue->SetInfo(); BAIL_ON_FAILURE(hr); error: if (pACLPrintQueueDispatch) pACLPrintQueueDispatch->Release(); if (pACLPrintQueue) pACLPrintQueue->Release(); if (pSDPrintQueueDispatch) pSDPrintQueueDispatch->Release(); if (pSDPrintQueue) pSDPrintQueue->Release(); FreeSplStr(pszClusterDN); FreeSplStr(pszAccount); return hr; }