1/*
2 This file is part of the KDE Baloo project.
3 SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#include "documenturldb.h"
9#include "postingiterator.h"
10#include "enginedebug.h"
11
12#include <algorithm>
13
14using namespace Baloo;
15
16DocumentUrlDB::DocumentUrlDB(MDB_dbi idTreeDb, MDB_dbi idFilenameDb, MDB_txn* txn)
17 : m_txn(txn)
18 , m_idFilenameDbi(idFilenameDb)
19 , m_idTreeDbi(idTreeDb)
20{
21}
22
23DocumentUrlDB::~DocumentUrlDB()
24{
25}
26
27bool DocumentUrlDB::addPath(const QByteArray& url)
28{
29 Q_ASSERT(!url.isEmpty());
30 Q_ASSERT(!url.endsWith('/'));
31 if (url.isEmpty() || url.endsWith(c: '/')) {
32 return false;
33 }
34
35 IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
36
37 QByteArray arr = url;
38 quint64 id = filePathToId(filePath: arr);
39
40 while (id) {
41 if (idFilenameDb.contains(docId: id)) {
42 return true;
43 }
44
45 int pos = arr.lastIndexOf(c: '/');
46 QByteArray name = arr.mid(index: pos + 1);
47
48 if (pos == 0) {
49 add(id, parentId: 0, name);
50 return true;
51 }
52
53 arr.resize(size: pos);
54 auto parentId = filePathToId(filePath: arr);
55 if (!parentId) {
56 return false;
57 }
58 add(id, parentId, name);
59
60 id = parentId;
61 }
62 return false;
63}
64
65bool DocumentUrlDB::put(quint64 docId, quint64 parentId, const QByteArray& filename)
66{
67 Q_ASSERT(docId);
68 Q_ASSERT(!filename.contains('/'));
69 Q_ASSERT(!filename.isEmpty());
70 if (!docId || filename.isEmpty() || filename.contains(c: '/')) {
71 return false;
72 }
73
74 add(id: docId, parentId, name: filename);
75 return true;
76}
77
78void DocumentUrlDB::updateUrl(quint64 id, quint64 newParentId, const QByteArray& newName)
79{
80 IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
81 IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
82
83 // Sanity checks
84 auto path = idFilenameDb.get(docId: id);
85 if (path.parentId != newParentId) {
86 // Remove from old parent
87 QVector<quint64> subDocs = idTreeDb.get(docId: path.parentId);
88 if (subDocs.removeOne(t: id)) {
89 idTreeDb.set(docId: path.parentId, subDocIds: subDocs);
90 }
91 // Add to new parent
92 subDocs = idTreeDb.get(docId: newParentId);
93 sortedIdInsert(vec&: subDocs, id);
94 idTreeDb.set(docId: newParentId, subDocIds: subDocs);
95 }
96
97 if ((newName != path.name) || (newParentId != path.parentId)) {
98 qCDebug(ENGINE) << id << "renaming" << path.name << "to" << newName;
99 path.parentId = newParentId;
100 path.name = newName;
101 idFilenameDb.put(docId: id, path);
102 }
103}
104
105void DocumentUrlDB::del(quint64 id)
106{
107 if (!id) {
108 qCWarning(ENGINE) << "del called with invalid docId";
109 return;
110 }
111
112 IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
113 IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
114
115 auto path = idFilenameDb.get(docId: id);
116
117 // Remove from parent
118 QVector<quint64> subDocs = idTreeDb.get(docId: path.parentId);
119 if (subDocs.removeOne(t: id)) {
120 idTreeDb.set(docId: path.parentId, subDocIds: subDocs);
121 }
122
123 qCDebug(ENGINE) << id << "deleting" << path.name;
124 idFilenameDb.del(docId: id);
125}
126
127void DocumentUrlDB::add(quint64 id, quint64 parentId, const QByteArray& name)
128{
129 if (!id || name.isEmpty()) {
130 return;
131 }
132
133 IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
134 IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
135
136 QVector<quint64> subDocs = idTreeDb.get(docId: parentId);
137
138 // insert if not there
139 sortedIdInsert(vec&: subDocs, id);
140
141 idTreeDb.set(docId: parentId, subDocIds: subDocs);
142
143 // Update the IdFileName
144 IdFilenameDB::FilePath path;
145 path.parentId = parentId;
146 path.name = name;
147
148 idFilenameDb.put(docId: id, path);
149}
150
151bool DocumentUrlDB::contains(quint64 docId) const
152{
153 if (!docId) {
154 return false;
155 }
156
157 IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
158
159 return idFilenameDb.contains(docId);
160}
161
162QByteArray DocumentUrlDB::get(quint64 docId) const
163{
164 if (!docId) {
165 return QByteArray();
166 }
167
168 IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
169
170 IdFilenameDB::FilePath path;
171 if (!idFilenameDb.get(docId, path)) {
172 return QByteArray();
173 }
174
175 QByteArray ret = '/' + path.name;
176 quint64 id = path.parentId;
177 // arbitrary path depth limit - we have to deal with
178 // possibly corrupted DBs out in the wild
179 int depth_limit = 512;
180
181 while (id) {
182 if (!idFilenameDb.get(docId: id, path)) {
183 return QByteArray();
184 }
185 if (!depth_limit--) {
186 return QByteArray();
187 }
188 path.name.prepend(c: '/');
189
190 ret.prepend(a: path.name);
191 id = path.parentId;
192 }
193
194 return ret;
195}
196
197QVector<quint64> DocumentUrlDB::getChildren(quint64 docId) const
198{
199 IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
200 return idTreeDb.get(docId);
201}
202
203quint64 DocumentUrlDB::getId(quint64 docId, const QByteArray& fileName) const
204{
205 if (fileName.isEmpty()) {
206 return 0;
207 }
208
209 IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
210 IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
211
212 const QVector<quint64> subFiles = idTreeDb.get(docId);
213 IdFilenameDB::FilePath path;
214 for (quint64 id : subFiles) {
215 if (idFilenameDb.get(docId: id, path) && (path.name == fileName)) {
216 return id;
217 }
218 }
219
220 return 0;
221}
222
223QMap<quint64, QByteArray> DocumentUrlDB::toTestMap() const
224{
225 IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
226
227 QMap<quint64, QVector<quint64>> idTreeMap = idTreeDb.toTestMap();
228 QSet<quint64> allIds;
229
230 for (auto it = idTreeMap.cbegin(); it != idTreeMap.cend(); it++) {
231 allIds.insert(value: it.key());
232 for (quint64 id : it.value()) {
233 allIds.insert(value: id);
234 }
235 }
236
237 QMap<quint64, QByteArray> map;
238 for (quint64 id : allIds) {
239 if (id) {
240 QByteArray path = get(docId: id);
241 // FIXME: this prevents sanitizing
242 // reactivate Q_ASSERT(!path.isEmpty());
243 map.insert(key: id, value: path);
244 }
245 }
246
247 return map;
248}
249

source code of baloo/src/engine/documenturldb.cpp