1
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
3
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
4
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
5
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
7
* Tomahawk is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* Tomahawk is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
21
#include "utils/TomahawkUtils.h"
23
#include "TomahawkVersion.h"
25
#include "TomahawkSettings.h"
28
#include "BinaryExtractWorker.h"
29
#include "SharedTimeLine.h"
31
#ifdef LIBLASTFM_FOUND
32
#include <lastfm/ws.h>
36
#include <quazipfile.h>
38
#include <QNetworkConfiguration>
39
#include <QNetworkAccessManager>
40
#include <QNetworkProxy>
42
#include <QCoreApplication>
46
#include <QCryptographicHash>
48
#include <QTranslator>
56
#include <Carbon/Carbon.h>
57
#include <sys/sysctl.h>
66
namespace TomahawkUtils
68
static quint64 s_infosystemRequestId = 0;
69
static QMutex s_infosystemRequestIdMutex;
70
static bool s_headless = false;
74
appSupportFolderPath()
76
// honestly, it is *always* this --mxcl
77
return QDir::home().filePath( "Library/Application Support" );
90
setHeadless( bool headless )
92
tLog() << Q_FUNC_INFO << "headless is" << (headless? "true" : "false");
93
s_headless = headless;
100
QStringList l = QString( TOMAHAWK_VERSION ).split( ".", QString::SkipEmptyParts );
101
while ( l.count() > 3 )
104
return l.join( "." );
114
if ( getenv( "HOME" ) )
116
return QDir( QString( "%1" ).arg( getenv( "HOME" ) ) );
120
tDebug() << "Error, $HOME not set.";
121
throw "$HOME not set";
122
return QDir( "/tmp" );
125
#elif defined(Q_WS_WIN)
127
return QDir( "c:\\" ); //TODO refer to Qt documentation to get code to do this
130
if ( getenv( "XDG_CONFIG_HOME" ) )
132
ret = QDir( QString( "%1/Tomahawk" ).arg( getenv( "XDG_CONFIG_HOME" ) ) );
134
else if ( getenv( "HOME" ) )
136
ret = QDir( QString( "%1/.config/Tomahawk" ).arg( getenv( "HOME" ) ) );
140
tDebug() << "Error, $HOME or $XDG_CONFIG_HOME not set.";
141
throw "Error, $HOME or $XDG_CONFIG_HOME not set.";
142
ret = QDir( "/tmp" );
147
ret.mkpath( ret.canonicalPath() );
161
if ( ( QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based ) == 0 )
163
// Use this for non-DOS-based Windowses
164
char acPath[MAX_PATH];
165
HRESULT h = SHGetFolderPathA( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE,
169
path = QString::fromLocal8Bit( acPath );
172
#elif defined(Q_WS_MAC)
173
path = appSupportFolderPath();
174
#elif defined(Q_WS_X11)
175
path = QDir::home().filePath( ".local/share" );
177
path = QCoreApplication::applicationDirPath();
180
path += "/" + QCoreApplication::organizationName();
194
return QDir( QDir::homePath() + "/Library/Logs" );
200
timeToString( int seconds )
202
int hrs = seconds / 60 / 60;
203
int mins = seconds / 60 % 60;
204
int secs = seconds % 60;
208
hrs = mins = secs = 0;
211
return QString( "%1%2:%3" ).arg( hrs > 0 ? hrs < 10 ? "0" + QString::number( hrs ) + ":" : QString::number( hrs ) + ":" : "" )
212
.arg( mins < 10 ? "0" + QString::number( mins ) : QString::number( mins ) )
213
.arg( secs < 10 ? "0" + QString::number( secs ) : QString::number( secs ) );
218
ageToString( const QDateTime& time, bool appendAgoString )
220
if ( time.toTime_t() == 0 )
223
QDateTime now = QDateTime::currentDateTime();
224
int mins = time.secsTo( now ) / 60;
225
int hours = mins / 60;
226
int days = time.daysTo( now );
227
int weeks = days / 7;
228
int months = days / 30.42;
229
int years = months / 12;
235
if ( appendAgoString )
236
return QObject::tr( "%n year(s) ago", "", years );
238
return QObject::tr( "%n year(s)", "", years );
243
if ( appendAgoString )
244
return QObject::tr( "%n month(s) ago", "", months );
246
return QObject::tr( "%n month(s)", "", months );
251
if ( appendAgoString )
252
return QObject::tr( "%n week(s) ago", "", weeks );
254
return QObject::tr( "%n week(s)", "", weeks );
259
if ( appendAgoString )
260
return QObject::tr( "%n day(s) ago", "", days );
261
else if ( hours >= 24 )
262
return QObject::tr( "%n day(s)", "", days );
267
if ( appendAgoString )
268
return QObject::tr( "%n hour(s) ago", "", hours );
270
return QObject::tr( "%n hour(s)", "", hours );
275
if ( appendAgoString )
276
return QObject::tr( "%1 minutes ago" ).arg( mins );
278
return QObject::tr( "%1 minutes" ).arg( mins );
282
return QObject::tr( "just now" );
287
filesizeToString( unsigned int size )
292
int kb = size / 1024;
297
return QString( "%1.%2 Mb" ).arg( mb ).arg( int( ( kb % 1024 ) / 102.4 ) );
301
return QString( "%1 Kb" ).arg( kb );
304
return QString::number( size );
309
extensionToMimetype( const QString& extension )
311
static QMap<QString, QString> s_ext2mime;
312
if ( s_ext2mime.isEmpty() )
314
s_ext2mime.insert( "mp3", "audio/mpeg" );
315
s_ext2mime.insert( "ogg", "application/ogg" );
316
s_ext2mime.insert( "oga", "application/ogg" );
317
s_ext2mime.insert( "mpc", "audio/x-musepack" );
318
s_ext2mime.insert( "wma", "audio/x-ms-wma" );
319
s_ext2mime.insert( "aac", "audio/mp4" );
320
s_ext2mime.insert( "m4a", "audio/mp4" );
321
s_ext2mime.insert( "mp4", "audio/mp4" );
322
s_ext2mime.insert( "flac", "audio/flac" );
323
s_ext2mime.insert( "aiff", "audio/aiff" );
324
s_ext2mime.insert( "aif", "audio/aiff" );
327
return s_ext2mime.value( extension, "unknown" );
332
msleep( unsigned int ms )
337
::usleep( ms * 1000 );
343
levenshtein( const QString& source, const QString& target )
346
const int n = source.length();
347
const int m = target.length();
354
// Good form to declare a TYPEDEF
355
typedef QVector< QVector<int> > Tmatrix;
357
matrix.resize( n + 1 );
359
// Size the vectors in the 2.nd dimension. Unfortunately C++ doesn't
360
// allow for allocation on declaration of 2.nd dimension of vec of vec
361
for ( int i = 0; i <= n; i++ )
365
matrix.insert( i, tmp );
369
for ( int i = 0; i <= n; i++ )
371
for ( int j = 0; j <= m; j++ )
375
for ( int i = 1; i <= n; i++ )
377
const QChar s_i = source[i - 1];
380
for ( int j = 1; j <= m; j++ )
382
const QChar t_j = target[j - 1];
392
const int above = matrix[i - 1][j];
393
const int left = matrix[i][j - 1];
394
const int diag = matrix[i - 1][j - 1];
396
int cell = ( ( ( left + 1 ) > ( diag + cost ) ) ? diag + cost : left + 1 );
397
if ( above + 1 < cell )
400
// Step 6A: Cover transposition, in addition to deletion,
401
// insertion and substitution. This step is taken from:
402
// Berghel, Hal ; Roach, David : "An Extension of Ukkonen's
403
// Enhanced Dynamic Programming ASM Algorithm"
404
// (http://www.acm.org/~hlb/publications/asm/asm.html)
405
if ( i > 2 && j > 2 )
407
int trans = matrix[i - 2][j - 2] + 1;
409
if ( source[ i - 2 ] != t_j ) trans++;
410
if ( s_i != target[ j - 2 ] ) trans++;
411
if ( cell > trans ) cell = trans;
422
static QMutex s_noProxyHostsMutex;
423
static QStringList s_noProxyHosts;
425
NetworkProxyFactory::NetworkProxyFactory( const NetworkProxyFactory& other )
427
m_proxy = QNetworkProxy( other.m_proxy );
431
QList< QNetworkProxy >
432
NetworkProxyFactory::queryProxy( const QNetworkProxyQuery& query )
434
//tDebug() << Q_FUNC_INFO << "query hostname is" << query.peerHostName() << ", proxy host is" << m_proxy.hostName();
436
QList< QNetworkProxy > proxies;
437
QString hostname = query.peerHostName();
438
s_noProxyHostsMutex.lock();
439
if ( !hostname.isEmpty() && s_noProxyHosts.contains( hostname ) )
440
proxies << QNetworkProxy::NoProxy << systemProxyForQuery( query );
441
else if ( m_proxy.hostName().isEmpty() || TomahawkSettings::instance()->proxyType() == QNetworkProxy::NoProxy )
442
proxies << systemProxyForQuery( query );
444
proxies << m_proxy << systemProxyForQuery( query );
445
s_noProxyHostsMutex.unlock();
451
NetworkProxyFactory::setNoProxyHosts( const QStringList& hosts )
454
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No-proxy hosts:" << hosts;
455
foreach ( const QString& host, hosts )
457
QString munge = host.simplified();
459
//TODO: wildcard support
462
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "New no-proxy hosts:" << newList;
464
s_noProxyHostsMutex.lock();
465
s_noProxyHosts = newList;
466
s_noProxyHostsMutex.unlock();
471
NetworkProxyFactory::setProxy( const QNetworkProxy& proxy )
473
m_proxyChanged = false;
474
if ( m_proxy != proxy )
475
m_proxyChanged = true;
478
QFlags< QNetworkProxy::Capability > proxyCaps;
479
proxyCaps |= QNetworkProxy::TunnelingCapability;
480
proxyCaps |= QNetworkProxy::ListeningCapability;
481
if ( TomahawkSettings::instance()->proxyDns() )
482
proxyCaps |= QNetworkProxy::HostNameLookupCapability;
484
m_proxy.setCapabilities( proxyCaps );
485
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Proxy using host" << proxy.hostName() << "and port" << proxy.port();
486
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "setting proxy to use proxy DNS?" << (TomahawkSettings::instance()->proxyDns() ? "true" : "false");
491
NetworkProxyFactory::operator=( const NetworkProxyFactory& rhs )
493
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
496
m_proxy = QNetworkProxy( rhs.m_proxy );
503
bool NetworkProxyFactory::operator==( const NetworkProxyFactory& other ) const
505
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
506
if ( m_proxy != other.m_proxy )
512
static QMap< QThread*, QNetworkAccessManager* > s_threadNamHash;
513
static QMap< QThread*, NetworkProxyFactory* > s_threadProxyFactoryHash;
514
static QMutex s_namAccessMutex;
517
proxyFactory( bool makeClone, bool noMutexLocker )
519
// Don't lock if being called from nam()
520
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
522
QMutexLocker locker( noMutexLocker ? &otherMutex : &s_namAccessMutex );
526
if ( s_threadProxyFactoryHash.contains( QThread::currentThread() ) )
527
return s_threadProxyFactoryHash[ QThread::currentThread() ];
530
// create a new proxy factory for this thread
531
TomahawkUtils::NetworkProxyFactory *newProxyFactory = new TomahawkUtils::NetworkProxyFactory();
532
if ( s_threadProxyFactoryHash.contains( QCoreApplication::instance()->thread() ) )
534
TomahawkUtils::NetworkProxyFactory *mainProxyFactory = s_threadProxyFactoryHash[ QCoreApplication::instance()->thread() ];
535
*newProxyFactory = *mainProxyFactory;
539
s_threadProxyFactoryHash[ QThread::currentThread() ] = newProxyFactory;
541
return newProxyFactory;
546
setProxyFactory( NetworkProxyFactory* factory, bool noMutexLocker )
548
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
550
// Don't lock if being called from setNam()
552
QMutexLocker locker( noMutexLocker ? &otherMutex : &s_namAccessMutex );
554
if ( !s_threadProxyFactoryHash.contains( QCoreApplication::instance()->thread() ) )
557
if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
559
foreach ( QThread* thread, s_threadProxyFactoryHash.keys() )
561
if ( thread != QThread::currentThread() )
563
TomahawkUtils::NetworkProxyFactory *currFactory = s_threadProxyFactoryHash[ thread ];
564
*currFactory = *factory;
567
QNetworkProxyFactory::setApplicationProxyFactory( factory );
570
*s_threadProxyFactoryHash[ QThread::currentThread() ] = *factory;
574
QNetworkAccessManager*
577
QMutexLocker locker( &s_namAccessMutex );
578
if ( s_threadNamHash.contains( QThread::currentThread() ) )
580
//tDebug() << Q_FUNC_INFO << "Found current thread in nam hash";
581
return s_threadNamHash[ QThread::currentThread() ];
584
if ( !s_threadNamHash.contains( QCoreApplication::instance()->thread() ) )
586
if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
588
setNam( new QNetworkAccessManager(), true );
589
return s_threadNamHash[ QThread::currentThread() ];
594
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Found gui thread in nam hash";
596
// Create a nam for this thread based on the main thread's settings but with its own proxyfactory
597
QNetworkAccessManager *mainNam = s_threadNamHash[ QCoreApplication::instance()->thread() ];
598
QNetworkAccessManager* newNam = new QNetworkAccessManager();
600
newNam->setConfiguration( QNetworkConfiguration( mainNam->configuration() ) );
601
newNam->setNetworkAccessible( mainNam->networkAccessible() );
602
newNam->setProxyFactory( proxyFactory( false, true ) );
604
s_threadNamHash[ QThread::currentThread() ] = newNam;
606
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "created new nam for thread" << QThread::currentThread();
607
//QNetworkProxy proxy = dynamic_cast< TomahawkUtils::NetworkProxyFactory* >( newNam->proxyFactory() )->proxy();
608
//tDebug() << Q_FUNC_INFO << "reply proxy properties:" << proxy.type() << proxy.hostName() << proxy.port();
615
setNam( QNetworkAccessManager* nam, bool noMutexLocker )
618
// Don't lock if being called from nam()()
620
QMutexLocker locker( noMutexLocker ? &otherMutex : &s_namAccessMutex );
621
if ( !s_threadNamHash.contains( QCoreApplication::instance()->thread() ) &&
622
QThread::currentThread() == QCoreApplication::instance()->thread() )
624
tDebug( LOGVERBOSE ) << "creating initial gui thread (" << QCoreApplication::instance()->thread() << ") nam";
625
// Should only get here on first initialization of the nam
626
TomahawkSettings *s = TomahawkSettings::instance();
627
TomahawkUtils::NetworkProxyFactory* proxyFactory = new TomahawkUtils::NetworkProxyFactory();
628
if ( s->proxyType() != QNetworkProxy::NoProxy && !s->proxyHost().isEmpty() )
630
tDebug( LOGVERBOSE ) << "Setting proxy to saved values";
631
QNetworkProxy proxy( s->proxyType(), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() );
632
proxyFactory->setProxy( proxy );
633
//FIXME: Jreen is broke without this
634
//QNetworkProxy::setApplicationProxy( proxy );
635
s_noProxyHostsMutex.lock();
636
if ( !s->proxyNoProxyHosts().isEmpty() && s_noProxyHosts.isEmpty() )
638
s_noProxyHostsMutex.unlock();
639
proxyFactory->setNoProxyHosts( s->proxyNoProxyHosts().split( ',', QString::SkipEmptyParts ) );
642
s_noProxyHostsMutex.unlock();
645
QNetworkProxyFactory::setApplicationProxyFactory( proxyFactory );
646
nam->setProxyFactory( proxyFactory );
647
s_threadNamHash[ QThread::currentThread() ] = nam;
648
s_threadProxyFactoryHash[ QThread::currentThread() ] = proxyFactory;
652
s_threadNamHash[ QThread::currentThread() ] = nam;
654
if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
655
setProxyFactory( dynamic_cast< TomahawkUtils::NetworkProxyFactory* >( nam->proxyFactory() ), true );
660
newerVersion( const QString& oldVersion, const QString& newVersion )
662
if ( oldVersion.isEmpty() || newVersion.isEmpty() )
665
QStringList oldVList = oldVersion.split( ".", QString::SkipEmptyParts );
666
QStringList newVList = newVersion.split( ".", QString::SkipEmptyParts );
669
foreach ( const QString& nvPart, newVList )
671
if ( i + 1 > oldVList.count() )
674
int nviPart = nvPart.toInt();
675
int oviPart = oldVList.at( i++ ).toInt();
677
if ( nviPart > oviPart )
680
if ( nviPart < oviPart )
688
QList< Tomahawk::query_ptr >
689
mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks, bool& changed )
692
QList< Tomahawk::query_ptr > tosave = newTracks;
695
foreach ( const Tomahawk::query_ptr& newquery, newTracks )
697
foreach ( const Tomahawk::query_ptr& oldq, orig )
699
if ( newquery->track() == oldq->track() &&
700
newquery->artist() == oldq->artist() &&
701
newquery->album() == oldq->album() )
704
if ( tosave.contains( newquery ) )
705
tosave.replace( tosave.indexOf( newquery ), oldq );
712
// No work to be done if all are the same
713
if ( orig.size() == newTracks.size() && sameCount == orig.size() )
721
// taken from util/fileutils.cpp in kdevplatform
723
removeDirectory( const QString& dir )
725
const QDir aDir( dir );
727
tLog() << "Deleting DIR:" << dir;
728
bool has_err = false;
731
foreach ( const QFileInfo& entry, aDir.entryInfoList( QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files | QDir::NoSymLinks ) )
733
QString path = entry.absoluteFilePath();
736
has_err = !removeDirectory( path ) || has_err;
738
else if ( !QFile::remove( path ) )
743
if ( !aDir.rmdir( aDir.absolutePath() ) )
754
infosystemRequestId()
756
QMutexLocker locker( &s_infosystemRequestIdMutex );
757
quint64 result = s_infosystemRequestId;
758
s_infosystemRequestId++;
764
md5( const QByteArray& data )
766
QByteArray const digest = QCryptographicHash::hash( data, QCryptographicHash::Md5 );
767
return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' );
774
volatile int* a = (int*)(NULL);
780
installTranslator( QObject* parent )
782
#if QT_VERSION >= 0x040800
783
QString locale = QLocale::system().uiLanguages().first().replace( "-", "_" );
785
QString locale = QLocale::system().name();
790
// Tomahawk translations
791
QTranslator* translator = new QTranslator( parent );
792
if ( translator->load( QString( ":/lang/tomahawk_" ) + locale ) )
794
qDebug() << "Translation: Tomahawk: Using system locale:" << locale;
798
qDebug() << "Translation: Tomahawk: Using default locale, system locale one not found:" << locale;
799
translator->load( QString( ":/lang/tomahawk_en" ) );
802
QCoreApplication::installTranslator( translator );
805
translator = new QTranslator( parent );
806
if ( translator->load( QString( ":/lang/qt_" ) + locale ) )
808
qDebug() << "Translation: Qt: Using system locale:" << locale;
812
qDebug() << "Translation: Qt: Using default locale, system locale one not found:" << locale;
815
QCoreApplication::installTranslator( translator );
820
verifyFile( const QString& filePath, const QString& signature )
822
QCA::Initializer init;
824
if ( !QCA::isSupported( "sha1" ) )
826
qWarning() << "SHA1 not supported by QCA, aborting.";
830
// The signature for the resolver.zip was created like so:
831
// openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[2]}" | openssl enc -base64
832
// which means we need to decode it with QCA's DSA public key signature verification tools
833
// The input data is:
834
// file -> SHA1 binary format -> DSS1/DSA signed -> base64 encoded.
836
// Step 1: Load the public key
837
// Public key is in :/data/misc/tomahawk_pubkey.pem
838
QFile f( ":/data/misc/tomahawk_pubkey.pem" );
839
if ( !f.open( QIODevice::ReadOnly ) )
841
qWarning() << "Unable to read public key from resources!";
845
const QString pubkeyData = QString::fromUtf8( f.readAll() );
846
QCA::ConvertResult conversionResult;
847
QCA::PublicKey publicKey = QCA::PublicKey::fromPEM( pubkeyData, &conversionResult );
848
if ( QCA::ConvertGood != conversionResult)
850
qWarning() << "Public key reading/loading failed! Tried to load public key:" << pubkeyData;
854
if ( !publicKey.canVerify() )
856
qWarning() << "Loaded Tomahawk public key but cannot use it to verify! What is up....";
860
// Step 2: Get the SHA1 of the file contents
861
QFile toVerify( filePath );
862
if ( !toVerify.exists() || !toVerify.open( QIODevice::ReadOnly ) )
864
qWarning() << "Failed to open file we are trying to verify!" << filePath;
868
const QByteArray fileHashData = QCA::Hash( "sha1" ).hash( toVerify.readAll() ).toByteArray();
871
// Step 3: Base64 decode the signature
872
QCA::Base64 decoder( QCA::Decode );
873
const QByteArray decodedSignature = decoder.decode( QCA::SecureArray( signature.trimmed().toUtf8() ) ).toByteArray();
874
if ( decodedSignature.isEmpty() )
876
qWarning() << "Got empty signature after we tried to decode it from Base64:" << signature.trimmed().toUtf8() << decodedSignature.toBase64();
880
// Step 4: Do the actual verifying!
881
const bool result = publicKey.verifyMessage( fileHashData, decodedSignature, QCA::EMSA1_SHA1, QCA::DERSequence );
884
qWarning() << "File" << filePath << "FAILED VERIFICATION against our input signature!";
888
tDebug( LOGVERBOSE ) << "Successfully verified signature of downloaded file:" << filePath;
895
extractScriptPayload( const QString& filename, const QString& resolverId )
897
// uses QuaZip to extract the temporary zip file to the user's tomahawk data/resolvers directory
898
QDir resolverDir = appDataDir();
899
if ( !resolverDir.mkpath( QString( "atticaresolvers/%1" ).arg( resolverId ) ) )
901
tLog() << "Failed to mkdir resolver save dir:" << TomahawkUtils::appDataDir().absoluteFilePath( QString( "atticaresolvers/%1" ).arg( resolverId ) );
904
resolverDir.cd( QString( "atticaresolvers/%1" ).arg( resolverId ) );
906
if ( !unzipFileInFolder( filename, resolverDir ) )
908
qWarning() << "Failed to unzip resolver. Ooops.";
912
return resolverDir.absolutePath();
917
unzipFileInFolder( const QString& zipFileName, const QDir& folder )
919
Q_ASSERT( !zipFileName.isEmpty() );
920
Q_ASSERT( folder.exists() );
922
QuaZip zipFile( zipFileName );
923
if ( !zipFile.open( QuaZip::mdUnzip ) )
925
qWarning() << "Failed to QuaZip open:" << zipFile.getZipError();
929
if ( !zipFile.goToFirstFile() )
931
tLog() << "Failed to go to first file in zip archive:" << zipFile.getZipError();
935
tDebug( LOGVERBOSE ) << "Unzipping files to:" << folder.absolutePath();
937
QuaZipFile fileInZip( &zipFile );
941
zipFile.getCurrentFileInfo( &info );
943
if ( !fileInZip.open( QIODevice::ReadOnly ) )
945
tLog() << "Failed to open file inside zip archive:" << info.name << zipFile.getZipName() << "with error:" << zipFile.getZipError();
949
QFile out( folder.absoluteFilePath( fileInZip.getActualFileName() ) );
951
// make dir if there is one needed
952
QStringList parts = fileInZip.getActualFileName().split( "/" );
953
if ( parts.size() > 1 )
955
QStringList dirs = parts.mid( 0, parts.size() - 1 );
956
QString dirPath = dirs.join( "/" ); // QDir translates / to \ internally if necessary
957
folder.mkpath( dirPath );
960
tDebug( LOGVERBOSE ) << "Writing to output file..." << out.fileName();
961
if ( !out.open( QIODevice::WriteOnly ) )
963
tLog() << "Failed to open zip extract file:" << out.errorString() << info.name;
969
out.write( fileInZip.readAll() );
973
} while ( zipFile.goToNextFile() );
980
extractBinaryResolver( const QString& zipFilename, QObject* receiver )
982
BinaryExtractWorker* worker = new BinaryExtractWorker( zipFilename, receiver );
983
worker->start( QThread::LowPriority );
988
whitelistedHttpResultHint( const QString& url )
990
// For now, just http/https
991
return url.startsWith( "http" );
997
#include "TomahawkUtils.moc"