Skip to content

Commit

Permalink
Fully decoupled Uploader from imgur stuff, moved authorization to cal…
Browse files Browse the repository at this point in the history
…lback static functions and added uploader-specific settings loaded from the main QSettings so uploaders themselves don't touch global settings.
  • Loading branch information
ckaiser committed Jul 9, 2016
1 parent fbd1e8f commit 0d9dfed
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 172 deletions.
56 changes: 54 additions & 2 deletions tools/uploader/imageuploader.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,63 @@
#include "imageuploader.h"
#include "imguruploader.h"

ImageUploader *ImageUploader::getNewUploader(const QString &name, const QVariantHash &options)
#include <QSettings>
#include "../screenshotmanager.h"

ImageUploader *ImageUploader::factory(const QString &name)
{
if (name == "imgur") {
return new ImgurUploader(options);
return new ImgurUploader(0);
}

return 0;
}

QVariantHash ImageUploader::loadSettings(const QString &uploaderType)
{
auto globalSettings = ScreenshotManager::instance()->settings();
globalSettings->beginGroup("upload/" + uploaderType);
auto keys = globalSettings->childKeys();

QVariantHash settings;

for (auto key : qAsConst(keys)) {
settings[key] = globalSettings->value(key);
}

globalSettings->endGroup();
return settings;
}

void ImageUploader::loadSettings()
{
mSettings = loadSettings(mUploaderType);
}

void ImageUploader::saveSettings(const QString &uploaderType, const QVariantHash &settings) {
auto globalSettings = ScreenshotManager::instance()->settings();
globalSettings->beginGroup("upload/" + uploaderType);

for (auto key : settings.keys()) {
globalSettings->setValue(key, settings[key]);
}

globalSettings->endGroup();
}

void ImageUploader::saveSettings()
{
saveSettings(mUploaderType, mSettings);
}

int ImageUploader::progress() const {
return mProgress;
}

void ImageUploader::setProgress(int progress)
{
if (mProgress != progress) {
mProgress = progress;
emit progressChanged(mProgress);
}
}
26 changes: 17 additions & 9 deletions tools/uploader/imageuploader.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,46 @@ class QNetworkReply;
class ImageUploader : public QObject
{
Q_OBJECT
Q_PROPERTY(int progress READ progress WRITE setProgress NOTIFY progressChanged)

public:
static ImageUploader *getNewUploader(const QString &name, const QVariantHash &options = QVariantHash());
static ImageUploader *factory(const QString &type);

enum Error {
FileError,
NetworkError,
HostError,
AuthorizationError,
CancelError,
OtherError
};
Q_ENUM(Error)

public:
inline ImageUploader(const QVariantHash &options) : QObject(0), mOptions(options), mProgress(0) {}
QVariantHash options() const { return mOptions; }
inline ImageUploader(QObject *parent = 0) : QObject(parent), mProgress(0) {}

static QVariantHash loadSettings(const QString &uploaderType);
void loadSettings();

static void saveSettings(const QString &uploaderType, const QVariantHash &settings);
void saveSettings();

public slots:
virtual void upload(const QString &fileName) = 0;
virtual void cancel() = 0;
virtual void retry() = 0;
int progress() const { return mProgress; }
void setProgress(int progress) { mProgress = progress; }
int progress() const;
void setProgress(int progress);

signals:
void uploaded(QString, QString, QString);
void error(Error, QString, QString);
void progressChange(int);
void uploaded(const QString &fileName, const QString &url, const QString &deleteHash); // TODO: Make last param generic
void error(ImageUploader::Error errorCode, const QString &errorString, const QString &fileName);
void progressChanged(int progress);

protected:
QVariantHash mOptions;
int mProgress;
QString mUploaderType;
QVariantHash mSettings;

};

Expand Down
101 changes: 93 additions & 8 deletions tools/uploader/imguruploader.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
#include "imguruploader.h"
#include "../uploader/uploader.h"

#include <QNetworkAccessManager>
#include <QtNetwork>
#include <QJsonDocument>
#include <QJsonObject>

ImgurUploader::ImgurUploader(const QVariantHash &options) : ImageUploader(options) {}
ImgurUploader::ImgurUploader(QObject *parent) : ImageUploader(parent)
{
mUploaderType = "imgur";
loadSettings();
}

const QString ImgurUploader::clientId()
{
Expand All @@ -14,6 +22,52 @@ const QString ImgurUploader::clientSecret()
return QString("0546b05d6a80b2092dcea86c57b792c9c9faebf0");
}

void ImgurUploader::authorize(const QString &pin, AuthorizationCallback callback)
{
if (pin.isEmpty()) {
callback(false);
return;
}

QByteArray parameters;
parameters.append(QString("client_id=").toUtf8());
parameters.append(QUrl::toPercentEncoding(clientId()));
parameters.append(QString("&client_secret=").toUtf8());
parameters.append(QUrl::toPercentEncoding(clientSecret()));
parameters.append(QString("&grant_type=pin").toUtf8());
parameters.append(QString("&pin=").toUtf8());
parameters.append(QUrl::toPercentEncoding(pin));

QNetworkRequest request(QUrl("https://api.imgur.com/oauth2/token"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

QNetworkReply *reply = Uploader::instance()->nam()->post(request, parameters);
authorizationReply(reply, callback);
}

void ImgurUploader::refreshAuthorization(const QString &refresh_token, AuthorizationCallback callback)
{
if (refresh_token.isEmpty()) {
callback(false);
return;
}

QByteArray parameters;
parameters.append(QString("refresh_token=").toUtf8());
parameters.append(QUrl::toPercentEncoding(refresh_token));
parameters.append(QString("&client_id=").toUtf8());
parameters.append(QUrl::toPercentEncoding(clientId()));
parameters.append(QString("&client_secret=").toUtf8());
parameters.append(QUrl::toPercentEncoding(clientSecret()));
parameters.append(QString("&grant_type=refresh_token").toUtf8());

QNetworkRequest request(QUrl("https://api.imgur.com/oauth2/token"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

QNetworkReply *reply = Uploader::instance()->nam()->post(request, parameters);
authorizationReply(reply, callback);
}

void ImgurUploader::upload(const QString &fileName)
{
QFile *file = new QFile(fileName);
Expand All @@ -29,13 +83,13 @@ void ImgurUploader::upload(const QString &fileName)

QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

if (!mOptions.value("anonymous", true).toBool()) {
request.setRawHeader("Authorization", QByteArray("Bearer ") + mOptions.value("access_token").toByteArray());
if (!mSettings.value("anonymous", true).toBool()) {
request.setRawHeader("Authorization", QByteArray("Bearer ") + mSettings.value("access_token").toByteArray());

if (!mOptions.value("album").toString().isEmpty()) {
if (!mSettings.value("album").toString().isEmpty()) {
QHttpPart albumPart;
albumPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"album\""));
albumPart.setBody(mOptions.value("album").toByteArray());
albumPart.setBody(mSettings.value("album").toByteArray());
multiPart->append(albumPart);
}
}
Expand All @@ -48,18 +102,20 @@ void ImgurUploader::upload(const QString &fileName)
file->setParent(multiPart);
multiPart->append(imagePart);

QNetworkReply *reply = mOptions.value("networkManager").value<QNetworkAccessManager *>()->post(request, multiPart);
QNetworkReply *reply = Uploader::instance()->nam()->post(request, multiPart);
reply->setProperty("fileName", fileName);
this->setProperty("fileName", fileName);

connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
connect(this , SIGNAL(cancelRequest()), reply, SLOT(abort()));
connect(this , SIGNAL(cancelRequest()), reply, SLOT(deleteLater()));

connect(reply, SIGNAL(finished()), this, SLOT(finished()));
}

void ImgurUploader::retry()
{
loadSettings();
upload(property("fileName").toString());
}

Expand All @@ -80,7 +136,15 @@ void ImgurUploader::finished()
emit error(ImageUploader::CancelError, "", fileName);
} else if (reply->error() == QNetworkReply::ContentOperationNotPermittedError ||
reply->error() == QNetworkReply::AuthenticationRequiredError) {
emit needAuthRefresh();

refreshAuthorization(mSettings["refresh_token"].toString(), [&](bool result) {
if (result) {
QTimer::singleShot(50, this, &ImgurUploader::retry);
} else {
cancel();
emit error(ImageUploader::AuthorizationError, tr("Imgur user authentication failed"), fileName);
}
});
} else {
emit error(ImageUploader::NetworkError, reply->errorString(), fileName);
}
Expand Down Expand Up @@ -109,6 +173,27 @@ void ImgurUploader::uploadProgress(qint64 bytesReceived, qint64 bytesTotal)
float b = (float) bytesReceived / bytesTotal;
int p = qRound(b * 100);
setProgress(p);
emit progressChange(p);
}

void ImgurUploader::authorizationReply(QNetworkReply *reply, AuthorizationCallback callback)
{
connect(reply, &QNetworkReply::finished, [reply, callback] {
bool authorized = false;

const QJsonObject imgurResponse = QJsonDocument::fromJson(reply->readAll()).object();

if (!imgurResponse.isEmpty() && imgurResponse.contains("access_token")) {
QVariantHash newSettings;
newSettings["access_token"] = imgurResponse.value("access_token").toString();
newSettings["refresh_token"] = imgurResponse.value("refresh_token").toString();
newSettings["account_username"] = imgurResponse.value("account_username").toString();
newSettings["expires_in"] = imgurResponse.value("expires_in").toInt();
saveSettings("imgur", newSettings);

authorized = true;
}

callback(authorized);
});
}

11 changes: 9 additions & 2 deletions tools/uploader/imguruploader.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "imageuploader.h"
#include <functional>

class ImgurUploader : public ImageUploader
{
Q_OBJECT

public:
ImgurUploader(const QVariantHash &options);
typedef std::function<void(bool)> AuthorizationCallback;

ImgurUploader(QObject *parent);
static const QString clientId();
static const QString clientSecret();
static void authorize(const QString &pin, AuthorizationCallback callback);
static void refreshAuthorization(const QString &refresh_token, AuthorizationCallback callback);

public slots:
void upload(const QString &fileName);
Expand All @@ -26,7 +31,9 @@ private slots:

signals:
void cancelRequest();
void needAuthRefresh();

private:
static void authorizationReply(QNetworkReply *reply, AuthorizationCallback callback);

};

Expand Down
Loading

0 comments on commit 0d9dfed

Please sign in to comment.