File indexing completed on 2024-11-24 04:34:34
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2018 Thomas Fischer <fischer@unix-ag.uni-kl.de> 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, see <https://www.gnu.org/licenses/>. * 0018 ***************************************************************************/ 0019 0020 #include "items.h" 0021 0022 #include <QNetworkRequest> 0023 #include <QNetworkReply> 0024 #include <QXmlStreamReader> 0025 #include <QUrlQuery> 0026 #include <QTimer> 0027 0028 #include <File> 0029 #include <FileImporterBibTeX> 0030 #include "zotero/api.h" 0031 #include "internalnetworkaccessmanager.h" 0032 #include "logging_networking.h" 0033 0034 using namespace Zotero; 0035 0036 class Zotero::Items::Private 0037 { 0038 private: 0039 Zotero::Items *p; 0040 0041 public: 0042 QSharedPointer<Zotero::API> api; 0043 0044 Private(QSharedPointer<Zotero::API> a, Zotero::Items *parent) 0045 : p(parent), api(a) { 0046 /// nothing 0047 } 0048 0049 QNetworkReply *requestZoteroUrl(const QUrl &url) { 0050 QUrl internalUrl = url; 0051 api->addLimitToUrl(internalUrl); 0052 QNetworkRequest request = api->request(internalUrl); 0053 QNetworkReply *reply = InternalNetworkAccessManager::instance().get(request); 0054 connect(reply, &QNetworkReply::finished, p, &Zotero::Items::finishedFetchingItems); 0055 return reply; 0056 } 0057 0058 void retrieveItems(const QUrl &url, int start) { 0059 QUrl internalUrl = url; 0060 0061 static const QString queryItemStart = QStringLiteral("start"); 0062 QUrlQuery query(internalUrl); 0063 query.removeQueryItem(queryItemStart); 0064 query.addQueryItem(queryItemStart, QString::number(start)); 0065 internalUrl.setQuery(query); 0066 0067 if (api->inBackoffMode()) 0068 /// If Zotero asked to 'back off', wait until this period is over before issuing the next request 0069 QTimer::singleShot((api->backoffSecondsLeft() + 1) * 1000, p, [ = ]() { 0070 requestZoteroUrl(internalUrl); 0071 }); 0072 else 0073 requestZoteroUrl(internalUrl); 0074 } 0075 }; 0076 0077 Items::Items(QSharedPointer<Zotero::API> api, QObject *parent) 0078 : QObject(parent), d(new Zotero::Items::Private(api, this)) 0079 { 0080 /// nothing 0081 } 0082 0083 Items::~Items() 0084 { 0085 delete d; 0086 } 0087 0088 void Items::retrieveItemsByCollection(const QString &collection) 0089 { 0090 QUrl url = d->api->baseUrl().adjusted(QUrl::StripTrailingSlash); 0091 if (collection.isEmpty()) 0092 url.setPath(url.path() + QStringLiteral("/items")); 0093 else 0094 url.setPath(url.path() + QString(QStringLiteral("/collections/%1/items")).arg(collection)); 0095 QUrlQuery query(url); 0096 query.addQueryItem(QStringLiteral("format"), QStringLiteral("bibtex")); 0097 url.setQuery(query); 0098 0099 if (d->api->inBackoffMode()) 0100 /// If Zotero asked to 'back off', wait until this period is over before issuing the next request 0101 QTimer::singleShot((d->api->backoffSecondsLeft() + 1) * 1000, this, [ = ]() { 0102 d->retrieveItems(url, 0); 0103 }); 0104 else 0105 d->retrieveItems(url, 0); 0106 } 0107 0108 void Items::retrieveItemsByTag(const QString &tag) 0109 { 0110 QUrl url = d->api->baseUrl().adjusted(QUrl::StripTrailingSlash); 0111 QUrlQuery query(url); 0112 if (!tag.isEmpty()) 0113 query.addQueryItem(QStringLiteral("tag"), tag); 0114 url.setPath(url.path() + QStringLiteral("/items")); 0115 query.addQueryItem(QStringLiteral("format"), QStringLiteral("bibtex")); 0116 url.setQuery(query); 0117 0118 if (d->api->inBackoffMode()) 0119 /// If Zotero asked to 'back off', wait until this period is over before issuing the next request 0120 QTimer::singleShot((d->api->backoffSecondsLeft() + 1) * 1000, this, [ = ]() { 0121 d->retrieveItems(url, 0); 0122 }); 0123 else 0124 d->retrieveItems(url, 0); 0125 } 0126 0127 void Items::finishedFetchingItems() 0128 { 0129 QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); 0130 static const QString queryItemStart = QStringLiteral("start"); 0131 bool ok = false; 0132 const int start = QUrlQuery(reply->url()).queryItemValue(queryItemStart).toInt(&ok); 0133 0134 if (reply->hasRawHeader("Backoff")) { 0135 bool ok = false; 0136 int time = QString::fromLatin1(reply->rawHeader("Backoff").constData()).toInt(&ok); 0137 if (!ok) time = 10; ///< parsing argument of raw header 'Backoff' failed? 10 seconds is fallback 0138 d->api->startBackoff(time); 0139 } else if (reply->hasRawHeader("Retry-After")) { 0140 bool ok = false; 0141 int time = QString::fromLatin1(reply->rawHeader("Retry-After").constData()).toInt(&ok); 0142 if (!ok) time = 10; ///< parsing argument of raw header 'Retry-After' failed? 10 seconds is fallback 0143 d->api->startBackoff(time); 0144 } 0145 0146 if (reply->error() == QNetworkReply::NoError && ok) { 0147 const QString bibTeXcode = QString::fromUtf8(reply->readAll().constData()); 0148 /// Non-empty result? 0149 if (!bibTeXcode.isEmpty()) { 0150 FileImporterBibTeX importer(this); 0151 /// Parse text into bibliography object 0152 File *bibtexFile = importer.fromString(bibTeXcode); 0153 0154 /// Perform basic sanity checks ... 0155 if (bibtexFile != nullptr && !bibtexFile->isEmpty()) { 0156 for (const QSharedPointer<Element> &element : const_cast<const File &>(*bibtexFile)) { 0157 Q_EMIT foundElement(element); ///< ... and publish result 0158 } 0159 } 0160 0161 delete bibtexFile; 0162 0163 /// Non-empty result means there may be more ... 0164 d->retrieveItems(reply->url(), start + Zotero::API::limit); 0165 } else { 0166 /// Done retrieving BibTeX code 0167 Q_EMIT stoppedSearch(0); // TODO proper error codes 0168 } 0169 } else { 0170 qCWarning(LOG_KBIBTEX_NETWORKING) << reply->errorString(); ///< something went wrong 0171 Q_EMIT stoppedSearch(1); // TODO proper error codes 0172 } 0173 }