source: trunk/qt/session.cc @ 11109

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

(trunk qt) #3481 "crash when accessing a remote session that's password protected" -- fixed

  • Property svn:keywords set to Date Rev Author Id
File size: 30.3 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 11109 2010-08-04 13:56:52Z 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 "qticonloader.h"
39#include "session.h"
40#include "session-dialog.h"
41#include "torrent.h"
42#include "utils.h"
43
44// #define DEBUG_HTTP
45
46namespace
47{
48    enum
49    {
50        TAG_SOME_TORRENTS,
51        TAG_ALL_TORRENTS,
52        TAG_SESSION_STATS,
53        TAG_SESSION_INFO,
54        TAG_BLOCKLIST_UPDATE,
55        TAG_ADD_TORRENT,
56        TAG_PORT_TEST,
57        TAG_MAGNET_LINK,
58
59        FIRST_UNIQUE_TAG
60    };
61}
62
63/***
64****
65***/
66
67namespace
68{
69    typedef Torrent::KeyList KeyList;
70    const KeyList& getInfoKeys( ) { return Torrent::getInfoKeys( ); }
71    const KeyList& getStatKeys( ) { return Torrent::getStatKeys( ); }
72    const KeyList& getExtraStatKeys( ) { return Torrent::getExtraStatKeys( ); }
73
74    void
75    addList( tr_benc * list, const KeyList& strings )
76    {
77        tr_bencListReserve( list, strings.size( ) );
78        foreach( const char * str, strings )
79            tr_bencListAddStr( list, str );
80    }
81}
82
83/***
84****
85***/
86
87void
88Session :: sessionSet( const char * key, const QVariant& value )
89{
90    tr_benc top;
91    tr_bencInitDict( &top, 2 );
92    tr_bencDictAddStr( &top, "method", "session-set" );
93    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 1 ) );
94    switch( value.type( ) ) {
95        case QVariant::Bool:   tr_bencDictAddBool ( args, key, value.toBool() ); break;
96        case QVariant::Int:    tr_bencDictAddInt  ( args, key, value.toInt() ); break;
97        case QVariant::Double: tr_bencDictAddReal ( args, key, value.toDouble() ); break;
98        case QVariant::String: tr_bencDictAddStr  ( args, key, value.toString().toUtf8() ); break;
99        default: assert( "unknown type" );
100    }
101    exec( &top );
102    tr_bencFree( &top );
103}
104
105void
106Session :: portTest( )
107{
108    tr_benc top;
109    tr_bencInitDict( &top, 2 );
110    tr_bencDictAddStr( &top, "method", "port-test" );
111    tr_bencDictAddInt( &top, "tag", TAG_PORT_TEST );
112    exec( &top );
113    tr_bencFree( &top );
114}
115
116void
117Session :: copyMagnetLinkToClipboard( int torrentId )
118{
119    tr_benc top;
120    tr_bencInitDict( &top, 3 );
121    tr_bencDictAddStr( &top, "method", "torrent-get" );
122    tr_bencDictAddInt( &top, "tag", TAG_MAGNET_LINK );
123    tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
124    tr_bencListAddInt( tr_bencDictAddList( args, "ids", 1 ), torrentId );
125    tr_bencListAddStr( tr_bencDictAddList( args, "fields", 1 ), "magnetLink" );
126
127    exec( &top );
128    tr_bencFree( &top );
129}
130
131void
132Session :: updatePref( int key )
133{
134    if( myPrefs.isCore( key ) ) switch( key )
135    {
136        case Prefs :: ALT_SPEED_LIMIT_DOWN:
137        case Prefs :: ALT_SPEED_LIMIT_ENABLED:
138        case Prefs :: ALT_SPEED_LIMIT_TIME_BEGIN:
139        case Prefs :: ALT_SPEED_LIMIT_TIME_DAY:
140        case Prefs :: ALT_SPEED_LIMIT_TIME_ENABLED:
141        case Prefs :: ALT_SPEED_LIMIT_TIME_END:
142        case Prefs :: ALT_SPEED_LIMIT_UP:
143        case Prefs :: BLOCKLIST_DATE:
144        case Prefs :: BLOCKLIST_ENABLED:
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_sessionSetRPCEnabled( 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 :: torrentSetLocation( const QSet<int>& ids, const QString& location, bool doMove )
457{
458    tr_benc top;
459    tr_bencInitDict( &top, 2 );
460    tr_bencDictAddStr( &top, "method", "torrent-set-location" );
461    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 3 ) );
462    addOptionalIds( args, ids );
463    tr_bencDictAddStr( args, "location", location.toUtf8().constData() );
464    tr_bencDictAddBool( args, "move", doMove );
465    exec( &top );
466    tr_bencFree( &top );
467}
468
469void
470Session :: refreshTorrents( const QSet<int>& ids )
471{
472    if( ids.empty( ) )
473    {
474        refreshAllTorrents( );
475    }
476    else
477    {
478        tr_benc top;
479        tr_bencInitDict( &top, 3 );
480        tr_bencDictAddStr( &top, "method", "torrent-get" );
481        tr_bencDictAddInt( &top, "tag", TAG_SOME_TORRENTS );
482        tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
483        addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) );
484        addOptionalIds( args, ids );
485        exec( &top );
486        tr_bencFree( &top );
487    }
488}
489
490void
491Session :: refreshExtraStats( const QSet<int>& ids )
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    addOptionalIds( args, ids );
499    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) + getExtraStatKeys( ));
500    exec( &top );
501    tr_bencFree( &top );
502}
503
504void
505Session :: sendTorrentRequest( const char * request, const QSet<int>& ids )
506{
507    tr_benc top;
508    tr_benc * args( buildRequest( request, top ) );
509    addOptionalIds( args, ids );
510    exec( &top );
511    tr_bencFree( &top );
512
513    refreshTorrents( ids );
514}
515
516void
517Session :: pauseTorrents( const QSet<int>& ids )
518{
519    sendTorrentRequest( "torrent-stop", ids );
520}
521
522void
523Session :: startTorrents( const QSet<int>& ids )
524{
525    sendTorrentRequest( "torrent-start", ids );
526}
527
528void
529Session :: refreshActiveTorrents( )
530{
531    tr_benc top;
532    tr_bencInitDict( &top, 3 );
533    tr_bencDictAddStr( &top, "method", "torrent-get" );
534    tr_bencDictAddInt( &top, "tag", TAG_SOME_TORRENTS );
535    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
536    tr_bencDictAddStr( args, "ids", "recently-active" );
537    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) );
538    exec( &top );
539    tr_bencFree( &top );
540}
541
542void
543Session :: refreshAllTorrents( )
544{
545    tr_benc top;
546    tr_bencInitDict( &top, 3 );
547    tr_bencDictAddStr( &top, "method", "torrent-get" );
548    tr_bencDictAddInt( &top, "tag", TAG_ALL_TORRENTS );
549    tr_benc * args( tr_bencDictAddDict( &top, "arguments", 1 ) );
550    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) );
551    exec( &top );
552    tr_bencFree( &top );
553}
554
555void
556Session :: initTorrents( const QSet<int>& ids )
557{
558    tr_benc top;
559    const int tag( ids.isEmpty() ? TAG_ALL_TORRENTS : TAG_SOME_TORRENTS );
560    tr_benc * args( buildRequest( "torrent-get", top, tag ) );
561    addOptionalIds( args, ids );
562    addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys()+getInfoKeys() );
563    exec( &top );
564    tr_bencFree( &top );
565}
566
567void
568Session :: refreshSessionStats( )
569{
570    tr_benc top;
571    tr_bencInitDict( &top, 2 );
572    tr_bencDictAddStr( &top, "method", "session-stats" );
573    tr_bencDictAddInt( &top, "tag", TAG_SESSION_STATS );
574    exec( &top );
575    tr_bencFree( &top );
576}
577
578void
579Session :: refreshSessionInfo( )
580{
581    tr_benc top;
582    tr_bencInitDict( &top, 2 );
583    tr_bencDictAddStr( &top, "method", "session-get" );
584    tr_bencDictAddInt( &top, "tag", TAG_SESSION_INFO );
585    exec( &top );
586    tr_bencFree( &top );
587}
588
589void
590Session :: updateBlocklist( )
591{
592    tr_benc top;
593    tr_bencInitDict( &top, 2 );
594    tr_bencDictAddStr( &top, "method", "blocklist-update" );
595    tr_bencDictAddInt( &top, "tag", TAG_BLOCKLIST_UPDATE );
596    exec( &top );
597    tr_bencFree( &top );
598}
599
600/***
601****
602***/
603
604void
605Session :: exec( const tr_benc * request )
606{
607    char * str = tr_bencToStr( request, TR_FMT_JSON_LEAN, NULL );
608    exec( str );
609    tr_free( str );
610}
611
612void
613Session :: localSessionCallback( tr_session * session, const char * json, size_t len, void * self )
614{
615    Q_UNUSED( session );
616
617    ((Session*)self)->parseResponse( json, len );
618}
619
620#define REQUEST_DATA_PROPERTY_KEY "requestData"
621
622void
623Session :: exec( const char * json )
624{
625    if( mySession  )
626    {
627        tr_rpc_request_exec_json( mySession, json, strlen( json ), localSessionCallback, this );
628    }
629    else if( !myUrl.isEmpty( ) )
630    {
631        QNetworkRequest request;
632        request.setUrl( myUrl );
633        request.setRawHeader( "User-Agent", QString( QCoreApplication::instance()->applicationName() + "/" + LONG_VERSION_STRING ).toAscii() );
634        request.setRawHeader( "Content-Type", "application/json; charset=UTF-8" );
635        if( !mySessionId.isEmpty( ) )
636            request.setRawHeader( TR_RPC_SESSION_ID_HEADER, mySessionId.toAscii() );
637
638        const QByteArray requestData( json );
639        QNetworkReply * reply = networkAccessManager()->post( request, requestData );
640        reply->setProperty( REQUEST_DATA_PROPERTY_KEY, requestData );
641        connect( reply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(dataReadProgress()));
642        connect( reply, SIGNAL(uploadProgress(qint64,qint64)), this, SIGNAL(dataSendProgress()));
643
644#ifdef DEBUG_HTTP
645        std::cerr << "sending " << "POST " << qPrintable( myUrl.path() ) << std::endl;
646        foreach( QByteArray b, request.rawHeaderList() )
647            std::cerr << b.constData()
648                      << ": "
649                      << request.rawHeader( b ).constData()
650                      << std::endl;
651        std::cerr << "Body:\n" << json << std::endl;
652#endif
653    }
654}
655
656void
657Session :: onFinished( QNetworkReply * reply )
658{
659#ifdef DEBUG_HTTP
660    std::cerr << "http response header: " << std::endl;
661    foreach( QByteArray b, reply->rawHeaderList() )
662        std::cerr << b.constData()
663                  << ": "
664                  << reply->rawHeader( b ).constData()
665                  << std::endl;
666    std::cerr << "json:\n" << reply->peek( reply->bytesAvailable() ).constData() << std::endl;
667#endif
668
669    if( ( reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 409 )
670        && ( reply->hasRawHeader( TR_RPC_SESSION_ID_HEADER ) ) )
671    {
672        // we got a 409 telling us our session id has expired.
673        // update it and resubmit the request.
674        mySessionId = QString( reply->rawHeader( TR_RPC_SESSION_ID_HEADER ) );
675        exec( reply->property( REQUEST_DATA_PROPERTY_KEY ).toByteArray( ).constData( ) );
676    }
677    else if( reply->error() != QNetworkReply::NoError )
678    {
679        std::cerr << "http error: " << qPrintable( reply->errorString() ) << std::endl;
680    }
681    else
682    {
683        const QByteArray response( reply->readAll() );
684        const char * json( response.constData( ) );
685        int jsonLength( response.size( ) );
686        if( jsonLength>0 && json[jsonLength-1] == '\n' ) --jsonLength;
687        parseResponse( json, jsonLength );
688    }
689
690    reply->deleteLater();
691}
692
693void
694Session :: parseResponse( const char * json, size_t jsonLength )
695{
696    tr_benc top;
697    const uint8_t * end( 0 );
698    const int err( tr_jsonParse( "rpc", json, jsonLength, &top, &end ) );
699    if( !err )
700    {
701        int64_t tag = -1;
702        const char * result = NULL;
703        tr_benc * args = NULL;
704
705        tr_bencDictFindInt ( &top, "tag", &tag );
706        tr_bencDictFindStr ( &top, "result", &result );
707        tr_bencDictFindDict( &top, "arguments", &args );
708
709        emit executed( tag, result, args );
710
711        tr_benc * torrents;
712        const char * str;
713
714        if( tr_bencDictFindInt( &top, "tag", &tag ) )
715        {
716            switch( tag )
717            {
718                case TAG_SOME_TORRENTS:
719                case TAG_ALL_TORRENTS:
720                    if( tr_bencDictFindDict( &top, "arguments", &args ) ) {
721                        if( tr_bencDictFindList( args, "torrents", &torrents ) )
722                            emit torrentsUpdated( torrents, tag==TAG_ALL_TORRENTS );
723                        if( tr_bencDictFindList( args, "removed", &torrents ) )
724                            emit torrentsRemoved( torrents );
725                    }
726                    break;
727
728                case TAG_SESSION_STATS:
729                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
730                        updateStats( args );
731                    break;
732
733                case TAG_SESSION_INFO:
734                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
735                        updateInfo( args );
736                    break;
737
738                case TAG_BLOCKLIST_UPDATE: {
739                    int64_t intVal = 0;
740                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
741                        if( tr_bencDictFindInt( args, "blocklist-size", &intVal ) )
742                            setBlocklistSize( intVal );
743                    break;
744                }
745
746                case TAG_PORT_TEST: {
747                    tr_bool isOpen = 0;
748                    if( tr_bencDictFindDict( &top, "arguments", &args ) )
749                        tr_bencDictFindBool( args, "port-is-open", &isOpen );
750                    emit portTested( (bool)isOpen );
751                }
752
753                case TAG_MAGNET_LINK: {
754                    tr_benc * args;
755                    tr_benc * torrents;
756                    tr_benc * child;
757                    const char * str;
758                    if( tr_bencDictFindDict( &top, "arguments", &args )
759                        && tr_bencDictFindList( args, "torrents", &torrents )
760                        && (( child = tr_bencListChild( torrents, 0 )))
761                        && tr_bencDictFindStr( child, "magnetLink", &str ) )
762                            QApplication::clipboard()->setText( str );
763                    break;
764                }
765
766                case TAG_ADD_TORRENT:
767                    str = "";
768                    if( tr_bencDictFindStr( &top, "result", &str ) && strcmp( str, "success" ) ) {
769                        QMessageBox * d = new QMessageBox( QMessageBox::Information,
770                                                           tr( "Add Torrent" ),
771                                                           QString::fromUtf8(str),
772                                                           QMessageBox::Close,
773                                                           QApplication::activeWindow());
774                        QPixmap pixmap;
775                        QIcon icon = QtIconLoader :: icon( "dialog-information" );
776                        if( !icon.isNull( ) ) {
777                            const int size = QApplication::style()->pixelMetric( QStyle::PM_LargeIconSize );
778                            d->setIconPixmap( icon.pixmap( size, size ) );
779                        }
780                        connect( d, SIGNAL(rejected()), d, SLOT(deleteLater()) );
781                        d->show( );
782                    }
783                    break;
784
785                default:
786                    break;
787            }
788        }
789        tr_bencFree( &top );
790    }
791}
792
793void
794Session :: updateStats( tr_benc * d, struct tr_session_stats * stats )
795{
796    int64_t i;
797
798    if( tr_bencDictFindInt( d, "uploadedBytes", &i ) )
799        stats->uploadedBytes = i;
800    if( tr_bencDictFindInt( d, "downloadedBytes", &i ) )
801        stats->downloadedBytes = i;
802    if( tr_bencDictFindInt( d, "filesAdded", &i ) )
803        stats->filesAdded = i;
804    if( tr_bencDictFindInt( d, "sessionCount", &i ) )
805        stats->sessionCount = i;
806    if( tr_bencDictFindInt( d, "secondsActive", &i ) )
807        stats->secondsActive = i;
808
809    stats->ratio = tr_getRatio( stats->uploadedBytes, stats->downloadedBytes );
810
811}
812
813void
814Session :: updateStats( tr_benc * d )
815{
816    tr_benc * c;
817
818    if( tr_bencDictFindDict( d, "current-stats", &c ) )
819        updateStats( c, &myStats );
820
821    if( tr_bencDictFindDict( d, "cumulative-stats", &c ) )
822        updateStats( c, &myCumulativeStats );
823
824    emit statsUpdated( );
825}
826
827void
828Session :: updateInfo( tr_benc * d )
829{
830    int64_t i;
831    const char * str;
832    disconnect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
833
834    for( int i=Prefs::FIRST_CORE_PREF; i<=Prefs::LAST_CORE_PREF; ++i )
835    {
836        const tr_benc * b( tr_bencDictFind( d, myPrefs.keyStr( i ) ) );
837
838        if( !b )
839            continue;
840
841        if( i == Prefs :: ENCRYPTION )
842        {
843            const char * val;
844            if( tr_bencGetStr( b, &val ) )
845            {
846                if( !qstrcmp( val , "required" ) )
847                    myPrefs.set( i, 2 );
848                else if( !qstrcmp( val , "preferred" ) )
849                    myPrefs.set( i, 1 );
850                else if( !qstrcmp( val , "tolerated" ) )
851                    myPrefs.set( i, 0 );
852            }
853            continue;
854        }
855
856        switch( myPrefs.type( i ) )
857        {
858            case QVariant :: Int: {
859                int64_t val;
860                if( tr_bencGetInt( b, &val ) )
861                    myPrefs.set( i, (int)val );
862                break;
863            }
864            case QVariant :: Double: {
865                double val;
866                if( tr_bencGetReal( b, &val ) )
867                    myPrefs.set( i, val );
868                break;
869            }
870            case QVariant :: Bool: {
871                tr_bool val;
872                if( tr_bencGetBool( b, &val ) )
873                    myPrefs.set( i, (bool)val );
874                break;
875            }
876            case TrTypes :: FilterModeType:
877            case TrTypes :: SortModeType:
878            case QVariant :: String: {
879                const char * val;
880                if( tr_bencGetStr( b, &val ) )
881                    myPrefs.set( i, QString(val) );
882                break;
883            }
884            default:
885                break;
886        }
887    }
888
889    tr_bool b;
890    double x;
891    if( tr_bencDictFindBool( d, "seedRatioLimited", &b ) )
892        myPrefs.set( Prefs::RATIO_ENABLED, b ? true : false );
893    if( tr_bencDictFindReal( d, "seedRatioLimit", &x ) )
894        myPrefs.set( Prefs::RATIO, x );
895
896    /* Use the C API to get settings that, for security reasons, aren't supported by RPC */
897    if( mySession != 0 )
898    {
899        myPrefs.set( Prefs::RPC_ENABLED,           tr_sessionIsRPCEnabled           ( mySession ) );
900        myPrefs.set( Prefs::RPC_AUTH_REQUIRED,     tr_sessionIsRPCPasswordEnabled   ( mySession ) );
901        myPrefs.set( Prefs::RPC_PASSWORD,          tr_sessionGetRPCPassword         ( mySession ) );
902        myPrefs.set( Prefs::RPC_PORT,              tr_sessionGetRPCPort             ( mySession ) );
903        myPrefs.set( Prefs::RPC_USERNAME,          tr_sessionGetRPCUsername         ( mySession ) );
904        myPrefs.set( Prefs::RPC_WHITELIST_ENABLED, tr_sessionGetRPCWhitelistEnabled ( mySession ) );
905        myPrefs.set( Prefs::RPC_WHITELIST,         tr_sessionGetRPCWhitelist        ( mySession ) );
906    }
907
908    if( tr_bencDictFindInt( d, "blocklist-size", &i ) && i!=blocklistSize( ) )
909        setBlocklistSize( i );
910
911    if( tr_bencDictFindStr( d, "version", &str ) && ( mySessionVersion != str ) )
912        mySessionVersion = str;
913
914    //std::cerr << "Session :: updateInfo end" << std::endl;
915    connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
916
917    emit sessionUpdated( );
918}
919
920void
921Session :: setBlocklistSize( int64_t i )
922{
923    myBlocklistSize = i;
924
925    emit blocklistUpdated( i );
926}
927
928void
929Session :: addTorrent( const AddData& addMe )
930{
931    const QByteArray b64 = addMe.toBase64();
932
933    tr_benc top, *args;
934    tr_bencInitDict( &top, 2 );
935    tr_bencDictAddStr( &top, "method", "torrent-add" );
936    args = tr_bencDictAddDict( &top, "arguments", 2 );
937    tr_bencDictAddBool( args, "paused", !myPrefs.getBool( Prefs::START ) );
938    switch( addMe.type ) {
939        case AddData::MAGNET:   tr_bencDictAddStr( args, "filename", addMe.magnet.toUtf8().constData() ); break;
940        case AddData::URL:      tr_bencDictAddStr( args, "filename", addMe.url.toString().toUtf8().constData() ); break;
941        case AddData::FILENAME: /* fall-through */
942        case AddData::METAINFO: tr_bencDictAddRaw( args, "metainfo", b64.constData(), b64.size() ); break;
943        default: std::cerr << "Unhandled AddData type: " << addMe.type << std::endl;
944    }
945    exec( &top );
946    tr_bencFree( &top );
947}
948
949void
950Session :: addNewlyCreatedTorrent( const QString& filename, const QString& localPath )
951{
952    const QByteArray b64 = AddData(filename).toBase64();
953
954    tr_benc top, *args;
955    tr_bencInitDict( &top, 2 );
956    tr_bencDictAddStr( &top, "method", "torrent-add" );
957    args = tr_bencDictAddDict( &top, "arguments", 3 );
958    tr_bencDictAddStr( args, "download-dir", qPrintable(localPath) );
959    tr_bencDictAddBool( args, "paused", !myPrefs.getBool( Prefs::START ) );
960    tr_bencDictAddRaw( args, "metainfo", b64.constData(), b64.size() );
961    exec( &top );
962    tr_bencFree( &top );
963}
964
965void
966Session :: removeTorrents( const QSet<int>& ids, bool deleteFiles )
967{
968    if( !ids.isEmpty( ) )
969    {
970        tr_benc top, *args;
971        tr_bencInitDict( &top, 2 );
972        tr_bencDictAddStr( &top, "method", "torrent-remove" );
973        args = tr_bencDictAddDict( &top, "arguments", 2 );
974        addOptionalIds( args, ids );
975        tr_bencDictAddInt( args, "delete-local-data", deleteFiles );
976        exec( &top );
977        tr_bencFree( &top );
978    }
979}
980
981void
982Session :: verifyTorrents( const QSet<int>& ids )
983{
984    if( !ids.isEmpty( ) )
985    {
986        tr_benc top, *args;
987        tr_bencInitDict( &top, 2 );
988        tr_bencDictAddStr( &top, "method", "torrent-verify" );
989        args = tr_bencDictAddDict( &top, "arguments", 1 );
990        addOptionalIds( args, ids );
991        exec( &top );
992        tr_bencFree( &top );
993    }
994}
995
996void
997Session :: reannounceTorrents( const QSet<int>& ids )
998{
999    if( !ids.isEmpty( ) )
1000    {
1001        tr_benc top, *args;
1002        tr_bencInitDict( &top, 2 );
1003        tr_bencDictAddStr( &top, "method", "torrent-reannounce" );
1004        args = tr_bencDictAddDict( &top, "arguments", 1 );
1005        addOptionalIds( args, ids );
1006        exec( &top );
1007        tr_bencFree( &top );
1008    }
1009}
1010
1011/***
1012****
1013***/
1014
1015void
1016Session :: launchWebInterface( )
1017{
1018    QUrl url;
1019    if( !mySession ) // remote session
1020        url = myUrl;
1021    else { // local session
1022        url.setScheme( "http" );
1023        url.setHost( "localhost" );
1024        url.setPort( myPrefs.getInt( Prefs::RPC_PORT ) );
1025    }
1026    QDesktopServices :: openUrl( url );
1027}
Note: See TracBrowser for help on using the repository browser.