source: trunk/qt/session.cc @ 10550

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

(trunk) minor transmission.h API cleanup.

  1. remove tr_sessionGetActiveTorrentCount()
  2. make tr_sessionCountTorrents() package-visible instead of public.
  3. make tr_torrentGetActivity() private instead of public.
  • 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 10550 2010-04-29 23:08:11Z 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 :: PEER_LIMIT_GLOBAL:
145        case Prefs :: PEER_LIMIT_TORRENT:
146        case Prefs :: USPEED_ENABLED:
147        case Prefs :: USPEED:
148        case Prefs :: DSPEED_ENABLED:
149        case Prefs :: DSPEED:
150        case Prefs :: START:
151        case Prefs :: TRASH_ORIGINAL:
152        case Prefs :: PEX_ENABLED:
153        case Prefs :: PORT_FORWARDING:
154        case Prefs :: PEER_PORT:
155        case Prefs :: PEER_PORT_RANDOM_ON_START:
156            sessionSet( myPrefs.keyStr(key), myPrefs.variant(key) );
157            break;
158
159        case Prefs :: RATIO:
160            sessionSet( "seedRatioLimit", myPrefs.variant(key) );
161            break;
162        case Prefs :: RATIO_ENABLED:
163            sessionSet( "seedRatioLimited", myPrefs.variant(key) );
164            break;
165
166        case Prefs :: RPC_AUTH_REQUIRED:
167            if( mySession )
168                tr_sessionSetRPCEnabled( mySession, myPrefs.getBool(key) );
169            break;
170        case Prefs :: RPC_ENABLED:
171            if( mySession )
172                tr_sessionSetRPCEnabled( mySession, myPrefs.getBool(key) );
173            break;
174        case Prefs :: RPC_PASSWORD:
175            if( mySession )
176                tr_sessionSetRPCPassword( mySession, myPrefs.getString(key).toUtf8().constData() );
177            break;
178        case Prefs :: RPC_PORT:
179            if( mySession )
180                tr_sessionSetRPCPort( mySession, myPrefs.getInt(key) );
181            break;
182        case Prefs :: RPC_USERNAME:
183            if( mySession )
184                tr_sessionSetRPCUsername( mySession, myPrefs.getString(key).toUtf8().constData() );
185            break;
186        case Prefs :: RPC_WHITELIST_ENABLED:
187            if( mySession )
188                tr_sessionSetRPCWhitelistEnabled( mySession, myPrefs.getBool(key) );
189            break;
190        case Prefs :: RPC_WHITELIST:
191            if( mySession )
192                tr_sessionSetRPCWhitelist( mySession, myPrefs.getString(key).toUtf8().constData() );
193            break;
194
195        default:
196            std::cerr << "unhandled pref: " << key << std::endl;
197    }
198}
199
200/***
201****
202***/
203
204Session :: Session( const char * configDir, Prefs& prefs ):
205    nextUniqueTag( FIRST_UNIQUE_TAG ),
206    myBlocklistSize( -1 ),
207    myPrefs( prefs ),
208    mySession( 0 ),
209    myConfigDir( configDir )
210{
211    myStats.ratio = TR_RATIO_NA;
212    myStats.uploadedBytes = 0;
213    myStats.downloadedBytes = 0;
214    myStats.filesAdded = 0;
215    myStats.sessionCount = 0;
216    myStats.secondsActive = 0;
217    myCumulativeStats = myStats;
218
219    connect( &myHttp, SIGNAL(requestStarted(int)), this, SLOT(onRequestStarted(int)));
220    connect( &myHttp, SIGNAL(requestFinished(int,bool)), this, SLOT(onRequestFinished(int,bool)));
221    connect( &myHttp, SIGNAL(dataReadProgress(int,int)), this, SIGNAL(dataReadProgress()));
222    connect( &myHttp, SIGNAL(dataSendProgress(int,int)), this, SIGNAL(dataSendProgress()));
223    connect( &myHttp, SIGNAL(authenticationRequired(QString, quint16, QAuthenticator*)), this, SIGNAL(httpAuthenticationRequired()) );
224    connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
225
226    myBuffer.open( QIODevice::ReadWrite );
227}
228
229Session :: ~Session( )
230{
231    stop( );
232}
233
234/***
235****
236***/
237
238void
239Session :: stop( )
240{
241    myHttp.abort( );
242    myUrl.clear( );
243
244    if( mySession )
245    {
246        tr_sessionClose( mySession );
247        mySession = 0;
248    }
249}
250
251void
252Session :: restart( )
253{
254    stop( );
255    start( );
256}
257
258void
259Session :: start( )
260{
261    if( myPrefs.get<bool>(Prefs::SESSION_IS_REMOTE) )
262    {
263        const int port( myPrefs.get<int>(Prefs::SESSION_REMOTE_PORT) );
264        const bool auth( myPrefs.get<bool>(Prefs::SESSION_REMOTE_AUTH) );
265        const QString host( myPrefs.get<QString>(Prefs::SESSION_REMOTE_HOST) );
266        const QString user( myPrefs.get<QString>(Prefs::SESSION_REMOTE_USERNAME) );
267        const QString pass( myPrefs.get<QString>(Prefs::SESSION_REMOTE_PASSWORD) );
268
269        QUrl url;
270        url.setScheme( "http" );
271        url.setHost( host );
272        url.setPort( port );
273        if( auth ) {
274            url.setUserName( user );
275            url.setPassword( pass );
276        }
277        myUrl = url;
278
279        myHttp.setHost( host, port );
280        myHttp.setUser( user, pass );
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 * request )
570{
571    if( mySession  )
572    {
573        tr_rpc_request_exec_json( mySession, request, strlen( request ), localSessionCallback, this );
574    }
575    else if( !myUrl.isEmpty( ) )
576    {
577        static const QString path( "/transmission/rpc" );
578        QHttpRequestHeader header( "POST", path );
579        header.setValue( "User-Agent", QCoreApplication::instance()->applicationName() + "/" + LONG_VERSION_STRING );
580        header.setValue( "Content-Type", "application/json; charset=UTF-8" );
581        if( !mySessionId.isEmpty( ) )
582            header.setValue( TR_RPC_SESSION_ID_HEADER, mySessionId );
583        QBuffer * reqbuf = new QBuffer;
584        reqbuf->setData( QByteArray( request ) );
585        myHttp.request( header, reqbuf, &myBuffer );
586#ifdef DEBUG_HTTP
587        std::cerr << "sending " << qPrintable(header.toString()) << "\nBody:\n" << request << std::endl;
588#endif
589    }
590}
591
592void
593Session :: onRequestStarted( int id )
594{
595    Q_UNUSED( id );
596
597    assert( myBuffer.atEnd( ) );
598}
599
600void
601Session :: onRequestFinished( int id, bool error )
602{
603    Q_UNUSED( id );
604    QIODevice * sourceDevice = myHttp.currentSourceDevice( );
605
606    QHttpResponseHeader response = myHttp.lastResponse();
607
608#ifdef DEBUG_HTTP
609    std::cerr << "http request " << id << " ended.. response header: "
610              << qPrintable( myHttp.lastResponse().toString() )
611              << std::endl
612              << "json: " << myBuffer.buffer( ).constData( )
613              << std::endl;
614#endif
615
616    if( ( response.statusCode() == 409 ) && ( myBuffer.buffer().indexOf("invalid session-id") != -1 ) )
617    {
618        // we got a 409 telling us our session id has expired.
619        // update it and resubmit the request.
620        mySessionId = response.value( TR_RPC_SESSION_ID_HEADER );
621        exec( qobject_cast<QBuffer*>(sourceDevice)->buffer().constData() );
622    }
623    else if( error )
624    {
625        std::cerr << "http error: " << qPrintable(myHttp.errorString()) << std::endl;
626    }
627    else
628    {
629        const QByteArray& response( myBuffer.buffer( ) );
630        const char * json( response.constData( ) );
631        int jsonLength( response.size( ) );
632        if( jsonLength>0 && json[jsonLength-1] == '\n' ) --jsonLength;
633        parseResponse( json, jsonLength );
634    }
635
636    delete sourceDevice;
637    myBuffer.buffer( ).clear( );
638    myBuffer.reset( );
639    assert( myBuffer.bytesAvailable( ) < 1 );
640}
641
642void
643Session :: parseResponse( const char * json, size_t jsonLength )
644{
645    tr_benc top;
646    const uint8_t * end( 0 );
647    const int err( tr_jsonParse( "rpc", json, jsonLength, &top, &end ) );
648    if( !err )
649    {
650        int64_t tag = -1;
651        const char * result = NULL;
652        tr_benc * args = NULL;
653
654        tr_bencDictFindInt ( &top, "tag", &tag );
655        tr_bencDictFindStr ( &top, "result", &result );
656        tr_bencDictFindDict( &top, "arguments", &args );
657
658        emit executed( tag, result, args );
659
660        tr_benc * torrents;
661        const char * str;
662
663        if( tr_bencDictFindInt( &top, "tag", &tag ) )
664        {
665            switch( tag )
666            {
667                case TAG_SOME_TORRENTS:
668                case TAG_ALL_TORRENTS:
669                    if( tr_bencDictFindDict( &top, "arguments", &args ) ) {
670                        if( tr_bencDictFindList( args, "torrents", &torrents ) )
671                            emit torrentsUpdated( torrents, tag==TAG_ALL_TORRENTS );
672                        if( tr_bencDictFindList( args, "removed", &torrents ) )
673                            emit torrentsRemoved( torrents );
674                    }
675                    break;
676
677                case TAG_SESSION_STATS:
678                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
679                        updateStats( args );
680                    break;
681
682                case TAG_SESSION_INFO:
683                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
684                        updateInfo( args );
685                    break;
686
687                case TAG_BLOCKLIST_UPDATE: {
688                    int64_t intVal = 0;
689                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
690                        if( tr_bencDictFindInt( args, "blocklist-size", &intVal ) )
691                            setBlocklistSize( intVal );
692                    break;
693                }
694
695                case TAG_PORT_TEST: {
696                    tr_bool isOpen = 0;
697                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
698                        tr_bencDictFindBool( args, "port-is-open", &isOpen );
699                    emit portTested( (bool)isOpen );
700                }
701
702                case TAG_MAGNET_LINK: {
703                    tr_benc * args;
704                    tr_benc * torrents;
705                    tr_benc * child;
706                    const char * str;
707                    if( tr_bencDictFindDict( &top, "arguments", &args )
708                        && tr_bencDictFindList( args, "torrents", &torrents )
709                        && (( child = tr_bencListChild( torrents, 0 )))
710                        && tr_bencDictFindStr( child, "magnetLink", &str ) )
711                            QApplication::clipboard()->setText( str );
712                    break;
713                }
714
715                case TAG_ADD_TORRENT:
716                    str = "";
717                    if( tr_bencDictFindStr( &top, "result", &str ) && strcmp( str, "success" ) ) {
718                        QMessageBox * d = new QMessageBox( QMessageBox::Information,
719                                                           tr( "Add Torrent" ),
720                                                           QString::fromUtf8(str),
721                                                           QMessageBox::Close,
722                                                           QApplication::activeWindow());
723                        QPixmap pixmap;
724                        QIcon icon = QtIconLoader :: icon( "dialog-information" );
725                        if( !icon.isNull( ) ) {
726                            const int size = QApplication::style()->pixelMetric( QStyle::PM_LargeIconSize );
727                            d->setIconPixmap( icon.pixmap( size, size ) );
728                        }
729                        connect( d, SIGNAL(rejected()), d, SLOT(deleteLater()) );
730                        d->show( );
731                    }
732                    break;
733
734                default:
735                    break;
736            }
737        }
738        tr_bencFree( &top );
739    }
740}
741
742void
743Session :: updateStats( tr_benc * d, struct tr_session_stats * stats )
744{
745    int64_t i;
746
747    if( tr_bencDictFindInt( d, "uploadedBytes", &i ) )
748        stats->uploadedBytes = i;
749    if( tr_bencDictFindInt( d, "downloadedBytes", &i ) )
750        stats->downloadedBytes = i;
751    if( tr_bencDictFindInt( d, "filesAdded", &i ) )
752        stats->filesAdded = i;
753    if( tr_bencDictFindInt( d, "sessionCount", &i ) )
754        stats->sessionCount = i;
755    if( tr_bencDictFindInt( d, "secondsActive", &i ) )
756        stats->secondsActive = i;
757
758    stats->ratio = tr_getRatio( stats->uploadedBytes, stats->downloadedBytes );
759
760}
761
762void
763Session :: updateStats( tr_benc * d )
764{
765    tr_benc * c;
766
767    if( tr_bencDictFindDict( d, "current-stats", &c ) )
768        updateStats( c, &myStats );
769
770    if( tr_bencDictFindDict( d, "cumulative-stats", &c ) )
771        updateStats( c, &myCumulativeStats );
772
773    emit statsUpdated( );
774}
775
776void
777Session :: updateInfo( tr_benc * d )
778{
779    int64_t i;
780    const char * str;
781    disconnect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
782
783    for( int i=Prefs::FIRST_CORE_PREF; i<=Prefs::LAST_CORE_PREF; ++i )
784    {
785        const tr_benc * b( tr_bencDictFind( d, myPrefs.keyStr( i ) ) );
786
787        if( !b )
788            continue;
789
790        switch( myPrefs.type( i ) )
791        {
792            case QVariant :: Int: {
793                int64_t val;
794                if( tr_bencGetInt( b, &val ) )
795                    myPrefs.set( i, (int)val );
796                break;
797            }
798            case QVariant :: Double: {
799                double val;
800                if( tr_bencGetReal( b, &val ) )
801                    myPrefs.set( i, val );
802                break;
803            }
804            case QVariant :: Bool: {
805                tr_bool val;
806                if( tr_bencGetBool( b, &val ) )
807                    myPrefs.set( i, (bool)val );
808                break;
809            }
810            case TrTypes :: FilterModeType:
811            case TrTypes :: SortModeType:
812            case QVariant :: String: {
813                const char * val;
814                if( tr_bencGetStr( b, &val ) )
815                    myPrefs.set( i, QString(val) );
816                break;
817            }
818            default:
819                break;
820        }
821    }
822
823    /* Use the C API to get settings that, for security reasons, aren't supported by RPC */
824    if( mySession != 0 )
825    {
826        myPrefs.set( Prefs::RPC_ENABLED,           tr_sessionIsRPCEnabled           ( mySession ) );
827        myPrefs.set( Prefs::RPC_AUTH_REQUIRED,     tr_sessionIsRPCPasswordEnabled   ( mySession ) );
828        myPrefs.set( Prefs::RPC_PASSWORD,          tr_sessionGetRPCPassword         ( mySession ) );
829        myPrefs.set( Prefs::RPC_PORT,              tr_sessionGetRPCPort             ( mySession ) );
830        myPrefs.set( Prefs::RPC_USERNAME,          tr_sessionGetRPCUsername         ( mySession ) );
831        myPrefs.set( Prefs::RPC_WHITELIST_ENABLED, tr_sessionGetRPCWhitelistEnabled ( mySession ) );
832        myPrefs.set( Prefs::RPC_WHITELIST,         tr_sessionGetRPCWhitelist        ( mySession ) );
833    }
834
835    if( tr_bencDictFindInt( d, "blocklist-size", &i ) && i!=blocklistSize( ) )
836        setBlocklistSize( i );
837
838    if( tr_bencDictFindStr( d, "version", &str ) && ( mySessionVersion != str ) )
839        mySessionVersion = str;
840
841    //std::cerr << "Session :: updateInfo end" << std::endl;
842    connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
843
844    emit sessionUpdated( );
845}
846
847void
848Session :: setBlocklistSize( int64_t i )
849{
850    myBlocklistSize = i;
851
852    emit blocklistUpdated( i );
853}
854
855void
856Session :: addTorrent( QString filename )
857{
858    addTorrent( filename, myPrefs.getString( Prefs::DOWNLOAD_DIR ) );
859}
860
861namespace
862{
863    bool isLink( const QString& str )
864    {
865        return str.startsWith( "magnet:?" )
866            || str.startsWith( "http://" )
867            || str.startsWith( "https://" )
868            || str.startsWith( "ftp://" );
869    }
870}
871
872void
873Session :: addTorrent( QString key, QString localPath )
874{
875    tr_benc top, *args;
876    tr_bencInitDict( &top, 2 );
877    tr_bencDictAddStr( &top, "method", "torrent-add" );
878    args = tr_bencDictAddDict( &top, "arguments", 3 );
879    tr_bencDictAddStr( args, "download-dir", qPrintable(localPath) );
880    tr_bencDictAddBool( args, "paused", !myPrefs.getBool( Prefs::START ) );
881
882    // figure out what to do with "key"....
883    bool keyHandled = false;
884    if( !keyHandled && isLink( key  )) {
885        tr_bencDictAddStr( args, "filename", key.toUtf8().constData() );
886        keyHandled = true; // it's a URL or magnet link...
887    }
888    if( !keyHandled ) {
889        QFile file( key );
890        file.open( QIODevice::ReadOnly );
891        const QByteArray raw( file.readAll( ) );
892        file.close( );
893        if( !raw.isEmpty( ) ) {
894            int b64len = 0;
895            char * b64 = tr_base64_encode( raw.constData(), raw.size(), &b64len );
896            tr_bencDictAddRaw( args, "metainfo", b64, b64len  );
897            tr_free( b64 );
898            keyHandled = true; // it's a local file...
899        }
900    }
901    if( !keyHandled ) {
902        const QByteArray tmp = key.toUtf8();
903        tr_bencDictAddRaw( args, "metainfo", tmp.constData(), tmp.length() );
904        keyHandled = true; // treat it as base64
905    }
906
907    exec( &top );
908    tr_bencFree( &top );
909}
910
911void
912Session :: removeTorrents( const QSet<int>& ids, bool deleteFiles )
913{
914    if( !ids.isEmpty( ) )
915    {
916        tr_benc top, *args;
917        tr_bencInitDict( &top, 2 );
918        tr_bencDictAddStr( &top, "method", "torrent-remove" );
919        args = tr_bencDictAddDict( &top, "arguments", 2 );
920        addOptionalIds( args, ids );
921        tr_bencDictAddInt( args, "delete-local-data", deleteFiles );
922        exec( &top );
923        tr_bencFree( &top );
924    }
925}
926
927void
928Session :: verifyTorrents( const QSet<int>& ids )
929{
930    if( !ids.isEmpty( ) )
931    {
932        tr_benc top, *args;
933        tr_bencInitDict( &top, 2 );
934        tr_bencDictAddStr( &top, "method", "torrent-verify" );
935        args = tr_bencDictAddDict( &top, "arguments", 1 );
936        addOptionalIds( args, ids );
937        exec( &top );
938        tr_bencFree( &top );
939    }
940}
941
942void
943Session :: reannounceTorrents( const QSet<int>& ids )
944{
945    if( !ids.isEmpty( ) )
946    {
947        tr_benc top, *args;
948        tr_bencInitDict( &top, 2 );
949        tr_bencDictAddStr( &top, "method", "torrent-reannounce" );
950        args = tr_bencDictAddDict( &top, "arguments", 1 );
951        addOptionalIds( args, ids );
952        exec( &top );
953        tr_bencFree( &top );
954    }
955}
956
957/***
958****
959***/
960
961void
962Session :: launchWebInterface( )
963{
964    QUrl url;
965    if( !mySession ) // remote session
966        url = myUrl;
967    else { // local session
968        url.setScheme( "http" );
969        url.setHost( "localhost" );
970        url.setPort( myPrefs.getInt( Prefs::RPC_PORT ) );
971    }
972    QDesktopServices :: openUrl( url );
973}
Note: See TracBrowser for help on using the repository browser.