source: trunk/qt/session.cc @ 10769

Last change on this file since 10769 was 10769, checked in by Longinus00, 12 years ago

(qt) #3147: qtr delayed to connect to remote session automatically

  • Property svn:keywords set to Date Rev Author Id
File size: 29.0 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: session.cc 10769 2010-06-16 02:58:44Z Longinus00 $
11 */
12
13#include <cassert>
14#include <iostream>
15
16#include <QApplication>
17#include <QByteArray>
18#include <QClipboard>
19#include <QCoreApplication>
20#include <QDesktopServices>
21#include <QMessageBox>
22#include <QNetworkReply>
23#include <QNetworkRequest>
24#include <QSet>
25#include <QStyle>
26#include <QTextStream>
27
28#include <libtransmission/transmission.h>
29#include <libtransmission/bencode.h>
30#include <libtransmission/json.h>
31#include <libtransmission/rpcimpl.h>
32#include <libtransmission/utils.h> /* tr_free */
33#include <libtransmission/version.h> /* LONG_VERSION */
34
35#include "prefs.h"
36#include "qticonloader.h"
37#include "session.h"
38#include "session-dialog.h"
39#include "torrent.h"
40#include "utils.h"
41
42// #define DEBUG_HTTP
43
44namespace
45{
46    enum
47    {
48        TAG_SOME_TORRENTS,
49        TAG_ALL_TORRENTS,
50        TAG_SESSION_STATS,
51        TAG_SESSION_INFO,
52        TAG_BLOCKLIST_UPDATE,
53        TAG_ADD_TORRENT,
54        TAG_PORT_TEST,
55        TAG_MAGNET_LINK,
56
57        FIRST_UNIQUE_TAG
58    };
59}
60
61/***
62****
63***/
64
65namespace
66{
67    typedef Torrent::KeyList KeyList;
68    const KeyList& getInfoKeys( ) { return Torrent::getInfoKeys( ); }
69    const KeyList& getStatKeys( ) { return Torrent::getStatKeys( ); }
70    const KeyList& getExtraStatKeys( ) { return Torrent::getExtraStatKeys( ); }
71
72    void
73    addList( tr_benc * list, const KeyList& strings )
74    {
75        tr_bencListReserve( list, strings.size( ) );
76        foreach( const char * str, strings )
77            tr_bencListAddStr( list, str );
78    }
79}
80
81/***
82****
83***/
84
85void
86Session :: sessionSet( const char * key, const QVariant& value )
87{
88    tr_benc top;
89    tr_bencInitDict( &top, 2 );
90    tr_bencDictAddStr( &top, "method", "session-set" );
91    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 1 ) );
92    switch( value.type( ) ) {
93        case QVariant::Bool:   tr_bencDictAddBool ( args, key, value.toBool() ); break;
94        case QVariant::Int:    tr_bencDictAddInt  ( args, key, value.toInt() ); break;
95        case QVariant::Double: tr_bencDictAddReal ( args, key, value.toDouble() ); break;
96        case QVariant::String: tr_bencDictAddStr  ( args, key, value.toString().toUtf8() ); break;
97        default: assert( "unknown type" );
98    }
99    exec( &top );
100    tr_bencFree( &top );
101}
102
103void
104Session :: portTest( )
105{
106    tr_benc top;
107    tr_bencInitDict( &top, 2 );
108    tr_bencDictAddStr( &top, "method", "port-test" );
109    tr_bencDictAddInt( &top, "tag", TAG_PORT_TEST );
110    exec( &top );
111    tr_bencFree( &top );
112}
113
114void
115Session :: copyMagnetLinkToClipboard( int torrentId )
116{
117    tr_benc top;
118    tr_bencInitDict( &top, 3 );
119    tr_bencDictAddStr( &top, "method", "torrent-get" );
120    tr_bencDictAddInt( &top, "tag", TAG_MAGNET_LINK );
121    tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
122    tr_bencListAddInt( tr_bencDictAddList( args, "ids", 1 ), torrentId );
123    tr_bencListAddStr( tr_bencDictAddList( args, "fields", 1 ), "magnetLink" );
124
125    exec( &top );
126    tr_bencFree( &top );
127}
128
129void
130Session :: updatePref( int key )
131{
132    if( myPrefs.isCore( key ) ) switch( key )
133    {
134        case Prefs :: ALT_SPEED_LIMIT_DOWN:
135        case Prefs :: ALT_SPEED_LIMIT_ENABLED:
136        case Prefs :: ALT_SPEED_LIMIT_TIME_BEGIN:
137        case Prefs :: ALT_SPEED_LIMIT_TIME_DAY:
138        case Prefs :: ALT_SPEED_LIMIT_TIME_ENABLED:
139        case Prefs :: ALT_SPEED_LIMIT_TIME_END:
140        case Prefs :: ALT_SPEED_LIMIT_UP:
141        case Prefs :: BLOCKLIST_DATE:
142        case Prefs :: BLOCKLIST_ENABLED:
143        case Prefs :: DHT_ENABLED:
144        case Prefs :: DOWNLOAD_DIR:
145        case Prefs :: DSPEED:
146        case Prefs :: DSPEED_ENABLED:
147        case Prefs :: INCOMPLETE_DIR:
148        case Prefs :: INCOMPLETE_DIR_ENABLED:
149        case Prefs :: LPD_ENABLED:
150        case Prefs :: PEER_LIMIT_GLOBAL:
151        case Prefs :: PEER_LIMIT_TORRENT:
152        case Prefs :: PEER_PORT:
153        case Prefs :: PEER_PORT_RANDOM_ON_START:
154        case Prefs :: PEX_ENABLED:
155        case Prefs :: PORT_FORWARDING:
156        case Prefs :: SCRIPT_TORRENT_DONE_ENABLED:
157        case Prefs :: SCRIPT_TORRENT_DONE_FILENAME:
158        case Prefs :: START:
159        case Prefs :: TRASH_ORIGINAL:
160        case Prefs :: USPEED:
161        case Prefs :: USPEED_ENABLED:
162            sessionSet( myPrefs.keyStr(key), myPrefs.variant(key) );
163            break;
164
165        case Prefs :: RATIO:
166            sessionSet( "seedRatioLimit", myPrefs.variant(key) );
167            break;
168        case Prefs :: RATIO_ENABLED:
169            sessionSet( "seedRatioLimited", myPrefs.variant(key) );
170            break;
171
172        case Prefs :: RPC_AUTH_REQUIRED:
173            if( mySession )
174                tr_sessionSetRPCEnabled( mySession, myPrefs.getBool(key) );
175            break;
176        case Prefs :: RPC_ENABLED:
177            if( mySession )
178                tr_sessionSetRPCEnabled( mySession, myPrefs.getBool(key) );
179            break;
180        case Prefs :: RPC_PASSWORD:
181            if( mySession )
182                tr_sessionSetRPCPassword( mySession, myPrefs.getString(key).toUtf8().constData() );
183            break;
184        case Prefs :: RPC_PORT:
185            if( mySession )
186                tr_sessionSetRPCPort( mySession, myPrefs.getInt(key) );
187            break;
188        case Prefs :: RPC_USERNAME:
189            if( mySession )
190                tr_sessionSetRPCUsername( mySession, myPrefs.getString(key).toUtf8().constData() );
191            break;
192        case Prefs :: RPC_WHITELIST_ENABLED:
193            if( mySession )
194                tr_sessionSetRPCWhitelistEnabled( mySession, myPrefs.getBool(key) );
195            break;
196        case Prefs :: RPC_WHITELIST:
197            if( mySession )
198                tr_sessionSetRPCWhitelist( mySession, myPrefs.getString(key).toUtf8().constData() );
199            break;
200
201        default:
202            std::cerr << "unhandled pref: " << key << std::endl;
203    }
204}
205
206/***
207****
208***/
209
210Session :: Session( const char * configDir, Prefs& prefs ):
211    nextUniqueTag( FIRST_UNIQUE_TAG ),
212    myBlocklistSize( -1 ),
213    myPrefs( prefs ),
214    mySession( 0 ),
215    myConfigDir( configDir )
216{
217    myStats.ratio = TR_RATIO_NA;
218    myStats.uploadedBytes = 0;
219    myStats.downloadedBytes = 0;
220    myStats.filesAdded = 0;
221    myStats.sessionCount = 0;
222    myStats.secondsActive = 0;
223    myCumulativeStats = myStats;
224
225    connect( &myNAM, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*)) );
226    connect( &myNAM, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SIGNAL(httpAuthenticationRequired()) );
227    connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
228}
229
230Session :: ~Session( )
231{
232    stop( );
233}
234
235/***
236****
237***/
238
239void
240Session :: stop( )
241{
242    foreach( Reply myReply, myReplies )
243        myReply.networkReply->abort();
244    myUrl.clear( );
245
246    if( mySession )
247    {
248        tr_sessionClose( mySession );
249        mySession = 0;
250    }
251}
252
253void
254Session :: restart( )
255{
256    stop( );
257    start( );
258}
259
260void
261Session :: start( )
262{
263    if( myPrefs.get<bool>(Prefs::SESSION_IS_REMOTE) )
264    {
265        const int port( myPrefs.get<int>(Prefs::SESSION_REMOTE_PORT) );
266        const bool auth( myPrefs.get<bool>(Prefs::SESSION_REMOTE_AUTH) );
267        const QString host( myPrefs.get<QString>(Prefs::SESSION_REMOTE_HOST) );
268        const QString user( myPrefs.get<QString>(Prefs::SESSION_REMOTE_USERNAME) );
269        const QString pass( myPrefs.get<QString>(Prefs::SESSION_REMOTE_PASSWORD) );
270
271        QUrl url;
272        url.setScheme( "http" );
273        url.setHost( host );
274        url.setPort( port );
275        url.setPath( "/transmission/rpc" );
276        if( auth ) {
277            url.setUserName( user );
278            url.setPassword( pass );
279        }
280        myUrl = url;
281    }
282    else
283    {
284        tr_benc settings;
285        tr_bencInitDict( &settings, 0 );
286        tr_sessionLoadSettings( &settings, myConfigDir.toUtf8().constData(), "qt" );
287        mySession = tr_sessionInit( "qt", myConfigDir.toUtf8().constData(), true, &settings );
288        tr_bencFree( &settings );
289
290        tr_ctor * ctor = tr_ctorNew( mySession );
291        int torrentCount;
292        tr_torrent ** torrents = tr_sessionLoadTorrents( mySession, ctor, &torrentCount );
293        tr_free( torrents );
294        tr_ctorFree( ctor );
295    }
296
297    emit sourceChanged( );
298}
299
300bool
301Session :: isServer( ) const
302{
303    return mySession != 0;
304}
305
306bool
307Session :: isLocal( ) const
308{
309    if( mySession != 0 )
310        return true;
311
312    if( myUrl.host() == "127.0.0.1" )
313        return true;
314
315    if( !myUrl.host().compare( "localhost", Qt::CaseInsensitive ) )
316        return true;
317
318    return false;
319}
320
321/***
322****
323***/
324
325namespace
326{
327    tr_benc *
328    buildRequest( const char * method, tr_benc& top, int tag=-1 )
329    {
330        tr_bencInitDict( &top, 3 );
331        tr_bencDictAddStr( &top, "method", method );
332        if( tag >= 0 )
333            tr_bencDictAddInt( &top, "tag", tag );
334        return tr_bencDictAddDict( &top, "arguments", 0 );
335    }
336
337    void
338    addOptionalIds( tr_benc * args, const QSet<int>& ids )
339    {
340        if( !ids.isEmpty( ) )
341        {
342            tr_benc * idList( tr_bencDictAddList( args, "ids", ids.size( ) ) );
343            foreach( int i, ids )
344                tr_bencListAddInt( idList, i );
345        }
346    }
347}
348
349void
350Session :: torrentSet( const QSet<int>& ids, const QString& key, double value )
351{
352    tr_benc top;
353    tr_bencInitDict( &top, 2 );
354    tr_bencDictAddStr( &top, "method", "torrent-set" );
355    tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
356    tr_bencDictAddReal( args, key.toUtf8().constData(), value );
357    addOptionalIds( args, ids );
358    exec( &top );
359    tr_bencFree( &top );
360}
361
362void
363Session :: torrentSet( const QSet<int>& ids, const QString& key, int value )
364{
365    tr_benc top;
366    tr_bencInitDict( &top, 2 );
367    tr_bencDictAddStr( &top, "method", "torrent-set" );
368    tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
369    tr_bencDictAddInt( args, key.toUtf8().constData(), value );
370    addOptionalIds( args, ids );
371    exec( &top );
372    tr_bencFree( &top );
373}
374
375void
376Session :: torrentSet( const QSet<int>& ids, const QString& key, bool value )
377{
378    tr_benc top;
379    tr_bencInitDict( &top, 2 );
380    tr_bencDictAddStr( &top, "method", "torrent-set" );
381    tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
382    tr_bencDictAddBool( args, key.toUtf8().constData(), value );
383    addOptionalIds( args, ids );
384    exec( &top );
385    tr_bencFree( &top );
386}
387
388void
389Session :: torrentSet( const QSet<int>& ids, const QString& key, const QList<int>& value )
390{
391    tr_benc top;
392    tr_bencInitDict( &top, 2 );
393    tr_bencDictAddStr( &top, "method", "torrent-set" );
394    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
395    addOptionalIds( args, ids );
396    tr_benc * list( tr_bencDictAddList( args, key.toUtf8().constData(), value.size( ) ) );
397    foreach( int i, value )
398        tr_bencListAddInt( list, i );
399    exec( &top );
400    tr_bencFree( &top );
401}
402
403void
404Session :: torrentSetLocation( const QSet<int>& ids, const QString& location, bool doMove )
405{
406    tr_benc top;
407    tr_bencInitDict( &top, 2 );
408    tr_bencDictAddStr( &top, "method", "torrent-set-location" );
409    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 3 ) );
410    addOptionalIds( args, ids );
411    tr_bencDictAddStr( args, "location", location.toUtf8().constData() );
412    tr_bencDictAddBool( args, "move", doMove );
413    exec( &top );
414    tr_bencFree( &top );
415}
416
417void
418Session :: refreshTorrents( const QSet<int>& ids )
419{
420    if( ids.empty( ) )
421    {
422        refreshAllTorrents( );
423    }
424    else
425    {
426        tr_benc top;
427        tr_bencInitDict( &top, 3 );
428        tr_bencDictAddStr( &top, "method", "torrent-get" );
429        tr_bencDictAddInt( &top, "tag", TAG_SOME_TORRENTS );
430        tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
431        addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) );
432        addOptionalIds( args, ids );
433        exec( &top );
434        tr_bencFree( &top );
435    }
436}
437
438void
439Session :: refreshExtraStats( const QSet<int>& ids )
440{
441    tr_benc top;
442    tr_bencInitDict( &top, 3 );
443    tr_bencDictAddStr( &top, "method", "torrent-get" );
444    tr_bencDictAddInt( &top, "tag", TAG_SOME_TORRENTS );
445    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
446    addOptionalIds( args, ids );
447    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) + getExtraStatKeys( ));
448    exec( &top );
449    tr_bencFree( &top );
450}
451
452void
453Session :: sendTorrentRequest( const char * request, const QSet<int>& ids )
454{
455    tr_benc top;
456    tr_benc * args( buildRequest( request, top ) );
457    addOptionalIds( args, ids );
458    exec( &top );
459    tr_bencFree( &top );
460
461    refreshTorrents( ids );
462}
463
464void
465Session :: pauseTorrents( const QSet<int>& ids )
466{
467    sendTorrentRequest( "torrent-stop", ids );
468}
469
470void
471Session :: startTorrents( const QSet<int>& ids )
472{
473    sendTorrentRequest( "torrent-start", ids );
474}
475
476void
477Session :: refreshActiveTorrents( )
478{
479    tr_benc top;
480    tr_bencInitDict( &top, 3 );
481    tr_bencDictAddStr( &top, "method", "torrent-get" );
482    tr_bencDictAddInt( &top, "tag", TAG_SOME_TORRENTS );
483    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
484    tr_bencDictAddStr( args, "ids", "recently-active" );
485    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) );
486    exec( &top );
487    tr_bencFree( &top );
488}
489
490void
491Session :: refreshAllTorrents( )
492{
493    tr_benc top;
494    tr_bencInitDict( &top, 3 );
495    tr_bencDictAddStr( &top, "method", "torrent-get" );
496    tr_bencDictAddInt( &top, "tag", TAG_ALL_TORRENTS );
497    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 1 ) );
498    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) );
499    exec( &top );
500    tr_bencFree( &top );
501}
502
503void
504Session :: initTorrents( const QSet<int>& ids )
505{
506    tr_benc top;
507    const int tag( ids.isEmpty() ? TAG_ALL_TORRENTS : TAG_SOME_TORRENTS );
508    tr_benc * args( buildRequest( "torrent-get", top, tag ) );
509    addOptionalIds( args, ids );
510    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys()+getInfoKeys() );
511    exec( &top );
512    tr_bencFree( &top );
513}
514
515void
516Session :: refreshSessionStats( )
517{
518    tr_benc top;
519    tr_bencInitDict( &top, 2 );
520    tr_bencDictAddStr( &top, "method", "session-stats" );
521    tr_bencDictAddInt( &top, "tag", TAG_SESSION_STATS );
522    exec( &top );
523    tr_bencFree( &top );
524}
525
526void
527Session :: refreshSessionInfo( )
528{
529    tr_benc top;
530    tr_bencInitDict( &top, 2 );
531    tr_bencDictAddStr( &top, "method", "session-get" );
532    tr_bencDictAddInt( &top, "tag", TAG_SESSION_INFO );
533    exec( &top );
534    tr_bencFree( &top );
535}
536
537void
538Session :: updateBlocklist( )
539{
540    tr_benc top;
541    tr_bencInitDict( &top, 2 );
542    tr_bencDictAddStr( &top, "method", "blocklist-update" );
543    tr_bencDictAddInt( &top, "tag", TAG_BLOCKLIST_UPDATE );
544    exec( &top );
545    tr_bencFree( &top );
546}
547
548/***
549****
550***/
551
552void
553Session :: exec( const tr_benc * request )
554{
555    char * str = tr_bencToStr( request, TR_FMT_JSON_LEAN, NULL );
556    exec( str );
557    tr_free( str );
558}
559
560void
561Session :: localSessionCallback( tr_session * session, const char * json, size_t len, void * self )
562{
563    Q_UNUSED( session );
564
565    ((Session*)self)->parseResponse( json, len );
566}
567
568void
569Session :: exec( const char * json )
570{
571    if( mySession  )
572    {
573        tr_rpc_request_exec_json( mySession, json, strlen( json ), localSessionCallback, this );
574    }
575    else if( !myUrl.isEmpty( ) )
576    {
577        QNetworkRequest request;
578        request.setUrl( myUrl );
579        request.setRawHeader( "User-Agent", QString( QCoreApplication::instance()->applicationName() + "/" + LONG_VERSION_STRING ).toAscii() );
580        request.setRawHeader( "Content-Type", "application/json; charset=UTF-8" );
581        if( !mySessionId.isEmpty( ) )
582            request.setRawHeader( TR_RPC_SESSION_ID_HEADER, mySessionId.toAscii() );
583
584        QBuffer * reqbuf = new QBuffer;
585        reqbuf->setData( QByteArray( json ) );
586
587        QNetworkReply * reply = myNAM.post( request, reqbuf );
588        connect( reply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(dataReadProgress()));
589        connect( reply, SIGNAL(uploadProgress(qint64,qint64)), this, SIGNAL(dataSendProgress()));
590
591        Reply myReply;
592        myReply.networkReply = reply;
593        myReply.buffer = reqbuf;
594        myReplies << myReply;
595#ifdef DEBUG_HTTP
596        std::cerr << "sending " << "POST " << qPrintable( myUrl.path() ) << std::endl;
597        foreach( QByteArray b, request.rawHeaderList() )
598            std::cerr << b.constData()
599                      << ": "
600                      << request.rawHeader( b ).constData()
601                      << std::endl;
602        std::cerr << "Body:\n" << json << std::endl;
603#endif
604    }
605}
606
607void
608Session :: onFinished( QNetworkReply * reply )
609{
610    QBuffer * buffer;
611    for( QList<Reply>::iterator i = myReplies.begin(); i != myReplies.end(); ++i )
612    {
613        if( reply == i->networkReply )
614        {
615            buffer = i->buffer;
616            myReplies.erase( i );
617            break;
618        }
619    }
620
621#ifdef DEBUG_HTTP
622    std::cerr << "http response header: " << std::endl;
623    foreach( QByteArray b, reply->rawHeaderList() )
624        std::cerr << b.constData()
625                  << ": "
626                  << reply->rawHeader( b ).constData()
627                  << std::endl;
628    std::cerr << "json:\n" << reply->peek( reply->bytesAvailable() ).constData() << std::endl;
629#endif
630
631    if( ( reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 409 )
632        && ( reply->hasRawHeader( TR_RPC_SESSION_ID_HEADER ) ) )
633    {
634        // we got a 409 telling us our session id has expired.
635        // update it and resubmit the request.
636        mySessionId = QString( reply->rawHeader( TR_RPC_SESSION_ID_HEADER ) );
637        exec( buffer->buffer().constData() );
638    }
639    else if( reply->error() != QNetworkReply::NoError )
640    {
641        std::cerr << "http error: " << qPrintable( reply->errorString() ) << std::endl;
642    }
643    else
644    {
645        const QByteArray response( reply->readAll() );
646        const char * json( response.constData( ) );
647        int jsonLength( response.size( ) );
648        if( jsonLength>0 && json[jsonLength-1] == '\n' ) --jsonLength;
649        parseResponse( json, jsonLength );
650    }
651
652    delete buffer;
653    reply->deleteLater();
654}
655
656void
657Session :: parseResponse( const char * json, size_t jsonLength )
658{
659    tr_benc top;
660    const uint8_t * end( 0 );
661    const int err( tr_jsonParse( "rpc", json, jsonLength, &top, &end ) );
662    if( !err )
663    {
664        int64_t tag = -1;
665        const char * result = NULL;
666        tr_benc * args = NULL;
667
668        tr_bencDictFindInt ( &top, "tag", &tag );
669        tr_bencDictFindStr ( &top, "result", &result );
670        tr_bencDictFindDict( &top, "arguments", &args );
671
672        emit executed( tag, result, args );
673
674        tr_benc * torrents;
675        const char * str;
676
677        if( tr_bencDictFindInt( &top, "tag", &tag ) )
678        {
679            switch( tag )
680            {
681                case TAG_SOME_TORRENTS:
682                case TAG_ALL_TORRENTS:
683                    if( tr_bencDictFindDict( &top, "arguments", &args ) ) {
684                        if( tr_bencDictFindList( args, "torrents", &torrents ) )
685                            emit torrentsUpdated( torrents, tag==TAG_ALL_TORRENTS );
686                        if( tr_bencDictFindList( args, "removed", &torrents ) )
687                            emit torrentsRemoved( torrents );
688                    }
689                    break;
690
691                case TAG_SESSION_STATS:
692                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
693                        updateStats( args );
694                    break;
695
696                case TAG_SESSION_INFO:
697                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
698                        updateInfo( args );
699                    break;
700
701                case TAG_BLOCKLIST_UPDATE: {
702                    int64_t intVal = 0;
703                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
704                        if( tr_bencDictFindInt( args, "blocklist-size", &intVal ) )
705                            setBlocklistSize( intVal );
706                    break;
707                }
708
709                case TAG_PORT_TEST: {
710                    tr_bool isOpen = 0;
711                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
712                        tr_bencDictFindBool( args, "port-is-open", &isOpen );
713                    emit portTested( (bool)isOpen );
714                }
715
716                case TAG_MAGNET_LINK: {
717                    tr_benc * args;
718                    tr_benc * torrents;
719                    tr_benc * child;
720                    const char * str;
721                    if( tr_bencDictFindDict( &top, "arguments", &args )
722                        && tr_bencDictFindList( args, "torrents", &torrents )
723                        && (( child = tr_bencListChild( torrents, 0 )))
724                        && tr_bencDictFindStr( child, "magnetLink", &str ) )
725                            QApplication::clipboard()->setText( str );
726                    break;
727                }
728
729                case TAG_ADD_TORRENT:
730                    str = "";
731                    if( tr_bencDictFindStr( &top, "result", &str ) && strcmp( str, "success" ) ) {
732                        QMessageBox * d = new QMessageBox( QMessageBox::Information,
733                                                           tr( "Add Torrent" ),
734                                                           QString::fromUtf8(str),
735                                                           QMessageBox::Close,
736                                                           QApplication::activeWindow());
737                        QPixmap pixmap;
738                        QIcon icon = QtIconLoader :: icon( "dialog-information" );
739                        if( !icon.isNull( ) ) {
740                            const int size = QApplication::style()->pixelMetric( QStyle::PM_LargeIconSize );
741                            d->setIconPixmap( icon.pixmap( size, size ) );
742                        }
743                        connect( d, SIGNAL(rejected()), d, SLOT(deleteLater()) );
744                        d->show( );
745                    }
746                    break;
747
748                default:
749                    break;
750            }
751        }
752        tr_bencFree( &top );
753    }
754}
755
756void
757Session :: updateStats( tr_benc * d, struct tr_session_stats * stats )
758{
759    int64_t i;
760
761    if( tr_bencDictFindInt( d, "uploadedBytes", &i ) )
762        stats->uploadedBytes = i;
763    if( tr_bencDictFindInt( d, "downloadedBytes", &i ) )
764        stats->downloadedBytes = i;
765    if( tr_bencDictFindInt( d, "filesAdded", &i ) )
766        stats->filesAdded = i;
767    if( tr_bencDictFindInt( d, "sessionCount", &i ) )
768        stats->sessionCount = i;
769    if( tr_bencDictFindInt( d, "secondsActive", &i ) )
770        stats->secondsActive = i;
771
772    stats->ratio = tr_getRatio( stats->uploadedBytes, stats->downloadedBytes );
773
774}
775
776void
777Session :: updateStats( tr_benc * d )
778{
779    tr_benc * c;
780
781    if( tr_bencDictFindDict( d, "current-stats", &c ) )
782        updateStats( c, &myStats );
783
784    if( tr_bencDictFindDict( d, "cumulative-stats", &c ) )
785        updateStats( c, &myCumulativeStats );
786
787    emit statsUpdated( );
788}
789
790void
791Session :: updateInfo( tr_benc * d )
792{
793    int64_t i;
794    const char * str;
795    disconnect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
796
797    for( int i=Prefs::FIRST_CORE_PREF; i<=Prefs::LAST_CORE_PREF; ++i )
798    {
799        const tr_benc * b( tr_bencDictFind( d, myPrefs.keyStr( i ) ) );
800
801        if( !b )
802            continue;
803
804        switch( myPrefs.type( i ) )
805        {
806            case QVariant :: Int: {
807                int64_t val;
808                if( tr_bencGetInt( b, &val ) )
809                    myPrefs.set( i, (int)val );
810                break;
811            }
812            case QVariant :: Double: {
813                double val;
814                if( tr_bencGetReal( b, &val ) )
815                    myPrefs.set( i, val );
816                break;
817            }
818            case QVariant :: Bool: {
819                tr_bool val;
820                if( tr_bencGetBool( b, &val ) )
821                    myPrefs.set( i, (bool)val );
822                break;
823            }
824            case TrTypes :: FilterModeType:
825            case TrTypes :: SortModeType:
826            case QVariant :: String: {
827                const char * val;
828                if( tr_bencGetStr( b, &val ) )
829                    myPrefs.set( i, QString(val) );
830                break;
831            }
832            default:
833                break;
834        }
835    }
836
837    tr_bool b;
838    double x;
839    if( tr_bencDictFindBool( d, "seedRatioLimited", &b ) )
840        myPrefs.set( Prefs::RATIO_ENABLED, b ? true : false );
841    if( tr_bencDictFindReal( d, "seedRatioLimit", &x ) )
842        myPrefs.set( Prefs::RATIO, x );
843
844    /* Use the C API to get settings that, for security reasons, aren't supported by RPC */
845    if( mySession != 0 )
846    {
847        myPrefs.set( Prefs::RPC_ENABLED,           tr_sessionIsRPCEnabled           ( mySession ) );
848        myPrefs.set( Prefs::RPC_AUTH_REQUIRED,     tr_sessionIsRPCPasswordEnabled   ( mySession ) );
849        myPrefs.set( Prefs::RPC_PASSWORD,          tr_sessionGetRPCPassword         ( mySession ) );
850        myPrefs.set( Prefs::RPC_PORT,              tr_sessionGetRPCPort             ( mySession ) );
851        myPrefs.set( Prefs::RPC_USERNAME,          tr_sessionGetRPCUsername         ( mySession ) );
852        myPrefs.set( Prefs::RPC_WHITELIST_ENABLED, tr_sessionGetRPCWhitelistEnabled ( mySession ) );
853        myPrefs.set( Prefs::RPC_WHITELIST,         tr_sessionGetRPCWhitelist        ( mySession ) );
854    }
855
856    if( tr_bencDictFindInt( d, "blocklist-size", &i ) && i!=blocklistSize( ) )
857        setBlocklistSize( i );
858
859    if( tr_bencDictFindStr( d, "version", &str ) && ( mySessionVersion != str ) )
860        mySessionVersion = str;
861
862    //std::cerr << "Session :: updateInfo end" << std::endl;
863    connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
864
865    emit sessionUpdated( );
866}
867
868void
869Session :: setBlocklistSize( int64_t i )
870{
871    myBlocklistSize = i;
872
873    emit blocklistUpdated( i );
874}
875
876void
877Session :: addTorrent( QString filename )
878{
879    addTorrent( filename, myPrefs.getString( Prefs::DOWNLOAD_DIR ) );
880}
881
882namespace
883{
884    bool isLink( const QString& str )
885    {
886        return Utils::isMagnetLink(str) || Utils::isURL(str);
887    }
888}
889
890void
891Session :: addTorrent( QString key, QString localPath )
892{
893    tr_benc top, *args;
894    tr_bencInitDict( &top, 2 );
895    tr_bencDictAddStr( &top, "method", "torrent-add" );
896    args = tr_bencDictAddDict( &top, "arguments", 3 );
897    tr_bencDictAddStr( args, "download-dir", qPrintable(localPath) );
898    tr_bencDictAddBool( args, "paused", !myPrefs.getBool( Prefs::START ) );
899
900    // figure out what to do with "key"....
901    bool keyHandled = false;
902    if( !keyHandled && isLink( key  )) {
903        tr_bencDictAddStr( args, "filename", key.toUtf8().constData() );
904        keyHandled = true; // it's a URL or magnet link...
905    }
906    if( !keyHandled ) {
907        QFile file( key );
908        file.open( QIODevice::ReadOnly );
909        const QByteArray raw( file.readAll( ) );
910        file.close( );
911        if( !raw.isEmpty( ) ) {
912            int b64len = 0;
913            char * b64 = tr_base64_encode( raw.constData(), raw.size(), &b64len );
914            tr_bencDictAddRaw( args, "metainfo", b64, b64len  );
915            tr_free( b64 );
916            keyHandled = true; // it's a local file...
917        }
918    }
919    if( !keyHandled ) {
920        const QByteArray tmp = key.toUtf8();
921        tr_bencDictAddRaw( args, "metainfo", tmp.constData(), tmp.length() );
922        keyHandled = true; // treat it as base64
923    }
924
925    exec( &top );
926    tr_bencFree( &top );
927}
928
929void
930Session :: removeTorrents( const QSet<int>& ids, bool deleteFiles )
931{
932    if( !ids.isEmpty( ) )
933    {
934        tr_benc top, *args;
935        tr_bencInitDict( &top, 2 );
936        tr_bencDictAddStr( &top, "method", "torrent-remove" );
937        args = tr_bencDictAddDict( &top, "arguments", 2 );
938        addOptionalIds( args, ids );
939        tr_bencDictAddInt( args, "delete-local-data", deleteFiles );
940        exec( &top );
941        tr_bencFree( &top );
942    }
943}
944
945void
946Session :: verifyTorrents( const QSet<int>& ids )
947{
948    if( !ids.isEmpty( ) )
949    {
950        tr_benc top, *args;
951        tr_bencInitDict( &top, 2 );
952        tr_bencDictAddStr( &top, "method", "torrent-verify" );
953        args = tr_bencDictAddDict( &top, "arguments", 1 );
954        addOptionalIds( args, ids );
955        exec( &top );
956        tr_bencFree( &top );
957    }
958}
959
960void
961Session :: reannounceTorrents( const QSet<int>& ids )
962{
963    if( !ids.isEmpty( ) )
964    {
965        tr_benc top, *args;
966        tr_bencInitDict( &top, 2 );
967        tr_bencDictAddStr( &top, "method", "torrent-reannounce" );
968        args = tr_bencDictAddDict( &top, "arguments", 1 );
969        addOptionalIds( args, ids );
970        exec( &top );
971        tr_bencFree( &top );
972    }
973}
974
975/***
976****
977***/
978
979void
980Session :: launchWebInterface( )
981{
982    QUrl url;
983    if( !mySession ) // remote session
984        url = myUrl;
985    else { // local session
986        url.setScheme( "http" );
987        url.setHost( "localhost" );
988        url.setPort( myPrefs.getInt( Prefs::RPC_PORT ) );
989    }
990    QDesktopServices :: openUrl( url );
991}
Note: See TracBrowser for help on using the repository browser.