/src/libtomahawk/AtticaManager.cpp
C++ | 798 lines | 587 code | 162 blank | 49 comment | 93 complexity | e311f19e06400eea1ad5683c66cc50e9 MD5 | raw file
1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === 2 * 3 * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> 4 * Copyright 2015, Christian Muehlhaeuser <muesli@tomahawk-player.org> 5 * 6 * Tomahawk is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * Tomahawk is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "AtticaManager.h" 21 22#include "utils/TomahawkUtils.h" 23#include "TomahawkSettings.h" 24#include "Pipeline.h" 25#include "Source.h" 26#include "config.h" 27 28#include "utils/Logger.h" 29#include "accounts/ResolverAccount.h" 30#include "accounts/AccountManager.h" 31#include "utils/BinaryInstallerHelper.h" 32#include "utils/Closure.h" 33#include "utils/NetworkAccessManager.h" 34 35#include <attica/downloaditem.h> 36 37#include <QCoreApplication> 38#include <QNetworkReply> 39#include <QTemporaryFile> 40#include <QDir> 41#include <QTimer> 42#include <QDomDocument> 43#include <QDomElement> 44#include <QDomNode> 45 46using namespace Attica; 47 48AtticaManager* AtticaManager::s_instance = 0; 49 50 51// Sort binary resolvers above script resolvers, and script resolvers by download count 52bool 53resolverSort( const Attica::Content& first, const Attica::Content& second ) 54{ 55 if ( !first.attribute( "typeid" ).isEmpty() && second.attribute( "typeid" ).isEmpty() ) 56 return true; 57 58 return first.downloads() > second.downloads(); 59} 60 61 62AtticaManager::AtticaManager( QObject* parent ) 63 : QObject( parent ) 64 , m_manager( Attica::ProviderManager::ProviderFlags( Attica::ProviderManager::DisablePlugins ) ) 65 , m_resolverJobsLoaded( 0 ) 66{ 67 connect( &m_manager, SIGNAL( providerAdded( Attica::Provider ) ), this, SLOT( providerAdded( Attica::Provider ) ) ); 68 69 // resolvers 70// m_manager.addProviderFile( QUrl( "http://v09.bakery.tomahawk-player.org/resolvers/providers.xml" ) ); 71 72 const QString url = QString( "%1/resolvers/providers.xml?version=%2" ).arg( hostname() ).arg( TomahawkUtils::appFriendlyVersion() ); 73 QNetworkReply* reply = Tomahawk::Utils::nam()->get( QNetworkRequest( QUrl( url ) ) ); 74 NewClosure( reply, SIGNAL( finished() ), this, SLOT( providerFetched( QNetworkReply* ) ), reply ); 75 connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( providerError( QNetworkReply::NetworkError ) ) ); 76 77// m_manager.addProviderFile( QUrl( "http://lycophron/resolvers/providers.xml" ) ); 78 79 qRegisterMetaType< Attica::Content >( "Attica::Content" ); 80} 81 82 83AtticaManager::~AtticaManager() 84{ 85 savePixmapsToCache(); 86 87 foreach( const QString& id, m_resolverStates.keys() ) 88 { 89 if ( !m_resolverStates[ id ].pixmap ) 90 continue; 91 92 delete m_resolverStates[ id ].pixmap; 93 } 94} 95 96 97void 98AtticaManager::fetchMissingIcons() 99{ 100 foreach ( Content resolver, m_resolvers ) 101 { 102 if ( !m_resolverStates.contains( resolver.id() ) ) 103 m_resolverStates.insert( resolver.id(), Resolver() ); 104 105 if ( !m_resolverStates.value( resolver.id() ).pixmap && !resolver.icons().isEmpty() && !resolver.icons().first().url().isEmpty() ) 106 { 107 QNetworkReply* fetch = Tomahawk::Utils::nam()->get( QNetworkRequest( resolver.icons().first().url() ) ); 108 fetch->setProperty( "resolverId", resolver.id() ); 109 110 connect( fetch, SIGNAL( finished() ), this, SLOT( resolverIconFetched() ) ); 111 } 112 } 113} 114 115 116QString 117AtticaManager::hostname() const 118{ 119 return "http://v09.bakery.tomahawk-player.org"; 120} 121 122 123void 124AtticaManager::loadPixmapsFromCache() 125{ 126 QDir cacheDir = TomahawkUtils::appDataDir(); 127 if ( !cacheDir.cd( "atticacache" ) ) // doesn't exist, no cache 128 return; 129 130 qDebug() << "Loading resolvers from cache dir:" << cacheDir.absolutePath(); 131 qDebug() << "Currently we know about these resolvers:" << m_resolverStates.keys(); 132 foreach ( const QString& file, cacheDir.entryList( QStringList() << "*.png", QDir::Files | QDir::NoSymLinks ) ) 133 { 134 // load all the pixmaps 135 QFileInfo info( file ); 136 if ( !m_resolverStates.contains( info.baseName() ) ) 137 { 138 tLog() << "Found resolver icon cached for resolver we no longer see in synchrotron repo:" << info.baseName(); 139 continue; 140 } 141 142 QPixmap* icon = new QPixmap( cacheDir.absoluteFilePath( file ) ); 143 if ( !icon->isNull() ) 144 { 145 m_resolverStates[ info.baseName() ].pixmap = icon; 146 } 147 } 148} 149 150 151void 152AtticaManager::savePixmapsToCache() 153{ 154 QDir cacheDir = TomahawkUtils::appDataDir(); 155 if ( !cacheDir.cd( "atticacache" ) ) // doesn't exist, create 156 { 157 cacheDir.mkdir( "atticacache" ); 158 cacheDir.cd( "atticache" ); 159 } 160 161 foreach( const QString& id, m_resolverStates.keys() ) 162 { 163 if ( !m_resolverStates[ id ].pixmap || !m_resolverStates[ id ].pixmapDirty ) 164 continue; 165 166 const QString filename = cacheDir.absoluteFilePath( QString( "%1.png" ).arg( id ) ); 167 QFile f( filename ); 168 if ( !f.open( QIODevice::WriteOnly ) ) 169 { 170 tLog() << "Failed to open cache file for writing:" << filename; 171 } 172 else 173 { 174 if ( !m_resolverStates[ id ].pixmap->save( &f ) ) 175 { 176 tLog() << "Failed to save pixmap into opened file for writing:" << filename; 177 } 178 } 179 } 180} 181 182 183QPixmap 184AtticaManager::iconForResolver( const Content& resolver ) 185{ 186 if ( !m_resolverStates[ resolver.id() ].pixmap ) 187 return QPixmap(); 188 189 return *m_resolverStates.value( resolver.id() ).pixmap; 190} 191 192 193Content::List 194AtticaManager::resolvers() const 195{ 196 return m_resolvers; 197} 198 199 200Content 201AtticaManager::resolverForId( const QString& id ) const 202{ 203 foreach ( const Attica::Content& c, m_resolvers ) 204 { 205 if ( c.id() == id ) 206 return c; 207 } 208 209 return Content(); 210} 211 212 213 214AtticaManager::ResolverState 215AtticaManager::resolverState ( const Content& resolver ) const 216{ 217 if ( !m_resolverStates.contains( resolver.id() ) ) 218 { 219 return AtticaManager::Uninstalled; 220 } 221 222 return m_resolverStates[ resolver.id() ].state; 223} 224 225 226bool 227AtticaManager::resolversLoaded() const 228{ 229 return !m_resolvers.isEmpty(); 230} 231 232 233QString 234AtticaManager::pathFromId( const QString& resolverId ) const 235{ 236 if ( !m_resolverStates.contains( resolverId ) ) 237 return QString(); 238 239 return m_resolverStates.value( resolverId ).scriptPath; 240} 241 242 243void 244AtticaManager::uploadRating( const Content& c ) 245{ 246 m_resolverStates[ c.id() ].userRating = c.rating(); 247 248 for ( int i = 0; i < m_resolvers.count(); i++ ) 249 { 250 if ( m_resolvers[ i ].id() == c.id() ) 251 { 252 Attica::Content atticaContent = m_resolvers[ i ]; 253 atticaContent.setRating( c.rating() ); 254 m_resolvers[ i ] = atticaContent; 255 break; 256 } 257 } 258 259 TomahawkSettings::instance()->setAtticaResolverStates( m_resolverStates ); 260 261 PostJob* job = m_resolverProvider.voteForContent( c.id(), (uint)c.rating() ); 262 connect( job, SIGNAL( finished( Attica::BaseJob* ) ), job, SLOT( deleteLater() ) ); 263 264 job->start(); 265 266 emit resolverStateChanged( c.id() ); 267} 268 269 270bool 271AtticaManager::userHasRated( const Content& c ) const 272{ 273 return m_resolverStates[ c.id() ].userRating != -1; 274} 275 276 277bool 278AtticaManager::hasCustomAccountForAttica( const QString &id ) const 279{ 280 return m_customAccounts.keys().contains( id ); 281} 282 283 284Tomahawk::Accounts::Account* 285AtticaManager::customAccountForAttica( const QString &id ) const 286{ 287 return m_customAccounts.value( id ); 288} 289 290 291void 292AtticaManager::registerCustomAccount( const QString &atticaId, Tomahawk::Accounts::Account *account ) 293{ 294 m_customAccounts.insert( atticaId, account ); 295} 296 297 298AtticaManager::Resolver 299AtticaManager::resolverData(const QString &atticaId) const 300{ 301 return m_resolverStates.value( atticaId ); 302} 303 304 305void 306AtticaManager::providerError( QNetworkReply::NetworkError err ) 307{ 308 Q_UNUSED( err ); 309 310 // So those who care know 311 emit resolversLoaded( Content::List() ); 312} 313 314 315void 316AtticaManager::providerFetched( QNetworkReply* reply ) 317{ 318 Q_ASSERT( reply ); 319 if ( !reply ) 320 return; 321 322 m_manager.addProviderFromXml( reply->readAll() ); 323} 324 325 326void 327AtticaManager::providerAdded( const Provider& provider ) 328{ 329 if ( provider.name() == "Tomahawk Resolvers" ) 330 { 331 m_resolverProvider = provider; 332 m_resolvers.clear(); 333 334 m_resolverStates = TomahawkSettings::instance()->atticaResolverStates(); 335 336 ListJob<Category>* job = m_resolverProvider.requestCategories(); 337 connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( categoriesReturned( Attica::BaseJob* ) ) ); 338 job->start(); 339 } 340} 341 342 343void 344AtticaManager::categoriesReturned( BaseJob* j ) 345{ 346 ListJob< Category >* job = static_cast< ListJob< Category >* >( j ); 347 348 Category::List categories = job->itemList(); 349 foreach ( const Category& category, categories ) 350 { 351 ListJob< Content >* job = m_resolverProvider.searchContents( Category::List() << category, QString(), Provider::Downloads, 0, 50 ); 352 353 if ( category.name() == "Resolver" ) 354 connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolversList( Attica::BaseJob* ) ) ); 355 else if ( category.name() == "BinaryResolver" ) 356 connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( binaryResolversList( Attica::BaseJob* ) ) ); 357 358 job->start(); 359 } 360} 361 362 363void 364AtticaManager::resolversList( BaseJob* j ) 365{ 366 ListJob< Content >* job = static_cast< ListJob< Content >* >( j ); 367 368 m_resolvers.append( job->itemList() ); 369 370 // Sanity check. if any resolvers are installed that don't exist on the hd, remove them. 371 foreach ( const QString& rId, m_resolverStates.keys() ) 372 { 373 if ( m_resolverStates[ rId ].state == Installed || 374 m_resolverStates[ rId ].state == NeedsUpgrade ) 375 { 376 if ( m_resolverStates[ rId ].binary ) 377 continue; 378 379 // Guess location on disk 380 QDir dir( QString( "%1/atticaresolvers/%2" ).arg( TomahawkUtils::appDataDir().absolutePath() ).arg( rId ) ); 381 if ( !dir.exists() ) 382 { 383 // Uh oh 384 qWarning() << "Found attica resolver marked as installed that didn't exist on disk! Setting to uninstalled: " << rId << dir.absolutePath(); 385 m_resolverStates[ rId ].state = Uninstalled; 386 TomahawkSettings::instance()->setAtticaResolverState( rId, Uninstalled ); 387 } 388 } 389 } 390 391 // load icon cache from disk, and fetch any we are missing 392 loadPixmapsFromCache(); 393 394 fetchMissingIcons(); 395 396 397 if ( ++m_resolverJobsLoaded == 2 ) 398 { 399 qSort( m_resolvers.begin(), m_resolvers.end(), resolverSort ); 400 syncServerData(); 401 emit resolversLoaded( m_resolvers ); 402 } 403} 404 405 406void 407AtticaManager::binaryResolversList( BaseJob* j ) 408{ 409 ListJob< Content >* job = static_cast< ListJob< Content >* >( j ); 410 411 Content::List binaryResolvers = job->itemList(); 412 413 QString platform; 414#if defined(Q_OS_MAC) 415 platform = "osx"; 416#elif defined(Q_OS_WIN) 417 platform = "win"; 418#elif defined(Q_OS_LINUX) && defined(__GNUC__) && defined(__x86_64__) 419 platform = "linux-x64"; 420#elif defined(Q_OS_LINUX) // Horrible assumption here... 421 platform = "linux-x86"; 422#endif 423 424 // Override if no binary resolvers were requested 425#ifndef WITH_BINARY_ATTICA 426 platform = QString(); 427#endif 428 429 foreach ( const Content& c, binaryResolvers ) 430 { 431 if ( !c.attribute( "typeid" ).isEmpty() && c.attribute( "typeid" ) == platform ) 432 { 433 // We have a binary resolver for this platform 434 qDebug() << "WE GOT A BINARY RESOLVER:" << c.id() << c.name() << c.attribute( "signature" ); 435 m_resolvers.append( c ); 436 if ( !m_resolverStates.contains( c.id() ) ) 437 { 438 Resolver r; 439 r.binary = true; 440 m_resolverStates.insert( c.id(), r ); 441 } 442 else if ( m_resolverStates[ c.id() ].binary != true ) 443 { // HACK workaround... why is this not set in the first place sometimes? Migration issue? 444 m_resolverStates[ c.id() ].binary = true; 445 } 446 447 448 } 449 } 450 451 if ( ++m_resolverJobsLoaded == 2 ) 452 { 453 qSort( m_resolvers.begin(), m_resolvers.end(), resolverSort ); 454 syncServerData(); 455 emit resolversLoaded( m_resolvers ); 456 } 457} 458 459 460void 461AtticaManager::resolverIconFetched() 462{ 463 QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); 464 Q_ASSERT( reply ); 465 reply->deleteLater(); 466 467 const QString resolverId = reply->property( "resolverId" ).toString(); 468 469 if ( reply->error() != QNetworkReply::NoError ) 470 { 471 tLog() << "Failed to fetch resolver icon image:" << reply->errorString(); 472 return; 473 } 474 475 QByteArray data = reply->readAll(); 476 QPixmap* icon = new QPixmap; 477 icon->loadFromData( data ); 478 m_resolverStates[ resolverId ].pixmap = icon; 479 m_resolverStates[ resolverId ].pixmapDirty = true; 480 481 emit resolverIconUpdated( resolverId ); 482} 483 484 485void 486AtticaManager::syncServerData() 487{ 488 // look for any newer. m_resolvers has list from server, and m_resolverStates will contain any locally installed ones 489 // also update ratings 490 tLog() << "Syncing server data!"; 491 foreach ( const QString& id, m_resolverStates.keys() ) 492 { 493 Resolver r = m_resolverStates[ id ]; 494 for ( int i = 0; i < m_resolvers.size(); i++ ) 495 { 496 Attica::Content upstream = m_resolvers[ i ]; 497 // same resolver 498 if ( id != upstream.id() ) 499 continue; 500 501 // Update our rating with the server's idea of rating if we haven't rated it 502 if ( m_resolverStates[ id ].userRating != -1 ) 503 { 504 upstream.setRating( m_resolverStates[ id ].userRating ); 505 m_resolvers[ i ] = upstream; 506 } 507 508 // DO we need to upgrade? 509 tDebug( LOGVERBOSE ) << "Upgrade check for" << upstream.id() << "local is" << r.version << "upstream is" << upstream.version() << "and state is: " << r.state; 510 if ( ( r.state == Installed || r.state == NeedsUpgrade ) && 511 !upstream.version().isEmpty() ) 512 { 513 if ( TomahawkUtils::newerVersion( r.version, upstream.version() ) ) 514 { 515 tLog() << "Doing upgrade of: " << id; 516 m_resolverStates[ id ].state = NeedsUpgrade; 517 QMetaObject::invokeMethod( this, "upgradeResolver", Qt::QueuedConnection, Q_ARG( Attica::Content, upstream ) ); 518 } 519 } 520 } 521 } 522} 523 524 525void 526AtticaManager::installResolver( const Content& resolver, bool autoCreate ) 527{ 528 doInstallResolver( resolver, autoCreate, 0 ); 529} 530 531 532void 533AtticaManager::installResolverWithHandler( const Content& resolver, Tomahawk::Accounts::AtticaResolverAccount* handler ) 534{ 535 doInstallResolver( resolver, false, handler ); 536} 537 538 539void AtticaManager::doInstallResolver( const Content& resolver, bool autoCreate, Tomahawk::Accounts::AtticaResolverAccount* handler ) 540{ 541 Q_ASSERT( !resolver.id().isNull() ); 542 543 emit startedInstalling( resolver.id() ); 544 545 if ( m_resolverStates[ resolver.id() ].state != Upgrading ) 546 m_resolverStates[ resolver.id() ].state = Installing; 547 548 m_resolverStates[ resolver.id() ].scriptPath = resolver.attribute( "mainscript" ); 549 m_resolverStates[ resolver.id() ].version = resolver.version(); 550 emit resolverStateChanged( resolver.id() ); 551 552// ItemJob< DownloadItem >* job = m_resolverProvider.downloadLink( resolver.id() ); 553 QUrl url( QString( "%1/resolvers/v1/content/download/%2/1" ).arg( hostname() ).arg( resolver.id() ) ); 554 555 TomahawkUtils::urlAddQueryItem( url, "tomahawkversion", TomahawkUtils::appFriendlyVersion() ); 556 QNetworkReply* r = Tomahawk::Utils::nam()->get( QNetworkRequest( url ) ); 557 NewClosure( r, SIGNAL( finished() ), this, SLOT( resolverDownloadFinished( QNetworkReply* ) ), r ); 558 r->setProperty( "resolverId", resolver.id() ); 559 r->setProperty( "createAccount", autoCreate ); 560 r->setProperty( "handler", QVariant::fromValue< QObject* >( handler ) ); 561 r->setProperty( "binarySignature", resolver.attribute("signature")); 562} 563 564 565void 566AtticaManager::upgradeResolver( const Content& resolver ) 567{ 568 tLog() << "UPGRADING:" << resolver.id() << m_resolverStates[ resolver.id() ].state; 569 Q_ASSERT( m_resolverStates.contains( resolver.id() ) ); 570 Q_ASSERT( m_resolverStates[ resolver.id() ].state == NeedsUpgrade ); 571 572 if ( !m_resolverStates.contains( resolver.id() ) || m_resolverStates[ resolver.id() ].state != NeedsUpgrade ) 573 return; 574 575 m_resolverStates[ resolver.id() ].state = Upgrading; 576 emit resolverStateChanged( resolver.id() ); 577 578 uninstallResolver( resolver ); 579 installResolver( resolver, false ); 580} 581 582 583void 584AtticaManager::resolverDownloadFinished ( QNetworkReply *j ) 585{ 586 Q_ASSERT( j ); 587 if ( !j ) 588 return; 589 590 if ( j->error() == QNetworkReply::NoError ) 591 { 592 QDomDocument doc; 593 doc.setContent( j ); 594 595 const QDomNodeList nodes = doc.documentElement().elementsByTagName( "downloadlink" ); 596 if ( nodes.isEmpty() ) 597 { 598 tLog() << "Found no download link for resolver:" << doc.toString(); 599 return; 600 } 601 602 QUrl url( nodes.item( 0 ).toElement().text() ); 603 // download the resolver itself :) 604 tDebug() << "Downloading resolver from url:" << url.toString(); 605 606 const QDomNodeList signatures = doc.documentElement().elementsByTagName( "signature" ); 607 608 // Use the original signature provided 609 QString signature = j->property( "binarySignature" ).toString(); 610 if ( signatures.size() > 0 ) 611 { 612 // THis download has an overriding signature. Take that one instead 613 const QString sig = signatures.item( 0 ).toElement().text(); 614 tLog() << "Found overridden signature in binary download:" << sig; 615 signature = sig; 616 } 617 QNetworkReply* reply = Tomahawk::Utils::nam()->get( QNetworkRequest( url ) ); 618 connect( reply, SIGNAL( finished() ), this, SLOT( payloadFetched() ) ); 619 reply->setProperty( "resolverId", j->property( "resolverId" ) ); 620 reply->setProperty( "createAccount", j->property( "createAccount" ) ); 621 reply->setProperty( "handler", j->property( "handler" ) ); 622 reply->setProperty( "binarySignature", signature ); 623 } 624 else 625 { 626 tLog() << "Failed to do resolver download job!" << j->errorString() << j->error(); 627 } 628} 629 630 631void 632AtticaManager::payloadFetched() 633{ 634 QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); 635 Q_ASSERT( reply ); 636 reply->deleteLater(); 637 638 bool installedSuccessfully = false; 639 const QString resolverId = reply->property( "resolverId" ).toString(); 640 641 // we got a zip file, save it to a temporary file, then unzip it to our destination data dir 642 if ( reply->error() == QNetworkReply::NoError ) 643 { 644 QTemporaryFile* f = new QTemporaryFile( QDir::tempPath() + QDir::separator() + "tomahawkattica_XXXXXX.zip" ); 645 if ( !f->open() ) 646 { 647 tLog() << "Failed to write zip file to temp file:" << f->fileName(); 648 return; 649 } 650 f->write( reply->readAll() ); 651 f->close(); 652 653 if ( m_resolverStates[ resolverId ].binary ) 654 { 655 // First ensure the signature matches. If we can't verify it, abort! 656 const QString signature = reply->property( "binarySignature" ).toString(); 657 // Must have a signature for binary resolvers... 658 Q_ASSERT( !signature.isEmpty() ); 659 if ( signature.isEmpty() ) 660 return; 661 if ( !TomahawkUtils::verifyFile( f->fileName(), signature ) ) 662 { 663 qWarning() << "FILE SIGNATURE FAILED FOR BINARY RESOLVER! WARNING! :" << f->fileName() << signature; 664 } 665 else 666 { 667 TomahawkUtils::extractBinaryResolver( f->fileName(), new BinaryInstallerHelper( f, resolverId, reply->property( "createAccount" ).toBool(), this ) ); 668 // Don't emit success or failed yet, helper will do that. 669 return; 670 } 671 } 672 else 673 { 674 QDir dir( TomahawkUtils::extractScriptPayload( f->fileName(), resolverId ) ); 675 QString resolverPath = dir.absoluteFilePath( m_resolverStates[ resolverId ].scriptPath ); 676 677 if ( !resolverPath.isEmpty() ) 678 { 679 // update with absolute, not relative, path 680 m_resolverStates[ resolverId ].scriptPath = resolverPath; 681 682 Tomahawk::Accounts::AtticaResolverAccount* handlerAccount = qobject_cast< Tomahawk::Accounts::AtticaResolverAccount* >( reply->property( "handler" ).value< QObject* >() ); 683 const bool createAccount = reply->property( "createAccount" ).toBool(); 684 if ( handlerAccount ) 685 { 686 handlerAccount->setPath( resolverPath ); 687 Tomahawk::Accounts::AccountManager::instance()->enableAccount( handlerAccount ); 688 } 689 else if ( createAccount ) 690 { 691 // Do the install / add to tomahawk 692 Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, "resolveraccount", true ); 693 Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver ); 694 TomahawkSettings::instance()->addAccount( resolver->accountId() ); 695 } 696 697 fetchMissingIcons(); 698 installedSuccessfully = true; 699 } 700 } 701 702 delete f; 703 } 704 else 705 { 706 tLog() << "Failed to download attica payload...:" << reply->errorString(); 707 } 708 709 710 if ( installedSuccessfully ) 711 { 712 tDebug( LOGVERBOSE ) << "Setting installed state to resolver:" << resolverId; 713 m_resolverStates[ resolverId ].state = Installed; 714 TomahawkSettings::instance()->setAtticaResolverStates( m_resolverStates ); 715 emit resolverInstalled( resolverId ); 716 emit resolverStateChanged( resolverId ); 717 } 718 else 719 { 720 emit resolverInstallationFailed( resolverId ); 721 } 722} 723 724 725void 726AtticaManager::uninstallResolver( const QString& pathToResolver ) 727{ 728 // when is this used? find and fix 729 Q_ASSERT(false); 730 731 // User manually removed a resolver not through attica dialog, simple remove 732 QRegExp r( ".*([^/]*)/contents/code/main.js" ); 733 r.indexIn( pathToResolver ); 734 const QString& atticaId = r.cap( 1 ); 735 tDebug() << "Got resolver ID to remove:" << atticaId; 736 if ( !atticaId.isEmpty() ) // this is an attica-installed resolver, mark as uninstalled 737 { 738 foreach ( const Content& resolver, m_resolvers ) 739 { 740 if ( resolver.id() == atticaId ) // this is the one 741 { 742 m_resolverStates[ atticaId ].state = Uninstalled; 743 delete m_resolverStates[ resolver.id() ].pixmap; 744 m_resolverStates[ atticaId ].pixmap = 0; 745 TomahawkSettings::instance()->setAtticaResolverState( atticaId, Uninstalled ); 746 747 doResolverRemove( atticaId ); 748 } 749 } 750 } 751} 752 753 754void 755AtticaManager::uninstallResolver( const Content& resolver ) 756{ 757 if ( m_resolverStates[ resolver.id() ].state != Upgrading ) 758 { 759 emit resolverUninstalled( resolver.id() ); 760 emit resolverStateChanged( resolver.id() ); 761 762 m_resolverStates[ resolver.id() ].state = Uninstalled; 763 TomahawkSettings::instance()->setAtticaResolverState( resolver.id(), Uninstalled ); 764 } 765 766 delete m_resolverStates[ resolver.id() ].pixmap; 767 m_resolverStates[ resolver.id() ].pixmap = 0; 768 769 doResolverRemove( resolver.id() ); 770} 771 772 773void 774AtticaManager::doResolverRemove( const QString& id ) const 775{ 776 // uninstalling is easy... just delete it! :) 777 QDir resolverDir = TomahawkUtils::appDataDir(); 778 if ( !resolverDir.cd( QString( "atticaresolvers/%1" ).arg( id ) ) ) 779 return; 780 781 if ( id.isEmpty() ) 782 return; 783 784 // sanity check 785 if ( !resolverDir.absolutePath().contains( "atticaresolvers" ) || 786 !resolverDir.absolutePath().contains( id ) ) 787 return; 788 789 TomahawkUtils::removeDirectory( resolverDir.absolutePath() ); 790 791 QDir cacheDir = TomahawkUtils::appDataDir(); 792 if ( !cacheDir.cd( "atticacache" ) ) 793 return; 794 795 const bool removed = cacheDir.remove( id + ".png" ); 796 tDebug() << "Tried to remove cached image, succeeded?" << removed << cacheDir.filePath( id ); 797} 798