File indexing completed on 2024-12-22 04:57:54

0001 /*
0002     SPDX-License-Identifier: BSD-2-Clause
0003 */
0004 
0005 #include "debug.h"
0006 #include <QTimer>
0007 #if QT_VERSION >= 0x050000
0008 #include <QUrlQuery>
0009 #endif
0010 
0011 #include "o2/o0globals.h"
0012 #include "o2/o2.h"
0013 #include "o2/o2requestor.h"
0014 #include <chrono>
0015 
0016 using namespace std::chrono_literals;
0017 
0018 O2Requestor::O2Requestor(QNetworkAccessManager *manager, O2 *authenticator, QObject *parent)
0019     : QObject(parent)
0020     , reply_(nullptr)
0021     , status_(Idle)
0022 {
0023     manager_ = manager;
0024     authenticator_ = authenticator;
0025     if (authenticator) {
0026         timedReplies_.setIgnoreSslErrors(authenticator->ignoreSslErrors());
0027     }
0028     qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
0029     connect(authenticator, &O2::refreshFinished, this, &O2Requestor::onRefreshFinished, Qt::QueuedConnection);
0030 }
0031 
0032 O2Requestor::~O2Requestor() = default;
0033 
0034 int O2Requestor::get(const QNetworkRequest &req)
0035 {
0036     if (-1 == setup(req, QNetworkAccessManager::GetOperation)) {
0037         return -1;
0038     }
0039     reply_ = manager_->get(request_);
0040     timedReplies_.add(reply_);
0041     connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
0042     connect(reply_, &QNetworkReply::finished, this, &O2Requestor::onRequestFinished, Qt::QueuedConnection);
0043     return id_;
0044 }
0045 
0046 int O2Requestor::post(const QNetworkRequest &req, const QByteArray &data)
0047 {
0048     if (-1 == setup(req, QNetworkAccessManager::PostOperation)) {
0049         return -1;
0050     }
0051     data_ = data;
0052     reply_ = manager_->post(request_, data_);
0053     timedReplies_.add(reply_);
0054     connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
0055     connect(reply_, &QNetworkReply::finished, this, &O2Requestor::onRequestFinished, Qt::QueuedConnection);
0056     connect(reply_, &QNetworkReply::uploadProgress, this, &O2Requestor::onUploadProgress);
0057     return id_;
0058 }
0059 
0060 int O2Requestor::put(const QNetworkRequest &req, const QByteArray &data)
0061 {
0062     if (-1 == setup(req, QNetworkAccessManager::PutOperation)) {
0063         return -1;
0064     }
0065     data_ = data;
0066     reply_ = manager_->put(request_, data_);
0067     timedReplies_.add(reply_);
0068     connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
0069     connect(reply_, &QNetworkReply::finished, this, &O2Requestor::onRequestFinished, Qt::QueuedConnection);
0070     connect(reply_, &QNetworkReply::uploadProgress, this, &O2Requestor::onUploadProgress);
0071     return id_;
0072 }
0073 
0074 void O2Requestor::onRefreshFinished(QNetworkReply::NetworkError error)
0075 {
0076     if (status_ != Requesting) {
0077         qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::onRefreshFinished: No pending request";
0078         return;
0079     }
0080     if (QNetworkReply::NoError == error) {
0081         QTimer::singleShot(100ms, this, &O2Requestor::retry);
0082     } else {
0083         error_ = error;
0084         QTimer::singleShot(10ms, this, &O2Requestor::finish);
0085     }
0086 }
0087 
0088 void O2Requestor::onRequestFinished()
0089 {
0090     auto senderReply = qobject_cast<QNetworkReply *>(sender());
0091     QNetworkReply::NetworkError error = senderReply->error();
0092     if (status_ == Idle) {
0093         return;
0094     }
0095     if (reply_ != senderReply) {
0096         return;
0097     }
0098     if (error == QNetworkReply::NoError) {
0099         QTimer::singleShot(10ms, this, &O2Requestor::finish);
0100     }
0101 }
0102 
0103 void O2Requestor::onRequestError(QNetworkReply::NetworkError error)
0104 {
0105     qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::onRequestError: Error" << (int)error;
0106     if (status_ == Idle) {
0107         return;
0108     }
0109     if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
0110         return;
0111     }
0112     int httpStatus = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
0113     qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::onRequestError: HTTP status" << httpStatus
0114                                        << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
0115     qCDebug(TOMBOYNOTESRESOURCE_LOG) << reply_->readAll();
0116     if ((status_ == Requesting) && (httpStatus == 401)) {
0117         // Call O2::refresh. Note the O2 instance might live in a different thread
0118         if (QMetaObject::invokeMethod(authenticator_, "refresh")) {
0119             return;
0120         }
0121         qCCritical(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::onRequestError: Invoking remote refresh failed";
0122     }
0123     error_ = error;
0124     QTimer::singleShot(10ms, this, &O2Requestor::finish);
0125 }
0126 
0127 void O2Requestor::onUploadProgress(qint64 uploaded, qint64 total)
0128 {
0129     if (status_ == Idle) {
0130         qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::onUploadProgress: No pending request";
0131         return;
0132     }
0133     if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
0134         return;
0135     }
0136     Q_EMIT uploadProgress(id_, uploaded, total);
0137 }
0138 
0139 int O2Requestor::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation)
0140 {
0141     static int currentId;
0142     QUrl url;
0143 
0144     if (status_ != Idle) {
0145         qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::setup: Another request pending";
0146         return -1;
0147     }
0148 
0149     request_ = req;
0150     operation_ = operation;
0151     id_ = currentId++;
0152     url_ = url = req.url();
0153 #if QT_VERSION < 0x050000
0154     url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token());
0155 #else
0156     QUrlQuery query(url);
0157     query.addQueryItem(QLatin1StringView(O2_OAUTH2_ACCESS_TOKEN), authenticator_->token());
0158     url.setQuery(query);
0159 #endif
0160     request_.setUrl(url);
0161     status_ = Requesting;
0162     error_ = QNetworkReply::NoError;
0163     return id_;
0164 }
0165 
0166 void O2Requestor::finish()
0167 {
0168     QByteArray data;
0169     if (status_ == Idle) {
0170         qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::finish: No pending request";
0171         return;
0172     }
0173     data = reply_->readAll();
0174     status_ = Idle;
0175     timedReplies_.remove(reply_);
0176     reply_->disconnect(this);
0177     reply_->deleteLater();
0178     Q_EMIT finished(id_, error_, data);
0179 }
0180 
0181 void O2Requestor::retry()
0182 {
0183     if (status_ != Requesting) {
0184         qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O2Requestor::retry: No pending request";
0185         return;
0186     }
0187     timedReplies_.remove(reply_);
0188     reply_->disconnect(this);
0189     reply_->deleteLater();
0190     QUrl url = url_;
0191 #if QT_VERSION < 0x050000
0192     url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token());
0193 #else
0194     QUrlQuery query(url);
0195     query.addQueryItem(QLatin1StringView(O2_OAUTH2_ACCESS_TOKEN), authenticator_->token());
0196     url.setQuery(query);
0197 #endif
0198     request_.setUrl(url);
0199     status_ = ReRequesting;
0200     switch (operation_) {
0201     case QNetworkAccessManager::GetOperation:
0202         reply_ = manager_->get(request_);
0203         break;
0204     case QNetworkAccessManager::PostOperation:
0205         reply_ = manager_->post(request_, data_);
0206         break;
0207     default:
0208         reply_ = manager_->put(request_, data_);
0209     }
0210     timedReplies_.add(reply_);
0211     connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
0212     connect(reply_, &QNetworkReply::finished, this, &O2Requestor::onRequestFinished, Qt::QueuedConnection);
0213     connect(reply_, &QNetworkReply::uploadProgress, this, &O2Requestor::onUploadProgress);
0214 }
0215 
0216 #include "moc_o2requestor.cpp"