/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 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 "FieldRefDlg.h"
#include "../KeePassLibCpp/Util/StrUtil.h"
#include "../KeePassLibCpp/Util/MemUtil.h"
#include "../KeePassLibCpp/Util/TranslateEx.h"
#include "NewGUI/NewGUICommon.h"
#include "Util/WinUtil.h"
IMPLEMENT_DYNAMIC(CFieldRefDlg, CDialog)
CFieldRefDlg::CFieldRefDlg(CWnd* pParent /*=NULL*/)
: CDialog(CFieldRefDlg::IDD, pParent)
{
m_pMgr = NULL;
m_pImages = NULL;
m_dwDefaultRef = 0;
m_bClosing = FALSE;
}
CFieldRefDlg::~CFieldRefDlg()
{
}
void CFieldRefDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDOK, m_btOK);
DDX_Control(pDX, IDCANCEL, m_btCancel);
DDX_Control(pDX, IDC_LIST_ENTRIES, m_lvEntries);
DDX_Control(pDX, IDC_RADIO_IDTITLE, m_radioId);
DDX_Control(pDX, IDC_RADIO_REFTITLE, m_radioRef);
DDX_Control(pDX, IDC_BTN_HELP, m_btHelp);
}
BEGIN_MESSAGE_MAP(CFieldRefDlg, CDialog)
ON_BN_CLICKED(IDOK, &CFieldRefDlg::OnBnClickedOk)
ON_BN_CLICKED(IDCANCEL, &CFieldRefDlg::OnBnClickedCancel)
ON_BN_CLICKED(IDC_BTN_HELP, &CFieldRefDlg::OnBnClickedBtnHelp)
ON_NOTIFY(NM_CLICK, IDC_LIST_ENTRIES, &CFieldRefDlg::OnNMClickListEntries)
END_MESSAGE_MAP()
BOOL CFieldRefDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT(m_pMgr != NULL);
NewGUI_TranslateCWnd(this);
EnumChildWindows(this->m_hWnd, NewGUI_TranslateWindowCb, 0);
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);
m_lvEntries.SetImageList(m_pImages, LVSIL_SMALL);
RECT rectClient;
m_lvEntries.GetClientRect(&rectClient);
const int nColWidth = (rectClient.right - rectClient.left -
GetSystemMetrics(SM_CXVSCROLL) - 6) / 5;
m_lvEntries.InsertColumn(0, TRL("Group"), LVCFMT_LEFT, nColWidth + 2, 0);
m_lvEntries.InsertColumn(1, TRL("Title"), LVCFMT_LEFT, nColWidth, 1);
m_lvEntries.InsertColumn(2, TRL("User Name"), LVCFMT_LEFT, nColWidth, 2);
m_lvEntries.InsertColumn(3, TRL("URL"), LVCFMT_LEFT, nColWidth, 3);
m_lvEntries.InsertColumn(4, TRL("Notes"), LVCFMT_LEFT, nColWidth, 4);
m_lvEntries.InsertColumn(5, TRL("UUID"), LVCFMT_LEFT, 0, 5);
LPARAM dw = 0;
dw |= LVS_EX_SI_REPORT | LVS_EX_FULLROWSELECT;
dw |= LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP | LVS_EX_GRIDLINES;
m_lvEntries.PostMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dw);
for(DWORD i = 0; i < m_pMgr->GetNumberOfEntries(); ++i)
_AddEntryToList(m_pMgr->GetEntry(i));
NewGUI_SortList(&m_lvEntries);
CheckRadioButton(IDC_RADIO_IDTITLE, IDC_RADIO_IDUUID, IDC_RADIO_IDUUID);
const int nRefIDMin = IDC_RADIO_REFTITLE;
const int nRefIDMax = IDC_RADIO_REFNOTES;
if(m_dwDefaultRef == PWMF_TITLE)
CheckRadioButton(nRefIDMin, nRefIDMax, IDC_RADIO_REFTITLE);
else if(m_dwDefaultRef == PWMF_USER)
CheckRadioButton(nRefIDMin, nRefIDMax, IDC_RADIO_REFUSER);
// else if(m_dwDefaultRef == PWMF_PASSWORD)
// CheckRadioButton(nRefIDMin, nRefIDMax, IDC_RADIO_REFPW);
else if(m_dwDefaultRef == PWMF_URL)
CheckRadioButton(nRefIDMin, nRefIDMax, IDC_RADIO_REFURL);
else if(m_dwDefaultRef == PWMF_ADDITIONAL)
CheckRadioButton(nRefIDMin, nRefIDMax, IDC_RADIO_REFNOTES);
else CheckRadioButton(nRefIDMin, nRefIDMax, IDC_RADIO_REFPW);
EnableChildControls();
return TRUE; // return TRUE unless you set the focus to a control
}
void CFieldRefDlg::InitEx(CPwManager* pMgr, CImageList* pImages,
DWORD dwDefaultRef)
{
m_pMgr = pMgr;
m_pImages = pImages;
m_dwDefaultRef = dwDefaultRef;
}
void CFieldRefDlg::_AddEntryToList(PW_ENTRY *p)
{
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(LV_ITEM));
lvi.iItem = m_lvEntries.GetItemCount(); // Insert at end
lvi.iSubItem = 0;
lvi.iImage = static_cast<int>(p->uImageId);
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
PW_GROUP *pwg = m_pMgr->GetGroupById(p->uGroupId);
ASSERT(pwg != NULL); if(pwg == NULL) return; // Must be non-virtual
if((_tcscmp(pwg->pszGroupName, PWS_BACKUPGROUP) == 0) ||
(_tcscmp(pwg->pszGroupName, PWS_BACKUPGROUP_SRC) == 0)) return;
lvi.pszText = pwg->pszGroupName;
m_lvEntries.InsertItem(&lvi); // Add
lvi.mask = LVIF_TEXT;
lvi.iImage = 0;
lvi.iSubItem = 1;
lvi.pszText = p->pszTitle;
m_lvEntries.SetItem(&lvi);
lvi.iSubItem = 2;
lvi.pszText = p->pszUserName;
m_lvEntries.SetItem(&lvi);
lvi.iSubItem = 3;
lvi.pszText = p->pszURL;
m_lvEntries.SetItem(&lvi);
lvi.iSubItem = 4;
lvi.pszText = p->pszAdditional;
m_lvEntries.SetItem(&lvi);
CString strTemp;
_UuidToString(p->uuid, &strTemp);
lvi.iSubItem = 5;
lvi.pszText = (LPTSTR)(LPCTSTR)strTemp;
m_lvEntries.SetItem(&lvi);
}
PW_ENTRY* CFieldRefDlg::GetSelectedEntry()
{
BOOL bFound = FALSE;
int i = 0;
for( ; i < m_lvEntries.GetItemCount(); ++i)
{
const UINT uState = m_lvEntries.GetItemState(i, LVIS_SELECTED);
if((uState & LVIS_SELECTED) != 0) { bFound = TRUE; break; }
}
if(bFound == FALSE) return NULL;
TCHAR tszTemp[42];
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(LV_ITEM));
lvi.iItem = i;
lvi.iSubItem = 5;
lvi.mask = LVIF_TEXT;
lvi.pszText = tszTemp;
lvi.cchTextMax = 40;
if(m_lvEntries.GetItem(&lvi) == FALSE) { ASSERT(FALSE); return NULL; }
CString strTemp = lvi.pszText;
BYTE aUuid[16];
_StringToUuid((LPCTSTR)strTemp, &aUuid[0]);
return m_pMgr->GetEntryByUuid(&aUuid[0]);
}
void CFieldRefDlg::OnBnClickedOk()
{
PW_ENTRY* p = GetSelectedEntry();
if(p == NULL) { ASSERT(FALSE); return; }
CString str = _T("{REF:");
const int nRef = this->GetCheckedRadioButton(IDC_RADIO_REFTITLE, IDC_RADIO_REFNOTES);
if(nRef == IDC_RADIO_REFTITLE) str += _T("T");
else if(nRef == IDC_RADIO_REFUSER) str += _T("U");
else if(nRef == IDC_RADIO_REFPW) str += _T("P");
else if(nRef == IDC_RADIO_REFURL) str += _T("A");
else if(nRef == IDC_RADIO_REFNOTES) str += _T("N");
else { ASSERT(FALSE); return; }
str += _T("@");
CString strId;
const int nID = this->GetCheckedRadioButton(IDC_RADIO_IDTITLE, IDC_RADIO_IDUUID);
if(nID == IDC_RADIO_IDTITLE) strId = CString(_T("T:")) + CString(p->pszTitle);
else if(nID == IDC_RADIO_IDUSER) strId = CString(_T("U:")) + CString(p->pszUserName);
else if(nID == IDC_RADIO_IDPASSWORD)
{
m_pMgr->UnlockEntryPassword(p);
strId = (CString(_T("P:")) + CString(p->pszPassword));
m_pMgr->LockEntryPassword(p);
}
else if(nID == IDC_RADIO_IDURL) strId = CString(_T("A:")) + CString(p->pszURL);
else if(nID == IDC_RADIO_IDNOTES) strId = CString(_T("N:")) + CString(p->pszAdditional);
else if(nID == IDC_RADIO_IDUUID)
{
CString strUuid;
_UuidToString(p->uuid, &strUuid);
strId = CString(_T("I:")) + strUuid;
}
else { ASSERT(FALSE); return; }
if((strId.Find(_T('{')) >= 0) || (strId.Find(_T('}')) >= 0) ||
(strId.Find(_T('\r')) >= 0) || (strId.Find(_T('\n')) >= 0))
{
MessageBox(TRL("The selected field, which identifies the source entry, contains illegal characters (like '{', '}', newline characters, ...)."),
PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
return;
}
CString strIdData = strId.Right(strId.GetLength() - 2);
if(_IdMatchesMultipleTimes(strIdData, strId.GetAt(0)) == TRUE)
{
CString strMsg = TRL("Multiple entries match the specified identifying field.");
strMsg += _T("\r\n\r\n");
strMsg += TRL("To avoid ambiguity, entries can be identified by their UUIDs, which are unique.");
MessageBox(strMsg, PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
return;
}
str += strId + CString(_T("}"));
m_strFieldRef = str;
EraseCString(&str);
m_bClosing = TRUE;
m_lvEntries.SetImageList(NULL, LVSIL_SMALL);
OnOK();
}
BOOL CFieldRefDlg::_IdMatchesMultipleTimes(LPCTSTR lpText, TCHAR tchScan)
{
if((lpText == NULL) || (lpText[0] == 0)) return TRUE;
DWORD dwFlags = 0;
if(tchScan == _T('T')) dwFlags |= PWMF_TITLE;
else if(tchScan == _T('U')) dwFlags |= PWMF_USER;
else if(tchScan == _T('A')) dwFlags |= PWMF_URL;
else if(tchScan == _T('P')) dwFlags |= PWMF_PASSWORD;
else if(tchScan == _T('N')) dwFlags |= PWMF_ADDITIONAL;
else if(tchScan == _T('I')) dwFlags |= PWMF_UUID;
else return FALSE;
const DWORD dwIndex = m_pMgr->FindEx(lpText, FALSE, dwFlags, 0, NULL);
if(dwIndex == DWORD_MAX) { ASSERT(FALSE); return FALSE; }
if((dwIndex + 1) == m_pMgr->GetNumberOfEntries()) return FALSE;
const DWORD dwMulti = m_pMgr->FindEx(lpText, FALSE, dwFlags, dwIndex + 1, NULL);
if(dwMulti != DWORD_MAX) return TRUE;
return FALSE;
}
void CFieldRefDlg::OnBnClickedCancel()
{
m_lvEntries.SetImageList(NULL, LVSIL_SMALL);
OnCancel();
}
void CFieldRefDlg::EnableChildControls()
{
if(m_bClosing == TRUE) return;
m_btOK.EnableWindow((GetSelectedEntry() != NULL) ? TRUE : FALSE);
}
void CFieldRefDlg::OnBnClickedBtnHelp()
{
WU_OpenAppHelp(PWM_HELP_FIELDREFS, m_hWnd);
}
void CFieldRefDlg::OnNMClickListEntries(NMHDR *pNMHDR, LRESULT *pResult)
{
UNREFERENCED_PARAMETER(pNMHDR);
*pResult = 0;
this->EnableChildControls();
}