source: trunk/qt/session.cc @ 10631

Last change on this file since 10631 was 10631, checked in by charles, 12 years ago

(trunk qt) #3060 "Local Peer Discovery protocol implementation" -- fix r10611 bug. Longinus00 found the bug and submitted a patch.

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