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"