source: trunk/qt/session.cc @ 13799

Last change on this file since 13799 was 13799, checked in by jordan, 8 years ago

(qt) #5219 'QPrintable and non Latin-1 symbols on Ubuntu' -- clean fix by rb07

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