/*
  KeePass Password Safe - The Open-Source Password Manager
  Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "StdAfx.h"
#include "PwSafe.h"
#include "PwSafeDlg.h"
#include "PasswordDlg.h"

#include "../KeePassLibCpp/Util/MemUtil.h"
#include "../KeePassLibCpp/Util/StrUtil.h"
#include "../KeePassLibCpp/Util/Base64.h"
#include "../KeePassLibCpp/Util/PwQualityEst.h"
#include "../KeePassLibCpp/Util/PwUtil.h"
#include "../KeePassLibCpp/Util/TranslateEx.h"
#include "Util/WinUtil.h"
#include "Util/PrivateConfigEx.h"
#include "Util/KeySourcesPool.h"
#include "NewGUI/FontUtil.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#pragma warning(push)
// Cast truncates constant value
#pragma warning(disable: 4310)

/////////////////////////////////////////////////////////////////////////////

CPasswordDlg::CPasswordDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CPasswordDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CPasswordDlg)
	m_bStars = TRUE;
	m_bKeyMethod = FALSE;
	//}}AFX_DATA_INIT

	m_bLoadMode = TRUE;
	m_bConfirm = FALSE;
	m_bChanging = FALSE;
	m_strDatabasePath = _T("");
	m_strDescriptiveName = _T("");
	m_bOnce = FALSE;
	m_hWindowIcon = NULL;
	m_lpPreSelectPath = m_lpKey = m_lpKey2 = NULL;
}

void CPasswordDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CPasswordDlg)
	DDX_Control(pDX, IDC_COMBO_DISKLIST, m_cbDiskList);
	DDX_Control(pDX, IDC_PROGRESS_PASSQUALITY, m_cPassQuality);
	DDX_Control(pDX, IDC_CHECK_STARS, m_btStars);
	DDX_Control(pDX, IDOK, m_btOK);
	DDX_Control(pDX, IDCANCEL, m_btCancel);
	DDX_Control(pDX, IDC_PWDLG_HELP_BTN, m_btHelp);
	DDX_Control(pDX, IDC_EDIT_PASSWORD, m_pEditPw);
	DDX_Check(pDX, IDC_CHECK_STARS, m_bStars);
	DDX_Check(pDX, IDC_CHECK_KEYMETHOD_AND, m_bKeyMethod);
	DDX_Control(pDX, IDC_BTN_BROWSE_KEYFILE, m_btBrowseKeyFile);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CPasswordDlg, CDialog)
	//{{AFX_MSG_MAP(CPasswordDlg)
	ON_BN_CLICKED(IDC_CHECK_STARS, OnCheckStars)
	ON_BN_CLICKED(IDC_PWDLG_HELP_BTN, OnHelpBtn)
	ON_EN_CHANGE(IDC_EDIT_PASSWORD, OnChangeEditPassword)
	ON_CBN_SELCHANGE(IDC_COMBO_DISKLIST, OnSelChangeComboDiskList)
	ON_BN_CLICKED(IDC_CHECK_KEYMETHOD_AND, OnCheckKeymethodAnd)
	ON_BN_CLICKED(IDC_BTN_BROWSE_KEYFILE, OnBnClickedBrowseKeyFile)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

BOOL CPasswordDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	ASSERT(m_bOnce == FALSE);

	m_bStarsMask = ((CPwSafeDlg::m_bDisableUnsafeAtStart == TRUE) ? FALSE : TRUE);

	if(m_bLoadMode == TRUE)
		ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_APPWINDOW); // Show in taskbar

	ASSERT(m_hWindowIcon != NULL);
	if(m_hWindowIcon != NULL)
	{
		SetIcon(m_hWindowIcon, TRUE);
		SetIcon(m_hWindowIcon, FALSE);
	}

	EnumChildWindows(this->m_hWnd, NewGUI_TranslateWindowCb, 0);

	// m_fStyle.CreateFont(NewGUI_Scale(-12, this), 0, 0, 0, 0, FALSE, FALSE, 0,
	//	DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
	//	DEFAULT_QUALITY, DEFAULT_PITCH | FF_MODERN, _T("Tahoma"));
	// m_fSymbol.CreateFont(NewGUI_Scale(-13, this), 0, 0, 0, 0, FALSE, FALSE, 0,
	//	DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
	//	DEFAULT_QUALITY, DEFAULT_PITCH | FF_MODERN, CPwSafeApp::GetPasswordFont());

	NewGUI_XPButton(m_btOK, IDB_OK, IDB_OK);
	NewGUI_XPButton(m_btCancel, IDB_CANCEL, IDB_CANCEL);
	NewGUI_XPButton(m_btHelp, IDB_HELP_SMALL, IDB_HELP_SMALL);
	NewGUI_XPButton(m_btStars, -1, -1);

	m_pEditPw.InitEx();

	CFontUtil::SetDefaultFontFrom(GetDlgItem(IDC_STATIC_ENTERPW));

	// m_btStars.SetFont(&m_fSymbol, TRUE);
	// m_pEditPw.SetFont(&m_fSymbol, TRUE);
	CFontUtil::AssignSymbol(&m_btStars, this);
	CFontUtil::AssignSymbol(&m_pEditPw, this);

	// LOGFONT lf;
	// CFont* pDialogFont = GetDlgItem(IDC_STATIC_ENTERPW)->GetFont();
	// pDialogFont->GetLogFont(&lf);
	// m_fBold.CreateFont(lf.lfHeight, lf.lfWidth, lf.lfEscapement, lf.lfOrientation,
	//	FW_BOLD, lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut,
	//	lf.lfCharSet, lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,
	//	lf.lfPitchAndFamily, lf.lfFaceName);

	// GetDlgItem(IDC_STATIC_ENTERPW)->SetFont(&m_fBold);
	// GetDlgItem(IDC_STATIC_SELDISK)->SetFont(&m_fBold);
	CFontUtil::AssignBold(GetDlgItem(IDC_STATIC_ENTERPW), this);
	CFontUtil::AssignBold(GetDlgItem(IDC_STATIC_SELDISK), this);

	m_btStars.SetColor(CButtonST::BTNST_COLOR_FG_IN, RGB(0, 0, 255), TRUE);
	m_btStars.SetTooltipText(TRL("Hide passwords behind asterisks (***)."), TRUE);

	NewGUI_ConfigQualityMeter(&m_cPassQuality);

	// m_ilIcons.Create(IDR_INFOICONS, 16, 1, RGB(255,0,255)); // Purple is transparent
	CPwSafeApp::CreateHiColorImageList(&m_ilIcons, IDB_INFOICONS_EX, 16);
	m_cbDiskList.SetImageList(&m_ilIcons);

	COMBOBOXEXITEM cbi;
	ZeroMemory(&cbi, sizeof(COMBOBOXEXITEM));
	cbi.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_INDENT | CBEIF_SELECTEDIMAGE;
	cbi.iItem = 0;
	cbi.pszText = (LPTSTR)TRL("(No key file selected)");
	cbi.cchTextMax = (int)_tcslen(cbi.pszText);
	cbi.iImage = cbi.iSelectedImage = ICOIDX_NODRIVE;
	cbi.iIndent = 0;
	m_cbDiskList.InsertItem(&cbi);

	int i;
	int j = 1;
	
	for(i = 0; i < 26; i++)
	{
		const TCHAR c = (TCHAR)(i + _T('A'));
		CString str = CString(c) + _T(":\\");

		const UINT uStat = GetDriveType((LPCTSTR)str);
		if(uStat != DRIVE_NO_ROOT_DIR)
		{
			BYTE idxImage = 0;
			if(uStat == DRIVE_REMOVABLE) idxImage = ICOIDX_REMOVABLE;
			if(uStat == DRIVE_FIXED) idxImage = ICOIDX_FIXED;
			if(uStat == DRIVE_REMOTE) idxImage = ICOIDX_REMOTE;
			if(uStat == DRIVE_CDROM) idxImage = ICOIDX_CDROM;
			if(uStat == DRIVE_RAMDISK) idxImage = ICOIDX_RAMDISK;

			str += PWS_DEFAULT_KEY_FILENAME;

			ZeroMemory(&cbi, sizeof(COMBOBOXEXITEM));
			cbi.mask = (CBEIF_IMAGE | CBEIF_TEXT | CBEIF_INDENT | CBEIF_SELECTEDIMAGE);
			cbi.iItem = j; j++;
			cbi.pszText = const_cast<LPTSTR>((LPCTSTR)str);
			cbi.cchTextMax = (int)_tcslen(cbi.pszText);
			cbi.iImage = cbi.iSelectedImage = (int)idxImage;
			cbi.iIndent = 0;
			m_cbDiskList.InsertItem(&cbi);
		}
	}

	QueryKeyProviders();
	for(size_t iProv = 0; iProv < m_vKeyProv.size(); ++iProv)
	{
		const KP_KEYPROV_INFO_CPP& keyProv = m_vKeyProv[iProv];

		ZeroMemory(&cbi, sizeof(COMBOBOXEXITEM));
		cbi.mask = (CBEIF_IMAGE | CBEIF_TEXT | CBEIF_INDENT | CBEIF_SELECTEDIMAGE);
		cbi.iItem = j; ++j;
		cbi.pszText = const_cast<TCHAR *>(keyProv.strName.c_str());
		cbi.cchTextMax = static_cast<int>(keyProv.strName.size());
		cbi.iImage = cbi.iSelectedImage = static_cast<int>(keyProv.dwImageIndex);
		cbi.iIndent = 0;
		m_cbDiskList.InsertItem(&cbi);
	}

	CString strStaticKeyFile = TRL("Key File");
	if(m_vKeyProv.size() > 0)
	{
		strStaticKeyFile += _T(" / ");
		strStaticKeyFile += TRL("Provider");
	}
	strStaticKeyFile += _T(":");
	GetDlgItem(IDC_STATIC_SELDISK)->SetWindowText(strStaticKeyFile);

	m_cbDiskList.SetCurSel(0);

	DWORD dw = m_cbDiskList.GetExtendedStyle();
	dw &= ~CBES_EX_NOEDITIMAGE;
	m_cbDiskList.SetExtendedStyle(0, dw);

	NewGUI_ConfigSideBanner(&m_banner, this);
	if(m_bLoadMode == FALSE)
		m_banner.SetIcon(AfxGetApp()->LoadIcon(IDI_KEY),
			KCSB_ICON_LEFT | KCSB_ICON_VCENTER);
	else
		m_banner.SetIcon(AfxGetApp()->LoadIcon(IDI_KEYHOLE),
			KCSB_ICON_LEFT | KCSB_ICON_VCENTER);

	m_btBrowseKeyFile.SetWindowText(_T(""));

	LPCTSTR lp;
	if(m_bConfirm == FALSE)
	{
		if(m_bLoadMode == FALSE)
		{
			NewGUI_XPButton(m_btBrowseKeyFile, IDB_TB_SAVE, IDB_TB_SAVE, TRUE);
			lp = TRL("Save key file manually to...");
			m_btBrowseKeyFile.SetTooltipText(lp);

			CString str = TRL("Set Master Key");
			if(m_strDescriptiveName.GetLength() != 0)
				str = m_strDescriptiveName;
			m_banner.SetTitle(str);

			if(m_bChanging == FALSE)
			{
				SetWindowText(TRL("Create New Database"));
				m_banner.SetCaption(TRL("Specify the master key."));
			}
			else
			{
				str = TRL("Change Master Key");
				if(m_strDescriptiveName.GetLength() != 0)
				{
					str += _T(" - ");
					str += m_strDescriptiveName;
				}
				SetWindowText(str);

				m_banner.SetCaption(TRL("Specify the new master key."));
			}
		}
		else // m_bConfirm == FALSE, m_bLoadMode == TRUE
		{
			NewGUI_XPButton(m_btBrowseKeyFile, IDB_TB_OPEN, IDB_TB_OPEN, TRUE);
			lp = TRL("Select key file manually...");
			m_btBrowseKeyFile.SetTooltipText(lp);

			CString str = TRL("Enter Master Key");
			if(m_strDescriptiveName.GetLength() != 0)
				str = m_strDescriptiveName;
			m_banner.SetTitle(str);

			str = TRL("Open Database");
			if(m_strDescriptiveName.GetLength() != 0)
			{
				str += _T(" - ");
				str += m_strDescriptiveName;
			}
			SetWindowText(str);

			m_banner.SetCaption(TRL("Enter the master key."));

			m_cPassQuality.ShowWindow(SW_HIDE);
			GetDlgItem(IDC_STATIC_PWQINFO)->ShowWindow(SW_HIDE);
		}
	}
	else // m_bConfirm == TRUE
	{
		if(m_bLoadMode == FALSE)
		{
			GetDlgItem(IDC_STATIC_SELDISK)->ShowWindow(SW_HIDE);
			m_cbDiskList.ShowWindow(SW_HIDE);
			m_btBrowseKeyFile.ShowWindow(SW_HIDE);

			if(m_bChanging == FALSE)
			{
				SetWindowText(TRL("Create New Database"));
				m_banner.SetCaption(TRL("Repeat the master password for the new database."));
			}
			else
			{
				SetWindowText(TRL("Repeat Master Password"));
				m_banner.SetCaption(TRL("Repeat the new master password for this database."));
			}
			m_banner.SetTitle(TRL("Repeat Master Password"));

			m_cPassQuality.ShowWindow(SW_HIDE);
			GetDlgItem(IDC_STATIC_PWQINFO)->ShowWindow(SW_HIDE);

			GetDlgItem(IDC_CHECK_KEYMETHOD_AND)->ShowWindow(SW_HIDE);
		}
		else
		{
			// This combination isn't possible
			ASSERT(FALSE);

			CString str = TRL("Enter Master Key");
			if(m_strDescriptiveName.GetLength() != 0)
				str = m_strDescriptiveName;
			m_banner.SetTitle(str);

			str = TRL("Open Database");
			if(m_strDescriptiveName.GetLength() != 0)
			{
				str += _T(" - ");
				str += m_strDescriptiveName;
			}
			SetWindowText(str);

			m_banner.SetCaption(TRL("Enter the master key."));
		}
	}

	m_tipSecClear.Create(this, TTS_BALLOON);
	m_tipSecClear.AddTool(&m_pEditPw, CPwSafeDlg::_GetSecureEditTipText(_T("Enter Password:")));
	m_tipSecClear.SetMaxTipWidth(630);
	m_tipSecClear.Activate(m_pEditPw.IsSecureModeEnabled());

	// Alternative password asterisk character: 0xB7 (smaller dot)
	// TCHAR tchDot = (TCHAR)(_T('z') + 27);
	TCHAR tchDot = CPwSafeApp::GetPasswordCharacter();
	CString strStars; strStars += tchDot; strStars += tchDot; strStars += tchDot;
	m_btStars.SetWindowText(strStars);
	m_bStars = TRUE;
	OnCheckStars();

	UpdateData(FALSE);

	NewGUI_ShowQualityMeter(&m_cPassQuality, GetDlgItem(IDC_STATIC_PWQINFO), _T(""));

	LPCTSTR lpSelect = m_lpPreSelectPath;
	std_string strSugg; // Data buffer, don't move inside 'if'
	if((lpSelect == NULL) && (m_strDatabasePath.GetLength() > 0))
	{
		CString strPathAbs = GetShortestAbsolutePath(m_strDatabasePath);
		strSugg = CKeySourcesPool::Get(strPathAbs);
		if(strSugg.size() > 0) lpSelect = strSugg.c_str();
	}

	if(lpSelect != NULL)
	{
		const int nSpecLen = static_cast<int>(_tcslen(lpSelect));
		BOOL bFound = FALSE;

		for(i = 0; i < m_cbDiskList.GetCount(); ++i)
		{
			CString strCBI;
			m_cbDiskList.GetLBText(i, strCBI);
			if(strCBI.GetLength() < nSpecLen) continue;

			if(_tcsnicmp((LPCTSTR)strCBI, lpSelect, nSpecLen) == 0)
			{
				m_cbDiskList.SetCurSel(i);
				OnSelChangeComboDiskList();
				bFound = TRUE;
				break;
			}
		}

		if(bFound == FALSE)
		{
			ZeroMemory(&cbi, sizeof(COMBOBOXEXITEM));
			cbi.mask = (CBEIF_IMAGE | CBEIF_TEXT | CBEIF_INDENT | CBEIF_SELECTEDIMAGE);
			cbi.iItem = m_cbDiskList.GetCount();
			cbi.pszText = const_cast<LPTSTR>(lpSelect);
			cbi.cchTextMax = (int)_tcslen(cbi.pszText);
			cbi.iImage = cbi.iSelectedImage = 28;
			cbi.iIndent = 0;
			const int nxInserted = m_cbDiskList.InsertItem(&cbi);

			m_cbDiskList.SetCurSel(nxInserted);
			OnSelChangeComboDiskList();
		}

		m_bKeyMethod = PWM_KEYMETHOD_AND;
		UpdateData(FALSE);
	}

	PerformMiniModeAdjustments();

	CPwSafeDlg::SetLastMasterKeyDlg(this->m_hWnd);
	EnableClientWindows();
	m_pEditPw.SetFocus();
	return FALSE; // Return TRUE unless you set the focus to a control
}

void CPasswordDlg::CleanUp()
{
	m_cbDiskList.ResetContent();
	m_ilIcons.DeleteImageList();
	// m_fStyle.DeleteObject();
	// m_fSymbol.DeleteObject();
	// m_fBold.DeleteObject();

	_CallPlugins(KPM_KEYPROV_FINALIZE, 0, 0);
}

void CPasswordDlg::PerformMiniModeAdjustments()
{
	if(CPwSafeDlg::m_bMiniMode == FALSE) return;

	NewGUI_DisableHideWnd(GetDlgItem(IDC_CHECK_KEYMETHOD_AND));
	NewGUI_DisableHideWnd(GetDlgItem(IDC_STATIC_SELDISK));
	NewGUI_DisableHideWnd(&m_cbDiskList);
	NewGUI_DisableHideWnd(&m_btBrowseKeyFile);
	NewGUI_DisableHideWnd(&m_btHelp);

	RECT rectTop, rectBottom;
	GetDlgItem(IDC_CHECK_KEYMETHOD_AND)->GetWindowRect(&rectTop);
	m_btOK.GetWindowRect(&rectBottom);

	long lMoveY = -(rectBottom.top - rectTop.top);
	// NewGUI_MoveWnd(&m_btHelp, 0, lMoveY, this);
	NewGUI_MoveWnd(&m_btOK, 0, lMoveY, this);
	NewGUI_MoveWnd(&m_btCancel, 0, lMoveY, this);

	NewGUI_Resize(this, 0, lMoveY, NULL);
}

void CPasswordDlg::FreePasswords()
{
	ASSERT(m_lpKey != NULL);

	if(m_lpKey != NULL)
	{
		CSecureEditEx::DeletePassword(m_lpKey);
		m_lpKey = NULL;
	}

	if(m_lpKey2 != NULL)
	{
		CSecureEditEx::DeletePassword(m_lpKey2);
		m_lpKey2 = NULL;
	}
}

void CPasswordDlg::OnOK() 
{
	UpdateData(TRUE);

	ASSERT((m_lpKey == NULL) && (m_lpKey2 == NULL));
	m_lpKey = m_pEditPw.GetPassword();

	if(m_bConfirm == FALSE)
	{
		const bool bPwEmpty = (_tcslen(m_lpKey) == 0);
		const bool bFileEmpty = (m_cbDiskList.GetCurSel() == 0);

		// Validate input
		if(m_bKeyMethod == PWM_KEYMETHOD_OR)
		{
			if(!(bPwEmpty ^ bFileEmpty))
			{
				MessageBox(TRL("EITHER enter a master password OR select a key file."),
					PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONINFORMATION);
				FreePasswords();

				m_pEditPw.SetFocus();
				return;
			}
		}
		else // m_bKeyMethod == PWM_KEYMETHOD_AND
		{
			if(bPwEmpty || bFileEmpty)
			{
				MessageBox(TRL("You've selected the AND key mode, so you must enter a password AND select a key file."),
					PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONINFORMATION);
				FreePasswords();

				if(bPwEmpty) m_pEditPw.SetFocus();
				else m_cbDiskList.SetFocus();
				return;
			}
		}
	}

	// Validate master password properties
	if((m_lpKey[0] != 0) && (m_bConfirm == FALSE) && (m_bLoadMode == FALSE))
	{
		CPrivateConfigEx cfg(FALSE);
		std::basic_string<TCHAR> strMinLen = cfg.GetSafe(PWMKEY_MASTERPW_MINLEN);
		std::basic_string<TCHAR> strMinQuality = cfg.GetSafe(PWMKEY_MASTERPW_MINQUALITY);
		CString strValMsg;

		if(strMinLen.size() > 0)
		{
			const long lMinLen = _ttol(strMinLen.c_str());
			if(static_cast<long>(_tcslen(m_lpKey)) < lMinLen)
			{
				strValMsg.Format(TRL("The master password must be at least %u characters long!"),
					static_cast<unsigned int>(lMinLen));
				MessageBox(strValMsg, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
				FreePasswords();

				m_pEditPw.SetFocus();
				return;
			}
		}

		if(strMinQuality.size() > 0)
		{
			const long lMinQuality = _ttol(strMinQuality.c_str());
			const DWORD lCurQuality = CPwQualityEst::EstimatePasswordBits(m_lpKey);
			if(static_cast<long>(lCurQuality) < lMinQuality)
			{
				strValMsg.Format(TRL("The estimated quality of the master password must be at least %u bits!"),
					static_cast<unsigned int>(lMinQuality));
				MessageBox(strValMsg, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
				FreePasswords();

				m_pEditPw.SetFocus();
				return;
			}
		}

		LPTSTR lpValMsg = NULL;
		_CallPlugins(KPM_VALIDATE_MASTERPASSWORD, (LPARAM)m_lpKey, (LPARAM)&lpValMsg);
		if((lpValMsg != NULL) && (lpValMsg[0] != 0))
		{
			MessageBox(lpValMsg, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
			FreePasswords();

			m_pEditPw.SetFocus();
			return;
		}
	}

	if((m_bKeyMethod == PWM_KEYMETHOD_OR) && (m_cbDiskList.GetCurSel() == 0))
	{
		m_bKeyFile = FALSE;
	}
	else // Key file provided
	{
		CString strTemp;
		m_cbDiskList.GetLBText(m_cbDiskList.GetCurSel(), strTemp);
		CString strLBText = strTemp;

		const BOOL bKeyProv = IsKeyProvider(strTemp);

		if((m_bLoadMode == FALSE) && (bKeyProv == FALSE))
		{
			if(strTemp.GetAt(strTemp.GetLength() - 1) == _T('\\'))
			{
				ULARGE_INTEGER aBytes[3];
				if(GetDiskFreeSpaceEx((LPCTSTR)strTemp, &aBytes[0], &aBytes[1], &aBytes[2]) == FALSE)
				{
					strTemp = TRL("Cannot access the selected drive.");
					strTemp += _T("\r\n\r\n");
					strTemp += TRL("Make sure a writable medium is inserted.");
					MessageBox(strTemp, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
					FreePasswords();
					return;
				}

				if(aBytes[2].QuadPart < 128)
				{
					MessageBox(TRL("Not enough free disk space!"), PWM_PRODUCT_NAME_SHORT,
						MB_OK | MB_ICONWARNING);
					FreePasswords();
					return;
				}
			}

			FILE *fpTest = NULL;
			CString strTemp2 = strTemp;
			strTemp2 += _T("t2ub8rf6.tmp"); // Just a random filename, will work for directories and files
			_tfopen_s(&fpTest, (LPCTSTR)strTemp2, _T("wb"));
			if(fpTest == NULL)
			{
				strTemp = TRL("Cannot access the selected drive.");
				strTemp += _T("\r\n\r\n");
				strTemp += TRL("Make sure a writable medium is inserted.");
				MessageBox(strTemp, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
				FreePasswords();
				return;
			}
			fclose(fpTest); Sleep(100);
			DeleteFile(strTemp2);
		}
		else if(bKeyProv == FALSE) // Load the key file
		{
			const DWORD dwDummyErrorCode = 0x6F4B1C80;
			::SetLastError(dwDummyErrorCode);
			HANDLE hKeyFile = CreateFile(strTemp, GENERIC_READ, FILE_SHARE_READ,
				NULL, OPEN_EXISTING, 0, NULL);
			if(hKeyFile != INVALID_HANDLE_VALUE) CloseHandle(hKeyFile);
			else // hKeyFile == INVALID_HANDLE_VALUE
			{
				const DWORD dwKeyFileError = ::GetLastError();
				if(dwKeyFileError != dwDummyErrorCode)
				{
					std::basic_string<TCHAR> strKeyError = strTemp;
					strKeyError += _T("\r\n\r\n");
					strKeyError += CPwUtil::FormatSystemMessage(dwKeyFileError);

					MessageBox(strKeyError.c_str(), PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
					FreePasswords();
					return;
				}
			}
		}

		// Get data from external key provider
		if(bKeyProv == TRUE)
		{
			strTemp = GetKeyFromProvider(strTemp).c_str();
			if(strTemp.GetLength() == 0)
			{
				FreePasswords();
				return;
			}

			m_strSelectedKeyProv = strLBText;
		}

		m_bKeyFile = TRUE;

		if(m_bKeyMethod == PWM_KEYMETHOD_OR) m_pEditPw.DeletePassword(m_lpKey);
		else m_lpKey2 = m_lpKey;

		const size_t sizeKeyBuffer = strTemp.GetLength() + 1;
		m_lpKey = new TCHAR[sizeKeyBuffer];
		ASSERT(m_lpKey != NULL); if(m_lpKey == NULL) { FreePasswords(); return; }
		_tcscpy_s(m_lpKey, sizeKeyBuffer, (LPCTSTR)strTemp);
	}

	CleanUp();
	ASSERT(m_bOnce == FALSE); m_bOnce = TRUE;
	CDialog::OnOK();
}

void CPasswordDlg::OnCancel() 
{
	CleanUp();
	ASSERT(m_bOnce == FALSE); m_bOnce = TRUE;
	CDialog::OnCancel();
}

void CPasswordDlg::OnCheckStars() 
{
	BOOL bPrevState = m_bStars;

	UpdateData(TRUE);

	if((bPrevState == TRUE) && (m_bStars == FALSE) &&
		(CPwSafeDlg::IsUnsafeAllowed(this->m_hWnd) == FALSE))
	{
		m_bStars = TRUE;
		UpdateData(FALSE);
		return;
	}

	if(m_bStars == FALSE)
	{
		m_pEditPw.EnableSecureMode(FALSE);
		m_pEditPw.SetPasswordChar(0);

		// m_pEditPw.SetFont(&m_fStyle, TRUE);
		CFontUtil::AssignPassword(&m_pEditPw, this);
	}
	else
	{
		// TCHAR tchDot = (TCHAR)(_T('z') + 27);
		TCHAR tchDot = CPwSafeApp::GetPasswordCharacter();
		m_pEditPw.EnableSecureMode(CPwSafeDlg::m_bSecureEdits);
		m_pEditPw.SetPasswordChar(tchDot);

		// m_pEditPw.SetFont(&m_fSymbol, TRUE);
		CFontUtil::AssignSymbol(&m_pEditPw, this);
	}

	m_tipSecClear.Activate(m_pEditPw.IsSecureModeEnabled());

	UpdateData(FALSE);
	m_pEditPw.RedrawWindow();
	m_pEditPw.SetFocus();
}

void CPasswordDlg::OnHelpBtn() 
{
	WU_OpenAppHelp(PWM_HELP_KEYS, m_hWnd);
}

void CPasswordDlg::OnChangeEditPassword() 
{
	UpdateData(TRUE);
	LPTSTR lp = m_pEditPw.GetPassword();
	NewGUI_ShowQualityMeter(&m_cPassQuality, GetDlgItem(IDC_STATIC_PWQINFO), lp);
	m_pEditPw.DeletePassword(lp); lp = NULL;
	EnableClientWindows();
}

void CPasswordDlg::OnSelChangeComboDiskList() 
{
	EnableClientWindows();
}

void CPasswordDlg::EnableClientWindows()
{
	UpdateData(TRUE);

	const int nPwLength = m_pEditPw.GetWindowTextLength();
	const int nComboSel = m_cbDiskList.GetCurSel();

	if(m_bKeyMethod == PWM_KEYMETHOD_OR)
	{
		if(nPwLength != 0)
		{
			m_btBrowseKeyFile.EnableWindow(FALSE);
			m_btStars.EnableWindow(TRUE & m_bStarsMask);
			m_pEditPw.EnableWindow(TRUE);
			m_cbDiskList.EnableWindow(FALSE);
			return;
		}
		else if(nComboSel != 0)
		{
			m_btBrowseKeyFile.EnableWindow(TRUE);
			m_btStars.EnableWindow(FALSE);
			m_pEditPw.EnableWindow(FALSE);
			m_cbDiskList.EnableWindow(TRUE);
			return;
		}
	}

	m_btBrowseKeyFile.EnableWindow(TRUE);
	m_btStars.EnableWindow(TRUE & m_bStarsMask);
	m_pEditPw.EnableWindow(TRUE);
	m_cbDiskList.EnableWindow(TRUE);

	// if(nComboSel == 0) m_btBrowseKeyFile.EnableWindow(TRUE);
	// else m_btBrowseKeyFile.EnableWindow(FALSE);
}

void CPasswordDlg::UpdateAndCheckBox()
{
	EnableClientWindows();
}

void CPasswordDlg::OnCheckKeymethodAnd() 
{
	EnableClientWindows();
}

BOOL CPasswordDlg::PreTranslateMessage(MSG* pMsg) 
{
	m_tipSecClear.RelayEvent(pMsg);

	return CDialog::PreTranslateMessage(pMsg);
}

void CPasswordDlg::OnBnClickedBrowseKeyFile()
{
	UpdateData(TRUE);

	CString strFilter = TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	DWORD dwFlags = (OFN_LONGNAMES | OFN_EXTENSIONDIFFERENT | OFN_EXPLORER |
		OFN_ENABLESIZING);

	if(m_bLoadMode == TRUE)
		dwFlags |= (OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY);
	else
		dwFlags |= OFN_HIDEREADONLY;

	const std::basic_string<TCHAR> strDir = WU_GetCurrentDirectory();

	CFileDialog dlg(m_bLoadMode, NULL, NULL, dwFlags, strFilter, this);
	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		CString strFile = dlg.GetPathName();

		COMBOBOXEXITEM cbi;
		ZeroMemory(&cbi, sizeof(COMBOBOXEXITEM));
		cbi.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_INDENT | CBEIF_SELECTEDIMAGE;
		cbi.iItem = m_cbDiskList.GetCount();
		cbi.pszText = (LPTSTR)(LPCTSTR)strFile;
		cbi.cchTextMax = static_cast<int>(_tcslen(cbi.pszText));
		cbi.iImage = cbi.iSelectedImage = 28;
		cbi.iIndent = 0;
		const int nx = m_cbDiskList.InsertItem(&cbi);

		// m_cbDiskList.SelectString(-1, (LPCTSTR)strFile);
		m_cbDiskList.SetCurSel(nx);
	}

	WU_SetCurrentDirectory(strDir.c_str());
	UpdateData(FALSE);
}

void CPasswordDlg::QueryKeyProviders()
{
	m_vKeyProv.clear();

	std::vector<KP_PLUGIN_INSTANCE>& v = CPluginManager::Instance().m_plugins;

	for(size_t i = 0; i < v.size(); ++i)
	{
		KP_PLUGIN_INSTANCE& p = v[i];

		// if(p.bEnabled == FALSE) continue;
		if(p.hinstDLL == NULL) continue;
		if(p.pInterface == NULL) { ASSERT(FALSE); continue; }

		KP_KEYPROV_INFO cInfo;
		ZeroMemory(&cInfo, sizeof(KP_KEYPROV_INFO));
		p.pInterface->OnMessage(KPM_KEYPROV_QUERY_INFO_FIRST, NULL, (LPARAM)&cInfo);

		if(cInfo.lpName != NULL)
		{
			AddKeyProvider(cInfo);

			while(true)
			{
				ZeroMemory(&cInfo, sizeof(KP_KEYPROV_INFO));
				p.pInterface->OnMessage(KPM_KEYPROV_QUERY_INFO_NEXT, NULL, (LPARAM)&cInfo);

				if(cInfo.lpName != NULL) AddKeyProvider(cInfo);
				else break;
			}
		}
	}
}

void CPasswordDlg::AddKeyProvider(const KP_KEYPROV_INFO& keyProvInfo)
{
	if((keyProvInfo.lpName == NULL) || (keyProvInfo.lpName[0] == 0)) return;

	// std::basic_string<TCHAR> strSpec = lpKeyProvInfo;
	// std::basic_string<TCHAR> strSplit = strSpec.substr(0, 1);
	// std::basic_string<TCHAR> strDef = strSpec.substr(1);
	// std::vector<std::basic_string<TCHAR> > vSpec;
	// boost::algorithm::split(vSpec, strDef,
	//	boost::algorithm::is_any_of(strSplit));
	// if(vSpec.size() == 0) return;

	m_vKeyProv.push_back(CPasswordDlg::KeyProvCToS(keyProvInfo));
}

BOOL CPasswordDlg::IsKeyProvider(LPCTSTR lpDisplayName)
{
	for(size_t i = 0; i < m_vKeyProv.size(); ++i)
	{
		if(m_vKeyProv[i].strName == lpDisplayName)
			return TRUE;
	}

	return FALSE;
}

std::basic_string<TCHAR> CPasswordDlg::GetKeyFromProvider(LPCTSTR lpDisplayName)
{
	std::basic_string<TCHAR> str;

	if((lpDisplayName == NULL) || (lpDisplayName[0] == 0)) return str;

	KP_KEYPROV_KEY kpKey;
	ZeroMemory(&kpKey, sizeof(KP_KEYPROV_KEY));

	_CallPlugins(KPM_KEYPROV_QUERY_KEY, (LPARAM)lpDisplayName, (LPARAM)&kpKey);
	if((kpKey.lpData == NULL) || (kpKey.dwDataSize == 0))
	{
		ZeroMemory(&kpKey, sizeof(KP_KEYPROV_KEY));

		KP_KEYPROV_CONTEXT ctx;
		ZeroMemory(&ctx, sizeof(KP_KEYPROV_CONTEXT));

		ctx.dwSize = sizeof(KP_KEYPROV_CONTEXT);
		ctx.lpProviderName = lpDisplayName;
		ctx.bCreatingNewKey = ((m_bLoadMode == FALSE) ? TRUE : FALSE);
		ctx.bConfirming = m_bConfirm;
		ctx.bChanging = m_bChanging;
		ctx.lpDescriptiveName = m_strDescriptiveName;

		_CallPlugins(KPM_KEYPROV_QUERY_KEY_EX, (LPARAM)&ctx, (LPARAM)&kpKey);
	}

	const BYTE* pData = (const BYTE*)kpKey.lpData;
	DWORD cbData = kpKey.dwDataSize;

	if((pData == NULL) || (cbData == 0))
	{
		CString strMsg = TRL("The key provider plugin did not supply a valid key");
		strMsg += _T(".");
		MessageBox(strMsg, PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
		return str;
	}

	const DWORD cbAnnotatedBuf = cbData + 16;
	BYTE* pAnnotatedBuf = new BYTE[cbAnnotatedBuf];
	if((kpKey.dwType & KPKTF_DIRECT) != 0)
	{
		*(UINT64*)(&pAnnotatedBuf[0]) = PWKI_DIRECT_1;
		*(UINT64*)(&pAnnotatedBuf[8]) = PWKI_DIRECT_2;
		memcpy(&pAnnotatedBuf[16], pData, cbData);

		pData = pAnnotatedBuf;
		cbData = cbAnnotatedBuf;
	}

	const DWORD dwBufSizeM = (cbData * 3) + 12;
	BYTE* pBase = new BYTE[dwBufSizeM];
	ZeroMemory(pBase, dwBufSizeM);
	DWORD dwBufSize = dwBufSizeM;
	VERIFY(CBase64Codec::Encode(pData, cbData, pBase, &dwBufSize));

	str = _T(CB64_PROTOCOL);

#ifndef _UNICODE
	str += (const char *)pBase;
#else // Unicode
	const WCHAR *pUni = _StringToUnicode((const char *)pBase);
	str += pUni;
	SAFE_DELETE_ARRAY(pUni);
#endif

	mem_erase(pBase, dwBufSizeM);
	SAFE_DELETE_ARRAY(pBase);
	mem_erase(pAnnotatedBuf, cbAnnotatedBuf);
	SAFE_DELETE_ARRAY(pAnnotatedBuf);
	return str;
}

KP_KEYPROV_INFO_CPP CPasswordDlg::KeyProvCToS(const KP_KEYPROV_INFO& c)
{
	KP_KEYPROV_INFO_CPP s;

	s.dwFlags = c.dwFlags;
	s.strName = c.lpName;
	s.dwImageIndex = c.dwImageIndex;

	return s;
}

#pragma warning(pop)
