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.
642 lines
17 KiB
642 lines
17 KiB
#include "mslocusr.h"
|
|
#include "msluglob.h"
|
|
#include "profiles.h"
|
|
|
|
#include <regentry.h>
|
|
|
|
#include <ole2.h>
|
|
|
|
CLUDatabase::CLUDatabase(void)
|
|
: m_cRef(0),
|
|
m_CurrentUser(NULL)
|
|
{
|
|
RefThisDLL(TRUE);
|
|
}
|
|
|
|
|
|
CLUDatabase::~CLUDatabase(void)
|
|
{
|
|
if (m_CurrentUser != NULL) {
|
|
m_CurrentUser->Release();
|
|
m_CurrentUser = NULL;
|
|
}
|
|
RefThisDLL(FALSE);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
if (!IsEqualIID(riid, IID_IUnknown) &&
|
|
!IsEqualIID(riid, IID_IUserDatabase)) {
|
|
*ppvObj = NULL;
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
}
|
|
|
|
*ppvObj = this;
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CLUDatabase::AddRef(void)
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CLUDatabase::Release(void)
|
|
{
|
|
ULONG cRef;
|
|
|
|
cRef = --m_cRef;
|
|
|
|
if (0L == m_cRef) {
|
|
delete this;
|
|
}
|
|
/* Handle circular refcount because of cached current user object. */
|
|
else if (1L == m_cRef && m_CurrentUser != NULL) {
|
|
IUser *pCurrentUser = m_CurrentUser;
|
|
m_CurrentUser = NULL;
|
|
pCurrentUser->Release();
|
|
}
|
|
|
|
return cRef;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::Install(LPCSTR pszSupervisorName,
|
|
LPCSTR pszSupervisorPassword,
|
|
LPCSTR pszRatingsPassword,
|
|
IUserProfileInit *pInit)
|
|
{
|
|
/* If the system already has a supervisor password, make sure the caller's
|
|
* password matches. If there isn't already a password, the caller's
|
|
* (account) password is it. We use the account password because the
|
|
* caller (the setup program) probably didn't pass us a ratings password
|
|
* in that case -- he also checks to see if there's an old ratings
|
|
* password and knows to prompt for one only if it's already there.
|
|
*/
|
|
HRESULT hres = ::VerifySupervisorPassword(pszRatingsPassword);
|
|
|
|
if (FAILED(hres)) {
|
|
if (pszRatingsPassword == NULL)
|
|
pszRatingsPassword = pszSupervisorPassword;
|
|
::ChangeSupervisorPassword(::szNULL, pszRatingsPassword);
|
|
}
|
|
else if (hres == S_FALSE)
|
|
return E_ACCESSDENIED;
|
|
|
|
|
|
/* User profiles and password caching have to be enabled for us to work.
|
|
* We also have to be able to open or create the supervisor's PWL using
|
|
* the given password. Thus we validate the password at the same time.
|
|
*/
|
|
|
|
{
|
|
RegEntry re(::szLogonKey, HKEY_LOCAL_MACHINE);
|
|
if (re.GetError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(re.GetError());
|
|
if (!re.GetNumber(::szUserProfiles))
|
|
re.SetValue(::szUserProfiles, 1);
|
|
if (re.GetError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(re.GetError());
|
|
}
|
|
|
|
/* Make copies of the username and password for passing to the PWL APIs.
|
|
* They need to be in OEM (PWL is accessible from DOS), and must be upper
|
|
* case since the Windows logon dialog uppercases all PWL passwords.
|
|
*/
|
|
NLS_STR nlsPWLName(pszSupervisorName);
|
|
NLS_STR nlsPWLPassword(pszSupervisorPassword);
|
|
if (nlsPWLName.QueryError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(nlsPWLName.QueryError());
|
|
if (nlsPWLPassword.QueryError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(nlsPWLPassword.QueryError());
|
|
nlsPWLName.strupr();
|
|
nlsPWLName.ToOEM();
|
|
nlsPWLPassword.strupr();
|
|
nlsPWLPassword.ToOEM();
|
|
|
|
HPWL hPWL = NULL;
|
|
APIERR err = ::OpenPasswordCache(&hPWL, nlsPWLName.QueryPch(),
|
|
nlsPWLPassword.QueryPch(), TRUE);
|
|
|
|
if (err != ERROR_SUCCESS) {
|
|
if (err != IERR_IncorrectUsername)
|
|
err = ::CreatePasswordCache(&hPWL, nlsPWLName.QueryPch(), nlsPWLPassword.QueryPch());
|
|
if (err != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(err);
|
|
}
|
|
|
|
|
|
/* Now that the system has a supervisor password, call a worker function
|
|
* to clone the supervisor account from the default profile. The worker
|
|
* function assumes that the caller has validated that the current user is
|
|
* a supervisor.
|
|
*/
|
|
|
|
err = ::MakeSupervisor(hPWL, pszRatingsPassword);
|
|
::ClosePasswordCache(hPWL, TRUE);
|
|
if (err != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(err);
|
|
|
|
IUser *pSupervisor = NULL;
|
|
hres = GetUser(pszSupervisorName, &pSupervisor);
|
|
if (FAILED(hres)) {
|
|
hres = CreateUser(pszSupervisorName, NULL, TRUE, pInit);
|
|
if (pSupervisor != NULL) {
|
|
pSupervisor->Release();
|
|
pSupervisor = NULL;
|
|
}
|
|
if (SUCCEEDED(hres))
|
|
hres = GetUser(pszSupervisorName, &pSupervisor); /* reinitialize with created profile */
|
|
}
|
|
|
|
if (pSupervisor != NULL) {
|
|
if (SUCCEEDED(hres))
|
|
hres = pSupervisor->Authenticate(pszSupervisorPassword);
|
|
if (SUCCEEDED(hres))
|
|
hres = SetCurrentUser(pSupervisor);
|
|
if (SUCCEEDED(hres))
|
|
pSupervisor->SetSupervisorPrivilege(TRUE, pszRatingsPassword); /* set appears-supervisor flag */
|
|
|
|
pSupervisor->Release();
|
|
pSupervisor = NULL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/* Some install stubs are "clone-user" install stubs, that get re-run if a
|
|
* profile is cloned to become a new user's profile. For example, if you
|
|
* clone Fred to make Barney, Outlook Express doesn't want Barney to inherit
|
|
* Fred's mailbox.
|
|
*
|
|
* When you run the go-multiuser wizard, we assume that the first user being
|
|
* created is the one who's been using the machine all along, so that one
|
|
* copy should be exempt from this. So we go through all the install stub
|
|
* keys for the newly created profile and, for any that are marked with a
|
|
* username (even a blank one indicates that it's a clone-user install stub),
|
|
* we mark it with the new username so it won't get re-run.
|
|
*/
|
|
void FixInstallStubs(LPCSTR pszName, HKEY hkeyProfile)
|
|
{
|
|
HKEY hkeyList;
|
|
LONG err = RegOpenKeyEx(hkeyProfile, "Software\\Microsoft\\Active Setup\\Installed Components", 0,
|
|
KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hkeyList);
|
|
|
|
if (err == ERROR_SUCCESS) {
|
|
DWORD cbKeyName, iKey;
|
|
TCHAR szKeyName[80];
|
|
|
|
/* Enumerate components that are installed for the profile. */
|
|
for (iKey = 0; ; iKey++)
|
|
{
|
|
LONG lEnum;
|
|
|
|
cbKeyName = ARRAYSIZE(szKeyName);
|
|
|
|
if ((lEnum = RegEnumKey(hkeyList, iKey, szKeyName, cbKeyName)) == ERROR_MORE_DATA)
|
|
{
|
|
// ERROR_MORE_DATA means the value name or data was too large
|
|
// skip to the next item
|
|
continue;
|
|
}
|
|
else if( lEnum != ERROR_SUCCESS )
|
|
{
|
|
// could be ERROR_NO_MORE_ENTRIES, or some kind of failure
|
|
// we can't recover from any other registry problem, anyway
|
|
break;
|
|
}
|
|
|
|
HKEY hkeyComponent;
|
|
if (RegOpenKeyEx(hkeyList, szKeyName, 0,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyComponent) == ERROR_SUCCESS) {
|
|
cbKeyName = sizeof(szKeyName);
|
|
err = RegQueryValueEx(hkeyComponent, "Username", NULL, NULL,
|
|
(LPBYTE)szKeyName, &cbKeyName);
|
|
if (err == ERROR_SUCCESS || err == ERROR_MORE_DATA) {
|
|
RegSetValueEx(hkeyComponent, "Username",
|
|
0, REG_SZ,
|
|
(LPBYTE)pszName,
|
|
lstrlen(pszName)+1);
|
|
}
|
|
RegCloseKey(hkeyComponent);
|
|
}
|
|
}
|
|
RegCloseKey(hkeyList);
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::CreateUser(LPCSTR pszName, IUser *pCloneFrom,
|
|
BOOL fFixInstallStubs, IUserProfileInit *pInit)
|
|
{
|
|
if (::strlenf(pszName) > cchMaxUsername)
|
|
return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
|
|
RegEntry reRoot(::szProfileList, HKEY_LOCAL_MACHINE);
|
|
if (reRoot.GetError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(reRoot.GetError());
|
|
|
|
/* See if the user's subkey exists. If it doesn't, create it. */
|
|
reRoot.MoveToSubKey(pszName);
|
|
if (reRoot.GetError() != ERROR_SUCCESS) {
|
|
RegEntry reUser(pszName, reRoot.GetKey());
|
|
if (reUser.GetError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(reUser.GetError());
|
|
|
|
reRoot.MoveToSubKey(pszName);
|
|
if (reRoot.GetError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(reRoot.GetError());
|
|
}
|
|
|
|
NLS_STR nlsProfilePath(MAX_PATH);
|
|
if (nlsProfilePath.QueryError() != ERROR_SUCCESS)
|
|
return E_OUTOFMEMORY;
|
|
|
|
reRoot.GetValue(::szProfileImagePath, &nlsProfilePath);
|
|
|
|
/* If the profile path is already recorded for the user, see if the
|
|
* profile itself exists. If it does, then CreateUser is an error.
|
|
*/
|
|
BOOL fComputePath = FALSE;
|
|
|
|
if (reRoot.GetError() == ERROR_SUCCESS) {
|
|
if (!DirExists(nlsProfilePath.QueryPch())) {
|
|
if (!::CreateDirectory(nlsProfilePath.QueryPch(), NULL)) {
|
|
fComputePath = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fComputePath = TRUE;
|
|
}
|
|
|
|
if (fComputePath) {
|
|
ComputeLocalProfileName(pszName, &nlsProfilePath);
|
|
reRoot.SetValue(::szProfileImagePath, nlsProfilePath.QueryPch());
|
|
}
|
|
|
|
AddBackslash(nlsProfilePath);
|
|
nlsProfilePath.strcat(::szStdNormalProfile);
|
|
if (FileExists(nlsProfilePath.QueryPch()))
|
|
return HRESULT_FROM_WIN32(ERROR_USER_EXISTS);
|
|
|
|
/* The user's profile directory now exists, and its path is recorded
|
|
* in the registry. nlsProfilePath is now the full pathname for the
|
|
* user's profile file, which does not exist yet.
|
|
*/
|
|
|
|
NLS_STR nlsOtherProfilePath(MAX_PATH);
|
|
if (nlsOtherProfilePath.QueryError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(nlsOtherProfilePath.QueryError());
|
|
|
|
HRESULT hres;
|
|
DWORD cbPath = nlsOtherProfilePath.QueryAllocSize();
|
|
if (pCloneFrom == NULL ||
|
|
FAILED(pCloneFrom->GetProfileDirectory(nlsOtherProfilePath.Party(), &cbPath)))
|
|
{
|
|
/* Cloning default profile. */
|
|
|
|
hres = GiveUserDefaultProfile(nlsProfilePath.QueryPch());
|
|
nlsOtherProfilePath.DonePartying();
|
|
nlsOtherProfilePath = "";
|
|
}
|
|
else {
|
|
/* Cloning other user's profile. */
|
|
nlsOtherProfilePath.DonePartying();
|
|
AddBackslash(nlsOtherProfilePath);
|
|
nlsOtherProfilePath.strcat(::szStdNormalProfile);
|
|
hres = CopyProfile(nlsOtherProfilePath.QueryPch(), nlsProfilePath.QueryPch());
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
return hres;
|
|
|
|
/* Now the user has a profile. Load it and perform directory
|
|
* reconciliation.
|
|
*/
|
|
|
|
LONG err = ::MyRegLoadKey(HKEY_USERS, pszName, nlsProfilePath.QueryPch());
|
|
if (err == ERROR_SUCCESS) {
|
|
HKEY hkeyNewProfile;
|
|
err = ::RegOpenKey(HKEY_USERS, pszName, &hkeyNewProfile);
|
|
if (err == ERROR_SUCCESS) {
|
|
|
|
/* Build just the profile directory, no "user.dat" on the end. */
|
|
ISTR istrBackslash(nlsProfilePath);
|
|
if (nlsProfilePath.strrchr(&istrBackslash, '\\')) {
|
|
++istrBackslash;
|
|
nlsProfilePath.DelSubStr(istrBackslash);
|
|
}
|
|
|
|
if (pInit != NULL) {
|
|
hres = pInit->PreInitProfile(hkeyNewProfile, nlsProfilePath.QueryPch());
|
|
if (hres == E_NOTIMPL)
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
hres = S_OK;
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
err = ReconcileFiles(hkeyNewProfile, nlsProfilePath, nlsOtherProfilePath); /* modifies nlsProfilePath */
|
|
hres = HRESULT_FROM_WIN32(err);
|
|
|
|
if (fFixInstallStubs) {
|
|
::FixInstallStubs(pszName, hkeyNewProfile);
|
|
}
|
|
|
|
if (pInit != NULL) {
|
|
hres = pInit->PostInitProfile(hkeyNewProfile, nlsProfilePath.QueryPch());
|
|
if (hres == E_NOTIMPL)
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
::RegFlushKey(hkeyNewProfile);
|
|
::RegCloseKey(hkeyNewProfile);
|
|
}
|
|
::RegUnLoadKey(HKEY_USERS, pszName);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::AddUser(LPCSTR pszName, IUser *pSourceUser,
|
|
IUserProfileInit *pInit, IUser **ppOut)
|
|
{
|
|
if (ppOut != NULL)
|
|
*ppOut = NULL;
|
|
|
|
if (IsCurrentUserSupervisor(this) != S_OK)
|
|
return E_ACCESSDENIED;
|
|
|
|
HRESULT hres = CreateUser(pszName, pSourceUser, FALSE, pInit);
|
|
if (FAILED(hres))
|
|
return hres;
|
|
|
|
if (ppOut != NULL)
|
|
hres = GetUser(pszName, ppOut);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::GetUser(LPCSTR pszName, IUser **ppOut)
|
|
{
|
|
*ppOut = NULL;
|
|
|
|
CLUUser *pUser = new CLUUser(this);
|
|
|
|
if (pUser == NULL) {
|
|
return ResultFromScode(E_OUTOFMEMORY);
|
|
}
|
|
|
|
HRESULT err = pUser->Init(pszName);
|
|
if (SUCCEEDED(err) && !pUser->Exists()) {
|
|
err = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
|
|
}
|
|
|
|
if (FAILED(err) || !pUser->Exists()) {
|
|
pUser->Release();
|
|
return err;
|
|
}
|
|
|
|
*ppOut = pUser;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::GetSpecialUser(DWORD nSpecialUserCode, IUser **ppOut)
|
|
{
|
|
switch (nSpecialUserCode) {
|
|
case GSU_CURRENT:
|
|
return GetCurrentUser(ppOut);
|
|
break;
|
|
|
|
case GSU_DEFAULT:
|
|
return GetUser(szDefaultUserName, ppOut);
|
|
break;
|
|
|
|
default:
|
|
return ResultFromScode(E_INVALIDARG);
|
|
};
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
HRESULT GetSystemCurrentUser(NLS_STR *pnlsCurrentUser)
|
|
{
|
|
DWORD cbBuffer = pnlsCurrentUser->QueryAllocSize();
|
|
UINT err;
|
|
if (!::GetUserName(pnlsCurrentUser->Party(), &cbBuffer))
|
|
err = ::GetLastError();
|
|
else
|
|
err = NOERROR;
|
|
pnlsCurrentUser->DonePartying();
|
|
|
|
return HRESULT_FROM_WIN32(err);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::GetCurrentUser(IUser **ppOut)
|
|
{
|
|
if (m_CurrentUser == NULL) {
|
|
NLS_STR nlsCurrentUser(cchMaxUsername+1);
|
|
UINT err = nlsCurrentUser.QueryError();
|
|
if (err)
|
|
return HRESULT_FROM_WIN32(err);
|
|
|
|
HRESULT hres = GetSystemCurrentUser(&nlsCurrentUser);
|
|
if (FAILED(hres))
|
|
return hres;
|
|
|
|
hres = GetUser(nlsCurrentUser.QueryPch(), (IUser **)&m_CurrentUser);
|
|
if (FAILED(hres))
|
|
return hres;
|
|
}
|
|
|
|
*ppOut = m_CurrentUser;
|
|
m_CurrentUser->AddRef();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::SetCurrentUser(IUser *pUser)
|
|
{
|
|
CLUUser *pCLUUser = (CLUUser *)pUser;
|
|
HPWL hpwlUser;
|
|
if (!pCLUUser->m_fAuthenticated ||
|
|
FAILED(pCLUUser->GetPasswordCache(pCLUUser->m_nlsPassword.QueryPch(), &hpwlUser)))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_AUTHENTICATED);
|
|
}
|
|
::ClosePasswordCache(hpwlUser, TRUE);
|
|
|
|
CLUUser *pClone;
|
|
|
|
HRESULT hres = GetUser(pCLUUser->m_nlsUsername.QueryPch(), (IUser **)&pClone);
|
|
if (FAILED(hres))
|
|
return hres;
|
|
|
|
/* Make sure the clone object is authenticated properly. */
|
|
hres = pClone->Authenticate(pCLUUser->m_nlsPassword.QueryPch());
|
|
if (FAILED(hres)) {
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_AUTHENTICATED);
|
|
}
|
|
|
|
if (m_CurrentUser != NULL) {
|
|
m_CurrentUser->Release();
|
|
}
|
|
|
|
m_CurrentUser = pClone;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::DeleteUser(LPCSTR pszName)
|
|
{
|
|
NLS_STR nlsName(MAX_PATH);
|
|
if (nlsName.QueryError() != ERROR_SUCCESS)
|
|
return HRESULT_FROM_WIN32(nlsName.QueryError());
|
|
|
|
/* Check supervisor privilege up front, this'll handle the not-logged-on
|
|
* case later if we re-enable supervisor stuff.
|
|
*/
|
|
if (IsCurrentUserSupervisor(this) != S_OK)
|
|
return E_ACCESSDENIED;
|
|
|
|
IUser *pCurrentUser;
|
|
|
|
HRESULT hres = GetCurrentUser(&pCurrentUser);
|
|
if (SUCCEEDED(hres)) {
|
|
|
|
/* Check current user's name and make sure we're not deleting him.
|
|
* Note that because the current user must be an authenticated supervisor,
|
|
* and you can't delete the current user, you can never delete the last
|
|
* supervisor using this function.
|
|
*/
|
|
DWORD cb = nlsName.QueryAllocSize();
|
|
hres = pCurrentUser->GetName(nlsName.Party(), &cb);
|
|
nlsName.DonePartying();
|
|
if (SUCCEEDED(hres) && !::stricmpf(pszName, nlsName.QueryPch()))
|
|
hres = HRESULT_FROM_WIN32(ERROR_BUSY);
|
|
|
|
if (FAILED(hres))
|
|
return hres;
|
|
}
|
|
|
|
/* Check system's idea of current user as well. */
|
|
|
|
hres = GetSystemCurrentUser(&nlsName);
|
|
if (SUCCEEDED(hres)) {
|
|
if (!::stricmpf(pszName, nlsName.QueryPch()))
|
|
return HRESULT_FROM_WIN32(ERROR_BUSY);
|
|
}
|
|
|
|
return DeleteProfile(pszName);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::RenameUser(LPCSTR pszOldName, LPCSTR pszNewName)
|
|
{
|
|
return ResultFromScode(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::EnumUsers(IEnumUnknown **ppOut)
|
|
{
|
|
*ppOut = NULL;
|
|
|
|
CLUEnum *pEnum = new CLUEnum(this);
|
|
|
|
if (pEnum == NULL) {
|
|
return ResultFromScode(E_OUTOFMEMORY);
|
|
}
|
|
|
|
HRESULT err = pEnum->Init();
|
|
if (FAILED(err)) {
|
|
pEnum->Release();
|
|
return err;
|
|
}
|
|
|
|
*ppOut = pEnum;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::Authenticate(HWND hwndOwner, DWORD dwFlags,
|
|
LPCSTR pszName, LPCSTR pszPassword,
|
|
IUser **ppOut)
|
|
{
|
|
if (dwFlags & LUA_DIALOG) {
|
|
if (!UseUserProfiles() || FAILED(VerifySupervisorPassword(szNULL))) {
|
|
return InstallWizard(hwndOwner);
|
|
}
|
|
return ::DoUserDialog(hwndOwner, dwFlags, ppOut);
|
|
}
|
|
|
|
/* Null out return pointer for error cases. */
|
|
if (ppOut != NULL)
|
|
*ppOut = NULL;
|
|
|
|
IUser *pUser;
|
|
BOOL fReleaseMe = TRUE;
|
|
|
|
HRESULT hres = GetUser(pszName, &pUser);
|
|
if (SUCCEEDED(hres)) {
|
|
hres = pUser->Authenticate(pszPassword);
|
|
if (SUCCEEDED(hres)) {
|
|
if ((dwFlags & LUA_SUPERVISORONLY) && (pUser->IsSupervisor() != S_OK)) {
|
|
hres = E_ACCESSDENIED;
|
|
}
|
|
else if (ppOut != NULL) {
|
|
*ppOut = pUser;
|
|
fReleaseMe = FALSE;
|
|
}
|
|
}
|
|
if (fReleaseMe)
|
|
pUser->Release();
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::InstallComponent(REFCLSID clsidComponent,
|
|
LPCSTR pszName, DWORD dwFlags)
|
|
{
|
|
return ResultFromScode(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLUDatabase::RemoveComponent(REFCLSID clsidComponent, LPCSTR pszName)
|
|
{
|
|
return ResultFromScode(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
#ifdef MSLOCUSR_USE_SUPERVISOR_PASSWORD
|
|
|
|
HRESULT IsCurrentUserSupervisor(IUserDatabase *pDB)
|
|
{
|
|
IUser *pCurrentUser = NULL;
|
|
|
|
HRESULT hres = pDB->GetCurrentUser(&pCurrentUser);
|
|
if (SUCCEEDED(hres)) {
|
|
hres = pCurrentUser->IsSupervisor();
|
|
}
|
|
if (pCurrentUser != NULL) {
|
|
pCurrentUser->Release();
|
|
}
|
|
return hres;
|
|
}
|
|
#else
|
|
HRESULT IsCurrentUserSupervisor(IUserDatabase *pDB) { return S_OK; }
|
|
#endif
|