File indexing completed on 2024-05-12 05:13:02
0001 /* 0002 This file is part of Akregator. 0003 0004 SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 0007 */ 0008 0009 #include "feedstorage.h" 0010 #include "storage.h" 0011 0012 #include <Syndication/DocumentSource> 0013 #include <Syndication/Feed> 0014 #include <Syndication/Item> 0015 0016 #include "mk4.h" 0017 0018 #include <QDateTime> 0019 #include <QDebug> 0020 #include <QStandardPaths> 0021 0022 namespace 0023 { 0024 static uint calcHash(const QString &str) 0025 { 0026 if (str.isNull()) { // handle null string as "", prevents crash 0027 return calcHash(QLatin1StringView("")); 0028 } 0029 const char *s = str.toLatin1().constData(); 0030 uint hash = 5381; 0031 int c; 0032 while ((c = *s++)) { 0033 hash = ((hash << 5) + hash) + c; // hash*33 + c 0034 } 0035 return hash; 0036 } 0037 } 0038 0039 namespace Akregator 0040 { 0041 namespace Backend 0042 { 0043 class FeedStorage::FeedStoragePrivate 0044 { 0045 public: 0046 FeedStoragePrivate() 0047 : modified(false) 0048 , pguid("guid") 0049 , ptitle("title") 0050 , pdescription("description") 0051 , pcontent("content") 0052 , plink("link") 0053 , pcommentsLink("commentsLink") 0054 , ptag("tag") 0055 , pEnclosureType("enclosureType") 0056 , pEnclosureUrl("enclosureUrl") 0057 , pcatTerm("catTerm") 0058 , pcatScheme("catScheme") 0059 , pcatName("catName") 0060 , pauthorName("authorName") 0061 , pauthorUri("authorUri") 0062 , pauthorEMail("authorEMail") 0063 , phash("hash") 0064 , pguidIsHash("guidIsHash") 0065 , pguidIsPermaLink("guidIsPermaLink") 0066 , pcomments("comments") 0067 , pstatus("status") 0068 , ppubDate("pubDate") 0069 , pHasEnclosure("hasEnclosure") 0070 , pEnclosureLength("enclosureLength") 0071 { 0072 } 0073 0074 QString url; 0075 c4_Storage *storage; 0076 Storage *mainStorage; 0077 c4_View archiveView; 0078 0079 bool autoCommit; 0080 bool modified; 0081 c4_StringProp pguid, ptitle, pdescription, pcontent, plink, pcommentsLink, ptag, pEnclosureType, pEnclosureUrl, pcatTerm, pcatScheme, pcatName, pauthorName, 0082 pauthorUri, pauthorEMail; 0083 c4_IntProp phash, pguidIsHash, pguidIsPermaLink, pcomments, pstatus, ppubDate, pHasEnclosure, pEnclosureLength; 0084 }; 0085 0086 FeedStorage::FeedStorage(const QString &url, Storage *main) 0087 : d(new FeedStoragePrivate) 0088 { 0089 d->autoCommit = main->autoCommit(); 0090 d->url = url; 0091 d->mainStorage = main; 0092 0093 QString url2 = url; 0094 0095 if (url.length() > 255) { 0096 url2 = url.left(200) + QString::number(::calcHash(url), 16); 0097 } 0098 0099 qDebug() << url2; 0100 QString t = url2; 0101 QString t2 = url2; 0102 QString filePath = main->archivePath() + QLatin1Char('/') + t.replace(QLatin1Char('/'), QLatin1Char('_')).replace(QLatin1Char(':'), QLatin1Char('_')); 0103 d->storage = new c4_Storage(QString(filePath + QLatin1StringView(".mk4")).toLocal8Bit().constData(), true); 0104 0105 d->archiveView = d->storage->GetAs( 0106 "articles[guid:S,title:S,hash:I,guidIsHash:I,guidIsPermaLink:I,description:S,link:S,comments:I,commentsLink:S,status:I,pubDate:I,tags[tag:S]," 0107 "hasEnclosure:I,enclosureUrl:S,enclosureType:S,enclosureLength:I,categories[catTerm:S,catScheme:S,catName:S],authorName:S,content:S,authorUri:S," 0108 "authorEMail:S]"); 0109 0110 c4_View hash = d->storage->GetAs("archiveHash[_H:I,_R:I]"); 0111 d->archiveView = d->archiveView.Hash(hash, 1); // hash on guid 0112 } 0113 0114 FeedStorage::~FeedStorage() 0115 { 0116 delete d->storage; 0117 } 0118 0119 void FeedStorage::markDirty() 0120 { 0121 if (!d->modified) { 0122 d->modified = true; 0123 // Tell this to mainStorage 0124 d->mainStorage->markDirty(); 0125 } 0126 } 0127 0128 void FeedStorage::commit() 0129 { 0130 if (d->modified) { 0131 d->storage->Commit(); 0132 } 0133 d->modified = false; 0134 } 0135 0136 void FeedStorage::rollback() 0137 { 0138 d->storage->Rollback(); 0139 } 0140 0141 void FeedStorage::close() 0142 { 0143 if (d->autoCommit) { 0144 commit(); 0145 } 0146 } 0147 0148 int FeedStorage::unread() const 0149 { 0150 return d->mainStorage->unreadFor(d->url); 0151 } 0152 0153 void FeedStorage::setUnread(int unread) 0154 { 0155 d->mainStorage->setUnreadFor(d->url, unread); 0156 } 0157 0158 int FeedStorage::totalCount() const 0159 { 0160 return d->mainStorage->totalCountFor(d->url); 0161 } 0162 0163 void FeedStorage::setTotalCount(int total) 0164 { 0165 d->mainStorage->setTotalCountFor(d->url, total); 0166 } 0167 0168 QDateTime FeedStorage::lastFetch() const 0169 { 0170 return d->mainStorage->lastFetchFor(d->url); 0171 } 0172 0173 void FeedStorage::setLastFetch(const QDateTime &lastFetch) 0174 { 0175 d->mainStorage->setLastFetchFor(d->url, lastFetch); 0176 } 0177 0178 QStringList FeedStorage::articles() const 0179 { 0180 QStringList list; 0181 int size = d->archiveView.GetSize(); 0182 list.reserve(size); 0183 for (int i = 0; i < size; ++i) { // fill with guids 0184 list += QString::fromLatin1(QByteArray(d->pguid(d->archiveView.GetAt(i)))); 0185 } 0186 return list; 0187 } 0188 0189 void FeedStorage::addEntry(const QString &guid) 0190 { 0191 c4_Row row; 0192 d->pguid(row) = guid.toLatin1().constData(); 0193 if (!contains(guid)) { 0194 d->archiveView.Add(row); 0195 markDirty(); 0196 setTotalCount(totalCount() + 1); 0197 } 0198 } 0199 0200 bool FeedStorage::contains(const QString &guid) const 0201 { 0202 return findArticle(guid) != -1; 0203 } 0204 0205 int FeedStorage::findArticle(const QString &guid) const 0206 { 0207 c4_Row findrow; 0208 d->pguid(findrow) = guid.toLatin1().constData(); 0209 return d->archiveView.Find(findrow); 0210 } 0211 0212 void FeedStorage::deleteArticle(const QString &guid) 0213 { 0214 int findidx = findArticle(guid); 0215 if (findidx != -1) { 0216 setTotalCount(totalCount() - 1); 0217 d->archiveView.RemoveAt(findidx); 0218 markDirty(); 0219 } 0220 } 0221 0222 bool FeedStorage::guidIsHash(const QString &guid) const 0223 { 0224 int findidx = findArticle(guid); 0225 return findidx != -1 ? d->pguidIsHash(d->archiveView.GetAt(findidx)) : false; 0226 } 0227 0228 bool FeedStorage::guidIsPermaLink(const QString &guid) const 0229 { 0230 int findidx = findArticle(guid); 0231 return findidx != -1 ? d->pguidIsPermaLink(d->archiveView.GetAt(findidx)) : false; 0232 } 0233 0234 uint FeedStorage::hash(const QString &guid) const 0235 { 0236 int findidx = findArticle(guid); 0237 return findidx != -1 ? d->phash(d->archiveView.GetAt(findidx)) : 0; 0238 } 0239 0240 void FeedStorage::setDeleted(const QString &guid) 0241 { 0242 int findidx = findArticle(guid); 0243 if (findidx == -1) { 0244 return; 0245 } 0246 0247 c4_Row row; 0248 row = d->archiveView.GetAt(findidx); 0249 d->pdescription(row) = ""; 0250 d->pcontent(row) = ""; 0251 d->ptitle(row) = ""; 0252 d->plink(row) = ""; 0253 d->pauthorName(row) = ""; 0254 d->pauthorUri(row) = ""; 0255 d->pauthorEMail(row) = ""; 0256 d->pcommentsLink(row) = ""; 0257 d->archiveView.SetAt(findidx, row); 0258 markDirty(); 0259 } 0260 0261 QString FeedStorage::link(const QString &guid) const 0262 { 0263 int findidx = findArticle(guid); 0264 return findidx != -1 ? QString::fromUtf8(QByteArray(d->plink(d->archiveView.GetAt(findidx)))) : QLatin1StringView(""); 0265 } 0266 0267 QDateTime FeedStorage::pubDate(const QString &guid) const 0268 { 0269 int findidx = findArticle(guid); 0270 return findidx != -1 ? QDateTime::fromSecsSinceEpoch(d->ppubDate(d->archiveView.GetAt(findidx))) : QDateTime(); 0271 } 0272 0273 int FeedStorage::status(const QString &guid) const 0274 { 0275 int findidx = findArticle(guid); 0276 return findidx != -1 ? d->pstatus(d->archiveView.GetAt(findidx)) : 0; 0277 } 0278 0279 void FeedStorage::setStatus(const QString &guid, int status) 0280 { 0281 int findidx = findArticle(guid); 0282 if (findidx == -1) { 0283 return; 0284 } 0285 c4_Row row; 0286 row = d->archiveView.GetAt(findidx); 0287 d->pstatus(row) = status; 0288 d->archiveView.SetAt(findidx, row); 0289 markDirty(); 0290 } 0291 0292 void FeedStorage::article(const QString &guid, uint &hash, QString &title, int &status, QDateTime &pubDate) const 0293 { 0294 int idx = findArticle(guid); 0295 if (idx != -1) { 0296 auto view = d->archiveView.GetAt(idx); 0297 hash = d->phash(view); 0298 title = QString::fromUtf8(QByteArray(d->ptitle(view))); 0299 status = d->pstatus(view); 0300 pubDate = QDateTime::fromSecsSinceEpoch(d->ppubDate(view)); 0301 } 0302 } 0303 0304 QString FeedStorage::title(const QString &guid) const 0305 { 0306 int findidx = findArticle(guid); 0307 return findidx != -1 ? QString::fromUtf8(QByteArray(d->ptitle(d->archiveView.GetAt(findidx)))) : QLatin1StringView(""); 0308 } 0309 0310 QString FeedStorage::description(const QString &guid) const 0311 { 0312 int findidx = findArticle(guid); 0313 return findidx != -1 ? QString::fromUtf8(QByteArray(d->pdescription(d->archiveView.GetAt(findidx)))) : QLatin1StringView(""); 0314 } 0315 0316 QString FeedStorage::content(const QString &guid) const 0317 { 0318 int findidx = findArticle(guid); 0319 return findidx != -1 ? QString::fromUtf8(QByteArray(d->pcontent(d->archiveView.GetAt(findidx)))) : QLatin1StringView(""); 0320 } 0321 0322 void FeedStorage::setPubDate(const QString &guid, const QDateTime &pubdate) 0323 { 0324 int findidx = findArticle(guid); 0325 if (findidx == -1) { 0326 return; 0327 } 0328 c4_Row row; 0329 row = d->archiveView.GetAt(findidx); 0330 d->ppubDate(row) = pubdate.toSecsSinceEpoch(); 0331 d->archiveView.SetAt(findidx, row); 0332 markDirty(); 0333 } 0334 0335 void FeedStorage::setGuidIsHash(const QString &guid, bool isHash) 0336 { 0337 int findidx = findArticle(guid); 0338 if (findidx == -1) { 0339 return; 0340 } 0341 c4_Row row; 0342 row = d->archiveView.GetAt(findidx); 0343 d->pguidIsHash(row) = isHash; 0344 d->archiveView.SetAt(findidx, row); 0345 markDirty(); 0346 } 0347 0348 void FeedStorage::setLink(const QString &guid, const QString &link) 0349 { 0350 int findidx = findArticle(guid); 0351 if (findidx == -1) { 0352 return; 0353 } 0354 c4_Row row; 0355 row = d->archiveView.GetAt(findidx); 0356 d->plink(row) = !link.isEmpty() ? link.toUtf8().constData() : ""; 0357 d->archiveView.SetAt(findidx, row); 0358 markDirty(); 0359 } 0360 0361 void FeedStorage::setHash(const QString &guid, uint hash) 0362 { 0363 int findidx = findArticle(guid); 0364 if (findidx == -1) { 0365 return; 0366 } 0367 c4_Row row; 0368 row = d->archiveView.GetAt(findidx); 0369 d->phash(row) = hash; 0370 d->archiveView.SetAt(findidx, row); 0371 markDirty(); 0372 } 0373 0374 void FeedStorage::setTitle(const QString &guid, const QString &title) 0375 { 0376 int findidx = findArticle(guid); 0377 if (findidx == -1) { 0378 return; 0379 } 0380 c4_Row row; 0381 row = d->archiveView.GetAt(findidx); 0382 d->ptitle(row) = !title.isEmpty() ? title.toUtf8().data() : ""; 0383 d->archiveView.SetAt(findidx, row); 0384 markDirty(); 0385 } 0386 0387 void FeedStorage::setDescription(const QString &guid, const QString &description) 0388 { 0389 int findidx = findArticle(guid); 0390 if (findidx == -1) { 0391 return; 0392 } 0393 c4_Row row; 0394 row = d->archiveView.GetAt(findidx); 0395 d->pdescription(row) = !description.isEmpty() ? description.toUtf8().data() : ""; 0396 d->archiveView.SetAt(findidx, row); 0397 markDirty(); 0398 } 0399 0400 void FeedStorage::setContent(const QString &guid, const QString &content) 0401 { 0402 int findidx = findArticle(guid); 0403 if (findidx == -1) { 0404 return; 0405 } 0406 c4_Row row; 0407 row = d->archiveView.GetAt(findidx); 0408 d->pcontent(row) = !content.isEmpty() ? content.toUtf8().data() : ""; 0409 d->archiveView.SetAt(findidx, row); 0410 markDirty(); 0411 } 0412 0413 void FeedStorage::setAuthorName(const QString &guid, const QString &author) 0414 { 0415 int findidx = findArticle(guid); 0416 if (findidx == -1) { 0417 return; 0418 } 0419 c4_Row row; 0420 row = d->archiveView.GetAt(findidx); 0421 d->pauthorName(row) = !author.isEmpty() ? author.toUtf8().data() : ""; 0422 d->archiveView.SetAt(findidx, row); 0423 markDirty(); 0424 } 0425 0426 void FeedStorage::setAuthorUri(const QString &guid, const QString &author) 0427 { 0428 int findidx = findArticle(guid); 0429 if (findidx == -1) { 0430 return; 0431 } 0432 c4_Row row; 0433 row = d->archiveView.GetAt(findidx); 0434 d->pauthorUri(row) = !author.isEmpty() ? author.toUtf8().data() : ""; 0435 d->archiveView.SetAt(findidx, row); 0436 markDirty(); 0437 } 0438 0439 void FeedStorage::setAuthorEMail(const QString &guid, const QString &author) 0440 { 0441 int findidx = findArticle(guid); 0442 if (findidx == -1) { 0443 return; 0444 } 0445 c4_Row row; 0446 row = d->archiveView.GetAt(findidx); 0447 d->pauthorEMail(row) = !author.isEmpty() ? author.toUtf8().data() : ""; 0448 d->archiveView.SetAt(findidx, row); 0449 markDirty(); 0450 } 0451 0452 QString FeedStorage::authorName(const QString &guid) const 0453 { 0454 int findidx = findArticle(guid); 0455 return findidx != -1 ? QString::fromUtf8(QByteArray(d->pauthorName(d->archiveView.GetAt(findidx)))) : QString(); 0456 } 0457 0458 QString FeedStorage::authorUri(const QString &guid) const 0459 { 0460 int findidx = findArticle(guid); 0461 return findidx != -1 ? QString::fromUtf8(QByteArray(d->pauthorUri(d->archiveView.GetAt(findidx)))) : QString(); 0462 } 0463 0464 QString FeedStorage::authorEMail(const QString &guid) const 0465 { 0466 int findidx = findArticle(guid); 0467 return findidx != -1 ? QString::fromUtf8(QByteArray(d->pauthorEMail(d->archiveView.GetAt(findidx)))) : QString(); 0468 } 0469 0470 void FeedStorage::setGuidIsPermaLink(const QString &guid, bool isPermaLink) 0471 { 0472 int findidx = findArticle(guid); 0473 if (findidx == -1) { 0474 return; 0475 } 0476 c4_Row row; 0477 row = d->archiveView.GetAt(findidx); 0478 d->pguidIsPermaLink(row) = isPermaLink; 0479 d->archiveView.SetAt(findidx, row); 0480 markDirty(); 0481 } 0482 0483 void FeedStorage::setEnclosure(const QString &guid, const QString &url, const QString &type, int length) 0484 { 0485 int findidx = findArticle(guid); 0486 if (findidx == -1) { 0487 return; 0488 } 0489 c4_Row row; 0490 row = d->archiveView.GetAt(findidx); 0491 d->pHasEnclosure(row) = true; 0492 d->pEnclosureUrl(row) = !url.isEmpty() ? url.toUtf8().data() : ""; 0493 d->pEnclosureType(row) = !type.isEmpty() ? type.toUtf8().data() : ""; 0494 d->pEnclosureLength(row) = length; 0495 0496 d->archiveView.SetAt(findidx, row); 0497 markDirty(); 0498 } 0499 0500 void FeedStorage::removeEnclosure(const QString &guid) 0501 { 0502 int findidx = findArticle(guid); 0503 if (findidx == -1) { 0504 return; 0505 } 0506 c4_Row row; 0507 row = d->archiveView.GetAt(findidx); 0508 d->pHasEnclosure(row) = false; 0509 d->pEnclosureUrl(row) = ""; 0510 d->pEnclosureType(row) = ""; 0511 d->pEnclosureLength(row) = -1; 0512 0513 d->archiveView.SetAt(findidx, row); 0514 markDirty(); 0515 } 0516 0517 void FeedStorage::enclosure(const QString &guid, bool &hasEnclosure, QString &url, QString &type, int &length) const 0518 { 0519 int findidx = findArticle(guid); 0520 if (findidx == -1) { 0521 hasEnclosure = false; 0522 url.clear(); 0523 type.clear(); 0524 length = -1; 0525 return; 0526 } 0527 c4_Row row = d->archiveView.GetAt(findidx); 0528 hasEnclosure = d->pHasEnclosure(row); 0529 url = QLatin1StringView(d->pEnclosureUrl(row)); 0530 type = QLatin1StringView(d->pEnclosureType(row)); 0531 length = d->pEnclosureLength(row); 0532 } 0533 0534 void FeedStorage::setCategories(const QString &, const QStringList &categories) 0535 { 0536 // TODO 0537 } 0538 0539 QStringList FeedStorage::categories(const QString &guid) const 0540 { 0541 // TODO 0542 return {}; 0543 } 0544 } // namespace Backend 0545 } // namespace Akregator