mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-09-08 21:20:45 +02:00
Import Hatchet plugin
This commit is contained in:
@@ -37,6 +37,7 @@ add_definitions( "-DQT_STRICT_ITERATORS" )
|
||||
option(BUILD_GUI "Build Tomahawk with GUI" ON)
|
||||
option(BUILD_RELEASE "Generate TOMAHAWK_VERSION without GIT info" OFF)
|
||||
option(BUILD_TESTS "Build Tomahawk with unit tests" ON)
|
||||
option(BUILD_HATCHET "Build the Hatchet plugin" OFF)
|
||||
|
||||
option(WITH_BREAKPAD "Build with breakpad integration" ON)
|
||||
option(WITH_CRASHREPORTER "Build with CrashReporter" ON)
|
||||
|
@@ -3,13 +3,14 @@ include( ${CMAKE_CURRENT_LIST_DIR}/../../TomahawkAddPlugin.cmake )
|
||||
file(GLOB SUBDIRECTORIES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*")
|
||||
foreach(SUBDIRECTORY ${SUBDIRECTORIES})
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}" AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}/CMakeLists.txt")
|
||||
if(SUBDIRECTORY STREQUAL "xmpp")
|
||||
if(SUBDIRECTORY STREQUAL "twitter")
|
||||
elseif(SUBDIRECTORY STREQUAL "xmpp")
|
||||
if( JREEN_FOUND )
|
||||
add_subdirectory( xmpp )
|
||||
endif()
|
||||
elseif(SUBDIRECTORY STREQUAL "twitter")
|
||||
if(QTWEETLIB_FOUND AND BUILD_GUI)
|
||||
add_subdirectory( twitter )
|
||||
elseif(SUBDIRECTORY STREQUAL "hatchet")
|
||||
if(BUILD_HATCHET AND BUILD_GUI)
|
||||
add_subdirectory(hatchet)
|
||||
endif()
|
||||
else()
|
||||
add_subdirectory(${SUBDIRECTORY})
|
||||
|
23
src/accounts/hatchet/.gitignore
vendored
Normal file
23
src/accounts/hatchet/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
*-build/*
|
||||
build/*
|
||||
.directory
|
||||
*.a
|
||||
*.o
|
||||
._*
|
||||
*.user
|
||||
*.swp
|
||||
*.swo
|
||||
Makefile*
|
||||
moc_*
|
||||
*~
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
/tomahawk
|
||||
.kdev4
|
||||
*.kdev4
|
||||
*.kate-swp
|
||||
clang/
|
||||
win/
|
||||
gcc/
|
||||
tags
|
||||
.DS_Store
|
52
src/accounts/hatchet/CMakeLists.txt
Normal file
52
src/accounts/hatchet/CMakeLists.txt
Normal file
@@ -0,0 +1,52 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
CMAKE_POLICY(SET CMP0017 NEW)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules")
|
||||
|
||||
if(NOT TOMAHAWK_LIBRARIES)
|
||||
message(STATUS "BUILDING OUTSIDE TOMAHAWK")
|
||||
find_package(Tomahawk REQUIRED)
|
||||
else()
|
||||
message(STATUS "BUILDING INSIDE TOMAHAWK")
|
||||
set(TOMAHAWK_USE_FILE "${CMAKE_SOURCE_DIR}/TomahawkUse.cmake")
|
||||
endif()
|
||||
include( ${TOMAHAWK_USE_FILE} )
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(QCA2 REQUIRED)
|
||||
find_package(websocketpp 0.2.99 REQUIRED)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${WEBSOCKETPP_INCLUDE_DIR}
|
||||
${TOMAHAWK_INCLUDE_DIRS}
|
||||
${QCA2_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||
|
||||
if(APPLE)
|
||||
# http://stackoverflow.com/questions/7226753/osx-lion-xcode-4-1-how-do-i-setup-a-c0x-project/7236451#7236451
|
||||
add_definitions(-std=c++11 -stdlib=libc++ -U__STRICT_ANSI__)
|
||||
set(PLATFORM_SPECIFIC_LINK_LIBRARIES "/usr/lib/libc++.dylib")
|
||||
else()
|
||||
add_definitions(-std=c++0x)
|
||||
endif()
|
||||
|
||||
tomahawk_add_plugin(hatchet
|
||||
TYPE account
|
||||
EXPORT_MACRO ACCOUNTDLLEXPORT_PRO
|
||||
SOURCES
|
||||
account/HatchetAccount.cpp
|
||||
account/HatchetAccountConfig.cpp
|
||||
sip/WebSocket.cpp
|
||||
sip/WebSocketThreadController.cpp
|
||||
sip/HatchetSip.cpp
|
||||
UI
|
||||
account/HatchetAccountConfig.ui
|
||||
LINK_LIBRARIES
|
||||
${TOMAHAWK_LIBRARIES}
|
||||
${QCA2_LIBRARIES}
|
||||
${PLATFORM_SPECIFIC_LINK_LIBRARIES}
|
||||
)
|
||||
|
381
src/accounts/hatchet/account/HatchetAccount.cpp
Normal file
381
src/accounts/hatchet/account/HatchetAccount.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012 Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "HatchetAccount.h"
|
||||
|
||||
#include "HatchetAccountConfig.h"
|
||||
#include "utils/Closure.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "sip/HatchetSip.h"
|
||||
#include "utils/TomahawkUtils.h"
|
||||
|
||||
#include <QtPlugin>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
|
||||
#include <qjson/parser.h>
|
||||
#include <qjson/serializer.h>
|
||||
|
||||
using namespace Tomahawk;
|
||||
using namespace Accounts;
|
||||
|
||||
static QPixmap* s_icon = 0;
|
||||
HatchetAccount* HatchetAccount::s_instance = 0;
|
||||
|
||||
#define AUTH_SERVER "http://auth.toma.hk"
|
||||
|
||||
HatchetAccountFactory::HatchetAccountFactory()
|
||||
{
|
||||
#ifndef ENABLE_HEADLESS
|
||||
if ( s_icon == 0 )
|
||||
s_icon = new QPixmap( ":/hatchet-account/hatchet-icon-512x512.png" );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
HatchetAccountFactory::~HatchetAccountFactory()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
QPixmap
|
||||
HatchetAccountFactory::icon() const
|
||||
{
|
||||
return *s_icon;
|
||||
}
|
||||
|
||||
|
||||
Account*
|
||||
HatchetAccountFactory::createAccount( const QString& pluginId )
|
||||
{
|
||||
return new HatchetAccount( pluginId.isEmpty() ? generateId( factoryId() ) : pluginId );
|
||||
}
|
||||
|
||||
|
||||
// Hatchet account
|
||||
|
||||
HatchetAccount::HatchetAccount( const QString& accountId )
|
||||
: Account( accountId )
|
||||
{
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
|
||||
HatchetAccount::~HatchetAccount()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
HatchetAccount*
|
||||
HatchetAccount::instance()
|
||||
{
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
|
||||
AccountConfigWidget*
|
||||
HatchetAccount::configurationWidget()
|
||||
{
|
||||
if ( m_configWidget.isNull() )
|
||||
m_configWidget = QWeakPointer<HatchetAccountConfig>( new HatchetAccountConfig( this ) );
|
||||
|
||||
return m_configWidget.data();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::authenticate()
|
||||
{
|
||||
if ( connectionState() == Connected )
|
||||
return;
|
||||
|
||||
if ( !authToken().isEmpty() )
|
||||
{
|
||||
qDebug() << "Have saved credentials with auth token:" << authToken();
|
||||
if ( sipPlugin() )
|
||||
sipPlugin()->connectPlugin();
|
||||
}
|
||||
else if ( !username().isEmpty() )
|
||||
{
|
||||
// Need to re-prompt for password, since we don't save it!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::deauthenticate()
|
||||
{
|
||||
if ( !m_tomahawkSipPlugin.isNull() )
|
||||
sipPlugin()->disconnectPlugin();
|
||||
emit deauthenticated();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::setConnectionState( Account::ConnectionState connectionState )
|
||||
{
|
||||
m_state = connectionState;
|
||||
|
||||
emit connectionStateChanged( connectionState );
|
||||
}
|
||||
|
||||
|
||||
Account::ConnectionState
|
||||
HatchetAccount::connectionState() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
|
||||
SipPlugin*
|
||||
HatchetAccount::sipPlugin()
|
||||
{
|
||||
if ( m_tomahawkSipPlugin.isNull() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO;
|
||||
m_tomahawkSipPlugin = QWeakPointer< HatchetSipPlugin >( new HatchetSipPlugin( this ) );
|
||||
connect( m_tomahawkSipPlugin.data(), SIGNAL( authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service, QString ) ),
|
||||
this, SLOT( authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service, QString ) ) );
|
||||
|
||||
return m_tomahawkSipPlugin.data();
|
||||
}
|
||||
return m_tomahawkSipPlugin.data();
|
||||
}
|
||||
|
||||
|
||||
QPixmap
|
||||
HatchetAccount::icon() const
|
||||
{
|
||||
return *s_icon;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
HatchetAccount::isAuthenticated() const
|
||||
{
|
||||
return credentials().contains( "authtoken" );
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
HatchetAccount::username() const
|
||||
{
|
||||
return credentials().value( "username" ).toString();
|
||||
}
|
||||
|
||||
|
||||
QByteArray
|
||||
HatchetAccount::authToken() const
|
||||
{
|
||||
return credentials().value( "authtoken" ).toByteArray();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::doRegister( const QString& username, const QString& password, const QString& email )
|
||||
{
|
||||
if ( username.isEmpty() || password.isEmpty() || email.isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap registerCmd;
|
||||
registerCmd[ "command" ] = "register";
|
||||
registerCmd[ "email" ] = email;
|
||||
registerCmd[ "password" ] = password;
|
||||
registerCmd[ "username" ] = username;
|
||||
|
||||
QNetworkReply* reply = buildRequest( "signup", registerCmd );
|
||||
connect( reply, SIGNAL( finished() ), this, SLOT( onRegisterFinished() ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::loginWithPassword( const QString& username, const QString& password )
|
||||
{
|
||||
if ( username.isEmpty() || password.isEmpty() )
|
||||
{
|
||||
tLog() << "No tomahawk account username or pw, not logging in";
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap params;
|
||||
params[ "password" ] = password;
|
||||
params[ "username" ] = username;
|
||||
|
||||
QNetworkReply* reply = buildRequest( "login", params );
|
||||
NewClosure( reply, SIGNAL( finished() ), this, SLOT( onPasswordLoginFinished( QNetworkReply*, const QString& ) ), reply, username );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::fetchAccessTokens()
|
||||
{
|
||||
if ( username().isEmpty() || authToken().isEmpty() )
|
||||
{
|
||||
tLog() << "No authToken, not logging in";
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap params;
|
||||
params[ "authtoken" ] = authToken();
|
||||
params[ "username" ] = username();
|
||||
|
||||
tLog() << "Fetching access tokens";
|
||||
QNetworkReply* reply = buildRequest( "tokens", params );
|
||||
connect( reply, SIGNAL( finished() ), this, SLOT( onFetchAccessTokensFinished() ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::onRegisterFinished()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
|
||||
Q_ASSERT( reply );
|
||||
bool ok;
|
||||
const QVariantMap resp = parseReply( reply, ok );
|
||||
if ( !ok )
|
||||
{
|
||||
emit registerFinished( false, resp.value( "errormsg" ).toString() );
|
||||
return;
|
||||
}
|
||||
|
||||
emit registerFinished( true, QString() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::onPasswordLoginFinished( QNetworkReply* reply, const QString& username )
|
||||
{
|
||||
Q_ASSERT( reply );
|
||||
bool ok;
|
||||
const QVariantMap resp = parseReply( reply, ok );
|
||||
if ( !ok )
|
||||
return;
|
||||
|
||||
const QByteArray authenticationToken = resp.value( "message" ).toMap().value( "authtoken" ).toMap().value("token").toByteArray();
|
||||
|
||||
QVariantHash creds = credentials();
|
||||
creds[ "username" ] = username;
|
||||
creds[ "authtoken" ] = authenticationToken;
|
||||
setCredentials( creds );
|
||||
syncConfig();
|
||||
|
||||
if ( !authenticationToken.isEmpty() )
|
||||
{
|
||||
// We're succesful! Now log in with our authtoken for access
|
||||
fetchAccessTokens();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::onFetchAccessTokensFinished()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
|
||||
Q_ASSERT( reply );
|
||||
bool ok;
|
||||
const QVariantMap resp = parseReply( reply, ok );
|
||||
if ( !ok )
|
||||
{
|
||||
if ( resp["code"].toInt() == 140 )
|
||||
{
|
||||
tLog() << "Expired credentials, need to reauthenticate with password";
|
||||
QVariantHash creds = credentials();
|
||||
creds.remove( "authtoken" );
|
||||
setCredentials( creds );
|
||||
syncConfig();
|
||||
}
|
||||
else
|
||||
tLog() << "Unable to fetch access tokens";
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantHash creds = credentials();
|
||||
creds[ "accesstokens" ] = resp[ "message" ].toMap()[ "accesstokens" ];
|
||||
setCredentials( creds );
|
||||
syncConfig();
|
||||
|
||||
emit accessTokensFetched();
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
HatchetAccount::authUrlForService( const Service &service ) const
|
||||
{
|
||||
return m_extraAuthUrls.value( service, QString() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccount::authUrlDiscovered( Service service, const QString &authUrl )
|
||||
{
|
||||
m_extraAuthUrls[ service ] = authUrl;
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
HatchetAccount::buildRequest( const QString& command, const QVariantMap& params ) const
|
||||
{
|
||||
QJson::Serializer s;
|
||||
const QByteArray msgJson = s.serialize( params );
|
||||
|
||||
QNetworkRequest req( QUrl( QString( "%1/%2" ).arg( AUTH_SERVER ).arg( command ) ) );
|
||||
req.setHeader( QNetworkRequest::ContentTypeHeader, "application/json; charset=utf-8" );
|
||||
QNetworkReply* reply = TomahawkUtils::nam()->post( req, msgJson );
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
QVariantMap
|
||||
HatchetAccount::parseReply( QNetworkReply* reply, bool& okRet ) const
|
||||
{
|
||||
QVariantMap resp;
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
if ( reply->error() != QNetworkReply::NoError )
|
||||
{
|
||||
tLog() << "Network error in command:" << reply->error() << reply->errorString();
|
||||
okRet = false;
|
||||
return resp;
|
||||
}
|
||||
|
||||
QJson::Parser p;
|
||||
bool ok;
|
||||
resp = p.parse( reply, &ok ).toMap();
|
||||
|
||||
if ( !ok || resp.value( "error", false ).toBool() )
|
||||
{
|
||||
tLog() << "Error from tomahawk server response, or in parsing from json:" << resp.value( "errormsg" ) << resp;
|
||||
okRet = false;
|
||||
return resp;
|
||||
}
|
||||
|
||||
tLog() << "Got reply" << resp.keys();
|
||||
tLog() << "Got reply" << resp.values();
|
||||
okRet = true;
|
||||
return resp;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::HatchetAccountFactory )
|
129
src/accounts/hatchet/account/HatchetAccount.h
Normal file
129
src/accounts/hatchet/account/HatchetAccount.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012 Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TOMAHAWK_ACCOUNT_H
|
||||
#define TOMAHAWK_ACCOUNT_H
|
||||
|
||||
|
||||
#include <accounts/Account.h>
|
||||
#include <accounts/AccountDllMacro.h>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class HatchetSipPlugin;
|
||||
|
||||
namespace Tomahawk
|
||||
{
|
||||
namespace Accounts
|
||||
{
|
||||
|
||||
class HatchetAccountConfig;
|
||||
|
||||
class ACCOUNTDLLEXPORT HatchetAccountFactory : public AccountFactory
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES( Tomahawk::Accounts::AccountFactory )
|
||||
public:
|
||||
HatchetAccountFactory();
|
||||
virtual ~HatchetAccountFactory();
|
||||
|
||||
virtual QString factoryId() const { return "hatchetaccount"; }
|
||||
virtual QString prettyName() const { return "Hatchet"; }
|
||||
virtual QString description() const { return tr( "Connect to your Hatchet account" ); }
|
||||
virtual bool isUnique() const { return true; }
|
||||
AccountTypes types() const { return AccountTypes( SipType ); };
|
||||
// virtual bool allowUserCreation() const { return false; }
|
||||
#ifndef ENABLE_HEADLESS
|
||||
virtual QPixmap icon() const;
|
||||
#endif
|
||||
|
||||
|
||||
virtual Account* createAccount ( const QString& pluginId = QString() );
|
||||
};
|
||||
|
||||
class ACCOUNTDLLEXPORT HatchetAccount : public Account
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Service {
|
||||
Facebook = 0
|
||||
};
|
||||
|
||||
HatchetAccount( const QString &accountId );
|
||||
virtual ~HatchetAccount();
|
||||
|
||||
static HatchetAccount* instance();
|
||||
|
||||
QPixmap icon() const;
|
||||
|
||||
void authenticate();
|
||||
void deauthenticate();
|
||||
bool isAuthenticated() const;
|
||||
|
||||
void setConnectionState( Account::ConnectionState connectionState );
|
||||
ConnectionState connectionState() const;
|
||||
|
||||
|
||||
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); }
|
||||
SipPlugin* sipPlugin();
|
||||
|
||||
AccountConfigWidget* configurationWidget();
|
||||
QWidget* aclWidget() { return 0; }
|
||||
|
||||
QString username() const;
|
||||
|
||||
void fetchAccessTokens();
|
||||
|
||||
QString authUrlForService( const Service& service ) const;
|
||||
|
||||
signals:
|
||||
void deauthenticated();
|
||||
void accessTokensFetched();
|
||||
void registerFinished( bool successful, const QString& msg );
|
||||
|
||||
private slots:
|
||||
void onRegisterFinished();
|
||||
void onPasswordLoginFinished( QNetworkReply*, const QString& username );
|
||||
void onFetchAccessTokensFinished();
|
||||
|
||||
void authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service service, const QString& authUrl );
|
||||
private:
|
||||
QByteArray authToken() const;
|
||||
|
||||
void doRegister( const QString& username, const QString& password, const QString& email );
|
||||
void loginWithPassword( const QString& username, const QString& password );
|
||||
|
||||
QNetworkReply* buildRequest( const QString& command, const QVariantMap& params ) const;
|
||||
QVariantMap parseReply( QNetworkReply* reply, bool& ok ) const;
|
||||
|
||||
QWeakPointer<HatchetAccountConfig> m_configWidget;
|
||||
|
||||
Account::ConnectionState m_state;
|
||||
|
||||
QWeakPointer< HatchetSipPlugin > m_tomahawkSipPlugin;
|
||||
QHash< Service, QString > m_extraAuthUrls;
|
||||
|
||||
static HatchetAccount* s_instance;
|
||||
friend class HatchetAccountConfig;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
207
src/accounts/hatchet/account/HatchetAccountConfig.cpp
Normal file
207
src/accounts/hatchet/account/HatchetAccountConfig.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012 Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "HatchetAccountConfig.h"
|
||||
#include "HatchetAccount.h"
|
||||
#include "utils/TomahawkUtils.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include "ui_HatchetAccountConfig.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
using namespace Accounts;
|
||||
|
||||
namespace {
|
||||
enum ButtonAction {
|
||||
Login,
|
||||
Register,
|
||||
Logout
|
||||
};
|
||||
}
|
||||
|
||||
HatchetAccountConfig::HatchetAccountConfig( HatchetAccount* account )
|
||||
: AccountConfigWidget( 0 )
|
||||
, m_ui( new Ui::HatchetAccountConfig )
|
||||
, m_account( account )
|
||||
{
|
||||
Q_ASSERT( m_account );
|
||||
|
||||
m_ui->setupUi( this );
|
||||
|
||||
m_ui->emailLabel->hide();
|
||||
m_ui->emailEdit->hide();
|
||||
|
||||
connect( m_ui->registerbutton, SIGNAL( clicked( bool ) ), this, SLOT( registerClicked() ) );
|
||||
connect( m_ui->loginOrRegisterButton, SIGNAL( clicked( bool ) ), this, SLOT( loginOrRegister() ) );
|
||||
|
||||
connect( m_ui->usernameEdit, SIGNAL( textChanged( QString ) ), this, SLOT( fieldsChanged() ) );
|
||||
connect( m_ui->passwordEdit, SIGNAL( textChanged( QString ) ), this, SLOT( fieldsChanged() ) );
|
||||
connect( m_ui->emailEdit, SIGNAL( textChanged( QString ) ), this, SLOT( fieldsChanged() ) );
|
||||
|
||||
connect( m_account, SIGNAL( registerFinished( bool, QString ) ), this, SLOT( registerFinished( bool, QString ) ) );
|
||||
connect( m_account, SIGNAL( deauthenticated() ), this, SLOT( showLoggedOut() ) );
|
||||
connect( m_account, SIGNAL( accessTokensFetched() ), this, SLOT( accountInfoUpdated() ) );
|
||||
|
||||
if ( !m_account->authToken().isEmpty() )
|
||||
accountInfoUpdated();
|
||||
else
|
||||
{
|
||||
m_ui->usernameEdit->setText( m_account->username() );
|
||||
showLoggedOut();
|
||||
}
|
||||
}
|
||||
|
||||
HatchetAccountConfig::~HatchetAccountConfig()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccountConfig::registerClicked()
|
||||
{
|
||||
m_ui->registerbutton->hide();
|
||||
|
||||
m_ui->emailLabel->show();
|
||||
m_ui->emailEdit->show();
|
||||
m_ui->loginOrRegisterButton->setText( tr( "Register" ) );
|
||||
m_ui->loginOrRegisterButton->setProperty( "action", Register );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccountConfig::loginOrRegister()
|
||||
{
|
||||
const ButtonAction action = static_cast< ButtonAction>( m_ui->loginOrRegisterButton->property( "action" ).toInt() );
|
||||
|
||||
if ( action == Login )
|
||||
{
|
||||
// Log in mode
|
||||
m_account->loginWithPassword( m_ui->usernameEdit->text(), m_ui->passwordEdit->text() );
|
||||
}
|
||||
else if ( action == Register )
|
||||
{
|
||||
// Register since the use clicked register and just entered his info
|
||||
const QString username = m_ui->usernameEdit->text();
|
||||
const QString password = m_ui->passwordEdit->text();
|
||||
const QString email = m_ui->emailEdit->text();
|
||||
m_account->doRegister( username, password, email );
|
||||
}
|
||||
else if ( action == Logout )
|
||||
{
|
||||
// TODO
|
||||
m_ui->usernameEdit->clear();
|
||||
m_ui->passwordEdit->clear();
|
||||
|
||||
QVariantHash creds = m_account->credentials();
|
||||
creds.clear();
|
||||
m_account->setCredentials( creds );
|
||||
m_account->sync();
|
||||
m_account->deauthenticate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccountConfig::fieldsChanged()
|
||||
{
|
||||
const QString username = m_ui->usernameEdit->text();
|
||||
const QString password = m_ui->passwordEdit->text();
|
||||
const QString email = m_ui->emailEdit->text();
|
||||
|
||||
const ButtonAction action = static_cast< ButtonAction>( m_ui->loginOrRegisterButton->property( "action" ).toInt() );
|
||||
|
||||
m_ui->loginOrRegisterButton->setEnabled( !username.isEmpty() && !password.isEmpty() && ( action == Login || !email.isEmpty() ) );
|
||||
|
||||
m_ui->errorLabel->clear();
|
||||
|
||||
if ( action == Login )
|
||||
m_ui->loginOrRegisterButton->setText( tr( "Login" ) );
|
||||
else if ( action == Register )
|
||||
m_ui->loginOrRegisterButton->setText( tr( "Register" ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccountConfig::registerFinished( bool success, const QString& error )
|
||||
{
|
||||
if ( success )
|
||||
{
|
||||
showLoggedOut();
|
||||
m_ui->errorLabel->setText( tr( "An email has been sent to activate your account" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->loginOrRegisterButton->setText( "Failed" );
|
||||
m_ui->loginOrRegisterButton->setEnabled( false );
|
||||
m_ui->errorLabel->setText( error );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccountConfig::showLoggedIn()
|
||||
{
|
||||
m_ui->registerbutton->hide();
|
||||
m_ui->usernameLabel->hide();
|
||||
m_ui->usernameEdit->hide();
|
||||
m_ui->emailLabel->hide();
|
||||
m_ui->emailEdit->hide();
|
||||
m_ui->passwordLabel->hide();
|
||||
m_ui->passwordEdit->hide();
|
||||
|
||||
m_ui->loggedInLabel->setText( tr( "Logged in as: %1" ).arg( m_account->username() ) );
|
||||
m_ui->loggedInLabel->show();
|
||||
|
||||
m_ui->errorLabel->clear();
|
||||
m_ui->errorLabel->hide();
|
||||
|
||||
m_ui->loginOrRegisterButton->setText( "Log out" );
|
||||
m_ui->loginOrRegisterButton->setProperty( "action", Logout );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccountConfig::showLoggedOut()
|
||||
{
|
||||
m_ui->emailEdit->hide();
|
||||
m_ui->emailLabel->hide();
|
||||
|
||||
m_ui->registerbutton->show();
|
||||
m_ui->usernameLabel->show();
|
||||
m_ui->usernameEdit->show();
|
||||
m_ui->passwordLabel->show();
|
||||
m_ui->passwordEdit->show();
|
||||
|
||||
m_ui->loggedInLabel->clear();
|
||||
m_ui->loggedInLabel->hide();
|
||||
|
||||
m_ui->errorLabel->clear();
|
||||
|
||||
m_ui->loginOrRegisterButton->setText( "Login" );
|
||||
m_ui->loginOrRegisterButton->setProperty( "action", Login );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetAccountConfig::accountInfoUpdated()
|
||||
{
|
||||
showLoggedIn();
|
||||
return;
|
||||
}
|
65
src/accounts/hatchet/account/HatchetAccountConfig.h
Normal file
65
src/accounts/hatchet/account/HatchetAccountConfig.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012 Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TOMAHAWK_ACCOUNT_CONFIG_H
|
||||
#define TOMAHAWK_ACCOUNT_CONFIG_H
|
||||
|
||||
#include <accounts/AccountConfigWidget.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVariantMap>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace Ui {
|
||||
class HatchetAccountConfig;
|
||||
};
|
||||
|
||||
namespace Tomahawk {
|
||||
namespace Accounts {
|
||||
|
||||
class HatchetAccount;
|
||||
|
||||
class HatchetAccountConfig : public AccountConfigWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HatchetAccountConfig( HatchetAccount* account );
|
||||
virtual ~HatchetAccountConfig();
|
||||
|
||||
private slots:
|
||||
void registerClicked();
|
||||
void loginOrRegister();
|
||||
|
||||
void registerFinished( bool success, const QString& error );
|
||||
|
||||
void fieldsChanged();
|
||||
|
||||
void showLoggedIn();
|
||||
void showLoggedOut();
|
||||
|
||||
void accountInfoUpdated();
|
||||
private:
|
||||
Ui::HatchetAccountConfig* m_ui;
|
||||
HatchetAccount* m_account;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
150
src/accounts/hatchet/account/HatchetAccountConfig.ui
Normal file
150
src/accounts/hatchet/account/HatchetAccountConfig.ui
Normal file
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>HatchetAccountConfig</class>
|
||||
<widget class="QWidget" name="HatchetAccountConfig">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>301</width>
|
||||
<height>404</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../resources.qrc">:/hatchet-account/hatchet-icon-512x512.png</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Connect to your Hatchet account</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="registerbutton">
|
||||
<property name="text">
|
||||
<string>Register for a free account</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="loggedInLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="credentialsLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="emailLabel">
|
||||
<property name="text">
|
||||
<string>Email:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="emailEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Enter email to use</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="usernameLabel">
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="usernameEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Hatchet username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="passwordLabel">
|
||||
<property name="text">
|
||||
<string>Password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="passwordEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="loginOrRegisterButton">
|
||||
<property name="text">
|
||||
<string>Login</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
14
src/accounts/hatchet/admin/certs/dreamcatcher.pem
Normal file
14
src/accounts/hatchet/admin/certs/dreamcatcher.pem
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyT6j9B1hiRHwV96BSZJp
|
||||
vLnGS0p6h1fxJiVGOjpcct/pA1rfVW66LTC7seYT6mF15flccIn5H8srAvH2a57S
|
||||
uCMHDrHkQIoNDoOf+Ersw4tYzVYv+5h8IN9apYr6iqfT7rn3Fzb5oEB+GqcDurX3
|
||||
4aaDjm+Wq0QjJAZf0fOaKor0ASejkj6EywpaHLwj51DKtf6SYbDEf52vDOzsDDnx
|
||||
2AzVeqzPNFnnau6/v7rCi081b33qOrbJBtJjSNAJcM3iVf465k8KP2VD8nX3wzwT
|
||||
9H0TEp67UMm0lz2i6gSAyEcIR2FNdTjVa1ZHYF2tnOihFDu0H4U4/4mPswpNNdOp
|
||||
mtc5WxxK5ouKqxD8ArSoclmZMybIBbL0lLRjo4VC/olfbd1RQ57L9ETtNIf0xxKU
|
||||
aweUkfWyJfU5ueaSlBaGoKSGugqp50ql3Td0m2JnJmaopdSi12L3EdlaO0+/OL3f
|
||||
0aXbtnsZXlIHoq7FBEYcLhCNbwEeceV5Cveb5Os8w2331jkgpcPoXQFOtIVyva2B
|
||||
3RadI0aALIbX5Tt0/AcbLeq10BJH8Ny2RSrhF2cxkjXEwQ68OUbCv1U4Si+EL44E
|
||||
tctg8zj9rB8SG+miE18cXaQEUxA/olOU/Axkm8dLLLTxqWsD3VQYDxBgBemaY1OT
|
||||
O32yipB9ko1sLCx6ZaQQ56sCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
44
src/accounts/hatchet/admin/certs/startcomroot.pem
Normal file
44
src/accounts/hatchet/admin/certs/startcomroot.pem
Normal file
@@ -0,0 +1,44 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
|
||||
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
|
||||
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
|
||||
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
|
||||
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
|
||||
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
|
||||
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
|
||||
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
|
||||
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
|
||||
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
|
||||
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
|
||||
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
|
||||
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
|
||||
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
|
||||
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
|
||||
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
|
||||
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
|
||||
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
|
||||
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
|
||||
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
|
||||
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
|
||||
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
|
||||
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
|
||||
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
|
||||
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
|
||||
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
|
||||
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
|
||||
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
|
||||
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
|
||||
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
|
||||
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
|
||||
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
|
||||
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
|
||||
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
|
||||
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
|
||||
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
|
||||
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
|
||||
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
|
||||
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
|
||||
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
|
||||
-----END CERTIFICATE-----
|
BIN
src/accounts/hatchet/admin/icons/hatchet-icon-512x512.png
Normal file
BIN
src/accounts/hatchet/admin/icons/hatchet-icon-512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
37
src/accounts/hatchet/cmake/Modules/FindQCA2.cmake
Normal file
37
src/accounts/hatchet/cmake/Modules/FindQCA2.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
# - Try to find QCA2 (Qt Cryptography Architecture 2)
|
||||
# Once done this will define
|
||||
#
|
||||
# QCA2_FOUND - system has QCA2
|
||||
# QCA2_INCLUDE_DIR - the QCA2 include directory
|
||||
# QCA2_LIBRARIES - the libraries needed to use QCA2
|
||||
# QCA2_DEFINITIONS - Compiler switches required for using QCA2
|
||||
#
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
|
||||
# Copyright (c) 2006, Michael Larouche, <michael.larouche@kdemail.net>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
if (NOT WIN32)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_QCA2 qca2)
|
||||
set(QCA2_DEFINITIONS ${PC_QCA2_CFLAGS_OTHER})
|
||||
endif (NOT WIN32)
|
||||
|
||||
find_library(QCA2_LIBRARIES
|
||||
NAMES qca
|
||||
HINTS ${PC_QCA2_LIBDIR} ${PC_QCA2_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
find_path(QCA2_INCLUDE_DIR qca.h
|
||||
HINTS ${PC_QCA2_INCLUDEDIR} ${PC_QCA2_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES QtCrypto
|
||||
PATHS /usr/local/lib/qca.framework/Headers/
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(QCA2 DEFAULT_MSG QCA2_LIBRARIES QCA2_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(QCA2_INCLUDE_DIR QCA2_LIBRARIES)
|
7
src/accounts/hatchet/resources.qrc
Normal file
7
src/accounts/hatchet/resources.qrc
Normal file
@@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/hatchet-account">
|
||||
<file alias="hatchet-icon-512x512.png">admin/icons/hatchet-icon-512x512.png</file>
|
||||
<file alias="dreamcatcher.pem">admin/certs/dreamcatcher.pem</file>
|
||||
<file alias="startcomroot.pem">admin/certs/startcomroot.pem</file>
|
||||
</qresource>
|
||||
</RCC>
|
554
src/accounts/hatchet/sip/HatchetSip.cpp
Normal file
554
src/accounts/hatchet/sip/HatchetSip.cpp
Normal file
@@ -0,0 +1,554 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012, Jeff Mitchell <jeff@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "HatchetSip.h"
|
||||
|
||||
#include "account/HatchetAccount.h"
|
||||
#include "WebSocketThreadController.h"
|
||||
//#include "WebSocket.h"
|
||||
|
||||
#include <database/Database.h>
|
||||
#include <database/DatabaseImpl.h>
|
||||
#include <database/DatabaseCommand_LoadOps.h>
|
||||
#include <network/ControlConnection.h>
|
||||
#include <network/Servent.h>
|
||||
#include <sip/PeerInfo.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <SourceList.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QUuid>
|
||||
#include <QtCrypto>
|
||||
|
||||
HatchetSipPlugin::HatchetSipPlugin( Tomahawk::Accounts::Account *account )
|
||||
: SipPlugin( account )
|
||||
, m_sipState( Closed )
|
||||
, m_version( 0 )
|
||||
, m_publicKey( 0 )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO;
|
||||
|
||||
connect( m_account, SIGNAL( accessTokensFetched() ), this, SLOT( connectWebSocket() ) );
|
||||
connect( Servent::instance(), SIGNAL( dbSyncTriggered() ), this, SLOT( dbSyncTriggered() ));
|
||||
|
||||
QFile pemFile( ":/hatchet-account/dreamcatcher.pem" );
|
||||
pemFile.open( QIODevice::ReadOnly );
|
||||
tLog() << Q_FUNC_INFO << "certs/dreamcatcher.pem: " << pemFile.readAll();
|
||||
pemFile.close();
|
||||
pemFile.open( QIODevice::ReadOnly );
|
||||
QCA::ConvertResult conversionResult;
|
||||
QCA::PublicKey publicKey = QCA::PublicKey::fromPEM(pemFile.readAll(), &conversionResult);
|
||||
if ( QCA::ConvertGood != conversionResult )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "INVALID PUBKEY READ";
|
||||
return;
|
||||
}
|
||||
m_publicKey = new QCA::PublicKey( publicKey );
|
||||
}
|
||||
|
||||
|
||||
HatchetSipPlugin::~HatchetSipPlugin()
|
||||
{
|
||||
if ( m_webSocketThreadController )
|
||||
{
|
||||
m_webSocketThreadController->quit();
|
||||
m_webSocketThreadController->wait( 60000 );
|
||||
|
||||
delete m_webSocketThreadController;
|
||||
m_webSocketThreadController = 0;
|
||||
}
|
||||
|
||||
m_sipState = Closed;
|
||||
|
||||
hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Disconnected );
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
HatchetSipPlugin::isValid() const
|
||||
{
|
||||
return m_account->enabled() && m_account->isAuthenticated() && m_publicKey;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::sendSipInfo(const Tomahawk::peerinfo_ptr& receiver, const SipInfo& info)
|
||||
{
|
||||
const QString dbid = receiver->data().toMap().value( "dbid" ).toString();
|
||||
tLog() << Q_FUNC_INFO << "Send local info to " << receiver->friendlyName() << "(" << dbid << ") we are" << info.nodeId() << "with offerkey " << info.key();
|
||||
|
||||
QVariantMap sendMap;
|
||||
sendMap[ "command" ] = "authorize-peer";
|
||||
sendMap[ "dbid" ] = dbid;
|
||||
sendMap[ "offerkey" ] = info.key();
|
||||
|
||||
|
||||
if ( !sendBytes( sendMap ) )
|
||||
tLog() << Q_FUNC_INFO << "Failed sending message";
|
||||
}
|
||||
|
||||
Tomahawk::Accounts::HatchetAccount*
|
||||
HatchetSipPlugin::hatchetAccount() const
|
||||
{
|
||||
return qobject_cast< Tomahawk::Accounts::HatchetAccount* >( m_account );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::connectPlugin()
|
||||
{
|
||||
if ( !m_account->isAuthenticated() )
|
||||
{
|
||||
//FIXME: Prompt user for password?
|
||||
return;
|
||||
}
|
||||
|
||||
m_webSocketThreadController = QPointer< WebSocketThreadController >( new WebSocketThreadController( this ) );
|
||||
|
||||
hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Connecting );
|
||||
hatchetAccount()->fetchAccessTokens();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::disconnectPlugin()
|
||||
{
|
||||
if ( m_webSocketThreadController )
|
||||
{
|
||||
m_webSocketThreadController->quit();
|
||||
m_webSocketThreadController->wait( 60000 );
|
||||
|
||||
delete m_webSocketThreadController;
|
||||
m_webSocketThreadController = 0;
|
||||
}
|
||||
|
||||
m_sipState = Closed;
|
||||
m_version = 0;
|
||||
|
||||
hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Disconnected );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////// Connection methods ////////////////////////////////////
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::connectWebSocket()
|
||||
{
|
||||
//Other things can request access tokens, so if we're already connected there's no need to pay attention
|
||||
// if ( !m_ws.isNull() )
|
||||
// return;
|
||||
|
||||
if ( !isValid() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Invalid state, not continuing with connection";
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantList tokensCreds = m_account->credentials()[ "accesstokens" ].toList();
|
||||
//FIXME: Don't blindly pick the first one that matches?
|
||||
QVariantMap connectVals;
|
||||
foreach ( QVariant credObj, tokensCreds )
|
||||
{
|
||||
QVariantMap creds = credObj.toMap();
|
||||
if ( creds.contains( "type" ) && creds[ "type" ].toString() == "dreamcatcher" )
|
||||
{
|
||||
connectVals = creds;
|
||||
m_userid = creds["userid"].toString();
|
||||
m_token = creds["token"].toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString url;
|
||||
if ( !connectVals.isEmpty() )
|
||||
url = connectVals[ "host" ].toString() + ':' + connectVals[ "port" ].toString();
|
||||
|
||||
if ( url.isEmpty() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Unable to find a proper connection endpoint; bailing";
|
||||
disconnectPlugin();
|
||||
return;
|
||||
}
|
||||
else
|
||||
tLog() << Q_FUNC_INFO << "Connecting to Dreamcatcher endpoint at: " << url;
|
||||
|
||||
m_webSocketThreadController->setUrl( url );
|
||||
// connect( m_ws.data(), SIGNAL( opened() ), this, SLOT( onWsOpened() ) );
|
||||
// connect( m_ws.data(), SIGNAL( failed( QString ) ), this, SLOT( onWsFailed( QString ) ) );
|
||||
// connect( m_ws.data(), SIGNAL( closed( QString ) ), this, SLOT( onWsClosed( QString ) ) );
|
||||
// connect( m_ws.data(), SIGNAL( message( QString ) ), this, SLOT( onWsMessage( QString ) ) );
|
||||
m_webSocketThreadController->start();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::webSocketConnected()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "WebSocket connected";
|
||||
|
||||
if ( m_token.isEmpty() || !m_account->credentials().contains( "username" ) )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "access token or username is empty, aborting";
|
||||
disconnectPlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Connected );
|
||||
m_sipState = AcquiringVersion;
|
||||
|
||||
m_uuid = QUuid::createUuid().toString();
|
||||
QCA::SecureArray sa( m_uuid.toLatin1() );
|
||||
QCA::SecureArray result = m_publicKey->encrypt( sa, QCA::EME_PKCS1_OAEP );
|
||||
|
||||
tLog() << Q_FUNC_INFO << "uuid:" << m_uuid << ", size of uuid:" << m_uuid.size() << ", size of sa:" << sa.size() << ", size of result:" << result.size();
|
||||
|
||||
QVariantMap nonceVerMap;
|
||||
nonceVerMap[ "version" ] = VERSION;
|
||||
nonceVerMap[ "nonce" ] = QString( result.toByteArray().toBase64() );
|
||||
sendBytes( nonceVerMap );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::webSocketDisconnected()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "WebSocket disconnected";
|
||||
m_sipState = Closed;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
HatchetSipPlugin::sendBytes( const QVariantMap& jsonMap ) const
|
||||
{
|
||||
tLog() << Q_FUNC_INFO;
|
||||
if ( m_sipState == Closed )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "was told to send bytes on a closed connection, not gonna do it";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJson::Serializer serializer;
|
||||
QByteArray bytes = serializer.serialize( jsonMap );
|
||||
if ( bytes.isEmpty() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "could not serialize register structure to JSON";
|
||||
return false;
|
||||
}
|
||||
|
||||
tLog() << Q_FUNC_INFO << "Sending bytes of size" << bytes.size();
|
||||
emit rawBytes( bytes );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::onWsFailed( const QString &msg )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "WebSocket failed with message: " << msg;
|
||||
disconnectPlugin();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::onWsClosed( const QString &msg )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "WebSocket closed with message: " << msg;
|
||||
disconnectPlugin();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::messageReceived( const QByteArray &msg )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "WebSocket message: " << msg;
|
||||
|
||||
QJson::Parser parser;
|
||||
bool ok;
|
||||
QVariant jsonVariant = parser.parse( msg, &ok );
|
||||
if ( !jsonVariant.isValid() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed to parse message back from server";
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap retMap = jsonVariant.toMap();
|
||||
|
||||
if ( m_sipState == AcquiringVersion )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "In acquiring version state, expecting version/nonce information";
|
||||
if ( !retMap.contains( "version" ) || !retMap.contains( "nonce" ) )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed to acquire version or nonce information";
|
||||
disconnectPlugin();
|
||||
return;
|
||||
}
|
||||
bool ok = false;
|
||||
int ver = retMap[ "version" ].toInt( &ok );
|
||||
if ( ver == 0 || !ok )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed to acquire version information";
|
||||
disconnectPlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( retMap[ "nonce" ].toString() != m_uuid )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed to validate nonce";
|
||||
disconnectPlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
m_version = ver;
|
||||
|
||||
QVariantMap registerMap;
|
||||
registerMap[ "command" ] = "register";
|
||||
registerMap[ "userid" ] = m_userid;
|
||||
registerMap[ "host" ] = Servent::instance()->externalAddress();
|
||||
registerMap[ "port" ] = Servent::instance()->externalPort();
|
||||
registerMap[ "token" ] = m_token;
|
||||
registerMap[ "dbid" ] = Database::instance()->impl()->dbid();
|
||||
registerMap[ "alias" ] = QHostInfo::localHostName();
|
||||
|
||||
if ( !sendBytes( registerMap ) )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed sending message";
|
||||
disconnectPlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
m_sipState = Registering;
|
||||
}
|
||||
else if ( m_sipState == Registering )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "In registering state, checking status of registration";
|
||||
if ( retMap.contains( "status" ) &&
|
||||
retMap[ "status" ].toString() == "success" )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Registered successfully";
|
||||
m_sipState = Connected;
|
||||
hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Connected );
|
||||
QTimer::singleShot(0, this, SLOT( dbSyncTriggered() ) );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed to register successfully";
|
||||
//m_ws.data()->stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( m_sipState != Connected )
|
||||
{
|
||||
// ...erm?
|
||||
tLog() << Q_FUNC_INFO << "Got a message from a non connected socket?";
|
||||
return;
|
||||
}
|
||||
else if ( !retMap.contains( "command" ) ||
|
||||
!retMap[ "command" ].canConvert< QString >() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Unable to convert and/or interepret command from server";
|
||||
return;
|
||||
}
|
||||
|
||||
QString command = retMap[ "command" ].toString();
|
||||
|
||||
if ( command == "new-peer" )
|
||||
newPeer( retMap );
|
||||
else if ( command == "peer-authorization" )
|
||||
peerAuthorization( retMap );
|
||||
else if ( command == "synclastseen" )
|
||||
sendOplog( retMap );
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
HatchetSipPlugin::checkKeys( QStringList keys, const QVariantMap& map ) const
|
||||
{
|
||||
foreach ( QString key, keys )
|
||||
{
|
||||
if ( !map.contains( key ) )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Did not find the value" << key << "in the new-peer structure";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////// Peer handling methods ////////////////////////////////////
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::newPeer( const QVariantMap& valMap )
|
||||
{
|
||||
const QString username = valMap[ "username" ].toString();
|
||||
const QString dbid = valMap[ "dbid" ].toString();
|
||||
const QString host = valMap[ "host" ].toString();
|
||||
unsigned int port = valMap[ "port" ].toUInt();
|
||||
|
||||
tLog() << Q_FUNC_INFO << "username:" << username << "dbid" << dbid;
|
||||
|
||||
QStringList keys( QStringList() << "command" << "username" << "host" << "port" << "dbid" );
|
||||
if ( !checkKeys( keys, valMap ) )
|
||||
return;
|
||||
|
||||
Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( this, dbid, Tomahawk::PeerInfo::AutoCreate );
|
||||
peerInfo->setContactId( username );
|
||||
peerInfo->setFriendlyName( username );
|
||||
QVariantMap data;
|
||||
data.insert( "dbid", QVariant::fromValue< QString >( dbid ) );
|
||||
peerInfo->setData( data );
|
||||
|
||||
|
||||
SipInfo sipInfo;
|
||||
sipInfo.setNodeId( dbid );
|
||||
if( !host.isEmpty() && port != 0 )
|
||||
{
|
||||
sipInfo.setHost( valMap[ "host" ].toString() );
|
||||
sipInfo.setPort( valMap[ "port" ].toUInt() );
|
||||
sipInfo.setVisible( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
sipInfo.setVisible( false );
|
||||
}
|
||||
peerInfo->setSipInfo( sipInfo );
|
||||
|
||||
peerInfo->setStatus( Tomahawk::PeerInfo::Online );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::peerAuthorization( const QVariantMap& valMap )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "dbid:" << valMap[ "dbid" ].toString() << "offerkey" << valMap[ "offerkey" ].toString();
|
||||
|
||||
QStringList keys( QStringList() << "command" << "dbid" << "offerkey" );
|
||||
if ( !checkKeys( keys, valMap ) )
|
||||
return;
|
||||
|
||||
|
||||
Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( this, valMap[ "dbid" ].toString() );
|
||||
if( peerInfo.isNull() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Received a peer-authorization for a peer we don't know about";
|
||||
return;
|
||||
}
|
||||
|
||||
SipInfo sipInfo = peerInfo->sipInfo();
|
||||
sipInfo.setKey( valMap[ "offerkey" ].toString() );
|
||||
peerInfo->setSipInfo( sipInfo );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////// Syncing methods ////////////////////////////////////
|
||||
|
||||
void
|
||||
HatchetSipPlugin::dbSyncTriggered()
|
||||
{
|
||||
if ( m_sipState != Connected )
|
||||
return;
|
||||
|
||||
if ( !SourceList::instance() || SourceList::instance()->getLocal().isNull() )
|
||||
return;
|
||||
|
||||
QVariantMap sourceMap;
|
||||
sourceMap[ "command" ] = "synctrigger";
|
||||
const Tomahawk::source_ptr src = SourceList::instance()->getLocal();
|
||||
sourceMap[ "name" ] = src->friendlyName();
|
||||
sourceMap[ "alias" ] = QHostInfo::localHostName();
|
||||
sourceMap[ "friendlyname" ] = src->dbFriendlyName();
|
||||
|
||||
if ( !sendBytes( sourceMap ) )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed sending message";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::sendOplog( const QVariantMap& valMap ) const
|
||||
{
|
||||
tLog() << Q_FUNC_INFO;
|
||||
DatabaseCommand_loadOps* cmd = new DatabaseCommand_loadOps( SourceList::instance()->getLocal(), valMap[ "lastrevision" ].toString() );
|
||||
connect( cmd, SIGNAL( done( QString, QString, QList< dbop_ptr > ) ), SLOT( oplogFetched( QString, QString, QList< dbop_ptr > ) ) );
|
||||
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HatchetSipPlugin::oplogFetched( const QString& sinceguid, const QString& /* lastguid */, const QList< dbop_ptr > ops ) const
|
||||
{
|
||||
tLog() << Q_FUNC_INFO;
|
||||
const uint_fast32_t byteMax = 1 << 25;
|
||||
int currBytes = 0;
|
||||
QVariantMap commandMap;
|
||||
commandMap[ "command" ] = "oplog";
|
||||
commandMap[ "startingrevision" ] = sinceguid;
|
||||
currBytes += 60; //baseline for quotes, keys, commas, colons, etc.
|
||||
QVariantList revisions;
|
||||
tLog() << Q_FUNC_INFO << "Found" << ops.size() << "ops";
|
||||
foreach( const dbop_ptr op, ops )
|
||||
{
|
||||
currBytes += 80; //baseline for quotes, keys, commas, colons, etc.
|
||||
QVariantMap revMap;
|
||||
revMap[ "revision" ] = op->guid;
|
||||
currBytes += op->guid.length();
|
||||
revMap[ "singleton" ] = op->singleton;
|
||||
currBytes += 5; //true or false
|
||||
revMap[ "command" ] = op->command;
|
||||
currBytes += op->command.length();
|
||||
currBytes += 5; //true or false
|
||||
if ( op->compressed )
|
||||
{
|
||||
revMap[ "compressed" ] = true;
|
||||
QByteArray b64 = op->payload.toBase64();
|
||||
revMap[ "payload" ] = op->payload.toBase64();
|
||||
currBytes += b64.length();
|
||||
}
|
||||
else
|
||||
{
|
||||
revMap[ "compressed" ] = false;
|
||||
revMap[ "payload" ] = op->payload;
|
||||
currBytes += op->payload.length();
|
||||
}
|
||||
if ( currBytes >= (int)(byteMax - 1000000) ) // tack on an extra 1M for safety as it seems qjson puts in spaces
|
||||
break;
|
||||
else
|
||||
revisions << revMap;
|
||||
}
|
||||
tLog() << Q_FUNC_INFO << "Sending" << revisions.size() << "revisions";
|
||||
commandMap[ "revisions" ] = revisions;
|
||||
|
||||
if ( !sendBytes( commandMap ) )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed sending message, attempting to send a blank message to clear sync state";
|
||||
QVariantMap rescueMap;
|
||||
rescueMap[ "command" ] = "oplog";
|
||||
if ( !sendBytes( rescueMap ) )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Failed to send rescue map; state may be out-of-sync with server";
|
||||
//FIXME: Do we want to disconnect and reconnect at this point to try to get sending working and clear the server state?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
95
src/accounts/hatchet/sip/HatchetSip.h
Normal file
95
src/accounts/hatchet/sip/HatchetSip.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012, Jeff Mitchell <jeff@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TOMAHAWK_SIP_H
|
||||
#define TOMAHAWK_SIP_H
|
||||
|
||||
#include "accounts/AccountDllMacro.h"
|
||||
#include "database/Op.h"
|
||||
#include "sip/SipPlugin.h"
|
||||
#include "account/HatchetAccount.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QtCrypto>
|
||||
|
||||
class WebSocketThreadController;
|
||||
|
||||
const int VERSION = 1;
|
||||
|
||||
class ACCOUNTDLLEXPORT HatchetSipPlugin : public SipPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum SipState {
|
||||
AcquiringVersion,
|
||||
Registering,
|
||||
Connected,
|
||||
Closed
|
||||
};
|
||||
|
||||
public:
|
||||
HatchetSipPlugin( Tomahawk::Accounts::Account *account );
|
||||
|
||||
virtual ~HatchetSipPlugin();
|
||||
|
||||
virtual bool isValid() const;
|
||||
|
||||
virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const SipInfo& info );
|
||||
|
||||
public slots:
|
||||
virtual void connectPlugin();
|
||||
void disconnectPlugin();
|
||||
void checkSettings() {}
|
||||
void configurationChanged() {}
|
||||
void addContact( const QString &, const QString& ) {}
|
||||
void sendMsg( const QString&, const SipInfo& ) {}
|
||||
void webSocketConnected();
|
||||
void webSocketDisconnected();
|
||||
|
||||
signals:
|
||||
void connectWebSocket() const;
|
||||
void disconnectWebSocket() const;
|
||||
void authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service service, const QString& authUrl ) const;
|
||||
void rawBytes( QByteArray bytes ) const;
|
||||
|
||||
private slots:
|
||||
void dbSyncTriggered();
|
||||
void messageReceived( const QByteArray& msg );
|
||||
void connectWebSocket();
|
||||
void onWsFailed( const QString &msg );
|
||||
void onWsClosed( const QString &msg );
|
||||
void oplogFetched( const QString& sinceguid, const QString& lastguid, const QList< dbop_ptr > ops ) const;
|
||||
|
||||
private:
|
||||
bool sendBytes( const QVariantMap& jsonMap ) const;
|
||||
bool checkKeys( QStringList keys, const QVariantMap& map ) const;
|
||||
void newPeer( const QVariantMap& valMap );
|
||||
void peerAuthorization( const QVariantMap& valMap );
|
||||
void sendOplog( const QVariantMap& valMap ) const;
|
||||
Tomahawk::Accounts::HatchetAccount* hatchetAccount() const;
|
||||
|
||||
QPointer< WebSocketThreadController > m_webSocketThreadController;
|
||||
QString m_token;
|
||||
QString m_userid;
|
||||
QString m_uuid;
|
||||
SipState m_sipState;
|
||||
int m_version;
|
||||
QCA::PublicKey* m_publicKey;
|
||||
};
|
||||
|
||||
#endif
|
291
src/accounts/hatchet/sip/WebSocket.cpp
Normal file
291
src/accounts/hatchet/sip/WebSocket.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "WebSocket.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include <functional>
|
||||
|
||||
typedef typename websocketpp::lib::error_code error_code;
|
||||
|
||||
WebSocket::WebSocket( const QString& url )
|
||||
: QObject( nullptr )
|
||||
, m_url( url )
|
||||
, m_outputStream()
|
||||
, m_lastSocketState( QAbstractSocket::UnconnectedState )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "WebSocket constructing";
|
||||
m_client = std::unique_ptr< hatchet_client >( new hatchet_client() );
|
||||
m_client->set_message_handler( std::bind(&onMessage, this, std::placeholders::_1, std::placeholders::_2 ) );
|
||||
m_client->register_ostream( &m_outputStream );
|
||||
}
|
||||
|
||||
|
||||
WebSocket::~WebSocket()
|
||||
{
|
||||
if ( m_connection )
|
||||
m_connection.reset();
|
||||
|
||||
if ( m_socket )
|
||||
{
|
||||
if ( m_socket->state() == QAbstractSocket::ConnectedState )
|
||||
{
|
||||
QObject::disconnect( m_socket, SIGNAL( stateChanged( QAbstractSocket::SocketState ) ) );
|
||||
m_socket->disconnectFromHost();
|
||||
QObject::connect( m_socket, SIGNAL( disconnected() ), m_socket, SLOT( deleteLater() ) );
|
||||
}
|
||||
else
|
||||
m_socket->deleteLater();
|
||||
}
|
||||
|
||||
m_client.reset();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::setUrl( const QString &url )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Setting url to" << url;
|
||||
if ( m_url == url )
|
||||
return;
|
||||
|
||||
if ( m_socket && m_socket->isEncrypted() )
|
||||
reconnectWs();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::connectWs()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Connecting";
|
||||
if ( m_socket )
|
||||
{
|
||||
if ( m_socket->isEncrypted() )
|
||||
return;
|
||||
|
||||
if ( m_socket->state() == QAbstractSocket::ClosingState )
|
||||
QMetaObject::invokeMethod( this, "connectWs", Qt::QueuedConnection );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tLog() << Q_FUNC_INFO << "Establishing new connection";
|
||||
m_socket = QPointer< QSslSocket >( new QSslSocket( nullptr ) );
|
||||
m_socket->addCaCertificate( QSslCertificate::fromPath( ":/hatchet-account/startcomroot.pem").first() );
|
||||
QObject::connect( m_socket, SIGNAL( stateChanged( QAbstractSocket::SocketState ) ), SLOT( socketStateChanged( QAbstractSocket::SocketState ) ) );
|
||||
QObject::connect( m_socket, SIGNAL( sslErrors( const QList< QSslError >& ) ), SLOT( sslErrors( const QList< QSslError >& ) ) );
|
||||
QObject::connect( m_socket, SIGNAL( encrypted() ), SLOT( encrypted() ) );
|
||||
QObject::connect( m_socket, SIGNAL( readyRead() ), SLOT( socketReadyRead() ) );
|
||||
m_socket->connectToHostEncrypted( m_url.host(), m_url.port() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::disconnectWs()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Disconnecting";
|
||||
m_outputStream.seekg( std::ios_base::end );
|
||||
m_outputStream.seekp( std::ios_base::end );
|
||||
if ( m_connection )
|
||||
m_connection.reset();
|
||||
m_queuedMessagesToSend.empty();
|
||||
m_socket->disconnectFromHost();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::reconnectWs()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Reconnecting";
|
||||
QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection );
|
||||
QMetaObject::invokeMethod( this, "connectWs", Qt::QueuedConnection );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::socketStateChanged( QAbstractSocket::SocketState state )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Socket state changed to" << state;
|
||||
switch ( state )
|
||||
{
|
||||
case QAbstractSocket::ClosingState:
|
||||
if ( m_lastSocketState == QAbstractSocket::ClosingState )
|
||||
{
|
||||
// It seems like it does not actually properly close, so force it
|
||||
tLog() << Q_FUNC_INFO << "Got a double closing state, cleaning up and emitting disconnected";
|
||||
m_socket->deleteLater();
|
||||
m_lastSocketState = QAbstractSocket::UnconnectedState;
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case QAbstractSocket::UnconnectedState:
|
||||
if ( m_lastSocketState == QAbstractSocket::UnconnectedState )
|
||||
return;
|
||||
tLog() << Q_FUNC_INFO << "Socket now unconnected, cleaning up and emitting disconnected";
|
||||
m_socket->deleteLater();
|
||||
m_lastSocketState = QAbstractSocket::UnconnectedState;
|
||||
emit disconnected();
|
||||
return;
|
||||
default:
|
||||
;
|
||||
}
|
||||
m_lastSocketState = state;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::sslErrors( const QList< QSslError >& errors )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Encountered errors when trying to connect via SSL";
|
||||
foreach( QSslError error, errors )
|
||||
tLog() << Q_FUNC_INFO << "Error: " << error.errorString();
|
||||
QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::encrypted()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Encrypted connection to Hatchet established";
|
||||
error_code ec;
|
||||
// Adjust wss:// to ws:// in the URL so it doesn't complain that the transport isn't encrypted
|
||||
QString url = m_url.toString();
|
||||
if ( url.startsWith( "wss") )
|
||||
url.remove( 2, 1 );
|
||||
m_connection = m_client->get_connection( url.toStdString(), ec );
|
||||
if ( !m_connection )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Got error creating WS connection, error is:" << QString::fromStdString( ec.message() );
|
||||
disconnectWs();
|
||||
return;
|
||||
}
|
||||
m_client->connect( m_connection );
|
||||
QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection );
|
||||
emit connected();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::readOutput()
|
||||
{
|
||||
if ( !m_connection )
|
||||
return;
|
||||
|
||||
tLog() << Q_FUNC_INFO;
|
||||
|
||||
std::string outputString = m_outputStream.str();
|
||||
if ( outputString.size() > 0 )
|
||||
{
|
||||
m_outputStream.str("");
|
||||
|
||||
tLog() << Q_FUNC_INFO << "Got string of size" << outputString.size() << "from ostream";
|
||||
qint64 sizeWritten = m_socket->write( outputString.data(), outputString.size() );
|
||||
tLog() << Q_FUNC_INFO << "Wrote" << sizeWritten << "bytes to the socket";
|
||||
if ( sizeWritten == -1 )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Error during writing, closing connection";
|
||||
QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_queuedMessagesToSend.size() )
|
||||
{
|
||||
if ( m_connection->get_state() == websocketpp::session::state::open )
|
||||
{
|
||||
foreach( QByteArray message, m_queuedMessagesToSend )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Sending queued message of size" << message.size();
|
||||
m_connection->send( std::string( message.constData(), message.size() ), websocketpp::frame::opcode::TEXT );
|
||||
}
|
||||
|
||||
m_queuedMessagesToSend.clear();
|
||||
QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection );
|
||||
}
|
||||
else
|
||||
QTimer::singleShot( 200, this, SLOT( readOutput() ) );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebSocket::socketReadyRead()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO;
|
||||
|
||||
if ( !m_socket || !m_socket->isEncrypted() )
|
||||
return;
|
||||
|
||||
if ( !m_socket->isValid() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Socket appears to no longer be valid. Something is wrong; disconnecting";
|
||||
QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( qint64 bytes = m_socket->bytesAvailable() )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Bytes available:" << bytes;
|
||||
QByteArray buf;
|
||||
buf.resize( bytes );
|
||||
qint64 bytesRead = m_socket->read( buf.data(), bytes );
|
||||
tLog() << Q_FUNC_INFO << "Bytes read:" << bytesRead; // << ", content is" << websocketpp::utility::to_hex( buf.constData(), bytesRead ).data();
|
||||
if ( bytesRead != bytes )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Error occurred during socket read. Something is wrong; disconnecting";
|
||||
QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection );
|
||||
return;
|
||||
}
|
||||
std::stringstream ss( std::string( buf.constData(), bytesRead ) );
|
||||
ss >> *m_connection;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocket::encodeMessage( const QByteArray &bytes )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Encoding message"; //, message is" << bytes.constData();
|
||||
if ( !m_connection )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Asked to send message but do not have a valid connection!";
|
||||
return;
|
||||
}
|
||||
|
||||
if ( m_connection->get_state() != websocketpp::session::state::open )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Connection not yet open/upgraded, queueing work to send";
|
||||
m_queuedMessagesToSend.append( bytes );
|
||||
}
|
||||
else
|
||||
m_connection->send( std::string( bytes.constData() ), websocketpp::frame::opcode::TEXT );
|
||||
|
||||
QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection );
|
||||
}
|
||||
|
||||
void
|
||||
onMessage( WebSocket* ws, websocketpp::connection_hdl, hatchet_client::message_ptr msg )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Handling message";
|
||||
std::string payload = msg->get_payload();
|
||||
ws->decodedMessage( QByteArray( payload.data(), payload.length() ) );
|
||||
}
|
78
src/accounts/hatchet/sip/WebSocket.h
Normal file
78
src/accounts/hatchet/sip/WebSocket.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef WEBSOCKET__H
|
||||
#define WEBSOCKET__H
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
#include "hatchet_config.hpp"
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <QPointer>
|
||||
#include <QSslSocket>
|
||||
#include <QUrl>
|
||||
|
||||
#include <memory>
|
||||
|
||||
typedef typename websocketpp::client< websocketpp::config::hatchet_client > hatchet_client;
|
||||
|
||||
class WebSocket;
|
||||
|
||||
void onMessage( WebSocket* ws, websocketpp::connection_hdl, hatchet_client::message_ptr msg );
|
||||
|
||||
class DLLEXPORT WebSocket : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WebSocket( const QString& url );
|
||||
virtual ~WebSocket();
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected();
|
||||
void decodedMessage( QByteArray bytes );
|
||||
|
||||
public slots:
|
||||
void setUrl( const QString& url );
|
||||
void connectWs();
|
||||
void disconnectWs();
|
||||
void encodeMessage( const QByteArray& bytes );
|
||||
|
||||
private slots:
|
||||
void socketStateChanged( QAbstractSocket::SocketState state );
|
||||
void sslErrors( const QList< QSslError >& errors );
|
||||
void encrypted();
|
||||
void reconnectWs();
|
||||
void readOutput();
|
||||
void socketReadyRead();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY( WebSocket )
|
||||
|
||||
friend void onMessage( WebSocket *ws, websocketpp::connection_hdl, hatchet_client::message_ptr msg );
|
||||
|
||||
QUrl m_url;
|
||||
std::stringstream m_outputStream;
|
||||
std::unique_ptr< hatchet_client > m_client;
|
||||
hatchet_client::connection_ptr m_connection;
|
||||
QPointer< QSslSocket > m_socket;
|
||||
QAbstractSocket::SocketState m_lastSocketState;
|
||||
QList< QByteArray > m_queuedMessagesToSend;
|
||||
};
|
||||
|
||||
#endif
|
70
src/accounts/hatchet/sip/WebSocketThreadController.cpp
Normal file
70
src/accounts/hatchet/sip/WebSocketThreadController.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "WebSocketThreadController.h"
|
||||
#include "WebSocket.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
|
||||
WebSocketThreadController::WebSocketThreadController( QObject* sip )
|
||||
: QThread( nullptr )
|
||||
, m_sip( sip )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WebSocketThreadController::~WebSocketThreadController()
|
||||
{
|
||||
if ( m_webSocket )
|
||||
{
|
||||
delete m_webSocket;
|
||||
m_webSocket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocketThreadController::setUrl( const QString &url )
|
||||
{
|
||||
m_url = url;
|
||||
if ( m_webSocket )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_webSocket, "setUrl", Qt::QueuedConnection, Q_ARG( QString, url ));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocketThreadController::run()
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Starting";
|
||||
m_webSocket = QPointer< WebSocket >( new WebSocket( m_url ) );
|
||||
if ( m_webSocket && m_sip )
|
||||
{
|
||||
tLog() << Q_FUNC_INFO << "Have a valid websocket and parent";
|
||||
connect( m_sip, SIGNAL( connectWebSocket() ), m_webSocket, SLOT( connectWs() ), Qt::QueuedConnection );
|
||||
connect( m_sip, SIGNAL( disconnectWebSocket() ), m_webSocket, SLOT( disconnectWs() ), Qt::QueuedConnection );
|
||||
connect( m_sip, SIGNAL( rawBytes( QByteArray ) ), m_webSocket, SLOT( encodeMessage( QByteArray ) ), Qt::QueuedConnection );
|
||||
connect( m_webSocket, SIGNAL( connected() ), m_sip, SLOT( webSocketConnected() ), Qt::QueuedConnection );
|
||||
connect( m_webSocket, SIGNAL( disconnected() ), m_sip, SLOT( webSocketDisconnected() ), Qt::QueuedConnection );
|
||||
connect( m_webSocket, SIGNAL( decodedMessage( QByteArray ) ), m_sip, SLOT( messageReceived( QByteArray ) ), Qt::QueuedConnection );
|
||||
QMetaObject::invokeMethod( m_webSocket, "connectWs", Qt::QueuedConnection );
|
||||
exec();
|
||||
delete m_webSocket;
|
||||
m_webSocket = 0;
|
||||
}
|
||||
}
|
49
src/accounts/hatchet/sip/WebSocketThreadController.h
Normal file
49
src/accounts/hatchet/sip/WebSocketThreadController.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef WEBSOCKET_THREAD_CONTROLLER_H
|
||||
#define WEBSOCKET_THREAD_CONTROLLER_H
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QThread>
|
||||
|
||||
class WebSocket;
|
||||
|
||||
class DLLEXPORT WebSocketThreadController : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WebSocketThreadController( QObject* sip );
|
||||
virtual ~WebSocketThreadController();
|
||||
|
||||
void setUrl( const QString &url );
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY( WebSocketThreadController )
|
||||
|
||||
QPointer< WebSocket > m_webSocket;
|
||||
QPointer< QObject > m_sip;
|
||||
QString m_url;
|
||||
};
|
||||
|
||||
#endif
|
72
src/accounts/hatchet/sip/hatchet_config.hpp
Normal file
72
src/accounts/hatchet/sip/hatchet_config.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2013, Jeff Mitchell <jeff@tomahawk-player.org>. 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 WebSocket++ Project 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 PETER THORSON 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 WEBSOCKETPP_CONFIG_HATCHET_CLIENT_HPP
|
||||
#define WEBSOCKETPP_CONFIG_HATCHET_CLIENT_HPP
|
||||
|
||||
#include <websocketpp/config/core_client.hpp>
|
||||
#include <websocketpp/transport/iostream/endpoint.hpp>
|
||||
#include <websocketpp/concurrency/none.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct hatchet_client : public core_client {
|
||||
typedef hatchet_client type;
|
||||
|
||||
typedef websocketpp::concurrency::none concurrency_type;
|
||||
|
||||
typedef core_client::request_type request_type;
|
||||
typedef core_client::response_type response_type;
|
||||
|
||||
typedef core_client::message_type message_type;
|
||||
typedef core_client::con_msg_manager_type con_msg_manager_type;
|
||||
typedef core_client::endpoint_msg_manager_type endpoint_msg_manager_type;
|
||||
|
||||
typedef core_client::alog_type alog_type;
|
||||
typedef core_client::elog_type elog_type;
|
||||
|
||||
typedef core_client::rng_type rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::transport::iostream::endpoint<transport_config> transport_type;
|
||||
|
||||
//static const websocketpp::log::level alog_level = websocketpp::log::alevel::all;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_CONFIG_HATCHET_CLIENT_HPP
|
@@ -54,7 +54,7 @@ Closure::Closure(QObject* sender,
|
||||
|
||||
Closure::Closure(QObject* sender,
|
||||
const char* signal,
|
||||
std::tr1::function<void()> callback)
|
||||
function<void()> callback)
|
||||
: callback_(callback) {
|
||||
Connect(sender, signal);
|
||||
}
|
||||
|
@@ -21,7 +21,13 @@
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
#ifdef _WEBSOCKETPP_CPP11_STL_
|
||||
#include <functional>
|
||||
using std::function;
|
||||
#else
|
||||
#include <tr1/functional>
|
||||
using std::tr1::function;
|
||||
#endif
|
||||
|
||||
#include <QMetaMethod>
|
||||
#include <QObject>
|
||||
@@ -64,7 +70,7 @@ class DLLEXPORT Closure : public QObject, boost::noncopyable {
|
||||
const ClosureArgumentWrapper* val3 = 0);
|
||||
|
||||
Closure(QObject* sender, const char* signal,
|
||||
std::tr1::function<void()> callback);
|
||||
function<void()> callback);
|
||||
|
||||
void setAutoDelete( bool autoDelete ) { autoDelete_ = autoDelete; }
|
||||
|
||||
@@ -87,7 +93,7 @@ class DLLEXPORT Closure : public QObject, boost::noncopyable {
|
||||
void Connect(QObject* sender, const char* signal);
|
||||
|
||||
QMetaMethod slot_;
|
||||
std::tr1::function<void()> callback_;
|
||||
function<void()> callback_;
|
||||
bool autoDelete_;
|
||||
QObject* outOfThreadReceiver_;
|
||||
|
||||
|
Reference in New Issue
Block a user