File indexing completed on 2024-04-21 03:51:39

0001 /*
0002     This file is part of the KDE Baloo project.
0003     SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "documenturldb.h"
0009 #include "postingiterator.h"
0010 #include "enginedebug.h"
0011 
0012 #include <algorithm>
0013 
0014 using namespace Baloo;
0015 
0016 DocumentUrlDB::DocumentUrlDB(MDB_dbi idTreeDb, MDB_dbi idFilenameDb, MDB_txn* txn)
0017     : m_txn(txn)
0018     , m_idFilenameDbi(idFilenameDb)
0019     , m_idTreeDbi(idTreeDb)
0020 {
0021 }
0022 
0023 DocumentUrlDB::~DocumentUrlDB()
0024 {
0025 }
0026 
0027 bool DocumentUrlDB::addPath(const QByteArray& url)
0028 {
0029     Q_ASSERT(!url.isEmpty());
0030     Q_ASSERT(!url.endsWith('/'));
0031     if (url.isEmpty() || url.endsWith('/')) {
0032         return false;
0033     }
0034 
0035     IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
0036 
0037     QByteArray arr = url;
0038     quint64 id = filePathToId(arr);
0039 
0040     while (id) {
0041         if (idFilenameDb.contains(id)) {
0042             return true;
0043         }
0044 
0045         int pos = arr.lastIndexOf('/');
0046         QByteArray name = arr.mid(pos + 1);
0047 
0048         if (pos == 0) {
0049             add(id, 0, name);
0050             return true;
0051         }
0052 
0053         arr.resize(pos);
0054         auto parentId = filePathToId(arr);
0055         if (!parentId) {
0056             return false;
0057         }
0058         add(id, parentId, name);
0059 
0060         id = parentId;
0061     }
0062     return false;
0063 }
0064 
0065 bool DocumentUrlDB::put(quint64 docId, quint64 parentId, const QByteArray& filename)
0066 {
0067     Q_ASSERT(docId);
0068     Q_ASSERT(!filename.contains('/'));
0069     Q_ASSERT(!filename.isEmpty());
0070     if (!docId || filename.isEmpty() || filename.contains('/')) {
0071         return false;
0072     }
0073 
0074     add(docId, parentId, filename);
0075     return true;
0076 }
0077 
0078 void DocumentUrlDB::updateUrl(quint64 id, quint64 newParentId, const QByteArray& newName)
0079 {
0080     IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
0081     IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
0082 
0083     // Sanity checks
0084     auto path = idFilenameDb.get(id);
0085     if (path.parentId != newParentId) {
0086         // Remove from old parent
0087         QVector<quint64> subDocs = idTreeDb.get(path.parentId);
0088         if (subDocs.removeOne(id)) {
0089             idTreeDb.set(path.parentId, subDocs);
0090         }
0091         // Add to new parent
0092         subDocs = idTreeDb.get(newParentId);
0093         sortedIdInsert(subDocs, id);
0094         idTreeDb.set(newParentId, subDocs);
0095     }
0096 
0097     if ((newName != path.name) || (newParentId != path.parentId)) {
0098         qCDebug(ENGINE) << id << "renaming" << path.name << "to" << newName;
0099         path.parentId = newParentId;
0100         path.name = newName;
0101         idFilenameDb.put(id, path);
0102     }
0103 }
0104 
0105 void DocumentUrlDB::del(quint64 id)
0106 {
0107     if (!id) {
0108         qCWarning(ENGINE) << "del called with invalid docId";
0109         return;
0110     }
0111 
0112     IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
0113     IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
0114 
0115     auto path = idFilenameDb.get(id);
0116 
0117     // Remove from parent
0118     QVector<quint64> subDocs = idTreeDb.get(path.parentId);
0119     if (subDocs.removeOne(id)) {
0120         idTreeDb.set(path.parentId, subDocs);
0121     }
0122 
0123     qCDebug(ENGINE) << id << "deleting" << path.name;
0124     idFilenameDb.del(id);
0125 }
0126 
0127 void DocumentUrlDB::add(quint64 id, quint64 parentId, const QByteArray& name)
0128 {
0129     if (!id || name.isEmpty()) {
0130         return;
0131     }
0132 
0133     IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
0134     IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
0135 
0136     QVector<quint64> subDocs = idTreeDb.get(parentId);
0137 
0138     // insert if not there
0139     sortedIdInsert(subDocs, id);
0140 
0141     idTreeDb.set(parentId, subDocs);
0142 
0143     // Update the IdFileName
0144     IdFilenameDB::FilePath path;
0145     path.parentId = parentId;
0146     path.name = name;
0147 
0148     idFilenameDb.put(id, path);
0149 }
0150 
0151 bool DocumentUrlDB::contains(quint64 docId) const
0152 {
0153     if (!docId) {
0154         return false;
0155     }
0156 
0157     IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
0158 
0159     return idFilenameDb.contains(docId);
0160 }
0161 
0162 QByteArray DocumentUrlDB::get(quint64 docId) const
0163 {
0164     if (!docId) {
0165         return QByteArray();
0166     }
0167 
0168     IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
0169 
0170     IdFilenameDB::FilePath path;
0171     if (!idFilenameDb.get(docId, path)) {
0172         return QByteArray();
0173     }
0174 
0175     QByteArray ret = '/' + path.name;
0176     quint64 id = path.parentId;
0177     // arbitrary path depth limit - we have to deal with
0178     // possibly corrupted DBs out in the wild
0179     int depth_limit = 512;
0180 
0181     while (id) {
0182         if (!idFilenameDb.get(id, path)) {
0183             return QByteArray();
0184         }
0185         if (!depth_limit--) {
0186             return QByteArray();
0187         }
0188         path.name.prepend('/');
0189 
0190         ret.prepend(path.name);
0191         id = path.parentId;
0192     }
0193 
0194     return ret;
0195 }
0196 
0197 QVector<quint64> DocumentUrlDB::getChildren(quint64 docId) const
0198 {
0199     IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
0200     return idTreeDb.get(docId);
0201 }
0202 
0203 quint64 DocumentUrlDB::getId(quint64 docId, const QByteArray& fileName) const
0204 {
0205     if (fileName.isEmpty()) {
0206         return 0;
0207     }
0208 
0209     IdFilenameDB idFilenameDb(m_idFilenameDbi, m_txn);
0210     IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
0211 
0212     const QVector<quint64> subFiles = idTreeDb.get(docId);
0213     IdFilenameDB::FilePath path;
0214     for (quint64 id : subFiles) {
0215         if (idFilenameDb.get(id, path) && (path.name == fileName)) {
0216             return id;
0217         }
0218     }
0219 
0220     return 0;
0221 }
0222 
0223 QMap<quint64, QByteArray> DocumentUrlDB::toTestMap() const
0224 {
0225     IdTreeDB idTreeDb(m_idTreeDbi, m_txn);
0226 
0227     QMap<quint64, QVector<quint64>> idTreeMap = idTreeDb.toTestMap();
0228     QSet<quint64> allIds;
0229 
0230     for (auto it = idTreeMap.cbegin(); it != idTreeMap.cend(); it++) {
0231         allIds.insert(it.key());
0232         for (quint64 id : it.value()) {
0233             allIds.insert(id);
0234         }
0235     }
0236 
0237     QMap<quint64, QByteArray> map;
0238     for (quint64 id : allIds) {
0239         if (id) {
0240             QByteArray path = get(id);
0241             // FIXME: this prevents sanitizing
0242             // reactivate  Q_ASSERT(!path.isEmpty());
0243             map.insert(id, path);
0244         }
0245     }
0246 
0247     return map;
0248 }