// Return whether first '«g» «t» «u»' is greater than the second '«g» «t» «u»' // used in std::sort below. // Need this as '»' is not in the correct lexical order for blank fields in entry bool MergeSyncGTUCompare(const StringX &elem1, const StringX &elem2) { StringX g1, t1, u1, g2, t2, u2, tmp1, tmp2; StringX::size_type i1 = elem1.find(L'\xbb'); g1 = (i1 == StringX::npos) ? elem1 : elem1.substr(0, i1 - 1); StringX::size_type i2 = elem2.find(L'\xbb'); g2 = (i2 == StringX::npos) ? elem2 : elem2.substr(0, i2 - 1); if (g1 != g2) return g1.compare(g2) < 0; tmp1 = elem1.substr(g1.length() + 3); tmp2 = elem2.substr(g2.length() + 3); i1 = tmp1.find(L'\xbb'); t1 = (i1 == StringX::npos) ? tmp1 : tmp1.substr(0, i1 - 1); i2 = tmp2.find(L'\xbb'); t2 = (i2 == StringX::npos) ? tmp2 : tmp2.substr(0, i2 - 1); if (t1 != t2) return t1.compare(t2) < 0; tmp1 = tmp1.substr(t1.length() + 3); tmp2 = tmp2.substr(t2.length() + 3); i1 = tmp1.find(L'\xbb'); u1 = (i1 == StringX::npos) ? tmp1 : tmp1.substr(0, i1 - 1); i2 = tmp2.find(L'\xbb'); u2 = (i2 == StringX::npos) ? tmp2 : tmp2.substr(0, i2 - 1); return u1.compare(u2) < 0; }
static void ParseNotes(StringX &sxNotes, std::vector<StringX> &vsxnotes_lines) { if (!sxNotes.empty()) { // Use \n and \r to tokenise this line StringX::size_type st_start(0), st_end(0); const StringX sxdelim = _T("\r\n"); StringX sxline; StringX::size_type st_index; while (st_end != StringX::npos) { st_end = sxNotes.find(sxdelim, st_start); sxline = (sxNotes.substr(st_start, (st_end == StringX::npos) ? StringX::npos : st_end - st_start)); st_index = 0; // Remove all tabs - \t for (;;) { st_index = sxline.find(_T("\\t"), st_index); if (st_index == StringX::npos) break; sxline.replace(st_index, 2, _T("")); st_index += 1; } vsxnotes_lines.push_back(sxline); st_start = ((st_end > (StringX::npos - sxdelim.size())) ? StringX::npos : st_end + sxdelim.size()); } } }
bool FindNoCase( const StringX& src, const StringX& dest) { StringX srcLower = src; ToLower(srcLower); StringX destLower = dest; ToLower(destLower); return destLower.find(srcLower) != StringX::npos; }
void PasswordSafeSearch::FindMatches(const StringX& searchText, bool fCaseSensitive, SearchPointer& searchPtr, const CItemData::FieldBits& bsFields, bool fUseSubgroups, const wxString& subgroupText, CItemData::FieldType subgroupObject, PWSMatch::MatchRule subgroupFunction, bool subgroupFunctionCaseSensitive, Iter begin, Iter end, Accessor afn) { if (searchText.empty()) return; searchPtr.Clear(); typedef StringX (CItemData::*ItemDataFuncT)() const; struct { CItemData::FieldType type; ItemDataFuncT func; } ItemDataFields[] = { {CItemData::GROUP, &CItemData::GetGroup}, {CItemData::TITLE, &CItemData::GetTitle}, {CItemData::USER, &CItemData::GetUser}, {CItemData::PASSWORD, &CItemData::GetPassword}, // {CItemData::NOTES, &CItemData::GetNotes}, {CItemData::URL, &CItemData::GetURL}, {CItemData::EMAIL, &CItemData::GetEmail}, {CItemData::RUNCMD, &CItemData::GetRunCommand}, {CItemData::AUTOTYPE, &CItemData::GetAutoType}, {CItemData::XTIME_INT, &CItemData::GetXTimeInt}, }; for ( Iter itr = begin; itr != end; ++itr) { const int fn = (subgroupFunctionCaseSensitive? -subgroupFunction: subgroupFunction); if (fUseSubgroups && !afn(itr).Matches(stringT(subgroupText.c_str()), subgroupObject, fn)) continue; bool found = false; for (size_t idx = 0; idx < NumberOf(ItemDataFields) && !found; ++idx) { if (bsFields.test(ItemDataFields[idx].type)) { const StringX str = (afn(itr).*ItemDataFields[idx].func)(); found = fCaseSensitive? str.find(searchText) != StringX::npos: FindNoCase(searchText, str); } } if (!found && bsFields.test(CItemData::NOTES)) { StringX str = afn(itr).GetNotes(); found = fCaseSensitive? str.find(searchText) != StringX::npos: FindNoCase(searchText, str); } if (!found && bsFields.test(CItemData::PWHIST)) { size_t pwh_max, err_num; PWHistList pwhistlist; CreatePWHistoryList(afn(itr).GetPWHistory(), pwh_max, err_num, pwhistlist, PWSUtil::TMC_XML); for (PWHistList::iterator iter = pwhistlist.begin(); iter != pwhistlist.end(); iter++) { PWHistEntry pwshe = *iter; found = fCaseSensitive? pwshe.password.find(searchText) != StringX::npos: FindNoCase(searchText, pwshe.password ); if (found) break; // break out of for loop } pwhistlist.clear(); } if (found) { uuid_array_t uuid; afn(itr).GetUUID(uuid); searchPtr.Add(pws_os::CUUID(uuid)); } } }
//----------------------------------------------------------------- // Internal functions //----------------------------------------------------------------- static UINT ParseRunCommand(const StringX &sxInputString, std::vector<st_RunCommandTokens> &v_rctokens, bool &bDoAutoType, StringX &sxAutoType, stringT &serrmsg, StringX::size_type &st_column) { // tokenize into separate elements std::vector<st_RunCommandTokens>::iterator rc_iter; std::vector<size_t> v_pos; StringX::iterator str_Iter; st_RunCommandTokens st_rctoken; size_t st_num_quotes(0); UINT uierr(0); int var_index(0); const stringT alphanum = _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); if (sxInputString.empty()) { // String is empty! uierr = IDSC_EXS_INPUTEMPTY; goto exit; } for (StringX::size_type l = 0; l < sxInputString.length(); l++) { if (sxInputString[l] == _T('"')) st_num_quotes++; } if (st_num_quotes % 2 != 0) { st_column = sxInputString.find(_T('"')); // Unmatched quotes uierr = IDSC_EXS_UNMATCHEDQUOTES; goto exit; } // tokenize into separate elements using $ as the field separator for (StringX::size_type st_startpos = 0; st_startpos < sxInputString.size(); /* st_startpos advanced in body */) { StringX::size_type st_next = sxInputString.find(_T('$'), st_startpos); if (st_next == StringX::npos) st_next = sxInputString.size(); if (st_next > 0) { st_rctoken.sxname = sxInputString.substr(st_startpos, st_next - st_startpos); st_rctoken.sxindex = _T(""); st_rctoken.index = 0; st_rctoken.is_variable = st_startpos == 0 ? false : true; st_rctoken.has_brackets = false; v_rctokens.push_back(st_rctoken); v_pos.push_back(st_startpos); } st_startpos = st_next + 1; // too complex for for statement } // tokenization for loop // Check if escaped - ending character of previous token == '\' // Make sure this '\' is not escaped itself! for (size_t st_idx = v_rctokens.size() - 1; st_idx > 0 ; st_idx--) { st_RunCommandTokens &st_rctokens = v_rctokens[st_idx - 1]; StringX::size_type name_len = st_rctokens.sxname.length(); if (name_len == 0 || (name_len >= 2 && st_rctokens.sxname.substr(name_len - 2, 2).compare(_T("\\\\")) == 0)) continue; if (st_rctokens.sxname.substr(name_len - 1, 1).compare(_T("\\")) == 0) { st_rctokens.sxname = st_rctokens.sxname.substr(0, name_len - 1) + _T("$") + v_rctokens[st_idx].sxname; v_rctokens.erase(v_rctokens.begin() + st_idx); } } // Check if variable enclosed in curly brackets for (size_t st_idx = 0; st_idx < v_rctokens.size(); st_idx++) { if (v_rctokens[st_idx].sxname.length() == 0) continue; str_Iter = v_rctokens[st_idx].sxname.begin(); // Does it start with a curly bracket? if (*str_Iter == _T('{')) { v_rctokens[st_idx].has_brackets = true; StringX sxvar, sxnonvar, sxindex(_T("")); // Yes - Find end curly bracket StringX::size_type st_end_cb = v_rctokens[st_idx].sxname.find(_T('}')); if (st_end_cb == StringX::npos) { st_column = v_pos[st_idx] + v_rctokens[st_idx].sxname.length(); // Missing end curly bracket uierr = IDSC_EXS_MISSINGCURLYBKT; goto exit; } // Now see if there is an Index here StringX::size_type st_start_sb = v_rctokens[st_idx].sxname.find(_T('[')); if (st_start_sb != StringX::npos) { // Yes - find end square bracket if (st_start_sb > st_end_cb) { // Square backet after end of variable sxvar = v_rctokens[st_idx].sxname.substr(1, st_end_cb - 1); sxnonvar = v_rctokens[st_idx].sxname.substr(st_end_cb + 1); v_rctokens[st_idx].sxname = sxvar; if (sxnonvar.length() > 0) { st_rctoken.sxname = sxnonvar; st_rctoken.sxindex = _T(""); st_rctoken.index = 0; st_rctoken.is_variable = false; st_rctoken.has_brackets = false; v_rctokens.insert(v_rctokens.begin() + st_idx + 1, st_rctoken); v_pos.insert(v_pos.begin() + st_idx + 1, v_pos[st_idx] + st_end_cb); } continue; } StringX::size_type st_end_sb = v_rctokens[st_idx].sxname.find(_T(']'), st_start_sb); if (st_end_sb == StringX::npos) { st_column = v_pos[st_idx] + 1; // Missing end square bracket uierr = IDSC_EXS_MISSINGSQUAREBKT; goto exit; } // The end-curly backet must immediately follow the end-square bracket if (st_end_cb != st_end_sb + 1) { st_column = v_pos[st_idx] + st_end_sb + 1; // Characters between ']' and ')' uierr = IDSC_EXS_INVALIDBRACKETS; goto exit; } sxindex = v_rctokens[st_idx].sxname.substr(st_start_sb + 1, st_end_sb - st_start_sb - 1); v_rctokens[st_idx].sxindex = sxindex; // Now check index uierr = ProcessIndex(sxindex, var_index, st_column); if (uierr > 0) { st_column += v_pos[st_idx]; goto exit; } v_rctokens[st_idx].index = var_index; sxvar = v_rctokens[st_idx].sxname.substr(1, st_start_sb - 1); sxnonvar = v_rctokens[st_idx].sxname.substr(st_end_cb + 1); } else { // No square bracket // Split current token into 'variable' and 'non-variable' parts sxvar = v_rctokens[st_idx].sxname.substr(1, st_end_cb - 1); sxnonvar = v_rctokens[st_idx].sxname.substr(st_end_cb + 1); } v_rctokens[st_idx].sxname = sxvar; if (sxnonvar.length() > 0) { st_rctoken.sxname = sxnonvar; st_rctoken.sxindex = _T(""); st_rctoken.index = 0; st_rctoken.is_variable = false; st_rctoken.has_brackets = false; v_rctokens.insert(v_rctokens.begin() + st_idx + 1, st_rctoken); v_pos.insert(v_pos.begin() + st_idx + 1, v_pos[st_idx] + st_end_cb); } } } // Now use rules of variables to get the real variable for (size_t st_idx = 0; st_idx < v_rctokens.size(); st_idx++) { if (!v_rctokens[st_idx].is_variable) continue; if (v_rctokens[st_idx].sxname.length() == 0) { st_column = v_pos[st_idx]; // Variable name is empty uierr = IDSC_EXS_VARNAMEEMPTY; goto exit; } str_Iter = v_rctokens[st_idx].sxname.begin(); if (!isalpha(*str_Iter)) { st_column = v_pos[st_idx]; // First character of variable is not alphabetic uierr = IDSC_EXS_FIRSTNOTALPHA; goto exit; } StringX::size_type st_next = v_rctokens[st_idx].sxname.find_first_not_of(alphanum.c_str()); if (st_next != StringX::npos) { // Split current token into 'variable' and 'non-variable' parts StringX sxvar = v_rctokens[st_idx].sxname.substr(0, st_next); StringX sxnonvar = v_rctokens[st_idx].sxname.substr(st_next); v_rctokens[st_idx].sxname = sxvar; // Before saving non-variable part - check if it is an Index e.g. var[i] if (sxnonvar.c_str()[0] == _T('[')) { // Find ending square bracket StringX::size_type st_end_sb = sxnonvar.find(_T(']')); if (st_end_sb == StringX::npos) { st_column = v_pos[st_idx] + sxvar.length() + 2; // Missing end square bracket uierr = IDSC_EXS_MISSINGSQUAREBKT; goto exit; } StringX sxindex = sxnonvar.substr(1, st_end_sb - 1); v_rctokens[st_idx].sxindex = sxindex; // Now check index uierr = ProcessIndex(sxindex, var_index, st_column); if (uierr > 0) { st_column += v_pos[st_idx] + sxvar.length(); goto exit; } v_rctokens[st_idx].index = var_index; sxnonvar = sxnonvar.substr(st_end_sb + 1); } else { // Not a square bracket if (v_rctokens[st_idx].has_brackets) { st_column = v_pos[st_idx] + st_next + 1; // Variable must be alphanumeric uierr = IDSC_EXS_VARNAMEINVALID; goto exit; } } if (!sxnonvar.empty()) { st_rctoken.sxname = sxnonvar; st_rctoken.sxindex = _T(""); st_rctoken.index = 0; st_rctoken.is_variable = false; st_rctoken.has_brackets = false; v_rctokens.insert(v_rctokens.begin() + st_idx + 1, st_rctoken); v_pos.insert(v_pos.begin() + st_idx + 1, v_pos[st_idx] + st_next); } } } // Special Autotype processing bDoAutoType = false; sxAutoType = _T(""); for (size_t st_idx = 0; st_idx < v_rctokens.size(); st_idx++) { if (!v_rctokens[st_idx].is_variable) continue; // Is it a autotype variable? if (v_rctokens[st_idx].sxname == _T("a") || v_rctokens[st_idx].sxname == _T("autotype")) { bDoAutoType = true; // Is the next token text and starts with '('? if (st_idx + 1 < v_rctokens.size() && !v_rctokens[st_idx + 1].is_variable && v_rctokens[st_idx + 1].sxname.c_str()[0] == _T('(')) { // Find ending round bracket StringX sx_autotype = v_rctokens[st_idx + 1].sxname; StringX::size_type st_end_rb = sx_autotype.find(_T(')')); if (st_end_rb == StringX::npos) { st_column = v_pos[st_idx + 1] + sx_autotype.length() + 2; // Missing end round bracket uierr = IDSC_EXS_MISSINGROUNDBKT; goto exit; } sxAutoType = sx_autotype.substr(1, st_end_rb - 1); v_rctokens[st_idx + 1].sxname = sx_autotype.substr(st_end_rb + 1); // Check if anything left in this text - none -> delete if (v_rctokens[st_idx + 1].sxname.length() == 0) v_rctokens.erase(v_rctokens.begin() + st_idx + 1); // Now delete Autotype variable v_rctokens.erase(v_rctokens.begin() + st_idx); break; } } } exit: if (uierr != 0) LoadAString(serrmsg, uierr); else serrmsg = _T(""); if (uierr > 0) { v_rctokens.clear(); } v_pos.clear(); return uierr; }
StringX PWSAuxParse::GetAutoTypeString(const StringX &sx_in_autotype, const StringX &sx_group, const StringX &sx_title, const StringX &sx_user, const StringX &sx_pwd, const StringX &sx_notes, const StringX &sx_url, const StringX &sx_email, std::vector<size_t> &vactionverboffsets) { StringX sxtmp(_T("")); StringX sxNotes(sx_notes); TCHAR curChar; StringX sx_autotype(sx_in_autotype); StringX::size_type st_index; std::vector<StringX> vsxnotes_lines; vactionverboffsets.clear(); // If empty, try the database default if (sx_autotype.empty()) { sx_autotype = PWSprefs::GetInstance()-> GetPref(PWSprefs::DefaultAutotypeString); // If still empty, take this default if (sx_autotype.empty()) { // checking for user and password for default settings if (!sx_pwd.empty()){ if (!sx_user.empty()) sx_autotype = DEFAULT_AUTOTYPE; else sx_autotype = _T("\\p\\n"); } } } // No recursive substitution (e.g. \p or \u), although '\t' will be replaced by a tab if (!sx_notes.empty()) { // Use \n and \r to tokenise this line StringX::size_type st_start(0), st_end(0); const StringX sxdelim = _T("\r\n"); StringX sxline; while (st_end != StringX::npos) { st_end = sxNotes.find_first_of(sxdelim, st_start); sxline = (sxNotes.substr(st_start, (st_end == StringX::npos) ? StringX::npos : st_end - st_start)); st_index = 0; for (;;) { st_index = sxline.find(_T("\\t"), st_index); if (st_index == StringX::npos) break; sxline.replace(st_index, 2, _T("\t")); st_index += 1; } vsxnotes_lines.push_back(sxline); // If we just hit a "\r\n", move past it. Or else, it is a "\r" without // a following "\n" or a "\n", so just move past one single char if (st_end != StringX::npos) { st_start = st_end + (sxNotes.compare(st_end, 2, sxdelim) == 0 ? 2 : 1); if (st_start >= sxNotes.length()) break; } } // Now change '\n' to '\r' in the complete notes field st_index = 0; for (;;) { st_index = sxNotes.find(sxdelim, st_index); if (st_index == StringX::npos) break; sxNotes.replace(st_index, 2, _T("\r")); st_index += 1; } st_index = 0; for (;;) { st_index = sxNotes.find(_T("\\t"), st_index); if (st_index == StringX::npos) break; sxNotes.replace(st_index, 2, _T("\t")); st_index += 1; } } const size_t N = sx_autotype.length(); const StringX sxZeroes = _T("000"); int gNumIts; for (size_t n = 0; n < N; n++){ curChar = sx_autotype[n]; if (curChar == TCHAR('\\')) { n++; if (n < N) curChar = sx_autotype[n]; switch (curChar){ case TCHAR('\\'): sxtmp += TCHAR('\\'); break; case TCHAR('n'): case TCHAR('r'): sxtmp += TCHAR('\r'); break; case TCHAR('t'): sxtmp += TCHAR('\t'); break; case TCHAR('s'): sxtmp += TCHAR('\v'); break; case TCHAR('g'): sxtmp += sx_group; break; case TCHAR('i'): sxtmp += sx_title; break; case TCHAR('u'): sxtmp += sx_user; break; case TCHAR('p'): sxtmp += sx_pwd; break; case TCHAR('l'): sxtmp += sx_url; break; case TCHAR('m'): sxtmp += sx_email; break; case TCHAR('o'): { if (n == (N - 1)) { // This was the last character - send the lot! sxtmp += sxNotes; break; } size_t line_number(0); gNumIts = 0; for (n++; n < N && (gNumIts < 3); ++gNumIts, n++) { if (_istdigit(sx_autotype[n])) { line_number *= 10; line_number += (sx_autotype[n] - TCHAR('0')); } else break; // for loop } if (line_number == 0) { // Send the lot sxtmp += sx_notes; } else if (line_number <= vsxnotes_lines.size()) { // User specifies a too big a line number - ignore the lot sxtmp += vsxnotes_lines[line_number - 1]; } // Backup the extra character that delimited the \oNNN string n--; break; // case 'o' } // Action Verbs: // These are the only ones processed specially by the UI as they involve // actions it performs whilst doing the key sending. // Copy them to output string unchanged. case TCHAR('b'): // backspace! case TCHAR('z'): // Use older method vactionverboffsets.push_back(sxtmp.length()); sxtmp += _T("\\"); sxtmp += curChar; break; // case 'b' & 'z' case TCHAR('d'): // Delay case TCHAR('w'): // Wait milli-seconds case TCHAR('W'): // Wait seconds { // Need to ensure that the field length is 3, even if it wasn't vactionverboffsets.push_back(sxtmp.length()); sxtmp += _T("\\"); sxtmp += curChar; gNumIts = 0; size_t i = n; for (i++; i < N && (gNumIts < 3); ++gNumIts, i++) { if (!_istdigit(sx_autotype[i])) break; } // Insert sufficient zeroes to ensure field is 3 characters long sxtmp += sxZeroes.substr(0, 3 - gNumIts); break; // case 'd', 'w' & 'W' } // Also copy explicit control characters to output string unchanged. case TCHAR('a'): // bell (can't hear it during testing!) case TCHAR('v'): // vertical tab case TCHAR('f'): // form feed case TCHAR('e'): // escape case TCHAR('x'): // hex digits (\xNN) // and any others we have forgotten! // '\cC', '\uXXXX', '\OOO', '\<any other charatcer not recognised above>' default: sxtmp += L'\\'; sxtmp += curChar; break; } } else sxtmp += curChar; } vsxnotes_lines.clear(); return sxtmp; }