From b75a575f51440fcc8774fdf7e6efed88e05a9bc3 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 3 May 2012 14:10:04 -0400 Subject: [PATCH 01/25] Don't show spacers on osx in settings dialog --- src/SettingsDialog.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index 3999121de..6cd2f5266 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -116,6 +116,13 @@ SettingsDialog::SettingsDialog( QWidget *parent ) m_proxySettings.setSizeGripEnabled( true ); QSizeGrip* p = m_proxySettings.findChild< QSizeGrip* >(); p->setFixedSize( 0, 0 ); + + ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer ); + ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer_2 ); + ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer_4 ); + delete ui->verticalSpacer; + delete ui->verticalSpacer_2; + delete ui->verticalSpacer_4; #endif // Accounts From 7adb3fc737d00a23326338ff142fd2e56501ee13 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 4 May 2012 14:13:57 -0400 Subject: [PATCH 02/25] Download binary resolver listing --- src/libtomahawk/AtticaManager.cpp | 81 +++++++++++++++++++++++-- src/libtomahawk/AtticaManager.h | 10 ++- src/libtomahawk/TomahawkSettings.h | 2 +- src/libtomahawk/TomahawkSettingsGui.cpp | 14 +++-- 4 files changed, 93 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 0dd491e8f..deeabeff6 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -42,11 +42,13 @@ AtticaManager* AtticaManager::s_instance = 0; AtticaManager::AtticaManager( QObject* parent ) : QObject( parent ) + , m_resolverJobsLoaded( 0 ) { connect( &m_manager, SIGNAL( providerAdded( Attica::Provider ) ), this, SLOT( providerAdded( Attica::Provider ) ) ); // resolvers - m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); +// m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); + m_manager.addProviderFile( QUrl( "http://localhost/resolvers/providers.xml" ) ); qRegisterMetaType< Attica::Content >( "Attica::Content" ); } @@ -250,9 +252,32 @@ AtticaManager::providerAdded( const Provider& provider ) if ( provider.name() == "Tomahawk Resolvers" ) { m_resolverProvider = provider; + m_resolvers.clear(); + + m_resolverStates = TomahawkSettingsGui::instanceGui()->atticaResolverStates(); + + ListJob* job = m_resolverProvider.requestCategories(); + connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( categoriesReturned( Attica::BaseJob* ) ) ); + job->start(); + } +} + + +void +AtticaManager::categoriesReturned( BaseJob* j ) +{ + ListJob< Category >* job = static_cast< ListJob< Category >* >( j ); + + Category::List categories = job->itemList(); + foreach ( const Category& category, categories ) + { + ListJob< Content >* job = m_resolverProvider.searchContents( Category::List() << category, QString(), Provider::Downloads, 0, 50 ); + + if ( category.name() == "Resolver" ) + connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolversList( Attica::BaseJob* ) ) ); + else if ( category.name() == "BinaryResolver" ) + connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( binaryResolversList( Attica::BaseJob* ) ) ); - ListJob< Content >* job = m_resolverProvider.searchContents( Category::List(), QString(), Provider::Downloads, 0, 30 ); - connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolversList( Attica::BaseJob* ) ) ); job->start(); } } @@ -263,8 +288,7 @@ AtticaManager::resolversList( BaseJob* j ) { ListJob< Content >* job = static_cast< ListJob< Content >* >( j ); - m_resolvers = job->itemList(); - m_resolverStates = TomahawkSettingsGui::instanceGui()->atticaResolverStates(); + m_resolvers.append( job->itemList() ); // Sanity check. if any resolvers are installed that don't exist on the hd, remove them. foreach ( const QString& rId, m_resolverStates.keys() ) @@ -272,6 +296,9 @@ AtticaManager::resolversList( BaseJob* j ) if ( m_resolverStates[ rId ].state == Installed || m_resolverStates[ rId ].state == NeedsUpgrade ) { + if ( m_resolverStates[ rId ].binary ) + continue; + // Guess location on disk QDir dir( QString( "%1/atticaresolvers/%2" ).arg( TomahawkUtils::appDataDir().absolutePath() ).arg( rId ) ); if ( !dir.exists() ) @@ -303,7 +330,49 @@ AtticaManager::resolversList( BaseJob* j ) syncServerData(); - emit resolversLoaded( m_resolvers ); + if ( ++m_resolverJobsLoaded == 2 ) + emit resolversLoaded( m_resolvers ); +} + + +void +AtticaManager::binaryResolversList( BaseJob* j ) +{ + ListJob< Content >* job = static_cast< ListJob< Content >* >( j ); + + Content::List binaryResolvers = job->itemList(); + + // NOTE: No binary support for linux distros + QString platform; +#ifdef Q_OS_MAC + platform = "osx"; +#elif Q_OS_WIN + platform = "win"; +#endif + + // NOTE HACK + // At the moment we are going to assume that all binary resolvers also have an associated full-fledged Tomahawk Account + // like SpotifyAccount. + + foreach ( const Content& c, binaryResolvers ) + { + if ( !c.attribute( "typeid" ).isEmpty() && c.attribute( "typeid" ) == platform ) + { + // We have a binary resolver for this platform + m_resolvers.append( c ); + if ( !m_resolverStates.contains( c.id() ) ) + { + Resolver r; + r.binary = true; + m_resolverStates.insert( c.id(), r ); + } + + + } + } + + if ( ++m_resolverJobsLoaded == 2 ) + emit resolversLoaded( m_resolvers ); } diff --git a/src/libtomahawk/AtticaManager.h b/src/libtomahawk/AtticaManager.h index 27bcacda6..70517c334 100644 --- a/src/libtomahawk/AtticaManager.h +++ b/src/libtomahawk/AtticaManager.h @@ -51,13 +51,14 @@ public: int userRating; // 0-100 ResolverState state; QPixmap* pixmap; + bool binary; // internal bool pixmapDirty; - Resolver( const QString& v, const QString& path, int userR, ResolverState s ) - : version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), pixmapDirty( false ) {} - Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), pixmapDirty( false ) {} + Resolver( const QString& v, const QString& path, int userR, ResolverState s, bool resolver ) + : version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), binary( false ), pixmapDirty( false ) {} + Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), binary( false ), pixmapDirty( false ) {} }; typedef QHash< QString, AtticaManager::Resolver > StateHash; @@ -111,7 +112,9 @@ signals: private slots: void providerAdded( const Attica::Provider& ); + void categoriesReturned( Attica::BaseJob* ); void resolversList( Attica::BaseJob* ); + void binaryResolversList( Attica::BaseJob* ); void resolverDownloadFinished( Attica::BaseJob* ); void payloadFetched(); @@ -131,6 +134,7 @@ private: Attica::Content::List m_resolvers; StateHash m_resolverStates; + int m_resolverJobsLoaded; QMap< QString, Tomahawk::Accounts::Account* > m_customAccounts; static AtticaManager* s_instance; diff --git a/src/libtomahawk/TomahawkSettings.h b/src/libtomahawk/TomahawkSettings.h index e7c6156e7..b998c46d6 100644 --- a/src/libtomahawk/TomahawkSettings.h +++ b/src/libtomahawk/TomahawkSettings.h @@ -30,7 +30,7 @@ #include "DllMacro.h" -#define TOMAHAWK_SETTINGS_VERSION 10 +#define TOMAHAWK_SETTINGS_VERSION 11 /** * Convenience wrapper around QSettings for tomahawk-specific config diff --git a/src/libtomahawk/TomahawkSettingsGui.cpp b/src/libtomahawk/TomahawkSettingsGui.cpp index dea0c5236..8aa15b3c2 100644 --- a/src/libtomahawk/TomahawkSettingsGui.cpp +++ b/src/libtomahawk/TomahawkSettingsGui.cpp @@ -30,7 +30,7 @@ inline QDataStream& operator<<(QDataStream& out, const AtticaManager::StateHash& foreach( const QString& key, states.keys() ) { AtticaManager::Resolver resolver = states[ key ]; - out << key << resolver.version << resolver.scriptPath << (qint32)resolver.state << resolver.userRating; + out << key << resolver.version << resolver.scriptPath << (qint32)resolver.state << resolver.userRating << resolver.binary; } return out; } @@ -38,19 +38,25 @@ inline QDataStream& operator<<(QDataStream& out, const AtticaManager::StateHash& inline QDataStream& operator>>(QDataStream& in, AtticaManager::StateHash& states) { - quint32 count = 0, version = 0; - in >> version; + quint32 count = 0, configVersion = 0; + in >> configVersion; in >> count; for ( uint i = 0; i < count; i++ ) { QString key, version, scriptPath; qint32 state, userRating; + bool binary = false; in >> key; in >> version; in >> scriptPath; in >> state; in >> userRating; - states[ key ] = AtticaManager::Resolver( version, scriptPath, userRating, (AtticaManager::ResolverState)state ); + if ( configVersion > 10 ) + { + // V11 includes 'bool binary' flag + in >> binary; + } + states[ key ] = AtticaManager::Resolver( version, scriptPath, userRating, (AtticaManager::ResolverState)state, binary ); } return in; } From 42a1119cba9793fa213b114de7c61b174369ca07 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 10 May 2012 23:01:55 -0400 Subject: [PATCH 03/25] Beginning of tomahawk-side osx handling of binary resolvers --- resources.qrc | 1 + src/libtomahawk/AtticaManager.cpp | 126 +++++--------- src/libtomahawk/CMakeLists.txt | 4 +- .../database/DatabaseCollection.cpp | 1 + src/libtomahawk/database/DatabaseCollection.h | 1 + src/libtomahawk/utils/TomahawkUtils.cpp | 163 ++++++++++++++++++ src/libtomahawk/utils/TomahawkUtils.h | 7 + src/libtomahawk/utils/TomahawkUtils_Mac.h | 37 ++++ src/libtomahawk/utils/TomahawkUtils_Mac.mm | 79 +++++++++ src/main.cpp | 2 +- 10 files changed, 335 insertions(+), 86 deletions(-) create mode 100644 src/libtomahawk/utils/TomahawkUtils_Mac.h diff --git a/resources.qrc b/resources.qrc index 602774165..ee77f0f51 100644 --- a/resources.qrc +++ b/resources.qrc @@ -143,5 +143,6 @@ data/images/process-stop.png data/icons/tomahawk-icon-128x128-grayscale.png data/images/collection.png + data/misc/tomahawk_pubkey.pem diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index deeabeff6..b8636f4b6 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -23,8 +23,6 @@ #include "Pipeline.h" #include -#include -#include #include #include @@ -451,6 +449,7 @@ AtticaManager::installResolver( const Content& resolver, bool autoCreateAccount connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolverDownloadFinished( Attica::BaseJob* ) ) ); job->setProperty( "resolverId", resolver.id() ); job->setProperty( "createAccount", autoCreateAccount ); + job->setProperty( "binarySignature", resolver.attribute("signature")); job->start(); } @@ -487,6 +486,7 @@ AtticaManager::resolverDownloadFinished ( BaseJob* j ) connect( reply, SIGNAL( finished() ), this, SLOT( payloadFetched() ) ); reply->setProperty( "resolverId", job->property( "resolverId" ) ); reply->setProperty( "createAccount", job->property( "createAccount" ) ); + reply->setProperty( "binarySignature", job->property( "binarySignature" ) ); } else { @@ -513,23 +513,50 @@ AtticaManager::payloadFetched() f.write( reply->readAll() ); f.close(); - QString resolverId = reply->property( "resolverId" ).toString(); - QDir dir( extractPayload( f.fileName(), resolverId ) ); - QString resolverPath = dir.absoluteFilePath( m_resolverStates[ resolverId ].scriptPath ); - - if ( !resolverPath.isEmpty() ) + bool installedSuccessfully = false; + const QString resolverId = reply->property( "resolverId" ).toString(); + if ( m_resolverStates[ resolverId ].binary ) { - // update with absolute, not relative, path - m_resolverStates[ resolverId ].scriptPath = resolverPath; - - if ( reply->property( "createAccount" ).toBool() ) + // First ensure the signature matches. If we can't verify it, abort! + const QString signature = reply->property( "binarySignature" ).toString(); + // Must have a signature for binary resolvers... + Q_ASSERT( !signature.isEmpty() ); + if ( signature.isEmpty() ) + return; + if ( !TomahawkUtils::verifyFile( f.fileName(), signature ) ) { - // Do the install / add to tomahawk - Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, "resolveraccount", true ); - Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver ); - TomahawkSettings::instance()->addAccount( resolver->accountId() ); + qWarning() << "FILE SIGNATURE FAILED FOR BINARY RESOLVER! WARNING! :" << f.fileName() << signature; + return; } +#ifdef Q_OS_MAC +#elif Q_OS_WIN +#endif + } + else + { + QDir dir( TomahawkUtils::extractScriptPayload( f.fileName(), resolverId ) ); + QString resolverPath = dir.absoluteFilePath( m_resolverStates[ resolverId ].scriptPath ); + + if ( !resolverPath.isEmpty() ) + { + // update with absolute, not relative, path + m_resolverStates[ resolverId ].scriptPath = resolverPath; + + if ( reply->property( "createAccount" ).toBool() ) + { + // Do the install / add to tomahawk + Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, "resolveraccount", true ); + Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver ); + TomahawkSettings::instance()->addAccount( resolver->accountId() ); + } + + installedSuccessfully = true; + } + } + + if ( installedSuccessfully ) + { m_resolverStates[ resolverId ].state = Installed; TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates ); emit resolverInstalled( resolverId ); @@ -543,75 +570,6 @@ AtticaManager::payloadFetched() } -QString -AtticaManager::extractPayload( const QString& filename, const QString& resolverId ) const -{ - // uses QuaZip to extract the temporary zip file to the user's tomahawk data/resolvers directory - QuaZip zipFile( filename ); - if ( !zipFile.open( QuaZip::mdUnzip ) ) - { - tLog() << "Failed to QuaZip open:" << zipFile.getZipError(); - return QString(); - } - - if ( !zipFile.goToFirstFile() ) - { - tLog() << "Failed to go to first file in zip archive: " << zipFile.getZipError(); - return QString(); - } - - QDir resolverDir = TomahawkUtils::appDataDir(); - if ( !resolverDir.mkpath( QString( "atticaresolvers/%1" ).arg( resolverId ) ) ) - { - tLog() << "Failed to mkdir resolver save dir: " << TomahawkUtils::appDataDir().absoluteFilePath( QString( "atticaresolvers/%1" ).arg( resolverId ) ); - return QString(); - } - resolverDir.cd( QString( "atticaresolvers/%1" ).arg( resolverId ) ); - tDebug() << "Installing resolver to:" << resolverDir.absolutePath(); - - QuaZipFile fileInZip( &zipFile ); - do - { - QuaZipFileInfo info; - zipFile.getCurrentFileInfo( &info ); - - if ( !fileInZip.open( QIODevice::ReadOnly ) ) - { - tLog() << "Failed to open file inside zip archive:" << info.name << zipFile.getZipName() << "with error:" << zipFile.getZipError(); - continue; - } - - QFile out( resolverDir.absoluteFilePath( fileInZip.getActualFileName() ) ); - - QStringList parts = fileInZip.getActualFileName().split( "/" ); - if ( parts.size() > 1 ) - { - QStringList dirs = parts.mid( 0, parts.size() - 1 ); - QString dirPath = dirs.join( "/" ); // QDir translates / to \ internally if necessary - resolverDir.mkpath( dirPath ); - } - - // make dir if there is one needed - QDir d( fileInZip.getActualFileName() ); - - tDebug() << "Writing to output file..." << out.fileName(); - if ( !out.open( QIODevice::WriteOnly ) ) - { - tLog() << "Failed to open resolver extract file:" << out.errorString() << info.name; - continue; - } - - - out.write( fileInZip.readAll() ); - out.close(); - fileInZip.close(); - - } while ( zipFile.goToNextFile() ); - - return resolverDir.absolutePath(); -} - - void AtticaManager::uninstallResolver( const QString& pathToResolver ) { diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index c77b302e5..af98f0fdc 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -391,6 +391,7 @@ IF( APPLE ) infosystem/infoplugins/mac/Adium.mm infosystem/infoplugins/mac/AdiumPlugin.cpp utils/TomahawkUtils_Mac.mm + mac/FileHelpers.mm thirdparty/Qocoa/qsearchfield_mac.mm ) SET_SOURCE_FILES_PROPERTIES(utils/TomahawkUtils_Mac.mm PROPERTIES COMPILE_FLAGS "-fvisibility=default") @@ -400,10 +401,11 @@ IF( APPLE ) # System ${COREAUDIO_LIBRARY} ${COREFOUNDATION_LIBRARY} - ${FOUNDATION_LIBRARY} + ${FOUNDATION_LIBRARY} ${SCRIPTINGBRIDGE_LIBRARY} /System/Library/Frameworks/AppKit.framework + /System/Library/Frameworks/Security.framework ) ELSE( APPLE ) SET( libGuiSources ${libGuiSources} thirdparty/Qocoa/qsearchfield.cpp ) diff --git a/src/libtomahawk/database/DatabaseCollection.cpp b/src/libtomahawk/database/DatabaseCollection.cpp index cb864cc66..2dca1980d 100644 --- a/src/libtomahawk/database/DatabaseCollection.cpp +++ b/src/libtomahawk/database/DatabaseCollection.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/database/DatabaseCollection.h b/src/libtomahawk/database/DatabaseCollection.h index a9e9fae65..de94bbd6c 100644 --- a/src/libtomahawk/database/DatabaseCollection.h +++ b/src/libtomahawk/database/DatabaseCollection.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index 8fcf170af..c2cc93b18 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -39,6 +39,9 @@ #include #include +#include +#include + #ifdef Q_WS_WIN #include #include @@ -49,6 +52,10 @@ #include #endif +#ifdef QCA2_FOUND +#include +#endif + namespace TomahawkUtils { static quint64 s_infosystemRequestId = 0; @@ -676,4 +683,160 @@ SharedTimeLine::disconnectNotify( const char* signal ) } +bool +verifyFile( const QString &filePath, const QString &signature ) +{ + QCA::Initializer init; + + if( !QCA::isSupported( "sha1" ) ) + { + qWarning() << "SHA1 not supported by QCA, aborting."; + return false; + } + + // The signature for the resolver.zip was created like so: + // openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[2]}" | openssl enc -base64 + // which means we need to decode it with QCA's DSA public key signature verification tools + // The input data is: + // file -> SHA1 binary format -> DSS1/DSA signed -> base64 encoded. + + // Step 1: Load the public key + // Public key is in :/data/misc/tomahawk_pubkey.pem + QFile f( ":/data/misc/tomahawk_pubkey.pem" ); + if ( !f.open( QIODevice::ReadOnly ) ) + { + qWarning() << "Unable to read public key from resources!"; + return false; + } + + const QString pubkeyData = QString::fromUtf8( f.readAll() ); + QCA::ConvertResult conversionResult; + QCA::PublicKey publicKey = QCA::PublicKey::fromPEM( pubkeyData, &conversionResult ); + if ( QCA::ConvertGood != conversionResult) + { + qWarning() << "Public key reading/loading failed! Tried to load public key:" << pubkeyData; + return false; + } + + if ( !publicKey.canVerify() ) + { + qWarning() << "Loaded Tomahawk public key but cannot use it to verify! What is up...."; + return false; + } + + // Step 2: Get the SHA1 of the file contents + QFile toVerify( filePath ); + if ( !toVerify.exists() || !toVerify.open( QIODevice::ReadOnly ) ) + { + qWarning() << "Failed to open file we are trying to verify!" << filePath; + return false; + } + + QCA::Hash fileHash = QCA::Hash( "sha1 "); + //QCA::SecureArray fileData( toVerify.readAll() ); + //fileHash.update( fileData ); + const QByteArray fileHashData = QCA::Hash( "sha1" ).hash( toVerify.readAll() ).toByteArray(); + toVerify.close(); + + // Step 3: Base64 decode the signature + QCA::Base64 decoder( QCA::Decode ); + const QByteArray decodedSignature = decoder.decode( QCA::SecureArray( signature.trimmed().toUtf8() ) ).toByteArray(); + if ( decodedSignature.isEmpty() ) + { + qWarning() << "Got empty signature after we tried to decode it from Base64:" << signature.trimmed().toUtf8() << decodedSignature.toBase64(); + return false; + } + + // Step 4: Do the actual verifying! + const bool result = publicKey.verifyMessage( fileHashData, decodedSignature, QCA::EMSA1_SHA1, QCA::DERSequence ); + if ( !result ) + { + qWarning() << "File" << filePath << "FAILED VERIFICATION against our input signature!"; + return false; + } + + qDebug() << "Successfully verified signature of downloaded file:" << filePath; + + return true; +} + + +QString +extractScriptPayload( const QString& filename, const QString& resolverId ) +{ + // uses QuaZip to extract the temporary zip file to the user's tomahawk data/resolvers directory + QuaZip zipFile( filename ); + if ( !zipFile.open( QuaZip::mdUnzip ) ) + { + tLog() << "Failed to QuaZip open:" << zipFile.getZipError(); + return QString(); + } + + if ( !zipFile.goToFirstFile() ) + { + tLog() << "Failed to go to first file in zip archive: " << zipFile.getZipError(); + return QString(); + } + + QDir resolverDir = appDataDir(); + if ( !resolverDir.mkpath( QString( "atticaresolvers/%1" ).arg( resolverId ) ) ) + { + tLog() << "Failed to mkdir resolver save dir: " << TomahawkUtils::appDataDir().absoluteFilePath( QString( "atticaresolvers/%1" ).arg( resolverId ) ); + return QString(); + } + resolverDir.cd( QString( "atticaresolvers/%1" ).arg( resolverId ) ); + tDebug() << "Installing resolver to:" << resolverDir.absolutePath(); + + QuaZipFile fileInZip( &zipFile ); + do + { + QuaZipFileInfo info; + zipFile.getCurrentFileInfo( &info ); + + if ( !fileInZip.open( QIODevice::ReadOnly ) ) + { + tLog() << "Failed to open file inside zip archive:" << info.name << zipFile.getZipName() << "with error:" << zipFile.getZipError(); + continue; + } + + QFile out( resolverDir.absoluteFilePath( fileInZip.getActualFileName() ) ); + + QStringList parts = fileInZip.getActualFileName().split( "/" ); + if ( parts.size() > 1 ) + { + QStringList dirs = parts.mid( 0, parts.size() - 1 ); + QString dirPath = dirs.join( "/" ); // QDir translates / to \ internally if necessary + resolverDir.mkpath( dirPath ); + } + + // make dir if there is one needed + QDir d( fileInZip.getActualFileName() ); + + tDebug() << "Writing to output file..." << out.fileName(); + if ( !out.open( QIODevice::WriteOnly ) ) + { + tLog() << "Failed to open resolver extract file:" << out.errorString() << info.name; + continue; + } + + + out.write( fileInZip.readAll() ); + out.close(); + fileInZip.close(); + + } while ( zipFile.goToNextFile() ); + + return resolverDir.absolutePath(); +} + + +#if !defined(Q_OS_MAC) // && !defined(Q_OS_WIN) +void +extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* ) +{ + // No support for binary resolvers on linux! Shouldn't even have been allowed to see/install.. + Q_ASSERT( false ); +} +#endif + } // ns diff --git a/src/libtomahawk/utils/TomahawkUtils.h b/src/libtomahawk/utils/TomahawkUtils.h index 8269d3134..93f73cc5d 100644 --- a/src/libtomahawk/utils/TomahawkUtils.h +++ b/src/libtomahawk/utils/TomahawkUtils.h @@ -137,6 +137,13 @@ namespace TomahawkUtils DLLEXPORT QString md5( const QByteArray& data ); DLLEXPORT bool removeDirectory( const QString& dir ); + DLLEXPORT bool verifyFile( const QString& filePath, const QString& signature ); + DLLEXPORT QString extractScriptPayload( const QString& filename, const QString& resolverId ); + + // Extracting may be asynchronous, pass in a receiver object with the following slots: + // extractSucceeded( const QString& path ) and extractFailed() to be notified/ + DLLEXPORT void extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* receiver ); + /** * This helper is designed to help "update" an existing playlist with a newer revision of itself. * To avoid re-loading the whole playlist and re-resolving tracks that are the same in the old playlist, diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.h b/src/libtomahawk/utils/TomahawkUtils_Mac.h new file mode 100644 index 000000000..635db4117 --- /dev/null +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.h @@ -0,0 +1,37 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi . + */ + +#ifndef TOMAHAWKUTILS_MAC_H +#define TOMAHAWKUTILS_MAC_H + +#include + +#import "mac/FileHelpers.h" + +@interface MoveDelegate : NSObject +{ + QObject* receiver; + QString path; +} +- (void)setReceiver:(QObject*)receiver; +- (void)setMoveTo:(QString)path; +- (void)moveFinished; +- (void)moveFailedWithError:(NSError *)error; +@end + +#endif // TOMAHAWKUTILS_MAC_H diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.mm b/src/libtomahawk/utils/TomahawkUtils_Mac.mm index aeac94866..5bb6558e5 100644 --- a/src/libtomahawk/utils/TomahawkUtils_Mac.mm +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.mm @@ -1,6 +1,54 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi . + */ + #include "TomahawkUtils.h" +#include "TomahawkUtils_Mac.h" +#include "mac/FileHelpers.h" + +#include + #import +#import + +@implementation MoveDelegate + + +-(void) setReceiver:(QObject*) object +{ + receiver = object; +} + +-(void) setMoveTo:(QString) p +{ + path = p; +} + +- (void)moveFinished +{ + QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); +} + +- (void)moveFailedWithError:(NSError *)error +{ + QMetaObject::invokeMethod(receiver, "installFailed", Qt::DirectConnection); +} +@end namespace TomahawkUtils { @@ -10,4 +58,35 @@ bringToFront() { [NSApp activateIgnoringOtherApps:YES]; } + +void +extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* receiver ) +{ + /** + On OS X, we have to do the following: + 2) Extract file in temporary location + 3) Authenticate to be able to have write access to the /Applications folder + 4) Copy the contents of the zipfile to the Tomahawk.app/Contents/MacOS/ folder + 5) Call result slots on receiver object + */ + + MoveDelegate* del = [[MoveDelegate alloc] init]; + [del setReceiver: receiver]; + + // Unzip in temporary folder and copy the contents to MacOS/ + NSError* err = NULL; + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; + NSURL* tempDir = [manager URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:NULL create:YES error:&err]; + if ( err ) + { + qDebug() << "GOT ERROR trying to create temp dir to unzip in...:" << err; + return; + } + + qDebug() << "Using temporary directory:" << [tempDir absoluteString]; + + +// [del setMoveTo: to]; +} + } diff --git a/src/main.cpp b/src/main.cpp index fe0332d1f..2c786bf55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -121,7 +121,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, int main( int argc, char *argv[] ) { - #ifdef Q_WS_MAC +#ifdef Q_WS_MAC // Do Mac specific startup to get media keys working. // This must go before QApplication initialisation. Tomahawk::macMain(); From 27eb1b51f15218f54e631a1e8dc0be7c033fc842 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 17:05:37 -0400 Subject: [PATCH 04/25] Enable move-with-authentication for osx binary resolvers --- src/libtomahawk/AtticaManager.cpp | 6 +- src/libtomahawk/utils/Closure.h | 3 +- src/libtomahawk/utils/TomahawkUtils.cpp | 99 ++++++++++++++++------ src/libtomahawk/utils/TomahawkUtils.h | 5 ++ src/libtomahawk/utils/TomahawkUtils_Mac.mm | 43 +++++----- 5 files changed, 102 insertions(+), 54 deletions(-) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index b8636f4b6..4be683ea1 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -357,6 +358,7 @@ AtticaManager::binaryResolversList( BaseJob* j ) if ( !c.attribute( "typeid" ).isEmpty() && c.attribute( "typeid" ) == platform ) { // We have a binary resolver for this platform + qDebug() << "WE GOT A BINARY RESOLVER:" << c.id() << c.name() << c.attribute( "signature" ); m_resolvers.append( c ); if ( !m_resolverStates.contains( c.id() ) ) { @@ -528,10 +530,8 @@ AtticaManager::payloadFetched() qWarning() << "FILE SIGNATURE FAILED FOR BINARY RESOLVER! WARNING! :" << f.fileName() << signature; return; } -#ifdef Q_OS_MAC -#elif Q_OS_WIN -#endif + TomahawkUtils::extractBinaryResolver( f.fileName(), resolverId, 0 ); } else { diff --git a/src/libtomahawk/utils/Closure.h b/src/libtomahawk/utils/Closure.h index f071c0e28..591712358 100644 --- a/src/libtomahawk/utils/Closure.h +++ b/src/libtomahawk/utils/Closure.h @@ -1,5 +1,6 @@ -/* This file is part of Clementine. +/* This file is part of Tomabawk. Copyright 2011, David Sansome + Copyright 2012, Leo Franchi Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index c2cc93b18..7c3256861 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -42,7 +42,7 @@ #include #include -#ifdef Q_WS_WIN +#ifdef Q_OS_WIN #include #include #endif @@ -732,9 +732,6 @@ verifyFile( const QString &filePath, const QString &signature ) return false; } - QCA::Hash fileHash = QCA::Hash( "sha1 "); - //QCA::SecureArray fileData( toVerify.readAll() ); - //fileHash.update( fileData ); const QByteArray fileHashData = QCA::Hash( "sha1" ).hash( toVerify.readAll() ).toByteArray(); toVerify.close(); @@ -765,18 +762,6 @@ QString extractScriptPayload( const QString& filename, const QString& resolverId ) { // uses QuaZip to extract the temporary zip file to the user's tomahawk data/resolvers directory - QuaZip zipFile( filename ); - if ( !zipFile.open( QuaZip::mdUnzip ) ) - { - tLog() << "Failed to QuaZip open:" << zipFile.getZipError(); - return QString(); - } - - if ( !zipFile.goToFirstFile() ) - { - tLog() << "Failed to go to first file in zip archive: " << zipFile.getZipError(); - return QString(); - } QDir resolverDir = appDataDir(); if ( !resolverDir.mkpath( QString( "atticaresolvers/%1" ).arg( resolverId ) ) ) @@ -785,7 +770,38 @@ extractScriptPayload( const QString& filename, const QString& resolverId ) return QString(); } resolverDir.cd( QString( "atticaresolvers/%1" ).arg( resolverId ) ); - tDebug() << "Installing resolver to:" << resolverDir.absolutePath(); + + + if ( !unzipFileInFolder( filename, resolverDir ) ) + { + qWarning() << "Failed to unzip resolver. Ooops."; + return QString(); + } + + return resolverDir.absolutePath(); +} + + +bool +TomahawkUtils::unzipFileInFolder( const QString &zipFileName, const QDir &folder ) +{ + Q_ASSERT( !zipFileName.isEmpty() ); + Q_ASSERT( folder.exists() ); + + QuaZip zipFile( zipFileName ); + if ( !zipFile.open( QuaZip::mdUnzip ) ) + { + qWarning() << "Failed to QuaZip open:" << zipFile.getZipError(); + return false; + } + + if ( !zipFile.goToFirstFile() ) + { + tLog() << "Failed to go to first file in zip archive: " << zipFile.getZipError(); + return false; + } + + tDebug() << "Unzipping files to:" << folder.absolutePath(); QuaZipFile fileInZip( &zipFile ); do @@ -799,23 +815,21 @@ extractScriptPayload( const QString& filename, const QString& resolverId ) continue; } - QFile out( resolverDir.absoluteFilePath( fileInZip.getActualFileName() ) ); + QFile out( folder.absoluteFilePath( fileInZip.getActualFileName() ) ); + // make dir if there is one needed QStringList parts = fileInZip.getActualFileName().split( "/" ); if ( parts.size() > 1 ) { QStringList dirs = parts.mid( 0, parts.size() - 1 ); QString dirPath = dirs.join( "/" ); // QDir translates / to \ internally if necessary - resolverDir.mkpath( dirPath ); + folder.mkpath( dirPath ); } - // make dir if there is one needed - QDir d( fileInZip.getActualFileName() ); - tDebug() << "Writing to output file..." << out.fileName(); if ( !out.open( QIODevice::WriteOnly ) ) { - tLog() << "Failed to open resolver extract file:" << out.errorString() << info.name; + tLog() << "Failed to open zip extract file:" << out.errorString() << info.name; continue; } @@ -826,17 +840,50 @@ extractScriptPayload( const QString& filename, const QString& resolverId ) } while ( zipFile.goToNextFile() ); - return resolverDir.absolutePath(); + return true; } -#if !defined(Q_OS_MAC) // && !defined(Q_OS_WIN) void extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* ) { +#if !defined(Q_OS_MAC) && !defined (Q_OS_WIN) + Q_ASSERT( false ); + qWarning() << "NO SUPPORT YET FOR LINUX BINARY RESOLVERS!"; + return; +#endif + + // Unzip the file. + QFileInfo info( zipFilename ); + QDir tmpDir = QDir::tempPath(); + if ( !tmpDir.mkdir( info.baseName() ) ) + { + qWarning() << "Failed to create temporary directory to unzip in:" << tmpDir.absolutePath(); + return; + } + tmpDir.cd( info.baseName() ); + TomahawkUtils::unzipFileInFolder( info.absoluteFilePath(), tmpDir ); + + // Platform-specific handling of resolver payload now. We know it's good and we unzipped it +#ifdef Q_OS_MAC + // On OSX it just contains 1 file, the resolver executable itself. For now. We just copy it to + // the Tomahawk.app/Contents/MacOS/ folder alongside the Tomahawk executable. + const QString dest = QCoreApplication::applicationDirPath(); + // Find the filename + const QDir toList( tmpDir.absolutePath() ); + const QStringList files = toList.entryList( QStringList(), QDir::Files ); + Q_ASSERT( files.size() == 1 ); + + const QString src = toList.absoluteFilePath( files.first() ); + qDebug() << "OS X: Copying binary resolver from to:" << src << dest; + + copyWithAuthentication( src, dest, 0 ); +#elif Q_OS_WIN +#endif + // No support for binary resolvers on linux! Shouldn't even have been allowed to see/install.. Q_ASSERT( false ); } -#endif + } // ns diff --git a/src/libtomahawk/utils/TomahawkUtils.h b/src/libtomahawk/utils/TomahawkUtils.h index 93f73cc5d..83441a3fd 100644 --- a/src/libtomahawk/utils/TomahawkUtils.h +++ b/src/libtomahawk/utils/TomahawkUtils.h @@ -139,11 +139,16 @@ namespace TomahawkUtils DLLEXPORT bool verifyFile( const QString& filePath, const QString& signature ); DLLEXPORT QString extractScriptPayload( const QString& filename, const QString& resolverId ); + DLLEXPORT bool unzipFileInFolder( const QString& zipFileName, const QDir& folder ); + // Extracting may be asynchronous, pass in a receiver object with the following slots: // extractSucceeded( const QString& path ) and extractFailed() to be notified/ DLLEXPORT void extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* receiver ); + // Used by the above, not exported + void copyWithAuthentication( const QString& srcFile, const QDir dest, QObject* receiver ); + /** * This helper is designed to help "update" an existing playlist with a newer revision of itself. * To avoid re-loading the whole playlist and re-resolving tracks that are the same in the old playlist, diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.mm b/src/libtomahawk/utils/TomahawkUtils_Mac.mm index 5bb6558e5..4c22f2589 100644 --- a/src/libtomahawk/utils/TomahawkUtils_Mac.mm +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.mm @@ -21,6 +21,7 @@ #include "TomahawkUtils_Mac.h" #include "mac/FileHelpers.h" +#include #include #import @@ -41,12 +42,14 @@ - (void)moveFinished { - QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); + if ( receiver ) + QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); } - (void)moveFailedWithError:(NSError *)error { - QMetaObject::invokeMethod(receiver, "installFailed", Qt::DirectConnection); + if ( receiver ) + QMetaObject::invokeMethod(receiver, "installFailed", Qt::DirectConnection); } @end @@ -58,35 +61,27 @@ bringToFront() { [NSApp activateIgnoringOtherApps:YES]; } - void -extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* receiver ) +copyWithAuthentication( const QString& srcFile, const QDir dest, QObject* receiver ) { - /** - On OS X, we have to do the following: - 2) Extract file in temporary location - 3) Authenticate to be able to have write access to the /Applications folder - 4) Copy the contents of the zipfile to the Tomahawk.app/Contents/MacOS/ folder - 5) Call result slots on receiver object - */ + /** + On OS X, we have to do the following: + 1) Authenticate to be able to have write access to the /Applications folder + 2) Copy file to dest + 5) Call result slots on receiver object + */ MoveDelegate* del = [[MoveDelegate alloc] init]; [del setReceiver: receiver]; + [del setMoveTo: dest.absolutePath()]; - // Unzip in temporary folder and copy the contents to MacOS/ - NSError* err = NULL; - NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; - NSURL* tempDir = [manager URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:NULL create:YES error:&err]; - if ( err ) - { - qDebug() << "GOT ERROR trying to create temp dir to unzip in...:" << err; - return; - } + const QFileInfo info( srcFile ); + const QString destPath = dest.absoluteFilePath( info.fileName() ); - qDebug() << "Using temporary directory:" << [tempDir absoluteString]; - - -// [del setMoveTo: to]; + NSString* src = [[NSString alloc] initWithBytes: srcFile.toUtf8() length: srcFile.length() encoding: NSUTF8StringEncoding]; + NSString* destStr = [[NSString alloc] initWithBytes: destPath.toUtf8() length: destPath.length() encoding: NSUTF8StringEncoding]; + [FileHelpers moveFile:src to:destStr withDelegate:del]; } + } From 8aae86cf162ab842e110885072402f8be1ff75bc Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 17:12:21 -0400 Subject: [PATCH 05/25] Add mac FileHelpers --- src/libtomahawk/mac/FileHelpers.h | 40 +++++ src/libtomahawk/mac/FileHelpers.mm | 226 +++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 src/libtomahawk/mac/FileHelpers.h create mode 100644 src/libtomahawk/mac/FileHelpers.mm diff --git a/src/libtomahawk/mac/FileHelpers.h b/src/libtomahawk/mac/FileHelpers.h new file mode 100644 index 000000000..1a25cd97d --- /dev/null +++ b/src/libtomahawk/mac/FileHelpers.h @@ -0,0 +1,40 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi . + */ + +#ifndef MAC_FILE_HELPERS_H +#define MAC_FILE_HELPERS_H + +#import +#import + +// Implement this delegate protocol to get notified about the result of your copy attempt +@interface NSObject (SUInstallerDelegateInformalProtocol) +- (void)moveFinished; +- (void)moveFailedWithError:(NSError *)error; +@end + +@interface FileHelpers : NSObject +{} +// Move a file from point A to point B, asking for authentication if necessary +// Will be asynchronous: Implement the delegate protocol know about the completion ++ (void) moveFile:(NSString *)source to:(NSString*)dest withDelegate:delegate; + + +@end + +#endif diff --git a/src/libtomahawk/mac/FileHelpers.mm b/src/libtomahawk/mac/FileHelpers.mm new file mode 100644 index 000000000..97ee3c4ed --- /dev/null +++ b/src/libtomahawk/mac/FileHelpers.mm @@ -0,0 +1,226 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi . + */ + +#import "FileHelpers.h" + +#import +#import +#import +#import +#import +#import +#import + +#include + +static NSString * const TKCopySourceKey = @"TKInstallerSourcePath"; +static NSString * const TKCopyDestinationKey = @"TKInstallerDestinationPath"; +static NSString * const TKInstallerDelegateKey = @"TKInstallerDelegate"; +static NSString * const TKInstallerResultKey = @"TKInstallerResult"; +static NSString * const TKInstallerErrorKey = @"TKInstallerError"; + +class CAutoreleasePool +{ + NSAutoreleasePool *pool; + +public: + CAutoreleasePool() + { + pool = [[NSAutoreleasePool alloc] init]; + } + + ~CAutoreleasePool() + { + [pool drain]; + } +}; + +// Authorization code based on generous contribution from Allan Odgaard. Thanks, Allan! +static BOOL AuthorizationExecuteWithPrivilegesAndWait(AuthorizationRef authorization, const char* executablePath, AuthorizationFlags options, const char* const* arguments) +{ + // *** MUST BE SAFE TO CALL ON NON-MAIN THREAD! + + sig_t oldSigChildHandler = signal(SIGCHLD, SIG_DFL); + BOOL returnValue = YES; + + if (AuthorizationExecuteWithPrivileges(authorization, executablePath, options, (char* const*)arguments, NULL) == errAuthorizationSuccess) + { + int status; + pid_t pid = wait(&status); + if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + returnValue = NO; + } + else + returnValue = NO; + + signal(SIGCHLD, oldSigChildHandler); + return returnValue; +} + +@implementation FileHelpers + ++ (void) moveFile:(NSString *)source to:(NSString*)dest withDelegate:delegate +{ + NSLog(@"FileHelpers moving file from %@ to %@", source, dest); + + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:source, TKCopySourceKey, dest, TKCopyDestinationKey, delegate, TKInstallerDelegateKey, nil]; + [NSThread detachNewThreadSelector:@selector(performMoveWithInfo:) toTarget:self withObject:info]; +} + + ++ (void)performMoveWithInfo:(NSDictionary *)info +{ + // *** GETS CALLED ON NON-MAIN THREAD! + + CAutoreleasePool _p; + + NSString* fromPath = [info objectForKey: TKCopySourceKey]; + NSString* toPath = [info objectForKey: TKCopyDestinationKey]; + + AuthorizationRef auth = NULL; + OSStatus authStat = errAuthorizationDenied; + + NSLog(@"FileHelpers moving file from %@ to %@", fromPath, toPath); + BOOL haveOld = [[NSFileManager defaultManager] fileExistsAtPath: toPath]; + + if (haveOld == YES) { // delete the old file if it's there + if (0 != access([[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK) + || 0 != access([[[toPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK)) + { + const char* rmParams[] = { [toPath fileSystemRepresentation], NULL }; + // NSLog( @"WOULD DELETE: %@", [toPath fileSystemRepresentation] ); + + while( authStat == errAuthorizationDenied ) + { + authStat = AuthorizationCreate(NULL, + kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, + &auth); + } + if (authStat == errAuthorizationSuccess) + { + BOOL res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/rm", kAuthorizationFlagDefaults, rmParams ); + if (!res) + NSLog(@"Could not delete: %@", toPath); + } else { + qDebug() << "Failed to authenticate to delete file under target to move, aborting"; + return; + } + } else { + // We can delete it ourselves w/out authenticating + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; + NSError* error; + BOOL success = [manager removeItemAtPath:toPath error:&error]; + + if (!success) { + NSLog(@"Failed to delete file (w/out perms) underneath copy!: %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); + return; + } + } + } + + FSRef dstRef, dstDirRef; + OSStatus err = FSPathMakeRefWithOptions((UInt8 *)[toPath fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &dstRef, NULL); + + if (err != noErr && err != fnfErr) { // If the file is not found that's fine, we're moving to there after all + qDebug() << "GOT AN ERROR DOING FSPathMakeRefWithOptions!!!!! aborting move"; + return; + } + + if (0 != access([[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK) + || 0 != access([[[toPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK)) + { + // Not writeable by user, so authenticate + if (!auth) { + while( authStat == errAuthorizationDenied ) + { + authStat = AuthorizationCreate(NULL, + kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, + &auth); + } + } + + if (authStat == errAuthorizationSuccess) + { + // Fix perms before moving so we have them correct when they arrive + struct stat dstSB; + stat([[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], &dstSB); + char uidgid[42]; + snprintf(uidgid, sizeof(uidgid), "%d:%d", + dstSB.st_uid, dstSB.st_gid); + + const char* coParams[] = { "-R", uidgid, [fromPath fileSystemRepresentation], NULL }; + BOOL res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/usr/sbin/chown", kAuthorizationFlagDefaults, coParams ); + if( !res ) + qDebug() << "Failed to set permissions before moving"; + + // Do the move + const char* mvParams[] = { "-f", [fromPath fileSystemRepresentation], [toPath fileSystemRepresentation], NULL }; + res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/mv", kAuthorizationFlagDefaults, mvParams ); + if( !res ) + NSLog(@"Failed to move source file from %@ to %@ with error %@", fromPath, toPath, res ); + + AuthorizationFree(auth, 0); + auth = NULL; + } + + return; + } + + if (auth) { + AuthorizationFree(auth, 0); + auth = NULL; + } + + err = FSPathMakeRef((UInt8 *)[[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], &dstDirRef, NULL); + + if (err != noErr) { + qDebug() << "GOT AN ERROR DOING FSPathMakeRef to get dir to copy into!!!!! aborting move"; + return; + } + + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; + NSError* error; + BOOL success = [manager moveItemAtPath:fromPath toPath:toPath error:&error]; + if (!success) { + NSLog( @"Failed to do non-authenticated move! Help! %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey] ); + } + [self notifyDelegate:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:success], TKInstallerResultKey, [info objectForKey:TKInstallerDelegateKey], TKInstallerDelegateKey, error, TKInstallerErrorKey, nil]]; +} + + ++ (void)notifyDelegate:(NSDictionary *)info +{ + // *** GETS CALLED ON NON-MAIN THREAD! + BOOL result = [[info objectForKey:TKInstallerResultKey] boolValue]; + if (result) + { + if ([[info objectForKey:TKInstallerDelegateKey] respondsToSelector:@selector(moveFinished)]) + [[info objectForKey:TKInstallerDelegateKey] performSelectorOnMainThread: @selector(moveFinished) withObject:nil waitUntilDone: NO]; + } + else + { + if ([[info objectForKey:TKInstallerDelegateKey] respondsToSelector:@selector(moveFailedWithError:)]) + { + [[info objectForKey:TKInstallerDelegateKey] performSelectorOnMainThread: @selector(moveFailedWithError) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[info objectForKey:TKInstallerErrorKey], TKInstallerErrorKey, nil] waitUntilDone: NO]; + } + } +} + +@end From 021cd546873a19eb9503672af6e8943debd853aa Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 17:31:17 -0400 Subject: [PATCH 06/25] Add tomahawk public key --- data/misc/tomahawk_pubkey.pem | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 data/misc/tomahawk_pubkey.pem diff --git a/data/misc/tomahawk_pubkey.pem b/data/misc/tomahawk_pubkey.pem new file mode 100644 index 000000000..5e9606170 --- /dev/null +++ b/data/misc/tomahawk_pubkey.pem @@ -0,0 +1,20 @@ +-----BEGIN PUBLIC KEY----- +MIIDOzCCAi0GByqGSM44BAEwggIgAoIBAQDRltnNbKWFroVCsG1nTSdlTDmo7fjl +tgOuQ0YB2s0a1bcqgQ5YJRE59pFvF/z2pkHEHdyBA6USd9N7/T9lolwNcJoByJpO +MobUNs04elqZXliriaAdoSb2g6ZpxiedppbbyNP/BlK6o+zpyn0LVYXDI/OwJFzS +xjGXM+rBEWdUJnogZxV31gF9W3yD1Quz6icBulT9V/Soo6me9Mc60ooKSYj4Zgqd +3ln8tG90RFnWfbb0nbrITvR3ll6XXLfn081tjhymcXqHcgvaaqcmpKWL6ZWwX1mH +3t1pImnif/tSSZPG21KGE3FtuQ/+YFo19apQ6U6l8kaSFxqcDLAYzBy9AhUA/QfN +8WEIvzOEZ9uSWT7lYy64mUkCggEABsUmcs3kwjrmszIAAmPIowA0DBrxWZL03JBV +bDKT6tNHZaFFlCufVSjiL1EFZjRARC16OWYaDcElUsZYFMcsNIIa8LyDQaq6+SSm +quhMO5heeJiYPrutDiJzbJr0+HoY77Ll+Q4/cEkl0UAN4Ovp18WKwaq6GpHAvBnv +71LunLGAKsVb5joXBQ8In6zQkibJhgiBJwzLK90/j0OTiDaaOwM3PsAegORBVlVE +TAk4AQmawmF8nBGLzTyKXl83J571ku1Mm2JTl16jMYziKARKXYBmkcP1at0YddVK +WWpAwRKSxOucVJYfV58JqmjZqst8BBeH6esQKr5dklUvvDMaEwOCAQYAAoIBAQCw +5mo+8/R3S9cNYg9o8JNJGdSbMhSkurILHh9WNElsIC3RNtPcpijmAnWtXTVDhe6w +77wLj37tUuFGbsu2qPXtZoup35emf9DDshZ5w5UOclPaZ9HYjlC1H64c6d66Rllk +fY6FRDv9qVfjT84APbvMDrk6csJ5YHxFPDaqeQaFB0nxFiCMVwjEx+ZSvQNK1jJ2 +o2gtuOvSPVSphsMeJ72DDNxO+SRRVnOmWaxg9rlmFuGle6Z+UJ2FItfmPEvhSBMY +hzndUbC7Wi4sIpBzbm9O5MiPYMv0VmN+0t1156EiC9uR4f7AKH2S94dnQob/YeY0 +jMH+XxU/wzGUCmsOx1lx +-----END PUBLIC KEY----- From be76cc49d3e65979a4ee64c4dac0126c52d2635e Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 17:36:32 -0400 Subject: [PATCH 07/25] add error handling to resolver in stall so we hide the spinner --- src/AccountDelegate.cpp | 8 ++++++ src/AccountDelegate.h | 2 ++ src/SettingsDialog.cpp | 5 ++-- src/libtomahawk/AtticaManager.cpp | 26 ++++++++++++------- src/libtomahawk/AtticaManager.h | 1 + src/libtomahawk/accounts/AccountModel.cpp | 17 ++++++++++++ src/libtomahawk/accounts/AccountModel.h | 3 +++ .../accounts/AccountModelFilterProxy.cpp | 9 +++++++ .../accounts/AccountModelFilterProxy.h | 2 ++ src/libtomahawk/utils/TomahawkUtils.cpp | 4 +-- 10 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/AccountDelegate.cpp b/src/AccountDelegate.cpp index 95e5ce418..505257a89 100644 --- a/src/AccountDelegate.cpp +++ b/src/AccountDelegate.cpp @@ -734,6 +734,14 @@ AccountDelegate::doneInstalling ( const QPersistentModelIndex& idx ) } +void +AccountDelegate::errorInstalling( const QPersistentModelIndex& idx ) +{ + // Just hide the loading spinner as we do after a successful install + doneInstalling( idx ); +} + + void AccountDelegate::doUpdateIndex( const QPersistentModelIndex& idx ) { diff --git a/src/AccountDelegate.h b/src/AccountDelegate.h index 2028eb09b..82e59f4d2 100644 --- a/src/AccountDelegate.h +++ b/src/AccountDelegate.h @@ -43,6 +43,8 @@ public: public slots: void startInstalling( const QPersistentModelIndex& idx ); void doneInstalling ( const QPersistentModelIndex& idx ); + void errorInstalling ( const QPersistentModelIndex& idx ); + void doUpdateIndex( const QPersistentModelIndex& idx ); diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index 6cd2f5266..a03e36262 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -97,7 +97,7 @@ SettingsDialog::SettingsDialog( QWidget *parent ) ui->enableProxyCheckBox->setChecked( useProxy ); ui->proxyButton->setEnabled( useProxy ); - + createIcons(); #ifdef Q_WS_X11 ui->listWidget->setFrameShape( QFrame::StyledPanel ); @@ -142,6 +142,7 @@ SettingsDialog::SettingsDialog( QWidget *parent ) connect( m_accountProxy, SIGNAL( startInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( startInstalling(QPersistentModelIndex) ) ); connect( m_accountProxy, SIGNAL( doneInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( doneInstalling(QPersistentModelIndex) ) ); + connect( m_accountProxy, SIGNAL( errorInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( errorInstalling(QPersistentModelIndex) ) ); connect( m_accountProxy, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( scrollTo( QModelIndex ) ) ); ui->accountsView->setModel( m_accountProxy ); @@ -262,7 +263,7 @@ SettingsDialog::~SettingsDialog() } else qDebug() << "Settings dialog cancelled, NOT saving prefs."; - + delete ui; } diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 4be683ea1..99479690c 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -503,6 +503,9 @@ AtticaManager::payloadFetched() QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); Q_ASSERT( reply ); + bool installedSuccessfully = false; + const QString resolverId = reply->property( "resolverId" ).toString(); + // we got a zip file, save it to a temporary file, then unzip it to our destination data dir if ( reply->error() == QNetworkReply::NoError ) { @@ -515,8 +518,6 @@ AtticaManager::payloadFetched() f.write( reply->readAll() ); f.close(); - bool installedSuccessfully = false; - const QString resolverId = reply->property( "resolverId" ).toString(); if ( m_resolverStates[ resolverId ].binary ) { // First ensure the signature matches. If we can't verify it, abort! @@ -554,19 +555,24 @@ AtticaManager::payloadFetched() installedSuccessfully = true; } } - - if ( installedSuccessfully ) - { - m_resolverStates[ resolverId ].state = Installed; - TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates ); - emit resolverInstalled( resolverId ); - emit resolverStateChanged( resolverId ); - } } else { tLog() << "Failed to download attica payload...:" << reply->errorString(); } + + + if ( installedSuccessfully ) + { + m_resolverStates[ resolverId ].state = Installed; + TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates ); + emit resolverInstalled( resolverId ); + emit resolverStateChanged( resolverId ); + } + else + { + emit resolverInstallationFailed( resolverId ); + } } diff --git a/src/libtomahawk/AtticaManager.h b/src/libtomahawk/AtticaManager.h index 70517c334..c5bf607a2 100644 --- a/src/libtomahawk/AtticaManager.h +++ b/src/libtomahawk/AtticaManager.h @@ -109,6 +109,7 @@ signals: void resolverStateChanged( const QString& resolverId ); void resolverInstalled( const QString& resolverId ); void resolverUninstalled( const QString& resolverId ); + void resolverInstallationFailed( const QString& resolverId ); private slots: void providerAdded( const Attica::Provider& ); diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index f58014435..5ddaf3cf1 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -35,6 +35,7 @@ AccountModel::AccountModel( QObject* parent ) : QAbstractListModel( parent ) { connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( loadData() ) ); + connect( AtticaManager::instance(), SIGNAL( resolverInstallationFailed( QString ) ), this, SLOT( resolverInstallFailed( QString ) ) ); connect( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( accountAdded( Tomahawk::Accounts::Account* ) ) ); connect( AccountManager::instance(), SIGNAL( removed( Tomahawk::Accounts::Account* ) ), this, SLOT( accountRemoved( Tomahawk::Accounts::Account* ) ) ); @@ -689,6 +690,22 @@ AccountModel::accountRemoved( Account* account ) } +void +AccountModel::resolverInstallFailed( const QString& resolverId ) +{ + for ( int i = 0; i < m_accounts.size(); i++ ) + { + if ( m_accounts[ i ]->type == AccountModelNode::AtticaType && m_accounts[ i ]->atticaContent.id() == resolverId ) + { + qDebug() << "Got failed attica install in account mode, emitting signal!"; + emit errorInstalling( index( i, 0, QModelIndex() ) ); + + return; + } + } +} + + int AccountModel::rowCount( const QModelIndex& ) const { diff --git a/src/libtomahawk/accounts/AccountModel.h b/src/libtomahawk/accounts/AccountModel.h index 68fcb89f6..e383b6ec6 100644 --- a/src/libtomahawk/accounts/AccountModel.h +++ b/src/libtomahawk/accounts/AccountModel.h @@ -96,6 +96,8 @@ signals: void startInstalling( const QPersistentModelIndex& idx ); void doneInstalling( const QPersistentModelIndex& idx ); + void errorInstalling( const QPersistentModelIndex& idx ); + private slots: void loadData(); @@ -103,6 +105,7 @@ private slots: void accountRemoved( Tomahawk::Accounts::Account* ); void accountStateChanged( Account*, Accounts::Account::ConnectionState ); + void resolverInstallFailed( const QString& resolverId ); private: QList< AccountModelNode* > m_accounts; QSet< QString > m_waitingForAtticaInstall; diff --git a/src/libtomahawk/accounts/AccountModelFilterProxy.cpp b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp index 1a559faed..2e3b9bc8d 100644 --- a/src/libtomahawk/accounts/AccountModelFilterProxy.cpp +++ b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp @@ -38,6 +38,7 @@ AccountModelFilterProxy::setSourceModel( QAbstractItemModel* sourceModel ) connect( sourceModel, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( onScrollTo( QModelIndex ) ) ); connect( sourceModel, SIGNAL( startInstalling( QPersistentModelIndex ) ), this, SLOT( onStartInstalling( QPersistentModelIndex ) ) ); connect( sourceModel, SIGNAL( doneInstalling( QPersistentModelIndex ) ), this, SLOT( onDoneInstalling( QPersistentModelIndex ) ) ); + connect( sourceModel, SIGNAL( doneInstalling( QPersistentModelIndex ) ), this, SLOT( errorInstalling( QPersistentModelIndex ) ) ); QSortFilterProxyModel::setSourceModel( sourceModel ); } @@ -86,3 +87,11 @@ AccountModelFilterProxy::onStartInstalling( const QPersistentModelIndex& idx ) { emit startInstalling( mapFromSource( idx ) ); } + + +void +AccountModelFilterProxy::onErrorInstalling( const QPersistentModelIndex& idx ) +{ + emit errorInstalling( mapFromSource( idx ) ); +} + diff --git a/src/libtomahawk/accounts/AccountModelFilterProxy.h b/src/libtomahawk/accounts/AccountModelFilterProxy.h index 75021c6b1..2a8868fd8 100644 --- a/src/libtomahawk/accounts/AccountModelFilterProxy.h +++ b/src/libtomahawk/accounts/AccountModelFilterProxy.h @@ -42,6 +42,7 @@ signals: void startInstalling( const QPersistentModelIndex& idx ); void doneInstalling( const QPersistentModelIndex& idx ); + void errorInstalling( const QPersistentModelIndex& idx ); protected: virtual bool filterAcceptsRow ( int sourceRow, const QModelIndex& sourceParent ) const; @@ -51,6 +52,7 @@ private slots: void onStartInstalling( const QPersistentModelIndex& idx ); void onDoneInstalling( const QPersistentModelIndex& idx ); + void onErrorInstalling( const QPersistentModelIndex& idx ); private: Tomahawk::Accounts::AccountType m_filterType; diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index 7c3256861..101954090 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -783,7 +783,7 @@ extractScriptPayload( const QString& filename, const QString& resolverId ) bool -TomahawkUtils::unzipFileInFolder( const QString &zipFileName, const QDir &folder ) +unzipFileInFolder( const QString &zipFileName, const QDir &folder ) { Q_ASSERT( !zipFileName.isEmpty() ); Q_ASSERT( folder.exists() ); @@ -878,7 +878,7 @@ extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QO qDebug() << "OS X: Copying binary resolver from to:" << src << dest; copyWithAuthentication( src, dest, 0 ); -#elif Q_OS_WIN +#elif defined(Q_OS_WIN) #endif // No support for binary resolvers on linux! Shouldn't even have been allowed to see/install.. From f64cce1567bd5313d545d24137be6394a42a7b76 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 19:02:28 -0400 Subject: [PATCH 08/25] set unzipped executable to executable --- src/libtomahawk/utils/TomahawkUtils_Mac.mm | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.mm b/src/libtomahawk/utils/TomahawkUtils_Mac.mm index 4c22f2589..07ab5da2c 100644 --- a/src/libtomahawk/utils/TomahawkUtils_Mac.mm +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.mm @@ -44,6 +44,20 @@ { if ( receiver ) QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); + + // HACK since I can't figure out how to get QuaZip to maintain executable permissions after unzip (nor find the info) + // we set the binary to executable here + + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; + NSError* error; + NSDictionary* attrs = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0755], NSFilePosixPermissions, nil]; + + NSString* target = [[NSString alloc] initWithBytes:path.toUtf8() length:path.length() encoding: NSUTF8StringEncoding]; + NSLog(@"Changing permissions to executable for: %@", target); + BOOL success = [manager setAttributes:attrs ofItemAtPath:target error:&error]; + if (!success) { + NSLog( @"Failed to do chmod +x of moved resolver! %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey] ); + } } - (void)moveFailedWithError:(NSError *)error @@ -73,7 +87,11 @@ copyWithAuthentication( const QString& srcFile, const QDir dest, QObject* receiv MoveDelegate* del = [[MoveDelegate alloc] init]; [del setReceiver: receiver]; - [del setMoveTo: dest.absolutePath()]; + + // Get the filename + path to save for later + QFileInfo srcInfo( srcFile ); + const QString resultingPath = dest.absoluteFilePath( srcInfo.fileName() ); + [del setMoveTo: resultingPath]; const QFileInfo info( srcFile ); const QString destPath = dest.absoluteFilePath( info.fileName() ); From a071105a26bd4e7946042c00c30cd7477c13a0b8 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 20:43:54 -0400 Subject: [PATCH 09/25] Add workaround for Qt bug, thanks David Sansome! --- src/TomahawkApp.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/TomahawkApp.cpp b/src/TomahawkApp.cpp index 6243e11b5..1588ff886 100644 --- a/src/TomahawkApp.cpp +++ b/src/TomahawkApp.cpp @@ -290,6 +290,16 @@ TomahawkApp::init() PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); + // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome + // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced + // with the default value of 3 in QApplicationPrivate::initialize. + { + QSettings qt_settings(QSettings::UserScope, "Trolltech"); + qt_settings.beginGroup("Qt"); + QApplication::setWheelScrollLines( + qt_settings.value("wheelScrollLines", QApplication::wheelScrollLines()).toInt()); + } + #ifndef ENABLE_HEADLESS // Make sure to init GAM in the gui thread GlobalActionManager::instance(); From bb5e5c33ca9f58d7c94eda17ddb2ec2398c1632b Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 20:44:03 -0400 Subject: [PATCH 10/25] listen to results of asynchronous binary installs --- src/libtomahawk/AtticaManager.cpp | 44 +++++++++++++++++++++++++++++++ src/libtomahawk/AtticaManager.h | 3 +++ 2 files changed, 47 insertions(+) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 99479690c..72fe40e88 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -39,6 +39,48 @@ using namespace Attica; AtticaManager* AtticaManager::s_instance = 0; +class BinaryInstallerHelper : public QObject +{ + Q_OBJECT +public: + explicit BinaryInstallerHelper( const QString& resolverId, AtticaManager* manager) + : QObject( manager ) + , m_manager( QWeakPointer< AtticaManager >( manager ) ) + , m_resolverId( resolverId ) + { + Q_ASSERT( !m_resolverId.isEmpty() ); + Q_ASSERT( !m_manager.isNull() ); + } + + virtual ~BinaryInstallerHelper() {} + +public slots: + void extractSucceeded( const QString& path ) + { + if ( m_manager.isNull() ) + return; + + + + m_manager.data()->m_resolverStates[ m_resolverId ].state = AtticaManager::Installed; + TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_manager.data()->m_resolverStates ); + emit m_manager.data()->resolverInstalled( m_resolverId ); + emit m_manager.data()->resolverStateChanged( m_resolverId ); + } + void extractFailed() + { + if ( m_manager.isNull() ) + return; + + m_manager.data()->resolverInstallationFailed( m_resolverId ); + } + +private: + QString m_resolverId; + QWeakPointer m_manager; +}; + + AtticaManager::AtticaManager( QObject* parent ) : QObject( parent ) , m_resolverJobsLoaded( 0 ) @@ -650,3 +692,5 @@ AtticaManager::doResolverRemove( const QString& id ) const TomahawkUtils::removeDirectory( resolverDir.absolutePath() ); } + +#include "AtticaManager.moc" \ No newline at end of file diff --git a/src/libtomahawk/AtticaManager.h b/src/libtomahawk/AtticaManager.h index c5bf607a2..7add543d9 100644 --- a/src/libtomahawk/AtticaManager.h +++ b/src/libtomahawk/AtticaManager.h @@ -32,6 +32,7 @@ #include #include +class BinaryInstallerHelper; class DLLEXPORT AtticaManager : public QObject { @@ -139,6 +140,8 @@ private: QMap< QString, Tomahawk::Accounts::Account* > m_customAccounts; static AtticaManager* s_instance; + + friend class ::BinaryInstallerHelper; }; class DLLEXPORT CustomAtticaAccount : public Tomahawk::Accounts::Account From 81575c68e8223ab9844858ae7c3191f1be9f5389 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 20:48:55 -0400 Subject: [PATCH 11/25] hook up --- src/libtomahawk/AtticaManager.cpp | 6 +++++- src/libtomahawk/utils/TomahawkUtils.cpp | 2 +- src/libtomahawk/utils/TomahawkUtils.h | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 72fe40e88..ffed903a6 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -66,6 +66,8 @@ public slots: TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_manager.data()->m_resolverStates ); emit m_manager.data()->resolverInstalled( m_resolverId ); emit m_manager.data()->resolverStateChanged( m_resolverId ); + + deleteLater(); } void extractFailed() { @@ -73,6 +75,8 @@ public slots: return; m_manager.data()->resolverInstallationFailed( m_resolverId ); + + deleteLater(); } private: @@ -574,7 +578,7 @@ AtticaManager::payloadFetched() return; } - TomahawkUtils::extractBinaryResolver( f.fileName(), resolverId, 0 ); + TomahawkUtils::extractBinaryResolver( f.fileName(), new BinaryInstallerHelper( resolverId, this ) ); } else { diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index 9d5049ada..c5533d599 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -845,7 +845,7 @@ unzipFileInFolder( const QString &zipFileName, const QDir &folder ) void -extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* ) +extractBinaryResolver( const QString& zipFilename, QObject* ) { #if !defined(Q_OS_MAC) && !defined (Q_OS_WIN) Q_ASSERT( false ); diff --git a/src/libtomahawk/utils/TomahawkUtils.h b/src/libtomahawk/utils/TomahawkUtils.h index 7a4d5c1af..27350e3e1 100644 --- a/src/libtomahawk/utils/TomahawkUtils.h +++ b/src/libtomahawk/utils/TomahawkUtils.h @@ -115,7 +115,7 @@ namespace TomahawkUtils QStringList m_noProxyHosts; QNetworkProxy m_proxy; }; - + DLLEXPORT QString appFriendlyVersion(); @@ -138,7 +138,7 @@ namespace TomahawkUtils DLLEXPORT QString md5( const QByteArray& data ); DLLEXPORT bool removeDirectory( const QString& dir ); - + DLLEXPORT bool verifyFile( const QString& filePath, const QString& signature ); DLLEXPORT QString extractScriptPayload( const QString& filename, const QString& resolverId ); DLLEXPORT bool unzipFileInFolder( const QString& zipFileName, const QDir& folder ); @@ -146,7 +146,7 @@ namespace TomahawkUtils // Extracting may be asynchronous, pass in a receiver object with the following slots: // extractSucceeded( const QString& path ) and extractFailed() to be notified/ - DLLEXPORT void extractBinaryResolver( const QString& zipFilename, const QString& resolverId, QObject* receiver ); + DLLEXPORT void extractBinaryResolver( const QString& zipFilename, QObject* receiver ); // Used by the above, not exported void copyWithAuthentication( const QString& srcFile, const QDir dest, QObject* receiver ); From 5bb31a2e7c60aa78de281b3c449b8e9a1c3e098c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 13 May 2012 21:28:18 -0400 Subject: [PATCH 12/25] try adding some account linkup --- src/libtomahawk/AtticaManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index ffed903a6..6f6c23a08 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -60,6 +60,11 @@ public slots: if ( m_manager.isNull() ) return; + Tomahawk::Accounts::Account* acct = Tomahawk::Accounts::AccountManager::instance()->accountFromPath( path ); + + Tomahawk::Accounts::AccountManager::instance()->addAccount( acct ); + TomahawkSettings::instance()->addAccount( acct->accountId() ); + Tomahawk::Accounts::AccountManager::instance()->enableAccount( acct ); m_manager.data()->m_resolverStates[ m_resolverId ].state = AtticaManager::Installed; From 95bb3629a153d1362bbc041a1d70705115dccb27 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 15 May 2012 17:48:07 -0400 Subject: [PATCH 13/25] slight work on spotify account --- src/accounts/spotify/SpotifyAccount.cpp | 143 +++++++++++++++++---- src/accounts/spotify/SpotifyAccount.h | 16 ++- src/libtomahawk/AtticaManager.cpp | 2 +- src/libtomahawk/accounts/ResolverAccount.h | 2 +- 4 files changed, 133 insertions(+), 30 deletions(-) diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index a15047d44..2d8278bf0 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -26,6 +26,7 @@ #include "resolvers/ScriptResolver.h" #include "utils/TomahawkUtils.h" #include "ActionCollection.h" +#include "Pipeline.h" #ifndef ENABLE_HEADLESS #include "jobview/JobStatusView.h" @@ -51,21 +52,6 @@ SpotifyAccountFactory::createAccount( const QString& accountId ) } -bool -SpotifyAccountFactory::acceptsPath( const QString& path ) const -{ - QFileInfo info( path ); - return info.baseName().startsWith( "spotify_" ); -} - - -Account* -SpotifyAccountFactory::createFromPath( const QString& path ) -{ - return new SpotifyAccount( generateId( factoryId() ), path ); -} - - QPixmap SpotifyAccountFactory::icon() const { @@ -77,14 +63,7 @@ SpotifyAccountFactory::icon() const SpotifyAccount::SpotifyAccount( const QString& accountId ) - : ResolverAccount( accountId ) -{ - init(); -} - - -SpotifyAccount::SpotifyAccount( const QString& accountId, const QString& path ) - : ResolverAccount( accountId, path ) +: CustomAtticaAccount( accountId ) { init(); } @@ -101,8 +80,36 @@ SpotifyAccount::init() { qRegisterMetaType< Tomahawk::Accounts::SpotifyPlaylistInfo* >( "Tomahawk::Accounts::SpotifyPlaylist*" ); - m_spotifyResolver = dynamic_cast< ScriptResolver* >( m_resolver.data() ); + AtticaManager::instance()->registerCustomAccount( "spotify", this ); + connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( resolverInstalled( QString ) ) ); + + const Attica::Content res = AtticaManager::instance()->resolverForId( "spotify" ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + + if ( state == AtticaManager::Installed ) + { + hookupResolver(); + } +} + + +void +SpotifyAccount::hookupResolver() +{ + // initialize the resolver itself. this is called if the account actually has an installed spotify resolver, + // as it might not. + // If there is a last.fm resolver from attica installed, create the corresponding ExternalResolver* and hook up to it + const Attica::Content res = AtticaManager::instance()->resolverForId( "spotify" ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + Q_ASSERT( state == AtticaManager::Installed ); + Q_UNUSED( state ); + + const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() ); + + m_spotifyResolver = QWeakPointer< ScriptResolver >( qobject_cast< ScriptResolver* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) ); + + connect( m_spotifyResolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); connect( m_spotifyResolver.data(), SIGNAL( customMessage( QString,QVariantMap ) ), this, SLOT( resolverMessage( QString, QVariantMap ) ) ); const bool hasMigrated = configuration().value( "hasMigrated" ).toBool(); @@ -113,6 +120,94 @@ SpotifyAccount::init() msg[ "_msgtype" ] = "getCredentials"; m_spotifyResolver.data()->sendMessage( msg ); } + +} + + +void +SpotifyAccount::resolverChanged() +{ + setAccountFriendlyName( m_spotifyResolver.data()->name() ); + emit connectionStateChanged( connectionState() ); +} + + +Attica::Content +SpotifyAccount::atticaContent() const +{ + return AtticaManager::instance()->resolverForId( "spotify" ); +} + + +void +SpotifyAccount::authenticate() +{ + if ( !AtticaManager::instance()->resolversLoaded() ) + { + // If we're still waiting to load, wait for the attica resolvers to come down the pipe + connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded( Attica::Content::List ) ), Qt::UniqueConnection ); + return; + } + + const Attica::Content res = AtticaManager::instance()->resolverForId( "spotify" ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + + qDebug() << "Spotify account authenticating..."; + if ( m_spotifyResolver.isNull() && state == AtticaManager::Installed ) + { + // We don;t have the resolver but it has been installed via attica already, so lets just turn it on + hookupResolver(); + } + else if ( m_spotifyResolver.isNull() ) + { + qDebug() << "Got null resolver but asked to authenticate, so installing if we have one from attica:" << res.isValid() << res.id(); + if ( res.isValid() && !res.id().isEmpty() ) + AtticaManager::instance()->installResolver( res, false ); + } + else + { + m_spotifyResolver.data()->start(); + } + + emit connectionStateChanged( connectionState() ); +} + + +void +SpotifyAccount::deauthenticate() +{ + if ( !m_spotifyResolver.isNull() && m_spotifyResolver.data()->running() ) + m_spotifyResolver.data()->stop(); + + emit connectionStateChanged( connectionState() ); +} + + +bool +SpotifyAccount::isAuthenticated() const +{ + return !m_spotifyResolver.isNull() && m_spotifyResolver.data()->running(); +} + + +Account::ConnectionState +SpotifyAccount::connectionState() const +{ + return (!m_spotifyResolver.isNull() && m_spotifyResolver.data()->running()) ? Account::Connected : Account::Disconnected; +} + + +void +SpotifyAccount::resolverInstalled(const QString& resolverId) +{ + +} + + +void +SpotifyAccount::atticaLoaded( Attica::Content::List ) +{ + } diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/accounts/spotify/SpotifyAccount.h index c3ee789d8..daf339e50 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/accounts/spotify/SpotifyAccount.h @@ -22,6 +22,7 @@ #include "accounts/ResolverAccount.h" #include "SourceList.h" +#include "AtticaManager.h" #include "Playlist.h" #include "utils/TomahawkUtils.h" #include "utils/SmartPointerList.h" @@ -61,9 +62,6 @@ public: virtual QString factoryId() const { return "spotifyaccount"; } virtual QString prettyName() const { return "Spotify"; } - virtual bool acceptsPath( const QString& path ) const; - virtual Account* createFromPath( const QString& path ); - virtual AccountTypes types() const { return AccountTypes( ResolverType ); } virtual bool allowUserCreation() const { return false; } virtual QPixmap icon() const; @@ -71,7 +69,7 @@ public: }; -class SpotifyAccount : public ResolverAccount +class SpotifyAccount : public CustomAtticaAccount { Q_OBJECT public: @@ -83,6 +81,11 @@ public: virtual QWidget* configurationWidget(); virtual QWidget* aboutWidget(); virtual void saveConfig(); + virtual Attica::Content atticaContent() const; + virtual void authenticate(); + virtual ConnectionState connectionState() const; + virtual bool isAuthenticated() const; + virtual void deauthenticate(); virtual QWidget* aclWidget() { return 0; } virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); } @@ -98,8 +101,12 @@ public: public slots: void aboutToShow( QAction* action, const Tomahawk::playlist_ptr& playlist ); void syncActionTriggered( bool ); + void atticaLoaded(Attica::Content::List); private slots: + void resolverChanged(); + void resolverInstalled( const QString& resolverId ); + void resolverMessage( const QString& msgType, const QVariantMap& msg ); void login( const QString& username, const QString& password ); @@ -110,6 +117,7 @@ private slots: private: void init(); + void hookupResolver(); void loadPlaylists(); void clearUser( bool permanentlyDelete = false ); diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 6f6c23a08..2652a3b55 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -98,7 +98,7 @@ AtticaManager::AtticaManager( QObject* parent ) // resolvers // m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); - m_manager.addProviderFile( QUrl( "http://localhost/resolvers/providers.xml" ) ); + m_manager.addProviderFile( QUrl( "http://lycophron/resolvers/providers.xml" ) ); qRegisterMetaType< Attica::Content >( "Attica::Content" ); } diff --git a/src/libtomahawk/accounts/ResolverAccount.h b/src/libtomahawk/accounts/ResolverAccount.h index 03bd46fc5..bcf167640 100644 --- a/src/libtomahawk/accounts/ResolverAccount.h +++ b/src/libtomahawk/accounts/ResolverAccount.h @@ -99,7 +99,7 @@ protected: * Extends ResolverAccount with what attica additionally provides---e.g. icon * Assumes certain file layout on disk. */ -class AtticaResolverAccount : public ResolverAccount +class DLLEXPORT AtticaResolverAccount : public ResolverAccount { Q_OBJECT public: From ff431b11832c4c8d2b1db77a4034276c95b5f11c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 16 May 2012 18:07:56 -0400 Subject: [PATCH 14/25] More work on handling binary resolver install --- src/AccountDelegate.cpp | 1 + src/accounts/spotify/SpotifyAccount.cpp | 38 ++++++++++++--- src/accounts/spotify/SpotifyAccount.h | 2 + src/libtomahawk/AtticaManager.cpp | 46 ++++++++++++------- src/libtomahawk/TomahawkSettings.cpp | 29 ++++++++++++ src/libtomahawk/TomahawkSettings.h | 2 +- .../accounts/AccountModelFilterProxy.cpp | 2 +- src/libtomahawk/utils/TomahawkUtils.cpp | 4 +- src/libtomahawk/utils/TomahawkUtils_Mac.mm | 10 ++-- 9 files changed, 104 insertions(+), 30 deletions(-) diff --git a/src/AccountDelegate.cpp b/src/AccountDelegate.cpp index 505257a89..6f6185708 100644 --- a/src/AccountDelegate.cpp +++ b/src/AccountDelegate.cpp @@ -738,6 +738,7 @@ void AccountDelegate::errorInstalling( const QPersistentModelIndex& idx ) { // Just hide the loading spinner as we do after a successful install + qDebug() << "ERROR INSTALLING index:" << idx; doneInstalling( idx ); } diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index 2d8278bf0..902c5695e 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -45,6 +45,14 @@ using namespace Accounts; static QPixmap* s_icon = 0; +#ifdef Q_OS_MAC +static QString s_resolverId = "spotify-osx"; +#elif defined(Q_OS_WIN) +static QString s_resolverId = "spotify-win"; +#else +static QString s_resolverId = "spotify-linux" +#endif + Account* SpotifyAccountFactory::createAccount( const QString& accountId ) { @@ -80,14 +88,21 @@ SpotifyAccount::init() { qRegisterMetaType< Tomahawk::Accounts::SpotifyPlaylistInfo* >( "Tomahawk::Accounts::SpotifyPlaylist*" ); - AtticaManager::instance()->registerCustomAccount( "spotify", this ); + setAccountFriendlyName( "Spotify" ); + + AtticaManager::instance()->registerCustomAccount( s_resolverId, this ); connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( resolverInstalled( QString ) ) ); - const Attica::Content res = AtticaManager::instance()->resolverForId( "spotify" ); + const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); - if ( state == AtticaManager::Installed ) + if ( !checkForResolver() && state != AtticaManager::Uninstalled ) + { + // If the user manually deleted the resolver, mark it as uninstalled, so we re-fetch for the user + AtticaManager::instance()->uninstallResolver( res ); + } + else if ( state == AtticaManager::Installed ) { hookupResolver(); } @@ -100,7 +115,7 @@ SpotifyAccount::hookupResolver() // initialize the resolver itself. this is called if the account actually has an installed spotify resolver, // as it might not. // If there is a last.fm resolver from attica installed, create the corresponding ExternalResolver* and hook up to it - const Attica::Content res = AtticaManager::instance()->resolverForId( "spotify" ); + const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); Q_ASSERT( state == AtticaManager::Installed ); Q_UNUSED( state ); @@ -124,6 +139,17 @@ SpotifyAccount::hookupResolver() } +bool SpotifyAccount::checkForResolver() +{ +#ifdef Q_OS_MAC + const QDir path = QCoreApplication::applicationDirPath(); + QFile file( path.absoluteFilePath( "spotify_tomahawkresolver" ) ); + return file.exists(); +#endif + + return false; +} + void SpotifyAccount::resolverChanged() { @@ -135,7 +161,7 @@ SpotifyAccount::resolverChanged() Attica::Content SpotifyAccount::atticaContent() const { - return AtticaManager::instance()->resolverForId( "spotify" ); + return AtticaManager::instance()->resolverForId( s_resolverId ); } @@ -149,7 +175,7 @@ SpotifyAccount::authenticate() return; } - const Attica::Content res = AtticaManager::instance()->resolverForId( "spotify" ); + const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); qDebug() << "Spotify account authenticating..."; diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/accounts/spotify/SpotifyAccount.h index daf339e50..44930cb5e 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/accounts/spotify/SpotifyAccount.h @@ -118,6 +118,8 @@ private slots: private: void init(); void hookupResolver(); + bool checkForResolver(); + void loadPlaylists(); void clearUser( bool permanentlyDelete = false ); diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 2652a3b55..857ed434f 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -43,10 +43,11 @@ class BinaryInstallerHelper : public QObject { Q_OBJECT public: - explicit BinaryInstallerHelper( const QString& resolverId, AtticaManager* manager) + explicit BinaryInstallerHelper( const QString& resolverId, bool createAccount, AtticaManager* manager) : QObject( manager ) , m_manager( QWeakPointer< AtticaManager >( manager ) ) , m_resolverId( resolverId ) + , m_createAccount( createAccount ) { Q_ASSERT( !m_resolverId.isEmpty() ); Q_ASSERT( !m_manager.isNull() ); @@ -55,17 +56,21 @@ public: virtual ~BinaryInstallerHelper() {} public slots: - void extractSucceeded( const QString& path ) + void installSucceeded( const QString& path ) { + qDebug() << Q_FUNC_INFO << "install of binary resolver succeeded, enabling"; + if ( m_manager.isNull() ) return; - Tomahawk::Accounts::Account* acct = Tomahawk::Accounts::AccountManager::instance()->accountFromPath( path ); - - Tomahawk::Accounts::AccountManager::instance()->addAccount( acct ); - TomahawkSettings::instance()->addAccount( acct->accountId() ); - Tomahawk::Accounts::AccountManager::instance()->enableAccount( acct ); + if ( m_createAccount ) + { + Tomahawk::Accounts::Account* acct = Tomahawk::Accounts::AccountManager::instance()->accountFromPath( path ); + Tomahawk::Accounts::AccountManager::instance()->addAccount( acct ); + TomahawkSettings::instance()->addAccount( acct->accountId() ); + Tomahawk::Accounts::AccountManager::instance()->enableAccount( acct ); + } m_manager.data()->m_resolverStates[ m_resolverId ].state = AtticaManager::Installed; TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_manager.data()->m_resolverStates ); @@ -74,8 +79,10 @@ public slots: deleteLater(); } - void extractFailed() + void installFailed() { + qDebug() << Q_FUNC_INFO << "install failed"; + if ( m_manager.isNull() ) return; @@ -86,6 +93,7 @@ public slots: private: QString m_resolverId; + bool m_createAccount; QWeakPointer m_manager; }; @@ -267,11 +275,8 @@ AtticaManager::userHasRated( const Content& c ) const bool AtticaManager::hasCustomAccountForAttica( const QString &id ) const { - // Only last.fm at the moment contains a custom account - if ( id == "lastfm" ) - return true; - - return false; + qDebug() << "Got custom account for?" << id << m_customAccounts.keys(); + return m_customAccounts.keys().contains( id ); } @@ -417,6 +422,10 @@ AtticaManager::binaryResolversList( BaseJob* j ) r.binary = true; m_resolverStates.insert( c.id(), r ); } + else if ( m_resolverStates[ c.id() ].binary != true ) + { // HACK workaround... why is this not set in the first place sometimes? Migration issue? + m_resolverStates[ c.id() ].binary = true; + } } @@ -580,10 +589,13 @@ AtticaManager::payloadFetched() if ( !TomahawkUtils::verifyFile( f.fileName(), signature ) ) { qWarning() << "FILE SIGNATURE FAILED FOR BINARY RESOLVER! WARNING! :" << f.fileName() << signature; - return; } - - TomahawkUtils::extractBinaryResolver( f.fileName(), new BinaryInstallerHelper( resolverId, this ) ); + else + { + TomahawkUtils::extractBinaryResolver( f.fileName(), new BinaryInstallerHelper( resolverId, reply->property( "createAccount" ).toBool(), this ) ); + // Don't emit failed yet + installedSuccessfully = true; + } } else { @@ -702,4 +714,4 @@ AtticaManager::doResolverRemove( const QString& id ) const TomahawkUtils::removeDirectory( resolverDir.absolutePath() ); } -#include "AtticaManager.moc" \ No newline at end of file +#include "AtticaManager.moc" diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 3f68f5f99..002de3865 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -516,6 +516,35 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) remove( "playlistupdaters" ); } + else if ( oldVersion == 11 ) + { + // If the user doesn't have a spotify account, create one, since now it + // is like the last.fm account and always exists + QStringList allAccounts = value( "accounts/allaccounts" ).toStringList(); + bool found = false; + foreach ( const QString& account, allAccounts ) + { + if ( account.startsWith( "spotifyaccount_" ) ) + { + found = true; + break; + } + } + + if ( !found ) + { + const QString accountKey = QString( "spotifyaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); + beginGroup( "accounts/" + accountKey ); + setValue( "enabled", false ); + setValue( "types", QStringList() << "ResolverType" ); + setValue( "credentials", QVariantHash() ); + setValue( "configuration", QVariantHash() ); + endGroup(); + + allAccounts << accountKey; + setValue( "accounts/allaccounts", allAccounts ); + } + } } diff --git a/src/libtomahawk/TomahawkSettings.h b/src/libtomahawk/TomahawkSettings.h index b998c46d6..421a6480c 100644 --- a/src/libtomahawk/TomahawkSettings.h +++ b/src/libtomahawk/TomahawkSettings.h @@ -30,7 +30,7 @@ #include "DllMacro.h" -#define TOMAHAWK_SETTINGS_VERSION 11 +#define TOMAHAWK_SETTINGS_VERSION 12 /** * Convenience wrapper around QSettings for tomahawk-specific config diff --git a/src/libtomahawk/accounts/AccountModelFilterProxy.cpp b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp index 2e3b9bc8d..fee5516f3 100644 --- a/src/libtomahawk/accounts/AccountModelFilterProxy.cpp +++ b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp @@ -38,7 +38,7 @@ AccountModelFilterProxy::setSourceModel( QAbstractItemModel* sourceModel ) connect( sourceModel, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( onScrollTo( QModelIndex ) ) ); connect( sourceModel, SIGNAL( startInstalling( QPersistentModelIndex ) ), this, SLOT( onStartInstalling( QPersistentModelIndex ) ) ); connect( sourceModel, SIGNAL( doneInstalling( QPersistentModelIndex ) ), this, SLOT( onDoneInstalling( QPersistentModelIndex ) ) ); - connect( sourceModel, SIGNAL( doneInstalling( QPersistentModelIndex ) ), this, SLOT( errorInstalling( QPersistentModelIndex ) ) ); + connect( sourceModel, SIGNAL( errorInstalling( QPersistentModelIndex ) ), this, SLOT( onErrorInstalling( QPersistentModelIndex ) ) ); QSortFilterProxyModel::setSourceModel( sourceModel ); } diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index c5533d599..e2b220e10 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -845,7 +845,7 @@ unzipFileInFolder( const QString &zipFileName, const QDir &folder ) void -extractBinaryResolver( const QString& zipFilename, QObject* ) +extractBinaryResolver( const QString& zipFilename, QObject* receiver ) { #if !defined(Q_OS_MAC) && !defined (Q_OS_WIN) Q_ASSERT( false ); @@ -877,7 +877,7 @@ extractBinaryResolver( const QString& zipFilename, QObject* ) const QString src = toList.absoluteFilePath( files.first() ); qDebug() << "OS X: Copying binary resolver from to:" << src << dest; - copyWithAuthentication( src, dest, 0 ); + copyWithAuthentication( src, dest, receiver ); #elif defined(Q_OS_WIN) #endif diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.mm b/src/libtomahawk/utils/TomahawkUtils_Mac.mm index 07ab5da2c..fc5c3d08c 100644 --- a/src/libtomahawk/utils/TomahawkUtils_Mac.mm +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.mm @@ -42,12 +42,11 @@ - (void)moveFinished { - if ( receiver ) - QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); - // HACK since I can't figure out how to get QuaZip to maintain executable permissions after unzip (nor find the info) // we set the binary to executable here + NSLog(@"Move succeeded!, handling result"); + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; NSError* error; NSDictionary* attrs = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0755], NSFilePosixPermissions, nil]; @@ -58,10 +57,15 @@ if (!success) { NSLog( @"Failed to do chmod +x of moved resolver! %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey] ); } + + if ( receiver ) + QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); + } - (void)moveFailedWithError:(NSError *)error { + NSLog(@"Move failed, handling result"); if ( receiver ) QMetaObject::invokeMethod(receiver, "installFailed", Qt::DirectConnection); } From 34656e63954357a3d02b59184713918ab36d04f5 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 16 May 2012 22:49:29 -0400 Subject: [PATCH 15/25] bugfixing adding spotify account --- admin/mac/macdeploy.py | 5 ----- src/accounts/spotify/SpotifyAccount.cpp | 19 +++++++++++++++++-- src/accounts/spotify/SpotifyAccount.h | 2 +- src/libtomahawk/AtticaManager.cpp | 6 ++++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/admin/mac/macdeploy.py b/admin/mac/macdeploy.py index 71bdefb77..3a2ffffd8 100755 --- a/admin/mac/macdeploy.py +++ b/admin/mac/macdeploy.py @@ -495,11 +495,6 @@ for plugin in VLC_PLUGINS: for plugin in TOMAHAWK_PLUGINS: FixPlugin(plugin, '../MacOS') -try: - FixPlugin('spotify_tomahawkresolver', '../MacOS') -except: - print 'Failed to find spotify resolver' - try: FixPlugin('tomahawk_crash_reporter', '../MacOS') except: diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index 902c5695e..c3e4106f5 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -27,6 +27,7 @@ #include "utils/TomahawkUtils.h" #include "ActionCollection.h" #include "Pipeline.h" +#include "accounts/AccountManager.h" #ifndef ENABLE_HEADLESS #include "jobview/JobStatusView.h" @@ -86,6 +87,13 @@ SpotifyAccount::~SpotifyAccount() void SpotifyAccount::init() { + if ( !AtticaManager::instance()->resolversLoaded() ) + { + // If we're still waiting to load, wait for the attica resolvers to come down the pipe + connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( init() ), Qt::UniqueConnection ); + return; + } + qRegisterMetaType< Tomahawk::Accounts::SpotifyPlaylistInfo* >( "Tomahawk::Accounts::SpotifyPlaylist*" ); setAccountFriendlyName( "Spotify" ); @@ -122,6 +130,7 @@ SpotifyAccount::hookupResolver() const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() ); + qDebug() << "Starting spotify resolver with path:" << data.scriptPath; m_spotifyResolver = QWeakPointer< ScriptResolver >( qobject_cast< ScriptResolver* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) ); connect( m_spotifyResolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); @@ -226,14 +235,20 @@ SpotifyAccount::connectionState() const void SpotifyAccount::resolverInstalled(const QString& resolverId) { - + if ( resolverId == s_resolverId ) + { + // We requested this install, so we want to launch it + hookupResolver(); + AccountManager::instance()->enableAccount( this ); + } } void SpotifyAccount::atticaLoaded( Attica::Content::List ) { - + disconnect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded( Attica::Content::List ) ) ); + authenticate(); } diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/accounts/spotify/SpotifyAccount.h index 44930cb5e..7d23dddde 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/accounts/spotify/SpotifyAccount.h @@ -115,8 +115,8 @@ private slots: void startPlaylistSyncWithPlaylist( const QString& msgType, const QVariantMap& msg ); void playlistCreated( const QString& msgType, const QVariantMap& msg ); -private: void init(); +private: void hookupResolver(); bool checkForResolver(); diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 857ed434f..9ae1d27cf 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -58,7 +58,7 @@ public: public slots: void installSucceeded( const QString& path ) { - qDebug() << Q_FUNC_INFO << "install of binary resolver succeeded, enabling"; + qDebug() << Q_FUNC_INFO << "install of binary resolver succeeded, enabling: " << path; if ( m_manager.isNull() ) return; @@ -72,7 +72,9 @@ public slots: Tomahawk::Accounts::AccountManager::instance()->enableAccount( acct ); } + m_manager.data()->m_resolverStates[ m_resolverId ].scriptPath = path; m_manager.data()->m_resolverStates[ m_resolverId ].state = AtticaManager::Installed; + TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_manager.data()->m_resolverStates ); emit m_manager.data()->resolverInstalled( m_resolverId ); emit m_manager.data()->resolverStateChanged( m_resolverId ); @@ -106,7 +108,7 @@ AtticaManager::AtticaManager( QObject* parent ) // resolvers // m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); - m_manager.addProviderFile( QUrl( "http://lycophron/resolvers/providers.xml" ) ); + m_manager.addProviderFile( QUrl( "http://localhost/resolvers/providers.xml" ) ); qRegisterMetaType< Attica::Content >( "Attica::Content" ); } From e8247421ab418e23b12f070d7b7872635eed81b3 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 17 May 2012 18:09:16 -0400 Subject: [PATCH 16/25] Don't show spotify config widget unless we have a resolver --- admin/mac/macdeploy.py | 2 +- src/accounts/spotify/SpotifyAccount.cpp | 3 +++ src/libtomahawk/AtticaManager.cpp | 6 ++++++ src/libtomahawk/mac/FileHelpers.mm | 2 -- src/libtomahawk/utils/TomahawkUtils_Mac.mm | 2 ++ src/main.cpp | 2 +- 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/admin/mac/macdeploy.py b/admin/mac/macdeploy.py index 3a2ffffd8..17dc312e3 100755 --- a/admin/mac/macdeploy.py +++ b/admin/mac/macdeploy.py @@ -206,7 +206,7 @@ TOMAHAWK_PLUGINS = [ ] QT_PLUGINS_SEARCH_PATH=[ - '/usr/local/Cellar/qt/4.7.4/plugins', + '/usr/local/Cellar/qt/4.8.0/plugins', ] diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index c3e4106f5..866cbc309 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -585,6 +585,9 @@ SpotifyAccount::icon() const QWidget* SpotifyAccount::configurationWidget() { + if ( m_spotifyResolver.isNull() ) + return 0; + if ( m_configWidget.isNull() ) { m_configWidget = QWeakPointer< SpotifyAccountConfig >( new SpotifyAccountConfig( this ) ); diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 9ae1d27cf..265dec190 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -200,12 +200,14 @@ AtticaManager::resolvers() const Content AtticaManager::resolverForId( const QString& id ) const { + qDebug() << "Asked for resolver for id:" << id; foreach ( const Attica::Content& c, m_resolvers ) { if ( c.id() == id ) return c; } + qDebug() << "Not found!"; return Content(); } @@ -214,6 +216,10 @@ AtticaManager::resolverForId( const QString& id ) const AtticaManager::ResolverState AtticaManager::resolverState ( const Content& resolver ) const { + qDebug() << Q_FUNC_INFO << "Returning resolver state for resolver:" << resolver .id() << "known?" << m_resolverStates.contains( resolver.id() ); + if ( m_resolverStates.contains( resolver.id() ) ) + qDebug() << "KNOWN and stateL" << m_resolverStates[ resolver.id() ].state; + if ( !m_resolverStates.contains( resolver.id() ) ) { return AtticaManager::Uninstalled; diff --git a/src/libtomahawk/mac/FileHelpers.mm b/src/libtomahawk/mac/FileHelpers.mm index 97ee3c4ed..a53d1690d 100644 --- a/src/libtomahawk/mac/FileHelpers.mm +++ b/src/libtomahawk/mac/FileHelpers.mm @@ -76,8 +76,6 @@ static BOOL AuthorizationExecuteWithPrivilegesAndWait(AuthorizationRef authoriza + (void) moveFile:(NSString *)source to:(NSString*)dest withDelegate:delegate { - NSLog(@"FileHelpers moving file from %@ to %@", source, dest); - NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:source, TKCopySourceKey, dest, TKCopyDestinationKey, delegate, TKInstallerDelegateKey, nil]; [NSThread detachNewThreadSelector:@selector(performMoveWithInfo:) toTarget:self withObject:info]; } diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.mm b/src/libtomahawk/utils/TomahawkUtils_Mac.mm index fc5c3d08c..346a4d662 100644 --- a/src/libtomahawk/utils/TomahawkUtils_Mac.mm +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.mm @@ -58,6 +58,8 @@ NSLog( @"Failed to do chmod +x of moved resolver! %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey] ); } + qDebug() << "Successfully install tomahawk resolver with path:" << path; + if ( receiver ) QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); diff --git a/src/main.cpp b/src/main.cpp index 2c786bf55..717fa6cd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -147,7 +147,7 @@ main( int argc, char *argv[] ) #ifndef ENABLE_HEADLESSs #ifdef WITH_BREAKPAD - new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() ); + //new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() ); #endif #endif From fb842a35809cf4d6974fdd5b9058484e95e84568 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 17 May 2012 18:09:38 -0400 Subject: [PATCH 17/25] Revert "Don't show spotify config widget unless we have a resolver" This reverts commit e8247421ab418e23b12f070d7b7872635eed81b3. --- admin/mac/macdeploy.py | 2 +- src/accounts/spotify/SpotifyAccount.cpp | 3 --- src/libtomahawk/AtticaManager.cpp | 6 ------ src/libtomahawk/mac/FileHelpers.mm | 2 ++ src/libtomahawk/utils/TomahawkUtils_Mac.mm | 2 -- src/main.cpp | 2 +- 6 files changed, 4 insertions(+), 13 deletions(-) diff --git a/admin/mac/macdeploy.py b/admin/mac/macdeploy.py index 17dc312e3..3a2ffffd8 100755 --- a/admin/mac/macdeploy.py +++ b/admin/mac/macdeploy.py @@ -206,7 +206,7 @@ TOMAHAWK_PLUGINS = [ ] QT_PLUGINS_SEARCH_PATH=[ - '/usr/local/Cellar/qt/4.8.0/plugins', + '/usr/local/Cellar/qt/4.7.4/plugins', ] diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index 866cbc309..c3e4106f5 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -585,9 +585,6 @@ SpotifyAccount::icon() const QWidget* SpotifyAccount::configurationWidget() { - if ( m_spotifyResolver.isNull() ) - return 0; - if ( m_configWidget.isNull() ) { m_configWidget = QWeakPointer< SpotifyAccountConfig >( new SpotifyAccountConfig( this ) ); diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 265dec190..9ae1d27cf 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -200,14 +200,12 @@ AtticaManager::resolvers() const Content AtticaManager::resolverForId( const QString& id ) const { - qDebug() << "Asked for resolver for id:" << id; foreach ( const Attica::Content& c, m_resolvers ) { if ( c.id() == id ) return c; } - qDebug() << "Not found!"; return Content(); } @@ -216,10 +214,6 @@ AtticaManager::resolverForId( const QString& id ) const AtticaManager::ResolverState AtticaManager::resolverState ( const Content& resolver ) const { - qDebug() << Q_FUNC_INFO << "Returning resolver state for resolver:" << resolver .id() << "known?" << m_resolverStates.contains( resolver.id() ); - if ( m_resolverStates.contains( resolver.id() ) ) - qDebug() << "KNOWN and stateL" << m_resolverStates[ resolver.id() ].state; - if ( !m_resolverStates.contains( resolver.id() ) ) { return AtticaManager::Uninstalled; diff --git a/src/libtomahawk/mac/FileHelpers.mm b/src/libtomahawk/mac/FileHelpers.mm index a53d1690d..97ee3c4ed 100644 --- a/src/libtomahawk/mac/FileHelpers.mm +++ b/src/libtomahawk/mac/FileHelpers.mm @@ -76,6 +76,8 @@ static BOOL AuthorizationExecuteWithPrivilegesAndWait(AuthorizationRef authoriza + (void) moveFile:(NSString *)source to:(NSString*)dest withDelegate:delegate { + NSLog(@"FileHelpers moving file from %@ to %@", source, dest); + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:source, TKCopySourceKey, dest, TKCopyDestinationKey, delegate, TKInstallerDelegateKey, nil]; [NSThread detachNewThreadSelector:@selector(performMoveWithInfo:) toTarget:self withObject:info]; } diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.mm b/src/libtomahawk/utils/TomahawkUtils_Mac.mm index 346a4d662..fc5c3d08c 100644 --- a/src/libtomahawk/utils/TomahawkUtils_Mac.mm +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.mm @@ -58,8 +58,6 @@ NSLog( @"Failed to do chmod +x of moved resolver! %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey] ); } - qDebug() << "Successfully install tomahawk resolver with path:" << path; - if ( receiver ) QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); diff --git a/src/main.cpp b/src/main.cpp index 717fa6cd7..2c786bf55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -147,7 +147,7 @@ main( int argc, char *argv[] ) #ifndef ENABLE_HEADLESSs #ifdef WITH_BREAKPAD - //new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() ); + new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() ); #endif #endif From 183b0c89584eb3c890b4aa168134b0aca36eaec4 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 17 May 2012 18:11:13 -0400 Subject: [PATCH 18/25] Don't show spotify config widget if we have no resolver --- src/accounts/spotify/SpotifyAccount.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index c3e4106f5..866cbc309 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -585,6 +585,9 @@ SpotifyAccount::icon() const QWidget* SpotifyAccount::configurationWidget() { + if ( m_spotifyResolver.isNull() ) + return 0; + if ( m_configWidget.isNull() ) { m_configWidget = QWeakPointer< SpotifyAccountConfig >( new SpotifyAccountConfig( this ) ); From 48ab96a1f6d9cffa2c30fa43e78fbea84a7a1fff Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 17 May 2012 19:03:18 -0400 Subject: [PATCH 19/25] Add hack/workaround for manually adding spotify resolver --- src/SettingsDialog.cpp | 34 ++++++++++++-- src/accounts/spotify/SpotifyAccount.cpp | 62 ++++++++++++++++++++----- src/accounts/spotify/SpotifyAccount.h | 6 ++- src/libtomahawk/AtticaManager.h | 2 +- 4 files changed, 87 insertions(+), 17 deletions(-) diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index a03e36262..c9cc2546c 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -52,6 +52,7 @@ #include #include "utils/Logger.h" #include "AccountFactoryWrapper.h" +#include "accounts/spotify/SpotifyAccount.h" #include "ui_ProxyDialog.h" #include "ui_StackedSettingsDialog.h" @@ -458,15 +459,40 @@ SettingsDialog::installFromFile() if( !resolver.isEmpty() ) { + const QFileInfo resolverAbsoluteFilePath( resolver ); + TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() ); + + if ( resolverAbsoluteFilePath.baseName() == "spotify_tomahawkresolver" ) + { + // HACK if this is a spotify resolver, we treat is specially. + // usually we expect the user to just download the spotify resolver from attica, + // however developers, those who build their own tomahawk, can't do that, or linux + // users can't do that. However, we have an already-existing SpotifyAccount that we + // know exists that we need to use this resolver path. + // + // Hence, we special-case the spotify resolver and directly set the path on it here. + SpotifyAccount* acct = 0; + foreach ( Account* account, AccountManager::instance()->accounts() ) + { + if ( SpotifyAccount* spotify = qobject_cast< SpotifyAccount* >( account ) ) + { + acct = spotify; + break; + } + } + + if ( acct ) + { + acct->setManualResolverPath( resolver ); + return; + } + } + Account* acct = AccountManager::instance()->accountFromPath( resolver ); AccountManager::instance()->addAccount( acct ); TomahawkSettings::instance()->addAccount( acct->accountId() ); AccountManager::instance()->enableAccount( acct ); - - - QFileInfo resolverAbsoluteFilePath( resolver ); - TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() ); } } diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index 866cbc309..e8dbafca4 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -28,6 +28,7 @@ #include "ActionCollection.h" #include "Pipeline.h" #include "accounts/AccountManager.h" +#include "utils/Closure.h" #ifndef ENABLE_HEADLESS #include "jobview/JobStatusView.h" @@ -105,12 +106,13 @@ SpotifyAccount::init() const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + const QString path = configuration().value( "resolverPath" ).toString(); // Manual path override if ( !checkForResolver() && state != AtticaManager::Uninstalled ) { // If the user manually deleted the resolver, mark it as uninstalled, so we re-fetch for the user AtticaManager::instance()->uninstallResolver( res ); } - else if ( state == AtticaManager::Installed ) + else if ( state == AtticaManager::Installed || !path.isEmpty() ) { hookupResolver(); } @@ -122,16 +124,21 @@ SpotifyAccount::hookupResolver() { // initialize the resolver itself. this is called if the account actually has an installed spotify resolver, // as it might not. - // If there is a last.fm resolver from attica installed, create the corresponding ExternalResolver* and hook up to it - const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); - const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); - Q_ASSERT( state == AtticaManager::Installed ); - Q_UNUSED( state ); + // If there is a spotify resolver from attica installed, create the corresponding ExternalResolver* and hook up to it + QString path = configuration().value( "resolverPath" ).toString(); + if ( path.isEmpty() ) + { + const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + Q_ASSERT( state == AtticaManager::Installed ); + Q_UNUSED( state ); - const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() ); + const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() ); + path = data.scriptPath; + } - qDebug() << "Starting spotify resolver with path:" << data.scriptPath; - m_spotifyResolver = QWeakPointer< ScriptResolver >( qobject_cast< ScriptResolver* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) ); + qDebug() << "Starting spotify resolver with path:" << path; + m_spotifyResolver = QWeakPointer< ScriptResolver >( qobject_cast< ScriptResolver* >( Pipeline::instance()->addScriptResolver( path, enabled() ) ) ); connect( m_spotifyResolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); connect( m_spotifyResolver.data(), SIGNAL( customMessage( QString,QVariantMap ) ), this, SLOT( resolverMessage( QString, QVariantMap ) ) ); @@ -162,7 +169,6 @@ bool SpotifyAccount::checkForResolver() void SpotifyAccount::resolverChanged() { - setAccountFriendlyName( m_spotifyResolver.data()->name() ); emit connectionStateChanged( connectionState() ); } @@ -199,7 +205,7 @@ SpotifyAccount::authenticate() if ( res.isValid() && !res.id().isEmpty() ) AtticaManager::instance()->installResolver( res, false ); } - else + else if ( !m_spotifyResolver.data()->running() ) { m_spotifyResolver.data()->start(); } @@ -252,6 +258,40 @@ SpotifyAccount::atticaLoaded( Attica::Content::List ) } +void +SpotifyAccount::setManualResolverPath( const QString &resolverPath ) +{ + Q_ASSERT( !resolverPath.isEmpty() ); + + QVariantHash configuration; + configuration[ "resolverPath" ] = resolverPath; + setConfiguration( configuration ); + sync(); + + if ( !m_spotifyResolver.isNull() ) + { + // replace + //connect( m_spotifyResolver.data(), SIGNAL( destroyed( QObject* ) ), this, SLOT( hookupResolver() ) ); + NewClosure( m_spotifyResolver.data(), SIGNAL( destroyed() ), this, SLOT( hookupAfterDeletion( bool ) ), true ); + m_spotifyResolver.data()->deleteLater(); + } + else + { + hookupResolver(); + authenticate(); + } +} + + +void +SpotifyAccount::hookupAfterDeletion( bool autostart ) +{ + hookupResolver(); + if ( autostart ) + authenticate(); +} + + void SpotifyAccount::aboutToShow( QAction* action, const playlist_ptr& playlist ) { diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/accounts/spotify/SpotifyAccount.h index 7d23dddde..b8a9884cb 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/accounts/spotify/SpotifyAccount.h @@ -98,6 +98,8 @@ public: bool deleteOnUnsync() const; + void setManualResolverPath( const QString& resolverPath ); + public slots: void aboutToShow( QAction* action, const Tomahawk::playlist_ptr& playlist ); void syncActionTriggered( bool ); @@ -116,9 +118,11 @@ private slots: void playlistCreated( const QString& msgType, const QVariantMap& msg ); void init(); + void hookupAfterDeletion( bool autostart ); + private: - void hookupResolver(); bool checkForResolver(); + void hookupResolver(); void loadPlaylists(); void clearUser( bool permanentlyDelete = false ); diff --git a/src/libtomahawk/AtticaManager.h b/src/libtomahawk/AtticaManager.h index 7add543d9..fecb2d27f 100644 --- a/src/libtomahawk/AtticaManager.h +++ b/src/libtomahawk/AtticaManager.h @@ -92,7 +92,7 @@ public: /** If the resolver coming from libattica has a native custom c++ account - as well. For example the last.fm account. + as well. For example the last.fm & spotify accounts. */ bool hasCustomAccountForAttica( const QString& id ) const; Tomahawk::Accounts::Account* customAccountForAttica( const QString& id ) const; From 841b151accfd79e625665f0ec4a2a5b14779a439 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 17 May 2012 20:56:36 -0400 Subject: [PATCH 20/25] fixes for linux binary ghns --- src/accounts/spotify/SpotifyAccount.cpp | 38 ++++++++++++++++----- src/accounts/spotify/SpotifyAccount.h | 5 ++- src/libtomahawk/accounts/Account.h | 4 +++ src/libtomahawk/accounts/AccountManager.cpp | 3 ++ src/libtomahawk/accounts/AccountModel.cpp | 3 +- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index e8dbafca4..ba6033081 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -40,6 +40,7 @@ #include #include #include +#include using namespace Tomahawk; using namespace Accounts; @@ -52,7 +53,7 @@ static QString s_resolverId = "spotify-osx"; #elif defined(Q_OS_WIN) static QString s_resolverId = "spotify-win"; #else -static QString s_resolverId = "spotify-linux" +static QString s_resolverId = "spotify-linux"; #endif Account* @@ -73,7 +74,8 @@ SpotifyAccountFactory::icon() const SpotifyAccount::SpotifyAccount( const QString& accountId ) -: CustomAtticaAccount( accountId ) + : CustomAtticaAccount( accountId ) + , m_preventEnabling( false ) { init(); } @@ -204,6 +206,22 @@ SpotifyAccount::authenticate() qDebug() << "Got null resolver but asked to authenticate, so installing if we have one from attica:" << res.isValid() << res.id(); if ( res.isValid() && !res.id().isEmpty() ) AtticaManager::instance()->installResolver( res, false ); + else + { +#ifdef Q_OS_LINUX + // Can't install from attica yet on linux, so show a warning if the user tries to turn it on. + // TODO make a prettier display + QMessageBox box; + box.setWindowTitle( tr( "Manual Install Required" ) ); + box.setTextFormat( Qt::RichText ); + box.setIcon( QMessageBox::Information ); + box.setText( tr( "Unfortunately, automatic installation of the Spotify resolver is not yet available on Linux.

" + "Please use \"Install from file\" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:

http://www.tomahawk-player.org/resolvers/spotify" ) ); + box.setStandardButtons( QMessageBox::Ok ); + box.exec(); +#endif + m_preventEnabling = true; + } } else if ( !m_spotifyResolver.data()->running() ) { @@ -263,11 +281,13 @@ SpotifyAccount::setManualResolverPath( const QString &resolverPath ) { Q_ASSERT( !resolverPath.isEmpty() ); - QVariantHash configuration; - configuration[ "resolverPath" ] = resolverPath; - setConfiguration( configuration ); + QVariantHash conf = configuration(); + conf[ "resolverPath" ] = resolverPath; + setConfiguration( conf ); sync(); + m_preventEnabling = false; + if ( !m_spotifyResolver.isNull() ) { // replace @@ -278,17 +298,17 @@ SpotifyAccount::setManualResolverPath( const QString &resolverPath ) else { hookupResolver(); - authenticate(); + AccountManager::instance()->enableAccount( this ); } } void -SpotifyAccount::hookupAfterDeletion( bool autostart ) +SpotifyAccount::hookupAfterDeletion( bool autoEnable ) { hookupResolver(); - if ( autostart ) - authenticate(); + if ( autoEnable ) + AccountManager::instance()->enableAccount( this ); } diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/accounts/spotify/SpotifyAccount.h index b8a9884cb..af05d0be6 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/accounts/spotify/SpotifyAccount.h @@ -90,6 +90,7 @@ public: virtual QWidget* aclWidget() { return 0; } virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); } virtual SipPlugin* sipPlugin() { return 0; } + virtual bool preventEnabling() const { return m_preventEnabling; } QString sendMessage( const QVariantMap& msg, QObject* receiver = 0, const QString& slot = QString() ); @@ -118,7 +119,7 @@ private slots: void playlistCreated( const QString& msgType, const QVariantMap& msg ); void init(); - void hookupAfterDeletion( bool autostart ); + void hookupAfterDeletion( bool autoEnable ); private: bool checkForResolver(); @@ -148,6 +149,8 @@ private: QHash< QString, playlist_ptr > m_waitingForCreateReply; + bool m_preventEnabling; + SmartPointerList< QAction > m_customActions; friend class ::SpotifyPlaylistUpdater; }; diff --git a/src/libtomahawk/accounts/Account.h b/src/libtomahawk/accounts/Account.h index 98afb3750..48aecefa6 100644 --- a/src/libtomahawk/accounts/Account.h +++ b/src/libtomahawk/accounts/Account.h @@ -102,6 +102,10 @@ public: virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() = 0; virtual SipPlugin* sipPlugin() = 0; + // Some accounts cannot be enabled if authentication fails. Return true after failing to authenticate + // if this is the case, and the account will not be enabled + virtual bool preventEnabling() const { return false; } + AccountTypes types() const; void setAccountServiceName( const QString &serviceName ) { QMutexLocker locker( &m_mutex ); m_accountServiceName = serviceName; } diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index 0c9e3d190..1bacf9c30 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -202,6 +202,9 @@ AccountManager::enableAccount( Account* account ) account->authenticate(); + if ( account->preventEnabling() ) + return; + account->setEnabled( true ); m_enabledAccounts << account; diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index 5ddaf3cf1..198340b8a 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -616,7 +616,8 @@ AccountModel::accountStateChanged( Account* account , Account::ConnectionState ) // For each type that this node could be, check the corresponding data if ( ( n->type == AccountModelNode::UniqueFactoryType && n->accounts.size() && n->accounts.first() == account ) || ( n->type == AccountModelNode::AtticaType && n->atticaAccount && n->atticaAccount == account ) || - ( n->type == AccountModelNode::ManualResolverType && n->resolverAccount && n->resolverAccount == account ) ) + ( n->type == AccountModelNode::ManualResolverType && n->resolverAccount && n->resolverAccount == account ) || + ( n->type == AccountModelNode::CustomAccountType && n->customAccount && n->customAccount == account ) ) { const QModelIndex idx = index( i, 0, QModelIndex() ); emit dataChanged( idx, idx ); From 4f947b893f54474106c0a67e964fa001f40c2967 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 18 May 2012 12:06:03 -0400 Subject: [PATCH 21/25] Fix some linux binary resolver stuff --- src/accounts/spotify/SpotifyAccount.cpp | 21 ++++++--------------- src/libtomahawk/TomahawkSettings.cpp | 14 +++++++++++--- src/libtomahawk/accounts/AccountModel.cpp | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index ba6033081..e2d0a86e0 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -100,6 +100,7 @@ SpotifyAccount::init() qRegisterMetaType< Tomahawk::Accounts::SpotifyPlaylistInfo* >( "Tomahawk::Accounts::SpotifyPlaylist*" ); setAccountFriendlyName( "Spotify" ); + setAccountServiceName( "spotify" ); AtticaManager::instance()->registerCustomAccount( s_resolverId, this ); @@ -108,7 +109,7 @@ SpotifyAccount::init() const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); - const QString path = configuration().value( "resolverPath" ).toString(); // Manual path override + const QString path = configuration().value( "path" ).toString(); // Manual path override if ( !checkForResolver() && state != AtticaManager::Uninstalled ) { // If the user manually deleted the resolver, mark it as uninstalled, so we re-fetch for the user @@ -127,7 +128,7 @@ SpotifyAccount::hookupResolver() // initialize the resolver itself. this is called if the account actually has an installed spotify resolver, // as it might not. // If there is a spotify resolver from attica installed, create the corresponding ExternalResolver* and hook up to it - QString path = configuration().value( "resolverPath" ).toString(); + QString path = configuration().value( "path" ).toString(); if ( path.isEmpty() ) { const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); @@ -209,18 +210,8 @@ SpotifyAccount::authenticate() else { #ifdef Q_OS_LINUX - // Can't install from attica yet on linux, so show a warning if the user tries to turn it on. - // TODO make a prettier display - QMessageBox box; - box.setWindowTitle( tr( "Manual Install Required" ) ); - box.setTextFormat( Qt::RichText ); - box.setIcon( QMessageBox::Information ); - box.setText( tr( "Unfortunately, automatic installation of the Spotify resolver is not yet available on Linux.

" - "Please use \"Install from file\" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:

http://www.tomahawk-player.org/resolvers/spotify" ) ); - box.setStandardButtons( QMessageBox::Ok ); - box.exec(); -#endif m_preventEnabling = true; +#endif } } else if ( !m_spotifyResolver.data()->running() ) @@ -282,7 +273,7 @@ SpotifyAccount::setManualResolverPath( const QString &resolverPath ) Q_ASSERT( !resolverPath.isEmpty() ); QVariantHash conf = configuration(); - conf[ "resolverPath" ] = resolverPath; + conf[ "path" ] = resolverPath; setConfiguration( conf ); sync(); @@ -291,7 +282,6 @@ SpotifyAccount::setManualResolverPath( const QString &resolverPath ) if ( !m_spotifyResolver.isNull() ) { // replace - //connect( m_spotifyResolver.data(), SIGNAL( destroyed( QObject* ) ), this, SLOT( hookupResolver() ) ); NewClosure( m_spotifyResolver.data(), SIGNAL( destroyed() ), this, SLOT( hookupAfterDeletion( bool ) ), true ); m_spotifyResolver.data()->deleteLater(); } @@ -421,6 +411,7 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg if ( msgType == "credentials" ) { QVariantHash creds = credentials(); + creds[ "username" ] = msg.value( "username" ); creds[ "password" ] = msg.value( "password" ); creds[ "highQuality" ] = msg.value( "highQuality" ); diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 002de3865..bf4619865 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -521,17 +521,25 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) // If the user doesn't have a spotify account, create one, since now it // is like the last.fm account and always exists QStringList allAccounts = value( "accounts/allaccounts" ).toStringList(); - bool found = false; + QString acct; foreach ( const QString& account, allAccounts ) { if ( account.startsWith( "spotifyaccount_" ) ) { - found = true; + acct = account; break; } } - if ( !found ) + if ( !acct.isEmpty() ) + { + beginGroup( "accounts/" + acct ); + QVariantHash conf = value( "configuration" ).toHash(); + foreach ( const QString& key, conf.keys() ) + qDebug() << key << conf[ key ].toString(); + endGroup(); + } + else { const QString accountKey = QString( "spotifyaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); beginGroup( "accounts/" + accountKey ); diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index 198340b8a..b462e7673 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -24,6 +24,10 @@ #include "AtticaManager.h" #include "ResolverAccount.h" +#ifndef ENABLE_HEADLESS +#include +#endif + #include using namespace Tomahawk; @@ -492,6 +496,21 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role else if( state == Qt::Unchecked ) AccountManager::instance()->disableAccount( acct ); +#if defined(Q_OS_LINUX) && !defined(ENABLE_HEADLESS) + if ( acct->preventEnabling() ) + { + // Can't install from attica yet on linux, so show a warning if the user tries to turn it on. + // TODO make a prettier display + QMessageBox box; + box.setWindowTitle( tr( "Manual Install Required" ) ); + box.setTextFormat( Qt::RichText ); + box.setIcon( QMessageBox::Information ); + box.setText( tr( "Unfortunately, automatic installation of the this resolver is not yet available on Linux.

" + "Please use \"Install from file\" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:

http://www.tomahawk-player.org/resolvers/%1" ).arg( acct->accountServiceName() ) ); + box.setStandardButtons( QMessageBox::Ok ); + box.exec(); + } +#endif emit dataChanged( index, index ); return true; From 647f68bd2289c245bd722a8af2db2cd34869f996 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 18 May 2012 12:46:11 -0400 Subject: [PATCH 22/25] Ensure we have lastfm and spotify accounts --- src/accounts/spotify/SpotifyAccount.cpp | 6 +-- src/libtomahawk/TomahawkSettings.cpp | 56 +++++++++++++++++++---- src/libtomahawk/TomahawkSettings.h | 2 + src/libtomahawk/accounts/AccountModel.cpp | 2 +- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index e2d0a86e0..3a740f551 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -90,6 +90,9 @@ SpotifyAccount::~SpotifyAccount() void SpotifyAccount::init() { + setAccountFriendlyName( "Spotify" ); + setAccountServiceName( "spotify" ); + if ( !AtticaManager::instance()->resolversLoaded() ) { // If we're still waiting to load, wait for the attica resolvers to come down the pipe @@ -99,9 +102,6 @@ SpotifyAccount::init() qRegisterMetaType< Tomahawk::Accounts::SpotifyPlaylistInfo* >( "Tomahawk::Accounts::SpotifyPlaylist*" ); - setAccountFriendlyName( "Spotify" ); - setAccountServiceName( "spotify" ); - AtticaManager::instance()->registerCustomAccount( s_resolverId, this ); connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( resolverInstalled( QString ) ) ); diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index bf4619865..cb563ec7a 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -127,6 +127,21 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) // insert upgrade code here as required setValue( "configversion", TOMAHAWK_SETTINGS_VERSION ); } + + // Ensure last.fm and spotify accounts always exist + QString spotifyAcct, lastfmAcct; + foreach ( const QString& acct, value( "accounts/allaccounts" ).toStringList() ) + { + if ( acct.startsWith( "lastfmaccount_" ) ) + lastfmAcct = acct; + else if ( acct.startsWith( "spotifyaccount_" ) ) + spotifyAcct = acct; + } + + if ( spotifyAcct.isEmpty() ) + createSpotifyAccount(); + if ( lastfmAcct.isEmpty() ) + createLastFmAccount(); } @@ -142,6 +157,15 @@ TomahawkSettings::doInitialSetup() // by default we add a local network resolver addAccount( "sipzeroconf_autocreated" ); + + createLastFmAccount(); + createSpotifyAccount(); +} + + +void +TomahawkSettings::createLastFmAccount() +{ // Add a last.fm account for scrobbling and infosystem const QString accountKey = QString( "lastfmaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); addAccount( accountKey ); @@ -151,6 +175,27 @@ TomahawkSettings::doInitialSetup() setValue( "autoconnect", true ); setValue( "types", QStringList() << "ResolverType" << "StatusPushType" ); endGroup(); + + QStringList allAccounts = value( "accounts/allaccounts" ).toStringList(); + allAccounts << accountKey; + setValue( "accounts/allaccounts", allAccounts ); +} + + +void +TomahawkSettings::createSpotifyAccount() +{ + const QString accountKey = QString( "spotifyaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); + beginGroup( "accounts/" + accountKey ); + setValue( "enabled", false ); + setValue( "types", QStringList() << "ResolverType" ); + setValue( "credentials", QVariantHash() ); + setValue( "configuration", QVariantHash() ); + endGroup(); + + QStringList allAccounts = value( "accounts/allaccounts" ).toStringList(); + allAccounts << accountKey; + setValue( "accounts/allaccounts", allAccounts ); } @@ -541,16 +586,7 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) } else { - const QString accountKey = QString( "spotifyaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); - beginGroup( "accounts/" + accountKey ); - setValue( "enabled", false ); - setValue( "types", QStringList() << "ResolverType" ); - setValue( "credentials", QVariantHash() ); - setValue( "configuration", QVariantHash() ); - endGroup(); - - allAccounts << accountKey; - setValue( "accounts/allaccounts", allAccounts ); + createSpotifyAccount(); } } } diff --git a/src/libtomahawk/TomahawkSettings.h b/src/libtomahawk/TomahawkSettings.h index 421a6480c..717da384d 100644 --- a/src/libtomahawk/TomahawkSettings.h +++ b/src/libtomahawk/TomahawkSettings.h @@ -214,6 +214,8 @@ private slots: private: void doInitialSetup(); + void createLastFmAccount(); + void createSpotifyAccount(); void doUpgrade( int oldVersion, int newVersion ); static TomahawkSettings* s_instance; diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index b462e7673..677e9c3c6 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -505,7 +505,7 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role box.setWindowTitle( tr( "Manual Install Required" ) ); box.setTextFormat( Qt::RichText ); box.setIcon( QMessageBox::Information ); - box.setText( tr( "Unfortunately, automatic installation of the this resolver is not yet available on Linux.

" + box.setText( tr( "Unfortunately, automatic installation of this resolver is not yet available on Linux.

" "Please use \"Install from file\" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:

http://www.tomahawk-player.org/resolvers/%1" ).arg( acct->accountServiceName() ) ); box.setStandardButtons( QMessageBox::Ok ); box.exec(); From ebd5a98d276bc3be66b3156d79429d70fc5872a9 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 18 May 2012 16:05:38 -0400 Subject: [PATCH 23/25] Windows-specific handling --- src/libtomahawk/AtticaManager.cpp | 6 ++---- src/libtomahawk/utils/TomahawkUtils.cpp | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 9ae1d27cf..3b6912a98 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -51,6 +51,8 @@ public: { Q_ASSERT( !m_resolverId.isEmpty() ); Q_ASSERT( !m_manager.isNull() ); + + setProperty( "resolverid", m_resolverId ); } virtual ~BinaryInstallerHelper() {} @@ -407,10 +409,6 @@ AtticaManager::binaryResolversList( BaseJob* j ) platform = "win"; #endif - // NOTE HACK - // At the moment we are going to assume that all binary resolvers also have an associated full-fledged Tomahawk Account - // like SpotifyAccount. - foreach ( const Content& c, binaryResolvers ) { if ( !c.attribute( "typeid" ).isEmpty() && c.attribute( "typeid" ) == platform ) diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index e2b220e10..3099cd170 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -762,7 +762,6 @@ QString extractScriptPayload( const QString& filename, const QString& resolverId ) { // uses QuaZip to extract the temporary zip file to the user's tomahawk data/resolvers directory - QDir resolverDir = appDataDir(); if ( !resolverDir.mkpath( QString( "atticaresolvers/%1" ).arg( resolverId ) ) ) { @@ -853,6 +852,8 @@ extractBinaryResolver( const QString& zipFilename, QObject* receiver ) return; #endif +#ifdef Q_OS_MAC + // Platform-specific handling of resolver payload now. We know it's good // Unzip the file. QFileInfo info( zipFilename ); QDir tmpDir = QDir::tempPath(); @@ -864,8 +865,6 @@ extractBinaryResolver( const QString& zipFilename, QObject* receiver ) tmpDir.cd( info.baseName() ); TomahawkUtils::unzipFileInFolder( info.absoluteFilePath(), tmpDir ); - // Platform-specific handling of resolver payload now. We know it's good and we unzipped it -#ifdef Q_OS_MAC // On OSX it just contains 1 file, the resolver executable itself. For now. We just copy it to // the Tomahawk.app/Contents/MacOS/ folder alongside the Tomahawk executable. const QString dest = QCoreApplication::applicationDirPath(); @@ -879,6 +878,20 @@ extractBinaryResolver( const QString& zipFilename, QObject* receiver ) copyWithAuthentication( src, dest, receiver ); #elif defined(Q_OS_WIN) + // We unzip directly to the target location, just like normal attica resolvers + Q_ASSERT( receiver ); + if ( !receiver ) + return; + + const QString resolverId = receiver->property( "resolverid" ).toString(); + + Q_ASSERT( !resolverId.isEmpty() ); + if ( resolverId.isEmpty() ) + return; + + const QString resolverDir = extractScriptPayload( zipFilename, resolverId ); + QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG( QString, path ) ); + #endif // No support for binary resolvers on linux! Shouldn't even have been allowed to see/install.. From dbbf93bfcd91ce4ae70b8caac019dc65e11a343b Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 18 May 2012 16:06:04 -0400 Subject: [PATCH 24/25] FIx macdeploy since plugins are .so now not .dylib --- admin/mac/macdeploy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/admin/mac/macdeploy.py b/admin/mac/macdeploy.py index 3a2ffffd8..6d7987982 100755 --- a/admin/mac/macdeploy.py +++ b/admin/mac/macdeploy.py @@ -199,10 +199,10 @@ QT_PLUGINS = [ ] TOMAHAWK_PLUGINS = [ - 'libtomahawk_account_xmpp.dylib', - 'libtomahawk_account_google.dylib', - 'libtomahawk_account_twitter.dylib', - 'libtomahawk_account_zeroconf.dylib', + 'libtomahawk_account_xmpp.so', + 'libtomahawk_account_google.so', + 'libtomahawk_account_twitter.so', + 'libtomahawk_account_zeroconf.so', ] QT_PLUGINS_SEARCH_PATH=[ From 3cb1778502381b0fdbc32e73ee142e2705bb025e Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 18 May 2012 16:34:26 -0400 Subject: [PATCH 25/25] Use real bakery again --- src/libtomahawk/AtticaManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 3b6912a98..4dc80d2fc 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -109,8 +109,8 @@ AtticaManager::AtticaManager( QObject* parent ) connect( &m_manager, SIGNAL( providerAdded( Attica::Provider ) ), this, SLOT( providerAdded( Attica::Provider ) ) ); // resolvers -// m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); - m_manager.addProviderFile( QUrl( "http://localhost/resolvers/providers.xml" ) ); + m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); +// m_manager.addProviderFile( QUrl( "http://lycophron/resolvers/providers.xml" ) ); qRegisterMetaType< Attica::Content >( "Attica::Content" ); }