~ubuntu-branches/ubuntu/trusty/tomahawk/trusty-proposed

« back to all changes in this revision

Viewing changes to src/libtomahawk/utils/TomahawkUtils.cpp

  • Committer: Package Import Robot
  • Author(s): Harald Sitter
  • Date: 2013-03-07 21:50:13 UTC
  • Revision ID: package-import@ubuntu.com-20130307215013-6gdjkdds7i9uenvs
Tags: upstream-0.6.0+dfsg
ImportĀ upstreamĀ versionĀ 0.6.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
 
2
 *
 
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>
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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/>.
 
19
 */
 
20
 
 
21
#include "utils/TomahawkUtils.h"
 
22
 
 
23
#include "TomahawkVersion.h"
 
24
#include "config.h"
 
25
#include "TomahawkSettings.h"
 
26
 
 
27
#include "Source.h"
 
28
#include "BinaryExtractWorker.h"
 
29
#include "SharedTimeLine.h"
 
30
 
 
31
#ifdef LIBLASTFM_FOUND
 
32
    #include <lastfm/ws.h>
 
33
#endif
 
34
 
 
35
#include <quazip.h>
 
36
#include <quazipfile.h>
 
37
 
 
38
#include <QNetworkConfiguration>
 
39
#include <QNetworkAccessManager>
 
40
#include <QNetworkProxy>
 
41
 
 
42
#include <QCoreApplication>
 
43
#include <QDateTime>
 
44
#include <QDir>
 
45
#include <QMutex>
 
46
#include <QCryptographicHash>
 
47
#include <QProcess>
 
48
#include <QTranslator>
 
49
 
 
50
#ifdef Q_OS_WIN
 
51
    #include <windows.h>
 
52
    #include <shlobj.h>
 
53
#endif
 
54
 
 
55
#ifdef Q_WS_MAC
 
56
    #include <Carbon/Carbon.h>
 
57
    #include <sys/sysctl.h>
 
58
#endif
 
59
 
 
60
#ifdef QCA2_FOUND
 
61
    #include <QtCrypto>
 
62
#endif
 
63
 
 
64
#include "Logger.h"
 
65
 
 
66
namespace TomahawkUtils
 
67
{
 
68
static quint64 s_infosystemRequestId = 0;
 
69
static QMutex s_infosystemRequestIdMutex;
 
70
static bool s_headless = false;
 
71
 
 
72
#ifdef Q_WS_MAC
 
73
QString
 
74
appSupportFolderPath()
 
75
{
 
76
    // honestly, it is *always* this --mxcl
 
77
    return QDir::home().filePath( "Library/Application Support" );
 
78
}
 
79
#endif // Q_WS_MAC
 
80
 
 
81
 
 
82
bool
 
83
headless()
 
84
{
 
85
    return s_headless;
 
86
}
 
87
 
 
88
 
 
89
void
 
90
setHeadless( bool headless )
 
91
{
 
92
    tLog() << Q_FUNC_INFO << "headless is" << (headless? "true" : "false");
 
93
    s_headless = headless;
 
94
}
 
95
 
 
96
 
 
97
QString
 
98
appFriendlyVersion()
 
99
{
 
100
    QStringList l = QString( TOMAHAWK_VERSION ).split( ".", QString::SkipEmptyParts );
 
101
    while ( l.count() > 3 )
 
102
        l.removeLast();
 
103
 
 
104
    return l.join( "." );
 
105
}
 
106
 
 
107
 
 
108
QDir
 
109
appConfigDir()
 
110
{
 
111
    QDir ret;
 
112
 
 
113
#ifdef Q_WS_MAC
 
114
    if ( getenv( "HOME" ) )
 
115
    {
 
116
        return QDir( QString( "%1" ).arg( getenv( "HOME" ) ) );
 
117
    }
 
118
    else
 
119
    {
 
120
        tDebug() << "Error, $HOME not set.";
 
121
        throw "$HOME not set";
 
122
        return QDir( "/tmp" );
 
123
    }
 
124
 
 
125
#elif defined(Q_WS_WIN)
 
126
    throw "TODO";
 
127
    return QDir( "c:\\" ); //TODO refer to Qt documentation to get code to do this
 
128
 
 
129
#else
 
130
    if ( getenv( "XDG_CONFIG_HOME" ) )
 
131
    {
 
132
        ret = QDir( QString( "%1/Tomahawk" ).arg( getenv( "XDG_CONFIG_HOME" ) ) );
 
133
    }
 
134
    else if ( getenv( "HOME" ) )
 
135
    {
 
136
        ret = QDir( QString( "%1/.config/Tomahawk" ).arg( getenv( "HOME" ) ) );
 
137
    }
 
138
    else
 
139
    {
 
140
        tDebug() << "Error, $HOME or $XDG_CONFIG_HOME not set.";
 
141
        throw "Error, $HOME or $XDG_CONFIG_HOME not set.";
 
142
        ret = QDir( "/tmp" );
 
143
    }
 
144
 
 
145
    if ( !ret.exists() )
 
146
    {
 
147
        ret.mkpath( ret.canonicalPath() );
 
148
    }
 
149
 
 
150
    return ret;
 
151
#endif
 
152
}
 
153
 
 
154
 
 
155
QDir
 
156
appDataDir()
 
157
{
 
158
    QString path;
 
159
 
 
160
    #ifdef Q_WS_WIN
 
161
        if ( ( QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based ) == 0 )
 
162
        {
 
163
            // Use this for non-DOS-based Windowses
 
164
            char acPath[MAX_PATH];
 
165
            HRESULT h = SHGetFolderPathA( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE,
 
166
                                          NULL, 0, acPath );
 
167
            if ( h == S_OK )
 
168
            {
 
169
                path = QString::fromLocal8Bit( acPath );
 
170
            }
 
171
        }
 
172
    #elif defined(Q_WS_MAC)
 
173
        path = appSupportFolderPath();
 
174
    #elif defined(Q_WS_X11)
 
175
        path = QDir::home().filePath( ".local/share" );
 
176
    #else
 
177
        path = QCoreApplication::applicationDirPath();
 
178
    #endif
 
179
 
 
180
    path += "/" + QCoreApplication::organizationName();
 
181
    QDir d( path );
 
182
    d.mkpath( path );
 
183
 
 
184
    return d;
 
185
}
 
186
 
 
187
 
 
188
QDir
 
189
appLogDir()
 
190
{
 
191
#ifndef Q_WS_MAC
 
192
    return appDataDir();
 
193
#else
 
194
    return QDir( QDir::homePath() + "/Library/Logs" );
 
195
#endif
 
196
}
 
197
 
 
198
 
 
199
QString
 
200
timeToString( int seconds )
 
201
{
 
202
    int hrs  = seconds / 60 / 60;
 
203
    int mins = seconds / 60 % 60;
 
204
    int secs = seconds % 60;
 
205
 
 
206
    if ( seconds < 0 )
 
207
    {
 
208
        hrs = mins = secs = 0;
 
209
    }
 
210
 
 
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 ) );
 
214
}
 
215
 
 
216
 
 
217
QString
 
218
ageToString( const QDateTime& time, bool appendAgoString )
 
219
{
 
220
    if ( time.toTime_t() == 0 )
 
221
        return QString();
 
222
 
 
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;
 
230
 
 
231
    if ( mins > 0 )
 
232
    {
 
233
        if ( years )
 
234
        {
 
235
            if ( appendAgoString )
 
236
                return QObject::tr( "%n year(s) ago", "", years );
 
237
            else
 
238
                return QObject::tr( "%n year(s)", "", years );
 
239
        }
 
240
 
 
241
        if ( months )
 
242
        {
 
243
            if ( appendAgoString )
 
244
                return QObject::tr( "%n month(s) ago", "", months );
 
245
            else
 
246
                return QObject::tr( "%n month(s)", "", months );
 
247
        }
 
248
 
 
249
        if ( weeks )
 
250
        {
 
251
            if ( appendAgoString )
 
252
                return QObject::tr( "%n week(s) ago", "", weeks );
 
253
            else
 
254
                return QObject::tr( "%n week(s)", "", weeks );
 
255
        }
 
256
 
 
257
        if ( days )
 
258
        {
 
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 );
 
263
        }
 
264
 
 
265
        if ( hours )
 
266
        {
 
267
            if ( appendAgoString )
 
268
                return QObject::tr( "%n hour(s) ago", "", hours );
 
269
            else
 
270
                return QObject::tr( "%n hour(s)", "", hours );
 
271
        }
 
272
 
 
273
        if ( mins > 1 )
 
274
        {
 
275
            if ( appendAgoString )
 
276
                return QObject::tr( "%1 minutes ago" ).arg( mins );
 
277
            else
 
278
                return QObject::tr( "%1 minutes" ).arg( mins );
 
279
        }
 
280
    }
 
281
 
 
282
    return QObject::tr( "just now" );
 
283
}
 
284
 
 
285
 
 
286
QString
 
287
filesizeToString( unsigned int size )
 
288
{
 
289
    if ( size == 0 )
 
290
        return QString();
 
291
 
 
292
    int kb = size / 1024;
 
293
    int mb = kb / 1024;
 
294
 
 
295
    if ( mb )
 
296
    {
 
297
        return QString( "%1.%2 Mb" ).arg( mb ).arg( int( ( kb % 1024 ) / 102.4 ) );
 
298
    }
 
299
    else if ( kb )
 
300
    {
 
301
        return QString( "%1 Kb" ).arg( kb );
 
302
    }
 
303
    else
 
304
        return QString::number( size );
 
305
}
 
306
 
 
307
 
 
308
QString
 
309
extensionToMimetype( const QString& extension )
 
310
{
 
311
    static QMap<QString, QString> s_ext2mime;
 
312
    if ( s_ext2mime.isEmpty() )
 
313
    {
 
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" );
 
325
    }
 
326
 
 
327
    return s_ext2mime.value( extension, "unknown" );
 
328
}
 
329
 
 
330
 
 
331
void
 
332
msleep( unsigned int ms )
 
333
{
 
334
  #ifdef WIN32
 
335
    Sleep( ms );
 
336
  #else
 
337
    ::usleep( ms * 1000 );
 
338
  #endif
 
339
}
 
340
 
 
341
 
 
342
int
 
343
levenshtein( const QString& source, const QString& target )
 
344
{
 
345
    // Step 1
 
346
    const int n = source.length();
 
347
    const int m = target.length();
 
348
 
 
349
    if ( n == 0 )
 
350
        return m;
 
351
    if ( m == 0 )
 
352
        return n;
 
353
 
 
354
    // Good form to declare a TYPEDEF
 
355
    typedef QVector< QVector<int> > Tmatrix;
 
356
    Tmatrix matrix;
 
357
    matrix.resize( n + 1 );
 
358
 
 
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++ )
 
362
    {
 
363
        QVector<int> tmp;
 
364
        tmp.resize( m + 1 );
 
365
        matrix.insert( i, tmp );
 
366
    }
 
367
 
 
368
    // Step 2
 
369
    for ( int i = 0; i <= n; i++ )
 
370
        matrix[i][0] = i;
 
371
    for ( int j = 0; j <= m; j++ )
 
372
        matrix[0][j] = j;
 
373
 
 
374
    // Step 3
 
375
    for ( int i = 1; i <= n; i++ )
 
376
    {
 
377
        const QChar s_i = source[i - 1];
 
378
 
 
379
        // Step 4
 
380
        for ( int j = 1; j <= m; j++ )
 
381
        {
 
382
            const QChar t_j = target[j - 1];
 
383
 
 
384
            // Step 5
 
385
            int cost;
 
386
            if ( s_i == t_j )
 
387
                cost = 0;
 
388
            else
 
389
                cost = 1;
 
390
 
 
391
            // Step 6
 
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];
 
395
 
 
396
            int cell = ( ( ( left + 1 ) > ( diag + cost ) ) ? diag + cost : left + 1 );
 
397
            if ( above + 1 < cell )
 
398
                cell = above + 1;
 
399
 
 
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 )
 
406
            {
 
407
                int trans = matrix[i - 2][j - 2] + 1;
 
408
 
 
409
                if ( source[ i - 2 ] != t_j ) trans++;
 
410
                if ( s_i != target[ j - 2 ] ) trans++;
 
411
                if ( cell > trans ) cell = trans;
 
412
            }
 
413
            matrix[i][j] = cell;
 
414
        }
 
415
    }
 
416
 
 
417
    // Step 7
 
418
    return matrix[n][m];
 
419
}
 
420
 
 
421
 
 
422
static QMutex s_noProxyHostsMutex;
 
423
static QStringList s_noProxyHosts;
 
424
 
 
425
NetworkProxyFactory::NetworkProxyFactory( const NetworkProxyFactory& other )
 
426
{
 
427
    m_proxy = QNetworkProxy( other.m_proxy );
 
428
}
 
429
 
 
430
 
 
431
QList< QNetworkProxy >
 
432
NetworkProxyFactory::queryProxy( const QNetworkProxyQuery& query )
 
433
{
 
434
    //tDebug() << Q_FUNC_INFO << "query hostname is" << query.peerHostName() << ", proxy host is" << m_proxy.hostName();
 
435
 
 
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 );
 
443
    else
 
444
        proxies << m_proxy << systemProxyForQuery( query );
 
445
    s_noProxyHostsMutex.unlock();
 
446
    return proxies;
 
447
}
 
448
 
 
449
 
 
450
void
 
451
NetworkProxyFactory::setNoProxyHosts( const QStringList& hosts )
 
452
{
 
453
    QStringList newList;
 
454
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No-proxy hosts:" << hosts;
 
455
    foreach ( const QString& host, hosts )
 
456
    {
 
457
        QString munge = host.simplified();
 
458
        newList << munge;
 
459
        //TODO: wildcard support
 
460
    }
 
461
 
 
462
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "New no-proxy hosts:" << newList;
 
463
 
 
464
    s_noProxyHostsMutex.lock();
 
465
    s_noProxyHosts = newList;
 
466
    s_noProxyHostsMutex.unlock();
 
467
}
 
468
 
 
469
 
 
470
void
 
471
NetworkProxyFactory::setProxy( const QNetworkProxy& proxy )
 
472
{
 
473
    m_proxyChanged = false;
 
474
    if ( m_proxy != proxy )
 
475
        m_proxyChanged = true;
 
476
 
 
477
    m_proxy = proxy;
 
478
    QFlags< QNetworkProxy::Capability > proxyCaps;
 
479
    proxyCaps |= QNetworkProxy::TunnelingCapability;
 
480
    proxyCaps |= QNetworkProxy::ListeningCapability;
 
481
    if ( TomahawkSettings::instance()->proxyDns() )
 
482
        proxyCaps |= QNetworkProxy::HostNameLookupCapability;
 
483
 
 
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");
 
487
}
 
488
 
 
489
 
 
490
NetworkProxyFactory&
 
491
NetworkProxyFactory::operator=( const NetworkProxyFactory& rhs )
 
492
{
 
493
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
 
494
    if ( this != &rhs )
 
495
    {
 
496
        m_proxy = QNetworkProxy( rhs.m_proxy );
 
497
    }
 
498
 
 
499
    return *this;
 
500
}
 
501
 
 
502
 
 
503
bool NetworkProxyFactory::operator==( const NetworkProxyFactory& other ) const
 
504
{
 
505
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
 
506
    if ( m_proxy != other.m_proxy )
 
507
        return false;
 
508
 
 
509
    return true;
 
510
}
 
511
 
 
512
static QMap< QThread*, QNetworkAccessManager* > s_threadNamHash;
 
513
static QMap< QThread*, NetworkProxyFactory* > s_threadProxyFactoryHash;
 
514
static QMutex s_namAccessMutex;
 
515
 
 
516
NetworkProxyFactory*
 
517
proxyFactory( bool makeClone, bool noMutexLocker )
 
518
{
 
519
    // Don't lock if being called from nam()
 
520
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
 
521
    QMutex otherMutex;
 
522
    QMutexLocker locker( noMutexLocker ? &otherMutex : &s_namAccessMutex );
 
523
 
 
524
    if ( !makeClone )
 
525
    {
 
526
        if ( s_threadProxyFactoryHash.contains( QThread::currentThread() ) )
 
527
            return s_threadProxyFactoryHash[ QThread::currentThread() ];
 
528
    }
 
529
 
 
530
    // create a new proxy factory for this thread
 
531
    TomahawkUtils::NetworkProxyFactory *newProxyFactory = new TomahawkUtils::NetworkProxyFactory();
 
532
    if ( s_threadProxyFactoryHash.contains( QCoreApplication::instance()->thread() ) )
 
533
    {
 
534
        TomahawkUtils::NetworkProxyFactory *mainProxyFactory = s_threadProxyFactoryHash[ QCoreApplication::instance()->thread() ];
 
535
        *newProxyFactory = *mainProxyFactory;
 
536
    }
 
537
 
 
538
    if ( !makeClone )
 
539
        s_threadProxyFactoryHash[ QThread::currentThread() ] = newProxyFactory;
 
540
 
 
541
    return newProxyFactory;
 
542
}
 
543
 
 
544
 
 
545
void
 
546
setProxyFactory( NetworkProxyFactory* factory, bool noMutexLocker )
 
547
{
 
548
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
 
549
    Q_ASSERT( factory );
 
550
    // Don't lock if being called from setNam()
 
551
    QMutex otherMutex;
 
552
    QMutexLocker locker( noMutexLocker ? &otherMutex : &s_namAccessMutex );
 
553
 
 
554
    if ( !s_threadProxyFactoryHash.contains( QCoreApplication::instance()->thread() ) )
 
555
        return;
 
556
 
 
557
    if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
 
558
    {
 
559
        foreach ( QThread* thread, s_threadProxyFactoryHash.keys() )
 
560
        {
 
561
            if ( thread != QThread::currentThread() )
 
562
            {
 
563
                TomahawkUtils::NetworkProxyFactory *currFactory = s_threadProxyFactoryHash[ thread ];
 
564
                *currFactory = *factory;
 
565
            }
 
566
        }
 
567
        QNetworkProxyFactory::setApplicationProxyFactory( factory );
 
568
    }
 
569
 
 
570
    *s_threadProxyFactoryHash[ QThread::currentThread() ] = *factory;
 
571
}
 
572
 
 
573
 
 
574
QNetworkAccessManager*
 
575
nam()
 
576
{
 
577
    QMutexLocker locker( &s_namAccessMutex );
 
578
    if ( s_threadNamHash.contains(  QThread::currentThread() ) )
 
579
    {
 
580
        //tDebug() << Q_FUNC_INFO << "Found current thread in nam hash";
 
581
        return s_threadNamHash[ QThread::currentThread() ];
 
582
    }
 
583
 
 
584
    if ( !s_threadNamHash.contains( QCoreApplication::instance()->thread() ) )
 
585
    {
 
586
        if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
 
587
        {
 
588
            setNam( new QNetworkAccessManager(), true );
 
589
            return s_threadNamHash[ QThread::currentThread() ];
 
590
        }
 
591
        else
 
592
            return 0;
 
593
    }
 
594
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Found gui thread in nam hash";
 
595
 
 
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();
 
599
 
 
600
    newNam->setConfiguration( QNetworkConfiguration( mainNam->configuration() ) );
 
601
    newNam->setNetworkAccessible( mainNam->networkAccessible() );
 
602
    newNam->setProxyFactory( proxyFactory( false, true ) );
 
603
 
 
604
    s_threadNamHash[ QThread::currentThread() ] = newNam;
 
605
 
 
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();
 
609
 
 
610
    return newNam;
 
611
}
 
612
 
 
613
 
 
614
void
 
615
setNam( QNetworkAccessManager* nam, bool noMutexLocker )
 
616
{
 
617
    Q_ASSERT( nam );
 
618
    // Don't lock if being called from nam()()
 
619
    QMutex otherMutex;
 
620
    QMutexLocker locker( noMutexLocker ? &otherMutex : &s_namAccessMutex );
 
621
    if ( !s_threadNamHash.contains( QCoreApplication::instance()->thread() ) &&
 
622
            QThread::currentThread() == QCoreApplication::instance()->thread() )
 
623
    {
 
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() )
 
629
        {
 
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() )
 
637
            {
 
638
                s_noProxyHostsMutex.unlock();
 
639
                proxyFactory->setNoProxyHosts( s->proxyNoProxyHosts().split( ',', QString::SkipEmptyParts ) );
 
640
            }
 
641
            else
 
642
                s_noProxyHostsMutex.unlock();
 
643
        }
 
644
 
 
645
        QNetworkProxyFactory::setApplicationProxyFactory( proxyFactory );
 
646
        nam->setProxyFactory( proxyFactory );
 
647
        s_threadNamHash[ QThread::currentThread() ] = nam;
 
648
        s_threadProxyFactoryHash[ QThread::currentThread() ] = proxyFactory;
 
649
        return;
 
650
    }
 
651
 
 
652
    s_threadNamHash[ QThread::currentThread() ] = nam;
 
653
 
 
654
    if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
 
655
        setProxyFactory( dynamic_cast< TomahawkUtils::NetworkProxyFactory* >( nam->proxyFactory() ), true );
 
656
}
 
657
 
 
658
 
 
659
bool
 
660
newerVersion( const QString& oldVersion, const QString& newVersion )
 
661
{
 
662
    if ( oldVersion.isEmpty() || newVersion.isEmpty() )
 
663
        return false;
 
664
 
 
665
    QStringList oldVList = oldVersion.split( ".", QString::SkipEmptyParts );
 
666
    QStringList newVList = newVersion.split( ".", QString::SkipEmptyParts );
 
667
 
 
668
    int i = 0;
 
669
    foreach ( const QString& nvPart, newVList )
 
670
    {
 
671
        if ( i + 1 > oldVList.count() )
 
672
            return true;
 
673
 
 
674
        int nviPart = nvPart.toInt();
 
675
        int oviPart = oldVList.at( i++ ).toInt();
 
676
 
 
677
        if ( nviPart > oviPart )
 
678
            return true;
 
679
 
 
680
        if ( nviPart < oviPart )
 
681
            return false;
 
682
    }
 
683
 
 
684
    return false;
 
685
}
 
686
 
 
687
 
 
688
QList< Tomahawk::query_ptr >
 
689
mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks, bool& changed )
 
690
{
 
691
    int sameCount = 0;
 
692
    QList< Tomahawk::query_ptr > tosave = newTracks;
 
693
    changed = false;
 
694
 
 
695
    foreach ( const Tomahawk::query_ptr& newquery, newTracks )
 
696
    {
 
697
        foreach ( const Tomahawk::query_ptr& oldq, orig )
 
698
        {
 
699
            if ( newquery->track() == oldq->track() &&
 
700
                newquery->artist() == oldq->artist() &&
 
701
                newquery->album() == oldq->album() )
 
702
            {
 
703
                sameCount++;
 
704
                if ( tosave.contains( newquery ) )
 
705
                    tosave.replace( tosave.indexOf( newquery ), oldq );
 
706
 
 
707
                break;
 
708
            }
 
709
        }
 
710
    }
 
711
 
 
712
    // No work to be done if all are the same
 
713
    if ( orig.size() == newTracks.size() && sameCount == orig.size() )
 
714
        return orig;
 
715
 
 
716
    changed = true;
 
717
    return tosave;
 
718
}
 
719
 
 
720
 
 
721
// taken from util/fileutils.cpp in kdevplatform
 
722
bool
 
723
removeDirectory( const QString& dir )
 
724
{
 
725
    const QDir aDir( dir );
 
726
 
 
727
    tLog() << "Deleting DIR:" << dir;
 
728
    bool has_err = false;
 
729
    if ( aDir.exists() )
 
730
    {
 
731
        foreach ( const QFileInfo& entry, aDir.entryInfoList( QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files | QDir::NoSymLinks ) )
 
732
        {
 
733
            QString path = entry.absoluteFilePath();
 
734
            if ( entry.isDir() )
 
735
            {
 
736
                has_err = !removeDirectory( path ) || has_err;
 
737
            }
 
738
            else if ( !QFile::remove( path ) )
 
739
            {
 
740
                has_err = true;
 
741
            }
 
742
        }
 
743
        if ( !aDir.rmdir( aDir.absolutePath() ) )
 
744
        {
 
745
            has_err = true;
 
746
        }
 
747
    }
 
748
 
 
749
    return !has_err;
 
750
}
 
751
 
 
752
 
 
753
quint64
 
754
infosystemRequestId()
 
755
{
 
756
    QMutexLocker locker( &s_infosystemRequestIdMutex );
 
757
    quint64 result = s_infosystemRequestId;
 
758
    s_infosystemRequestId++;
 
759
    return result;
 
760
}
 
761
 
 
762
 
 
763
QString
 
764
md5( const QByteArray& data )
 
765
{
 
766
    QByteArray const digest = QCryptographicHash::hash( data, QCryptographicHash::Md5 );
 
767
    return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' );
 
768
}
 
769
 
 
770
 
 
771
void
 
772
crash()
 
773
{
 
774
    volatile int* a = (int*)(NULL);
 
775
    *a = 1;
 
776
}
 
777
 
 
778
 
 
779
void
 
780
installTranslator( QObject* parent )
 
781
{
 
782
#if QT_VERSION >= 0x040800
 
783
    QString locale = QLocale::system().uiLanguages().first().replace( "-", "_" );
 
784
#else
 
785
    QString locale = QLocale::system().name();
 
786
#endif
 
787
    if ( locale == "C" )
 
788
        locale = "en";
 
789
 
 
790
    // Tomahawk translations
 
791
    QTranslator* translator = new QTranslator( parent );
 
792
    if ( translator->load( QString( ":/lang/tomahawk_" ) + locale ) )
 
793
    {
 
794
        qDebug() << "Translation: Tomahawk: Using system locale:" << locale;
 
795
    }
 
796
    else
 
797
    {
 
798
        qDebug() << "Translation: Tomahawk: Using default locale, system locale one not found:" << locale;
 
799
        translator->load( QString( ":/lang/tomahawk_en" ) );
 
800
    }
 
801
 
 
802
    QCoreApplication::installTranslator( translator );
 
803
 
 
804
    // Qt translations
 
805
    translator = new QTranslator( parent );
 
806
    if ( translator->load( QString( ":/lang/qt_" ) + locale ) )
 
807
    {
 
808
        qDebug() << "Translation: Qt: Using system locale:" << locale;
 
809
    }
 
810
    else
 
811
    {
 
812
        qDebug() << "Translation: Qt: Using default locale, system locale one not found:" << locale;
 
813
    }
 
814
 
 
815
    QCoreApplication::installTranslator( translator );
 
816
}
 
817
 
 
818
 
 
819
bool
 
820
verifyFile( const QString& filePath, const QString& signature )
 
821
{
 
822
    QCA::Initializer init;
 
823
 
 
824
    if ( !QCA::isSupported( "sha1" ) )
 
825
    {
 
826
        qWarning() << "SHA1 not supported by QCA, aborting.";
 
827
        return false;
 
828
    }
 
829
 
 
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.
 
835
 
 
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 ) )
 
840
    {
 
841
        qWarning() << "Unable to read public key from resources!";
 
842
        return false;
 
843
    }
 
844
 
 
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)
 
849
    {
 
850
        qWarning() << "Public key reading/loading failed! Tried to load public key:" << pubkeyData;
 
851
        return false;
 
852
    }
 
853
 
 
854
    if ( !publicKey.canVerify() )
 
855
    {
 
856
        qWarning() << "Loaded Tomahawk public key but cannot use it to verify! What is up....";
 
857
        return false;
 
858
    }
 
859
 
 
860
    // Step 2: Get the SHA1 of the file contents
 
861
    QFile toVerify( filePath );
 
862
    if ( !toVerify.exists() || !toVerify.open( QIODevice::ReadOnly ) )
 
863
    {
 
864
        qWarning() << "Failed to open file we are trying to verify!" << filePath;
 
865
        return false;
 
866
    }
 
867
 
 
868
    const QByteArray fileHashData = QCA::Hash( "sha1" ).hash( toVerify.readAll() ).toByteArray();
 
869
    toVerify.close();
 
870
 
 
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() )
 
875
    {
 
876
        qWarning() << "Got empty signature after we tried to decode it from Base64:" << signature.trimmed().toUtf8() << decodedSignature.toBase64();
 
877
        return false;
 
878
    }
 
879
 
 
880
    // Step 4: Do the actual verifying!
 
881
    const bool result = publicKey.verifyMessage( fileHashData, decodedSignature, QCA::EMSA1_SHA1, QCA::DERSequence );
 
882
    if ( !result )
 
883
    {
 
884
        qWarning() << "File" << filePath << "FAILED VERIFICATION against our input signature!";
 
885
        return false;
 
886
    }
 
887
 
 
888
    tDebug( LOGVERBOSE ) << "Successfully verified signature of downloaded file:" << filePath;
 
889
 
 
890
    return true;
 
891
}
 
892
 
 
893
 
 
894
QString
 
895
extractScriptPayload( const QString& filename, const QString& resolverId )
 
896
{
 
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 ) ) )
 
900
    {
 
901
        tLog() << "Failed to mkdir resolver save dir:" << TomahawkUtils::appDataDir().absoluteFilePath( QString( "atticaresolvers/%1" ).arg( resolverId ) );
 
902
        return QString();
 
903
    }
 
904
    resolverDir.cd( QString( "atticaresolvers/%1" ).arg( resolverId ) );
 
905
 
 
906
    if ( !unzipFileInFolder( filename, resolverDir ) )
 
907
    {
 
908
        qWarning() << "Failed to unzip resolver. Ooops.";
 
909
        return QString();
 
910
    }
 
911
 
 
912
    return resolverDir.absolutePath();
 
913
}
 
914
 
 
915
 
 
916
bool
 
917
unzipFileInFolder( const QString& zipFileName, const QDir& folder )
 
918
{
 
919
    Q_ASSERT( !zipFileName.isEmpty() );
 
920
    Q_ASSERT( folder.exists() );
 
921
 
 
922
    QuaZip zipFile( zipFileName );
 
923
    if ( !zipFile.open( QuaZip::mdUnzip ) )
 
924
    {
 
925
        qWarning() << "Failed to QuaZip open:" << zipFile.getZipError();
 
926
        return false;
 
927
    }
 
928
 
 
929
    if ( !zipFile.goToFirstFile() )
 
930
    {
 
931
        tLog() << "Failed to go to first file in zip archive:" << zipFile.getZipError();
 
932
        return false;
 
933
    }
 
934
 
 
935
    tDebug( LOGVERBOSE ) << "Unzipping files to:" << folder.absolutePath();
 
936
 
 
937
    QuaZipFile fileInZip( &zipFile );
 
938
    do
 
939
    {
 
940
        QuaZipFileInfo info;
 
941
        zipFile.getCurrentFileInfo( &info );
 
942
 
 
943
        if ( !fileInZip.open( QIODevice::ReadOnly ) )
 
944
        {
 
945
            tLog() << "Failed to open file inside zip archive:" << info.name << zipFile.getZipName() << "with error:" << zipFile.getZipError();
 
946
            continue;
 
947
        }
 
948
 
 
949
        QFile out( folder.absoluteFilePath( fileInZip.getActualFileName() ) );
 
950
 
 
951
        // make dir if there is one needed
 
952
        QStringList parts = fileInZip.getActualFileName().split( "/" );
 
953
        if ( parts.size() > 1 )
 
954
        {
 
955
            QStringList dirs = parts.mid( 0, parts.size() - 1 );
 
956
            QString dirPath = dirs.join( "/" ); // QDir translates / to \ internally if necessary
 
957
            folder.mkpath( dirPath );
 
958
        }
 
959
 
 
960
        tDebug( LOGVERBOSE ) << "Writing to output file..." << out.fileName();
 
961
        if ( !out.open( QIODevice::WriteOnly ) )
 
962
        {
 
963
            tLog() << "Failed to open zip extract file:" << out.errorString() << info.name;
 
964
            fileInZip.close();
 
965
            continue;
 
966
        }
 
967
 
 
968
 
 
969
        out.write( fileInZip.readAll() );
 
970
        out.close();
 
971
        fileInZip.close();
 
972
 
 
973
    } while ( zipFile.goToNextFile() );
 
974
 
 
975
    return true;
 
976
}
 
977
 
 
978
 
 
979
void
 
980
extractBinaryResolver( const QString& zipFilename, QObject* receiver )
 
981
{
 
982
    BinaryExtractWorker* worker = new BinaryExtractWorker( zipFilename, receiver );
 
983
    worker->start( QThread::LowPriority );
 
984
}
 
985
 
 
986
 
 
987
bool
 
988
whitelistedHttpResultHint( const QString& url )
 
989
{
 
990
    // For now, just http/https
 
991
    return url.startsWith( "http" );
 
992
}
 
993
 
 
994
 
 
995
} // ns
 
996
 
 
997
#include "TomahawkUtils.moc"