text: /*
  * Copyright (c) 2003-2020 Rony Shapiro <ronys@pwsafe.org>.
  * All rights reserved. Use of the code is allowed under the
  * Artistic License 2.0 terms, as specified in the LICENSE file
  * distributed with this code, or available from
  * http://www.opensource.org/licenses/artistic-license-2.0.php
  */
  
  #include "PasswordSafe.h"
  #include "DboxMain.h"
  #include "AddEdit_PropertySheet.h"
  #include "GeneralMsgBox.h"
  
  #include "core/ItemData.h"
  #include "core/PWSprefs.h"
  
  using pws_os::CUUID;
  
  IMPLEMENT_DYNAMIC(CAddEdit_PropertySheet, CPWPropertySheet)
  
  CAddEdit_PropertySheet::CAddEdit_PropertySheet(UINT nID, CWnd* pParent,
                                                 PWScore *pcore,
                                                 CItemData *pci_original, CItemData *pci,
                                                 const bool bLongPPs,
                                                 const StringX currentDB)
    : CPWPropertySheet(nID, pParent, bLongPPs), m_bIsModified(false), m_bChanged(false),
    m_bNotesChanged(false), m_bSymbolsChanged(false)
  {
    m_AEMD.bLongPPs = bLongPPs;
    m_AEMD.uicaller = nID;
  
    ASSERT(pParent != NULL);
    ASSERT(pcore != NULL);
    ASSERT(pci != NULL);
  
    m_AEMD.pcore = pcore;
    m_AEMD.pci_original = pci_original;
    m_AEMD.pci = pci;
  
    m_AEMD.currentDB = currentDB;
  
    m_AEMD.entry_uuid = pws_os::CUUID::NullUUID();
    m_AEMD.base_uuid = pws_os::CUUID::NullUUID();
  
    PWSprefs *prefs = PWSprefs::GetInstance();
  
    m_AEMD.default_pwp = prefs->GetDefaultPolicy();
    m_AEMD.default_symbols = prefs->GetPref(PWSprefs::DefaultSymbols);
  
    // Preferences min/max values
    m_AEMD.prefminPWLength = (short)prefs->GetPrefMinVal(PWSprefs::PWDefaultLength);
    m_AEMD.prefmaxPWLength = (short)prefs->GetPrefMaxVal(PWSprefs::PWDefaultLength);
    m_AEMD.prefminPWHNumber = (short)prefs->GetPrefMinVal(PWSprefs::NumPWHistoryDefault);
    m_AEMD.prefmaxPWHNumber = (short)prefs->GetPrefMaxVal(PWSprefs::NumPWHistoryDefault);
  
    // Set up data used by all Property Pages, as appropriate
    if (m_AEMD.uicaller == IDS_ADDENTRY) {
      // Basic initialisation
      m_AEMD.group = L"";
      m_AEMD.title = L"";
      m_AEMD.username = L"";
      m_AEMD.realpassword = L"";
      m_AEMD.lastpassword = L"";
      m_AEMD.notes = m_AEMD.originalnotesTRC = L"";
      m_AEMD.URL = L"";
      m_AEMD.email = L"";
      m_AEMD.symbols = m_AEMD.oldsymbols = L"";
  
      // Entry type initialisation
      m_AEMD.original_entrytype = CItemData::ET_NORMAL;
  
      // Additional initialisation
      m_AEMD.autotype = L"";
      m_AEMD.runcommand = L"";
      m_AEMD.oldDCA = m_AEMD.DCA = m_AEMD.oldShiftDCA = m_AEMD.ShiftDCA = -1;
  
      // Date & Time initialisation
      m_AEMD.locCTime.LoadString(IDS_NA);
      m_AEMD.locXTime = m_AEMD.locATime = m_AEMD.locRMTime = m_AEMD.locPMTime =
            m_AEMD.oldlocXTime = m_AEMD.locCTime;
      m_AEMD.tttXTime = m_AEMD.tttCPMTime = (time_t)0;
      m_AEMD.oldXTimeInt = m_AEMD.XTimeInt = 0;
  
      // PWHistory initialisation
      m_AEMD.SavePWHistory = m_AEMD.oldSavePWHistory =
              PWSprefs::GetInstance()->GetPref(PWSprefs::SavePasswordHistory) ? TRUE : FALSE;
      m_AEMD.MaxPWHistory = m_AEMD.oldMaxPWHistory =
              PWSprefs::GetInstance()->GetPref(PWSprefs::NumPWHistoryDefault);
      m_AEMD.NumPWHistory = m_AEMD.oldNumPWHistory = 0;
  
      // PWPolicy fields
      m_AEMD.pwp = m_AEMD.oldpwp = m_AEMD.default_pwp;
      m_AEMD.ipolicy = m_AEMD.oldipolicy = DEFAULT_POLICY;
      m_AEMD.iownsymbols = m_AEMD.ioldownsymbols = DEFAULT_SYMBOLS;
      m_AEMD.symbols = L"";
      m_AEMD.policyname = m_AEMD.oldpolicyname = L"";
  
      // Protected
      m_AEMD.ucprotected = 0;
      
      // Entry Keyboard shortcut
      m_AEMD.oldKBShortcut = m_AEMD.KBShortcut = 0;
    } else {
      SetupInitialValues();
    }
  
    // Only now allocate the PropertyPages - after all data there
    // to be used by their c'tors
    m_pp_basic      = new CAddEdit_Basic(this, &m_AEMD);
    m_pp_additional = new CAddEdit_Additional(this, &m_AEMD);
    m_pp_datetimes  = new CAddEdit_DateTimes(this, &m_AEMD);
    m_pp_pwpolicy   = new CAddEdit_PasswordPolicy(this, &m_AEMD);
    if (pcore->GetReadFileVersion() == PWSfile::V40)
      m_pp_attachment = new CAddEdit_Attachment(this, &m_AEMD);
    else
      m_pp_attachment = NULL;
  
    AddPage(m_pp_basic);
    AddPage(m_pp_additional);
    AddPage(m_pp_datetimes);
    AddPage(m_pp_pwpolicy);
    if (pcore->GetReadFileVersion() == PWSfile::V40)
      AddPage(m_pp_attachment);
  }
  
  CAddEdit_PropertySheet::~CAddEdit_PropertySheet()
  {
    delete m_pp_basic;
    delete m_pp_additional;
    delete m_pp_datetimes;
    delete m_pp_pwpolicy;
    delete m_pp_attachment;
  }
  
  BEGIN_MESSAGE_MAP(CAddEdit_PropertySheet, CPWPropertySheet)
    //{{AFX_MSG_MAP(CAddEdit_PropertySheet)
    ON_WM_SYSCOMMAND()
    //}}AFX_MSG_MAP
  END_MESSAGE_MAP()
  
  void CAddEdit_PropertySheet::OnSysCommand(UINT nID, LPARAM lParam)
  {
    const UINT nSysID = nID & 0xFFF0;
  
    // Cancel external editor in use for Notes
    if (nSysID == SC_CLOSE && m_pp_basic->IsNotesExternalEditorActive()) {
      CGeneralMsgBox gmb;
      CString cs_message(MAKEINTRESOURCE(IDS_CANCEL_EXT_EDITOR)),
        cs_title(MAKEINTRESOURCE(IDS_EXT_EDITOR_ACTIVE));
      INT_PTR rc = gmb.MessageBox(cs_message, cs_title, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION);
      if (rc == IDYES)
        m_pp_basic->CancelThreadWait();
      
      // Do NOT cancel the whole edit by processing the message as this will free Add/Edit Basic
      // property page whilst it is still being executed causing an application crash.
      // So just return causing the external edit to be cancelled and return passed back
      // to Add/Edit
      return;
    }
  
    if (nSysID == SC_CLOSE &&
        (m_AEMD.uicaller == IDS_VIEWENTRY ||
        (m_AEMD.uicaller == IDS_EDITENTRY &&  m_AEMD.ucprotected != 0))) {
      EndDialog(IDCANCEL);
      return;
    }
    CPWPropertySheet::OnSysCommand(nID, lParam);
  }
  
  BOOL CAddEdit_PropertySheet::OnInitDialog()
  {
    CPWPropertySheet::OnInitDialog();
  
    // Change the Window title for Edit/View
    switch (m_AEMD.uicaller) {
      case IDS_ADDENTRY:
        break;
      case IDS_VIEWENTRY:
      case IDS_EDITENTRY:
      {
        CString cs_title;
        StringX sx_group(L""), sx_title, sx_user(L"");
        if (!m_AEMD.pci->IsGroupEmpty())
          sx_group = m_AEMD.pci->GetGroup();
        sx_title = m_AEMD.pci->GetTitle();
        if (!m_AEMD.pci->IsUserEmpty())
          sx_user = m_AEMD.pci->GetUser();
  
        // Set up and pass Property sheet caption showing entry being edited/viewed
        // If entry is protected, set to 'View' even if DB is in R/W mode
        cs_title.Format(m_AEMD.ucprotected != 0 ? IDS_PROTECTEDENTRY : m_AEMD.uicaller,
                        sx_group.c_str(), sx_title.c_str(), sx_user.c_str());
        SetWindowText(cs_title);
        break;
      }
      default:
        ASSERT(0);
    }
  
    // Change the Control ID for the Apply button so that we get it
    switch (m_AEMD.uicaller) {
      case IDS_ADDENTRY:
        break;
      case IDS_VIEWENTRY:
        GetDlgItem(IDCANCEL)->EnableWindow(FALSE);
        GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);
        break;
      case IDS_EDITENTRY:
        GetDlgItem(ID_APPLY_NOW)->EnableWindow(m_bChanged || m_bSymbolsChanged ? TRUE : FALSE);
        break;
    }
    
    return TRUE;  // return TRUE unless you set the focus to a control
  }
  
  void CAddEdit_PropertySheet::SetSymbolsChanged(bool bSymbolsChanged)
  {
    m_bSymbolsChanged = bSymbolsChanged;
    bool bChanged = m_bChanged || m_bSymbolsChanged;
  
    GetDlgItem(IDOK)->EnableWindow(bChanged ? TRUE : FALSE);
    if (m_AEMD.uicaller == IDS_EDITENTRY)
      GetDlgItem(ID_APPLY_NOW)->EnableWindow(bChanged ? TRUE : FALSE);
  }
  
  void CAddEdit_PropertySheet::SetChanged(const bool bChanged)
  {
    // Can't be modified if protected
    if (m_AEMD.ucprotected != 0 && bChanged) {
      ASSERT(0);
      return;
    }
  
    if (m_bChanged != bChanged) {
      if (m_AEMD.uicaller == IDS_EDITENTRY)
        GetDlgItem(ID_APPLY_NOW)->EnableWindow(bChanged ? TRUE : FALSE);
      switch (m_AEMD.uicaller) {
      case IDS_ADDENTRY:
        break;
      case IDS_VIEWENTRY:
      case IDS_EDITENTRY:
      {
        CString cs_title;
        StringX sx_group(L""), sx_title, sx_user(L"");
        if (!m_AEMD.pci->IsGroupEmpty())
          sx_group = m_AEMD.pci->GetGroup();
        sx_title = m_AEMD.pci->GetTitle();
        if (!m_AEMD.pci->IsUserEmpty())
          sx_user = m_AEMD.pci->GetUser();
  
        // Set up and pass Property sheet caption showing entry being edited/viewed
        // If entry is protected, set to 'View' even if DB is in R/W mode
        cs_title.Format(m_AEMD.ucprotected != 0 ? IDS_PROTECTEDENTRY : m_AEMD.uicaller,
                        sx_group.c_str(), sx_title.c_str(), sx_user.c_str());
  
        const CString asterisk = L"*";
        if (bChanged)
          cs_title += asterisk;
        else
          cs_title.TrimRight(asterisk[0]);
  
        SetWindowText(cs_title);
        break;
      }
      default:
        ASSERT(0);
      }
      m_bChanged = bChanged;
    }
  }
  
  BOOL CAddEdit_PropertySheet::OnCommand(WPARAM wParam, LPARAM lParam)
  {
    // There is no OnOK for classes derived from CPropertySheet,
    // so we make our own!
    const int iCID = LOWORD(wParam);
    if (HIWORD(wParam) == BN_CLICKED && (iCID == IDOK || iCID == ID_APPLY_NOW)) {
      // Don't care what user has done if entry is protected or DB R-O.
      if (m_AEMD.ucprotected != 0 || m_AEMD.uicaller == IDS_VIEWENTRY) {
        CPWPropertySheet::EndDialog(IDOK);
        return TRUE;
      }
  
      BOOL brc = OnApply(iCID);
      if (brc == TRUE)
        return TRUE;
    }
  
    return CPWPropertySheet::OnCommand(wParam, lParam);
  }
  
  BOOL CAddEdit_PropertySheet::OnApply(const int &iCID)
  {
    // First send a message to all loaded pages using base class function.
    // We want them all to update their variables in the Master Data area.
    // And call OnApply() rather than the default OnOK processing
    // Note: This message is only sent to PropertyPages that have been
    // loaded - i.e. the user has selected to view them, since obviously
    // the user would not have changed their values if not displayed. Duh!
    if (SendMessage(PSM_QUERYSIBLINGS,
                (WPARAM)CPWPropertyPage::PP_UPDATE_VARIABLES, 0L) != 0) {
      return TRUE;
    }
  
    time_t t;
    bool bIsPSWDModified(false);
    short iDCA, iShiftDCA;
  
    switch (m_AEMD.uicaller) {
      case IDS_EDITENTRY:
        // Make as View entry if protected
        if (m_AEMD.ucprotected != 0)
          break;
  
        m_AEMD.pci->GetDCA(iDCA);
        m_AEMD.pci->GetShiftDCA(iShiftDCA);
  
        // Check if modified
        m_bIsModified = (m_AEMD.group       != m_AEMD.pci->GetGroup()      ||
                         m_AEMD.title       != m_AEMD.pci->GetTitle()      ||
                         m_AEMD.username    != m_AEMD.pci->GetUser()       ||
                         m_AEMD.notes       != m_AEMD.originalnotesTRC     ||
                         m_AEMD.URL         != m_AEMD.pci->GetURL()        ||
                         m_AEMD.autotype    != m_AEMD.pci->GetAutoType()   ||
                         m_AEMD.runcommand  != m_AEMD.pci->GetRunCommand() ||
                         m_AEMD.DCA         != iDCA                        ||
                         m_AEMD.ShiftDCA    != iShiftDCA                   ||
                         m_AEMD.email       != m_AEMD.pci->GetEmail()      ||
                         m_AEMD.symbols     != m_AEMD.oldsymbols           ||
                         m_AEMD.PWHistory   != m_AEMD.pci->GetPWHistory()  ||
                         m_AEMD.locXTime    != m_AEMD.oldlocXTime          ||
                         m_AEMD.XTimeInt    != m_AEMD.oldXTimeInt          ||
                         m_AEMD.ipolicy     != m_AEMD.oldipolicy           ||
                         (m_AEMD.ipolicy    == SPECIFIC_POLICY &&
                          m_AEMD.pwp        != m_AEMD.oldpwp)              ||
                         (m_AEMD.ipolicy    == NAMED_POLICY &&
                          m_AEMD.policyname != m_AEMD.oldpolicyname)       ||
                         m_AEMD.KBShortcut  != m_AEMD.oldKBShortcut        ||
                         m_AEMD.attachment  != m_AEMD.oldattachment);
  
        bIsPSWDModified = (m_AEMD.realpassword != m_AEMD.oldRealPassword);
  
        if (m_bIsModified) {
          // Just modify all - even though only 1 may have actually been modified
          m_AEMD.pci->SetGroup(m_AEMD.group);
          m_AEMD.pci->SetTitle(m_AEMD.title);
          m_AEMD.pci->SetUser(m_AEMD.username);
          if (m_bNotesChanged)
            m_AEMD.pci->SetNotes(m_AEMD.notes);
  
          m_AEMD.pci->SetURL(m_AEMD.URL);
          m_AEMD.pci->SetAutoType(m_AEMD.autotype);
          m_AEMD.pci->SetPWHistory(m_AEMD.PWHistory);
          m_AEMD.PWHistory = m_AEMD.PWHistory;
          m_AEMD.oldNumPWHistory = m_AEMD.NumPWHistory;
          m_AEMD.oldMaxPWHistory = m_AEMD.MaxPWHistory;
          m_AEMD.oldSavePWHistory = m_AEMD.SavePWHistory;
  
          switch (m_AEMD.ipolicy) {
            case DEFAULT_POLICY:
              m_AEMD.pci->SetPWPolicy(L"");
              m_AEMD.policyname = L"";
              m_AEMD.pci->SetPolicyName(L"");
              break;
            case NAMED_POLICY:
              m_AEMD.pci->SetPWPolicy(L"");
              m_AEMD.pci->SetPolicyName(m_AEMD.policyname);
              break;
            case SPECIFIC_POLICY:
              m_AEMD.pci->SetPWPolicy(m_AEMD.pwp);
              m_AEMD.policyname = L"";
              m_AEMD.pci->SetPolicyName(L"");
              break;
            }
  
          m_AEMD.oldipolicy = m_AEMD.ipolicy;
          m_AEMD.oldpwp = m_AEMD.pwp;
          m_AEMD.oldsymbols = m_AEMD.symbols;
          m_AEMD.oldpolicyname = m_AEMD.policyname;
  
          m_AEMD.pci->SetRunCommand(m_AEMD.runcommand);
          m_AEMD.pci->SetDCA(m_AEMD.DCA);
          m_AEMD.pci->SetShiftDCA(m_AEMD.ShiftDCA);
          m_AEMD.pci->SetEmail(m_AEMD.email);
          m_AEMD.pci->SetSymbols(m_AEMD.symbols);
          m_AEMD.pci->SetProtected(m_AEMD.ucprotected != 0);
  
          m_AEMD.oldKBShortcut = m_AEMD.KBShortcut;
          m_AEMD.pci->SetKBShortcut(m_AEMD.KBShortcut);
  
          // TODO - What if user has removed the old attachment or changed it? (Rony)
          if (m_AEMD.attachment.HasUUID()) {
            m_AEMD.pci->SetAttUUID(m_AEMD.attachment.GetUUID());
            m_AEMD.pcore->PutAtt(m_AEMD.attachment);
          } else {
            m_AEMD.pci->ClearAttUUID();
            if (m_AEMD.oldattachment.HasUUID())
              m_AEMD.pcore->RemoveAtt(m_AEMD.oldattachment.GetUUID());
          }
        } // m_bIsModified
  
        m_AEMD.pci->SetXTimeInt(m_AEMD.XTimeInt);
  
        if (bIsPSWDModified || m_AEMD.locXTime != m_AEMD.oldlocXTime) {
          CItemData *pciA = (m_AEMD.pci->IsAlias()) ? m_AEMD.pcore->GetBaseEntry(m_AEMD.pci) : m_AEMD.pci;
  
          if (m_AEMD.locXTime != m_AEMD.oldlocXTime) {
            pciA->SetXTime(m_AEMD.tttXTime);
            m_AEMD.locXTime = pciA->GetXTimeL();
            m_AEMD.oldlocXTime = m_AEMD.locXTime;
          }
  
          if (bIsPSWDModified) {
            m_AEMD.pci->UpdatePassword(m_AEMD.realpassword); // also updates exp time if entry's recurring exp set
            m_AEMD.locPMTime = m_AEMD.pci->GetPMTimeL();
           if (m_AEMD.XTimeInt != 0) {
             // if entry has a recurring password interval, then
             // changing the password ==> updated expiry
              pciA->GetXTime(m_AEMD.tttXTime);
              m_AEMD.locXTime = pciA->GetXTimeL();
            }
          } // bIsPSWDModified
        }
  
        if (m_bIsModified && !bIsPSWDModified) {
          time(&t);
          m_AEMD.pci->SetRMTime(t);
        }
  
        m_bIsModified = m_bIsModified || bIsPSWDModified;
  
        if (m_bIsModified)
          SendMessage(PSM_QUERYSIBLINGS,
                (WPARAM)CPWPropertyPage::PP_UPDATE_TIMES, 0L);
        break;
  
      case IDS_ADDENTRY:
        m_bIsModified = true;
        m_AEMD.pci->SetGroup(m_AEMD.group);
        m_AEMD.pci->SetTitle(m_AEMD.title);
        m_AEMD.pci->SetUser(m_AEMD.username.IsEmpty() ?
                                  m_AEMD.defusername : m_AEMD.username);
        m_AEMD.pci->SetPassword(m_AEMD.realpassword);
        m_AEMD.pci->SetNotes(m_AEMD.notes);
        m_AEMD.pci->SetURL(m_AEMD.URL);
        m_AEMD.pci->SetAutoType(m_AEMD.autotype);
        m_AEMD.pci->SetRunCommand(m_AEMD.runcommand);
        m_AEMD.pci->SetDCA(m_AEMD.DCA);
        m_AEMD.pci->SetShiftDCA(m_AEMD.ShiftDCA);
        m_AEMD.pci->SetEmail(m_AEMD.email);
        m_AEMD.pci->SetSymbols(m_AEMD.symbols);
        m_AEMD.pci->SetProtected(m_AEMD.ucprotected != 0);
        m_AEMD.pci->SetKBShortcut(m_AEMD.KBShortcut);
  
        time(&t);
        m_AEMD.pci->SetCTime(t);
  
        if (m_AEMD.XTimeInt > 0 && m_AEMD.XTimeInt <= 3650)
          m_AEMD.pci->SetXTimeInt(m_AEMD.XTimeInt);
  
        if (m_AEMD.SavePWHistory == TRUE)
          m_AEMD.pci->SetPWHistory(MakePWHistoryHeader(TRUE, m_AEMD.MaxPWHistory, 0));
  
        if (m_AEMD.ibasedata > 0) {
          // Password in alias format AND base entry exists
          // No need to check if base is an alias as already done in
          // call to PWScore::ParseBaseEntryPWD
          m_AEMD.pci->SetPassword(L"[Alias]");
          m_AEMD.pci->SetAlias();
          ItemListIter iter = m_AEMD.pcore->Find(m_AEMD.base_uuid);
          if (iter != GetMainDlg()->End())
            GetMainDlg()->UpdateEntryImages(iter->second);
        } else {
          m_AEMD.pci->SetPassword(m_AEMD.realpassword);
          m_AEMD.pci->SetNormal();
        }
  
        if (m_AEMD.pci->IsAlias()) {
          m_AEMD.pci->SetXTime((time_t)0);
          m_AEMD.pci->SetPWPolicy(L"");
        } else {
          m_AEMD.pci->SetXTime(m_AEMD.tttXTime);
  
          switch (m_AEMD.ipolicy) {
            case DEFAULT_POLICY:
              m_AEMD.pci->SetPWPolicy(L"");
              m_AEMD.policyname = L"";
              m_AEMD.pci->SetPolicyName(L"");
              break;
            case NAMED_POLICY:
              m_AEMD.pci->SetPWPolicy(L"");
              m_AEMD.pci->SetPolicyName(m_AEMD.policyname);
              break;
            case SPECIFIC_POLICY:
              m_AEMD.pci->SetPWPolicy(m_AEMD.pwp);
              m_AEMD.policyname = L"";
              m_AEMD.pci->SetPolicyName(L"");
              break;
          }
        }
  
        // TODO - Add attachment if present (Rony)
  
        if (m_bIsModified)
          SendMessage(PSM_QUERYSIBLINGS,
                (WPARAM)CPWPropertyPage::PP_UPDATE_TIMES, 0L);
        break;
      case IDS_VIEWENTRY:
        // No Update
        break;
      default:
        ASSERT(0);
        break;
    }
  
    // Now end it all so that OnApply isn't called again
    if (iCID == IDOK) {
      // Just end it
      CPWPropertySheet::EndDialog(IDOK);
    } else {
      // Send message to DboxMain to update entry
      GetMainDlg()->SendMessage(PWS_MSG_EDIT_APPLY, (WPARAM)this, NULL);
  
      // After all the commands have been executed, hopefully all fields
      // have been updated consistently (especially true for editing aliases)
      // Refresh entry from DB
      CItemData *pci(NULL);
      ItemListIter iter = m_AEMD.pcore->Find(m_AEMD.entry_uuid);
  
      // ASSERT should never happen as we are editing this entry!
      ASSERT(iter != m_AEMD.pcore->GetEntryEndIter());
  
      // Now make the original equal to new intermediate state
      *(m_AEMD.pci_original) = *(m_AEMD.pci) = *(pci = &iter->second);
  
      // Now Reset starting values
      SetupInitialValues();
      SetChanged(false);
    }
    m_AEMD.entrysize = m_AEMD.pci->GetSize();
    m_pp_datetimes->UpdateStats();
  
    // Update the password history only if the password has been changed,
    // password history is being saved and the Add_Additional property page
    // has already been shown
    if (bIsPSWDModified && m_AEMD.SavePWHistory == TRUE && m_pp_additional->HasBeenShown()) {
      m_pp_additional->UpdatePasswordHistoryLC();
    }
    return TRUE;
  }
  
  BOOL CAddEdit_PropertySheet::PreTranslateMessage(MSG *pMsg)
  {
    // In View mode, there is no 'Cancel' button and 'OK' is renamed 'Close'
    // Make Escape key still work as designed
    if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) {
      if (m_AEMD.uicaller == IDS_VIEWENTRY || m_AEMD.ucprotected != 0) {
        CPWPropertySheet::EndDialog(IDCANCEL);
        return TRUE;
      } else {
        // Ask user whether to allow Escape to cancel
        CPWPropertyPage *pp = (CPWPropertyPage *)GetActivePage();
        BOOL brc = pp->OnQueryCancel();
        if (brc == TRUE)
          CPWPropertySheet::EndDialog(IDCANCEL);
       
        return TRUE;
      }
    }
  
    if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F1) {
      CAddEdit_PropertyPage *pp = (CAddEdit_PropertyPage *)GetActivePage();
      pp->PostMessage(WM_COMMAND, MAKELONG(ID_HELP, BN_CLICKED), NULL);
      return TRUE;
    }
  
    return CPWPropertySheet::PreTranslateMessage(pMsg);
  }
  
  void CAddEdit_PropertySheet::SetupInitialValues()
  {
    // This is called once when the property sheet is being initialised for Add & Edit
    // However, it is called again whenever the user does OnApply during Edit
  
    // Basic Data
    m_AEMD.entrysize = m_AEMD.pci->GetSize();
    m_AEMD.entry_uuid = m_AEMD.pci->GetUUID();
    m_AEMD.group = m_AEMD.pci->GetGroup();
    m_AEMD.title = m_AEMD.pci->GetTitle();
    m_AEMD.username = m_AEMD.pci->GetUser();
    m_AEMD.realpassword = m_AEMD.oldRealPassword = m_AEMD.pci->GetPassword();
    m_AEMD.lastpassword = m_AEMD.pci->GetPreviousPassword();
    m_AEMD.notes = m_AEMD.originalnotesTRC = m_AEMD.pci->GetNotes();
    m_AEMD.URL = m_AEMD.pci->GetURL();
    m_AEMD.email = m_AEMD.pci->GetEmail();
    m_AEMD.symbols = m_AEMD.oldsymbols = m_AEMD.pci->GetSymbols();
    m_AEMD.ioldownsymbols = m_AEMD.symbols.IsEmpty() == TRUE ?
                              DEFAULT_SYMBOLS : OWN_SYMBOLS;
    m_AEMD.iownsymbols = m_AEMD.ioldownsymbols;
    m_AEMD.pci->GetProtected(m_AEMD.ucprotected);
  
    if ((!m_AEMD.pcore->IsReadOnly() && m_AEMD.ucprotected == 0) &&
        m_AEMD.notes.GetLength() > MAXTEXTCHARS) {
      // Limit the Notes field to what can be displayed in Edit mode
      m_AEMD.notes =  m_AEMD.notes.Left(MAXTEXTCHARS);
      m_AEMD.originalnotesTRC = m_AEMD.notes;
      CGeneralMsgBox gmb;
      CString cs_text, cs_title(MAKEINTRESOURCE(IDS_WARNINGTEXTLENGTH));
      cs_text.Format(IDS_TRUNCATETEXT, MAXTEXTCHARS);
      gmb.MessageBox(cs_text, cs_title, MB_OK | MB_ICONEXCLAMATION);
    }
  
    // Entry type initialisation
    m_AEMD.original_entrytype = m_AEMD.pci->GetEntryType();
  
    const CItemData *pciA(m_AEMD.pci);
    if (m_AEMD.pci->IsAlias()) {
      pciA = m_AEMD.pcore->GetBaseEntry(m_AEMD.pci);
      if (pciA == NULL) {
        CString err(MAKEINTRESOURCE(IDS_ABASE_MISSING));
        throw err;
      }
    }
  
    // Additional data
    m_AEMD.autotype = m_AEMD.pci->GetAutoType();
    m_AEMD.runcommand = m_AEMD.pci->GetRunCommand();
    m_AEMD.pci->GetDCA(m_AEMD.DCA);
    m_AEMD.oldDCA = m_AEMD.DCA;
    m_AEMD.pci->GetShiftDCA(m_AEMD.ShiftDCA);
    m_AEMD.oldShiftDCA = m_AEMD.ShiftDCA;
    
    m_AEMD.pci->GetKBShortcut(m_AEMD.KBShortcut);
    m_AEMD.oldKBShortcut = m_AEMD.KBShortcut;
  
    // Date Time fields
    m_AEMD.locCTime = m_AEMD.pci->GetCTimeL();
    if (m_AEMD.locCTime.IsEmpty())
      m_AEMD.locCTime.LoadString(IDS_NA);
  
    m_AEMD.locATime = m_AEMD.pci->GetATimeL();
    if (m_AEMD.locATime.IsEmpty())
      m_AEMD.locATime.LoadString(IDS_NA);
  
    m_AEMD.locRMTime = m_AEMD.pci->GetRMTimeL();
    if (m_AEMD.locRMTime.IsEmpty())
      m_AEMD.locRMTime = m_AEMD.locCTime;
  
    m_AEMD.locPMTime = m_AEMD.pci->GetPMTimeL();
    if (m_AEMD.locPMTime.IsEmpty())
      m_AEMD.locPMTime = m_AEMD.locCTime;
  
    if (!m_AEMD.locPMTime.IsEmpty()) // ??? always true ???
      m_AEMD.pci->GetPMTime(m_AEMD.tttCPMTime);
  
    if ((long)m_AEMD.tttCPMTime == 0L) // if never changed - try creation date
      m_AEMD.pci->GetCTime(m_AEMD.tttCPMTime);
  
    // Note different pci depending on if Alias
    m_AEMD.locXTime = pciA->GetXTimeL();
    if (m_AEMD.locXTime.IsEmpty()) {
      m_AEMD.locXTime.LoadString(IDS_NEVER);
      m_AEMD.tttXTime = 0;
    } else {
      pciA->GetXTime(m_AEMD.tttXTime);
    }
    m_AEMD.oldlocXTime = m_AEMD.locXTime;
  
    // Note different pci depending on if Alias
    pciA->GetXTimeInt(m_AEMD.XTimeInt);
    m_AEMD.oldXTimeInt = m_AEMD.XTimeInt;
  
    // PWHistory fields
    // If user changes the password of its base entry from the
    // alias, we do record them in the base entry if the user wants them.
    // For an alias, we will show its base entry's password history
    size_t num_err;
    if (m_AEMD.pci->IsAlias())
      m_AEMD.PWHistory = pciA->GetPWHistory();
    else
      m_AEMD.PWHistory = m_AEMD.pci->GetPWHistory();
  
    BOOL HasHistory = CreatePWHistoryList(m_AEMD.PWHistory,
                                          m_AEMD.MaxPWHistory,
                                          num_err,
                                          m_AEMD.pwhistlist,
                                          PWSUtil::TMC_EXPORT_IMPORT) ? TRUE : FALSE;
    m_AEMD.oldNumPWHistory = m_AEMD.NumPWHistory = m_AEMD.pwhistlist.size();
    m_AEMD.oldSavePWHistory = m_AEMD.SavePWHistory = HasHistory;
    if (m_AEMD.MaxPWHistory == 0)
      m_AEMD.MaxPWHistory = PWSprefs::GetInstance()->GetPref(PWSprefs::NumPWHistoryDefault);
    
    // PWPolicy fields
    // Note different pci depending on if Alias
    pciA->GetPWPolicy(m_AEMD.pwp);
    m_AEMD.policyname = m_AEMD.oldpolicyname = pciA->GetPolicyName();
  
    if (!m_AEMD.policyname.IsEmpty())
      m_AEMD.ipolicy = NAMED_POLICY;
    else
    if (pciA->GetPWPolicy().empty())
      m_AEMD.ipolicy = DEFAULT_POLICY;
    else
      m_AEMD.ipolicy = SPECIFIC_POLICY;
  
    m_AEMD.oldipolicy = m_AEMD.ipolicy;
  
    if (m_AEMD.ipolicy == DEFAULT_POLICY) {
      m_AEMD.pwp = m_AEMD.default_pwp;
    }
  
    m_AEMD.oldpwp = m_AEMD.pwp;
  
    // Set up dependents
    m_AEMD.base_uuid = m_AEMD.original_base_uuid = pws_os::CUUID::NullUUID();
  
    pws_os::CUUID original_base_uuid(pws_os::CUUID::NullUUID());
  
    pws_os::CUUID original_uuid = m_AEMD.pci_original->GetUUID();  // Edit doesn't change this!
    if (m_AEMD.pci->IsBase()) {
      UUIDVector dependentslist;
      std::vector<StringX> vsxDependents;
  
      m_AEMD.pcore->GetAllDependentEntries(original_uuid, dependentslist,
                                    m_AEMD.pci->IsAliasBase() ?
                                    CItemData::ET_ALIAS : CItemData::ET_SHORTCUT);
      
      if (!dependentslist.empty()) {
        m_AEMD.pcore->SortDependents(dependentslist, vsxDependents);
      }
  
      m_AEMD.vsxdependents = vsxDependents;
      dependentslist.clear();
    } else
    if (m_AEMD.pci->IsAlias()) {
      // Get corresponding base entry
      const CItemData *pbci = m_AEMD.pcore->GetBaseEntry(m_AEMD.pci);
      ASSERT(pbci != NULL);
      if (pbci != NULL) {
        m_AEMD.base_uuid = m_AEMD.original_base_uuid = pbci->GetUUID();
        m_AEMD.base = L"[" +
                  pbci->GetGroup() + L":" +
                  pbci->GetTitle() + L":" +
                  pbci->GetUser()  + L"]";
        m_AEMD.realpassword = m_AEMD.oldRealPassword = m_AEMD.base;
      }
    } // IsAlias
  
    // Attachment
    if (m_AEMD.pci->HasAttRef()) {
      ASSERT(m_AEMD.pcore->HasAtt(m_AEMD.pci->GetAttUUID()));
      m_AEMD.oldattachment = m_AEMD.attachment = 
        m_AEMD.pcore->GetAtt(m_AEMD.pci->GetAttUUID());
    }
  }
  