diff --git a/resources/desktop/rssguard.appdata.xml b/resources/desktop/com.github.rssguard.appdata.xml similarity index 100% rename from resources/desktop/rssguard.appdata.xml rename to resources/desktop/com.github.rssguard.appdata.xml diff --git a/resources/desktop/rssguard.desktop b/resources/desktop/com.github.rssguard.desktop similarity index 100% rename from resources/desktop/rssguard.desktop rename to resources/desktop/com.github.rssguard.desktop diff --git a/resources/rssguard.qrc b/resources/rssguard.qrc index 559a134ee..e48655830 100755 --- a/resources/rssguard.qrc +++ b/resources/rssguard.qrc @@ -1534,7 +1534,7 @@ localizations/rssguard_pt.qm localizations/rssguard_sv.qm localizations/rssguard_zh.qm - desktop/rssguard.desktop + desktop/com.github.rssguard.desktop desktop/rssguard.desktop.autostart localizations/qt_bg.qm localizations/qt_ca.qm diff --git a/rssguard.pro b/rssguard.pro index 514c329a1..043d3165d 100755 --- a/rssguard.pro +++ b/rssguard.pro @@ -66,6 +66,7 @@ lessThan(QT_MAJOR_VERSION, 5)|lessThan(QT_MINOR_VERSION, 8) { APP_NAME = "RSS Guard" APP_LOW_NAME = "rssguard" +APP_REVERSE_NAME = "com.github.rssguard" APP_LOW_H_NAME = ".rssguard" APP_AUTHOR = "Martin Rotter" APP_COPYRIGHT = "(C) 2011-2017 $$APP_AUTHOR" @@ -682,10 +683,10 @@ win32 { unix:!mac:!android { target.path = $$PREFIX/bin - desktop_file.files = resources/desktop/$${TARGET}.desktop + desktop_file.files = resources/desktop/$${APP_REVERSE_NAME}.desktop desktop_file.path = $$quote($$PREFIX/share/applications/) - appdata.files = resources/desktop/$${TARGET}.appdata.xml + appdata.files = resources/desktop/$${APP_REVERSE_NAME}.appdata.xml appdata.path = $$quote($$PREFIX/share/metainfo/) desktop_icon.files = resources/graphics/$${TARGET}.png diff --git a/src/definitions/definitions.h b/src/definitions/definitions.h index 5233eaa77..b74a87752 100755 --- a/src/definitions/definitions.h +++ b/src/definitions/definitions.h @@ -228,7 +228,7 @@ #if defined(Q_OS_LINUX) #define APP_DESKTOP_SOURCE_ENTRY_FILE "rssguard.desktop.autostart" -#define APP_DESKTOP_ENTRY_FILE "rssguard.desktop" +#define APP_DESKTOP_ENTRY_FILE "com.github.rssguard.desktop" #define APP_DESKTOP_ENTRY_PATH QSL(":/desktop") #endif diff --git a/src/network-web/downloader.cpp b/src/network-web/downloader.cpp index 2a4d36d58..3d4809475 100755 --- a/src/network-web/downloader.cpp +++ b/src/network-web/downloader.cpp @@ -5,13 +5,15 @@ #include "network-web/silentnetworkaccessmanager.h" #include +#include #include Downloader::Downloader(QObject* parent) : QObject(parent), m_activeReply(nullptr), m_downloadManager(new SilentNetworkAccessManager(this)), m_timer(new QTimer(this)), m_customHeaders(QHash()), m_inputData(QByteArray()), m_inputMultipartData(nullptr), m_targetProtected(false), m_targetUsername(QString()), m_targetPassword(QString()), - m_lastOutputData(QByteArray()), m_lastOutputError(QNetworkReply::NoError), m_lastContentType(QVariant()) { + m_lastOutputData(QByteArray()), m_lastOutputMultipartData(QList()), m_lastOutputError(QNetworkReply::NoError), + m_lastContentType(QVariant()) { m_timer->setInterval(DOWNLOAD_TIMEOUT); m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &Downloader::cancel); @@ -124,7 +126,12 @@ void Downloader::finished() { runGetRequest(request); } else if (reply_operation == QNetworkAccessManager::PostOperation) { - runPostRequest(request, m_inputData); + if (m_inputMultipartData == nullptr) { + runPostRequest(request, m_inputData); + } + else { + runPostRequest(request, m_inputMultipartData); + } } else if (reply_operation == QNetworkAccessManager::PutOperation) { runPutRequest(request, m_inputData); @@ -136,8 +143,15 @@ void Downloader::finished() { else { // No redirection is indicated. Final file is obtained in our "reply" object. // Read the data into output buffer. - m_lastOutputData = reply->readAll(); + if (m_inputMultipartData == nullptr) { + m_lastOutputData = reply->readAll(); + } + else { + m_lastOutputMultipartData = decodeMultipartAnswer(reply); + } + m_lastContentType = reply->header(QNetworkRequest::ContentTypeHeader); + m_lastOutputError = reply->error(); m_activeReply->deleteLater(); m_activeReply = nullptr; @@ -158,6 +172,25 @@ void Downloader::progressInternal(qint64 bytes_received, qint64 bytes_total) { emit progress(bytes_received, bytes_total); } +QList Downloader::decodeMultipartAnswer(QNetworkReply* reply) { + QByteArray data = reply->readAll(); + + if (data.isEmpty()) { + return QList(); + } + + QString content_type = reply->header(QNetworkRequest::KnownHeaders::ContentTypeHeader).toString(); + QString boundary = content_type.mid(content_type.indexOf(QL1S("boundary=")) + 9); + QRegularExpression regex(QL1S("--") + boundary + QL1S("(--)?(\\r\\n)?")); + QStringList list = QString::fromUtf8(data).split(regex, QString::SplitBehavior::SkipEmptyParts); + + QList parts; + + parts.reserve(list.size()); + + return parts; +} + void Downloader::runDeleteRequest(const QNetworkRequest& request) { m_timer->start(); m_activeReply = m_downloadManager->deleteResource(request); @@ -229,6 +262,10 @@ QNetworkReply::NetworkError Downloader::lastOutputError() const { return m_lastOutputError; } +QList Downloader::lastOutputMultipartData() const { + return m_lastOutputMultipartData; +} + QByteArray Downloader::lastOutputData() const { return m_lastOutputData; } diff --git a/src/network-web/downloader.h b/src/network-web/downloader.h index 8c0749b09..35556953e 100755 --- a/src/network-web/downloader.h +++ b/src/network-web/downloader.h @@ -12,6 +12,7 @@ class SilentNetworkAccessManager; class QTimer; +class QHttpPart; class Downloader : public QObject { Q_OBJECT @@ -25,6 +26,7 @@ class Downloader : public QObject { // Access to last received full output data/error/content-type. QByteArray lastOutputData() const; QNetworkReply::NetworkError lastOutputError() const; + QList lastOutputMultipartData() const; QVariant lastContentType() const; public slots: @@ -65,6 +67,7 @@ class Downloader : public QObject { void progressInternal(qint64 bytes_received, qint64 bytes_total); private: + QList decodeMultipartAnswer(QNetworkReply* reply); void manipulateData(const QString& url, QNetworkAccessManager::Operation operation, const QByteArray& data, QHttpMultiPart* multipart_data, int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false, @@ -91,6 +94,8 @@ class Downloader : public QObject { // Response data. QByteArray m_lastOutputData; + QList m_lastOutputMultipartData; + QNetworkReply::NetworkError m_lastOutputError; QVariant m_lastContentType; }; diff --git a/src/network-web/networkfactory.cpp b/src/network-web/networkfactory.cpp index b693712e0..06810dab8 100755 --- a/src/network-web/networkfactory.cpp +++ b/src/network-web/networkfactory.cpp @@ -204,3 +204,34 @@ NetworkResult NetworkFactory::performNetworkOperation(const QString& url, int ti result.second = downloader.lastContentType(); return result; } + +NetworkResult NetworkFactory::performNetworkOperation(const QString& url, + int timeout, + QHttpMultiPart* input_data, + QList& output, + QNetworkAccessManager::Operation operation, + QList> additional_headers, + bool protected_contents, + const QString& username, + const QString& password) { + Downloader downloader; + QEventLoop loop; + NetworkResult result; + + // We need to quit event loop when the download finishes. + QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit); + + foreach (const auto& header, additional_headers) { + if (!header.first.isEmpty()) { + downloader.appendRawHeader(header.first, header.second); + } + } + + downloader.manipulateData(url, operation, input_data, timeout, protected_contents, username, password); + loop.exec(); + + output = downloader.lastOutputMultipartData(); + result.first = downloader.lastOutputError(); + result.second = downloader.lastContentType(); + return result; +} diff --git a/src/network-web/networkfactory.h b/src/network-web/networkfactory.h index 822738689..a97a6602b 100755 --- a/src/network-web/networkfactory.h +++ b/src/network-web/networkfactory.h @@ -4,6 +4,7 @@ #define NETWORKFACTORY_H #include +#include #include #include #include @@ -39,7 +40,8 @@ class NetworkFactory { bool protected_contents = false, const QString& username = QString(), const QString& password = QString()); - static NetworkResult performNetworkOperation(const QString& url, int timeout, const QByteArray& input_data, + static NetworkResult performNetworkOperation(const QString& url, int timeout, + const QByteArray& input_data, QByteArray& output, QNetworkAccessManager::Operation operation, QList& output, + QNetworkAccessManager::Operation operation, + QList> additional_headers = QList>(), + bool protected_contents = false, + const QString& username = QString(), + const QString& password = QString()); }; #endif // NETWORKFACTORY_H diff --git a/src/services/gmail/network/gmailnetworkfactory.cpp b/src/services/gmail/network/gmailnetworkfactory.cpp index 02f940183..76fcd6cf8 100755 --- a/src/services/gmail/network/gmailnetworkfactory.cpp +++ b/src/services/gmail/network/gmailnetworkfactory.cpp @@ -334,6 +334,8 @@ bool GmailNetworkFactory::obtainAndDecodeFullMessages(const QList& lite multi->setContentType(QHttpMultiPart::ContentType::MixedType); + QHash msgs; + foreach (const Message& msg, lite_messages) { QHttpPart part; @@ -342,27 +344,34 @@ bool GmailNetworkFactory::obtainAndDecodeFullMessages(const QList& lite part.setBody(full_msg_endpoint.toUtf8()); multi->append(part); + msgs.insert(msg.m_customId, msg); } - QEventLoop loop; - QNetworkRequest req; QString bearer = m_oauth2->bearer(); - req.setRawHeader(QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), bearer.toLocal8Bit()); - req.setUrl(QUrl(GMAIL_API_BATCH)); - auto* repl = SilentNetworkAccessManager::instance()->post(req, multi); + if (bearer.isEmpty()) { + return false; + } - connect(repl, &QNetworkReply::finished, &loop, &QEventLoop::quit); - loop.exec(); + QList> headers; + QList output; + int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); - auto resp = repl->readAll(); - auto aa = repl->error(); + headers.append(QPair(QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), + bearer.toLocal8Bit())); - multi->deleteLater(); - repl->deleteLater(); - IOFactory::writeTextFile("b.html", resp); + if (NetworkFactory::performNetworkOperation(GMAIL_API_BATCH, + timeout, + multi, + output, + QNetworkAccessManager::Operation::PostOperation, + headers).first == QNetworkReply::NetworkError::NoError) { + // We parse each part of HTTP response (it contains HTTP headers and payload with msg full data). - return false; + } + else { + return false; + } } QList GmailNetworkFactory::decodeLiteMessages(const QString& messages_json_data, const QString& stream_id,