source: trunk/qt/session.cc @ 11209

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

switch trackerRemove and trackerReplace rpc calls to use tracker id instead of announce urls as identifiers

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