From 54812db981a24dff8e3a6e373ec1793249e9988a Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Mon, 22 Jun 2020 18:07:14 +0300 Subject: [PATCH] Add S3 cloud storage support --- flameshot.pro | 6 + src/imgs3.pri | 7 + src/tools/imgs3/imgs3uploader.cpp | 274 ++++++++++++++++++++++++++ src/tools/imgs3/imgs3uploader.h | 73 +++++++ src/tools/imgs3/imgs3uploadertool.cpp | 58 ++++++ src/tools/imgs3/imgs3uploadertool.h | 43 ++++ src/tools/toolfactory.cpp | 4 +- 7 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 src/imgs3.pri create mode 100644 src/tools/imgs3/imgs3uploader.cpp create mode 100644 src/tools/imgs3/imgs3uploader.h create mode 100644 src/tools/imgs3/imgs3uploadertool.cpp create mode 100644 src/tools/imgs3/imgs3uploadertool.h diff --git a/flameshot.pro b/flameshot.pro index 84a2e65b..e48f4dd8 100644 --- a/flameshot.pro +++ b/flameshot.pro @@ -96,6 +96,7 @@ SOURCES += src/main.cpp \ src/tools/copy/copytool.cpp \ src/tools/exit/exittool.cpp \ src/tools/imgur/imguruploadertool.cpp \ + src/tools/imgs3/imgs3uploadertool.cpp \ src/tools/line/linetool.cpp \ src/tools/marker/markertool.cpp \ src/tools/move/movetool.cpp \ @@ -113,6 +114,7 @@ SOURCES += src/main.cpp \ src/cli/commandargument.cpp \ src/utils/screenshotsaver.cpp \ src/tools/imgur/imguruploader.cpp \ + src/tools/imgs3/imgs3uploader.cpp \ src/widgets/loadspinner.cpp \ src/widgets/imagelabel.cpp \ src/widgets/notificationwidget.cpp \ @@ -170,6 +172,7 @@ HEADERS += src/widgets/capture/buttonhandler.h \ src/tools/copy/copytool.h \ src/tools/exit/exittool.h \ src/tools/imgur/imguruploadertool.h \ + src/tools/imgs3/imgs3uploadertool.h \ src/tools/line/linetool.h \ src/tools/marker/markertool.h \ src/tools/move/movetool.h \ @@ -186,6 +189,7 @@ HEADERS += src/widgets/capture/buttonhandler.h \ src/cli/commandargument.h \ src/utils/screenshotsaver.h \ src/tools/imgur/imguruploader.h \ + src/tools/imgs3/imgs3uploader.h \ src/widgets/loadspinner.h \ src/widgets/imagelabel.h \ src/widgets/notificationwidget.h \ @@ -289,3 +293,5 @@ unix:!macx { # Imgur API data include(src/imgur.pri) +# ImgS3 API data +include(src/imgs3.pri) diff --git a/src/imgs3.pri b/src/imgs3.pri new file mode 100644 index 00000000..662abfde --- /dev/null +++ b/src/imgs3.pri @@ -0,0 +1,7 @@ +# Use default ImgS3 client_id if user did not pass +# this variable to qmake +isEmpty(IMG_S3_CLIENT_ID) { + IMG_S3_CLIENT_ID = "313baf0c7b4d3f0" +} + +DEFINES += IMG_S3_CLIENT_ID=\\\"$${IMG_S3_CLIENT_ID}\\\" diff --git a/src/tools/imgs3/imgs3uploader.cpp b/src/tools/imgs3/imgs3uploader.cpp new file mode 100644 index 00000000..ee8f7a09 --- /dev/null +++ b/src/tools/imgs3/imgs3uploader.cpp @@ -0,0 +1,274 @@ +// 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 "src/utils/filenamehandler.h" +#include "src/utils/systemnotification.h" +#include "src/widgets/loadspinner.h" +#include "src/widgets/imagelabel.h" +#include "src/widgets/notificationwidget.h" +#include "src/utils/confighandler.h" +#include +#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) : + QWidget(parent), m_pixmap(capture) +{ + setWindowTitle(tr("Upload to ImgS3")); + setWindowIcon(QIcon(":img/app/flameshot.svg")); + + m_spinner = new LoadSpinner(this); + m_spinner->setColor(ConfigHandler().uiMainColorValue()); + m_spinner->start(); + + m_infoLabel = new QLabel(tr("Uploading Image")); + + m_vLayout = new QVBoxLayout(); + setLayout(m_vLayout); + m_vLayout->addWidget(m_spinner, 0, Qt::AlignHCenter); + m_vLayout->addWidget(m_infoLabel); + + m_NetworkAM = new QNetworkAccessManager(this); + connect(m_NetworkAM, &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleReply); + + m_NetworkAMCreds = new QNetworkAccessManager(this); + connect(m_NetworkAMCreds, &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleCredsReply); + + setAttribute(Qt::WA_DeleteOnClose); + + upload(); +} + +void ImgS3Uploader::handleReply(QNetworkReply *reply) { + m_spinner->deleteLater(); + if (reply->error() == QNetworkReply::NoError) { + if (ConfigHandler().copyAndCloseAfterUploadEnabled()) { + QApplication::clipboard()->setText(m_imageURL.toString()); + SystemNotification().sendMessage(QObject::tr("URL copied to clipboard.")); + close(); + } else { + onUploadOk(); + } + } else { + QString reason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString(); + qDebug() << reply->header(QNetworkRequest::ContentDispositionHeader); + qDebug() << reply->header(QNetworkRequest::ContentTypeHeader); + qDebug() << reply->readAll(); + qDebug() << reason; + m_infoLabel->setText(reply->errorString()); + } + new QShortcut(Qt::Key_Escape, this, SLOT(close())); +} + +void ImgS3Uploader::startDrag() { + QMimeData *mimeData = new QMimeData; + mimeData->setUrls(QList { m_imageURL }); + mimeData->setImageData(m_pixmap); + + QDrag *dragHandler = new QDrag(this); + dragHandler->setMimeData(mimeData); + dragHandler->setPixmap(m_pixmap.scaled(256, 256, Qt::KeepAspectRatioByExpanding, + Qt::SmoothTransformation)); + dragHandler->exec(); +} + +void ImgS3Uploader::handleCredsReply(QNetworkReply *reply){ + if (reply->error() == QNetworkReply::NoError) { + QJsonDocument response = QJsonDocument::fromJson(reply->readAll()); + uploadToS3(response); + } else { + m_infoLabel->setText(reply->errorString()); + } + new QShortcut(Qt::Key_Escape, this, SLOT(close())); +} + +void ImgS3Uploader::uploadToS3(QJsonDocument &response) { + QJsonObject json = response.object(); + QJsonObject formData = json["formData"].toObject(); + QJsonObject fields = formData["fields"].toObject(); + + QString resultURL = json["resultURL"].toString(); + + QString url = formData["url"].toString(); + + QString acl = fields["acl"].toString(); + QString contentType = fields["Content-Type"].toString(); + QString key = fields["Key"].toString(); + QString bucket = fields["bucket"].toString(); + QString xAmzAlgorithm = fields["X-Amz-Algorithm"].toString(); + QString xAmzCredential = fields["X-Amz-Credential"].toString(); + QString xAmzDate = fields["X-Amz-Date"].toString(); + QString xAmzSecurityToken = fields["X-Amz-Security-Token"].toString(); + QString policy = fields["Policy"].toString(); + QString xAmzSignature = fields["X-Amz-Signature"].toString(); + + // + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart aclPart; + aclPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"acl\"")); + aclPart.setBody(acl.toLatin1()); + multiPart->append(aclPart); + + QHttpPart contentTypePart; + contentTypePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"Content-Type\"")); + contentTypePart.setBody(contentType.toLatin1()); + multiPart->append(contentTypePart); + + QHttpPart keyPart; + keyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"Key\"")); + keyPart.setBody(key.toLatin1()); + multiPart->append(keyPart); + + QHttpPart bucketPart; + bucketPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"bucket\"")); + bucketPart.setBody(bucket.toLatin1()); + multiPart->append(bucketPart); + + QHttpPart xAmzAlgorithmPart; + xAmzAlgorithmPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"X-Amz-Algorithm\"")); + xAmzAlgorithmPart.setBody(xAmzAlgorithm.toLatin1()); + multiPart->append(xAmzAlgorithmPart); + + QHttpPart xAmzCredentialPart; + xAmzCredentialPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"X-Amz-Credential\"")); + xAmzCredentialPart.setBody(xAmzCredential.toLatin1()); + multiPart->append(xAmzCredentialPart); + + QHttpPart xAmzDatePart; + xAmzDatePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"X-Amz-Date\"")); + xAmzDatePart.setBody(xAmzDate.toLatin1()); + multiPart->append(xAmzDatePart); + + QHttpPart xAmzSecurityTokenPart; + xAmzSecurityTokenPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"X-Amz-Security-Token\"")); + xAmzSecurityTokenPart.setBody(xAmzSecurityToken.toLatin1()); + multiPart->append(xAmzSecurityTokenPart); + + QHttpPart policyPart; + policyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"Policy\"")); + policyPart.setBody(policy.toLatin1()); + multiPart->append(policyPart); + + QHttpPart xAmzSignaturePart; + xAmzSignaturePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"X-Amz-Signature\"")); + xAmzSignaturePart.setBody(xAmzSignature.toLatin1()); + multiPart->append(xAmzSignaturePart); + + + 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); + m_pixmap.save(&buffer, "PNG"); + + imagePart.setBody(byteArray); + multiPart->append(imagePart); + + m_imageURL.setUrl(resultURL); + + QUrl qUrl(url); + QNetworkRequest request(qUrl); + m_NetworkAM->post(request, multiPart); +} + +void ImgS3Uploader::upload() { + // get creads + QUrl creds("https://api.img.rnd.namecheap.net/"); + QNetworkRequest requestCreds(creds); + m_NetworkAMCreds->get(requestCreds); +} + +void ImgS3Uploader::onUploadOk() { + m_infoLabel->deleteLater(); + + m_notification = new NotificationWidget(); + m_vLayout->addWidget(m_notification); + + ImageLabel *imageLabel = new ImageLabel(); + imageLabel->setScreenshot(m_pixmap); + imageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + connect(imageLabel, &ImageLabel::dragInitiated, this, &ImgS3Uploader::startDrag); + m_vLayout->addWidget(imageLabel); + + m_hLayout = new QHBoxLayout(); + m_vLayout->addLayout(m_hLayout); + + m_copyUrlButton = new QPushButton(tr("Copy URL")); + m_openUrlButton = new QPushButton(tr("Open URL")); + m_openDeleteUrlButton = new QPushButton(tr("Delete image")); + m_toClipboardButton = new QPushButton(tr("Image to Clipboard.")); + m_hLayout->addWidget(m_copyUrlButton); + m_hLayout->addWidget(m_openUrlButton); + m_hLayout->addWidget(m_openDeleteUrlButton); + m_hLayout->addWidget(m_toClipboardButton); + + connect(m_copyUrlButton, &QPushButton::clicked, + this, &ImgS3Uploader::copyURL); + connect(m_openUrlButton, &QPushButton::clicked, + this, &ImgS3Uploader::openURL); + connect(m_openDeleteUrlButton, &QPushButton::clicked, + this, &ImgS3Uploader::openDeleteURL); + connect(m_toClipboardButton, &QPushButton::clicked, + this, &ImgS3Uploader::copyImage); +} + +void ImgS3Uploader::openURL() { + bool successful = QDesktopServices::openUrl(m_imageURL); + if (!successful) { + m_notification->showMessage(tr("Unable to open the URL.")); + } +} + +void ImgS3Uploader::copyURL() { + QApplication::clipboard()->setText(m_imageURL.toString()); + m_notification->showMessage(tr("URL copied to clipboard.")); +} + +void ImgS3Uploader::openDeleteURL() +{ + bool successful = QDesktopServices::openUrl(m_deleteImageURL); + if (!successful) { + m_notification->showMessage(tr("Unable to open the URL.")); + } +} + +void ImgS3Uploader::copyImage() { + QApplication::clipboard()->setPixmap(m_pixmap); + m_notification->showMessage(tr("Screenshot copied to clipboard.")); +} diff --git a/src/tools/imgs3/imgs3uploader.h b/src/tools/imgs3/imgs3uploader.h new file mode 100644 index 00000000..4be2f95a --- /dev/null +++ b/src/tools/imgs3/imgs3uploader.h @@ -0,0 +1,73 @@ +// 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 . + +#pragma once + +#include +#include + +class QNetworkReply; +class QNetworkAccessManager; +class QHBoxLayout; +class QVBoxLayout; +class QLabel; +class LoadSpinner; +class QPushButton; +class QUrl; +class NotificationWidget; + +class ImgS3Uploader : public QWidget { + Q_OBJECT +public: + explicit ImgS3Uploader(const QPixmap &capture, QWidget *parent = nullptr); + +private slots: + void handleReply(QNetworkReply *reply); + void handleCredsReply(QNetworkReply *reply); + void startDrag(); + + void openURL(); + void copyURL(); + void openDeleteURL(); + void copyImage(); + +private: + void uploadToS3(QJsonDocument &response); + +private: + QString m_hostName; + QPixmap m_pixmap; + QNetworkAccessManager *m_NetworkAM; + QNetworkAccessManager *m_NetworkAMCreds; + + QVBoxLayout *m_vLayout; + QHBoxLayout *m_hLayout; + // loading + QLabel *m_infoLabel; + LoadSpinner *m_spinner; + // uploaded + QPushButton *m_openUrlButton; + QPushButton *m_openDeleteUrlButton; + QPushButton *m_copyUrlButton; + QPushButton *m_toClipboardButton; + QUrl m_imageURL; + QUrl m_deleteImageURL; + NotificationWidget *m_notification; + + void upload(); + void onUploadOk(); +}; diff --git a/src/tools/imgs3/imgs3uploadertool.cpp b/src/tools/imgs3/imgs3uploadertool.cpp new file mode 100644 index 00000000..e803df5e --- /dev/null +++ b/src/tools/imgs3/imgs3uploadertool.cpp @@ -0,0 +1,58 @@ +// 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 "imgs3uploadertool.h" +#include "imgs3uploader.h" +#include + +ImgS3UploaderTool::ImgS3UploaderTool(QObject *parent) : AbstractActionTool(parent) { + +} + +bool ImgS3UploaderTool::closeOnButtonPressed() const { + return true; +} + +QIcon ImgS3UploaderTool::icon(const QColor &background, bool inEditor) const { + Q_UNUSED(inEditor); + return QIcon(iconPath(background) + "cloud-upload.svg"); +} +QString ImgS3UploaderTool::name() const { + return tr("Image Uploader"); +} + +QString ImgS3UploaderTool::nameID() { + return QLatin1String(""); +} + +QString ImgS3UploaderTool::description() const { + return tr("Upload the selection to ImgS3"); +} + +QWidget* ImgS3UploaderTool::widget() { + return new ImgS3Uploader(capture); +} + +CaptureTool* ImgS3UploaderTool::copy(QObject *parent) { + return new ImgS3UploaderTool(parent); +} + +void ImgS3UploaderTool::pressed(const CaptureContext &context) { + capture = context.selectedScreenshotArea(); + emit requestAction(REQ_CAPTURE_DONE_OK); + emit requestAction(REQ_ADD_EXTERNAL_WIDGETS); +} diff --git a/src/tools/imgs3/imgs3uploadertool.h b/src/tools/imgs3/imgs3uploadertool.h new file mode 100644 index 00000000..43bc4d1f --- /dev/null +++ b/src/tools/imgs3/imgs3uploadertool.h @@ -0,0 +1,43 @@ +// 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 . + +#pragma once + +#include "src/tools/abstractactiontool.h" + +class ImgS3UploaderTool : public AbstractActionTool { + Q_OBJECT +public: + explicit ImgS3UploaderTool(QObject *parent = nullptr); + + bool closeOnButtonPressed() const; + + QIcon icon(const QColor &background, bool inEditor) const override; + QString name() const override; + static QString nameID(); + QString description() const override; + + QWidget* widget() override; + + CaptureTool* copy(QObject *parent = nullptr) override; + +public slots: + void pressed(const CaptureContext &context) override; + +private: + QPixmap capture; +}; diff --git a/src/tools/toolfactory.cpp b/src/tools/toolfactory.cpp index 23c5032b..f76d7ca8 100644 --- a/src/tools/toolfactory.cpp +++ b/src/tools/toolfactory.cpp @@ -21,6 +21,7 @@ #include "copy/copytool.h" #include "exit/exittool.h" #include "imgur/imguruploadertool.h" +#include "imgs3/imgs3uploadertool.h" #include "line/linetool.h" #include "marker/markertool.h" #include "move/movetool.h" @@ -59,7 +60,8 @@ CaptureTool* ToolFactory::CreateTool( tool = new ExitTool(parent); break; case CaptureButton::TYPE_IMAGEUPLOADER: - tool = new ImgurUploaderTool(parent); +// tool = new ImgurUploaderTool(parent); + tool = new ImgS3UploaderTool(parent); break; case CaptureButton::TYPE_DRAWER: tool = new LineTool(parent);