#include "stdafx.h"

#include "resource.h"
#include "ImpersonateUser.hpp"

#include <assert.h>


CImpersonateUser::CImpersonateUser()
	: userToken_(NULL)
	, bImpersonate_(false)
	, pLogger_(NULL)
{
}

/*!
 * 偽装されていれば偽装解除し、ログオンされていればログオフします。
 * そうでなければ何もしません。
 */
CImpersonateUser::~CImpersonateUser() throw()
{
	revertToSelf();
	logoff();
}

	/*!
 * ログオンします。
 * すでにログオンされていてはなりません。
 * ログインに失敗した場合はCImpersonateUserException例外が発生します。
 * \param userName ユーザ名
 * \param domain ドメイン、空可
 * \param password パスワード、空可
 */
void CImpersonateUser::logon(const tstring& user, const tstring& domain, const tstring& password)
{
	assert( !userToken_ && "すでにログオンされています。");
	assert( !bImpersonate_ && "すでに偽装されています。");

	if (userToken_ || user.empty()) {
		::SetLastError(ERROR_BAD_ARGUMENTS);
		throw CImpersonateUserException(ERROR_BAD_ARGUMENTS);
	}

	bImpersonate_ = false;

	DWORD dwLogonType = LOGON32_LOGON_INTERACTIVE;
	DWORD dwLogonProvider = LOGON32_PROVIDER_DEFAULT;
	//if (info_.impersonateDomain_.empty()) {
	//	dwLogonType = LOGON32_LOGON_NEW_CREDENTIALS;
	//	dwLogonProvider = LOGON32_PROVIDER_WINNT50;
	//}

	if ( !LogonUser(
		user.c_str(),
		domain.c_str(),
		password.c_str(),
		dwLogonType,
		dwLogonProvider,
		&userToken_)) {
		// ログインに失敗
		userToken_ = NULL;

		// ログイン失敗のログ
		DWORD err = GetLastError();
		if (pLogger_) {
			pLogger_->LogMessage(0, NULL, err, IDS_LOGMES_IMPERSONATE_LOGIN_FAILED, user.c_str(), domain.c_str());
		}
		throw CImpersonateUserException(err);
	}

	assert(userToken_ && "ログオンユーザハンドルがnullです。");

	// ログインに成功
	user_ = user;
	domain_ = domain;

	if (pLogger_) {
		pLogger_->LogMessage(10, NULL, 0, _T("logonUser succeeded"));
	}
}

void CImpersonateUser::impersonate()
{
	assert(userToken_ && "ログオンされていません。");
	assert( !bImpersonate_ && "すでに偽装されています。");

	if ( !userToken_ || bImpersonate_) {
		::SetLastError(ERROR_BAD_ARGUMENTS);
		throw CImpersonateUserException(ERROR_BAD_ARGUMENTS);
	}

	if ( !ImpersonateLoggedOnUser(userToken_)) {
		DWORD err = GetLastError();
		// 偽装失敗のログ
		if (pLogger_) {
			pLogger_->LogMessage(0, NULL, err, IDS_LOGMES_IMPERSONATE_FAILED, user_.c_str(), domain_.c_str());
		}
		throw CImpersonateUserException(err);
	}

	bImpersonate_ = true;

	// 偽装成功のログ
	if (pLogger_) {
		pLogger_->LogMessage(4, NULL, 0, IDS_LOGMES_IMPERSONATE, user_.c_str(), domain_.c_str());
	}
}

bool CImpersonateUser::revertToSelf() throw()
{
	if ( !userToken_ || !bImpersonate_) {
		::SetLastError(NO_ERROR);
		return false;
	}

	bImpersonate_ = false;

	if ( !::RevertToSelf()) {
		// 偽装解除に失敗
		DWORD err = ::GetLastError();
		if (pLogger_) {
			pLogger_->LogMessage(0, NULL, err, IDS_LOGMES_IMPERSONATE_REVERT_FAILED, user_.c_str(), domain_.c_str());
		}
		::SetLastError(err);
		return false;
	}

	// 偽装解除のログ
	if (pLogger_) {
		pLogger_->LogMessage(4, NULL, 0, IDS_LOGMES_IMPERSONATE_REVERT, user_.c_str(), domain_.c_str());
	}
	return true;
}

bool CImpersonateUser::logoff() throw()
{
	// ログオンされていなければ何もしない。
	if (!userToken_) {
		return true;
	}

	// 偽装を解除する。(偽装されていれば。)
	revertToSelf();

	if (!CloseHandle(userToken_)) {
		// ログオフに失敗。
		return false;
	}

	userToken_ = NULL;
	if (pLogger_) {
		pLogger_->LogMessage(10, NULL, 0, _T("logonUser closed"));
	}
	return true;
}
