File indexing completed on 2025-03-09 04:58:14

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2022-2023 Harald Sitter <sitter@kde.org>
0003 
0004 #include "sentrypostman.h"
0005 
0006 #include <chrono>
0007 #include <thread>
0008 
0009 #include <QDirIterator>
0010 #include <QEventLoopLocker>
0011 #include <QJsonDocument>
0012 
0013 #include "debug.h"
0014 #include "sentrypaths.h"
0015 
0016 using namespace Qt::StringLiterals;
0017 using namespace std::chrono_literals;
0018 
0019 struct Transfer {
0020     QString path;
0021     QString sentPath;
0022     SentryReply *reply;
0023     std::shared_ptr<QEventLoopLocker> lock = std::make_shared<QEventLoopLocker>();
0024 };
0025 
0026 void SentryPostman::run()
0027 {
0028     QEventLoopLocker lock;
0029 
0030     const auto cacheDir = SentryPaths::payloadsDir();
0031     if (cacheDir.isEmpty()) {
0032         qCWarning(SENTRY_DEBUG) << "Failed to resolve payloadsDir";
0033         return;
0034     }
0035 
0036     qCDebug(SENTRY_DEBUG) << "looking at " << cacheDir;
0037     QDirIterator it(cacheDir, QDir::Files);
0038     if (!it.hasNext()) {
0039         qCDebug(SENTRY_DEBUG) << "nothing there";
0040         return; // nothing to process
0041     }
0042 
0043     while (it.hasNext()) {
0044         const auto path = it.next();
0045         const auto filename = it.fileName();
0046         const auto mtime = it.fileInfo().lastModified();
0047         post(path, filename, mtime);
0048     }
0049 }
0050 void SentryPostman::post(const QString &path, const QString &filename, const QDateTime &mtime)
0051 {
0052     const auto currentTime = QDateTime::currentDateTime();
0053 
0054     qCDebug(SENTRY_DEBUG) << "processing" << path;
0055     static constexpr auto daysInMonth = 30;
0056     if (currentTime - mtime >= daysInMonth * 24h) {
0057         // Incredibly old report. Probably not worth to submit it anymore. This can happen if the file is getting
0058         // rejected by the server over and over mostly.
0059         QFile::remove(path);
0060         return;
0061     }
0062 
0063     static constexpr auto flushTime = 8s; // arbitrary timeout in which we expect a flush to finish
0064     if (currentTime - mtime < flushTime) {
0065         std::this_thread::sleep_for(flushTime); // make sure the file is fully flushed
0066     }
0067 
0068     QFile file(path);
0069     if (!file.open(QFile::ReadOnly)) {
0070         qCWarning(SENTRY_DEBUG) << "Failed to open" << path;
0071         return;
0072     }
0073 
0074     const auto doc = QJsonDocument::fromJson(file.readLine());
0075     file.reset();
0076     const auto dsn = doc["dsn"_L1].toString();
0077     if (dsn.isEmpty()) {
0078         qCWarning(SENTRY_DEBUG) << "Missing DSN. Discarding" << path;
0079         QFile::remove(path); // invalid, discard it
0080         return;
0081     }
0082 
0083     QNetworkRequest request(QUrl(dsn + "/envelope/"_L1));
0084     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-sentry-envelope"_L1);
0085     request.setHeader(QNetworkRequest::UserAgentHeader, "DrKonqi"_L1);
0086     // Auth is handled through the payload itself, it should carry a DSN.
0087     qCDebug(SENTRY_DEBUG) << "requesting" << request.url();
0088 
0089     auto reply = m_connection.post(request, file.readAll());
0090     Transfer transfer{.path = path, .sentPath = SentryPaths::sentPayloadPath(filename), .reply = reply};
0091     QObject::connect(reply, &SentryReply::finished, this, [transfer] {
0092         transfer.reply->deleteLater();
0093         qCDebug(SENTRY_DEBUG) << transfer.reply->error();
0094         if (transfer.reply->error() != QNetworkReply::NoError) {
0095             qCWarning(SENTRY_DEBUG) << transfer.reply->error() << transfer.reply->errorString();
0096             return;
0097         }
0098         qCDebug(SENTRY_DEBUG) << "renaming" << transfer.path << "to" << transfer.sentPath;
0099         if (QFile::exists(transfer.sentPath)) {
0100             QFile::remove(transfer.sentPath);
0101         }
0102         QFile::rename(transfer.path, transfer.sentPath);
0103     });
0104 }
0105 
0106 #include "moc_sentrypostman.cpp"