source: trunk/qt/session.cc @ 11368

Last change on this file since 11368 was 11368, checked in by charles, 11 years ago

(trunk) #3697 "make blocklist URL configurable" -- implemented in GTK+, Qt, and RPC

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