Add 3rdparty oauth 2 library

This commit is contained in:
Jan Kaluza 2015-11-19 14:13:46 +01:00
parent 096324974a
commit 7291e4c1ca
53 changed files with 4060 additions and 0 deletions

1
3rdparty/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1 @@
ADD_SUBDIRECTORY(o2)

2
3rdparty/o2/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*CMakeLists.txt.user
_build/

7
3rdparty/o2/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 2.8.11)
add_subdirectory(src)
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif(BUILD_EXAMPLES)

23
3rdparty/o2/LICENSE vendored Normal file
View file

@ -0,0 +1,23 @@
Copyright (c) 2012, Akos Polster
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

192
3rdparty/o2/README.md vendored Normal file
View file

@ -0,0 +1,192 @@
# OAuth 1.0 and 2.0 for Qt
This library encapsulates the OAuth 1.0 and 2.0 client authentication flows, and the sending of authenticated HTTP requests.
The primary target is Qt Quick applications on mobile devices.
## Contents
Class | Header | Purpose
:-- | :-- | :--
O1 | o1.h | Generic OAuth 1.0 authentication
O1RequestParameter | o1.h | An extra request parameter participating in request signing
O1Dropbox | o1dropbox.h | Dropbox OAuth specializations
O1Flickr | o1flickr.h | Flickr OAuth specializations
O1Requestor | o1requestor.h | Makes authenticated OAuth 1.0 requests: GET, POST or PUT, handles timeouts
O1Twitter | o1twitter.h | Twitter OAuth specializations
OXTwitter | oxtwitter.h | Twitter XAuth specialization
O2 | o2.h | Generic OAuth 2.0 authentication
O2Facebook | o2facebook.h | Facebook OAuth specialization
O2Gft | o2gft.h | Google Fusion Tables OAuth specialization
O2Reply | o2reply.h | A network request/reply that can time out
O2ReplyServer | o2replyserver.h | HTTP server to process authentication responses
O2Requestor | o2requestor.h | Makes authenticated OAuth 2.0 requests (GET, POST or PUT), handles timeouts and token expiry
O2Skydrive | o2skydrive.h | Skydrive OAuth specialization
SimpleCrypt | simplecrypt.h | Simple encryption and decryption by Andre Somers
O2AbstractStore | o2abstractstore.h | Base class for implemnting persistent stores
O2SettingsStore | o2settingsstore.h | A QSettings based persistent store for writing OAuth tokens
## Installation
Clone the Github repository, then add all files to your Qt project.
## Basic Usage
This example assumes a hypothetical Twitter client that will post tweets. Twitter is using OAuth 1.0.
### Setup
Include the required header files, and have some member variables that will be used for authentication and sending requests:
#include "o1twitter.h"
#include "o1requestor.h"
O1Twitter *o1;
### Initialization
Instantiate one of the authenticator classes, like O1Twitter, set your application ID and application secret, and install the signal handlers:
o1 = new O1Twitter(this);
o1->setClientId(MY_CLIENT_ID);
o1->setClientSecret(MY_CLIENT_SECRET);
connect(o1, SIGNAL(linkedChanged()), this, SLOT(onLinkedChanged()));
connect(o1, SIGNAL(linkingFailed()), this, SLOT(onLinkingFailed()));
connect(o1, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded()));
connect(o1, SIGNAL(openBrowser(QUrl)), this, SLOT(onOpenBrowser(QUrl)));
connect(o1, SIGNAL(closeBrowser()), this, SLOT(onCloseBrowser()));
**Note:** For browserless Twitter authentication, you can use the OXTwitter specialized class which can do Twitter XAuth. You will need to additionally provide your Twitter login credentials (username & password) before calling *link()*.
### Handling Signals
O2 is an asynchronous library. It will send signals at various stages of authentication and request processing.
To handle these signals, implement the following slots in your code:
void onLinkedChanged() {
// Linking (login) state has changed.
// Use o1->linked() to get the actual state
}
void onLinkingFailed() {
// Login has failed
}
void onLinkingSucceeded() {
// Login has succeeded
}
void onOpenBrowser(const QUrl *url) {
// Open a web browser or a web view with the given URL.
// The user will interact with this browser window to
// enter login name, password, and authorize your application
// to access the Twitter account
}
void onCloseBrowser() {
// Close the browser window opened in openBrowser()
}
### Logging In
To log in (or, to be more accurate, to link your application to the OAuth service), call the link() method:
o1->link();
This initiates the authentication sequence. Your signal handlers above will be called at various stages. Lastly, if linking succeeds, onLinkingSucceeded() will be called.
### Logging Out
To log out, call the unlink() method:
o1->unlink();
Logging out always succeeds, and requires no user interaction.
### Sending Authenticated Requests
Once linked, you can start sending authenticated requests to the service. We start with a simple example of sending a text-only tweet or as it's known in Twitter docs, a 'status update'.
First we need a Qt network manager and an O1 requestor object:
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
O1Requestor* requestor = new O1Requestor(manager, o1, this);
Next, create parameters for posting the update:
QByteArray paramName("status");
QByteArray tweetText("My first tweet!");
QList<O1RequestParameter> reqParams = QList<O1RequestParameter>();
reqParams << O1RequestParameter(paramName, tweetText);
QByteArray postData = O1::createQueryParams(reqParams);
// Using Twitter's REST API ver 1.1
QUrl url = QUrl("https://api.twitter.com/1.1/statuses/update.json");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM);
Finally we authenticate and send the request using the O1 requestor object:
QNetworkReply *reply = requestor->post(request, reqParams, postData);
Continuing with the example, we will now send a tweet containing an image as well as a message.
We create an HTTP request containing the image and the message, in the format specified by Twitter:
QString imagePath("/tmp/image.jpg");
QString message("My tweet with an image!");
QFileInfo fileInfo(imagePath);
QFile file(imagePath);
file.open(QIODevice::ReadOnly);
QString boundary("7d44e178b0439");
QByteArray data(QString("--" + boundary + "\r\n").toAscii());
data += "Content-Disposition: form-data; name=\"media[]\"; filename=\"" + fileInfo.fileName() + "\"\r\n";
data += "Content-Transfer-Encoding: binary\r\n";
data += "Content-Type: application/octet-stream\r\n\r\n";
data += file.readAll();
file.close();
data += QString("\r\n--") + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"status\"\r\n";
data += "Content-Transfer-Encoding: binary\r\n";
data += "Content-Type: text/plain; charset=utf-8\r\n\r\n";
data += message.toUtf8();
data += QString("\r\n--") + boundary + "--\r\n";
QNetworkRequest request;
// Using Twitter's REST API ver 1.1
static const char *uploadUrl = "https://api.twitter.com/1.1/statuses/update_with_media.json";
request.setUrl(QUrl(uploadUrl));
request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=" + boundary);
request.setHeader(QNetworkRequest::ContentLengthHeader, data.length());
QNetworkReply *reply = requestor->post(request, QList<O1RequestParameter>(), data);
That's it. Tweets using the O2 library!
### Storing OAuth Tokens
O2 provides simple storage classes for writing OAuth tokens in a peristent location. Currently, a QSettings based backing store **O2SettingsStore** is provided in O2. O2SettingsStore keeps all token values in an encrypted form. You have to specify the encryption key to use while constructing the object:
O2SettingsStore settings = new O2SettingsStore("myencryptionkey");
// Set the store before starting OAuth, i.e before calling link()
o1->setStore(settings);
...
You can also create it with your customized QSettings object. O2SettingsStore will then use that QSettings object for storing the tokens:
O2SettingsStore settings = new O2SettingsStore(mySettingsObject, "myencryptionkey");
Once set, O2SettingsStore takes ownership of the QSettings object.
**Note:** If you do not specify a storage object to use, O2 will create one by default (which QSettings based), and use it. In such a case, a default encryption key is used for encrypting the data.
### Extra OAuth Tokens
Some OAuth service providers provide additional information in the access token response. Eg: Twitter returns 2 additional tokens in it's access token response - *screen_name* and *user_id*.
O2 provides all such tokens via the property - *extraTokens*. You can query this property after a successful OAuth exchange, i.e after the *linkingSucceeded()* signal has been emitted.

37
3rdparty/o2/acknowledgements.md vendored Normal file
View file

@ -0,0 +1,37 @@
# SimpleCrypt
Copyright (c) 2011, Andre Somers
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Rathenau Instituut, Andre Somers nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Contributions by Mandeep Sandhu <mandeepsandhu.chd@gmail.com>
"Hi Akos,
I'm writing this mail to confirm that my contributions to the O2 library, available here https://github.com/pipacs/o2, can be freely distributed according to the project's license (as shown in the LICENSE file).
Regards,
-mandeep"

6
3rdparty/o2/examples/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
*.pro.user
build*
*.o
*.moc
moc_*
Makefile

9
3rdparty/o2/examples/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 2.8.11)
if(WITH_FACEBOOK)
add_subdirectory(facebookdemo)
endif(WITH_FACEBOOK)
if(WITH_TWITTER)
add_subdirectory(twitterdemo)
endif(WITH_TWITTER)

View file

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 2.8.11)
project( fbexample )
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(QT_USE_QTNETWORK true)
set(QT_USE_QTSCRIPT true)
find_package(Qt4 REQUIRED)
include( ${QT_USE_FILE} )
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "../../src" )
set(fb_SRCS
main.cpp
fbdemo.cpp
)
add_definitions(${QT4_DEFINITIONS})
add_executable( fbexample ${fb_SRCS} )
target_link_libraries( fbexample ${QT_LIBRARIES} o2 )

View file

@ -0,0 +1,16 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4) {
QT += widgets
}
include(../../src/src.pri)
TARGET = facebookdemo
TEMPLATE = app
SOURCES += main.cpp \
fbdemo.cpp
HEADERS += \
fbdemo.h

View file

@ -0,0 +1,132 @@
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDesktopServices>
#include <QMetaEnum>
#include <QDebug>
#include "fbdemo.h"
#include "o2globals.h"
#include "o2settingsstore.h"
const char FB_APP_KEY[] = "227896037359072";
const char FB_APP_SECRET[] = "3d35b063872579cf7213e09e76b65ceb";
const char FB_REQUEST_URL[] = "https://www.facebook.com/dialog/oauth";
const char FB_TOKEN_URL[] = "https://graph.facebook.com/oauth/access_token";
const char FB_DEBUG_TOKEN[] = "https://graph.facebook.com/me?fields=id&access_token=%1";
const int localPort = 8888;
#define QENUM_NAME(o,e,v) \
(o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).\
valueToKey((v)))
#define GRANTFLOW_STR(v) QString(QENUM_NAME(O2, \
GrantFlow, v))
FBDemo::FBDemo(QObject *parent) :
QObject(parent) {
o2Facebook_ = new O2Facebook(this);
o2Facebook_->setClientId(FB_APP_KEY);
o2Facebook_->setClientSecret(FB_APP_SECRET);
o2Facebook_->setLocalPort(localPort);
o2Facebook_->setRequestUrl(FB_REQUEST_URL);
o2Facebook_->setTokenUrl(FB_TOKEN_URL);
// Create a store object for writing the received tokens
O2SettingsStore *store = new O2SettingsStore(O2_ENCRYPTION_KEY);
store->setGroupKey("facebook");
o2Facebook_->setStore(store);
connect(o2Facebook_, SIGNAL(linkedChanged()),
this, SLOT(onLinkedChanged()));
connect(o2Facebook_, SIGNAL(linkingFailed()),
this, SIGNAL(linkingFailed()));
connect(o2Facebook_, SIGNAL(linkingSucceeded()),
this, SLOT(onLinkingSucceeded()));
connect(o2Facebook_, SIGNAL(openBrowser(QUrl)),
this, SLOT(onOpenBrowser(QUrl)));
connect(o2Facebook_, SIGNAL(closeBrowser()),
this, SLOT(onCloseBrowser()));
}
void FBDemo::doOAuth(O2::GrantFlow grantFlowType) {
qDebug() << "Starting OAuth 2 with grant flow type"
<< GRANTFLOW_STR(grantFlowType) << "...";
o2Facebook_->setGrantFlow(grantFlowType);
o2Facebook_->link();
}
void FBDemo::validateToken() {
if (!o2Facebook_->linked()) {
qWarning() << "ERROR: Application is not linked!";
emit linkingFailed();
return;
}
QString accessToken = o2Facebook_->token();
QString debugUrlStr = QString(FB_DEBUG_TOKEN).arg(accessToken);
QNetworkRequest request = QNetworkRequest(QUrl(debugUrlStr));
QNetworkAccessManager *mgr = new QNetworkAccessManager(this);
QNetworkReply *reply = mgr->get(request);
connect(reply, SIGNAL(finished()), this, SLOT(onFinished()));
qDebug() << "Validating user token. Please wait...";
}
void FBDemo::onOpenBrowser(const QUrl &url) {
QDesktopServices::openUrl(url);
}
void FBDemo::onCloseBrowser() {
}
void FBDemo::onLinkedChanged() {
qDebug() << "Link changed!";
}
void FBDemo::onLinkingSucceeded() {
O2Facebook* o1t = qobject_cast<O2Facebook *>(sender());
QVariantMap extraTokens = o1t->extraTokens();
if (!extraTokens.isEmpty()) {
emit extraTokensReady(extraTokens);
qDebug() << "Extra tokens in response:";
foreach (QString key, extraTokens.keys()) {
qDebug() << "\t" << key << ":" << extraTokens.value(key).toString();
}
}
emit linkingSucceeded();
}
void FBDemo::onFinished() {
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if (!reply) {
qWarning() << "NULL reply!";
emit linkingFailed();
return;
}
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "Reply error:" << reply->error();
qWarning() << "Reason:" << reply->errorString();
emit linkingFailed();
return;
}
QByteArray replyData = reply->readAll();
bool valid = !replyData.contains("error");
if (valid) {
qDebug() << "Token is valid";
emit linkingSucceeded();
} else {
qDebug() << "Token is invalid";
emit linkingFailed();
}
}

View file

@ -0,0 +1,35 @@
#ifndef FBDEMO_H
#define FBDEMO_H
#include <QObject>
#include "o2facebook.h"
class FBDemo : public QObject
{
Q_OBJECT
public:
explicit FBDemo(QObject *parent = 0);
signals:
void extraTokensReady(const QVariantMap &extraTokens);
void linkingFailed();
void linkingSucceeded();
public slots:
void doOAuth(O2::GrantFlow grantFlowType);
void validateToken();
private slots:
void onLinkedChanged();
void onLinkingSucceeded();
void onOpenBrowser(const QUrl &url);
void onCloseBrowser();
void onFinished();
private:
O2Facebook *o2Facebook_;
};
#endif // FBDEMO_H

View file

@ -0,0 +1,83 @@
#include <QApplication>
#include <QStringList>
#include <QTimer>
#include <QDebug>
#include "fbdemo.h"
const char OPT_OAUTH_CODE[] = "-o";
const char OPT_VALIDATE_TOKEN[] = "-v";
const char USAGE[] = "\n"
"Usage: facebookdemo [OPTION]...\n"
"Get OAuth2 access tokens from Facebook's OAuth service\n"
"\nOptions:\n"
" %1\t\tLink with Facebook OAuth2 service using Authorization Code\n"
" %2\t\tValidate Access Token\n";
class Helper : public QObject
{
Q_OBJECT
public:
Helper() : QObject(), fbdemo_(this), waitForMsg_(false), msg_(QString()) {}
public slots:
void processArgs() {
QStringList argList = qApp->arguments();
QByteArray help = QString(USAGE).arg(OPT_OAUTH_CODE,
OPT_VALIDATE_TOKEN).toLatin1();
const char* helpText = help.constData();
connect(&fbdemo_, SIGNAL(linkingFailed()),
this, SLOT(onLinkingFailed()));
connect(&fbdemo_, SIGNAL(linkingSucceeded()),
this, SLOT(onLinkingSucceeded()));
if (argList.contains(OPT_OAUTH_CODE)) {
// Start OAuth
fbdemo_.doOAuth(O2::GrantFlowAuthorizationCode);
} else if (argList.contains(OPT_VALIDATE_TOKEN)) {
fbdemo_.validateToken();
} else {
qDebug() << helpText;
qApp->exit(1);
}
}
void onLinkingFailed() {
qDebug() << "Linking failed!";
qApp->exit(1);
}
void onLinkingSucceeded() {
qDebug() << "Linking succeeded!";
if (waitForMsg_) {
//postStatusUpdate(msg_);
} else {
qApp->quit();
}
}
private:
FBDemo fbdemo_;
bool waitForMsg_;
QString msg_;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Helper helper;
QTimer::singleShot(0, &helper, SLOT(processArgs()));
return a.exec();
}
#include "main.moc"

View file

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 2.8.11)
project( twitterexample )
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(QT_USE_QTNETWORK true)
set(QT_USE_QTSCRIPT true)
find_package(Qt4 REQUIRED)
include( ${QT_USE_FILE} )
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "../../src" )
set(fb_SRCS
main.cpp
tweeter.cpp
)
add_definitions(${QT4_DEFINITIONS})
add_executable( twitterexample ${fb_SRCS} )
target_link_libraries( twitterexample ${QT_LIBRARIES} o2 )

View file

@ -0,0 +1,122 @@
#include <QApplication>
#include <QStringList>
#include <QTimer>
#include <QDebug>
#include "tweeter.h"
const char OPT_OAUTH[] = "-o";
const char OPT_XAUTH[] = "-x";
const char OPT_USERNAME[] = "-u";
const char OPT_PASSWORD[] = "-p";
const char OPT_STATUS[] = "-m";
const char USAGE[] = "\n"
"Usage: tweetdemo [OPTION]...\n"
"Get OAuth access tokens from Twitter's OAuth service and "
"(optionally) post a status update on a user's timeline\n"
"\nOptions:\n"
" %1\t\tLink with Twitter OAuth service, i.e get access tokens\n"
" %2\t\tLink with Twitter XAuth service, i.e get access tokens using the XAuth protocol\n"
" %3 <username>\tTwitter username to be used while using XAuth (-x option)\n"
" %4 <password>\tTwitter password to be used while using XAuth (-x option)\n"
" %5\t\tStatus update message, enclosed in double quotes\n";
class Helper : public QObject
{
Q_OBJECT
public:
Helper() : QObject(), tweeter_(this), waitForMsg_(false), msg_(QString()) {}
public slots:
void processArgs() {
QStringList argList = qApp->arguments();
QByteArray help = QString(USAGE).arg(OPT_OAUTH,
OPT_XAUTH,
OPT_USERNAME,
OPT_PASSWORD,
OPT_STATUS).toLatin1();
const char* helpText = help.constData();
connect(&tweeter_, SIGNAL(linkingFailed()),
this, SLOT(onLinkingFailed()));
connect(&tweeter_, SIGNAL(linkingSucceeded()),
this, SLOT(onLinkingSucceeded()));
if (argList.contains(OPT_OAUTH)) {
if (argList.contains(OPT_STATUS)) {
waitForMsg_ = true;
msg_ = argList.at(argList.indexOf(OPT_STATUS) + 1);
}
// Start OAuth
tweeter_.doOAuth();
} else if (argList.contains(OPT_XAUTH)) {
if (!(argList.contains(OPT_USERNAME) && argList.contains(OPT_PASSWORD))) {
qDebug() << "\nError: Username or Password missing!";
qDebug() << helpText;
qApp->exit(1);
}
QString username = argList.at(argList.indexOf(OPT_USERNAME) + 1);
QString password = argList.at(argList.indexOf(OPT_PASSWORD) + 1);
if (argList.contains(OPT_STATUS)) {
waitForMsg_ = true;
msg_ = argList.at(argList.indexOf(OPT_STATUS) + 1);
}
// Start XAuth
tweeter_.doXAuth(username, password);
} else if (argList.contains(OPT_STATUS)) {
QString statusMessage = argList.at(argList.indexOf(OPT_STATUS) + 1);
postStatusUpdate(statusMessage);
} else {
qDebug() << helpText;
qApp->exit(1);
}
}
void onLinkingFailed() {
qDebug() << "Linking failed!";
qApp->exit(1);
}
void onLinkingSucceeded() {
qDebug() << "Linking succeeded!";
if (waitForMsg_) {
postStatusUpdate(msg_);
} else {
qApp->quit();
}
}
private slots:
void postStatusUpdate(const QString& msg) {
connect(&tweeter_, SIGNAL(statusPosted()),
qApp, SLOT(quit()));
tweeter_.postStatusUpdate(msg);
}
private:
Tweeter tweeter_;
bool waitForMsg_;
QString msg_;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Helper helper;
QTimer::singleShot(0, &helper, SLOT(processArgs()));
return a.exec();
}
#include "main.moc"

View file

@ -0,0 +1,144 @@
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QDesktopServices>
#include <QDebug>
#include "tweeter.h"
#include "o2globals.h"
#include "o1requestor.h"
#include "o2settingsstore.h"
const char O2_CONSUMER_KEY[] = "2vHeyIxjywIadjEhvbDpg";
const char O2_CONSUMER_SECRET[] = "Xfwe195Kp3ZpcCKgkYs7RKfugTm8EfpLkQvsKfX2vvs";
const int localPort = 8888;
Tweeter::Tweeter(QObject *parent) :
QObject(parent) {
}
void Tweeter::doOAuth() {
o1Twitter_ = new O1Twitter(this);
o1Twitter_->setClientId(O2_CONSUMER_KEY);
o1Twitter_->setClientSecret(O2_CONSUMER_SECRET);
o1Twitter_->setLocalPort(localPort);
// Create a store object for writing the received tokens
O2SettingsStore *store = new O2SettingsStore(O2_ENCRYPTION_KEY);
store->setGroupKey("twitter");
o1Twitter_->setStore(store);
// Connect signals
connect(o1Twitter_, SIGNAL(linkedChanged()),
this, SLOT(onLinkedChanged()));
connect(o1Twitter_, SIGNAL(linkingFailed()),
this, SIGNAL(linkingFailed()));
connect(o1Twitter_, SIGNAL(linkingSucceeded()),
this, SLOT(onLinkingSucceeded()));
connect(o1Twitter_, SIGNAL(openBrowser(QUrl)),
this, SLOT(onOpenBrowser(QUrl)));
connect(o1Twitter_, SIGNAL(closeBrowser()),
this, SLOT(onCloseBrowser()));
qDebug() << "Starting OAuth...";
o1Twitter_->link();
}
void Tweeter::doXAuth(const QString &username, const QString &password) {
oxTwitter_ = new OXTwitter(this);
oxTwitter_->setClientId(O2_CONSUMER_KEY);
oxTwitter_->setClientSecret(O2_CONSUMER_SECRET);
oxTwitter_->setLocalPort(localPort);
oxTwitter_->setUsername(username);
oxTwitter_->setPassword(password);
// Create a store object for writing the received tokens
O2SettingsStore *store = new O2SettingsStore(O2_ENCRYPTION_KEY);
store->setGroupKey("twitter");
oxTwitter_->setStore(store);
connect(oxTwitter_, SIGNAL(linkedChanged()),
this, SLOT(onLinkedChanged()));
connect(oxTwitter_, SIGNAL(linkingFailed()),
this, SIGNAL(linkingFailed()));
connect(oxTwitter_, SIGNAL(linkingSucceeded()),
this, SLOT(onLinkingSucceeded()));
connect(oxTwitter_, SIGNAL(openBrowser(QUrl)),
this, SLOT(onOpenBrowser(QUrl)));
connect(oxTwitter_, SIGNAL(closeBrowser()),
this, SLOT(onCloseBrowser()));
qDebug() << "Starting XAuth...";
qDebug() << "Username:" << username << "Password:" << password;
oxTwitter_->link();
}
void Tweeter::postStatusUpdate(const QString &message) {
if (!o1Twitter_->linked()) {
qWarning() << "Application is not linked to Twitter!";
emit statusPosted();
return;
}
qDebug() << "Status update message:" << message.toLatin1().constData();
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
O1Twitter* o1 = o1Twitter_;
O1Requestor* requestor = new O1Requestor(manager, o1, this);
QByteArray paramName("status");
QList<O1RequestParameter> reqParams = QList<O1RequestParameter>();
reqParams << O1RequestParameter(paramName, message.toLatin1());
QByteArray postData = O1::createQueryParams(reqParams);
QUrl url = QUrl("https://api.twitter.com/1.1/statuses/update.json");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM);
QNetworkReply *reply = requestor->post(request, reqParams, postData);
connect(reply, SIGNAL(finished()), this, SLOT(tweetReplyDone()));
}
void Tweeter::onOpenBrowser(const QUrl &url) {
qDebug() << "Opening browser with url" << url.toString();
QDesktopServices::openUrl(url);
}
void Tweeter::onCloseBrowser() {
}
void Tweeter::onLinkedChanged() {
qDebug() << "Link changed!";
}
void Tweeter::onLinkingSucceeded() {
O1Twitter* o1t = qobject_cast<O1Twitter *>(sender());
QVariantMap extraTokens = o1t->extraTokens();
if (!extraTokens.isEmpty()) {
emit extraTokensReady(extraTokens);
qDebug() << "Extra tokens in response:";
foreach (QString key, extraTokens.keys()) {
qDebug() << "\t" << key << ":" << extraTokens.value(key).toString();
}
}
emit linkingSucceeded();
}
void Tweeter::tweetReplyDone()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "ERROR:" << reply->errorString();
qDebug() << "content:" << reply->readAll();
} else {
qDebug() << "Tweet posted sucessfully!";
}
emit statusPosted();
}

View file

@ -0,0 +1,41 @@
#ifndef TWEETER_H
#define TWEETER_H
#include <QObject>
#include <QString>
#include <QUrl>
#include <QVariantMap>
#include "o1twitter.h"
#include "oxtwitter.h"
class Tweeter : public QObject
{
Q_OBJECT
public:
explicit Tweeter(QObject *parent = 0);
signals:
void extraTokensReady(const QVariantMap &extraTokens);
void linkingFailed();
void linkingSucceeded();
void statusPosted();
public slots:
void doOAuth();
void doXAuth(const QString &username, const QString &password);
void postStatusUpdate(const QString &message);
private slots:
void onLinkedChanged();
void onLinkingSucceeded();
void onOpenBrowser(const QUrl &url);
void onCloseBrowser();
void tweetReplyDone();
private:
O1Twitter* o1Twitter_;
OXTwitter* oxTwitter_;
};
#endif // TWEETER_H

View file

@ -0,0 +1,14 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
include(../../src/src.pri)
TARGET = twitterdemo
TEMPLATE = app
SOURCES += main.cpp \
tweeter.cpp
HEADERS += \
tweeter.h

98
3rdparty/o2/src/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,98 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
if(WITH_QT5)
find_package(Qt5 COMPONENTS Core Network Script REQUIRED)
else(WITH_QT5)
set(QT_USE_QTNETWORK true)
set(QT_USE_QTSCRIPT true)
find_package(Qt4 REQUIRED)
endif(WITH_QT5)
#find_package(QJson REQUIRED)
if (NOT WITH_QT5)
include( ${QT_USE_FILE} )
endif(NOT WITH_QT5)
set( o2_SRCS
o2.cpp
o2reply.cpp
o2replyserver.cpp
o2requestor.cpp
simplecrypt.cpp
o2settingsstore.cpp
o2abstractstore.h
)
if(WITH_TWITTER OR WITH_DROPBOX OR WITH_FLICKR)
set( o2_SRCS
${o2_SRCS}
o1.cpp
o1requestor.cpp
)
endif(WITH_TWITTER OR WITH_DROPBOX OR WITH_FLICKR)
if(WITH_TWITTER)
set( o2_SRCS
${o2_SRCS}
o1twitter.h
oxtwitter.cpp
)
endif(WITH_TWITTER)
if(WITH_DROPBOX)
set( o2_SRCS
${o2_SRCS}
o1dropbox.h
)
endif(WITH_DROPBOX)
if(WITH_GOOGLE)
set( o2_SRCS
${o2_SRCS}
o2gft.cpp
)
endif(WITH_GOOGLE)
if(WITH_FACEBOOK)
set( o2_SRCS
${o2_SRCS}
o2facebook.cpp
)
endif(WITH_FACEBOOK)
if(WITH_SKYDRIVE)
set( o2_SRCS
${o2_SRCS}
o2skydrive.cpp
)
endif(WITH_SKYDRIVE)
if(WITH_FLICKR)
set( o2_SRCS
${o2_SRCS}
o1flickr.h
)
endif(WITH_FLICKR)
if(WITH_HUBIC)
set( o2_SRCS
${o2_SRCS}
o2hubic.cpp
)
endif(WITH_HUBIC)
if(NOT WITH_QT5)
add_definitions(${QT4_DEFINITIONS})
endif(NOT WITH_QT5)
add_library( o2 ${o2_SRCS} )
if(WITH_QT5)
target_link_libraries( o2 Qt5::Core Qt5::Network Qt5::Script)
else(WITH_QT5)
target_link_libraries( o2 ${QT_LIBRARIES} )
endif(WITH_QT5)

447
3rdparty/o2/src/o1.cpp vendored Normal file
View file

@ -0,0 +1,447 @@
#include <QCryptographicHash>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QDateTime>
#include <QByteArray>
#include <QDebug>
#include <QStringList>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#if QT_VERSION >= 0x050100
#include <QMessageAuthenticationCode>
#endif
#include "o1.h"
#include "o2replyserver.h"
#include "o2globals.h"
#include "o2settingsstore.h"
#define trace() if (1) qDebug()
// #define trace() if (0) qDebug()
O1::O1(QObject *parent) :
QObject(parent) {
setSignatureMethod(O2_SIGNATURE_TYPE_HMAC_SHA1);
manager_ = new QNetworkAccessManager(this);
replyServer_ = new O2ReplyServer(this);
localPort_ = 0;
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
connect(replyServer_, SIGNAL(verificationReceived(QMap<QString,QString>)),
this, SLOT(onVerificationReceived(QMap<QString,QString>)));
store_ = new O2SettingsStore(O2_ENCRYPTION_KEY, this);
}
O1::~O1() {
}
void O1::setStore(O2AbstractStore *store) {
if (!store) {
qWarning() << "Store object is null! Using default O2SettingsStore";
return;
}
// Delete the previously stored object
store_->deleteLater();
store_ = store;
// re-parent it to this class as we take ownership of it now
store_->setParent(this);
}
bool O1::linked() {
return !token().isEmpty();
}
QString O1::tokenSecret() {
QString key = QString(O2_KEY_TOKEN_SECRET).arg(clientId_);
return store_->value(key);
}
void O1::setTokenSecret(const QString &v) {
QString key = QString(O2_KEY_TOKEN_SECRET).arg(clientId_);
store_->setValue(key, v);
}
QString O1::token() {
QString key = QString(O2_KEY_TOKEN).arg(clientId_);
return store_->value(key);
}
void O1::setToken(const QString &v) {
QString key = QString(O2_KEY_TOKEN).arg(clientId_);
store_->setValue(key, v);
}
QString O1::clientId() {
return clientId_;
}
void O1::setClientId(const QString &value) {
clientId_ = value;
emit clientIdChanged();
}
QString O1::clientSecret() {
return clientSecret_;
}
void O1::setClientSecret(const QString &value) {
clientSecret_ = value;
emit clientSecretChanged();
}
int O1::localPort() {
return localPort_;
}
void O1::setLocalPort(int value) {
localPort_ = value;
emit localPortChanged();
}
QUrl O1::requestTokenUrl() {
return requestTokenUrl_;
}
void O1::setRequestTokenUrl(const QUrl &v) {
requestTokenUrl_ = v;
emit requestTokenUrlChanged();
}
QUrl O1::authorizeUrl() {
return authorizeUrl_;
}
void O1::setAuthorizeUrl(const QUrl &value) {
authorizeUrl_ = value;
emit authorizeUrlChanged();
}
QUrl O1::accessTokenUrl() {
return accessTokenUrl_;
}
void O1::setAccessTokenUrl(const QUrl &value) {
accessTokenUrl_ = value;
emit accessTokenUrlChanged();
}
QString O1::signatureMethod()
{
return signatureMethod_;
}
void O1::setSignatureMethod(const QString &value)
{
signatureMethod_ = value;
}
QVariantMap O1::extraTokens() const {
return extraTokens_;
}
void O1::setExtraTokens(QVariantMap extraTokens) {
extraTokens_ = extraTokens;
}
void O1::unlink() {
trace() << "O1::unlink";
if (linked()) {
setToken("");
setTokenSecret("");
emit linkedChanged();
}
emit linkingSucceeded();
}
#if QT_VERSION < 0x050100
/// Calculate the HMAC variant of SHA1 hash.
/// @author http://qt-project.org/wiki/HMAC-SHA1.
/// @copyright Creative Commons Attribution-ShareAlike 2.5 Generic.
static QByteArray hmacSha1(QByteArray key, QByteArray baseString) {
int blockSize = 64;
if (key.length() > blockSize) {
key = QCryptographicHash::hash(key, QCryptographicHash::Sha1);
}
QByteArray innerPadding(blockSize, char(0x36));
QByteArray outerPadding(blockSize, char(0x5c));
for (int i = 0; i < key.length(); i++) {
innerPadding[i] = innerPadding[i] ^ key.at(i);
outerPadding[i] = outerPadding[i] ^ key.at(i);
}
QByteArray total = outerPadding;
QByteArray part = innerPadding;
part.append(baseString);
total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1));
QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1);
return hashed.toBase64();
}
#endif
/// Get HTTP operation name.
static QString getOperationName(QNetworkAccessManager::Operation op) {
switch (op) {
case QNetworkAccessManager::GetOperation: return "GET";
case QNetworkAccessManager::PostOperation: return "POST";
case QNetworkAccessManager::PutOperation: return "PUT";
case QNetworkAccessManager::DeleteOperation: return "DEL";
default: return "";
}
}
/// Build a concatenated/percent-encoded string from a list of headers.
QByteArray O1::encodeHeaders(const QList<O1RequestParameter> &headers) {
return QUrl::toPercentEncoding(createQueryParams(headers));
}
/// Construct query string from list of headers
QByteArray O1::createQueryParams(const QList<O1RequestParameter> &params) {
QByteArray ret;
bool first = true;
foreach (O1RequestParameter h, params) {
if (first) {
first = false;
} else {
ret.append("&");
}
ret.append(QUrl::toPercentEncoding(h.name) + "=" + QUrl::toPercentEncoding(h.value));
}
return ret;
}
/// Build a base string for signing.
QByteArray O1::getRequestBase(const QList<O1RequestParameter> &oauthParams, const QList<O1RequestParameter> &otherParams, const QUrl &url, QNetworkAccessManager::Operation op) {
QByteArray base;
// Initialize base string with the operation name (e.g. "GET") and the base URL
base.append(getOperationName(op).toUtf8() + "&");
base.append(QUrl::toPercentEncoding(url.toString(QUrl::RemoveQuery)) + "&");
// Append a sorted+encoded list of all request parameters to the base string
QList<O1RequestParameter> headers(oauthParams);
headers.append(otherParams);
qSort(headers);
base.append(encodeHeaders(headers));
return base;
}
QByteArray O1::sign(const QList<O1RequestParameter> &oauthParams, const QList<O1RequestParameter> &otherParams, const QUrl &url, QNetworkAccessManager::Operation op, const QString &consumerSecret, const QString &tokenSecret) {
QByteArray baseString = getRequestBase(oauthParams, otherParams, url, op);
QByteArray secret = QUrl::toPercentEncoding(consumerSecret) + "&" + QUrl::toPercentEncoding(tokenSecret);
#if QT_VERSION >= 0x050100
return QMessageAuthenticationCode::hash(baseString, secret, QCryptographicHash::Sha1).toBase64();
#else
return hmacSha1(secret, baseString);
#endif
}
QByteArray O1::buildAuthorizationHeader(const QList<O1RequestParameter> &oauthParams) {
bool first = true;
QByteArray ret("OAuth ");
QList<O1RequestParameter> headers(oauthParams);
qSort(headers);
foreach (O1RequestParameter h, headers) {
if (first) {
first = false;
} else {
ret.append(",");
}
ret.append(h.name);
ret.append("=\"");
ret.append(QUrl::toPercentEncoding(h.value));
ret.append("\"");
}
return ret;
}
QByteArray O1::generateSignature(const QList<O1RequestParameter> headers,
const QNetworkRequest &req,
const QList<O1RequestParameter> &signingParameters,
QNetworkAccessManager::Operation operation)
{
QByteArray signature;
if(signatureMethod() == O2_SIGNATURE_TYPE_HMAC_SHA1)
{
signature = sign(headers, signingParameters, req.url(), operation, clientSecret(), tokenSecret());
}
else if(signatureMethod() == O2_SIGNATURE_TYPE_PLAINTEXT)
{
signature = clientSecret().toLatin1() + "&" + tokenSecret().toLatin1();
}
return signature;
}
void O1::link() {
trace() << "O1::link";
if (linked()) {
trace() << "O1::link: Linked already";
emit linkingSucceeded();
return;
}
// Start reply server
replyServer_->listen(QHostAddress::Any, localPort());
// Create request
QNetworkRequest request(requestTokenUrl());
// Create initial token request
QList<O1RequestParameter> headers;
headers.append(O1RequestParameter(O2_OAUTH_CALLBACK, QString(O2_CALLBACK_URL).arg(replyServer_->serverPort()).toLatin1()));
headers.append(O1RequestParameter(O2_OAUTH_CONSUMER_KEY, clientId().toLatin1()));
headers.append(O1RequestParameter(O2_OAUTH_NONCE, nonce()));
headers.append(O1RequestParameter(O2_OAUTH_TIMESTAMP, QString::number(QDateTime::currentDateTimeUtc().toTime_t()).toLatin1()));
headers.append(O1RequestParameter(O2_OAUTH_VERSION, "1.0"));
headers.append(O1RequestParameter(O2_OAUTH_SIGNATURE_METHOD, signatureMethod().toLatin1()));
headers.append(O1RequestParameter(O2_OAUTH_SIGNATURE, generateSignature(headers, request, QList<O1RequestParameter>(), QNetworkAccessManager::PostOperation)));
// Clear request token
requestToken_.clear();
requestTokenSecret_.clear();
// Post request
request.setRawHeader(O2_HTTP_AUTHORIZATION_HEADER, buildAuthorizationHeader(headers));
request.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM);
QNetworkReply *reply = manager_->post(request, QByteArray());
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenRequestError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), this, SLOT(onTokenRequestFinished()));
}
void O1::onTokenRequestError(QNetworkReply::NetworkError error) {
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
qWarning() << "O1::onTokenRequestError:" << (int)error << reply->errorString() << reply->readAll();
emit linkingFailed();
}
void O1::onTokenRequestFinished() {
trace() << "O1::onTokenRequestFinished";
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
return;
}
// Get request token and secret
QByteArray data = reply->readAll();
QMap<QString, QString> response = parseResponse(data);
requestToken_ = response.value(O2_OAUTH_TOKEN, "");
requestTokenSecret_ = response.value(O2_OAUTH_TOKEN_SECRET, "");
setToken(requestToken_);
setTokenSecret(requestTokenSecret_);
// Checking for "oauth_callback_confirmed" is present and set to true
QString oAuthCbConfirmed = response.value(O2_OAUTH_CALLBACK_CONFIRMED, "false");
if (requestToken_.isEmpty() || requestTokenSecret_.isEmpty() || (oAuthCbConfirmed == "false")) {
qWarning() << "O1::onTokenRequestFinished: No oauth_token, oauth_token_secret or oauth_callback_confirmed in response :" << data;
emit linkingFailed();
return;
}
// Continue authorization flow in the browser
QUrl url(authorizeUrl());
#if QT_VERSION < 0x050000
url.addQueryItem(O2_OAUTH_TOKEN, requestToken_);
url.addQueryItem(O2_OAUTH_CALLBACK, QString(O2_CALLBACK_URL).arg(replyServer_->serverPort()).toLatin1());
#else
QUrlQuery query(url);
query.addQueryItem(O2_OAUTH_TOKEN, requestToken_);
query.addQueryItem(O2_OAUTH_CALLBACK, QString(O2_CALLBACK_URL).arg(replyServer_->serverPort()).toLatin1());
url.setQuery(query);
#endif
emit openBrowser(url);
}
void O1::onVerificationReceived(QMap<QString, QString> params) {
trace() << "O1::onVerificationReceived";
emit closeBrowser();
verifier_ = params.value(O2_OAUTH_VERFIER, "");
if (params.value(O2_OAUTH_TOKEN) == requestToken_) {
// Exchange request token for access token
exchangeToken();
} else {
qWarning() << "O1::onVerificationReceived: oauth_token missing or doesn't match";
emit linkingFailed();
}
}
void O1::exchangeToken() {
// Create token exchange request
QNetworkRequest request(accessTokenUrl());
QList<O1RequestParameter> oauthParams;
oauthParams.append(O1RequestParameter(O2_OAUTH_CONSUMER_KEY, clientId().toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_VERSION, "1.0"));
oauthParams.append(O1RequestParameter(O2_OAUTH_TIMESTAMP, QString::number(QDateTime::currentDateTimeUtc().toTime_t()).toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_NONCE, nonce()));
oauthParams.append(O1RequestParameter(O2_OAUTH_TOKEN, requestToken_.toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_VERFIER, verifier_.toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_SIGNATURE_METHOD, signatureMethod().toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_SIGNATURE, generateSignature(oauthParams, request, QList<O1RequestParameter>(), QNetworkAccessManager::PostOperation)));
// Post request
request.setRawHeader(O2_HTTP_AUTHORIZATION_HEADER, buildAuthorizationHeader(oauthParams));
request.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM);
QNetworkReply *reply = manager_->post(request, QByteArray());
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenExchangeError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), this, SLOT(onTokenExchangeFinished()));
}
void O1::onTokenExchangeError(QNetworkReply::NetworkError error) {
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
qWarning() << "O1::onTokenExchangeError:" << (int)error << reply->errorString() << reply->readAll();
emit linkingFailed();
}
void O1::onTokenExchangeFinished() {
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
return;
}
// Get access token and secret
QByteArray data = reply->readAll();
QMap<QString, QString> response = parseResponse(data);
if (response.contains(O2_OAUTH_TOKEN) && response.contains(O2_OAUTH_TOKEN_SECRET)) {
setToken(response.take(O2_OAUTH_TOKEN));
setTokenSecret(response.take(O2_OAUTH_TOKEN_SECRET));
// Set extra tokens if any
if (!response.isEmpty()) {
QVariantMap extraTokens;
foreach (QString key, response.keys()) {
extraTokens.insert(key, response.value(key));
}
setExtraTokens(extraTokens);
}
emit linkedChanged();
emit linkingSucceeded();
} else {
qWarning() << "O1::onTokenExchangeFinished: oauth_token or oauth_token_secret missing from response" << data;
emit linkingFailed();
}
}
QMap<QString, QString> O1::parseResponse(const QByteArray &response) {
QMap<QString, QString> ret;
foreach (QByteArray param, response.split('&')) {
QList<QByteArray> kv = param.split('=');
if (kv.length() == 2) {
ret.insert(QUrl::fromPercentEncoding(kv[0]), QUrl::fromPercentEncoding(kv[1]));
}
}
return ret;
}
QByteArray O1::nonce() {
static bool firstTime = true;
if (firstTime) {
firstTime = false;
qsrand(QTime::currentTime().msec());
}
QString u = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
u.append(QString::number(qrand()));
return u.toLatin1();
}

208
3rdparty/o2/src/o1.h vendored Normal file
View file

@ -0,0 +1,208 @@
#ifndef O1_H
#define O1_H
#include <QObject>
#include <QString>
#include <QMap>
#include <QList>
#include <QByteArray>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkReply>
#include "o2abstractstore.h"
class O2ReplyServer;
/// Request parameter (name-value pair) participating in authentication.
struct O1RequestParameter {
O1RequestParameter(const QByteArray &n, const QByteArray &v): name(n), value(v) {
}
bool operator <(const O1RequestParameter &other) const {
return (name == other.name)? (value < other.value): (name < other.name);
}
QByteArray name;
QByteArray value;
};
/// Simple OAuth 1.0 authenticator.
class O1: public QObject {
Q_OBJECT
public:
/// Are we authenticated?
Q_PROPERTY(bool linked READ linked NOTIFY linkedChanged)
bool linked();
/// Authentication token.
QString token();
/// Authentication token secret.
QString tokenSecret();
/// Extra tokens available after a successful OAuth exchange
Q_PROPERTY(QMap extraTokens READ extraTokens)
QVariantMap extraTokens() const;
/// Client application ID.
/// O1 instances with the same (client ID, client secret) share the same "linked", "token" and "tokenSecret" properties.
Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged)
QString clientId();
void setClientId(const QString &value);
/// Client application secret.
/// O1 instances with the same (client ID, client secret) share the same "linked", "token" and "tokenSecret" properties.
Q_PROPERTY(QString clientSecret READ clientSecret WRITE setClientSecret NOTIFY clientSecretChanged)
QString clientSecret();
void setClientSecret(const QString &value);
/// Token request URL.
Q_PROPERTY(QUrl requestTokenUrl READ requestTokenUrl WRITE setRequestTokenUrl NOTIFY requestTokenUrlChanged)
QUrl requestTokenUrl();
void setRequestTokenUrl(const QUrl &value);
/// Authorization URL.
Q_PROPERTY(QUrl authorizeUrl READ authorizeUrl WRITE setAuthorizeUrl NOTIFY authorizeUrlChanged)
QUrl authorizeUrl();
void setAuthorizeUrl(const QUrl &value);
/// Access token URL.
Q_PROPERTY(QUrl accessTokenUrl READ accessTokenUrl WRITE setAccessTokenUrl NOTIFY accessTokenUrlChanged)
QUrl accessTokenUrl();
void setAccessTokenUrl(const QUrl &value);
/// Signature method
Q_PROPERTY(QString signatureMethod READ signatureMethod WRITE setSignatureMethod NOTIFY signatureMethodChanged)
QString signatureMethod();
void setSignatureMethod(const QString &value);
/// TCP port number to use in local redirections.
/// The OAuth "redirect_uri" will be set to "http://localhost:<localPort>/".
/// If localPort is set to 0 (default), O1 will replace it with a free one.
Q_PROPERTY(int localPort READ localPort WRITE setLocalPort NOTIFY localPortChanged)
int localPort();
void setLocalPort(int value);
/// Constructor.
/// @param parent Parent object.
explicit O1(QObject *parent = 0);
/// Destructor.
virtual ~O1();
/// Sets the storage object to use for storing the OAuth tokens on a peristent medium
void setStore(O2AbstractStore *store);
/// Parse a URL-encoded response string.
static QMap<QString, QString> parseResponse(const QByteArray &response);
/// Build the value of the "Authorization:" header.
static QByteArray buildAuthorizationHeader(const QList<O1RequestParameter> &oauthParams);
/// Create unique bytes to prevent replay attacks.
static QByteArray nonce();
/// Generate signature string depending on signature method type
QByteArray generateSignature(const QList<O1RequestParameter> headers, const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, QNetworkAccessManager::Operation operation);
/// Calculate the HMAC-SHA1 signature of a request.
/// @param oauthParams OAuth parameters.
/// @param otherParams Other parameters participating in signing.
/// @param URL Request URL. May contain query parameters, but they will not be used for signing.
/// @param op HTTP operation.
/// @param consumerSecret Consumer (application) secret.
/// @param tokenSecret Authorization token secret (empty if not yet available).
/// @return Signature that can be used as the value of the "oauth_signature" parameter.
static QByteArray sign(const QList<O1RequestParameter> &oauthParams, const QList<O1RequestParameter> &otherParams, const QUrl &url, QNetworkAccessManager::Operation op, const QString &consumerSecret, const QString &tokenSecret);
/// Build a base string for signing.
static QByteArray getRequestBase(const QList<O1RequestParameter> &oauthParams, const QList<O1RequestParameter> &otherParams, const QUrl &url, QNetworkAccessManager::Operation op);
/// Build a concatenated/percent-encoded string from a list of headers.
static QByteArray encodeHeaders(const QList<O1RequestParameter> &headers);
/// Construct query string from list of headers
static QByteArray createQueryParams(const QList<O1RequestParameter> &params);
public slots:
/// Authenticate.
Q_INVOKABLE virtual void link();
/// De-authenticate.
Q_INVOKABLE virtual void unlink();
signals:
/// Emitted when client needs to open a web browser window, with the given URL.
void openBrowser(const QUrl &url);
/// Emitted when client can close the browser window.
void closeBrowser();
/// Emitted when authentication/deauthentication succeeded.
void linkingSucceeded();
/// Emitted when authentication/deauthentication failed.
void linkingFailed();
// Property change signals
void linkedChanged();
void clientIdChanged();
void clientSecretChanged();
void requestTokenUrlChanged();
void authorizeUrlChanged();
void accessTokenUrlChanged();
void localPortChanged();
void signatureMethodChanged();
protected slots:
/// Handle verification received from the reply server.
virtual void onVerificationReceived(QMap<QString,QString> params);
/// Handle token request error.
virtual void onTokenRequestError(QNetworkReply::NetworkError error);
/// Handle token request finished.
virtual void onTokenRequestFinished();
/// Handle token exchange error.
void onTokenExchangeError(QNetworkReply::NetworkError error);
/// Handle token exchange finished.
void onTokenExchangeFinished();
protected:
/// Set authentication token.
void setToken(const QString &v);
/// Set authentication token secret.
void setTokenSecret(const QString &v);
/// Exchange token for authorizaton token.
virtual void exchangeToken();
/// Set extra tokens found in OAuth response
void setExtraTokens(QVariantMap extraTokens);
protected:
QString clientId_;
QString clientSecret_;
QString scope_;
QString code_;
QString redirectUri_;
QString requestToken_;
QString requestTokenSecret_;
QString verifier_;
QString signatureMethod_;
QUrl requestTokenUrl_;
QUrl authorizeUrl_;
QUrl accessTokenUrl_;
QNetworkAccessManager *manager_;
O2ReplyServer *replyServer_;
quint16 localPort_;
O2AbstractStore *store_;
QVariantMap extraTokens_;
};
#endif // O1_H

18
3rdparty/o2/src/o1dropbox.h vendored Normal file
View file

@ -0,0 +1,18 @@
#ifndef O1DROPBOX_H
#define O1DROPBOX_H
#include "o1.h"
class O1Dropbox: public O1 {
Q_OBJECT
public:
explicit O1Dropbox(QObject *parent = 0): O1(parent) {
setRequestTokenUrl(QUrl("https://api.dropbox.com/1/oauth/request_token"));
setAuthorizeUrl(QUrl("https://www.dropbox.com/1/oauth/authorize?display=mobile"));
setAccessTokenUrl(QUrl("https://api.dropbox.com/1/oauth/access_token"));
setLocalPort(1965);
}
};
#endif // O1DROPBOX_H

18
3rdparty/o2/src/o1flickr.h vendored Normal file
View file

@ -0,0 +1,18 @@
#ifndef O1FLICKR_H
#define O1FLICKR_H
#include "o1.h"
class O1Flickr: public O1 {
Q_OBJECT
public:
explicit O1Flickr(QObject *parent = 0): O1(parent) {
setRequestTokenUrl(QUrl("http://www.flickr.com/services/oauth/request_token"));
setAuthorizeUrl(QUrl("http://www.flickr.com/services/oauth/authorize?perms=write"));
setAccessTokenUrl(QUrl("http://www.flickr.com/services/oauth/access_token"));
setLocalPort(1965); // FIXME: Really needed?
}
};
#endif // O1FLICKR_H

26
3rdparty/o2/src/o1freshbooks.h vendored Executable file
View file

@ -0,0 +1,26 @@
#ifndef O1FRESHBOOKS_H
#define O1FRESHBOOKS_H
#include "o1.h"
class O1Freshbooks: public O1 {
Q_OBJECT
public:
explicit O1Freshbooks(QObject *parent = 0): O1(parent)
{
}
void setClientId(const QString &value)
{
O1::setClientId(value);
setRequestTokenUrl(QUrl("https://" + clientId() + ".freshbooks.com/oauth/oauth_request.php"));
setAuthorizeUrl(QUrl("https://" + clientId() + ".freshbooks.com/oauth/oauth_authorize.php"));
setAccessTokenUrl(QUrl("https://" + clientId() + ".freshbooks.com/oauth/oauth_access.php"));
}
};
#endif // O1FRESHBOOKS_H

78
3rdparty/o2/src/o1requestor.cpp vendored Normal file
View file

@ -0,0 +1,78 @@
#include <QDebug>
#include <QTimer>
#include <QDateTime>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include "o1requestor.h"
#include "o2globals.h"
/// A timer connected to a network reply.
class TimedReply: public QTimer {
Q_OBJECT
public:
explicit TimedReply(QNetworkReply *parent): QTimer(parent) {
setSingleShot(true);
setInterval(60 * 1000); // FIXME: Expose me
connect(this, SIGNAL(error(QNetworkReply::NetworkError)), parent, SIGNAL(error(QNetworkReply::NetworkError)));
connect(this, SIGNAL(timeout()), this, SLOT(onTimeout()));
}
signals:
void error(QNetworkReply::NetworkError);
public slots:
void onTimeout() {emit error(QNetworkReply::TimeoutError);}
};
O1Requestor::O1Requestor(QNetworkAccessManager *manager, O1 *authenticator, QObject *parent): QObject(parent) {
manager_ = manager;
authenticator_ = authenticator;
}
QNetworkReply *O1Requestor::get(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters) {
QNetworkRequest request = setup(req, signingParameters, QNetworkAccessManager::GetOperation);
return addTimer(manager_->get(request));
}
QNetworkReply *O1Requestor::post(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, const QByteArray &data) {
QNetworkRequest request = setup(req, signingParameters, QNetworkAccessManager::PostOperation);
return addTimer(manager_->post(request, data));
}
QNetworkReply *O1Requestor::post(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, QHttpMultiPart * multiPart) {
QNetworkRequest request = setup(req, signingParameters, QNetworkAccessManager::PostOperation);
return addTimer(manager_->post(request, multiPart));
}
QNetworkReply *O1Requestor::put(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, const QByteArray &data) {
QNetworkRequest request = setup(req, signingParameters, QNetworkAccessManager::PutOperation);
return addTimer(manager_->put(request, data));
}
QNetworkReply *O1Requestor::addTimer(QNetworkReply *reply) {
(void)new TimedReply(reply);
return reply;
}
QNetworkRequest O1Requestor::setup(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, QNetworkAccessManager::Operation operation) {
// Collect OAuth parameters
QList<O1RequestParameter> oauthParams;
oauthParams.append(O1RequestParameter(O2_OAUTH_CONSUMER_KEY, authenticator_->clientId().toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_VERSION, "1.0"));
oauthParams.append(O1RequestParameter(O2_OAUTH_TOKEN, authenticator_->token().toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_SIGNATURE_METHOD, authenticator_->signatureMethod().toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_NONCE, O1::nonce()));
oauthParams.append(O1RequestParameter(O2_OAUTH_TIMESTAMP, QString::number(QDateTime::currentDateTimeUtc().toTime_t()).toLatin1()));
// Add signature parameter
oauthParams.append(O1RequestParameter(O2_OAUTH_SIGNATURE, authenticator_->generateSignature(oauthParams, req, signingParameters, operation)));
// Return a copy of the original request with authorization header set
QNetworkRequest request(req);
request.setRawHeader(O2_HTTP_AUTHORIZATION_HEADER, O1::buildAuthorizationHeader(oauthParams));
return request;
}
#include "o1requestor.moc"

61
3rdparty/o2/src/o1requestor.h vendored Normal file
View file

@ -0,0 +1,61 @@
#ifndef O1REQUESTOR_H
#define O1REQUESTOR_H
#include <QObject>
#include <QNetworkRequest>
#include <QByteArray>
#include "o1.h"
class QNetworkAccessManager;
class QNetworkReply;
class O1;
/// Makes authenticated requests using OAuth 1.0.
class O1Requestor: public QObject {
Q_OBJECT
public:
explicit O1Requestor(QNetworkAccessManager *manager, O1 *authenticator, QObject *parent = 0);
public slots:
/// Make a GET request.
/// @param req Network request.
/// @param signingParameters Extra (non-OAuth) parameters participating in signing.
/// @return Reply.
QNetworkReply *get(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters);
/// Make a POST request.
/// @param req Network request.
/// @param signingParameters Extra (non-OAuth) parameters participating in signing.
/// @param data Request payload.
/// @return Reply.
QNetworkReply *post(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, const QByteArray &data);
/// Make a POST request.
/// @param req Network request.
/// @param signingParameters Extra (non-OAuth) parameters participating in signing.
/// @param multiPart HTTPMultiPart.
/// @return Reply.
QNetworkReply *post(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, QHttpMultiPart *multiPart);
/// Make a PUT request.
/// @param req Network request.
/// @param signingParameters Extra (non-OAuth) parameters participating in signing.
/// @param data Request payload.
/// @return Reply.
QNetworkReply *put(const QNetworkRequest &req, const QList<O1RequestParameter> &signingParameters, const QByteArray &data);
protected:
/// Return new request based on the original, with the "Authentication:" header added.
QNetworkRequest setup(const QNetworkRequest &request, const QList<O1RequestParameter> &signingParameters, QNetworkAccessManager::Operation operation);
/// Augment reply with a timer.
QNetworkReply *addTimer(QNetworkReply *reply);
QNetworkAccessManager *manager_;
O1 *authenticator_;
};
#endif // O1REQUESTOR_H

17
3rdparty/o2/src/o1twitter.h vendored Normal file
View file

@ -0,0 +1,17 @@
#ifndef O1TWITTER_H
#define O1TWITTER_H
#include "o1.h"
class O1Twitter: public O1 {
Q_OBJECT
public:
explicit O1Twitter(QObject *parent = 0): O1(parent) {
setRequestTokenUrl(QUrl("https://api.twitter.com/oauth/request_token"));
setAuthorizeUrl(QUrl("https://api.twitter.com/oauth/authenticate"));
setAccessTokenUrl(QUrl("https://api.twitter.com/oauth/access_token"));
}
};
#endif // O1TWITTER_H

397
3rdparty/o2/src/o2.cpp vendored Normal file
View file

@ -0,0 +1,397 @@
#include <QList>
#include <QPair>
#include <QDebug>
#include <QTcpServer>
#include <QMap>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QDateTime>
#include <QCryptographicHash>
#include <QTimer>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include "o2.h"
#include "o2replyserver.h"
#include "o2globals.h"
#include "o2settingsstore.h"
#define trace() if (1) qDebug()
// define trace() if (0) qDebug()
O2::O2(QObject *parent): QObject(parent) {
manager_ = new QNetworkAccessManager(this);
replyServer_ = new O2ReplyServer(this);
grantFlow_ = GrantFlowAuthorizationCode;
localPort_ = 0;
localhostPolicy_ = QString(O2_CALLBACK_URL);
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
connect(replyServer_, SIGNAL(verificationReceived(QMap<QString,QString>)),
this, SLOT(onVerificationReceived(QMap<QString,QString>)));
store_ = new O2SettingsStore(O2_ENCRYPTION_KEY, this);
}
O2::~O2() {
}
void O2::setStore(O2AbstractStore *store) {
if (!store) {
qWarning() << "Store object is null! Using default O2SettingsStore";
return;
}
// Delete the previously stored object
store_->deleteLater();
store_ = store;
// re-parent it to this class as we take ownership of it now
store_->setParent(this);
}
O2::GrantFlow O2::grantFlow() {
return grantFlow_;
}
void O2::setGrantFlow(O2::GrantFlow value) {
grantFlow_ = value;
emit grantFlowChanged();
}
QString O2::clientId() {
return clientId_;
}
void O2::setClientId(const QString &value) {
clientId_ = value;
emit clientIdChanged();
}
QString O2::clientSecret() {
return clientSecret_;
}
void O2::setClientSecret(const QString &value) {
clientSecret_ = value;
emit clientSecretChanged();
}
QString O2::scope() {
return scope_;
}
void O2::setScope(const QString &value) {
scope_ = value;
emit scopeChanged();
}
QString O2::requestUrl() {
return requestUrl_.toString();
}
void O2::setRequestUrl(const QString &value) {
requestUrl_ = value;
emit requestUrlChanged();
}
QString O2::tokenUrl() {
return tokenUrl_.toString();
}
void O2::setTokenUrl(const QString &value) {
tokenUrl_= value;
emit tokenUrlChanged();
}
QString O2::refreshTokenUrl() {
return refreshTokenUrl_.toString();
}
void O2::setRefreshTokenUrl(const QString &value) {
refreshTokenUrl_ = value;
emit refreshTokenUrlChanged();
}
int O2::localPort() {
return localPort_;
}
void O2::setLocalPort(int value) {
localPort_ = value;
emit localPortChanged();
}
QVariantMap O2::extraTokens() const {
return extraTokens_;
}
void O2::setExtraTokens(QVariantMap extraTokens) {
extraTokens_ = extraTokens;
}
void O2::link() {
trace() << "O2::link";
if (linked()) {
trace() << " Linked already";
emit linkingSucceeded();
return;
}
// Start listening to authentication replies
replyServer_->listen(QHostAddress::Any, localPort_);
// Save redirect URI, as we have to reuse it when requesting the access token
redirectUri_ = localhostPolicy_.arg(replyServer_->serverPort());
// Assemble intial authentication URL
QList<QPair<QString, QString> > parameters;
parameters.append(qMakePair(QString(O2_OAUTH2_RESPONSE_TYPE), (grantFlow_ == GrantFlowAuthorizationCode) ? QString(O2_OAUTH2_CODE) : QString(O2_OAUTH2_TOKEN)));
parameters.append(qMakePair(QString(O2_OAUTH2_CLIENT_ID), clientId_));
parameters.append(qMakePair(QString(O2_OAUTH2_REDIRECT_URI), redirectUri_));
// parameters.append(qMakePair(QString(OAUTH2_REDIRECT_URI), QString(QUrl::toPercentEncoding(redirectUri_))));
parameters.append(qMakePair(QString(O2_OAUTH2_SCOPE), scope_));
// Show authentication URL with a web browser
QUrl url(requestUrl_);
#if QT_VERSION < 0x050000
url.setQueryItems(parameters);
#else
QUrlQuery query(url);
query.setQueryItems(parameters);
url.setQuery(query);
#endif
trace() << "Emit openBrowser" << url.toString();
emit openBrowser(url);
}
void O2::unlink() {
if (!linked()) {
return;
}
setToken(QString());
setRefreshToken(QString());
setExpires(0);
emit linkedChanged();
emit linkingSucceeded();
}
bool O2::linked() {
return token().length();
}
void O2::onVerificationReceived(const QMap<QString, QString> response) {
trace() << "O2::onVerificationReceived";
trace() << "" << response;
emit closeBrowser();
if (response.contains("error")) {
trace() << " Verification failed";
emit linkingFailed();
return;
}
if (grantFlow_ == GrantFlowAuthorizationCode) {
// Save access code
setCode(response.value(QString(O2_OAUTH2_CODE)));
// Exchange access code for access/refresh tokens
QNetworkRequest tokenRequest(tokenUrl_);
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM);
QMap<QString, QString> parameters;
parameters.insert(O2_OAUTH2_CODE, code());
parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_);
parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_);
parameters.insert(O2_OAUTH2_REDIRECT_URI, redirectUri_);
parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_AUTHORIZATION_CODE);
QByteArray data = buildRequestBody(parameters);
QNetworkReply *tokenReply = manager_->post(tokenRequest, data);
timedReplies_.add(tokenReply);
connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection);
connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
} else {
setToken(response.value(O2_OAUTH2_ACCESS_TOKEN));
setRefreshToken(response.value(O2_OAUTH2_REFRESH_TOKEN));
}
}
QString O2::code() {
QString key = QString(O2_KEY_CODE).arg(clientId_);
return store_->value(key);
}
void O2::setCode(const QString &c) {
QString key = QString(O2_KEY_CODE).arg(clientId_);
store_->setValue(key, c);
}
void O2::onTokenReplyFinished() {
trace() << "O2::onTokenReplyFinished";
QNetworkReply *tokenReply = qobject_cast<QNetworkReply *>(sender());
if (tokenReply->error() == QNetworkReply::NoError) {
QByteArray replyData = tokenReply->readAll();
QScriptEngine engine;
QScriptValueIterator it(engine.evaluate("(" + QString(replyData) + ")"));
QVariantMap tokens;
while (it.hasNext()) {
it.next();
tokens.insert(it.name(), it.value().toVariant());
}
// Check for mandatory tokens
if (tokens.contains(O2_OAUTH2_ACCESS_TOKEN)) {
setToken(tokens.take(O2_OAUTH2_ACCESS_TOKEN).toString());
bool ok = false;
int expiresIn = tokens.take(O2_OAUTH2_EXPIRES_IN).toInt(&ok);
if (ok) {
trace() << "Token expires in" << expiresIn << "seconds";
setExpires(QDateTime::currentMSecsSinceEpoch() / 1000 + expiresIn);
}
setRefreshToken(tokens.take(O2_OAUTH2_REFRESH_TOKEN).toString());
// Set extra tokens if any
if (!tokens.isEmpty()) {
setExtraTokens(tokens);
}
timedReplies_.remove(tokenReply);
emit linkedChanged();
emit tokenChanged();
emit linkingSucceeded();
} else {
qWarning() << "O2::onTokenReplyFinished: oauth_token missing from response" << replyData;
emit linkedChanged();
emit tokenChanged();
emit linkingFailed();
}
}
tokenReply->deleteLater();
}
void O2::onTokenReplyError(QNetworkReply::NetworkError error) {
QNetworkReply *tokenReply = qobject_cast<QNetworkReply *>(sender());
trace() << "O2::onTokenReplyError" << error << tokenReply->errorString();
trace() << "" << tokenReply->readAll();
setToken(QString());
setRefreshToken(QString());
timedReplies_.remove(tokenReply);
emit linkedChanged();
emit tokenChanged();
emit linkingFailed();
}
QByteArray O2::buildRequestBody(const QMap<QString, QString> &parameters) {
QByteArray body;
bool first = true;
foreach (QString key, parameters.keys()) {
if (first) {
first = false;
} else {
body.append("&");
}
QString value = parameters.value(key);
body.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() + QUrl::toPercentEncoding(value));
}
return body;
}
QString O2::token() {
QString key = QString(O2_KEY_TOKEN).arg(clientId_);
return store_->value(key);
}
void O2::setToken(const QString &v) {
QString key = QString(O2_KEY_TOKEN).arg(clientId_);
store_->setValue(key, v);
}
int O2::expires() {
QString key = QString(O2_KEY_EXPIRES).arg(clientId_);
return store_->value(key).toInt();
}
void O2::setExpires(int v) {
QString key = QString(O2_KEY_EXPIRES).arg(clientId_);
store_->setValue(key, QString::number(v));
}
QString O2::refreshToken() {
QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_);
return store_->value(key);
}
void O2::setRefreshToken(const QString &v) {
trace() << "O2::setRefreshToken" << v.left(4) << "...";
QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_);
store_->setValue(key, v);
}
void O2::refresh() {
trace() << "O2::refresh: Token: ..." << refreshToken().right(7);
if (refreshToken().isEmpty()) {
qWarning() << "O2::refresh: No refresh token";
onRefreshError(QNetworkReply::AuthenticationRequiredError);
return;
}
if (refreshTokenUrl_.isEmpty()) {
qWarning() << "O2::refresh: Refresh token URL not set";
onRefreshError(QNetworkReply::AuthenticationRequiredError);
return;
}
QNetworkRequest refreshRequest(refreshTokenUrl_);
refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM);
QMap<QString, QString> parameters;
parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_);
parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_);
parameters.insert(O2_OAUTH2_REFRESH_TOKEN, refreshToken());
parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_OAUTH2_REFRESH_TOKEN);
QByteArray data = buildRequestBody(parameters);
QNetworkReply *refreshReply = manager_->post(refreshRequest, data);
timedReplies_.add(refreshReply);
connect(refreshReply, SIGNAL(finished()), this, SLOT(onRefreshFinished()), Qt::QueuedConnection);
connect(refreshReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRefreshError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
}
void O2::onRefreshFinished() {
QNetworkReply *refreshReply = qobject_cast<QNetworkReply *>(sender());
trace() << "O2::onRefreshFinished: Error" << (int)refreshReply->error() << refreshReply->errorString();
if (refreshReply->error() == QNetworkReply::NoError) {
QByteArray reply = refreshReply->readAll();
QScriptValue value;
QScriptEngine engine;
value = engine.evaluate("(" + QString(reply) + ")");
setToken(value.property(O2_OAUTH2_ACCESS_TOKEN).toString());
setExpires(QDateTime::currentMSecsSinceEpoch() / 1000 + value.property(O2_OAUTH2_EXPIRES_IN).toInteger());
setRefreshToken(value.property(O2_OAUTH2_REFRESH_TOKEN).toString());
timedReplies_.remove(refreshReply);
emit linkingSucceeded();
emit tokenChanged();
emit linkedChanged();
emit refreshFinished(QNetworkReply::NoError);
trace() << " New token expires in" << expires() << "seconds";
}
refreshReply->deleteLater();
}
void O2::onRefreshError(QNetworkReply::NetworkError error) {
QNetworkReply *refreshReply = qobject_cast<QNetworkReply *>(sender());
qWarning() << "O2::onRefreshFailed: Error" << error << ", resetting tokens";
setToken(QString());
setRefreshToken(QString());
timedReplies_.remove(refreshReply);
emit tokenChanged();
emit linkingFailed();
emit linkedChanged();
emit refreshFinished(error);
}
QString O2::localhostPolicy() const
{
return localhostPolicy_;
}
void O2::setLocalhostPolicy(const QString& value)
{
localhostPolicy_ = value;
}

199
3rdparty/o2/src/o2.h vendored Normal file
View file

@ -0,0 +1,199 @@
#ifndef O2_H
#define O2_H
#include <QObject>
#include <QString>
#include <QUrl>
#include <QMap>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QList>
#include <QPair>
#include "o2reply.h"
#include "o2abstractstore.h"
class O2ReplyServer;
/// Simple OAuth2 authenticator.
class O2: public QObject {
Q_OBJECT
Q_ENUMS(GrantFlow)
public:
enum GrantFlow {GrantFlowAuthorizationCode, GrantFlowImplicit};
/// Authorization flow: Authorization Code (default, see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1) or Implicit (see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.2)
Q_PROPERTY(GrantFlow grantFlow READ grantFlow WRITE setGrantFlow NOTIFY grantFlowChanged)
GrantFlow grantFlow();
void setGrantFlow(GrantFlow value);
/// Are we authenticated?
Q_PROPERTY(bool linked READ linked NOTIFY linkedChanged)
bool linked();
/// Extra tokens available after a successful OAuth exchange
Q_PROPERTY(QMap extraTokens READ extraTokens)
QVariantMap extraTokens() const;
/// Authentication token.
Q_PROPERTY(QString token READ token WRITE setToken NOTIFY tokenChanged)
QString token();
void setToken(const QString &v);
/// Client ID.
/// O2 instances with the same (client ID, client secret) share the same "linked" and "token" properties.
Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged)
QString clientId();
void setClientId(const QString &value);
/// Client secret.
/// O2 instances with the same (client ID, client secret) share the same "linked" and "token" properties.
Q_PROPERTY(QString clientSecret READ clientSecret WRITE setClientSecret NOTIFY clientSecretChanged)
QString clientSecret();
void setClientSecret(const QString &value);
/// Scope of authentication.
Q_PROPERTY(QString scope READ scope WRITE setScope NOTIFY scopeChanged)
QString scope();
void setScope(const QString &value);
/// Request URL.
Q_PROPERTY(QString requestUrl READ requestUrl WRITE setRequestUrl NOTIFY requestUrlChanged)
QString requestUrl();
void setRequestUrl(const QString &value);
/// Token request URL.
Q_PROPERTY(QString tokenUrl READ tokenUrl WRITE setTokenUrl NOTIFY tokenUrlChanged)
QString tokenUrl();
void setTokenUrl(const QString &value);
/// Token refresh URL.
Q_PROPERTY(QString refreshTokenUrl READ refreshTokenUrl WRITE setRefreshTokenUrl NOTIFY refreshTokenUrlChanged)
QString refreshTokenUrl();
void setRefreshTokenUrl(const QString &value);
/// TCP port number to use in local redirections.
/// The OAuth 2.0 "redirect_uri" will be set to "http://localhost:<localPort>/".
/// If localPort is set to 0 (default), O2 will replace it with a free one.
Q_PROPERTY(int localPort READ localPort WRITE setLocalPort NOTIFY localPortChanged)
int localPort();
void setLocalPort(int value);
/// Localhost policy. By default it's value is http://127.0.0.1:%1/, however some services may
/// require the use of http://localhost:%1/ or any other value.
Q_PROPERTY(QString localhostPolicy READ localhostPolicy WRITE setLocalhostPolicy)
QString localhostPolicy() const;
void setLocalhostPolicy(const QString &value);
public:
/// Constructor.
/// @param parent Parent object.
explicit O2(QObject *parent = 0);
/// Destructor.
virtual ~O2();
/// Get authentication code.
QString code();
/// Get refresh token.
QString refreshToken();
/// Get token expiration time (seconds from Epoch).
int expires();
/// Sets the storage object to use for storing the OAuth tokens on a peristent medium
void setStore(O2AbstractStore *store);
public slots:
/// Authenticate.
Q_INVOKABLE virtual void link();
/// De-authenticate.
Q_INVOKABLE virtual void unlink();
/// Refresh token.
void refresh();
signals:
/// Emitted when client needs to open a web browser window, with the given URL.
void openBrowser(const QUrl &url);
/// Emitted when client can close the browser window.
void closeBrowser();
/// Emitted when authentication/deauthentication succeeded.
void linkingSucceeded();
/// Emitted when authentication/deauthentication failed.
void linkingFailed();
/// Emitted when a token refresh has been completed or failed.
void refreshFinished(QNetworkReply::NetworkError error);
// Property change signals
void grantFlowChanged();
void linkedChanged();
void tokenChanged();
void clientIdChanged();
void clientSecretChanged();
void scopeChanged();
void requestUrlChanged();
void tokenUrlChanged();
void refreshTokenUrlChanged();
void localPortChanged();
protected slots:
/// Handle verification response.
virtual void onVerificationReceived(QMap<QString, QString>);
/// Handle completion of a token request.
virtual void onTokenReplyFinished();
/// Handle failure of a token request.
virtual void onTokenReplyError(QNetworkReply::NetworkError error);
/// Handle completion of a refresh request.
virtual void onRefreshFinished();
/// Handle failure of a refresh request.
virtual void onRefreshError(QNetworkReply::NetworkError error);
protected:
/// Build HTTP request body.
QByteArray buildRequestBody(const QMap<QString, QString> &parameters);
/// Set authentication code.
void setCode(const QString &v);
/// Set refresh token.
void setRefreshToken(const QString &v);
/// Set token expiration time.
void setExpires(int v);
/// Set extra tokens found in OAuth response
void setExtraTokens(QVariantMap extraTokens);
protected:
QString clientId_;
QString clientSecret_;
QString scope_;
QString code_;
QString redirectUri_;
QString localhostPolicy_;
QUrl requestUrl_;
QUrl tokenUrl_;
QUrl refreshTokenUrl_;
QNetworkAccessManager *manager_;
O2ReplyServer *replyServer_;
O2ReplyList timedReplies_;
quint16 localPort_;
GrantFlow grantFlow_;
O2AbstractStore *store_;
QVariantMap extraTokens_;
};
#endif // O2_H

24
3rdparty/o2/src/o2abstractstore.h vendored Normal file
View file

@ -0,0 +1,24 @@
#ifndef O2ABSTRACTSTORE_H
#define O2ABSTRACTSTORE_H
#include <QObject>
#include <QString>
class O2AbstractStore: public QObject
{
Q_OBJECT
public:
explicit O2AbstractStore(QObject *parent = 0): QObject(parent) {
}
virtual ~O2AbstractStore() {
}
virtual QString value(const QString &key, const QString &defaultValue = QString()) = 0;
virtual void setValue(const QString &key, const QString &value) = 0;
};
#endif // O2ABSTRACTSTORE_H

92
3rdparty/o2/src/o2facebook.cpp vendored Normal file
View file

@ -0,0 +1,92 @@
#include <QDebug>
#include <QMap>
#include <QString>
#include <QStringList>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include "o2facebook.h"
#include "o2globals.h"
static const char *FbEndpoint = "https://graph.facebook.com/oauth/authorize?display=touch";
static const char *FbTokenUrl = "https://graph.facebook.com/oauth/access_token";
static const quint16 FbLocalPort = 1965;
const char FB_EXPIRES_KEY[] = "expires";
O2Facebook::O2Facebook(QObject *parent): O2(parent) {
setRequestUrl(FbEndpoint);
setTokenUrl(FbTokenUrl);
setLocalPort(FbLocalPort);
}
void O2Facebook::onVerificationReceived(const QMap<QString, QString> response) {
emit closeBrowser();
if (response.contains("error")) {
qWarning() << "O2Facebook::onVerificationReceived: Verification failed";
foreach (QString key, response.keys()) {
qWarning() << "O2Facebook::onVerificationReceived:" << key << response.value(key);
}
emit linkingFailed();
return;
}
// Save access code
setCode(response.value(O2_OAUTH2_CODE));
// Exchange access code for access/refresh tokens
QUrl url(tokenUrl_);
#if QT_VERSION < 0x050000
url.addQueryItem(O2_OAUTH2_CLIENT_ID, clientId_);
url.addQueryItem(O2_OAUTH2_CLIENT_SECRET, clientSecret_);
url.addQueryItem(O2_OAUTH2_SCOPE, scope_);
url.addQueryItem(O2_OAUTH2_CODE, code());
url.addQueryItem(O2_OAUTH2_REDIRECT_URI, redirectUri_);
#else
QUrlQuery query(url);
query.addQueryItem(O2_OAUTH2_CLIENT_ID, clientId_);
query.addQueryItem(O2_OAUTH2_CLIENT_SECRET, clientSecret_);
query.addQueryItem(O2_OAUTH2_SCOPE, scope_);
query.addQueryItem(O2_OAUTH2_CODE, code());
query.addQueryItem(O2_OAUTH2_REDIRECT_URI, redirectUri_);
url.setQuery(query);
#endif
QNetworkRequest tokenRequest(url);
QNetworkReply *tokenReply = manager_->get(tokenRequest);
timedReplies_.add(tokenReply);
connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection);
connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
}
void O2Facebook::onTokenReplyFinished() {
QNetworkReply *tokenReply = qobject_cast<QNetworkReply *>(sender());
if (tokenReply->error() == QNetworkReply::NoError) {
// Process reply
QByteArray replyData = tokenReply->readAll();
QMap<QString, QString> reply;
foreach (QString pair, QString(replyData).split("&")) {
QStringList kv = pair.split("=");
if (kv.length() == 2) {
reply.insert(kv[0], kv[1]);
}
}
// Interpret reply
setToken(reply.value(O2_OAUTH2_ACCESS_TOKEN, ""));
setExpires(reply.value(FB_EXPIRES_KEY).toInt());
setRefreshToken(reply.value(O2_OAUTH2_REFRESH_TOKEN, ""));
timedReplies_.remove(tokenReply);
emit linkedChanged();
emit tokenChanged();
emit linkingSucceeded();
}
}
void O2Facebook::unlink() {
O2::unlink();
// FIXME: Delete relevant cookies, too
}

21
3rdparty/o2/src/o2facebook.h vendored Normal file
View file

@ -0,0 +1,21 @@
#ifndef O2FACEBOOK_H
#define O2FACEBOOK_H
#include "o2.h"
/// Facebook's dialect of OAuth 2.0
class O2Facebook: public O2 {
Q_OBJECT
public:
explicit O2Facebook(QObject *parent = 0);
public slots:
Q_INVOKABLE void unlink();
protected slots:
void onVerificationReceived(QMap<QString, QString>);
virtual void onTokenReplyFinished();
};
#endif // O2FACEBOOK_H

13
3rdparty/o2/src/o2gft.cpp vendored Normal file
View file

@ -0,0 +1,13 @@
#include "o2gft.h"
static const char *GftScope = "https://www.googleapis.com/auth/fusiontables";
static const char *GftEndpoint = "https://accounts.google.com/o/oauth2/auth";
static const char *GftTokenUrl = "https://accounts.google.com/o/oauth2/token";
static const char *GftRefreshUrl = "https://accounts.google.com/o/oauth2/token";
O2Gft::O2Gft(QObject *parent): O2(parent) {
setRequestUrl(GftEndpoint);
setTokenUrl(GftTokenUrl);
setRefreshTokenUrl(GftRefreshUrl);
setScope(GftScope);
}

14
3rdparty/o2/src/o2gft.h vendored Normal file
View file

@ -0,0 +1,14 @@
#ifndef O2GFT_H
#define O2GFT_H
#include "o2.h"
/// Google Fusion Tables' dialect of OAuth 2.0
class O2Gft: public O2 {
Q_OBJECT
public:
explicit O2Gft(QObject *parent = 0);
};
#endif // O2GFT_H

54
3rdparty/o2/src/o2globals.h vendored Normal file
View file

@ -0,0 +1,54 @@
#ifndef O2GLOBALS_H
#define O2GLOBALS_H
// Common constants
const char O2_ENCRYPTION_KEY[] = "12345678";
const char O2_CALLBACK_URL[] = "http://127.0.0.1:%1/";
const char O2_MIME_TYPE_XFORM[] = "application/x-www-form-urlencoded";
// QSettings key names
const char O2_KEY_TOKEN[] = "token.%1";
const char O2_KEY_TOKEN_SECRET[] = "tokensecret.%1";
const char O2_KEY_CODE[] = "code.%1";
const char O2_KEY_EXPIRES[] = "expires.%1";
const char O2_KEY_REFRESH_TOKEN[] = "refreshtoken.%1";
// OAuth 1/1.1 Request Parameters
const char O2_OAUTH_CALLBACK[] = "oauth_callback";
const char O2_OAUTH_CONSUMER_KEY[] = "oauth_consumer_key";
const char O2_OAUTH_NONCE[] = "oauth_nonce";
const char O2_OAUTH_SIGNATURE[] = "oauth_signature";
const char O2_OAUTH_SIGNATURE_METHOD[] = "oauth_signature_method";
const char O2_OAUTH_TIMESTAMP[] = "oauth_timestamp";
const char O2_OAUTH_VERSION[] = "oauth_version";
// OAuth 1/1.1 Response Parameters
const char O2_OAUTH_TOKEN[] = "oauth_token";
const char O2_OAUTH_TOKEN_SECRET[] = "oauth_token_secret";
const char O2_OAUTH_CALLBACK_CONFIRMED[] = "oauth_callback_confirmed";
const char O2_OAUTH_VERFIER[] = "oauth_verifier";
// OAuth 2 Request Parameters
const char O2_OAUTH2_RESPONSE_TYPE[] = "response_type";
const char O2_OAUTH2_CLIENT_ID[] = "client_id";
const char O2_OAUTH2_CLIENT_SECRET[] = "client_secret";
const char O2_OAUTH2_REDIRECT_URI[] = "redirect_uri";
const char O2_OAUTH2_SCOPE[] = "scope";
const char O2_OAUTH2_CODE[] = "code";
const char O2_OAUTH2_TOKEN[] = "token";
const char O2_OAUTH2_GRANT_TYPE[] = "grant_type";
// OAuth 2 Response Parameters
const char O2_OAUTH2_ACCESS_TOKEN[] = "access_token";
const char O2_OAUTH2_REFRESH_TOKEN[] = "refresh_token";
const char O2_OAUTH2_EXPIRES_IN[] = "expires_in";
// OAuth signature types
const char O2_SIGNATURE_TYPE_HMAC_SHA1[] = "HMAC-SHA1";
const char O2_SIGNATURE_TYPE_PLAINTEXT[] = "PLAINTEXT";
// Parameter values
const char O2_AUTHORIZATION_CODE[] = "authorization_code";
// Standard HTTP headers
const char O2_HTTP_AUTHORIZATION_HEADER[] = "Authorization";
#endif // O2GLOBALS_H

17
3rdparty/o2/src/o2hubic.cpp vendored Normal file
View file

@ -0,0 +1,17 @@
#include "o2hubic.h"
#include "o2globals.h"
#include "o2replyserver.h"
#include <QHostAddress>
static const char *HubicScope = "usage.r,account.r,getAllLinks.r,credentials.r,activate.w,links.drw";
static const char *HubicEndpoint = "https://api.hubic.com/oauth/auth/";
static const char *HubicTokenUrl = "https://api.hubic.com/oauth/token/";
static const char *HubicRefreshUrl = "https://api.hubic.com/oauth/token/";
O2Hubic::O2Hubic(QObject *parent): O2(parent) {
setRequestUrl(HubicEndpoint);
setTokenUrl(HubicTokenUrl);
setRefreshTokenUrl(HubicRefreshUrl);
setScope(HubicScope);
setLocalhostPolicy("http://localhost:%1/");
}

17
3rdparty/o2/src/o2hubic.h vendored Normal file
View file

@ -0,0 +1,17 @@
#ifndef O2HUBIC_H
#define O2HUBIC_H
#include "o2.h"
/// Hubic's dialect of OAuth 2.0
class O2Hubic: public O2 {
Q_OBJECT
public:
/// Constructor.
/// @param parent Parent object.
explicit O2Hubic(QObject *parent = 0);
};
#endif // O2_HUBIC

46
3rdparty/o2/src/o2reply.cpp vendored Normal file
View file

@ -0,0 +1,46 @@
#include <QTimer>
#include <QNetworkReply>
#include "o2reply.h"
O2Reply::O2Reply(QNetworkReply *r, int timeOut, QObject *parent): QTimer(parent), reply(r) {
setSingleShot(true);
connect(this, SIGNAL(error(QNetworkReply::NetworkError)), reply, SIGNAL(error(QNetworkReply::NetworkError)), Qt::QueuedConnection);
connect(this, SIGNAL(timeout()), this, SLOT(onTimeOut()), Qt::QueuedConnection);
start(timeOut);
}
void O2Reply::onTimeOut() {
emit error(QNetworkReply::TimeoutError);
}
O2ReplyList::~O2ReplyList() {
foreach (O2Reply *timedReply, replies_) {
delete timedReply;
}
}
void O2ReplyList::add(QNetworkReply *reply) {
add(new O2Reply(reply));
}
void O2ReplyList::add(O2Reply *reply) {
replies_.append(reply);
}
void O2ReplyList::remove(QNetworkReply *reply) {
O2Reply *o2Reply = find(reply);
if (o2Reply) {
o2Reply->stop();
(void)replies_.removeOne(o2Reply);
}
}
O2Reply *O2ReplyList::find(QNetworkReply *reply) {
foreach (O2Reply *timedReply, replies_) {
if (timedReply->reply == reply) {
return timedReply;
}
}
return 0;
}

53
3rdparty/o2/src/o2reply.h vendored Normal file
View file

@ -0,0 +1,53 @@
#ifndef O2TIMEDREPLYLIST_H
#define O2TIMEDREPLYLIST_H
#include <QList>
#include <QTimer>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QByteArray>
/// A network request/reply pair that can time out.
class O2Reply: public QTimer {
Q_OBJECT
public:
O2Reply(QNetworkReply *reply, int timeOut = 60 * 1000, QObject *parent = 0);
signals:
void error(QNetworkReply::NetworkError);
public slots:
/// When time out occurs, the QNetworkReply's error() signal is triggered.
void onTimeOut();
public:
QNetworkReply *reply;
};
/// List of O2Replies.
class O2ReplyList {
public:
/// Destructor.
/// Deletes all O2Reply instances in the list.
virtual ~O2ReplyList();
/// Create a new O2Reply from a QNetworkReply, and add it to this list.
void add(QNetworkReply *reply);
/// Add an O2Reply to the list, while taking ownership of it.
void add(O2Reply *reply);
/// Remove item from the list that corresponds to a QNetworkReply.
void remove(QNetworkReply *reply);
/// Find an O2Reply in the list, corresponding to a QNetworkReply.
/// @return Matching O2Reply or NULL.
O2Reply *find(QNetworkReply *reply);
protected:
QList<O2Reply *> replies_;
};
#endif // O2TIMEDREPLYLIST_H

79
3rdparty/o2/src/o2replyserver.cpp vendored Normal file
View file

@ -0,0 +1,79 @@
#include <QTcpServer>
#include <QTcpSocket>
#include <QByteArray>
#include <QString>
#include <QMap>
#include <QPair>
#include <QStringList>
#include <QUrl>
#include <QDebug>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include "o2replyserver.h"
#define trace() if (1) qDebug()
// #define trace() if (0) qDebug()
O2ReplyServer::O2ReplyServer(QObject *parent): QTcpServer(parent) {
connect(this, SIGNAL(newConnection()), this, SLOT(onIncomingConnection()));
}
O2ReplyServer::~O2ReplyServer() {
}
void O2ReplyServer::onIncomingConnection() {
QTcpSocket* socket = nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(onBytesReady()), Qt::UniqueConnection);
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
}
void O2ReplyServer::onBytesReady() {
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
if (!socket) {
return;
}
QByteArray reply;
QByteArray content;
content.append("<HTML></HTML>");
reply.append("HTTP/1.0 200 OK \r\n");
reply.append("Content-Type: text/html; charset=\"utf-8\"\r\n");
reply.append(QString("Content-Length: %1\r\n\r\n").arg(content.size()));
reply.append(content);
socket->write(reply);
QByteArray data = socket->readAll();
QMap<QString, QString> queryParams = parseQueryParams(&data);
socket->disconnectFromHost();
close();
emit verificationReceived(queryParams);
}
QMap<QString, QString> O2ReplyServer::parseQueryParams(QByteArray *data) {
trace() << "O2ReplyServer::parseQueryParams";
QString splitGetLine = QString(*data).split("\r\n").first();
splitGetLine.remove("GET ");
splitGetLine.remove("HTTP/1.1");
splitGetLine.remove("\r\n");
splitGetLine.prepend("http://localhost");
QUrl getTokenUrl(splitGetLine);
QList< QPair<QString, QString> > tokens;
#if QT_VERSION < 0x050000
tokens = getTokenUrl.queryItems();
#else
QUrlQuery query(getTokenUrl);
tokens = query.queryItems();
#endif
QMultiMap<QString, QString> queryParams;
QPair<QString, QString> tokenPair;
foreach (tokenPair, tokens) {
// FIXME: We are decoding key and value again. This helps with Google OAuth, but is it mandated by the standard?
QString key = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.first.trimmed()));
QString value = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.second.trimmed()));
queryParams.insert(key, value);
}
return queryParams;
}

26
3rdparty/o2/src/o2replyserver.h vendored Normal file
View file

@ -0,0 +1,26 @@
#ifndef O2REPLYSERVER_H
#define O2REPLYSERVER_H
#include <QTcpServer>
#include <QMap>
#include <QByteArray>
#include <QString>
/// HTTP server to process authentication response.
class O2ReplyServer: public QTcpServer {
Q_OBJECT
public:
explicit O2ReplyServer(QObject *parent = 0);
~O2ReplyServer();
signals:
void verificationReceived(QMap<QString, QString>);
public slots:
void onIncomingConnection();
void onBytesReady();
QMap<QString, QString> parseQueryParams(QByteArray *data);
};
#endif // O2REPLYSERVER_H

193
3rdparty/o2/src/o2requestor.cpp vendored Normal file
View file

@ -0,0 +1,193 @@
#include <QDebug>
#include <QTimer>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include "o2requestor.h"
#include "o2.h"
#include "o2globals.h"
#define trace() if (1) qDebug()
// define trace() if (0) qDebug()
O2Requestor::O2Requestor(QNetworkAccessManager *manager, O2 *authenticator, QObject *parent): QObject(parent), reply_(NULL), status_(Idle) {
manager_ = manager;
authenticator_ = authenticator;
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
connect(authenticator, SIGNAL(refreshFinished(QNetworkReply::NetworkError)), this, SLOT(onRefreshFinished(QNetworkReply::NetworkError)), Qt::QueuedConnection);
}
O2Requestor::~O2Requestor() {
}
int O2Requestor::get(const QNetworkRequest &req) {
if (-1 == setup(req, QNetworkAccessManager::GetOperation)) {
return -1;
}
reply_ = manager_->get(request_);
timedReplies_.add(reply_);
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection);
return id_;
}
int O2Requestor::post(const QNetworkRequest &req, const QByteArray &data) {
if (-1 == setup(req, QNetworkAccessManager::PostOperation)) {
return -1;
}
data_ = data;
reply_ = manager_->post(request_, data_);
timedReplies_.add(reply_);
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection);
connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64)));
return id_;
}
int O2Requestor::put(const QNetworkRequest &req, const QByteArray &data) {
if (-1 == setup(req, QNetworkAccessManager::PutOperation)) {
return -1;
}
data_ = data;
reply_ = manager_->put(request_, data_);
timedReplies_.add(reply_);
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection);
connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64)));
return id_;
}
void O2Requestor::onRefreshFinished(QNetworkReply::NetworkError error) {
if (status_ != Requesting) {
qWarning() << "O2Requestor::onRefreshFinished: No pending request";
return;
}
if (QNetworkReply::NoError == error) {
QTimer::singleShot(100, this, SLOT(retry()));
} else {
error_ = error;
QTimer::singleShot(10, this, SLOT(finish()));
}
}
void O2Requestor::onRequestFinished() {
QNetworkReply *senderReply = qobject_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError error = senderReply->error();
if (status_ == Idle) {
return;
}
if (reply_ != senderReply) {
return;
}
if (error == QNetworkReply::NoError) {
QTimer::singleShot(10, this, SLOT(finish()));
}
}
void O2Requestor::onRequestError(QNetworkReply::NetworkError error) {
qWarning() << "O2Requestor::onRequestError: Error" << (int)error;
if (status_ == Idle) {
return;
}
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
return;
}
int httpStatus = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qWarning() << "O2Requestor::onRequestError: HTTP status" << httpStatus << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
trace() << reply_->readAll();
if ((status_ == Requesting) && (httpStatus == 401)) {
// Call O2::refresh. Note the O2 instance might live in a different thread
if (QMetaObject::invokeMethod(authenticator_, "refresh")) {
return;
}
qCritical() << "O2Requestor::onRequestError: Invoking remote refresh failed";
}
error_ = error;
QTimer::singleShot(10, this, SLOT(finish()));
}
void O2Requestor::onUploadProgress(qint64 uploaded, qint64 total) {
if (status_ == Idle) {
qWarning() << "O2Requestor::onUploadProgress: No pending request";
return;
}
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
return;
}
emit uploadProgress(id_, uploaded, total);
}
int O2Requestor::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation) {
static int currentId;
QUrl url;
if (status_ != Idle) {
qWarning() << "O2Requestor::setup: Another request pending";
return -1;
}
request_ = req;
operation_ = operation;
id_ = currentId++;
url_ = url = req.url();
#if QT_VERSION < 0x050000
url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token());
#else
QUrlQuery query(url);
query.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token());
url.setQuery(query);
#endif
request_.setUrl(url);
status_ = Requesting;
error_ = QNetworkReply::NoError;
return id_;
}
void O2Requestor::finish() {
QByteArray data;
if (status_ == Idle) {
qWarning() << "O2Requestor::finish: No pending request";
return;
}
data = reply_->readAll();
status_ = Idle;
timedReplies_.remove(reply_);
reply_->disconnect(this);
reply_->deleteLater();
emit finished(id_, error_, data);
}
void O2Requestor::retry() {
if (status_ != Requesting) {
qWarning() << "O2Requestor::retry: No pending request";
return;
}
timedReplies_.remove(reply_);
reply_->disconnect(this);
reply_->deleteLater();
QUrl url = url_;
#if QT_VERSION < 0x050000
url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token());
#else
QUrlQuery query(url);
query.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token());
url.setQuery(query);
#endif
request_.setUrl(url);
status_ = ReRequesting;
switch (operation_) {
case QNetworkAccessManager::GetOperation:
reply_ = manager_->get(request_);
break;
case QNetworkAccessManager::PostOperation:
reply_ = manager_->post(request_, data_);
break;
default:
reply_ = manager_->put(request_, data_);
}
timedReplies_.add(reply_);
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection);
connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64)));
}

82
3rdparty/o2/src/o2requestor.h vendored Normal file
View file

@ -0,0 +1,82 @@
#ifndef O2REQUESTOR_H
#define O2REQUESTOR_H
#include <QObject>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QByteArray>
#include "o2reply.h"
class O2;
/// Makes authenticated requests.
class O2Requestor: public QObject {
Q_OBJECT
public:
explicit O2Requestor(QNetworkAccessManager *manager, O2 *authenticator, QObject *parent = 0);
~O2Requestor();
public slots:
/// Make a GET request.
/// @return Request ID or -1 if there are too many requests in the queue.
int get(const QNetworkRequest &req);
/// Make a POST request.
/// @return Request ID or -1 if there are too many requests in the queue.
int post(const QNetworkRequest &req, const QByteArray &data);
/// Make a PUT request.
/// @return Request ID or -1 if there are too many requests in the queue.
int put(const QNetworkRequest &req, const QByteArray &data);
signals:
/// Emitted when a request has been completed or failed.
void finished(int id, QNetworkReply::NetworkError error, QByteArray data);
/// Emitted when an upload has progressed.
void uploadProgress(int id, qint64 bytesSent, qint64 bytesTotal);
protected slots:
/// Handle refresh completion.
void onRefreshFinished(QNetworkReply::NetworkError error);
/// Handle request finished.
void onRequestFinished();
/// Handle request error.
void onRequestError(QNetworkReply::NetworkError error);
/// Re-try request (after successful token refresh).
void retry();
/// Finish the request, emit finished() signal.
void finish();
/// Handle upload progress.
void onUploadProgress(qint64 uploaded, qint64 total);
protected:
int setup(const QNetworkRequest &request, QNetworkAccessManager::Operation operation);
enum Status {
Idle, Requesting, ReRequesting
};
QNetworkAccessManager *manager_;
O2 *authenticator_;
QNetworkRequest request_;
QByteArray data_;
QNetworkReply *reply_;
Status status_;
int id_;
QNetworkAccessManager::Operation operation_;
QUrl url_;
O2ReplyList timedReplies_;
QNetworkReply::NetworkError error_;
};
#endif // O2REQUESTOR_H

47
3rdparty/o2/src/o2settingsstore.cpp vendored Normal file
View file

@ -0,0 +1,47 @@
#include <QCryptographicHash>
#include <QByteArray>
#include "o2settingsstore.h"
static quint64 getHash(const QString &encryptionKey) {
return QCryptographicHash::hash(encryptionKey.toLatin1(), QCryptographicHash::Sha1).toULongLong();
}
O2SettingsStore::O2SettingsStore(const QString &encryptionKey, QObject *parent):
O2AbstractStore(parent), crypt_(getHash(encryptionKey)) {
settings_ = new QSettings(this);
}
O2SettingsStore::O2SettingsStore(QSettings *settings, const QString &encryptionKey, QObject *parent):
O2AbstractStore(parent), crypt_(getHash(encryptionKey)) {
settings_ = settings;
settings_->setParent(this);
}
O2SettingsStore::~O2SettingsStore() {
}
QString O2SettingsStore::groupKey() const {
return groupKey_;
}
void O2SettingsStore::setGroupKey(const QString &groupKey) {
if (groupKey_ == groupKey) {
return;
}
groupKey_ = groupKey;
emit groupKeyChanged();
}
QString O2SettingsStore::value(const QString &key, const QString &defaultValue) {
QString fullKey = groupKey_.isEmpty() ? key : (groupKey_ + '/' + key);
if (!settings_->contains(fullKey)) {
return defaultValue;
}
return crypt_.decryptToString(settings_->value(fullKey).toString());
}
void O2SettingsStore::setValue(const QString &key, const QString &value) {
QString fullKey = groupKey_.isEmpty() ? key : (groupKey_ + '/' + key);
settings_->setValue(fullKey, crypt_.encryptToString(value));
}

39
3rdparty/o2/src/o2settingsstore.h vendored Normal file
View file

@ -0,0 +1,39 @@
#ifndef O2SETTINGSSTORE_H
#define O2SETTINGSSTORE_H
#include <QSettings>
#include <QString>
#include "o2abstractstore.h"
#include "simplecrypt.h"
class O2SettingsStore: public O2AbstractStore
{
Q_OBJECT
public:
explicit O2SettingsStore(const QString &encryptionKey, QObject *parent = 0);
explicit O2SettingsStore(QSettings *settings, const QString &encryptionKey, QObject *parent = 0);
~O2SettingsStore();
Q_PROPERTY(QString groupKey READ groupKey WRITE setGroupKey NOTIFY groupKeyChanged)
QString groupKey() const;
void setGroupKey(const QString &groupKey);
QString value(const QString &key, const QString &defaultValue = QString());
void setValue(const QString &key, const QString &value);
signals:
// Property change signals
void groupKeyChanged();
protected:
QSettings* settings_;
QString groupKey_;
SimpleCrypt crypt_;
};
#endif // O2SETTINGSSTORE_H

121
3rdparty/o2/src/o2skydrive.cpp vendored Normal file
View file

@ -0,0 +1,121 @@
#include <QDebug>
#include <QDateTime>
#include <QMap>
#include <QString>
#include <QStringList>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include "o2skydrive.h"
#include "o2globals.h"
#define trace() if (1) qDebug()
// define trace() if (0) qDebug()
O2Skydrive::O2Skydrive(QObject *parent): O2(parent) {
setRequestUrl("https://login.live.com/oauth20_authorize.srf");
setTokenUrl("https://login.live.com/oauth20_token.srf");
setRefreshTokenUrl("https://login.live.com/oauth20_token.srf");
}
void O2Skydrive::link() {
trace() << "O2::link";
if (linked()) {
trace() << "Linked already";
return;
}
redirectUri_ = QString("https://login.live.com/oauth20_desktop.srf");
// Assemble intial authentication URL
QList<QPair<QString, QString> > parameters;
parameters.append(qMakePair(QString(O2_OAUTH2_RESPONSE_TYPE), (grantFlow_ == GrantFlowAuthorizationCode) ? QString(O2_OAUTH2_CODE) : QString(O2_OAUTH2_TOKEN)));
parameters.append(qMakePair(QString(O2_OAUTH2_CLIENT_ID), clientId_));
parameters.append(qMakePair(QString(O2_OAUTH2_REDIRECT_URI), redirectUri_));
parameters.append(qMakePair(QString(O2_OAUTH2_SCOPE), scope_));
// Show authentication URL with a web browser
QUrl url(requestUrl_);
#if QT_VERSION < 0x050000
url.setQueryItems(parameters);
#else
QUrlQuery query(url);
query.setQueryItems(parameters);
url.setQuery(query);
#endif
emit openBrowser(url);
}
void O2Skydrive::redirected(const QUrl &url) {
trace() << "O2::redirected" << url;
emit closeBrowser();
if (grantFlow_ == GrantFlowAuthorizationCode) {
// Get access code
QString urlCode;
#if QT_VERSION < 0x050000
urlCode = url.queryItemValue(O2_OAUTH2_CODE);
#else
QUrlQuery query(url);
urlCode = query.queryItemValue(O2_OAUTH2_CODE);
#endif
if (urlCode.isEmpty()) {
trace() << " Code not received";
emit linkingFailed();
return;
}
setCode(urlCode);
// Exchange access code for access/refresh tokens
QNetworkRequest tokenRequest(tokenUrl_);
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QMap<QString, QString> parameters;
parameters.insert(O2_OAUTH2_CODE, code());
parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_);
parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_);
parameters.insert(O2_OAUTH2_REDIRECT_URI, redirectUri_);
parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_AUTHORIZATION_CODE);
QByteArray data = buildRequestBody(parameters);
QNetworkReply *tokenReply = manager_->post(tokenRequest, data);
timedReplies_.add(tokenReply);
connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection);
connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
} else {
// Get access token
QString urlToken = "";
QString urlRefreshToken = "";
int urlExpiresIn = 0;
QStringList parts = url.toString().split("#");
if (parts.length() > 1) {
foreach (QString item, parts[1].split("&")) {
int index = item.indexOf("=");
if (index == -1) {
continue;
}
QString key = item.left(index);
QString value = item.mid(index + 1);
trace() << "" << key;
if (key == O2_OAUTH2_ACCESS_TOKEN) {
urlToken = value;
} else if (key == O2_OAUTH2_EXPIRES_IN) {
urlExpiresIn = value.toInt();
} else if (key == O2_OAUTH2_REFRESH_TOKEN) {
urlRefreshToken = value;
}
}
}
setToken(urlToken);
setRefreshToken(urlRefreshToken);
setExpires(QDateTime::currentMSecsSinceEpoch() / 1000 + urlExpiresIn);
if (urlToken.isEmpty()) {
emit linkingFailed();
} else {
emit linkedChanged();
emit linkingSucceeded();
}
}
}

18
3rdparty/o2/src/o2skydrive.h vendored Normal file
View file

@ -0,0 +1,18 @@
#ifndef O2SKYDRIVE_H
#define O2SKYDRIVE_H
#include "o2.h"
/// Skydrive's dialect of OAuth 2.0
class O2Skydrive: public O2 {
Q_OBJECT
public:
explicit O2Skydrive(QObject *parent = 0);
public slots:
Q_INVOKABLE void link();
Q_INVOKABLE virtual void redirected(const QUrl &url);
};
#endif // O2SKYDRIVE_H

71
3rdparty/o2/src/oxtwitter.cpp vendored Normal file
View file

@ -0,0 +1,71 @@
#include <QDateTime>
#include <QDebug>
#include "oxtwitter.h"
#include "o2globals.h"
#define trace() if (1) qDebug()
const char XAUTH_USERNAME[] = "x_auth_username";
const char XAUTH_PASSWORD[] = "x_auth_password";
const char XAUTH_MODE[] = "x_auth_mode";
const char XAUTH_MODE_VALUE[] = "client_auth";
OXTwitter::OXTwitter(QObject *parent): O1Twitter(parent) {
}
QString OXTwitter::username() {
return username_;
}
void OXTwitter::setUsername(const QString &username) {
username_ = username;
emit usernameChanged();
}
QString OXTwitter::password() {
return password_;
}
void OXTwitter::setPassword(const QString &password) {
password_ = password;
emit passwordChanged();
}
void OXTwitter::link() {
trace() << "OXTwitter::link";
if (linked()) {
trace() << "Linked already";
return;
}
if (username_.isEmpty() || password_.isEmpty()) {
qWarning() << "Error: XAuth parameters not set. Aborting!";
return;
}
// prepare XAuth parameters
xAuthParams_.append(O1RequestParameter(QByteArray(XAUTH_USERNAME), username_.toLatin1()));
xAuthParams_.append(O1RequestParameter(QByteArray(XAUTH_PASSWORD), password_.toLatin1()));
xAuthParams_.append(O1RequestParameter(QByteArray(XAUTH_MODE), QByteArray(XAUTH_MODE_VALUE)));
QList<O1RequestParameter> oauthParams;
oauthParams.append(O1RequestParameter(O2_OAUTH_SIGNATURE_METHOD, O2_SIGNATURE_TYPE_HMAC_SHA1));
oauthParams.append(O1RequestParameter(O2_OAUTH_CONSUMER_KEY, clientId().toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_VERSION, "1.0"));
oauthParams.append(O1RequestParameter(O2_OAUTH_TIMESTAMP, QString::number(QDateTime::currentDateTimeUtc().toTime_t()).toLatin1()));
oauthParams.append(O1RequestParameter(O2_OAUTH_NONCE, nonce()));
oauthParams.append(O1RequestParameter(O2_OAUTH_TOKEN, QByteArray("")));
oauthParams.append(O1RequestParameter(O2_OAUTH_VERFIER, QByteArray("")));
QByteArray signature = sign(oauthParams, xAuthParams_, accessTokenUrl(), QNetworkAccessManager::PostOperation, clientSecret(), "");
oauthParams.append(O1RequestParameter(O2_OAUTH_SIGNATURE, signature));
// Post request
QNetworkRequest request(accessTokenUrl());
request.setRawHeader(O2_HTTP_AUTHORIZATION_HEADER, buildAuthorizationHeader(oauthParams));
request.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM);
QNetworkReply *reply = manager_->post(request, createQueryParams(xAuthParams_));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenExchangeError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), this, SLOT(onTokenExchangeFinished()));
}

36
3rdparty/o2/src/oxtwitter.h vendored Normal file
View file

@ -0,0 +1,36 @@
#ifndef OXTWITTER_H
#define OXTWITTER_H
#include "o1twitter.h"
class OXTwitter: public O1Twitter {
Q_OBJECT
public:
explicit OXTwitter(QObject *parent = 0);
/// Twitter XAuth login parameters
/// XAuth Username
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)
QString username();
void setUsername(const QString &username);
/// XAuth Password
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
QString password();
void setPassword(const QString &username);
public slots:
/// Authenticate.
Q_INVOKABLE virtual void link();
signals:
void usernameChanged();
void passwordChanged();
private:
QList<O1RequestParameter> xAuthParams_;
QString username_;
QString password_;
};
#endif // OXTWITTER_H

254
3rdparty/o2/src/simplecrypt.cpp vendored Normal file
View file

@ -0,0 +1,254 @@
/*
Copyright (c) 2011, Andre Somers
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Rathenau Instituut, Andre Somers nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "simplecrypt.h"
#include <QByteArray>
#include <QtDebug>
#include <QtGlobal>
#include <QDateTime>
#include <QCryptographicHash>
#include <QDataStream>
SimpleCrypt::SimpleCrypt():
m_key(0),
m_compressionMode(CompressionAuto),
m_protectionMode(ProtectionChecksum),
m_lastError(ErrorNoError)
{
qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF));
}
SimpleCrypt::SimpleCrypt(quint64 key):
m_key(key),
m_compressionMode(CompressionAuto),
m_protectionMode(ProtectionChecksum),
m_lastError(ErrorNoError)
{
qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF));
splitKey();
}
void SimpleCrypt::setKey(quint64 key)
{
m_key = key;
splitKey();
}
void SimpleCrypt::splitKey()
{
m_keyParts.clear();
m_keyParts.resize(8);
for (int i=0;i<8;i++) {
quint64 part = m_key;
for (int j=i; j>0; j--)
part = part >> 8;
part = part & 0xff;
m_keyParts[i] = static_cast<char>(part);
}
}
QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext)
{
QByteArray plaintextArray = plaintext.toUtf8();
return encryptToByteArray(plaintextArray);
}
QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext)
{
if (m_keyParts.isEmpty()) {
qWarning() << "No key set.";
m_lastError = ErrorNoKeySet;
return QByteArray();
}
QByteArray ba = plaintext;
CryptoFlags flags = CryptoFlagNone;
if (m_compressionMode == CompressionAlways) {
ba = qCompress(ba, 9); //maximum compression
flags |= CryptoFlagCompression;
} else if (m_compressionMode == CompressionAuto) {
QByteArray compressed = qCompress(ba, 9);
if (compressed.count() < ba.count()) {
ba = compressed;
flags |= CryptoFlagCompression;
}
}
QByteArray integrityProtection;
if (m_protectionMode == ProtectionChecksum) {
flags |= CryptoFlagChecksum;
QDataStream s(&integrityProtection, QIODevice::WriteOnly);
s << qChecksum(ba.constData(), ba.length());
} else if (m_protectionMode == ProtectionHash) {
flags |= CryptoFlagHash;
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(ba);
integrityProtection += hash.result();
}
//prepend a random char to the string
char randomChar = char(qrand() & 0xFF);
ba = randomChar + integrityProtection + ba;
int pos(0);
char lastChar(0);
int cnt = ba.count();
while (pos < cnt) {
ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar;
lastChar = ba.at(pos);
++pos;
}
QByteArray resultArray;
resultArray.append(char(0x03)); //version for future updates to algorithm
resultArray.append(char(flags)); //encryption flags
resultArray.append(ba);
m_lastError = ErrorNoError;
return resultArray;
}
QString SimpleCrypt::encryptToString(const QString& plaintext)
{
QByteArray plaintextArray = plaintext.toUtf8();
QByteArray cypher = encryptToByteArray(plaintextArray);
QString cypherString = QString::fromLatin1(cypher.toBase64());
return cypherString;
}
QString SimpleCrypt::encryptToString(QByteArray plaintext)
{
QByteArray cypher = encryptToByteArray(plaintext);
QString cypherString = QString::fromLatin1(cypher.toBase64());
return cypherString;
}
QString SimpleCrypt::decryptToString(const QString &cyphertext)
{
QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1());
QByteArray plaintextArray = decryptToByteArray(cyphertextArray);
QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size());
return plaintext;
}
QString SimpleCrypt::decryptToString(QByteArray cypher)
{
QByteArray ba = decryptToByteArray(cypher);
QString plaintext = QString::fromUtf8(ba, ba.size());
return plaintext;
}
QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext)
{
QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1());
QByteArray ba = decryptToByteArray(cyphertextArray);
return ba;
}
QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher)
{
if (m_keyParts.isEmpty()) {
qWarning() << "No key set.";
m_lastError = ErrorNoKeySet;
return QByteArray();
}
if (!cypher.length()) {
m_lastError = ErrorUnknownVersion;
return QByteArray();
}
QByteArray ba = cypher;
char version = ba.at(0);
if (version !=3) { //we only work with version 3
m_lastError = ErrorUnknownVersion;
qWarning() << "Invalid version or not a cyphertext.";
return QByteArray();
}
CryptoFlags flags = CryptoFlags(ba.at(1));
ba = ba.mid(2);
int pos(0);
int cnt(ba.count());
char lastChar = 0;
while (pos < cnt) {
char currentChar = ba[pos];
ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8);
lastChar = currentChar;
++pos;
}
ba = ba.mid(1); //chop off the random number at the start
bool integrityOk(true);
if (flags.testFlag(CryptoFlagChecksum)) {
if (ba.length() < 2) {
m_lastError = ErrorIntegrityFailed;
return QByteArray();
}
quint16 storedChecksum;
{
QDataStream s(&ba, QIODevice::ReadOnly);
s >> storedChecksum;
}
ba = ba.mid(2);
quint16 checksum = qChecksum(ba.constData(), ba.length());
integrityOk = (checksum == storedChecksum);
} else if (flags.testFlag(CryptoFlagHash)) {
if (ba.length() < 20) {
m_lastError = ErrorIntegrityFailed;
return QByteArray();
}
QByteArray storedHash = ba.left(20);
ba = ba.mid(20);
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(ba);
integrityOk = (hash.result() == storedHash);
}
if (!integrityOk) {
m_lastError = ErrorIntegrityFailed;
return QByteArray();
}
if (flags.testFlag(CryptoFlagCompression))
ba = qUncompress(ba);
m_lastError = ErrorNoError;
return ba;
}

225
3rdparty/o2/src/simplecrypt.h vendored Normal file
View file

@ -0,0 +1,225 @@
/*
Copyright (c) 2011, Andre Somers
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Rathenau Instituut, Andre Somers nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SIMPLECRYPT_H
#define SIMPLECRYPT_H
#include <QString>
#include <QVector>
#include <QFlags>
/**
@short Simple encryption and decryption of strings and byte arrays
This class provides a simple implementation of encryption and decryption
of strings and byte arrays.
@warning The encryption provided by this class is NOT strong encryption. It may
help to shield things from curious eyes, but it will NOT stand up to someone
determined to break the encryption. Don't say you were not warned.
The class uses a 64 bit key. Simply create an instance of the class, set the key,
and use the encryptToString() method to calculate an encrypted version of the input string.
To decrypt that string again, use an instance of SimpleCrypt initialized with
the same key, and call the decryptToString() method with the encrypted string. If the key
matches, the decrypted version of the string will be returned again.
If you do not provide a key, or if something else is wrong, the encryption and
decryption function will return an empty string or will return a string containing nonsense.
lastError() will return a value indicating if the method was succesful, and if not, why not.
SimpleCrypt is prepared for the case that the encryption and decryption
algorithm is changed in a later version, by prepending a version identifier to the cypertext.
*/
class SimpleCrypt
{
public:
/**
CompressionMode describes if compression will be applied to the data to be
encrypted.
*/
enum CompressionMode {
CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */
CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */
CompressionNever /*!< Never apply compression. */
};
/**
IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data
or wrong decryption keys.
Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This
increases the length of the resulting cypertext, but makes it possible to check if the plaintext
appears to be valid after decryption.
*/
enum IntegrityProtectionMode {
ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */
ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */
ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */
};
/**
Error describes the type of error that occured.
*/
enum Error {
ErrorNoError, /*!< No error occurred. */
ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */
ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */
ErrorIntegrityFailed /*!< The integrity check of the data failed. Perhaps the wrong key was used. */
};
/**
Constructor.
Constructs a SimpleCrypt instance without a valid key set on it.
*/
SimpleCrypt();
/**
Constructor.
Constructs a SimpleCrypt instance and initializes it with the given @arg key.
*/
explicit SimpleCrypt(quint64 key);
/**
(Re-) initializes the key with the given @arg key.
*/
void setKey(quint64 key);
/**
Returns true if SimpleCrypt has been initialized with a key.
*/
bool hasKey() const {return !m_keyParts.isEmpty();}
/**
Sets the compression mode to use when encrypting data. The default mode is Auto.
Note that decryption is not influenced by this mode, as the decryption recognizes
what mode was used when encrypting.
*/
void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;}
/**
Returns the CompressionMode that is currently in use.
*/
CompressionMode compressionMode() const {return m_compressionMode;}
/**
Sets the integrity mode to use when encrypting data. The default mode is Checksum.
Note that decryption is not influenced by this mode, as the decryption recognizes
what mode was used when encrypting.
*/
void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;}
/**
Returns the IntegrityProtectionMode that is currently in use.
*/
IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;}
/**
Returns the last error that occurred.
*/
Error lastError() const {return m_lastError;}
/**
Encrypts the @arg plaintext string with the key the class was initialized with, and returns
a cyphertext the result. The result is a base64 encoded version of the binary array that is the
actual result of the string, so it can be stored easily in a text format.
*/
QString encryptToString(const QString& plaintext) ;
/**
Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns
a cyphertext the result. The result is a base64 encoded version of the binary array that is the
actual result of the encryption, so it can be stored easily in a text format.
*/
QString encryptToString(QByteArray plaintext) ;
/**
Encrypts the @arg plaintext string with the key the class was initialized with, and returns
a binary cyphertext in a QByteArray the result.
This method returns a byte array, that is useable for storing a binary format. If you need
a string you can store in a text file, use encryptToString() instead.
*/
QByteArray encryptToByteArray(const QString& plaintext) ;
/**
Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns
a binary cyphertext in a QByteArray the result.
This method returns a byte array, that is useable for storing a binary format. If you need
a string you can store in a text file, use encryptToString() instead.
*/
QByteArray encryptToByteArray(QByteArray plaintext) ;
/**
Decrypts a cyphertext string encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QString decryptToString(const QString& cyphertext) ;
/**
Decrypts a cyphertext string encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QByteArray decryptToByteArray(const QString& cyphertext) ;
/**
Decrypts a cyphertext binary encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QString decryptToString(QByteArray cypher) ;
/**
Decrypts a cyphertext binary encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QByteArray decryptToByteArray(QByteArray cypher) ;
//enum to describe options that have been used for the encryption. Currently only one, but
//that only leaves room for future extensions like adding a cryptographic hash...
enum CryptoFlag{CryptoFlagNone = 0,
CryptoFlagCompression = 0x01,
CryptoFlagChecksum = 0x02,
CryptoFlagHash = 0x04
};
Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag);
private:
void splitKey();
quint64 m_key;
QVector<char> m_keyParts;
CompressionMode m_compressionMode;
IntegrityProtectionMode m_protectionMode;
Error m_lastError;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags)
#endif // SimpleCrypt_H

36
3rdparty/o2/src/src.pri vendored Normal file
View file

@ -0,0 +1,36 @@
QT *= network script
INCLUDEPATH += $$PWD
SOURCES += \
$$PWD/o1.cpp \
$$PWD/o1requestor.cpp \
$$PWD/o2.cpp \
$$PWD/o2facebook.cpp \
$$PWD/o2gft.cpp \
$$PWD/o2reply.cpp \
$$PWD/o2replyserver.cpp \
$$PWD/o2requestor.cpp \
$$PWD/simplecrypt.cpp \
$$PWD/o2skydrive.cpp \
$$PWD/oxtwitter.cpp \
$$PWD/o2settingsstore.cpp
HEADERS += \
$$PWD/o1.h \
$$PWD/o1dropbox.h \
$$PWD/o1flickr.h \
$$PWD/o1requestor.h \
$$PWD/o1twitter.h \
$$PWD/o2.h \
$$PWD/o2facebook.h \
$$PWD/o2gft.h \
$$PWD/o2reply.h \
$$PWD/o2replyserver.h \
$$PWD/o2requestor.h \
$$PWD/simplecrypt.h \
$$PWD/o2skydrive.h \
$$PWD/o2globals.h \
$$PWD/oxtwitter.h \
$$PWD/o2abstractstore.h \
$$PWD/o2settingsstore.h \
$$PWD/o1freshbooks.h

View file

@ -460,6 +460,7 @@ ADD_SUBDIRECTORY(plugin)
ADD_SUBDIRECTORY(include)
ADD_SUBDIRECTORY(spectrum)
ADD_SUBDIRECTORY(backends)
ADD_SUBDIRECTORY(3rdparty)
if (NOT WIN32)
ADD_SUBDIRECTORY(spectrum_manager)
# ADD_SUBDIRECTORY(spectrum2_send_message)