2009-04-09 18:55:47 +00:00
/*
2010-01-04 21:00:47 +00:00
* This file Copyright ( C ) 2009 - 2010 Mnemosyne LLC
2009-04-09 18:55:47 +00:00
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2 ( b )
* so that the bulk of its code can remain under the MIT license .
* This exemption does not extend to derived works not owned by
* the Transmission project .
*
2009-05-31 19:33:48 +00:00
* $ Id $
2009-04-09 18:55:47 +00:00
*/
# include <cassert>
# include <ctime>
# include <iostream>
2010-04-27 03:10:32 +00:00
# include <QDBusConnection>
# include <QDBusError>
# include <QDBusMessage>
2009-10-02 22:53:19 +00:00
# include <QDialogButtonBox>
2009-04-09 18:55:47 +00:00
# include <QIcon>
2009-10-02 22:53:19 +00:00
# include <QLabel>
2009-10-06 00:27:26 +00:00
# include <QLibraryInfo>
2009-04-09 18:55:47 +00:00
# include <QRect>
# include <QTranslator>
# include <libtransmission/transmission.h>
# include <libtransmission/tr-getopt.h>
2010-06-22 04:34:16 +00:00
# include <libtransmission/utils.h>
2009-04-13 19:04:21 +00:00
# include <libtransmission/version.h>
2009-04-09 18:55:47 +00:00
# include "app.h"
2010-04-27 03:10:32 +00:00
# include "dbus-adaptor.h"
2009-04-09 18:55:47 +00:00
# include "mainwin.h"
# include "options.h"
# include "prefs.h"
# include "session.h"
2009-05-03 17:37:39 +00:00
# include "session-dialog.h"
# include "torrent-model.h"
2010-07-03 00:25:22 +00:00
# include "units.h"
2009-04-09 18:55:47 +00:00
# include "utils.h"
# include "watchdir.h"
namespace
{
2010-04-27 03:10:32 +00:00
const char * DBUS_SERVICE ( " com.transmissionbt.Transmission " ) ;
const char * DBUS_OBJECT_PATH ( " /com/transmissionbt/Transmission " ) ;
const char * DBUS_INTERFACE ( " com.transmissionbt.Transmission " ) ;
2009-04-09 18:55:47 +00:00
const char * MY_NAME ( " transmission " ) ;
const tr_option opts [ ] =
{
{ ' g ' , " config-dir " , " Where to look for configuration files " , " g " , 1 , " <path> " } ,
{ ' m ' , " minimized " , " Start minimized in system tray " , " m " , 0 , NULL } ,
2009-10-05 21:33:39 +00:00
{ ' p ' , " port " , " Port to use when connecting to an existing session " , " p " , 1 , " <port> " } ,
{ ' r ' , " remote " , " Connect to an existing session at the specified hostname " , " r " , 1 , " <host> " } ,
2010-06-05 00:02:20 +00:00
{ ' u ' , " username " , " Username to use when connecting to an existing session " , " u " , 1 , " <username> " } ,
2009-04-09 18:55:47 +00:00
{ ' v ' , " version " , " Show version number and exit " , " v " , 0 , NULL } ,
2009-10-05 21:33:39 +00:00
{ ' w ' , " password " , " Password to use when connecting to an existing session " , " w " , 1 , " <password> " } ,
2009-04-09 18:55:47 +00:00
{ 0 , NULL , NULL , NULL , 0 , NULL }
} ;
const char *
getUsage ( void )
{
2009-09-26 04:11:00 +00:00
return " Usage: \n "
" transmission [OPTIONS...] [torrent files] " ;
2009-04-09 18:55:47 +00:00
}
void
showUsage ( void )
{
tr_getopt_usage ( MY_NAME , getUsage ( ) , opts ) ;
exit ( 0 ) ;
}
enum
{
STATS_REFRESH_INTERVAL_MSEC = 3000 ,
SESSION_REFRESH_INTERVAL_MSEC = 3000 ,
MODEL_REFRESH_INTERVAL_MSEC = 3000
} ;
}
MyApp : : MyApp ( int & argc , char * * argv ) :
QApplication ( argc , argv ) ,
myLastFullUpdateTime ( 0 )
{
setApplicationName ( MY_NAME ) ;
// install the qt translator
QTranslator * t = new QTranslator ( ) ;
t - > load ( " qt_ " + QLocale : : system ( ) . name ( ) , QLibraryInfo : : location ( QLibraryInfo : : TranslationsPath ) ) ;
installTranslator ( t ) ;
// install the transmission translator
t = new QTranslator ( ) ;
t - > load ( QString ( MY_NAME ) + " _ " + QLocale : : system ( ) . name ( ) ) ;
installTranslator ( t ) ;
2010-06-22 04:34:16 +00:00
// initialize the units formatter
2010-07-03 00:25:22 +00:00
tr_formatter_mem_init ( Units : : mem_K ,
qPrintable ( Units : : mem_B_str ) ,
qPrintable ( Units : : mem_K_str ) ,
qPrintable ( Units : : mem_M_str ) ,
qPrintable ( Units : : mem_G_str ) ) ;
tr_formatter_size_init ( Units : : size_K ,
qPrintable ( Units : : size_B_str ) ,
qPrintable ( Units : : size_K_str ) ,
qPrintable ( Units : : size_M_str ) ,
qPrintable ( Units : : size_G_str ) ) ;
tr_formatter_speed_init ( Units : : speed_K ,
qPrintable ( Units : : speed_B_str ) ,
qPrintable ( Units : : speed_K_str ) ,
qPrintable ( Units : : speed_M_str ) ,
qPrintable ( Units : : speed_G_str ) ) ;
2010-06-22 04:34:16 +00:00
2009-04-09 18:55:47 +00:00
// set the default icon
QIcon icon ;
icon . addPixmap ( QPixmap ( " :/icons/transmission-16.png " ) ) ;
icon . addPixmap ( QPixmap ( " :/icons/transmission-22.png " ) ) ;
icon . addPixmap ( QPixmap ( " :/icons/transmission-24.png " ) ) ;
icon . addPixmap ( QPixmap ( " :/icons/transmission-32.png " ) ) ;
icon . addPixmap ( QPixmap ( " :/icons/transmission-48.png " ) ) ;
setWindowIcon ( icon ) ;
// parse the command-line arguments
int c ;
bool minimized = false ;
const char * optarg ;
2009-10-05 21:33:39 +00:00
const char * host = 0 ;
const char * port = 0 ;
const char * username = 0 ;
const char * password = 0 ;
2009-04-09 18:55:47 +00:00
const char * configDir = 0 ;
2009-09-26 04:11:00 +00:00
QStringList filenames ;
2009-04-09 18:55:47 +00:00
while ( ( c = tr_getopt ( getUsage ( ) , argc , ( const char * * ) argv , opts , & optarg ) ) ) {
switch ( c ) {
case ' g ' : configDir = optarg ; break ;
2009-10-05 21:33:39 +00:00
case ' p ' : port = optarg ; break ;
case ' r ' : host = optarg ; break ;
case ' u ' : username = optarg ; break ;
case ' w ' : password = optarg ; break ;
2009-04-09 18:55:47 +00:00
case ' m ' : minimized = true ; break ;
2010-06-05 10:59:24 +00:00
case ' v ' : Utils : : toStderr ( QObject : : tr ( " transmission %1 " ) . arg ( LONG_VERSION_STRING ) ) ; : : exit ( 0 ) ; break ;
2009-04-09 18:55:47 +00:00
case TR_OPT_ERR : Utils : : toStderr ( QObject : : tr ( " Invalid option " ) ) ; showUsage ( ) ; break ;
2009-09-26 04:11:00 +00:00
default : filenames . append ( optarg ) ; break ;
2009-04-09 18:55:47 +00:00
}
}
// set the fallback config dir
if ( configDir = = 0 )
configDir = tr_getDefaultConfigDir ( MY_NAME ) ;
2009-05-03 17:37:39 +00:00
// is this the first time we've run transmission?
const bool firstTime = ! QFile ( QDir ( configDir ) . absoluteFilePath ( " settings.json " ) ) . exists ( ) ;
2009-10-05 21:33:39 +00:00
// initialize the prefs
2009-04-09 18:55:47 +00:00
myPrefs = new Prefs ( configDir ) ;
2009-10-05 21:33:39 +00:00
if ( host ! = 0 )
myPrefs - > set ( Prefs : : SESSION_REMOTE_HOST , host ) ;
if ( port ! = 0 )
myPrefs - > set ( Prefs : : SESSION_REMOTE_PORT , port ) ;
if ( username ! = 0 )
myPrefs - > set ( Prefs : : SESSION_REMOTE_USERNAME , username ) ;
if ( password ! = 0 )
2010-04-16 20:17:52 +00:00
myPrefs - > set ( Prefs : : SESSION_REMOTE_PASSWORD , password ) ;
2009-10-05 21:33:39 +00:00
if ( ( host ! = 0 ) | | ( port ! = 0 ) | | ( username ! = 0 ) | | ( password ! = 0 ) )
myPrefs - > set ( Prefs : : SESSION_IS_REMOTE , true ) ;
2009-05-03 17:37:39 +00:00
mySession = new Session ( configDir , * myPrefs ) ;
2009-04-09 18:55:47 +00:00
myModel = new TorrentModel ( * myPrefs ) ;
myWindow = new TrMainWindow ( * mySession , * myPrefs , * myModel , minimized ) ;
myWatchDir = new WatchDir ( * myModel ) ;
2009-05-03 17:37:39 +00:00
// when the session gets torrent info, update the model
2009-04-09 18:55:47 +00:00
connect ( mySession , SIGNAL ( torrentsUpdated ( tr_benc * , bool ) ) , myModel , SLOT ( updateTorrents ( tr_benc * , bool ) ) ) ;
connect ( mySession , SIGNAL ( torrentsUpdated ( tr_benc * , bool ) ) , myWindow , SLOT ( refreshActionSensitivity ( ) ) ) ;
connect ( mySession , SIGNAL ( torrentsRemoved ( tr_benc * ) ) , myModel , SLOT ( removeTorrents ( tr_benc * ) ) ) ;
2010-06-16 03:11:10 +00:00
// when the session source gets changed, request a full refresh
connect ( mySession , SIGNAL ( sourceChanged ( ) ) , this , SLOT ( onSessionSourceChanged ( ) ) ) ;
2009-05-03 17:37:39 +00:00
// when the model sees a torrent for the first time, ask the session for full info on it
2009-04-09 18:55:47 +00:00
connect ( myModel , SIGNAL ( torrentsAdded ( QSet < int > ) ) , mySession , SLOT ( initTorrents ( QSet < int > ) ) ) ;
mySession - > initTorrents ( ) ;
mySession - > refreshSessionStats ( ) ;
2009-05-03 17:37:39 +00:00
// when torrents are added to the watch directory, tell the session
2009-04-09 18:55:47 +00:00
connect ( myWatchDir , SIGNAL ( torrentFileAdded ( QString ) ) , this , SLOT ( addTorrent ( QString ) ) ) ;
2009-05-03 17:37:39 +00:00
// init from preferences
2009-04-09 18:55:47 +00:00
QList < int > initKeys ;
initKeys < < Prefs : : DIR_WATCH ;
foreach ( int key , initKeys )
refreshPref ( key ) ;
connect ( myPrefs , SIGNAL ( changed ( int ) ) , this , SLOT ( refreshPref ( const int ) ) ) ;
QTimer * timer = & myModelTimer ;
connect ( timer , SIGNAL ( timeout ( ) ) , this , SLOT ( refreshTorrents ( ) ) ) ;
timer - > setSingleShot ( false ) ;
timer - > setInterval ( MODEL_REFRESH_INTERVAL_MSEC ) ;
timer - > start ( ) ;
timer = & myStatsTimer ;
connect ( timer , SIGNAL ( timeout ( ) ) , mySession , SLOT ( refreshSessionStats ( ) ) ) ;
timer - > setSingleShot ( false ) ;
timer - > setInterval ( STATS_REFRESH_INTERVAL_MSEC ) ;
timer - > start ( ) ;
timer = & mySessionTimer ;
connect ( timer , SIGNAL ( timeout ( ) ) , mySession , SLOT ( refreshSessionInfo ( ) ) ) ;
timer - > setSingleShot ( false ) ;
timer - > setInterval ( SESSION_REFRESH_INTERVAL_MSEC ) ;
timer - > start ( ) ;
maybeUpdateBlocklist ( ) ;
2009-05-03 17:37:39 +00:00
if ( ! firstTime )
mySession - > restart ( ) ;
else {
QDialog * d = new SessionDialog ( * mySession , * myPrefs , myWindow ) ;
d - > show ( ) ;
}
2009-09-26 04:11:00 +00:00
2009-10-02 22:53:19 +00:00
if ( ! myPrefs - > getBool ( Prefs : : USER_HAS_GIVEN_INFORMED_CONSENT ) )
{
QDialog * dialog = new QDialog ( myWindow ) ;
dialog - > setModal ( true ) ;
QVBoxLayout * v = new QVBoxLayout ( dialog ) ;
2010-05-01 02:33:44 +00:00
QLabel * l = new QLabel ( tr ( " Transmission is a file-sharing program. When you run a torrent, its data will be made available to others by means of upload. You and you alone are fully responsible for exercising proper judgement and abiding by your local laws. " ) ) ;
2009-10-02 22:53:19 +00:00
l - > setWordWrap ( true ) ;
v - > addWidget ( l ) ;
QDialogButtonBox * box = new QDialogButtonBox ;
box - > addButton ( new QPushButton ( tr ( " &Cancel " ) ) , QDialogButtonBox : : RejectRole ) ;
QPushButton * agree = new QPushButton ( tr ( " I &Agree " ) ) ;
agree - > setDefault ( true ) ;
box - > addButton ( agree , QDialogButtonBox : : AcceptRole ) ;
box - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Fixed ) ;
box - > setOrientation ( Qt : : Horizontal ) ;
v - > addWidget ( box ) ;
connect ( box , SIGNAL ( rejected ( ) ) , this , SLOT ( quit ( ) ) ) ;
connect ( box , SIGNAL ( accepted ( ) ) , dialog , SLOT ( deleteLater ( ) ) ) ;
connect ( box , SIGNAL ( accepted ( ) ) , this , SLOT ( consentGiven ( ) ) ) ;
dialog - > show ( ) ;
}
2009-09-26 04:11:00 +00:00
for ( QStringList : : const_iterator it = filenames . begin ( ) , end = filenames . end ( ) ; it ! = end ; + + it )
2010-05-13 23:54:32 +00:00
addTorrent ( * it ) ;
2010-04-27 03:10:32 +00:00
// register as the dbus handler for Transmission
new TrDBusAdaptor ( this ) ;
QDBusConnection bus = QDBusConnection : : sessionBus ( ) ;
if ( ! bus . registerService ( " com.transmissionbt.Transmission " ) )
fprintf ( stderr , " %s \n " , qPrintable ( bus . lastError ( ) . message ( ) ) ) ;
if ( ! bus . registerObject ( " /com/transmissionbt/Transmission " , this ) )
fprintf ( stderr , " %s \n " , qPrintable ( bus . lastError ( ) . message ( ) ) ) ;
2009-04-09 18:55:47 +00:00
}
2009-10-02 22:53:19 +00:00
void
MyApp : : consentGiven ( )
{
myPrefs - > set < bool > ( Prefs : : USER_HAS_GIVEN_INFORMED_CONSENT , true ) ;
}
2009-04-09 18:55:47 +00:00
MyApp : : ~ MyApp ( )
{
const QRect mainwinRect ( myWindow - > geometry ( ) ) ;
delete myWatchDir ;
delete myWindow ;
delete myModel ;
delete mySession ;
myPrefs - > set ( Prefs : : MAIN_WINDOW_HEIGHT , std : : max ( 100 , mainwinRect . height ( ) ) ) ;
myPrefs - > set ( Prefs : : MAIN_WINDOW_WIDTH , std : : max ( 100 , mainwinRect . width ( ) ) ) ;
myPrefs - > set ( Prefs : : MAIN_WINDOW_X , mainwinRect . x ( ) ) ;
myPrefs - > set ( Prefs : : MAIN_WINDOW_Y , mainwinRect . y ( ) ) ;
delete myPrefs ;
}
/***
* * * *
* * */
void
MyApp : : refreshPref ( int key )
{
switch ( key )
{
case Prefs : : BLOCKLIST_UPDATES_ENABLED :
maybeUpdateBlocklist ( ) ;
break ;
case Prefs : : DIR_WATCH :
case Prefs : : DIR_WATCH_ENABLED : {
const QString path ( myPrefs - > getString ( Prefs : : DIR_WATCH ) ) ;
const bool isEnabled ( myPrefs - > getBool ( Prefs : : DIR_WATCH_ENABLED ) ) ;
myWatchDir - > setPath ( path , isEnabled ) ;
break ;
}
default :
break ;
}
}
void
MyApp : : maybeUpdateBlocklist ( )
{
if ( ! myPrefs - > getBool ( Prefs : : BLOCKLIST_UPDATES_ENABLED ) )
return ;
const QDateTime lastUpdatedAt = myPrefs - > getDateTime ( Prefs : : BLOCKLIST_DATE ) ;
const QDateTime nextUpdateAt = lastUpdatedAt . addDays ( 7 ) ;
const QDateTime now = QDateTime : : currentDateTime ( ) ;
if ( now < nextUpdateAt )
{
mySession - > updateBlocklist ( ) ;
myPrefs - > set ( Prefs : : BLOCKLIST_DATE , now ) ;
}
}
2010-06-16 03:11:10 +00:00
void
MyApp : : onSessionSourceChanged ( )
{
mySession - > initTorrents ( ) ;
mySession - > refreshSessionStats ( ) ;
mySession - > refreshSessionInfo ( ) ;
}
2009-04-09 18:55:47 +00:00
void
MyApp : : refreshTorrents ( )
{
// usually we just poll the torrents that have shown recent activity,
// but we also periodically ask for updates on the others to ensure
// nothing's falling through the cracks.
const time_t now = time ( NULL ) ;
if ( myLastFullUpdateTime + 60 > = now )
mySession - > refreshActiveTorrents ( ) ;
else {
myLastFullUpdateTime = now ;
mySession - > refreshAllTorrents ( ) ;
}
}
2010-04-27 03:10:32 +00:00
/***
* * * *
* * */
2009-04-09 18:55:47 +00:00
void
2010-04-27 03:10:32 +00:00
MyApp : : addTorrent ( const QString & key )
2009-04-09 18:55:47 +00:00
{
2010-04-27 03:10:32 +00:00
if ( ! myPrefs - > getBool ( Prefs : : OPTIONS_PROMPT ) )
2010-05-13 23:54:32 +00:00
{
2010-04-27 03:10:32 +00:00
mySession - > addTorrent ( key ) ;
2010-05-13 23:54:32 +00:00
}
else if ( Utils : : isMagnetLink ( key ) | | QFile ( key ) . exists ( ) )
{
2010-04-27 03:10:32 +00:00
Options * o = new Options ( * mySession , * myPrefs , key , myWindow ) ;
2009-04-09 18:55:47 +00:00
o - > show ( ) ;
}
2010-05-13 23:54:32 +00:00
else if ( Utils : : isURL ( key ) )
{
myWindow - > openURL ( key ) ;
}
2010-04-27 03:10:32 +00:00
raise ( ) ;
2009-04-09 18:55:47 +00:00
}
2010-04-27 03:10:32 +00:00
void
MyApp : : raise ( )
{
QApplication : : alert ( myWindow ) ;
}
2009-04-09 18:55:47 +00:00
/***
* * * *
* * */
int
main ( int argc , char * argv [ ] )
{
2010-04-27 03:10:32 +00:00
// find .torrents, URLs, magnet links, etc in the command-line args
int c ;
QStringList addme ;
const char * optarg ;
char * * argvv = argv ;
while ( ( c = tr_getopt ( getUsage ( ) , argc , ( const char * * ) argvv , opts , & optarg ) ) )
if ( c = = TR_OPT_UNK )
addme . append ( optarg ) ;
// try to delegate the work to an existing copy of Transmission
// before starting ourselves...
bool delegated = false ;
QDBusConnection bus = QDBusConnection : : sessionBus ( ) ;
for ( int i = 0 , n = addme . size ( ) ; i < n ; + + i )
{
const QString key = addme [ i ] ;
QDBusMessage request = QDBusMessage : : createMethodCall ( DBUS_SERVICE ,
DBUS_OBJECT_PATH ,
DBUS_INTERFACE ,
" AddMetainfo " ) ;
QList < QVariant > arguments ;
arguments . push_back ( QVariant ( key ) ) ;
request . setArguments ( arguments ) ;
QDBusMessage response = bus . call ( request ) ;
arguments = response . arguments ( ) ;
delegated | = ( arguments . size ( ) = = 1 ) & & arguments [ 0 ] . toBool ( ) ;
}
if ( delegated )
return 0 ;
tr_optind = 1 ;
2009-04-09 18:55:47 +00:00
MyApp app ( argc , argv ) ;
return app . exec ( ) ;
}