diff --git a/data/www/auth.html b/data/www/auth.html new file mode 100644 index 000000000..a8aac4c37 --- /dev/null +++ b/data/www/auth.html @@ -0,0 +1,64 @@ + + + + + Allow Tomahawk Access + + + + + Tomahawk - Powered by Playdar + + +
+
+

Allow access to Tomahawk from <%NAME%>

+

+ + +

+ + + + +
+
+ + diff --git a/data/www/auth.na.html b/data/www/auth.na.html new file mode 100644 index 000000000..450a2b114 --- /dev/null +++ b/data/www/auth.na.html @@ -0,0 +1,44 @@ + + + + + Allow Tomahawk Access + + + + + Tomahawk - Powered by Playdar + + +
+

You have allowed access to Tomahawk from <%NAME%>

+

Copy and paste this authentication token into the status bar then close this window.

+

Token: + + diff --git a/data/www/playdar_auth_logo.gif b/data/www/playdar_auth_logo.gif new file mode 100644 index 000000000..22b06bfa3 Binary files /dev/null and b/data/www/playdar_auth_logo.gif differ diff --git a/resources.qrc b/resources.qrc index e1bc45cf5..ea5df7969 100644 --- a/resources.qrc +++ b/resources.qrc @@ -80,5 +80,7 @@ ./data/icons/audio-x-generic-22x22.png ./data/icons/audio-x-generic-32x32.png ./data/icons/audio-x-generic-16x16.png +./data/www/auth.html +./data/www/auth.na.html diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 84afbc167..6465a3ec6 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -68,6 +68,8 @@ set( libSources database/databasecommand_loaddynamicplaylist.cpp database/databasecommand_loadalldynamicplaylists.cpp database/databasecommand_deletedynamicplaylist.cpp + database/databasecommand_addclientauth.cpp + database/databasecommand_clientauthvalid.cpp database/database.cpp playlist/collectionmodel.cpp @@ -207,6 +209,8 @@ set( libHeaders database/databasecommand_loaddynamicplaylist.h database/databasecommand_deletedynamicplaylist.h database/databasecommand_loadalldynamicplaylists.h + database/databasecommand_addclientauth.h + database/databasecommand_clientauthvalid.h network/bufferiodevice.h network/msgprocessor.h diff --git a/src/libtomahawk/database/databasecommand_addclientauth.cpp b/src/libtomahawk/database/databasecommand_addclientauth.cpp new file mode 100644 index 000000000..d8e705952 --- /dev/null +++ b/src/libtomahawk/database/databasecommand_addclientauth.cpp @@ -0,0 +1,46 @@ +/**************************************************************************************** + * Copyright (c) 2011 Leo Franchi * + * * + * This program 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 2 of the License, or (at your option) any later * + * version. * + * * + * This program 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 * + * this program. If not, see . * + ****************************************************************************************/ + +#include "databasecommand_addclientauth.h" + +DatabaseCommand_AddClientAuth::DatabaseCommand_AddClientAuth( const QString& clientToken, + const QString& website, + const QString& name, + const QString& userAgent, + QObject* parent ) + : DatabaseCommand( parent ) + , m_clientToken( clientToken ) + , m_website( website ) + , m_name( name ) + , m_userAgent( userAgent ) +{ +} + +void DatabaseCommand_AddClientAuth::exec(DatabaseImpl* lib) +{ + TomahawkSqlQuery q = lib->newquery(); + q.prepare( "INSERT INTO http_client_auth (token, website, name, ua, mtime, permissions) VALUES (?, ?, ?, ?, ?, ?)" ); + q.addBindValue( m_clientToken ); + q.addBindValue( m_website ); + q.addBindValue( m_name ); + q.addBindValue( m_userAgent ); + q.addBindValue( 0 ); + q.addBindValue( "*" ); + + if( !q.exec() ) { + qWarning() << "Failed to insert http client into auth table!"; + } +} diff --git a/src/libtomahawk/database/databasecommand_addclientauth.h b/src/libtomahawk/database/databasecommand_addclientauth.h new file mode 100644 index 000000000..fccec5947 --- /dev/null +++ b/src/libtomahawk/database/databasecommand_addclientauth.h @@ -0,0 +1,45 @@ +/**************************************************************************************** + * Copyright (c) 2011 Leo Franchi * + * * + * This program 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 2 of the License, or (at your option) any later * + * version. * + * * + * This program 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 * + * this program. If not, see . * + ****************************************************************************************/ + +#ifndef DATABASECOMMAND_ADDCLIENTAUTH_H +#define DATABASECOMMAND_ADDCLIENTAUTH_H + +#include "databaseimpl.h" +#include "databasecommand.h" +#include "dllmacro.h" + +#include + +class DLLEXPORT DatabaseCommand_AddClientAuth : public DatabaseCommand +{ + Q_OBJECT +public: + explicit DatabaseCommand_AddClientAuth( QObject* parent = 0 ) + : DatabaseCommand( parent ) + {} + + explicit DatabaseCommand_AddClientAuth( const QString& clientToken, const QString& website, const QString& name, const QString& userAgent, QObject* parent = 0 ); + + QString commandname() const { return "addclientauth"; } + + virtual void exec( DatabaseImpl* lib ); + virtual bool doesMutates() const { return true; } + +private: + QString m_clientToken, m_website, m_name, m_userAgent; +}; + +#endif // DATABASECOMMAND_ADDCLIENTAUTH_H diff --git a/src/libtomahawk/database/databasecommand_clientauthvalid.cpp b/src/libtomahawk/database/databasecommand_clientauthvalid.cpp new file mode 100644 index 000000000..e751748bf --- /dev/null +++ b/src/libtomahawk/database/databasecommand_clientauthvalid.cpp @@ -0,0 +1,42 @@ +/**************************************************************************************** + * Copyright (c) 2011 Leo Franchi * + * * + * This program 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 2 of the License, or (at your option) any later * + * version. * + * * + * This program 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 * + * this program. If not, see . * + ****************************************************************************************/ + +#include "databasecommand_clientauthvalid.h" + +DatabaseCommand_ClientAuthValid::DatabaseCommand_ClientAuthValid( const QString& clientToken, QObject* parent ) + : DatabaseCommand( parent ) + , m_clientToken( clientToken ) +{ + +} + +void DatabaseCommand_ClientAuthValid::exec(DatabaseImpl* lib) +{ + TomahawkSqlQuery q = lib->newquery(); + q.prepare( "SELECT name FROM http_client_auth WHERE token = ?" ); + q.addBindValue( m_clientToken ); + + if( q.exec() ) { + if( q.next() ) { + QString name = q.value( 0 ).toString(); + emit authValid( m_clientToken, name, true ); + } else { + emit authValid( m_clientToken, QString(), false ); + } + } else { + qWarning() << "Failed to query http auth table for client:" << m_clientToken; + } +} diff --git a/src/libtomahawk/database/databasecommand_clientauthvalid.h b/src/libtomahawk/database/databasecommand_clientauthvalid.h new file mode 100644 index 000000000..efb3c7bb1 --- /dev/null +++ b/src/libtomahawk/database/databasecommand_clientauthvalid.h @@ -0,0 +1,49 @@ +/**************************************************************************************** + * Copyright (c) 2011 Leo Franchi * + * * + * This program 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 2 of the License, or (at your option) any later * + * version. * + * * + * This program 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 * + * this program. If not, see . * + ****************************************************************************************/ + +#ifndef DATABASECOMMAND_CLIENTAUTHVALID_H +#define DATABASECOMMAND_CLIENTAUTHVALID_H + +#include "databaseimpl.h" +#include "databasecommand.h" +#include "dllmacro.h" + +#include + +class DLLEXPORT DatabaseCommand_ClientAuthValid : public DatabaseCommand +{ + Q_OBJECT +public: + explicit DatabaseCommand_ClientAuthValid( QObject* parent = 0 ) + : DatabaseCommand( parent ) + {} + + explicit DatabaseCommand_ClientAuthValid( const QString& clientToken, QObject* parent = 0 ); + + QString commandname() const { return "clientauthvalid"; } + + virtual void exec( DatabaseImpl* lib ); + virtual bool doesMutates() const { return false; } + +signals: + // if auth is invalid name is empty + void authValid( const QString& clientToken, const QString& name, bool valid ); + +private: + QString m_clientToken; +}; + +#endif // DATABASECOMMAND_CLIENTAUTHVALID_H diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index 7746f8a0f..5652dfae8 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -16,8 +16,7 @@ */ #include "schema.sql.h" -#define CURRENT_SCHEMA_VERSION 20 - +#define CURRENT_SCHEMA_VERSION 21 DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) : QObject( (QObject*) parent ) diff --git a/src/libtomahawk/database/schema.sql b/src/libtomahawk/database/schema.sql index ad8b000d1..c30132ab9 100644 --- a/src/libtomahawk/database/schema.sql +++ b/src/libtomahawk/database/schema.sql @@ -241,6 +241,16 @@ CREATE TABLE IF NOT EXISTS playback_log ( CREATE INDEX playback_log_source ON playback_log(source); CREATE INDEX playback_log_track ON playback_log(track); +-- auth information for http clients + +CREATE TABLE IF NOT EXISTS http_client_auth ( + token TEXT NOT NULL PRIMARY KEY, + website TEXT NOT NULL, + name TEXT NOT NULL, + ua TEXT, + mtime INTEGER, + permissions TEXT NOT NULL +); -- Schema version, and misc tomahawk settings relating to the collection db @@ -250,4 +260,4 @@ CREATE TABLE IF NOT EXISTS settings ( v TEXT NOT NULL DEFAULT '' ); -INSERT INTO settings(k,v) VALUES('schema_version', '20'); +INSERT INTO settings(k,v) VALUES('schema_version', '21'); diff --git a/src/libtomahawk/database/schema.sql.h b/src/libtomahawk/database/schema.sql.h index a580c1851..9283091f3 100644 --- a/src/libtomahawk/database/schema.sql.h +++ b/src/libtomahawk/database/schema.sql.h @@ -1,5 +1,5 @@ /* - This file was automatically generated from ./schema.sql on Wed Feb 23 12:39:07 CET 2011. + This file was automatically generated from schema.sql on Thu Feb 24 19:05:46 EST 2011. */ static const char * tomahawk_schema_sql = @@ -161,11 +161,19 @@ static const char * tomahawk_schema_sql = ");" "CREATE INDEX playback_log_source ON playback_log(source);" "CREATE INDEX playback_log_track ON playback_log(track);" +"CREATE TABLE IF NOT EXISTS http_client_auth (" +" token TEXT NOT NULL PRIMARY KEY," +" website TEXT NOT NULL," +" name TEXT NOT NULL," +" ua TEXT," +" mtime INTEGER," +" permissions TEXT NOT NULL" +");" "CREATE TABLE IF NOT EXISTS settings (" " k TEXT NOT NULL PRIMARY KEY," " v TEXT NOT NULL DEFAULT ''" ");" -"INSERT INTO settings(k,v) VALUES('schema_version', '20');" +"INSERT INTO settings(k,v) VALUES('schema_version', '21');" ; const char * get_tomahawk_sql() diff --git a/src/tomahawk.protocol b/src/tomahawk.protocol new file mode 100644 index 000000000..3a393aa61 --- /dev/null +++ b/src/tomahawk.protocol @@ -0,0 +1,12 @@ +[Protocol] +exec=/home/leo/kde/tomahawk/build/tomahawk "%u" +protocol=tomahawk +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false + diff --git a/src/web/api_v1.h b/src/web/api_v1.h index 6d89beddd..daaacc4fb 100644 --- a/src/web/api_v1.h +++ b/src/web/api_v1.h @@ -17,8 +17,15 @@ #include #include +#include #include "network/servent.h" +#include "tomahawkutils.h" +#include "tomahawk/tomahawkapp.h" +#include +#include +#include +#include class Api_v1 : public QxtWebSlotService { @@ -32,7 +39,81 @@ public: } public slots: + + // authenticating uses /auth_1 + // we redirect to /auth_2 for the callback + void auth_1( QxtWebRequestEvent* event ) { + qDebug() << "AUTH_1 HTTP" << event->url.toString(); + + if( !event->url.hasQueryItem( "website" ) || !event->url.hasQueryItem( "name" ) ) { + qDebug() << "Malformed HTTP resolve request"; + send404( event ); + } + + QString formToken = uuid(); + + if( event->url.hasQueryItem( "json" ) ) { // JSON response + QVariantMap m; + m[ "formtoken" ] = formToken; + sendJSON( m, event ); + } else { // webpage request + QString authPage = RESPATH "www/auth.html"; + QHash< QString, QString > args; + if( event->url.hasQueryItem( "receiverurl" ) ) + args[ "url" ] = QUrl::fromPercentEncoding( event->url.queryItemValue( "receiverurl" ).toUtf8() ); + args[ "formtoken" ] = formToken; + args[ "website" ] = QUrl::fromPercentEncoding( event->url.queryItemValue( "website" ).toUtf8() ); + args[ "name" ] = QUrl::fromPercentEncoding( event->url.queryItemValue( "name" ).toUtf8() ); + sendWebpageWithArgs( event, authPage, args ); + } + } + + void auth_2( QxtWebRequestEvent* event ) { + + qDebug() << "AUTH_2 HTTP" << event->url.toString(); + QUrl url = event->url; + url.setEncodedQuery( event->content->readAll() ); + if( !url.hasQueryItem( "website" ) || !url.hasQueryItem( "name" ) || !url.hasQueryItem( "formtoken" ) ) { + qDebug() << "Malformed HTTP resolve request"; + qDebug() << url.hasQueryItem( "website" ) << url.hasQueryItem( "name" ) << url.hasQueryItem( "formtoken" ); + send404( event ); + return; + } + + QString website = QUrl::fromPercentEncoding( url.queryItemValue( "website" ).toUtf8() ); + QString name = QUrl::fromPercentEncoding( url.queryItemValue( "name" ).toUtf8() ); + QByteArray authtoken = uuid().toLatin1(); + qDebug() << "HEADERS:" << event->headers; + if( !url.hasQueryItem( "receiverurl" ) && url.queryItemValue( "receiverurl" ).isEmpty() ) { //no receiver url, so do it ourselves + QString receiverUrl = QUrl::fromPercentEncoding( url.queryItemValue( "receiverurl" ).toUtf8() ); + if( url.hasQueryItem( "json" ) ) { + QVariantMap m; + m[ "authtoken" ] = authtoken; + + sendJSON( m, event ); + } else { + QString authPage = RESPATH "www/auth.na.html"; + QHash< QString, QString > args; + args[ "authcode" ] = authPage; + args[ "website" ] = QUrl::fromPercentEncoding( url.queryItemValue( "website" ).toUtf8() ); + args[ "name" ] = QUrl::fromPercentEncoding( url.queryItemValue( "name" ).toUtf8() ); + sendWebpageWithArgs( event, authPage, args ); + } + } else { // do what the client wants + QUrl receiverurl = QUrl( url.queryItemValue( "receiverurl" ).toUtf8(), QUrl::TolerantMode ); + receiverurl.addEncodedQueryItem( "authtoken", "#" + authtoken ); + qDebug() << "Got receiver url:" << receiverurl.toString(); + + QxtWebRedirectEvent* e = new QxtWebRedirectEvent( event->sessionID, event->requestID, receiverurl.toString() ); + postEvent( e ); + // TODO validation of receiverurl? + } + + DatabaseCommand_AddClientAuth* dbcmd = new DatabaseCommand_AddClientAuth( authtoken, website, name, event->headers.key( "ua" ) ); + Database::instance()->enqueue( QSharedPointer(dbcmd) ); + } + // all v1 api calls go to /api/ void api(QxtWebRequestEvent* event) { @@ -80,18 +161,35 @@ public slots: qDebug() << "404" << event->url.toString(); QxtWebPageEvent* wpe = new QxtWebPageEvent(event->sessionID, event->requestID, "

Not Found

"); wpe->status = 404; - wpe->statusMessage = "not found"; + wpe->statusMessage = "not feventound"; postEvent( wpe ); } void stat( QxtWebRequestEvent* event ) { + qDebug() << "Got Stat request:" << event->url.toString(); + m_storedEvent = event; + if( !event->content.isNull() ) + qDebug() << "BODY:" << event->content->readAll(); + if( event->url.hasQueryItem( "auth" ) ) { // check for auth status + DatabaseCommand_ClientAuthValid* dbcmd = new DatabaseCommand_ClientAuthValid( event->url.queryItemValue( "auth" ), this ); + connect( dbcmd, SIGNAL( authValid( QString, QString, bool ) ), this, SLOT( statResult( QString, QString, bool ) ) ); + Database::instance()->enqueue( QSharedPointer(dbcmd) ); + + } else { + statResult( QString(), QString(), false ); + } + } + + void statResult( const QString& clientToken, const QString& name, bool valid ) { QVariantMap m; m.insert( "name", "playdar" ); m.insert( "version", "0.1.1" ); // TODO (needs to be >=0.1.1 for JS to work) - m.insert( "authenticated", true ); // TODO + m.insert( "authenticated", valid ); // TODO m.insert( "capabilities", QVariantList() ); - sendJSON( m, event ); + sendJSON( m, m_storedEvent ); + + m_storedEvent = 0; } void resolve( QxtWebRequestEvent* event ) @@ -119,6 +217,12 @@ public slots: sendJSON( r, event ); } + void staticdata( QxtWebRequestEvent* event ) { + if( event->url.path().contains( "playdar_auth_logo.gif" ) ) { + // TODO handle + } + } + void get_results( QxtWebRequestEvent* event ) { if( !event->url.hasQueryItem("qid") ) @@ -174,6 +278,23 @@ public slots: qDebug() << "JSON response" << event->url.toString() << body; } + // load an html template from a file, replace args from map + // then serve + void sendWebpageWithArgs( QxtWebRequestEvent* event, const QString& filenameSource, const QHash< QString, QString >& args ) { + if( !QFile::exists( filenameSource ) ) + qWarning() << "Passed invalid file for html source:" << filenameSource; + + QFile f( filenameSource ); + f.open( QIODevice::ReadOnly ); + QByteArray html = f.readAll(); + + foreach( const QString& param, args.keys() ) { + html.replace( QString( "<%%1%>" ).arg( param.toUpper() ), args.value( param ).toUtf8() ); + } + + QxtWebPageEvent* e = new QxtWebPageEvent( event->sessionID, event->requestID, html ); + postEvent( e ); + } void index(QxtWebRequestEvent* event) { @@ -182,6 +303,8 @@ public slots: } +private: + QxtWebRequestEvent* m_storedEvent; }; #endif