Add 3rdparty oauth 2 library
This commit is contained in:
parent
096324974a
commit
7291e4c1ca
53 changed files with 4060 additions and 0 deletions
1
3rdparty/CMakeLists.txt
vendored
Normal file
1
3rdparty/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ADD_SUBDIRECTORY(o2)
|
2
3rdparty/o2/.gitignore
vendored
Normal file
2
3rdparty/o2/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*CMakeLists.txt.user
|
||||
_build/
|
7
3rdparty/o2/CMakeLists.txt
vendored
Normal file
7
3rdparty/o2/CMakeLists.txt
vendored
Normal 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
23
3rdparty/o2/LICENSE
vendored
Normal 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
192
3rdparty/o2/README.md
vendored
Normal 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
37
3rdparty/o2/acknowledgements.md
vendored
Normal 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
6
3rdparty/o2/examples/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
*.pro.user
|
||||
build*
|
||||
*.o
|
||||
*.moc
|
||||
moc_*
|
||||
Makefile
|
9
3rdparty/o2/examples/CMakeLists.txt
vendored
Normal file
9
3rdparty/o2/examples/CMakeLists.txt
vendored
Normal 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)
|
25
3rdparty/o2/examples/facebookdemo/CMakeLists.txt
vendored
Normal file
25
3rdparty/o2/examples/facebookdemo/CMakeLists.txt
vendored
Normal 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 )
|
16
3rdparty/o2/examples/facebookdemo/facebookdemo.pro
vendored
Normal file
16
3rdparty/o2/examples/facebookdemo/facebookdemo.pro
vendored
Normal 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
|
132
3rdparty/o2/examples/facebookdemo/fbdemo.cpp
vendored
Normal file
132
3rdparty/o2/examples/facebookdemo/fbdemo.cpp
vendored
Normal 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();
|
||||
}
|
||||
}
|
35
3rdparty/o2/examples/facebookdemo/fbdemo.h
vendored
Normal file
35
3rdparty/o2/examples/facebookdemo/fbdemo.h
vendored
Normal 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
|
83
3rdparty/o2/examples/facebookdemo/main.cpp
vendored
Normal file
83
3rdparty/o2/examples/facebookdemo/main.cpp
vendored
Normal 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"
|
25
3rdparty/o2/examples/twitterdemo/CMakeLists.txt
vendored
Normal file
25
3rdparty/o2/examples/twitterdemo/CMakeLists.txt
vendored
Normal 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 )
|
122
3rdparty/o2/examples/twitterdemo/main.cpp
vendored
Normal file
122
3rdparty/o2/examples/twitterdemo/main.cpp
vendored
Normal 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"
|
144
3rdparty/o2/examples/twitterdemo/tweeter.cpp
vendored
Normal file
144
3rdparty/o2/examples/twitterdemo/tweeter.cpp
vendored
Normal 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();
|
||||
}
|
41
3rdparty/o2/examples/twitterdemo/tweeter.h
vendored
Normal file
41
3rdparty/o2/examples/twitterdemo/tweeter.h
vendored
Normal 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
|
14
3rdparty/o2/examples/twitterdemo/twitterdemo.pro
vendored
Normal file
14
3rdparty/o2/examples/twitterdemo/twitterdemo.pro
vendored
Normal 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
98
3rdparty/o2/src/CMakeLists.txt
vendored
Normal 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
447
3rdparty/o2/src/o1.cpp
vendored
Normal 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> ¶ms) {
|
||||
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
208
3rdparty/o2/src/o1.h
vendored
Normal 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> ¶ms);
|
||||
|
||||
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
18
3rdparty/o2/src/o1dropbox.h
vendored
Normal 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
18
3rdparty/o2/src/o1flickr.h
vendored
Normal 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
26
3rdparty/o2/src/o1freshbooks.h
vendored
Executable 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
78
3rdparty/o2/src/o1requestor.cpp
vendored
Normal 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
61
3rdparty/o2/src/o1requestor.h
vendored
Normal 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
17
3rdparty/o2/src/o1twitter.h
vendored
Normal 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
397
3rdparty/o2/src/o2.cpp
vendored
Normal 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> ¶meters) {
|
||||
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
199
3rdparty/o2/src/o2.h
vendored
Normal 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> ¶meters);
|
||||
|
||||
/// 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
24
3rdparty/o2/src/o2abstractstore.h
vendored
Normal 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
92
3rdparty/o2/src/o2facebook.cpp
vendored
Normal 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
21
3rdparty/o2/src/o2facebook.h
vendored
Normal 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
13
3rdparty/o2/src/o2gft.cpp
vendored
Normal 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
14
3rdparty/o2/src/o2gft.h
vendored
Normal 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
54
3rdparty/o2/src/o2globals.h
vendored
Normal 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
17
3rdparty/o2/src/o2hubic.cpp
vendored
Normal 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
17
3rdparty/o2/src/o2hubic.h
vendored
Normal 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
46
3rdparty/o2/src/o2reply.cpp
vendored
Normal 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
53
3rdparty/o2/src/o2reply.h
vendored
Normal 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
79
3rdparty/o2/src/o2replyserver.cpp
vendored
Normal 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
26
3rdparty/o2/src/o2replyserver.h
vendored
Normal 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
193
3rdparty/o2/src/o2requestor.cpp
vendored
Normal 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
82
3rdparty/o2/src/o2requestor.h
vendored
Normal 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
47
3rdparty/o2/src/o2settingsstore.cpp
vendored
Normal 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
39
3rdparty/o2/src/o2settingsstore.h
vendored
Normal 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
121
3rdparty/o2/src/o2skydrive.cpp
vendored
Normal 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
18
3rdparty/o2/src/o2skydrive.h
vendored
Normal 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
71
3rdparty/o2/src/oxtwitter.cpp
vendored
Normal 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
36
3rdparty/o2/src/oxtwitter.h
vendored
Normal 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
254
3rdparty/o2/src/simplecrypt.cpp
vendored
Normal 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
225
3rdparty/o2/src/simplecrypt.h
vendored
Normal 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
36
3rdparty/o2/src/src.pri
vendored
Normal 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
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue