TestEntry.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. /*
  2. * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
  3. * Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 or (at your option)
  8. * version 3 of the License.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <QScopedPointer>
  19. #include "TestEntry.h"
  20. #include "TestGlobal.h"
  21. #include "core/Clock.h"
  22. #include "core/Metadata.h"
  23. #include "crypto/Crypto.h"
  24. QTEST_GUILESS_MAIN(TestEntry)
  25. void TestEntry::initTestCase()
  26. {
  27. QVERIFY(Crypto::init());
  28. }
  29. void TestEntry::testHistoryItemDeletion()
  30. {
  31. QScopedPointer<Entry> entry(new Entry());
  32. QPointer<Entry> historyEntry = new Entry();
  33. entry->addHistoryItem(historyEntry);
  34. QCOMPARE(entry->historyItems().size(), 1);
  35. QList<Entry*> historyEntriesToRemove;
  36. historyEntriesToRemove.append(historyEntry);
  37. entry->removeHistoryItems(historyEntriesToRemove);
  38. QCOMPARE(entry->historyItems().size(), 0);
  39. QVERIFY(historyEntry.isNull());
  40. }
  41. void TestEntry::testCopyDataFrom()
  42. {
  43. QScopedPointer<Entry> entry(new Entry());
  44. entry->setTitle("testtitle");
  45. entry->attributes()->set("attr1", "abc");
  46. entry->attributes()->set("attr2", "def");
  47. entry->attachments()->set("test", "123");
  48. entry->attachments()->set("test2", "456");
  49. AutoTypeAssociations::Association assoc;
  50. assoc.window = "1";
  51. assoc.sequence = "2";
  52. entry->autoTypeAssociations()->add(assoc);
  53. assoc.window = "3";
  54. assoc.sequence = "4";
  55. entry->autoTypeAssociations()->add(assoc);
  56. QScopedPointer<Entry> entry2(new Entry());
  57. entry2->copyDataFrom(entry.data());
  58. QCOMPARE(entry2->title(), QString("testtitle"));
  59. QCOMPARE(entry2->attributes()->value("attr1"), QString("abc"));
  60. QCOMPARE(entry2->attributes()->value("attr2"), QString("def"));
  61. QCOMPARE(entry2->attachments()->keys().size(), 2);
  62. QCOMPARE(entry2->attachments()->value("test"), QByteArray("123"));
  63. QCOMPARE(entry2->attachments()->value("test2"), QByteArray("456"));
  64. QCOMPARE(entry2->autoTypeAssociations()->size(), 2);
  65. QCOMPARE(entry2->autoTypeAssociations()->get(0).window, QString("1"));
  66. QCOMPARE(entry2->autoTypeAssociations()->get(1).window, QString("3"));
  67. }
  68. void TestEntry::testClone()
  69. {
  70. QScopedPointer<Entry> entryOrg(new Entry());
  71. entryOrg->setUuid(QUuid::createUuid());
  72. entryOrg->setTitle("Original Title");
  73. entryOrg->beginUpdate();
  74. entryOrg->setTitle("New Title");
  75. entryOrg->endUpdate();
  76. TimeInfo entryOrgTime = entryOrg->timeInfo();
  77. QDateTime dateTime = Clock::datetimeUtc(60);
  78. entryOrgTime.setCreationTime(dateTime);
  79. entryOrg->setTimeInfo(entryOrgTime);
  80. QScopedPointer<Entry> entryCloneNone(entryOrg->clone(Entry::CloneNoFlags));
  81. QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid());
  82. QCOMPARE(entryCloneNone->title(), QString("New Title"));
  83. QCOMPARE(entryCloneNone->historyItems().size(), 0);
  84. QCOMPARE(entryCloneNone->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
  85. QScopedPointer<Entry> entryCloneNewUuid(entryOrg->clone(Entry::CloneNewUuid));
  86. QVERIFY(entryCloneNewUuid->uuid() != entryOrg->uuid());
  87. QVERIFY(!entryCloneNewUuid->uuid().isNull());
  88. QCOMPARE(entryCloneNewUuid->title(), QString("New Title"));
  89. QCOMPARE(entryCloneNewUuid->historyItems().size(), 0);
  90. QCOMPARE(entryCloneNewUuid->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
  91. // Reset modification time
  92. entryOrgTime.setLastModificationTime(Clock::datetimeUtc(60));
  93. entryOrg->setTimeInfo(entryOrgTime);
  94. QScopedPointer<Entry> entryCloneRename(entryOrg->clone(Entry::CloneRenameTitle));
  95. QCOMPARE(entryCloneRename->uuid(), entryOrg->uuid());
  96. QCOMPARE(entryCloneRename->title(), QString("New Title - Clone"));
  97. // Cloning should not modify time info unless explicity requested
  98. QCOMPARE(entryCloneRename->timeInfo(), entryOrg->timeInfo());
  99. QScopedPointer<Entry> entryCloneResetTime(entryOrg->clone(Entry::CloneResetTimeInfo));
  100. QCOMPARE(entryCloneResetTime->uuid(), entryOrg->uuid());
  101. QCOMPARE(entryCloneResetTime->title(), QString("New Title"));
  102. QCOMPARE(entryCloneResetTime->historyItems().size(), 0);
  103. QVERIFY(entryCloneResetTime->timeInfo().creationTime() != entryOrg->timeInfo().creationTime());
  104. // Date back history of original entry
  105. Entry* firstHistoryItem = entryOrg->historyItems()[0];
  106. TimeInfo entryOrgHistoryTimeInfo = firstHistoryItem->timeInfo();
  107. QDateTime datedBackEntryOrgModificationTime = entryOrgHistoryTimeInfo.lastModificationTime().addMSecs(-10);
  108. entryOrgHistoryTimeInfo.setLastModificationTime(datedBackEntryOrgModificationTime);
  109. entryOrgHistoryTimeInfo.setCreationTime(datedBackEntryOrgModificationTime);
  110. firstHistoryItem->setTimeInfo(entryOrgHistoryTimeInfo);
  111. QScopedPointer<Entry> entryCloneHistory(entryOrg->clone(Entry::CloneIncludeHistory | Entry::CloneResetTimeInfo));
  112. QCOMPARE(entryCloneHistory->uuid(), entryOrg->uuid());
  113. QCOMPARE(entryCloneHistory->title(), QString("New Title"));
  114. QCOMPARE(entryCloneHistory->historyItems().size(), entryOrg->historyItems().size());
  115. QCOMPARE(entryCloneHistory->historyItems().at(0)->title(), QString("Original Title"));
  116. QVERIFY(entryCloneHistory->timeInfo().creationTime() != entryOrg->timeInfo().creationTime());
  117. // Timeinfo of history items should not be modified
  118. QList<Entry*> entryOrgHistory = entryOrg->historyItems(), clonedHistory = entryCloneHistory->historyItems();
  119. auto entryOrgHistoryItem = entryOrgHistory.constBegin();
  120. for (auto entryCloneHistoryItem = clonedHistory.constBegin(); entryCloneHistoryItem != clonedHistory.constEnd();
  121. ++entryCloneHistoryItem, ++entryOrgHistoryItem) {
  122. QCOMPARE((*entryOrgHistoryItem)->timeInfo(), (*entryCloneHistoryItem)->timeInfo());
  123. }
  124. Database db;
  125. auto* entryOrgClone = entryOrg->clone(Entry::CloneNoFlags);
  126. entryOrgClone->setGroup(db.rootGroup());
  127. Entry* entryCloneUserRef = entryOrgClone->clone(Entry::CloneUserAsRef);
  128. entryCloneUserRef->setGroup(db.rootGroup());
  129. QCOMPARE(entryCloneUserRef->uuid(), entryOrgClone->uuid());
  130. QCOMPARE(entryCloneUserRef->title(), QString("New Title"));
  131. QCOMPARE(entryCloneUserRef->historyItems().size(), 0);
  132. QCOMPARE(entryCloneUserRef->timeInfo().creationTime(), entryOrgClone->timeInfo().creationTime());
  133. QVERIFY(entryCloneUserRef->attributes()->isReference(EntryAttributes::UserNameKey));
  134. QCOMPARE(entryCloneUserRef->resolvePlaceholder(entryCloneUserRef->username()), entryOrgClone->username());
  135. Entry* entryClonePassRef = entryOrgClone->clone(Entry::ClonePassAsRef);
  136. entryClonePassRef->setGroup(db.rootGroup());
  137. QCOMPARE(entryClonePassRef->uuid(), entryOrgClone->uuid());
  138. QCOMPARE(entryClonePassRef->title(), QString("New Title"));
  139. QCOMPARE(entryClonePassRef->historyItems().size(), 0);
  140. QCOMPARE(entryClonePassRef->timeInfo().creationTime(), entryOrgClone->timeInfo().creationTime());
  141. QVERIFY(entryClonePassRef->attributes()->isReference(EntryAttributes::PasswordKey));
  142. QCOMPARE(entryClonePassRef->resolvePlaceholder(entryCloneUserRef->password()), entryOrg->password());
  143. QCOMPARE(entryClonePassRef->attributes()->referenceUuid(EntryAttributes::PasswordKey), entryOrgClone->uuid());
  144. }
  145. void TestEntry::testResolveUrl()
  146. {
  147. QScopedPointer<Entry> entry(new Entry());
  148. QString testUrl("www.google.com");
  149. QString testCmd("cmd://firefox " + testUrl);
  150. QString testFileUnix("/home/example/test.txt");
  151. QString testFileWindows("c:/WINDOWS/test.txt");
  152. QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit");
  153. QString nonHttpUrl("ftp://google.com");
  154. QString noUrl("random text inserted here");
  155. // Test standard URL's
  156. QCOMPARE(entry->resolveUrl(""), QString(""));
  157. QCOMPARE(entry->resolveUrl(testUrl), "https://" + testUrl);
  158. QCOMPARE(entry->resolveUrl("http://" + testUrl), "http://" + testUrl);
  159. // Test file:// URL's
  160. QCOMPARE(entry->resolveUrl("file://" + testFileUnix), "file://" + testFileUnix);
  161. QCOMPARE(entry->resolveUrl(testFileUnix), "file://" + testFileUnix);
  162. QCOMPARE(entry->resolveUrl("file:///" + testFileWindows), "file:///" + testFileWindows);
  163. QCOMPARE(entry->resolveUrl(testFileWindows), "file:///" + testFileWindows);
  164. // Test cmd:// with no URL
  165. QCOMPARE(entry->resolveUrl("cmd://firefox"), QString(""));
  166. QCOMPARE(entry->resolveUrl("cmd://firefox --no-url"), QString(""));
  167. // Test cmd:// with URL's
  168. QCOMPARE(entry->resolveUrl(testCmd), "https://" + testUrl);
  169. QCOMPARE(entry->resolveUrl(testComplexCmd), "http://" + testUrl);
  170. // Test non-http URL
  171. QCOMPARE(entry->resolveUrl(nonHttpUrl), QString(""));
  172. // Test no URL
  173. QCOMPARE(entry->resolveUrl(noUrl), QString(""));
  174. }
  175. void TestEntry::testResolveUrlPlaceholders()
  176. {
  177. Entry entry;
  178. entry.setUrl("https://user:pw@keepassxc.org:80/path/example.php?q=e&s=t+2#fragment");
  179. QString rmvscm("//user:pw@keepassxc.org:80/path/example.php?q=e&s=t+2#fragment"); // Entry URL without scheme name.
  180. QString scm("https"); // Scheme name of the entry URL.
  181. QString host("keepassxc.org"); // Host component of the entry URL.
  182. QString port("80"); // Port number of the entry URL.
  183. QString path("/path/example.php"); // Path component of the entry URL.
  184. QString query("q=e&s=t+2"); // Query information of the entry URL.
  185. QString userinfo("user:pw"); // User information of the entry URL.
  186. QString username("user"); // User name of the entry URL.
  187. QString password("pw"); // Password of the entry URL.
  188. QString fragment("fragment"); // Fragment of the entry URL.
  189. QCOMPARE(entry.resolvePlaceholder("{URL:RMVSCM}"), rmvscm);
  190. QCOMPARE(entry.resolvePlaceholder("{URL:WITHOUTSCHEME}"), rmvscm);
  191. QCOMPARE(entry.resolvePlaceholder("{URL:SCM}"), scm);
  192. QCOMPARE(entry.resolvePlaceholder("{URL:SCHEME}"), scm);
  193. QCOMPARE(entry.resolvePlaceholder("{URL:HOST}"), host);
  194. QCOMPARE(entry.resolvePlaceholder("{URL:PORT}"), port);
  195. QCOMPARE(entry.resolvePlaceholder("{URL:PATH}"), path);
  196. QCOMPARE(entry.resolvePlaceholder("{URL:QUERY}"), query);
  197. QCOMPARE(entry.resolvePlaceholder("{URL:USERINFO}"), userinfo);
  198. QCOMPARE(entry.resolvePlaceholder("{URL:USERNAME}"), username);
  199. QCOMPARE(entry.resolvePlaceholder("{URL:PASSWORD}"), password);
  200. QCOMPARE(entry.resolvePlaceholder("{URL:FRAGMENT}"), fragment);
  201. }
  202. void TestEntry::testResolveRecursivePlaceholders()
  203. {
  204. Database db;
  205. auto* root = db.rootGroup();
  206. auto* entry1 = new Entry();
  207. entry1->setGroup(root);
  208. entry1->setUuid(QUuid::createUuid());
  209. entry1->setTitle("{USERNAME}");
  210. entry1->setUsername("{PASSWORD}");
  211. entry1->setPassword("{URL}");
  212. entry1->setUrl("{S:CustomTitle}");
  213. entry1->attributes()->set("CustomTitle", "RecursiveValue");
  214. QCOMPARE(entry1->resolveMultiplePlaceholders(entry1->title()), QString("RecursiveValue"));
  215. auto* entry2 = new Entry();
  216. entry2->setGroup(root);
  217. entry2->setUuid(QUuid::createUuid());
  218. entry2->setTitle("Entry2Title");
  219. entry2->setUsername("{S:CustomUserNameAttribute}");
  220. entry2->setPassword(QString("{REF:P@I:%1}").arg(entry1->uuidToHex()));
  221. entry2->setUrl("http://{S:IpAddress}:{S:Port}/{S:Uri}");
  222. entry2->attributes()->set("CustomUserNameAttribute", "CustomUserNameValue");
  223. entry2->attributes()->set("IpAddress", "127.0.0.1");
  224. entry2->attributes()->set("Port", "1234");
  225. entry2->attributes()->set("Uri", "uri/path");
  226. auto* entry3 = new Entry();
  227. entry3->setGroup(root);
  228. entry3->setUuid(QUuid::createUuid());
  229. entry3->setTitle(QString("{REF:T@I:%1}").arg(entry2->uuidToHex()));
  230. entry3->setUsername(QString("{REF:U@I:%1}").arg(entry2->uuidToHex()));
  231. entry3->setPassword(QString("{REF:P@I:%1}").arg(entry2->uuidToHex()));
  232. entry3->setUrl(QString("{REF:A@I:%1}").arg(entry2->uuidToHex()));
  233. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->title()), QString("Entry2Title"));
  234. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->username()), QString("CustomUserNameValue"));
  235. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->password()), QString("RecursiveValue"));
  236. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->url()), QString("http://127.0.0.1:1234/uri/path"));
  237. auto* entry4 = new Entry();
  238. entry4->setGroup(root);
  239. entry4->setUuid(QUuid::createUuid());
  240. entry4->setTitle(QString("{REF:T@I:%1}").arg(entry3->uuidToHex()));
  241. entry4->setUsername(QString("{REF:U@I:%1}").arg(entry3->uuidToHex()));
  242. entry4->setPassword(QString("{REF:P@I:%1}").arg(entry3->uuidToHex()));
  243. entry4->setUrl(QString("{REF:A@I:%1}").arg(entry3->uuidToHex()));
  244. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->title()), QString("Entry2Title"));
  245. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->username()), QString("CustomUserNameValue"));
  246. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->password()), QString("RecursiveValue"));
  247. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->url()), QString("http://127.0.0.1:1234/uri/path"));
  248. auto* entry5 = new Entry();
  249. entry5->setGroup(root);
  250. entry5->setUuid(QUuid::createUuid());
  251. entry5->attributes()->set("Scheme", "http");
  252. entry5->attributes()->set("Host", "host.org");
  253. entry5->attributes()->set("Port", "2017");
  254. entry5->attributes()->set("Path", "/some/path");
  255. entry5->attributes()->set("UserName", "username");
  256. entry5->attributes()->set("Password", "password");
  257. entry5->attributes()->set("Query", "q=e&t=s");
  258. entry5->attributes()->set("Fragment", "fragment");
  259. entry5->setUrl("{S:Scheme}://{S:UserName}:{S:Password}@{S:Host}:{S:Port}{S:Path}?{S:Query}#{S:Fragment}");
  260. entry5->setTitle("title+{URL:Path}+{URL:Fragment}+title");
  261. const QString url("http://username:password@host.org:2017/some/path?q=e&t=s#fragment");
  262. QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->url()), url);
  263. QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->title()), QString("title+/some/path+fragment+title"));
  264. auto* entry6 = new Entry();
  265. entry6->setGroup(root);
  266. entry6->setUuid(QUuid::createUuid());
  267. entry6->setTitle(QString("{REF:T@I:%1}").arg(entry3->uuidToHex()));
  268. entry6->setUsername(QString("{TITLE}"));
  269. entry6->setPassword(QString("{PASSWORD}"));
  270. QCOMPARE(entry6->resolvePlaceholder(entry6->title()), QString("Entry2Title"));
  271. QCOMPARE(entry6->resolvePlaceholder(entry6->username()), QString("Entry2Title"));
  272. QCOMPARE(entry6->resolvePlaceholder(entry6->password()), QString("{PASSWORD}"));
  273. auto* entry7 = new Entry();
  274. entry7->setGroup(root);
  275. entry7->setUuid(QUuid::createUuid());
  276. entry7->setTitle(QString("{REF:T@I:%1} and something else").arg(entry3->uuidToHex()));
  277. entry7->setUsername(QString("{TITLE}"));
  278. entry7->setPassword(QString("PASSWORD"));
  279. QCOMPARE(entry7->resolvePlaceholder(entry7->title()), QString("Entry2Title and something else"));
  280. QCOMPARE(entry7->resolvePlaceholder(entry7->username()), QString("Entry2Title and something else"));
  281. QCOMPARE(entry7->resolvePlaceholder(entry7->password()), QString("PASSWORD"));
  282. }
  283. void TestEntry::testResolveReferencePlaceholders()
  284. {
  285. Database db;
  286. auto* root = db.rootGroup();
  287. auto* entry1 = new Entry();
  288. entry1->setGroup(root);
  289. entry1->setUuid(QUuid::createUuid());
  290. entry1->setTitle("Title1");
  291. entry1->setUsername("Username1");
  292. entry1->setPassword("Password1");
  293. entry1->setUrl("Url1");
  294. entry1->setNotes("Notes1");
  295. entry1->attributes()->set("CustomAttribute1", "CustomAttributeValue1");
  296. auto* group = new Group();
  297. group->setParent(root);
  298. auto* entry2 = new Entry();
  299. entry2->setGroup(group);
  300. entry2->setUuid(QUuid::createUuid());
  301. entry2->setTitle("Title2");
  302. entry2->setUsername("Username2");
  303. entry2->setPassword("Password2");
  304. entry2->setUrl("Url2");
  305. entry2->setNotes("Notes2");
  306. entry2->attributes()->set("CustomAttribute2", "CustomAttributeValue2");
  307. auto* entry3 = new Entry();
  308. entry3->setGroup(group);
  309. entry3->setUuid(QUuid::createUuid());
  310. entry3->setTitle("{S:AttributeTitle}");
  311. entry3->setUsername("{S:AttributeUsername}");
  312. entry3->setPassword("{S:AttributePassword}");
  313. entry3->setUrl("{S:AttributeUrl}");
  314. entry3->setNotes("{S:AttributeNotes}");
  315. entry3->attributes()->set("AttributeTitle", "TitleValue");
  316. entry3->attributes()->set("AttributeUsername", "UsernameValue");
  317. entry3->attributes()->set("AttributePassword", "PasswordValue");
  318. entry3->attributes()->set("AttributeUrl", "UrlValue");
  319. entry3->attributes()->set("AttributeNotes", "NotesValue");
  320. auto* tstEntry = new Entry();
  321. tstEntry->setGroup(root);
  322. tstEntry->setUuid(QUuid::createUuid());
  323. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), entry1->title());
  324. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title());
  325. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry1->username())), entry1->title());
  326. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry1->password())), entry1->title());
  327. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@A:%1}").arg(entry1->url())), entry1->title());
  328. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@N:%1}").arg(entry1->notes())), entry1->title());
  329. QCOMPARE(tstEntry->resolveMultiplePlaceholders(
  330. QString("{REF:T@O:%1}").arg(entry1->attributes()->value("CustomAttribute1"))),
  331. entry1->title());
  332. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), entry1->title());
  333. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title());
  334. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@U:%1}").arg(entry1->username())),
  335. entry1->username());
  336. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@P:%1}").arg(entry1->password())),
  337. entry1->password());
  338. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry1->url())), entry1->url());
  339. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry1->notes())), entry1->notes());
  340. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry2->uuidToHex())), entry2->title());
  341. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry2->title())), entry2->title());
  342. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry2->username())), entry2->title());
  343. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry2->password())), entry2->title());
  344. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@A:%1}").arg(entry2->url())), entry2->title());
  345. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@N:%1}").arg(entry2->notes())), entry2->title());
  346. QCOMPARE(tstEntry->resolveMultiplePlaceholders(
  347. QString("{REF:T@O:%1}").arg(entry2->attributes()->value("CustomAttribute2"))),
  348. entry2->title());
  349. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry2->title())), entry2->title());
  350. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@U:%1}").arg(entry2->username())),
  351. entry2->username());
  352. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@P:%1}").arg(entry2->password())),
  353. entry2->password());
  354. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry2->url())), entry2->url());
  355. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry2->notes())), entry2->notes());
  356. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex())),
  357. entry3->attributes()->value("AttributeTitle"));
  358. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex())),
  359. entry3->attributes()->value("AttributeUsername"));
  360. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex())),
  361. entry3->attributes()->value("AttributePassword"));
  362. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex())),
  363. entry3->attributes()->value("AttributeUrl"));
  364. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex())),
  365. entry3->attributes()->value("AttributeNotes"));
  366. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex().toUpper())),
  367. entry3->attributes()->value("AttributeTitle"));
  368. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex().toUpper())),
  369. entry3->attributes()->value("AttributeUsername"));
  370. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex().toUpper())),
  371. entry3->attributes()->value("AttributePassword"));
  372. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex().toUpper())),
  373. entry3->attributes()->value("AttributeUrl"));
  374. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex().toUpper())),
  375. entry3->attributes()->value("AttributeNotes"));
  376. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:t@i:%1}").arg(entry3->uuidToHex().toLower())),
  377. entry3->attributes()->value("AttributeTitle"));
  378. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:u@i:%1}").arg(entry3->uuidToHex().toLower())),
  379. entry3->attributes()->value("AttributeUsername"));
  380. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:p@i:%1}").arg(entry3->uuidToHex().toLower())),
  381. entry3->attributes()->value("AttributePassword"));
  382. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:a@i:%1}").arg(entry3->uuidToHex().toLower())),
  383. entry3->attributes()->value("AttributeUrl"));
  384. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:n@i:%1}").arg(entry3->uuidToHex().toLower())),
  385. entry3->attributes()->value("AttributeNotes"));
  386. }
  387. void TestEntry::testResolveNonIdPlaceholdersToUuid()
  388. {
  389. Database db;
  390. auto* root = db.rootGroup();
  391. auto* referencedEntryTitle = new Entry();
  392. referencedEntryTitle->setGroup(root);
  393. referencedEntryTitle->setTitle("myTitle");
  394. referencedEntryTitle->setUuid(QUuid::createUuid());
  395. auto* referencedEntryUsername = new Entry();
  396. referencedEntryUsername->setGroup(root);
  397. referencedEntryUsername->setUsername("myUser");
  398. referencedEntryUsername->setUuid(QUuid::createUuid());
  399. auto* referencedEntryPassword = new Entry();
  400. referencedEntryPassword->setGroup(root);
  401. referencedEntryPassword->setPassword("myPassword");
  402. referencedEntryPassword->setUuid(QUuid::createUuid());
  403. auto* referencedEntryUrl = new Entry();
  404. referencedEntryUrl->setGroup(root);
  405. referencedEntryUrl->setUrl("myUrl");
  406. referencedEntryUrl->setUuid(QUuid::createUuid());
  407. auto* referencedEntryNotes = new Entry();
  408. referencedEntryNotes->setGroup(root);
  409. referencedEntryNotes->setNotes("myNotes");
  410. referencedEntryNotes->setUuid(QUuid::createUuid());
  411. const QList<QChar> placeholders{'T', 'U', 'P', 'A', 'N'};
  412. for (const QChar& searchIn : placeholders) {
  413. const Entry* referencedEntry = nullptr;
  414. QString newEntryNotesRaw("{REF:I@%1:%2}");
  415. switch (searchIn.toLatin1()) {
  416. case 'T':
  417. referencedEntry = referencedEntryTitle;
  418. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->title());
  419. break;
  420. case 'U':
  421. referencedEntry = referencedEntryUsername;
  422. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->username());
  423. break;
  424. case 'P':
  425. referencedEntry = referencedEntryPassword;
  426. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->password());
  427. break;
  428. case 'A':
  429. referencedEntry = referencedEntryUrl;
  430. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->url());
  431. break;
  432. case 'N':
  433. referencedEntry = referencedEntryNotes;
  434. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->notes());
  435. break;
  436. default:
  437. break;
  438. }
  439. auto* newEntry = new Entry();
  440. newEntry->setGroup(root);
  441. newEntry->setNotes(newEntryNotesRaw);
  442. const QString newEntryNotesResolved = newEntry->resolveMultiplePlaceholders(newEntry->notes());
  443. QCOMPARE(newEntryNotesResolved, referencedEntry->uuidToHex());
  444. }
  445. }
  446. void TestEntry::testResolveClonedEntry()
  447. {
  448. Database db;
  449. auto* root = db.rootGroup();
  450. auto* original = new Entry();
  451. original->setGroup(root);
  452. original->setUuid(QUuid::createUuid());
  453. original->setTitle("Title");
  454. original->setUsername("SomeUsername");
  455. original->setPassword("SomePassword");
  456. QCOMPARE(original->resolveMultiplePlaceholders(original->username()), original->username());
  457. QCOMPARE(original->resolveMultiplePlaceholders(original->password()), original->password());
  458. // Top-level clones.
  459. Entry* clone1 = original->clone(Entry::CloneNewUuid);
  460. clone1->setGroup(root);
  461. Entry* clone2 = original->clone(Entry::CloneUserAsRef | Entry::CloneNewUuid);
  462. clone2->setGroup(root);
  463. Entry* clone3 = original->clone(Entry::ClonePassAsRef | Entry::CloneNewUuid);
  464. clone3->setGroup(root);
  465. Entry* clone4 = original->clone(Entry::CloneUserAsRef | Entry::ClonePassAsRef | Entry::CloneNewUuid);
  466. clone4->setGroup(root);
  467. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->username()), original->username());
  468. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->password()), original->password());
  469. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->username()), original->username());
  470. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->password()), original->password());
  471. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->username()), original->username());
  472. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->password()), original->password());
  473. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->username()), original->username());
  474. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->password()), original->password());
  475. // Second-level clones.
  476. Entry* cclone1 = clone4->clone(Entry::CloneNewUuid);
  477. cclone1->setGroup(root);
  478. Entry* cclone2 = clone4->clone(Entry::CloneUserAsRef | Entry::CloneNewUuid);
  479. cclone2->setGroup(root);
  480. Entry* cclone3 = clone4->clone(Entry::ClonePassAsRef | Entry::CloneNewUuid);
  481. cclone3->setGroup(root);
  482. Entry* cclone4 = clone4->clone(Entry::CloneUserAsRef | Entry::ClonePassAsRef | Entry::CloneNewUuid);
  483. cclone4->setGroup(root);
  484. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->username()), original->username());
  485. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->password()), original->password());
  486. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->username()), original->username());
  487. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->password()), original->password());
  488. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->username()), original->username());
  489. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->password()), original->password());
  490. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->username()), original->username());
  491. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->password()), original->password());
  492. // Change the original's attributes and make sure that the changes are tracked.
  493. QString oldUsername = original->username();
  494. QString oldPassword = original->password();
  495. original->setUsername("DifferentUsername");
  496. original->setPassword("DifferentPassword");
  497. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->username()), oldUsername);
  498. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->password()), oldPassword);
  499. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->username()), original->username());
  500. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->password()), oldPassword);
  501. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->username()), oldUsername);
  502. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->password()), original->password());
  503. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->username()), original->username());
  504. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->password()), original->password());
  505. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->username()), original->username());
  506. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->password()), original->password());
  507. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->username()), original->username());
  508. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->password()), original->password());
  509. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->username()), original->username());
  510. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->password()), original->password());
  511. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->username()), original->username());
  512. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->password()), original->password());
  513. }
  514. void TestEntry::testIsRecycled()
  515. {
  516. Entry* entry = new Entry();
  517. QVERIFY(!entry->isRecycled());
  518. Database db;
  519. Group* root = db.rootGroup();
  520. QVERIFY(root);
  521. entry->setGroup(root);
  522. QVERIFY(!entry->isRecycled());
  523. QVERIFY(db.metadata()->recycleBinEnabled());
  524. db.recycleEntry(entry);
  525. QVERIFY(entry->isRecycled());
  526. Group* group1 = new Group();
  527. group1->setParent(root);
  528. Entry* entry1 = new Entry();
  529. entry1->setGroup(group1);
  530. QVERIFY(!entry1->isRecycled());
  531. db.recycleGroup(group1);
  532. QVERIFY(entry1->isRecycled());
  533. }
  534. void TestEntry::testMove()
  535. {
  536. Database db;
  537. Group* root = db.rootGroup();
  538. QVERIFY(root);
  539. Entry* entry0 = new Entry();
  540. QVERIFY(entry0);
  541. entry0->setGroup(root);
  542. Entry* entry1 = new Entry();
  543. QVERIFY(entry1);
  544. entry1->setGroup(root);
  545. Entry* entry2 = new Entry();
  546. QVERIFY(entry2);
  547. entry2->setGroup(root);
  548. Entry* entry3 = new Entry();
  549. QVERIFY(entry3);
  550. entry3->setGroup(root);
  551. // default order, straight
  552. QCOMPARE(root->entries().at(0), entry0);
  553. QCOMPARE(root->entries().at(1), entry1);
  554. QCOMPARE(root->entries().at(2), entry2);
  555. QCOMPARE(root->entries().at(3), entry3);
  556. entry0->moveDown();
  557. QCOMPARE(root->entries().at(0), entry1);
  558. QCOMPARE(root->entries().at(1), entry0);
  559. QCOMPARE(root->entries().at(2), entry2);
  560. QCOMPARE(root->entries().at(3), entry3);
  561. entry0->moveDown();
  562. QCOMPARE(root->entries().at(0), entry1);
  563. QCOMPARE(root->entries().at(1), entry2);
  564. QCOMPARE(root->entries().at(2), entry0);
  565. QCOMPARE(root->entries().at(3), entry3);
  566. entry0->moveDown();
  567. QCOMPARE(root->entries().at(0), entry1);
  568. QCOMPARE(root->entries().at(1), entry2);
  569. QCOMPARE(root->entries().at(2), entry3);
  570. QCOMPARE(root->entries().at(3), entry0);
  571. // no effect
  572. entry0->moveDown();
  573. QCOMPARE(root->entries().at(0), entry1);
  574. QCOMPARE(root->entries().at(1), entry2);
  575. QCOMPARE(root->entries().at(2), entry3);
  576. QCOMPARE(root->entries().at(3), entry0);
  577. entry0->moveUp();
  578. QCOMPARE(root->entries().at(0), entry1);
  579. QCOMPARE(root->entries().at(1), entry2);
  580. QCOMPARE(root->entries().at(2), entry0);
  581. QCOMPARE(root->entries().at(3), entry3);
  582. entry0->moveUp();
  583. QCOMPARE(root->entries().at(0), entry1);
  584. QCOMPARE(root->entries().at(1), entry0);
  585. QCOMPARE(root->entries().at(2), entry2);
  586. QCOMPARE(root->entries().at(3), entry3);
  587. entry0->moveUp();
  588. QCOMPARE(root->entries().at(0), entry0);
  589. QCOMPARE(root->entries().at(1), entry1);
  590. QCOMPARE(root->entries().at(2), entry2);
  591. QCOMPARE(root->entries().at(3), entry3);
  592. // no effect
  593. entry0->moveUp();
  594. QCOMPARE(root->entries().at(0), entry0);
  595. QCOMPARE(root->entries().at(1), entry1);
  596. QCOMPARE(root->entries().at(2), entry2);
  597. QCOMPARE(root->entries().at(3), entry3);
  598. entry2->moveUp();
  599. QCOMPARE(root->entries().at(0), entry0);
  600. QCOMPARE(root->entries().at(1), entry2);
  601. QCOMPARE(root->entries().at(2), entry1);
  602. QCOMPARE(root->entries().at(3), entry3);
  603. entry0->moveDown();
  604. QCOMPARE(root->entries().at(0), entry2);
  605. QCOMPARE(root->entries().at(1), entry0);
  606. QCOMPARE(root->entries().at(2), entry1);
  607. QCOMPARE(root->entries().at(3), entry3);
  608. entry3->moveUp();
  609. QCOMPARE(root->entries().at(0), entry2);
  610. QCOMPARE(root->entries().at(1), entry0);
  611. QCOMPARE(root->entries().at(2), entry3);
  612. QCOMPARE(root->entries().at(3), entry1);
  613. entry3->moveUp();
  614. QCOMPARE(root->entries().at(0), entry2);
  615. QCOMPARE(root->entries().at(1), entry3);
  616. QCOMPARE(root->entries().at(2), entry0);
  617. QCOMPARE(root->entries().at(3), entry1);
  618. entry2->moveDown();
  619. QCOMPARE(root->entries().at(0), entry3);
  620. QCOMPARE(root->entries().at(1), entry2);
  621. QCOMPARE(root->entries().at(2), entry0);
  622. QCOMPARE(root->entries().at(3), entry1);
  623. entry1->moveUp();
  624. QCOMPARE(root->entries().at(0), entry3);
  625. QCOMPARE(root->entries().at(1), entry2);
  626. QCOMPARE(root->entries().at(2), entry1);
  627. QCOMPARE(root->entries().at(3), entry0);
  628. }