/src/libtomahawk/AtticaManager.cpp

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