TEST_F(CommandsTest, CreateShortcutEntry) { CItemData bi, si; bi.CreateUUID(); bi.SetTitle(L"base entry"); bi.SetPassword(L"base password"); const pws_os::CUUID base_uuid = bi.GetUUID(); si.SetTitle(L"shortcut to base"); si.SetPassword(L"[Shortcut]"); si.SetShortcut(); si.CreateUUID(); // call after setting to shortcut! time_t t; time(&t); si.SetCTime(t); si.SetXTime((time_t)0); si.SetStatus(CItemData::ES_ADDED); MultiCommands *pmulticmds = MultiCommands::Create(&core); pmulticmds->Add(AddEntryCommand::Create(&core, bi)); pmulticmds->Add(AddEntryCommand::Create(&core, si, base_uuid)); core.Execute(pmulticmds); EXPECT_EQ(2, core.GetNumEntries()); // Check that the base entry is correctly marked ItemListConstIter iter = core.Find(base_uuid); ASSERT_NE(core.GetEntryEndIter(), iter); EXPECT_TRUE(core.GetEntry(iter).IsShortcutBase()); core.Undo(); EXPECT_EQ(0, core.GetNumEntries()); core.Redo(); EXPECT_EQ(2, core.GetNumEntries()); // Delete base, expect both to be gone // Get base from core for correct type const CItemData bi2 = core.GetEntry(core.Find(base_uuid)); DeleteEntryCommand *pcmd1 = DeleteEntryCommand::Create(&core, bi2); core.Execute(pcmd1); EXPECT_EQ(0, core.GetNumEntries()); core.Undo(); EXPECT_EQ(2, core.GetNumEntries()); // Now just delete the shortcut, check that // base is left, and that it reverts to a normal entry const CItemData si2 = core.GetEntry(core.Find(si.GetUUID())); // si2 has baseUUID set DeleteEntryCommand *pcmd2 = DeleteEntryCommand::Create(&core, si2); core.Execute(pcmd2); ASSERT_EQ(1, core.GetNumEntries()); EXPECT_TRUE(core.GetEntry(core.Find(base_uuid)).IsNormal()); // Get core to delete any existing commands core.ClearCommands(); }
TEST_F(AliasShortcutTest, Alias) { CItemData al; al.CreateUUID(); al.SetTitle(L"alias"); al.SetPassword(L"alias-password-not-used"); al.SetUser(L"alias-user"); al.SetNotes(L"alias-notes"); al.SetGroup(L"Galias"); al.SetURL(L"http://alias-url.com"); al.SetAutoType(L"alias-autotype"); al.SetEmail(L"*****@*****.**"); al.SetRunCommand(L"Run alias, run"); al.SetAlias(); const pws_os::CUUID base_uuid = base.GetUUID(); MultiCommands *pmulticmds = MultiCommands::Create(&core); pmulticmds->Add(AddEntryCommand::Create(&core, base)); pmulticmds->Add(AddEntryCommand::Create(&core, al, base_uuid)); core.Execute(pmulticmds); EXPECT_EQ(2, core.GetNumEntries()); const CItemData al2 = core.GetEntry(core.Find(al.GetUUID())); StringX sx_group, sx_title, sx_user, sx_pswd, sx_lastpswd, sx_notes, sx_url, sx_email, sx_autotype, sx_runcmd; bool status = PWSAuxParse::GetEffectiveValues(&al2, &base, sx_group, sx_title, sx_user, sx_pswd, sx_lastpswd, sx_notes, sx_url, sx_email, sx_autotype, sx_runcmd); EXPECT_TRUE(status); // Password should be from base: EXPECT_EQ(sx_pswd, base.GetPassword()); // All the rest should be from alias: EXPECT_EQ(sx_group, al.GetGroup()); EXPECT_EQ(sx_title, al.GetTitle()); EXPECT_EQ(sx_user, al.GetUser()); EXPECT_EQ(sx_lastpswd, L""); EXPECT_EQ(sx_notes, al.GetNotes()); EXPECT_EQ(sx_url, al.GetURL()); EXPECT_EQ(sx_email, al.GetEmail()); EXPECT_EQ(sx_autotype, al.GetAutoType()); EXPECT_EQ(sx_runcmd, al.GetRunCommand()); }
void PWSTreeCtrl::FinishRenamingGroup(wxTreeEvent& evt, wxTreeItemId groupItem, const wxString& oldPath) { wxCHECK_RET(ItemIsGroup(groupItem), wxT("Cannot handle renaming of non-group items")); if (evt.IsEditCancelled()) return; // We DON'T need to handle these two as they can only occur while moving items // not removing groups as they become empty // renaming of groups that have only other groups as children MultiCommands* pmcmd = MultiCommands::Create(&m_core); if (!pmcmd) return; // For some reason, Command objects can't handle const references StringX sxOldPath = tostringx(oldPath); StringX sxNewPath = tostringx(GetItemGroup(groupItem)); // This takes care of modifying all the actual items pmcmd->Add(RenameGroupCommand::Create(&m_core, sxOldPath, sxNewPath)); // But we have to do the empty groups ourselves because EG_RENAME is not recursive typedef std::vector<StringX> EmptyGroupsArray; const EmptyGroupsArray& emptyGroups = m_core.GetEmptyGroups(); StringX sxOldPathWithDot = sxOldPath + _T('.'); for( EmptyGroupsArray::const_iterator itr = emptyGroups.begin(); itr != emptyGroups.end(); ++itr) { if (*itr == sxOldPath || itr->find(sxOldPathWithDot) == 0) { StringX sxOld = *itr; StringX sxNew = sxNewPath + itr->substr(sxOldPath.size()); pmcmd->Add(DBEmptyGroupsCommand::Create(&m_core, sxOld, sxNew)); } } if (pmcmd->GetSize()) m_core.Execute(pmcmd); // The old treeItem is gone, since it was renamed. We need to find the new one to select it wxTreeItemId newItem = Find(towxstring(sxNewPath), GetRootItem()); if (newItem.IsOk()) wxTreeCtrl::SelectItem(newItem); }
TEST_F(AliasShortcutTest, Shortcut) { CItemData sc; sc.SetTitle(L"shortcut"); sc.SetUser(L"sc-user"); sc.SetGroup(L"sc-group"); sc.SetPassword(L"[Shortcut]"); sc.SetShortcut(); sc.CreateUUID(); // call after setting to shortcut! const pws_os::CUUID base_uuid = base.GetUUID(); MultiCommands *pmulticmds = MultiCommands::Create(&core); pmulticmds->Add(AddEntryCommand::Create(&core, base)); pmulticmds->Add(AddEntryCommand::Create(&core, sc, base_uuid)); core.Execute(pmulticmds); EXPECT_EQ(2, core.GetNumEntries()); const CItemData sc2 = core.GetEntry(core.Find(sc.GetUUID())); StringX sx_group, sx_title, sx_user, sx_pswd, sx_lastpswd, sx_notes, sx_url, sx_email, sx_autotype, sx_runcmd; bool status = PWSAuxParse::GetEffectiveValues(&sc2, &base, sx_group, sx_title, sx_user, sx_pswd, sx_lastpswd, sx_notes, sx_url, sx_email, sx_autotype, sx_runcmd); EXPECT_TRUE(status); // Group, title and user should all be from sc: EXPECT_EQ(sx_group, sc.GetGroup()); EXPECT_EQ(sx_title, sc.GetTitle()); EXPECT_EQ(sx_user, sc.GetUser()); // All the rest should be from base: EXPECT_EQ(sx_pswd, base.GetPassword()); EXPECT_EQ(sx_lastpswd, L""); EXPECT_EQ(sx_notes, base.GetNotes()); EXPECT_EQ(sx_url, base.GetURL()); EXPECT_EQ(sx_email, base.GetEmail()); EXPECT_EQ(sx_autotype, base.GetAutoType()); EXPECT_EQ(sx_runcmd, base.GetRunCommand()); }
void CManagePasswordPolicies::OnOkClick( wxCommandEvent& ) { /* * User may have changed default policy, named policy, none or both. * If anything has changed, we treat the change as atomic, creating a multicommand * s.t. Undo/Redo will work as expected. */ PWPolicy olddefpol(PWSprefs::GetInstance()->GetDefaultPolicy()); bool defChanged = (olddefpol != m_st_default_pp); bool namedChanged = (m_MapPSWDPLC != m_core.GetPasswordPolicies()); if (defChanged || namedChanged) { MultiCommands *pmulticmds = MultiCommands::Create(&m_core); if (defChanged) { // User has changed database default policy - need to update preferences // Update the copy only! PWSprefs::GetInstance()->SetupCopyPrefs(); PWSprefs::GetInstance()->SetDefaultPolicy(m_st_default_pp, true); // Now get new DB preferences String value StringX sxNewDBPrefsString(PWSprefs::GetInstance()->Store(true)); // Set up Command to update string in database if (m_core.GetReadFileVersion() == PWSfile::VCURRENT) pmulticmds->Add(DBPrefsCommand::Create(&m_core, sxNewDBPrefsString)); } // defChanged if (namedChanged) { pmulticmds->Add(DBPolicyNamesCommand::Create(&m_core, m_MapPSWDPLC, DBPolicyNamesCommand::NP_REPLACEALL)); } m_core.Execute(pmulticmds); } // defChanged || namedChanged EndModal(wxID_OK); }
Command *PasswordSafeFrame::Delete(wxTreeItemId tid) { // Called for deleting a group // Recursively build the appropriate multi-command if (!tid) return NULL; MultiCommands *retval = MultiCommands::Create(&m_core); if (m_tree->GetChildrenCount(tid) > 0) { wxTreeItemIdValue cookie; wxTreeItemId ti = m_tree->GetFirstChild(tid, cookie); while (ti.IsOk()) { Command *delCmd = Delete(ti); if (delCmd != NULL) retval->Add(delCmd); ti = m_tree->GetNextChild(tid, cookie); } // while children // Explicitly delete any empty groups coinciding with this wxTreeItem's group hierarchy // Otherwise the user will see a these empty groups still hanging around inspite // of just deleting the parent/ancestor StringX sxGroup = tostringx(m_tree->GetItemGroup(tid)); if (m_core.IsEmptyGroup(sxGroup)) { Command *delGrp = DBEmptyGroupsCommand::Create(&m_core, sxGroup, DBEmptyGroupsCommand::EG_DELETE); if (delGrp) retval->Add(delGrp); } } else { // leaf CItemData *leaf = m_tree->GetItem(tid); if (leaf != NULL) { Command *delLeafCmd = Delete(leaf); // gets user conf. if needed if (delLeafCmd != NULL) retval->Add(delLeafCmd); } else { wxASSERT_MSG(m_tree->ItemIsGroup(tid), wxT("Childless item without CItemData must be an empty group")); StringX sxGroup = tostringx(m_tree->GetItemGroup(tid)); Command *delGrp = DBEmptyGroupsCommand::Create(&m_core, sxGroup, DBEmptyGroupsCommand::EG_DELETE); if (delGrp) retval->Add(delGrp); } } // If MultiCommands is empty, delete and return NULL if (retval->GetSize() == 0) { delete retval; retval = NULL; } return retval; }
void PWScore::Synchronize(PWScore *pothercore, const CItemData::FieldBits &bsFields, const bool &subgroup_bset, const stringT &subgroup_name, const int &subgroup_object, const int &subgroup_function, int &numUpdated, CReport *pRpt, bool *pbCancel) { /* Purpose: Synchronize entries from otherCore to m_core Algorithm: Foreach entry in otherCore Find in m_core if find a match update requested fields */ std::vector<StringX> vs_updated; numUpdated = 0; MultiCommands *pmulticmds = MultiCommands::Create(this); Command *pcmd1 = UpdateGUICommand::Create(this, UpdateGUICommand::WN_UNDO, UpdateGUICommand::GUI_UNDO_MERGESYNC); pmulticmds->Add(pcmd1); // Make sure we don't add it multiple times std::map<StringX, StringX> mapRenamedPolicies; std::vector<StringX> vs_PoliciesAdded; const StringX sxSync_DateTime = PWSUtil::GetTimeStamp(true).c_str(); ItemListConstIter otherPos; for (otherPos = pothercore->GetEntryIter(); otherPos != pothercore->GetEntryEndIter(); otherPos++) { // See if user has cancelled if (pbCancel != NULL && *pbCancel) { delete pmulticmds; return; } CItemData otherItem = pothercore->GetEntry(otherPos); CItemData::EntryType et = otherItem.GetEntryType(); // Do not process Aliases and Shortcuts if (et == CItemData::ET_ALIAS || et == CItemData::ET_SHORTCUT) continue; if (subgroup_bset && !otherItem.Matches(subgroup_name, subgroup_object, subgroup_function)) continue; const StringX sx_otherGroup = otherItem.GetGroup(); const StringX sx_otherTitle = otherItem.GetTitle(); const StringX sx_otherUser = otherItem.GetUser(); StringX sx_mergedentry; Format(sx_mergedentry, GROUPTITLEUSERINCHEVRONS, sx_otherGroup.c_str(), sx_otherTitle.c_str(), sx_otherUser.c_str()); ItemListConstIter foundPos = Find(sx_otherGroup, sx_otherTitle, sx_otherUser); if (foundPos != GetEntryEndIter()) { // found a match CItemData curItem = GetEntry(foundPos); // Don't update if entry is protected if (curItem.IsProtected()) continue; CItemData updItem(curItem); updItem.SetDisplayInfo(NULL); if (curItem.GetUUID() != otherItem.GetUUID()) { pws_os::Trace(_T("Synchronize: Mis-match UUIDs for [%ls:%ls:%ls]\n"), sx_otherGroup.c_str(), sx_otherTitle.c_str(), sx_otherUser.c_str()); } bool bUpdated(false); // Do not try and change GROUPTITLE = 0x00 (use GROUP & TITLE separately) or UUID = 0x01 for (size_t i = 2; i < bsFields.size(); i++) { if (bsFields.test(i)) { StringX sxValue = otherItem.GetFieldValue(static_cast<CItemData::FieldType>(i)); // Special processing for password policies (default & named) if (static_cast<CItemData::FieldType>(i) == CItemData::POLICYNAME) { Command *pPolicyCmd = ProcessPolicyName(pothercore, updItem, mapRenamedPolicies, vs_PoliciesAdded, sxValue, bUpdated, sxSync_DateTime, IDSC_SYNCPOLICY); if (pPolicyCmd != NULL) pmulticmds->Add(pPolicyCmd); } else { if (sxValue != updItem.GetFieldValue(static_cast<CItemData::FieldType>(i))) { bUpdated = true; updItem.SetFieldValue(static_cast<CItemData::FieldType>(i), sxValue); } } } } if (!bUpdated) continue; GUISetupDisplayInfo(updItem); updItem.SetStatus(CItemData::ES_MODIFIED); StringX sx_updated; Format(sx_updated, GROUPTITLEUSERINCHEVRONS, sx_otherGroup.c_str(), sx_otherTitle.c_str(), sx_otherUser.c_str()); vs_updated.push_back(sx_updated); Command *pcmd = EditEntryCommand::Create(this, curItem, updItem); pcmd->SetNoGUINotify(); pmulticmds->Add(pcmd); // Update the Wizard page UpdateWizard(sx_updated.c_str()); numUpdated++; } // Found match via [g:t:u] } // iteration over other core's entries stringT str_results; if (numUpdated > 0 && pRpt != NULL) { std::sort(vs_updated.begin(), vs_updated.end(), MergeSyncGTUCompare); stringT str_singular_plural_type, str_singular_plural_verb; LoadAString(str_singular_plural_type, numUpdated == 1 ? IDSC_ENTRY : IDSC_ENTRIES); LoadAString(str_singular_plural_verb, numUpdated == 1 ? IDSC_WAS : IDSC_WERE); Format(str_results, IDSC_SYNCHUPDATED, str_singular_plural_type.c_str(), str_singular_plural_verb.c_str()); pRpt->WriteLine(str_results.c_str()); for (size_t i = 0; i < vs_updated.size(); i++) { Format(str_results, L"\t%ls", vs_updated[i].c_str()); pRpt->WriteLine(str_results.c_str()); } } // See if user has cancelled if (pbCancel != NULL && *pbCancel) { delete pmulticmds; return; } Command *pcmd2 = UpdateGUICommand::Create(this, UpdateGUICommand::WN_REDO, UpdateGUICommand::GUI_REDO_MERGESYNC); pmulticmds->Add(pcmd2); Execute(pmulticmds); // See if user has cancelled too late - reset flag so incorrect information not given to user if (pbCancel != NULL && *pbCancel) { *pbCancel = false; return; } // tell the user we're done & provide short Synchronize report stringT str_entries; LoadAString(str_entries, numUpdated == 1 ? IDSC_ENTRY : IDSC_ENTRIES); Format(str_results, IDSC_SYNCHCOMPLETED, numUpdated, str_entries.c_str()); pRpt->WriteLine(str_results.c_str()); }
stringT PWScore::Merge(PWScore *pothercore, const bool &subgroup_bset, const stringT &subgroup_name, const int &subgroup_object, const int &subgroup_function, CReport *pRpt, bool *pbCancel) { std::vector<StringX> vs_added; std::vector<StringX> vs_AliasesAdded; std::vector<StringX> vs_ShortcutsAdded; std::vector<StringX> vs_PoliciesAdded; std::map<StringX, StringX> mapRenamedPolicies; const StringX sxMerge_DateTime = PWSUtil::GetTimeStamp(true).c_str(); stringT str_timestring; // To append to title if already in current database str_timestring = sxMerge_DateTime.c_str(); Remove(str_timestring, _T('/')); Remove(str_timestring, _T(':')); /* Purpose: Merge entries from otherCore to m_core Algorithm: Foreach entry in otherCore Find in m_core based on group/title/username if match found { if all other fields match { no merge } else { add to m_core with new title suffixed with -merged-YYYYMMDD-HHMMSS } } else { add to m_core directly } */ int numAdded = 0; int numConflicts = 0; int numAliasesAdded = 0; int numShortcutsAdded = 0; uuid_array_t base_uuid, new_base_uuid; bool bTitleRenamed(false); StringX sx_merged; LoadAString(sx_merged, IDSC_MERGED); MultiCommands *pmulticmds = MultiCommands::Create(this); Command *pcmd1 = UpdateGUICommand::Create(this, UpdateGUICommand::WN_UNDO, UpdateGUICommand::GUI_UNDO_MERGESYNC); pmulticmds->Add(pcmd1); ItemListConstIter otherPos; for (otherPos = pothercore->GetEntryIter(); otherPos != pothercore->GetEntryEndIter(); otherPos++) { // See if user has cancelled if (pbCancel != NULL && *pbCancel) { delete pmulticmds; return _T(""); } CItemData otherItem = pothercore->GetEntry(otherPos); CItemData::EntryType et = otherItem.GetEntryType(); // Need to check that entry keyboard shortcut not already in use! int32 iKBShortcut; otherItem.GetKBShortcut(iKBShortcut); CUUID kbshortcut_uuid = GetKBShortcut(iKBShortcut); bool bKBShortcutInUse = (iKBShortcut != 0&& kbshortcut_uuid != CUUID::NullUUID()); // Handle Aliases and Shortcuts when processing their base entries if (otherItem.IsDependent()) continue; if (subgroup_bset && !otherItem.Matches(subgroup_name, subgroup_object, subgroup_function)) continue; const StringX sx_otherGroup = otherItem.GetGroup(); const StringX sx_otherTitle = otherItem.GetTitle(); const StringX sx_otherUser = otherItem.GetUser(); StringX sxMergedEntry; Format(sxMergedEntry, GROUPTITLEUSERINCHEVRONS, sx_otherGroup.c_str(), sx_otherTitle.c_str(), sx_otherUser.c_str()); ItemListConstIter foundPos = Find(sx_otherGroup, sx_otherTitle, sx_otherUser); otherItem.GetUUID(base_uuid); memcpy(new_base_uuid, base_uuid, sizeof(new_base_uuid)); bTitleRenamed = false; if (foundPos != GetEntryEndIter()) { // Found a match, see if other fields also match CItemData curItem = GetEntry(foundPos); // Can't merge into a protected entry. If we were going to - add instead unsigned char ucprotected; curItem.GetProtected(ucprotected); stringT str_diffs(_T("")), str_temp; int diff_flags = 0; int32 cxtint, oxtint; time_t cxt, oxt; if (otherItem.GetPassword() != curItem.GetPassword()) { diff_flags |= MRG_PASSWORD; LoadAString(str_temp, IDSC_FLDNMPASSWORD); str_diffs += str_temp + _T(", "); } if (otherItem.GetNotes() != curItem.GetNotes()) { diff_flags |= MRG_NOTES; LoadAString(str_temp, IDSC_FLDNMNOTES); str_diffs += str_temp + _T(", "); } if (otherItem.GetURL() != curItem.GetURL()) { diff_flags |= MRG_URL; LoadAString(str_temp, IDSC_FLDNMURL); str_diffs += str_temp + _T(", "); } if (otherItem.GetAutoType() != curItem.GetAutoType()) { diff_flags |= MRG_AUTOTYPE; LoadAString(str_temp, IDSC_FLDNMAUTOTYPE); str_diffs += str_temp + _T(", "); } if (otherItem.GetPWHistory() != curItem.GetPWHistory()) { diff_flags |= MRG_HISTORY; LoadAString(str_temp, IDSC_FLDNMPWHISTORY); str_diffs += str_temp + _T(", "); } // Don't test policy or symbols if either entry is using a named policy // as these are meaningless to compare if (otherItem.GetPolicyName().empty() && curItem.GetPolicyName().empty()) { PWPolicy cur_pwp, oth_pwp; if (curItem.GetPWPolicy().empty()) cur_pwp = PWSprefs::GetInstance()->GetDefaultPolicy(); else curItem.GetPWPolicy(cur_pwp); if (otherItem.GetPWPolicy().empty()) oth_pwp = PWSprefs::GetInstance()->GetDefaultPolicy(true); else otherItem.GetPWPolicy(oth_pwp); if (cur_pwp != oth_pwp) { diff_flags |= MRG_POLICY; LoadAString(str_temp, IDSC_FLDNMPWPOLICY); str_diffs += str_temp + _T(", "); } } otherItem.GetXTime(oxt); curItem.GetXTime(cxt); if (oxt != cxt) { diff_flags |= MRG_XTIME; LoadAString(str_temp, IDSC_FLDNMXTIME); str_diffs += str_temp + _T(", "); } otherItem.GetXTimeInt(oxtint); curItem.GetXTimeInt(cxtint); if (oxtint != cxtint) { diff_flags |= MRG_XTIME_INT; LoadAString(str_temp, IDSC_FLDNMXTIMEINT); str_diffs += str_temp + _T(", "); } if (otherItem.GetRunCommand() != curItem.GetRunCommand()) { diff_flags |= MRG_EXECUTE; LoadAString(str_temp, IDSC_FLDNMRUNCOMMAND); str_diffs += str_temp + _T(", "); } // Must use integer values not compare strings short other_hDCA, cur_hDCA; otherItem.GetDCA(other_hDCA); curItem.GetDCA(cur_hDCA); if (other_hDCA != cur_hDCA) { diff_flags |= MRG_DCA; LoadAString(str_temp, IDSC_FLDNMDCA); str_diffs += str_temp + _T(", "); } if (otherItem.GetEmail() != curItem.GetEmail()) { diff_flags |= MRG_EMAIL; LoadAString(str_temp, IDSC_FLDNMEMAIL); str_diffs += str_temp + _T(", "); } if (otherItem.GetSymbols() != curItem.GetSymbols()) { diff_flags |= MRG_SYMBOLS; LoadAString(str_temp, IDSC_FLDNMSYMBOLS); str_diffs += str_temp + _T(", "); } otherItem.GetShiftDCA(other_hDCA); curItem.GetShiftDCA(cur_hDCA); if (other_hDCA != cur_hDCA) { diff_flags |= MRG_SHIFTDCA; LoadAString(str_temp, IDSC_FLDNMSHIFTDCA); str_diffs += str_temp + _T(", "); } PWPolicy st_to_pp, st_from_pp; StringX sxCurrentPolicyName = curItem.GetPolicyName(); StringX sxOtherPolicyName = otherItem.GetPolicyName(); bool bCurrent(false), bOther(false); if (!sxCurrentPolicyName.empty()) bCurrent = GetPolicyFromName(sxCurrentPolicyName, st_to_pp); if (!sxOtherPolicyName.empty()) bOther = pothercore->GetPolicyFromName(sxOtherPolicyName, st_from_pp); /* There will be differences if only one has a named password policy, or both have policies but the new entry's one is not in our database, or both have the same policy but they are different */ if ((bCurrent && !bOther) || (!bCurrent && bOther) || sxCurrentPolicyName != sxOtherPolicyName || (bCurrent && bOther && st_to_pp != st_from_pp)) { diff_flags |= MRG_POLICYNAME; LoadAString(str_temp, IDSC_FLDNMPWPOLICYNAME); str_diffs += str_temp + _T(", "); } if (diff_flags != 0) { // have a match on group/title/user, but not on other fields // add an entry suffixed with -merged-YYYYMMDD-HHMMSS StringX sx_newTitle; Format(sx_newTitle, L"%ls-%ls-%ls", sx_otherTitle.c_str(), sx_merged.c_str(), str_timestring.c_str()); // note it as an issue for the user stringT strWarnMsg; Format(strWarnMsg, IDSC_MERGECONFLICTS, sx_otherGroup.c_str(), sx_otherTitle.c_str(), sx_otherUser.c_str(), sx_otherGroup.c_str(), sx_newTitle.c_str(), sx_otherUser.c_str(), str_diffs.c_str()); // log it if (pRpt != NULL) pRpt->WriteLine(strWarnMsg.c_str()); // Check no conflict of unique uuid if (Find(base_uuid) != GetEntryEndIter()) { otherItem.CreateUUID(); otherItem.GetUUID(new_base_uuid); } // Special processing for password policies (default & named) bool bUpdated; // not needed for Merge Command *pPolicyCmd = ProcessPolicyName(pothercore, otherItem, mapRenamedPolicies, vs_PoliciesAdded, sxOtherPolicyName, bUpdated, sxMerge_DateTime, IDSC_MERGEPOLICY); if (pPolicyCmd != NULL) pmulticmds->Add(pPolicyCmd); // About to add entry - check keyboard shortcut if (bKBShortcutInUse) { // Remove it otherItem.SetKBShortcut(0); // Tell user via the report ItemListIter iter = Find(kbshortcut_uuid); if (iter != m_pwlist.end()) { StringX sxTemp, sxExistingEntry; Format(sxExistingEntry, GROUPTITLEUSERINCHEVRONS, iter->second.GetGroup().c_str(), iter->second.GetTitle().c_str(), iter->second.GetUser().c_str()); Format(sxTemp, IDSC_KBSHORTCUT_REMOVED, sx_merged.c_str(), sxMergedEntry.c_str(), sxExistingEntry.c_str(), sx_merged.c_str()); pRpt->WriteLine(sxTemp.c_str()); } } otherItem.SetTitle(sx_newTitle); otherItem.SetStatus(CItemData::ES_ADDED); Command *pcmd = AddEntryCommand::Create(this, otherItem); pcmd->SetNoGUINotify(); pmulticmds->Add(pcmd); // Update the Wizard page UpdateWizard(sxMergedEntry.c_str()); numConflicts++; } } else { // Didn't find any match...add it directly // Check no conflict of unique uuid if (Find(base_uuid) != GetEntryEndIter()) { otherItem.CreateUUID(); otherItem.GetUUID(new_base_uuid); } // Special processing for password policies (default & named) bool bUpdated; // Not needed for Merge StringX sxOtherPolicyName = otherItem.GetPolicyName(); Command *pPolicyCmd = ProcessPolicyName(pothercore, otherItem, mapRenamedPolicies, vs_PoliciesAdded, sxOtherPolicyName, bUpdated, sxMerge_DateTime, IDSC_MERGEPOLICY); if (pPolicyCmd != NULL) pmulticmds->Add(pPolicyCmd); // About to add entry - check keyboard shortcut if (bKBShortcutInUse) { // Remove it otherItem.SetKBShortcut(0); // Tell user via the report ItemListIter iter = Find(kbshortcut_uuid); if (iter != m_pwlist.end()) { StringX sxTemp, sxExistingEntry; Format(sxExistingEntry, GROUPTITLEUSERINCHEVRONS, iter->second.GetGroup().c_str(), iter->second.GetTitle().c_str(), iter->second.GetUser().c_str()); Format(sxTemp, IDSC_KBSHORTCUT_REMOVED, sx_merged.c_str(), sxMergedEntry.c_str(), sxExistingEntry.c_str(), sx_merged.c_str()); pRpt->WriteLine(sxTemp.c_str()); } } otherItem.SetStatus(CItemData::ES_ADDED); Command *pcmd = AddEntryCommand::Create(this, otherItem); pcmd->SetNoGUINotify(); pmulticmds->Add(pcmd); StringX sx_added; Format(sx_added, GROUPTITLEUSERINCHEVRONS, sx_otherGroup.c_str(), sx_otherTitle.c_str(), sx_otherUser.c_str()); vs_added.push_back(sx_added); // Update the Wizard page UpdateWizard(sx_added.c_str()); numAdded++; } if (et == CItemData::ET_ALIASBASE) numAliasesAdded += MergeDependents(pothercore, pmulticmds, base_uuid, new_base_uuid, bTitleRenamed, str_timestring, CItemData::ET_ALIAS, vs_AliasesAdded); if (et == CItemData::ET_SHORTCUTBASE) numShortcutsAdded += MergeDependents(pothercore, pmulticmds, base_uuid, new_base_uuid, bTitleRenamed, str_timestring, CItemData::ET_SHORTCUT, vs_ShortcutsAdded); } // iteration over other core's entries stringT str_results; if (numAdded > 0 && pRpt != NULL) { std::sort(vs_added.begin(), vs_added.end(), MergeSyncGTUCompare); stringT str_singular_plural_type, str_singular_plural_verb; LoadAString(str_singular_plural_type, numAdded == 1 ? IDSC_ENTRY : IDSC_ENTRIES); LoadAString(str_singular_plural_verb, numAdded == 1 ? IDSC_WAS : IDSC_WERE); Format(str_results, IDSC_MERGEADDED, str_singular_plural_type.c_str(), str_singular_plural_verb.c_str()); pRpt->WriteLine(str_results.c_str()); for (size_t i = 0; i < vs_added.size(); i++) { Format(str_results, L"\t%ls", vs_added[i].c_str()); pRpt->WriteLine(str_results.c_str()); } } if (numAliasesAdded > 0 && pRpt != NULL) { std::sort(vs_AliasesAdded.begin(), vs_AliasesAdded.end(), MergeSyncGTUCompare); stringT str_singular_plural_type, str_singular_plural_verb; LoadAString(str_singular_plural_type, numAliasesAdded == 1 ? IDSC_ENTRY : IDSC_ENTRIES); LoadAString(str_singular_plural_verb, numAliasesAdded == 1 ? IDSC_WAS : IDSC_WERE); Format(str_results, IDSC_MERGEADDED, str_singular_plural_type.c_str(), str_singular_plural_verb.c_str()); pRpt->WriteLine(str_results.c_str()); for (size_t i = 0; i < vs_AliasesAdded.size(); i++) { Format(str_results, _T("\t%ls"), vs_AliasesAdded[i].c_str()); pRpt->WriteLine(str_results.c_str()); } } if (numShortcutsAdded > 0 && pRpt != NULL) { std::sort(vs_ShortcutsAdded.begin(), vs_ShortcutsAdded.end(), MergeSyncGTUCompare); stringT str_singular_plural_type, str_singular_plural_verb; LoadAString(str_singular_plural_type, numShortcutsAdded == 1 ? IDSC_ENTRY : IDSC_ENTRIES); LoadAString(str_singular_plural_verb, numShortcutsAdded == 1 ? IDSC_WAS : IDSC_WERE); Format(str_results, IDSC_MERGEADDED, str_singular_plural_type.c_str(), str_singular_plural_verb.c_str()); pRpt->WriteLine(str_results.c_str()); for (size_t i = 0; i < vs_ShortcutsAdded.size(); i++) { Format(str_results, L"\t%ls", vs_ShortcutsAdded[i].c_str()); pRpt->WriteLine(str_results.c_str()); } } // See if user has cancelled if (pbCancel != NULL && *pbCancel) { delete pmulticmds; return _T(""); } Command *pcmd2 = UpdateGUICommand::Create(this, UpdateGUICommand::WN_REDO, UpdateGUICommand::GUI_REDO_MERGESYNC); pmulticmds->Add(pcmd2); Execute(pmulticmds); // See if user has cancelled too late - reset flag so incorrect information not given to user if (pbCancel != NULL && *pbCancel) { *pbCancel = false; } // Tell the user we're done & provide short merge report stringT str_entries, str_conflicts, str_aliases, str_shortcuts; int totalAdded = numAdded + numConflicts + numAliasesAdded + numShortcutsAdded; LoadAString(str_entries, totalAdded == 1 ? IDSC_ENTRY : IDSC_ENTRIES); LoadAString(str_conflicts, numConflicts == 1 ? IDSC_CONFLICT : IDSC_CONFLICTS); LoadAString(str_aliases, numAliasesAdded == 1 ? IDSC_ALIAS : IDSC_ALIASES); LoadAString(str_shortcuts, numShortcutsAdded == 1 ? IDSC_SHORTCUT : IDSC_SHORTCUTS); Format(str_results, IDSC_MERGECOMPLETED, totalAdded, str_entries.c_str(), numConflicts, str_conflicts.c_str(), numAliasesAdded, str_aliases.c_str(), numShortcutsAdded, str_shortcuts.c_str()); pRpt->WriteLine(str_results.c_str()); return str_results; }