// Copyright(c) 2017-2019 Alejandro Sirgo Rica & Contributors // Copyright(c) 2017-2019 Alejandro Sirgo Rica & Contributors // // This file is part of Flameshot. // // Flameshot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Flameshot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Flameshot. If not, see . #include "imgs3uploader.h" #include "imgs3settings.h" #include "src/core/controller.h" #include "src/utils/confighandler.h" #include "src/utils/history.h" #include "src/utils/systemnotification.h" #include "src/widgets/imagelabel.h" #include "src/widgets/loadspinner.h" #include "src/widgets/notificationwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ImgS3Uploader::ImgS3Uploader(const QPixmap& capture, QWidget* parent) : ImgUploader(capture, parent) { init(tr("Upload image to S3"), tr("Uploading Image")); } ImgS3Uploader::ImgS3Uploader(QWidget* parent) : ImgUploader(parent) { init(tr("Delete image from S3"), tr("Deleting image...")); } ImgS3Uploader::~ImgS3Uploader() { clearProxy(); } void ImgS3Uploader::init(const QString& title, const QString& label) { m_proxy = nullptr; m_NetworkAMUpload = nullptr; m_NetworkAMGetCreds = nullptr; m_NetworkAMRemove = nullptr; resultStatus = false; setWindowTitle(title); setWindowIcon(QIcon(":img/app/flameshot.svg")); } QNetworkProxy* ImgS3Uploader::proxy() { if (m_proxy == nullptr) { initProxy(); } return m_proxy; } QNetworkProxy* ImgS3Uploader::initProxy() { // get s3 settings ImgS3Settings imgS3Settings; // get proxy settings from "config.ini" file QSettings* settings = imgS3Settings.settings(); QString httpProxyHost = settings->value("HTTP_PROXY_HOST").toString(); if (httpProxyHost.length() > 0) { m_proxy = new QNetworkProxy(); if (settings->contains("HTTP_PROXY_TYPE")) { switch (settings->value("HTTP_PROXY_TYPE").toInt()) { case 0: m_proxy->setType(QNetworkProxy::DefaultProxy); break; case 1: m_proxy->setType(QNetworkProxy::Socks5Proxy); break; case 2: m_proxy->setType(QNetworkProxy::NoProxy); break; case 4: m_proxy->setType(QNetworkProxy::HttpCachingProxy); break; case 5: m_proxy->setType(QNetworkProxy::FtpCachingProxy); break; case 3: default: m_proxy->setType(QNetworkProxy::HttpProxy); break; } } m_proxy->setHostName(httpProxyHost); int nProxyPort = 3128; if (settings->contains("HTTP_PROXY_PORT")) { nProxyPort = settings->value("HTTP_PROXY_PORT").toInt(); } m_proxy->setPort(nProxyPort); if (settings->contains("HTTP_PROXY_USER")) { qDebug() << "Proxy user" << settings->value("HTTP_PROXY_PASSWORD").toString(); m_proxy->setUser(settings->value("HTTP_PROXY_USER").toString()); } if (settings->contains("HTTP_PROXY_PASSWORD")) { qDebug() << "Proxy password is not empty"; m_proxy->setPassword( settings->value("HTTP_PROXY_PASSWORD").toString()); } } else { // Get proxy settings from OS settings QNetworkProxyQuery q(QUrl(m_s3Settings.credsUrl().toUtf8())); q.setQueryType(QNetworkProxyQuery::UrlRequest); q.setProtocolTag("http"); QList proxies = QNetworkProxyFactory::systemProxyForQuery(q); if (proxies.size() > 0 && proxies[0].type() != QNetworkProxy::NoProxy) { m_proxy = new QNetworkProxy(); m_proxy->setHostName(proxies[0].hostName()); m_proxy->setPort(proxies[0].port()); m_proxy->setType(proxies[0].type()); m_proxy->setUser(proxies[0].user()); m_proxy->setPassword(proxies[0].password()); } } #ifdef QT_DEBUG if (m_proxy != nullptr) { qDebug() << "Using proxy server"; qDebug() << "proxy host:" << m_proxy->hostName(); qDebug() << "proxy port:" << m_proxy->port(); qDebug() << "proxy type:" << m_proxy->type(); qDebug() << "proxy user:" << (m_proxy->user().length() > 0 ? m_proxy->user() : "no user"); qDebug() << "proxy password:" << (m_proxy->password().length() > 0 ? "***" : "no password"); } else { qDebug() << "No proxy"; } #endif return m_proxy; } void ImgS3Uploader::clearProxy() { if (m_proxy != nullptr) { delete m_proxy; m_proxy = nullptr; } } void ImgS3Uploader::handleReplyUpload(QNetworkReply* reply) { hideSpinner(); m_storageImageName.clear(); if (reply->error() == QNetworkReply::NoError) { // save history QString imageName = imageUrl().toString(); int lastSlash = imageName.lastIndexOf("/"); if (lastSlash >= 0) { imageName = imageName.mid(lastSlash + 1); } m_storageImageName = imageName; // save image to history History history; imageName = history.packFileName( SCREENSHOT_STORAGE_TYPE_S3, m_deleteToken, imageName); history.save(pixmap(), imageName); resultStatus = true; // Copy url to clipboard if required if (ConfigHandler().copyAndCloseAfterUploadEnabled()) { QApplication::clipboard()->setText(imageUrl().toString()); SystemNotification().sendMessage(tr("URL copied to clipboard.")); Controller::getInstance()->updateRecentScreenshots(); close(); } else { onUploadOk(); } } else { QString reason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute) .toString(); setInfoLabelText(reply->errorString()); } new QShortcut(Qt::Key_Escape, this, SLOT(close())); } void ImgS3Uploader::handleReplyDeleteResource(QNetworkReply* reply) { auto replyError = reply->error(); if (replyError == QNetworkReply::NoError) { removeImagePreview(); } else { hide(); // generate error message QString message = tr("Unable to remove screenshot from the remote storage."); if (replyError == QNetworkReply::UnknownNetworkError) { message += "\n" + tr("Network error"); } else if (replyError == QNetworkReply::UnknownServerError) { message += "\n" + tr("Possibly it doesn't exist anymore"); } message += "\n\n" + reply->errorString(); message += "\n\n" + tr("Do you want to remove screenshot from local history anyway?"); if (QMessageBox::Yes == QMessageBox::question(NULL, tr("Remove screenshot from history?"), message, QMessageBox::Yes | QMessageBox::No)) { removeImagePreview(); } } close(); } void ImgS3Uploader::handleReplyGetCreds(QNetworkReply* reply) { if (reply->error() == QNetworkReply::NoError) { QJsonDocument response = QJsonDocument::fromJson(reply->readAll()); uploadToS3(response); } else { if (m_s3Settings.credsUrl().length() == 0) { setInfoLabelText( tr("S3 Creds URL is not found in your configuration file")); } else { setInfoLabelText(reply->errorString()); } } new QShortcut(Qt::Key_Escape, this, SLOT(close())); } void ImgS3Uploader::uploadToS3(QJsonDocument& response) { // set paramets from "fields" QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); // read JSON response QJsonObject json = response.object(); QString resultUrl = json["resultURL"].toString(); QJsonObject formData = json["formData"].toObject(); QString url = formData["url"].toString(); m_deleteToken = json["deleteToken"].toString(); QJsonObject fields = formData["fields"].toObject(); foreach (auto key, fields.keys()) { QString field = fields[key].toString(); QHttpPart part; part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + key + "\"")); part.setBody(field.toLatin1()); multiPart->append(part); } QHttpPart imagePart; imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"")); QByteArray byteArray; QBuffer buffer(&byteArray); buffer.open(QIODevice::WriteOnly); pixmap().save(&buffer, "PNG"); imagePart.setBody(byteArray); multiPart->append(imagePart); setImageUrl(QUrl(resultUrl)); QUrl qUrl(url); QNetworkRequest request(qUrl); // upload m_NetworkAMUpload->post(request, multiPart); } void ImgS3Uploader::deleteResource(const QString& fileName, const QString& deleteToken) { // read network settings on each call to simplify configuration management // without restarting clearProxy(); if (m_NetworkAMRemove != nullptr) { delete m_NetworkAMRemove; m_NetworkAMRemove = nullptr; } m_NetworkAMRemove = new QNetworkAccessManager(this); connect(m_NetworkAMRemove, &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleReplyDeleteResource); if (proxy() != nullptr) { m_NetworkAMRemove->setProxy(*proxy()); } QNetworkRequest request; m_storageImageName = fileName; m_deleteToken = deleteToken; request.setUrl(m_s3Settings.credsUrl().toUtf8() + fileName); request.setRawHeader("X-API-Key", m_s3Settings.xApiKey().toLatin1()); request.setRawHeader("Authorization", "Bearer " + deleteToken.toLatin1()); m_NetworkAMRemove->deleteResource(request); } void ImgS3Uploader::upload() { m_deleteToken.clear(); m_storageImageName.clear(); // read network settings on each call to simplify configuration management // without restarting init creds and upload network access managers clearProxy(); if (m_NetworkAMGetCreds != nullptr) { delete m_NetworkAMGetCreds; m_NetworkAMGetCreds = nullptr; } m_NetworkAMGetCreds = new QNetworkAccessManager(this); connect(m_NetworkAMGetCreds, &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleReplyGetCreds); if (m_NetworkAMUpload != nullptr) { delete m_NetworkAMUpload; m_NetworkAMUpload = nullptr; } m_NetworkAMUpload = new QNetworkAccessManager(this); connect(m_NetworkAMUpload, &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleReplyUpload); if (proxy() != nullptr) { m_NetworkAMGetCreds->setProxy(*proxy()); m_NetworkAMUpload->setProxy(*proxy()); } // get creads QUrl creds(m_s3Settings.credsUrl()); QNetworkRequest requestCreds(creds); if (m_s3Settings.xApiKey().length() > 0) { requestCreds.setRawHeader( QByteArray("X-API-Key"), QByteArray(m_s3Settings.xApiKey().toLocal8Bit())); } m_NetworkAMGetCreds->get(requestCreds); } void ImgS3Uploader::removeImagePreview() { // remove local file History history; QString packedFileName = history.packFileName( SCREENSHOT_STORAGE_TYPE_S3, m_deleteToken, m_storageImageName); QString fullFileName = history.path() + packedFileName; QFile file(fullFileName); if (file.exists()) { file.remove(); } m_deleteToken.clear(); m_storageImageName.clear(); resultStatus = true; }