~ubuntu-branches/ubuntu/precise/transmission/precise

« back to all changes in this revision

Viewing changes to gtk/tr-core.c

  • Committer: Bazaar Package Importer
  • Author(s): Leo Costela
  • Date: 2009-05-17 19:39:51 UTC
  • mto: (1.3.4 upstream) (2.2.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 36.
  • Revision ID: james.westby@ubuntu.com-20090517193951-k8x15sqoxzf7cuyx
ImportĀ upstreamĀ versionĀ 1.61

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/******************************************************************************
2
 
 * $Id: tr-core.c 5673 2008-04-24 01:42:53Z charles $
 
2
 * $Id: tr-core.c 8279 2009-04-24 01:37:04Z charles $
3
3
 *
4
4
 * Copyright (c) 2007-2008 Transmission authors and contributors
5
5
 *
27
27
#include <gtk/gtk.h>
28
28
#include <glib/gi18n.h>
29
29
#ifdef HAVE_GIO
30
 
#include <gio/gio.h>
 
30
 #include <gio/gio.h>
 
31
#endif
 
32
#ifdef HAVE_DBUS_GLIB
 
33
 #include <dbus/dbus-glib.h>
31
34
#endif
32
35
 
33
36
#include <libtransmission/transmission.h>
 
37
#include <libtransmission/bencode.h>
 
38
#include <libtransmission/rpcimpl.h>
 
39
#include <libtransmission/json.h>
34
40
#include <libtransmission/utils.h> /* tr_free */
35
41
 
36
42
#include "conf.h"
37
43
#include "tr-core.h"
 
44
#ifdef HAVE_DBUS_GLIB
 
45
 #include "tr-core-dbus.h"
 
46
#endif
38
47
#include "tr-prefs.h"
39
48
#include "tr-torrent.h"
40
49
#include "util.h"
 
50
#include "actions.h"
 
51
 
 
52
static void     maybeInhibitHibernation( TrCore * core );
 
53
 
 
54
static gboolean our_instance_adds_remote_torrents = FALSE;
41
55
 
42
56
struct TrCorePrivate
43
57
{
44
58
#ifdef HAVE_GIO
45
 
    GFileMonitor     * monitor;
46
 
    gulong             monitor_tag;
47
 
    char             * monitor_path;
48
 
    GSList           * monitor_files;
49
 
    guint              monitor_idle_tag;
 
59
    GFileMonitor *  monitor;
 
60
    gulong          monitor_tag;
 
61
    char *          monitor_path;
 
62
    GSList *        monitor_files;
 
63
    guint           monitor_idle_tag;
50
64
#endif
51
 
    GtkTreeModel     * model;
52
 
    tr_handle        * handle;
53
 
    int                nextid;
 
65
    gboolean        adding_from_watch_dir;
 
66
    gboolean        inhibit_allowed;
 
67
    gboolean        have_inhibit_cookie;
 
68
    gboolean        dbus_error;
 
69
    guint           inhibit_cookie;
 
70
    GtkTreeModel *  model;
 
71
    tr_session *    session;
54
72
};
55
73
 
56
 
static void
57
 
tr_core_marshal_err( GClosure * closure, GValue * ret UNUSED,
58
 
                     guint count, const GValue * vals,
59
 
                     gpointer hint UNUSED, gpointer marshal )
60
 
{
61
 
    typedef void (*TRMarshalErr)
62
 
        ( gpointer, enum tr_core_err, const char *, gpointer );
63
 
    TRMarshalErr     callback;
64
 
    GCClosure      * cclosure = (GCClosure*) closure;
65
 
    enum tr_core_err errcode;
66
 
    const char     * errstr;
67
 
    gpointer         inst, gdata;
68
 
 
69
 
    g_return_if_fail( count == 3 );
70
 
 
71
 
    inst    = g_value_peek_pointer( vals );
72
 
    errcode = g_value_get_int( vals + 1 );
73
 
    errstr  = g_value_get_string( vals + 2 );
74
 
    gdata   = closure->data;
75
 
 
76
 
    callback = (TRMarshalErr)( marshal ? marshal : cclosure->callback );
77
 
    callback( inst, errcode, errstr, gdata );
78
 
}
79
 
 
80
 
static void
81
 
tr_core_marshal_prompt( GClosure * closure, GValue * ret UNUSED,
82
 
                        guint count, const GValue * vals,
83
 
                        gpointer hint UNUSED, gpointer marshal )
84
 
{
85
 
    typedef void (*TRMarshalPrompt)( gpointer, tr_ctor *, gpointer );
86
 
    TRMarshalPrompt        callback;
87
 
    GCClosure            * cclosure = (GCClosure*) closure;
88
 
    gpointer               ctor;
89
 
    gpointer               inst, gdata;
90
 
 
91
 
    g_return_if_fail( count == 2 );
92
 
 
93
 
    inst      = g_value_peek_pointer( vals );
94
 
    ctor      = g_value_peek_pointer( vals + 1 );
95
 
    gdata     = closure->data;
96
 
 
97
 
    callback = (TRMarshalPrompt)( marshal ? marshal : cclosure->callback );
98
 
    callback( inst, ctor, gdata );
99
 
}
100
 
 
101
74
static int
102
75
isDisposed( const TrCore * core )
103
76
{
113
86
    {
114
87
        GObjectClass * parent;
115
88
 
116
 
        pref_save( NULL );
117
89
        core->priv = NULL;
118
90
 
119
91
        parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) );
122
94
}
123
95
 
124
96
static void
125
 
tr_core_class_init( gpointer g_class, gpointer g_class_data UNUSED )
 
97
tr_core_class_init( gpointer              g_class,
 
98
                    gpointer g_class_data UNUSED )
126
99
{
127
100
    GObjectClass * gobject_class;
128
 
    TrCoreClass  * core_class;
 
101
    TrCoreClass *  cc;
129
102
 
130
 
    g_type_class_add_private( g_class, sizeof(struct TrCorePrivate) );
 
103
    g_type_class_add_private( g_class, sizeof( struct TrCorePrivate ) );
131
104
 
132
105
    gobject_class = G_OBJECT_CLASS( g_class );
133
106
    gobject_class->dispose = tr_core_dispose;
134
107
 
135
 
 
136
 
    core_class = TR_CORE_CLASS( g_class );
137
 
    core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ),
138
 
                                       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
139
 
                                       tr_core_marshal_err, G_TYPE_NONE,
140
 
                                       2, G_TYPE_INT, G_TYPE_STRING );
141
 
    core_class->promptsig = g_signal_new( "add-torrent-prompt",
142
 
                                          G_TYPE_FROM_CLASS( g_class ),
143
 
                                          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
144
 
                                          tr_core_marshal_prompt, G_TYPE_NONE,
145
 
                                          1, G_TYPE_POINTER );
146
 
    core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ),
147
 
                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
148
 
                                        g_cclosure_marshal_VOID__VOID,
149
 
                                        G_TYPE_NONE, 0 );
150
 
    core_class->prefsig = g_signal_new( "prefs-changed",
151
 
                                        G_TYPE_FROM_CLASS( g_class ),
152
 
                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
153
 
                                        g_cclosure_marshal_VOID__STRING,
154
 
                                        G_TYPE_NONE, 1, G_TYPE_STRING );
 
108
    cc = TR_CORE_CLASS( g_class );
 
109
 
 
110
    cc->blocklistSignal = g_signal_new( "blocklist-updated",          /* name */
 
111
                                        G_TYPE_FROM_CLASS( g_class ), /* applies to TrCore */
 
112
                                        G_SIGNAL_RUN_FIRST,           /* when to invoke */
 
113
                                        0, NULL, NULL,                /* accumulator */
 
114
                                        g_cclosure_marshal_VOID__INT, /* marshaler */
 
115
                                        G_TYPE_NONE,                  /* return type */
 
116
                                        1, G_TYPE_INT );              /* signal arguments */
 
117
 
 
118
    cc->portSignal = g_signal_new( "port-tested",
 
119
                                   G_TYPE_FROM_CLASS( g_class ),
 
120
                                   G_SIGNAL_RUN_LAST,
 
121
                                   0, NULL, NULL,
 
122
                                   g_cclosure_marshal_VOID__BOOLEAN,
 
123
                                   G_TYPE_NONE,
 
124
                                   1, G_TYPE_BOOLEAN );
 
125
 
 
126
    cc->errsig = g_signal_new( "error",
 
127
                               G_TYPE_FROM_CLASS( g_class ),
 
128
                               G_SIGNAL_RUN_LAST,
 
129
                               0, NULL, NULL,
 
130
                               g_cclosure_marshal_VOID__UINT_POINTER,
 
131
                               G_TYPE_NONE,
 
132
                               2, G_TYPE_UINT, G_TYPE_POINTER );
 
133
 
 
134
    cc->promptsig = g_signal_new( "add-torrent-prompt",
 
135
                                  G_TYPE_FROM_CLASS( g_class ),
 
136
                                  G_SIGNAL_RUN_LAST,
 
137
                                  0, NULL, NULL,
 
138
                                  g_cclosure_marshal_VOID__POINTER,
 
139
                                  G_TYPE_NONE,
 
140
                                  1, G_TYPE_POINTER );
 
141
 
 
142
    cc->quitsig = g_signal_new( "quit",
 
143
                                G_TYPE_FROM_CLASS( g_class ),
 
144
                                G_SIGNAL_RUN_LAST,
 
145
                                0, NULL, NULL,
 
146
                                g_cclosure_marshal_VOID__VOID,
 
147
                                G_TYPE_NONE,
 
148
                                0 );
 
149
 
 
150
    cc->prefsig = g_signal_new( "prefs-changed",
 
151
                                G_TYPE_FROM_CLASS( g_class ),
 
152
                                G_SIGNAL_RUN_LAST,
 
153
                                0, NULL, NULL,
 
154
                                g_cclosure_marshal_VOID__STRING,
 
155
                                G_TYPE_NONE,
 
156
                                1, G_TYPE_STRING );
 
157
 
 
158
#ifdef HAVE_DBUS_GLIB
 
159
    {
 
160
        DBusGConnection * bus = dbus_g_bus_get( DBUS_BUS_SESSION, NULL );
 
161
        DBusGProxy *      bus_proxy = NULL;
 
162
        if( bus )
 
163
            bus_proxy =
 
164
                dbus_g_proxy_new_for_name( bus, "org.freedesktop.DBus",
 
165
                                           "/org/freedesktop/DBus",
 
166
                                           "org.freedesktop.DBus" );
 
167
        if( bus_proxy )
 
168
        {
 
169
            int result = 0;
 
170
            dbus_g_proxy_call( bus_proxy, "RequestName", NULL,
 
171
                               G_TYPE_STRING,
 
172
                               "com.transmissionbt.Transmission",
 
173
                               G_TYPE_UINT, 0,
 
174
                               G_TYPE_INVALID,
 
175
                               G_TYPE_UINT, &result,
 
176
                               G_TYPE_INVALID );
 
177
            if( ( our_instance_adds_remote_torrents = result == 1 ) )
 
178
                dbus_g_object_type_install_info(
 
179
                    TR_CORE_TYPE,
 
180
                    &
 
181
                    dbus_glib_tr_core_object_info );
 
182
        }
 
183
    }
 
184
#endif
155
185
}
156
186
 
157
187
/***
158
188
****  SORTING
159
189
***/
160
190
 
 
191
static gboolean
 
192
isValidETA( int t )
 
193
{
 
194
    return ( t != TR_ETA_NOT_AVAIL ) && ( t != TR_ETA_UNKNOWN );
 
195
}
 
196
 
 
197
static int
 
198
compareETA( int a, int b )
 
199
{
 
200
    const gboolean a_valid = isValidETA( a );
 
201
    const gboolean b_valid = isValidETA( b );
 
202
 
 
203
    if( !a_valid && !b_valid ) return 0;
 
204
    if( !a_valid ) return -1;
 
205
    if( !b_valid ) return 1;
 
206
    return a < b ? 1 : -1;
 
207
}
 
208
 
161
209
static int
162
210
compareDouble( double a, double b )
163
211
{
176
224
}
177
225
 
178
226
static int
179
 
compareByRatio( GtkTreeModel * model,
180
 
                GtkTreeIter  * a,
181
 
                GtkTreeIter  * b,
182
 
                gpointer       user_data UNUSED )
 
227
compareTime( time_t a, time_t b )
 
228
{
 
229
    if( a < b ) return -1;
 
230
    if( a > b ) return 1;
 
231
    return 0;
 
232
}
 
233
 
 
234
static int
 
235
compareByRatio( GtkTreeModel  * model,
 
236
                GtkTreeIter   * a,
 
237
                GtkTreeIter   * b,
 
238
                gpointer        user_data UNUSED )
183
239
{
184
240
    tr_torrent *ta, *tb;
185
241
    const tr_stat *sa, *sb;
194
250
}
195
251
 
196
252
static int
197
 
compareByActivity( GtkTreeModel * model,
198
 
                   GtkTreeIter  * a,
199
 
                   GtkTreeIter  * b,
 
253
compareByActivity( GtkTreeModel *           model,
 
254
                   GtkTreeIter *            a,
 
255
                   GtkTreeIter *            b,
200
256
                   gpointer       user_data UNUSED )
201
257
{
202
 
    int i;
203
 
    tr_torrent *ta, *tb;
 
258
    int            i;
 
259
    tr_torrent *   ta, *tb;
204
260
    const tr_stat *sa, *sb;
205
261
 
206
262
    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
209
265
    sa = tr_torrentStatCached( ta );
210
266
    sb = tr_torrentStatCached( tb );
211
267
 
212
 
    if(( i = compareDouble( sa->rateUpload + sa->rateDownload,
213
 
                            sb->rateUpload + sb->rateDownload ) ))
 
268
    if( ( i = compareDouble( sa->pieceUploadSpeed + sa->pieceDownloadSpeed,
 
269
                             sb->pieceUploadSpeed + sb->pieceDownloadSpeed ) ) )
214
270
        return i;
215
271
 
216
272
    if( sa->uploadedEver != sb->uploadedEver )
220
276
}
221
277
 
222
278
static int
223
 
compareByName( GtkTreeModel   * model,
224
 
               GtkTreeIter    * a,
225
 
               GtkTreeIter    * b,
 
279
compareByName( GtkTreeModel *             model,
 
280
               GtkTreeIter *              a,
 
281
               GtkTreeIter *              b,
226
282
               gpointer         user_data UNUSED )
227
283
{
228
 
    int ret;
 
284
    int   ret;
229
285
    char *ca, *cb;
 
286
 
230
287
    gtk_tree_model_get( model, a, MC_NAME_COLLATED, &ca, -1 );
231
288
    gtk_tree_model_get( model, b, MC_NAME_COLLATED, &cb, -1 );
232
289
    ret = strcmp( ca, cb );
236
293
}
237
294
 
238
295
static int
239
 
compareByProgress( GtkTreeModel   * model,
240
 
                   GtkTreeIter    * a,
241
 
                   GtkTreeIter    * b,
 
296
compareByAge( GtkTreeModel * model,
 
297
              GtkTreeIter  * a,
 
298
              GtkTreeIter  * b,
 
299
              gpointer       user_data UNUSED )
 
300
{
 
301
    tr_torrent *ta, *tb;
 
302
 
 
303
    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
 
304
    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
 
305
    return compareTime( tr_torrentStatCached( ta )->addedDate,
 
306
                        tr_torrentStatCached( tb )->addedDate );
 
307
}
 
308
 
 
309
static int
 
310
compareBySize( GtkTreeModel * model,
 
311
               GtkTreeIter  * a,
 
312
               GtkTreeIter  * b,
 
313
               gpointer       user_data UNUSED )
 
314
{
 
315
    tr_torrent *t;
 
316
    const tr_info *ia, *ib;
 
317
 
 
318
    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &t, -1 );
 
319
    ia = tr_torrentInfo( t );
 
320
    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &t, -1 );
 
321
    ib = tr_torrentInfo( t );
 
322
 
 
323
    if( ia->totalSize < ib->totalSize ) return 1;
 
324
    if( ia->totalSize > ib->totalSize ) return -1;
 
325
    return 0;
 
326
}
 
327
 
 
328
static int
 
329
compareByProgress( GtkTreeModel *             model,
 
330
                   GtkTreeIter *              a,
 
331
                   GtkTreeIter *              b,
242
332
                   gpointer         user_data UNUSED )
243
333
{
244
334
    int ret;
245
 
    tr_torrent *ta, *tb;
 
335
    tr_torrent * t;
246
336
    const tr_stat *sa, *sb;
247
 
    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
248
 
    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
249
 
    sa = tr_torrentStatCached( ta );
250
 
    sb = tr_torrentStatCached( tb );
 
337
 
 
338
    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &t, -1 );
 
339
    sa = tr_torrentStatCached( t );
 
340
    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &t, -1 );
 
341
    sb = tr_torrentStatCached( t );
251
342
    ret = compareDouble( sa->percentDone, sb->percentDone );
252
343
    if( !ret )
253
344
        ret = compareRatio( sa->ratio, sb->ratio );
255
346
}
256
347
 
257
348
static int
258
 
compareByState( GtkTreeModel   * model,
259
 
                GtkTreeIter    * a,
260
 
                GtkTreeIter    * b,
261
 
                gpointer         user_data )
 
349
compareByETA( GtkTreeModel * model,
 
350
              GtkTreeIter  * a,
 
351
              GtkTreeIter  * b,
 
352
              gpointer       user_data UNUSED )
 
353
{
 
354
    tr_torrent *ta, *tb;
 
355
 
 
356
    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
 
357
    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
 
358
 
 
359
    return compareETA( tr_torrentStatCached( ta )->eta,
 
360
                       tr_torrentStatCached( tb )->eta );
 
361
}
 
362
 
 
363
static int
 
364
compareByState( GtkTreeModel * model,
 
365
                GtkTreeIter *  a,
 
366
                GtkTreeIter *  b,
 
367
                gpointer       user_data )
262
368
{
263
369
    int sa, sb, ret;
264
370
 
265
371
    /* first by state */
266
 
    gtk_tree_model_get( model, a, MC_STATUS, &sa, -1 );
267
 
    gtk_tree_model_get( model, b, MC_STATUS, &sb, -1 );
 
372
    gtk_tree_model_get( model, a, MC_ACTIVITY, &sa, -1 );
 
373
    gtk_tree_model_get( model, b, MC_ACTIVITY, &sb, -1 );
268
374
    ret = sa - sb;
269
375
 
270
376
    /* second by progress */
275
381
}
276
382
 
277
383
static int
278
 
compareByTracker( GtkTreeModel   * model,
279
 
                  GtkTreeIter    * a,
280
 
                  GtkTreeIter    * b,
281
 
                  gpointer         user_data UNUSED )
 
384
compareByTracker( GtkTreeModel * model,
 
385
                  GtkTreeIter  * a,
 
386
                  GtkTreeIter  * b,
 
387
                  gpointer       user_data UNUSED )
282
388
{
283
389
    const tr_torrent *ta, *tb;
 
390
 
284
391
    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
285
392
    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
286
 
    return strcmp( tr_torrentInfo(ta)->trackers[0].announce,
287
 
                   tr_torrentInfo(tb)->trackers[0].announce );
 
393
    return strcmp( tr_torrentInfo( ta )->trackers[0].announce,
 
394
                   tr_torrentInfo( tb )->trackers[0].announce );
288
395
}
289
396
 
290
397
static void
291
 
setSort( TrCore * core, const char * mode, gboolean isReversed  )
 
398
setSort( TrCore *     core,
 
399
         const char * mode,
 
400
         gboolean     isReversed  )
292
401
{
293
 
    const int col = MC_TORRENT_RAW;
 
402
    const int              col = MC_TORRENT_RAW;
294
403
    GtkTreeIterCompareFunc sort_func;
295
 
    GtkSortType type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
296
 
    GtkTreeSortable * sortable = GTK_TREE_SORTABLE( tr_core_model( core )  );
 
404
    GtkSortType            type =
 
405
        isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
 
406
    GtkTreeSortable *      sortable =
 
407
        GTK_TREE_SORTABLE( tr_core_model( core ) );
297
408
 
298
409
    if( !strcmp( mode, "sort-by-activity" ) )
299
410
        sort_func = compareByActivity;
 
411
    else if( !strcmp( mode, "sort-by-age" ) )
 
412
        sort_func = compareByAge;
300
413
    else if( !strcmp( mode, "sort-by-progress" ) )
301
414
        sort_func = compareByProgress;
 
415
    else if( !strcmp( mode, "sort-by-eta" ) )
 
416
        sort_func = compareByETA;
302
417
    else if( !strcmp( mode, "sort-by-ratio" ) )
303
418
        sort_func = compareByRatio;
304
419
    else if( !strcmp( mode, "sort-by-state" ) )
305
420
        sort_func = compareByState;
306
421
    else if( !strcmp( mode, "sort-by-tracker" ) )
307
422
        sort_func = compareByTracker;
 
423
    else if( !strcmp( mode, "sort-by-size" ) )
 
424
        sort_func = compareBySize;
308
425
    else {
309
426
        sort_func = compareByName;
310
427
        type = isReversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
311
428
    }
312
 
 
 
429
 
313
430
    gtk_tree_sortable_set_sort_func( sortable, col, sort_func, NULL, NULL );
314
431
    gtk_tree_sortable_set_sort_column_id( sortable, col, type );
315
432
}
320
437
    if( tr_ctorGetPaused( ctor, TR_FORCE, NULL ) )
321
438
        tr_ctorSetPaused( ctor, TR_FORCE, !pref_flag_get( PREF_KEY_START ) );
322
439
 
323
 
    if( tr_ctorGetDeleteSource( ctor, NULL ) ) 
324
 
        tr_ctorSetDeleteSource( ctor, pref_flag_get( PREF_KEY_TRASH_ORIGINAL ) ); 
325
 
 
326
 
    if( tr_ctorGetMaxConnectedPeers( ctor, TR_FORCE, NULL ) )
327
 
        tr_ctorSetMaxConnectedPeers( ctor, TR_FORCE,
328
 
                              pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
329
 
 
330
 
    if( tr_ctorGetDestination( ctor, TR_FORCE, NULL ) ) {
331
 
        char * path = pref_string_get( PREF_KEY_DIR_DEFAULT );
332
 
        tr_ctorSetDestination( ctor, TR_FORCE, path );
333
 
        g_free( path );
 
440
    if( tr_ctorGetDeleteSource( ctor, NULL ) )
 
441
        tr_ctorSetDeleteSource( ctor,
 
442
                               pref_flag_get( PREF_KEY_TRASH_ORIGINAL ) );
 
443
 
 
444
    if( tr_ctorGetPeerLimit( ctor, TR_FORCE, NULL ) )
 
445
        tr_ctorSetPeerLimit( ctor, TR_FORCE,
 
446
                             pref_int_get( TR_PREFS_KEY_PEER_LIMIT_TORRENT ) );
 
447
 
 
448
    if( tr_ctorGetDownloadDir( ctor, TR_FORCE, NULL ) )
 
449
    {
 
450
        const char * path = pref_string_get( TR_PREFS_KEY_DOWNLOAD_DIR );
 
451
        tr_ctorSetDownloadDir( ctor, TR_FORCE, path );
334
452
    }
335
453
}
336
454
 
 
455
static int
 
456
tr_strcmp( const void * a,
 
457
           const void * b )
 
458
{
 
459
    if( a && b ) return strcmp( a, b );
 
460
    if( a ) return 1;
 
461
    if( b ) return -1;
 
462
    return 0;
 
463
}
 
464
 
337
465
#ifdef HAVE_GIO
338
466
static gboolean
339
467
watchFolderIdle( gpointer gcore )
340
468
{
341
469
    TrCore * core = TR_CORE( gcore );
 
470
 
 
471
    core->priv->adding_from_watch_dir = TRUE;
342
472
    tr_core_add_list_defaults( core, core->priv->monitor_files );
 
473
    core->priv->adding_from_watch_dir = FALSE;
343
474
 
344
475
    /* cleanup */
345
476
    core->priv->monitor_files = NULL;
348
479
}
349
480
 
350
481
static void
351
 
maybeAddTorrent( TrCore * core, const char * filename )
 
482
maybeAddTorrent( TrCore *     core,
 
483
                 const char * filename )
352
484
{
353
485
    const gboolean isTorrent = g_str_has_suffix( filename, ".torrent" );
354
486
 
356
488
    {
357
489
        struct TrCorePrivate * p = core->priv;
358
490
 
359
 
        if( !g_slist_find_custom( p->monitor_files, filename, (GCompareFunc)strcmp ) )
360
 
            p->monitor_files = g_slist_append( p->monitor_files, g_strdup( filename ) );
 
491
        if( !g_slist_find_custom( p->monitor_files, filename,
 
492
                                  (GCompareFunc)strcmp ) )
 
493
            p->monitor_files =
 
494
                g_slist_append( p->monitor_files, g_strdup( filename ) );
361
495
        if( !p->monitor_idle_tag )
362
 
            p->monitor_idle_tag = g_timeout_add( 1000, watchFolderIdle, core );
 
496
            p->monitor_idle_tag = gtr_timeout_add_seconds( 1, watchFolderIdle, core );
363
497
    }
364
498
}
365
499
 
366
500
static void
367
 
watchFolderChanged( GFileMonitor       * monitor UNUSED,
368
 
                    GFile              * file,
 
501
watchFolderChanged( GFileMonitor       * monitor    UNUSED,
 
502
                    GFile *                         file,
369
503
                    GFile              * other_type UNUSED,
370
 
                    GFileMonitorEvent    event_type,
371
 
                    gpointer             core )
 
504
                    GFileMonitorEvent               event_type,
 
505
                    gpointer                        core )
372
506
{
373
507
    if( event_type == G_FILE_MONITOR_EVENT_CREATED )
374
508
    {
382
516
scanWatchDir( TrCore * core )
383
517
{
384
518
    const gboolean isEnabled = pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED );
 
519
 
385
520
    if( isEnabled )
386
521
    {
387
 
        char * dirname = pref_string_get( PREF_KEY_DIR_WATCH );
388
 
        GDir * dir = g_dir_open( dirname, 0, NULL );
 
522
        const char * dirname = pref_string_get( PREF_KEY_DIR_WATCH );
 
523
        GDir *       dir = g_dir_open( dirname, 0, NULL );
389
524
        const char * basename;
390
 
        while(( basename = g_dir_read_name( dir ))) {
 
525
        while( ( basename = g_dir_read_name( dir ) ) )
 
526
        {
391
527
            char * filename = g_build_filename( dirname, basename, NULL );
392
528
            maybeAddTorrent( core, filename );
393
529
            g_free( filename );
394
530
        }
395
 
        g_free( dirname );
396
531
    }
397
532
}
398
533
 
399
534
static void
400
535
updateWatchDir( TrCore * core )
401
536
{
402
 
    char * filename = pref_string_get( PREF_KEY_DIR_WATCH );
403
 
    const gboolean isEnabled = pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED );
 
537
    const char *           filename = pref_string_get( PREF_KEY_DIR_WATCH );
 
538
    const gboolean         isEnabled = pref_flag_get(
 
539
        PREF_KEY_DIR_WATCH_ENABLED );
404
540
    struct TrCorePrivate * p = TR_CORE( core )->priv;
405
541
 
406
542
    if( p->monitor && ( !isEnabled || tr_strcmp( filename, p->monitor_path ) ) )
416
552
 
417
553
    if( isEnabled && !p->monitor )
418
554
    {
419
 
        GFile * file = g_file_new_for_path( filename );
 
555
        GFile *        file = g_file_new_for_path( filename );
420
556
        GFileMonitor * m = g_file_monitor_directory( file, 0, NULL, NULL );
421
557
        scanWatchDir( core );
422
558
        p->monitor = m;
423
559
        p->monitor_path = g_strdup( filename );
424
560
        p->monitor_tag = g_signal_connect( m, "changed",
425
 
                                           G_CALLBACK( watchFolderChanged ), core );
 
561
                                           G_CALLBACK(
 
562
                                               watchFolderChanged ), core );
426
563
    }
427
 
 
428
 
    g_free( filename );
429
564
}
 
565
 
430
566
#endif
431
567
 
432
568
static void
433
 
prefsChanged( TrCore * core, const char * key, gpointer data UNUSED )
 
569
prefsChanged( TrCore *      core,
 
570
              const char *  key,
 
571
              gpointer data UNUSED )
434
572
{
435
 
    if( !strcmp( key, PREF_KEY_SORT_MODE ) ||
436
 
        !strcmp( key, PREF_KEY_SORT_REVERSED ) )
 
573
    if( !strcmp( key, PREF_KEY_SORT_MODE )
 
574
      || !strcmp( key, PREF_KEY_SORT_REVERSED ) )
437
575
    {
438
 
        char * mode = pref_string_get( PREF_KEY_SORT_MODE );
439
 
        gboolean isReversed = pref_flag_get( PREF_KEY_SORT_REVERSED );
 
576
        const char * mode = pref_string_get( PREF_KEY_SORT_MODE );
 
577
        gboolean     isReversed = pref_flag_get( PREF_KEY_SORT_REVERSED );
440
578
        setSort( core, mode, isReversed );
441
 
        g_free( mode );
442
 
    }
443
 
    else if( !strcmp( key, PREF_KEY_MAX_PEERS_GLOBAL ) )
444
 
    {
445
 
        const uint16_t val = pref_int_get( key );
446
 
        tr_setGlobalPeerLimit( tr_core_handle( core ), val );
 
579
    }
 
580
    else if( !strcmp( key, TR_PREFS_KEY_PEER_LIMIT_GLOBAL ) )
 
581
    {
 
582
        const uint16_t val = pref_int_get( key );
 
583
        tr_sessionSetPeerLimit( tr_core_session( core ), val );
 
584
    }
 
585
    else if( !strcmp( key, TR_PREFS_KEY_PEER_LIMIT_TORRENT ) )
 
586
    {
 
587
        const uint16_t val = pref_int_get( key );
 
588
        tr_sessionSetPeerLimitPerTorrent( tr_core_session( core ), val );
 
589
    }
 
590
    else if( !strcmp( key, PREF_KEY_INHIBIT_HIBERNATION ) )
 
591
    {
 
592
        maybeInhibitHibernation( core );
447
593
    }
448
594
#ifdef HAVE_GIO
449
 
    else if( !strcmp( key, PREF_KEY_DIR_WATCH ) ||
450
 
             !strcmp( key, PREF_KEY_DIR_WATCH_ENABLED ) )
 
595
    else if( !strcmp( key, PREF_KEY_DIR_WATCH )
 
596
           || !strcmp( key, PREF_KEY_DIR_WATCH_ENABLED ) )
451
597
    {
452
598
        updateWatchDir( core );
453
599
    }
455
601
}
456
602
 
457
603
static void
458
 
tr_core_init( GTypeInstance * instance, gpointer g_class UNUSED )
 
604
tr_core_init( GTypeInstance *  instance,
 
605
              gpointer g_class UNUSED )
459
606
{
460
 
    TrCore * self = (TrCore *) instance;
461
 
    GtkListStore * store;
 
607
    TrCore *               self = (TrCore *) instance;
 
608
    GtkListStore *         store;
462
609
    struct TrCorePrivate * p;
463
610
 
464
611
    /* column types for the model used to store torrent information */
465
612
    /* keep this in sync with the enum near the bottom of tr_core.h */
466
 
    GType types[] = {
 
613
    GType                  types[] = {
467
614
        G_TYPE_STRING,    /* name */
468
615
        G_TYPE_STRING,    /* collated name */
469
 
        G_TYPE_STRING,    /* hash string */
470
616
        TR_TORRENT_TYPE,  /* TrTorrent object */
471
617
        G_TYPE_POINTER,   /* tr_torrent* */
472
 
        G_TYPE_INT,       /* tr_stat()->status */
473
 
        G_TYPE_INT        /* ID for IPC */
 
618
        G_TYPE_INT        /* tr_stat()->status */
474
619
    };
475
620
 
476
621
    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self,
482
627
    store = gtk_list_store_newv( MC_ROW_COUNT, types );
483
628
 
484
629
    p->model    = GTK_TREE_MODEL( store );
485
 
    p->nextid   = 1;
 
630
 
 
631
#ifdef HAVE_DBUS_GLIB
 
632
    if( our_instance_adds_remote_torrents )
 
633
    {
 
634
        DBusGConnection * bus = dbus_g_bus_get( DBUS_BUS_SESSION, NULL );
 
635
        if( bus )
 
636
            dbus_g_connection_register_g_object(
 
637
                 bus,
 
638
                "/com/transmissionbt/Transmission",
 
639
                G_OBJECT( self ) );
 
640
    }
 
641
#endif
486
642
}
487
643
 
488
644
GType
495
651
        static const GTypeInfo info =
496
652
        {
497
653
            sizeof( TrCoreClass ),
498
 
            NULL,                       /* base_init */
499
 
            NULL,                       /* base_finalize */
500
 
            tr_core_class_init,         /* class_init */
501
 
            NULL,                       /* class_finalize */
502
 
            NULL,                       /* class_data */
 
654
            NULL,                 /* base_init */
 
655
            NULL,                 /* base_finalize */
 
656
            tr_core_class_init,   /* class_init */
 
657
            NULL,                 /* class_finalize */
 
658
            NULL,                 /* class_data */
503
659
            sizeof( TrCore ),
504
 
            0,                          /* n_preallocs */
505
 
            tr_core_init,               /* instance_init */
 
660
            0,                    /* n_preallocs */
 
661
            tr_core_init,         /* instance_init */
506
662
            NULL,
507
663
        };
508
664
        type = g_type_register_static( G_TYPE_OBJECT, "TrCore", &info, 0 );
516
672
**/
517
673
 
518
674
TrCore *
519
 
tr_core_new( tr_handle * h )
 
675
tr_core_new( tr_session * session )
520
676
{
521
677
    TrCore * core = TR_CORE( g_object_new( TR_CORE_TYPE, NULL ) );
522
 
    core->priv->handle   = h;
 
678
 
 
679
    core->priv->session  = session;
523
680
 
524
681
    /* init from prefs & listen to pref changes */
525
682
    prefsChanged( core, PREF_KEY_SORT_MODE, NULL );
526
683
    prefsChanged( core, PREF_KEY_SORT_REVERSED, NULL );
527
684
    prefsChanged( core, PREF_KEY_DIR_WATCH_ENABLED, NULL );
528
 
    prefsChanged( core, PREF_KEY_MAX_PEERS_GLOBAL, NULL );
529
 
    g_signal_connect( core, "prefs-changed", G_CALLBACK(prefsChanged), NULL );
 
685
    prefsChanged( core, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, NULL );
 
686
    prefsChanged( core, PREF_KEY_INHIBIT_HIBERNATION, NULL );
 
687
    g_signal_connect( core, "prefs-changed", G_CALLBACK( prefsChanged ), NULL );
530
688
 
531
689
    return core;
532
690
}
534
692
void
535
693
tr_core_close( TrCore * core )
536
694
{
537
 
    tr_handle * handle = tr_core_handle( core );
538
 
    if( handle )
 
695
    tr_session * session = tr_core_session( core );
 
696
 
 
697
    if( session )
539
698
    {
540
 
        core->priv->handle = NULL;
541
 
        tr_close( handle ); 
 
699
        core->priv->session = NULL;
 
700
        pref_save( session );
 
701
        tr_sessionClose( session );
542
702
    }
543
703
}
544
704
 
548
708
    return isDisposed( core ) ? NULL : core->priv->model;
549
709
}
550
710
 
551
 
tr_handle *
552
 
tr_core_handle( TrCore * core )
 
711
tr_session *
 
712
tr_core_session( TrCore * core )
553
713
{
554
 
    return isDisposed( core ) ? NULL : core->priv->handle;
 
714
    return isDisposed( core ) ? NULL : core->priv->session;
555
715
}
556
716
 
557
717
static gboolean
560
720
              GtkTreeIter  * iter,
561
721
              gpointer       gstats )
562
722
{
563
 
    tr_torrent * tor;
 
723
    tr_torrent *        tor;
564
724
    struct core_stats * stats = gstats;
565
 
    int status;
 
725
    int                 activity;
566
726
 
567
727
    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
568
 
    status = tr_torrentGetStatus( tor );
 
728
    activity = tr_torrentGetActivity( tor );
569
729
 
570
 
    if( status == TR_STATUS_DOWNLOAD )
 
730
    if( activity == TR_STATUS_DOWNLOAD )
571
731
        ++stats->downloadCount;
572
 
    else if( status == TR_STATUS_SEED )
 
732
    else if( activity == TR_STATUS_SEED )
573
733
        ++stats->seedingCount;
574
734
 
575
735
    return FALSE;
576
736
}
577
737
 
578
738
void
579
 
tr_core_get_stats( const TrCore       * core,
580
 
                   struct core_stats  * setme )
 
739
tr_core_get_stats( const TrCore *      core,
 
740
                   struct core_stats * setme )
581
741
{
582
742
    memset( setme, 0, sizeof( struct core_stats ) );
583
743
 
584
744
    if( !isDisposed( core ) )
585
745
    {
586
 
        tr_torrentRates( core->priv->handle,
587
 
                         &setme->clientDownloadSpeed,
588
 
                         &setme->clientUploadSpeed );
589
 
 
590
 
        gtk_tree_model_foreach( core->priv->model,
591
 
                                statsForeach,
592
 
                                setme );
 
746
        tr_session * session = core->priv->session;
 
747
 
 
748
        setme->clientDownloadSpeed = tr_sessionGetPieceSpeed( session, TR_DOWN );
 
749
 
 
750
        setme->clientUploadSpeed = tr_sessionGetPieceSpeed( session, TR_UP );
 
751
 
 
752
        gtk_tree_model_foreach( core->priv->model, statsForeach, setme );
593
753
    }
594
754
}
595
755
 
597
757
doCollate( const char * in )
598
758
{
599
759
    const char * end = in + strlen( in );
600
 
    char * casefold;
601
 
    char * ret;
 
760
    char *       casefold;
 
761
    char *       ret;
602
762
 
603
 
    while( in < end ) {
 
763
    while( in < end )
 
764
    {
604
765
        const gunichar ch = g_utf8_get_char( in );
605
 
        if (!g_unichar_isalnum (ch)) /* eat everything before the first alnum */
 
766
        if( !g_unichar_isalnum ( ch ) ) /* eat everything before the first alnum
 
767
                                          */
606
768
            in += g_unichar_to_utf8( ch, NULL );
607
769
        else
608
770
            break;
609
771
    }
610
772
 
611
 
    if ( in == end )
612
 
        return g_strdup ("");
 
773
    if( in == end )
 
774
        return g_strdup ( "" );
613
775
 
614
 
    casefold = g_utf8_casefold( in, end-in );
 
776
    casefold = g_utf8_casefold( in, end - in );
615
777
    ret = g_utf8_collate_key( casefold, -1 );
616
778
    g_free( casefold );
617
779
 
619
781
}
620
782
 
621
783
void
622
 
tr_core_add_torrent( TrCore * self, TrTorrent * tor )
 
784
tr_core_add_torrent( TrCore *    self,
 
785
                     TrTorrent * gtor )
623
786
{
624
 
    const tr_info * inf = tr_torrent_info( tor );
625
 
    const tr_stat * torStat = tr_torrent_stat( tor );
626
 
    char * collated = doCollate( inf->name );
627
 
    GtkListStore * store = GTK_LIST_STORE( tr_core_model( self ) );
628
 
    GtkTreeIter unused;
 
787
    const tr_info * inf = tr_torrent_info( gtor );
 
788
    const tr_stat * torStat = tr_torrent_stat( gtor );
 
789
    tr_torrent *    tor = tr_torrent_handle( gtor );
 
790
    char *          collated = doCollate( inf->name );
 
791
    GtkListStore *  store = GTK_LIST_STORE( tr_core_model( self ) );
 
792
    GtkTreeIter     unused;
629
793
 
630
 
    gtk_list_store_insert_with_values( store, &unused, 0, 
 
794
    gtk_list_store_insert_with_values( store, &unused, 0,
631
795
                                       MC_NAME,          inf->name,
632
796
                                       MC_NAME_COLLATED, collated,
633
 
                                       MC_HASH,          inf->hashString,
634
 
                                       MC_TORRENT,       tor,
635
 
                                       MC_TORRENT_RAW,   tr_torrent_handle( tor ),
636
 
                                       MC_STATUS,        torStat->status,
637
 
                                       MC_ID,            self->priv->nextid,
638
 
                                       -1);
639
 
    ++self->priv->nextid;
 
797
                                       MC_TORRENT,       gtor,
 
798
                                       MC_TORRENT_RAW,   tor,
 
799
                                       MC_ACTIVITY,      torStat->activity,
 
800
                                       -1 );
640
801
 
641
802
    /* cleanup */
642
 
    g_object_unref( G_OBJECT( tor ) );
 
803
    g_object_unref( G_OBJECT( gtor ) );
643
804
    g_free( collated );
644
805
}
645
806
 
646
807
int
647
 
tr_core_load( TrCore * self, gboolean forcePaused )
 
808
tr_core_load( TrCore * self,
 
809
              gboolean forcePaused )
648
810
{
649
 
    int i;
650
 
    int count = 0;
 
811
    int           i;
 
812
    int           count = 0;
651
813
    tr_torrent ** torrents;
652
 
    char * path;
653
 
    tr_ctor * ctor;
654
 
 
655
 
    path = getdownloaddir( );
656
 
 
657
 
    ctor = tr_ctorNew( tr_core_handle( self ) );
 
814
    tr_ctor *     ctor;
 
815
 
 
816
    ctor = tr_ctorNew( tr_core_session( self ) );
658
817
    if( forcePaused )
659
818
        tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
660
 
    tr_ctorSetDestination( ctor, TR_FALLBACK, path );
661
 
    tr_ctorSetMaxConnectedPeers( ctor, TR_FALLBACK,
662
 
                                 pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
 
819
    tr_ctorSetPeerLimit( ctor, TR_FALLBACK,
 
820
                         pref_int_get( TR_PREFS_KEY_PEER_LIMIT_TORRENT ) );
663
821
 
664
 
    torrents = tr_loadTorrents ( tr_core_handle( self ), ctor, &count );
665
 
    for( i=0; i<count; ++i )
 
822
    torrents = tr_sessionLoadTorrents ( tr_core_session( self ), ctor, &count );
 
823
    for( i = 0; i < count; ++i )
666
824
        tr_core_add_torrent( self, tr_torrent_new_preexisting( torrents[i] ) );
667
825
 
668
826
    tr_free( torrents );
669
827
    tr_ctorFree( ctor );
670
 
    g_free( path );
671
828
 
672
829
    return count;
673
830
}
674
831
 
675
832
static void
676
 
tr_core_errsig( TrCore * core, enum tr_core_err type, const char * msg )
677
 
{
678
 
    g_signal_emit( core, TR_CORE_GET_CLASS(core)->errsig, 0, type, msg );
679
 
}
680
 
 
681
 
void
682
 
tr_core_add_ctor( TrCore * self, tr_ctor * ctor )
683
 
{
684
 
    TrTorrent * tor;
685
 
    char      * errstr = NULL;
686
 
 
687
 
    tr_core_apply_defaults( ctor );
688
 
 
689
 
    if(( tor = tr_torrent_new_ctor( tr_core_handle( self ), ctor, &errstr )))
690
 
        tr_core_add_torrent( self, tor );
691
 
    else{ 
692
 
        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
693
 
        g_free( errstr );
 
833
emitBlocklistUpdated( TrCore * core, int ruleCount )
 
834
{
 
835
    g_signal_emit( core, TR_CORE_GET_CLASS( core )->blocklistSignal, 0, ruleCount );
 
836
}
 
837
 
 
838
static void
 
839
emitPortTested( TrCore * core, gboolean isOpen )
 
840
{
 
841
    g_signal_emit( core, TR_CORE_GET_CLASS( core )->portSignal, 0, isOpen );
 
842
}
 
843
 
 
844
static void
 
845
tr_core_errsig( TrCore *         core,
 
846
                enum tr_core_err type,
 
847
                const char *     msg )
 
848
{
 
849
    g_signal_emit( core, TR_CORE_GET_CLASS( core )->errsig, 0, type, msg );
 
850
}
 
851
 
 
852
static void
 
853
add_filename( TrCore *     core,
 
854
              const char * filename,
 
855
              gboolean     doStart,
 
856
              gboolean     doPrompt )
 
857
{
 
858
    tr_session * session = tr_core_session( core );
 
859
 
 
860
    if( filename && session )
 
861
    {
 
862
        tr_ctor * ctor;
 
863
 
 
864
        ctor = tr_ctorNew( session );
 
865
        tr_core_apply_defaults( ctor );
 
866
        tr_ctorSetPaused( ctor, TR_FORCE, !doStart );
 
867
 
 
868
        if( tr_ctorSetMetainfoFromFile( ctor, filename ) )
 
869
        {
 
870
            tr_core_errsig( core, TR_EINVALID, filename );
 
871
            tr_ctorFree( ctor );
 
872
        }
 
873
        else
 
874
        {
 
875
            tr_info inf;
 
876
            int err = tr_torrentParse( ctor, &inf );
 
877
 
 
878
            switch( err )
 
879
            {
 
880
                case TR_EINVALID:
 
881
                    tr_core_errsig( core, err, filename );
 
882
                    break;
 
883
 
 
884
                case TR_EDUPLICATE:
 
885
                    /* don't complain about .torrent files in the watch directory
 
886
                     * that have already been added... that gets annoying and we
 
887
                     * don't want to be naggign users to clean up their watch dirs */
 
888
                    if( !core->priv->adding_from_watch_dir )
 
889
                        tr_core_errsig( core, err, inf.name );
 
890
                    tr_metainfoFree( &inf );
 
891
                    break;
 
892
 
 
893
                default:
 
894
                    if( doPrompt )
 
895
                        g_signal_emit( core, TR_CORE_GET_CLASS( core )->promptsig, 0, ctor );
 
896
                    else {
 
897
                        TrTorrent * gtor = tr_torrent_new_ctor( session, ctor, &err );
 
898
                        if( err )
 
899
                            tr_core_errsig( core, err, filename );
 
900
                        else
 
901
                            tr_core_add_torrent( core, gtor );
 
902
                    }
 
903
                    tr_metainfoFree( &inf );
 
904
                    break;
 
905
            }
 
906
        }
694
907
    }
695
 
 
696
 
    /* cleanup */
697
 
    tr_ctorFree( ctor );
 
908
}
 
909
 
 
910
gboolean
 
911
tr_core_add_file( TrCore *          core,
 
912
                  const char *      filename,
 
913
                  gboolean *        success,
 
914
                  GError     ** err UNUSED )
 
915
{
 
916
    add_filename( core, filename,
 
917
                  pref_flag_get( PREF_KEY_START ),
 
918
                  pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) );
 
919
    *success = TRUE;
 
920
    return TRUE;
 
921
}
 
922
 
 
923
gboolean
 
924
tr_core_present_window( TrCore      * core UNUSED,
 
925
                        gboolean *         success,
 
926
                        GError     ** err  UNUSED )
 
927
{
 
928
    action_activate( "present-main-window" );
 
929
    *success = TRUE;
 
930
    return TRUE;
698
931
}
699
932
 
700
933
void
701
 
tr_core_add_list( TrCore      * core,
702
 
                  GSList      * torrentFiles,
703
 
                  pref_flag_t   start,
704
 
                  pref_flag_t   prompt )
 
934
tr_core_add_list( TrCore *    core,
 
935
                  GSList *    torrentFiles,
 
936
                  pref_flag_t start,
 
937
                  pref_flag_t prompt )
705
938
{
706
939
    const gboolean doStart = pref_flag_eval( start, PREF_KEY_START );
707
940
    const gboolean doPrompt = pref_flag_eval( prompt, PREF_KEY_OPTIONS_PROMPT );
708
 
 
709
 
    if( torrentFiles && !isDisposed( core ) )
710
 
    {
711
 
        GSList * l;
712
 
        tr_handle * handle = core->priv->handle;
713
 
 
714
 
        for( l=torrentFiles; l!=NULL; l=l->next )
715
 
        {
716
 
            tr_ctor * ctor = tr_ctorNew( handle );
717
 
            tr_core_apply_defaults( ctor );
718
 
            tr_ctorSetPaused( ctor, TR_FORCE, !doStart );
719
 
            if( tr_ctorSetMetainfoFromFile( ctor, l->data ) )
720
 
                tr_ctorFree( ctor );
721
 
            else if( tr_torrentParse( handle, ctor, NULL ) )
722
 
                tr_ctorFree( ctor );
723
 
            else if( doPrompt )
724
 
                g_signal_emit( core, TR_CORE_GET_CLASS(core)->promptsig, 0, ctor );
725
 
            else
726
 
                tr_core_add_ctor( core, ctor );
727
 
        }
728
 
    }
729
 
 
 
941
    GSList * l;
 
942
 
 
943
    for( l = torrentFiles; l != NULL; l = l->next )
 
944
        add_filename( core, l->data, doStart, doPrompt );
 
945
 
 
946
    tr_core_torrents_added( core );
730
947
    freestrlist( torrentFiles );
731
948
}
732
949
 
737
954
    tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL );
738
955
}
739
956
 
740
 
void
741
 
tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter )
742
 
{
743
 
    TrTorrent * tor;
744
 
    GtkTreeModel * model = tr_core_model( self );
745
 
 
746
 
    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
747
 
    gtk_list_store_remove( GTK_LIST_STORE( model ), iter );
748
 
    tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
749
 
 
750
 
    g_object_unref( G_OBJECT( tor ) );
751
 
}
752
 
 
753
957
static gboolean
754
 
findTorrentInModel( TrCore * core, const TrTorrent * gtor, GtkTreeIter * setme )
 
958
findTorrentInModel( TrCore *      core,
 
959
                    int           id,
 
960
                    GtkTreeIter * setme )
755
961
{
756
 
    int match = 0;
757
 
    GtkTreeIter iter;
 
962
    int            match = 0;
 
963
    GtkTreeIter    iter;
758
964
    GtkTreeModel * model = tr_core_model( core );
759
965
 
760
966
    if( gtk_tree_model_iter_children( model, &iter, NULL ) ) do
761
 
    {
762
 
        TrTorrent * tmp;
763
 
        gtk_tree_model_get( model, &iter, MC_TORRENT, &tmp, -1 );
764
 
        match = tmp == gtor;
765
 
        g_object_unref( G_OBJECT( tmp ) );
766
 
    }
767
 
    while( !match && gtk_tree_model_iter_next( model, &iter ) );
 
967
        {
 
968
            tr_torrent * tor;
 
969
            gtk_tree_model_get( model, &iter, MC_TORRENT_RAW, &tor, -1 );
 
970
            match = tr_torrentId( tor ) == id;
 
971
        }
 
972
        while( !match && gtk_tree_model_iter_next( model, &iter ) );
768
973
 
769
974
    if( match )
770
975
        *setme = iter;
773
978
}
774
979
 
775
980
void
776
 
tr_core_remove_torrent( TrCore * self, TrTorrent * gtor, int deleteFiles )
 
981
tr_core_torrent_destroyed( TrCore * core,
 
982
                           int      id )
777
983
{
778
984
    GtkTreeIter iter;
779
 
    GtkTreeModel * model = tr_core_model( self );
780
985
 
781
 
    /* remove from the gui */
782
 
    if( findTorrentInModel( self, gtor, &iter ) )
 
986
    if( findTorrentInModel( core, id, &iter ) )
 
987
    {
 
988
        TrTorrent * gtor;
 
989
        GtkTreeModel * model = tr_core_model( core );
 
990
        gtk_tree_model_get( model, &iter, MC_TORRENT, &gtor, -1 );
 
991
        tr_torrent_clear( gtor );
783
992
        gtk_list_store_remove( GTK_LIST_STORE( model ), &iter );
784
 
 
785
 
    /* maybe delete the downloaded files */
786
 
    if( deleteFiles )
787
 
        tr_torrent_delete_files( gtor );
788
 
 
789
 
    /* delete the torrent */
790
 
    tr_torrent_set_delete_flag( gtor, TRUE );
791
 
    g_object_unref( G_OBJECT( gtor ) );
792
 
}
793
 
 
 
993
        g_object_unref( G_OBJECT( gtor ) );
 
994
    }
 
995
}
 
996
 
 
997
void
 
998
tr_core_remove_torrent( TrCore *    core,
 
999
                        TrTorrent * gtor,
 
1000
                        int         deleteFiles )
 
1001
{
 
1002
    const tr_torrent * tor = tr_torrent_handle( gtor );
 
1003
 
 
1004
    if( tor )
 
1005
    {
 
1006
        int         id = tr_torrentId( tor );
 
1007
        GtkTreeIter iter;
 
1008
        if( findTorrentInModel( core, id, &iter ) )
 
1009
        {
 
1010
            GtkTreeModel * model = tr_core_model( core );
 
1011
 
 
1012
            /* remove from the gui */
 
1013
            gtk_list_store_remove( GTK_LIST_STORE( model ), &iter );
 
1014
 
 
1015
            /* maybe delete the downloaded files */
 
1016
            if( deleteFiles )
 
1017
                tr_torrent_delete_files( gtor );
 
1018
 
 
1019
            /* remove the torrent */
 
1020
            tr_torrent_set_remove_flag( gtor, TRUE );
 
1021
            g_object_unref( G_OBJECT( gtor ) );
 
1022
        }
 
1023
    }
 
1024
}
794
1025
 
795
1026
/***
796
1027
****
797
1028
***/
798
1029
 
799
1030
static gboolean
800
 
update_foreach( GtkTreeModel * model,
 
1031
update_foreach( GtkTreeModel *      model,
801
1032
                GtkTreePath  * path UNUSED,
802
 
                GtkTreeIter  * iter,
 
1033
                GtkTreeIter *       iter,
803
1034
                gpointer       data UNUSED )
804
1035
{
805
 
    int oldStatus;
806
 
    int newStatus;
 
1036
    int         oldActivity;
 
1037
    int         newActivity;
807
1038
    TrTorrent * gtor;
808
1039
 
809
1040
    /* maybe update the status column in the model */
810
1041
    gtk_tree_model_get( model, iter,
811
1042
                        MC_TORRENT, &gtor,
812
 
                        MC_STATUS, &oldStatus,
 
1043
                        MC_ACTIVITY, &oldActivity,
813
1044
                        -1 );
814
 
    newStatus = tr_torrentGetStatus( tr_torrent_handle( gtor ) );
815
 
    if( newStatus != oldStatus )
 
1045
    newActivity = tr_torrentGetActivity( tr_torrent_handle( gtor ) );
 
1046
    if( newActivity != oldActivity )
816
1047
        gtk_list_store_set( GTK_LIST_STORE( model ), iter,
817
 
                            MC_STATUS, newStatus,
 
1048
                            MC_ACTIVITY, newActivity,
818
1049
                            -1 );
819
1050
 
820
 
    /* check the seeding cap */
821
 
    tr_torrent_check_seeding_cap ( gtor );
822
 
 
823
1051
    /* cleanup */
824
1052
    g_object_unref( gtor );
825
1053
    return FALSE;
828
1056
void
829
1057
tr_core_update( TrCore * self )
830
1058
{
831
 
    int column;
832
 
    GtkSortType order;
 
1059
    int               column;
 
1060
    GtkSortType       order;
833
1061
    GtkTreeSortable * sortable;
834
 
    GtkTreeModel * model = tr_core_model( self );
 
1062
    GtkTreeModel *    model = tr_core_model( self );
835
1063
 
836
1064
    /* pause sorting */
837
1065
    sortable = GTK_TREE_SORTABLE( model );
838
1066
    gtk_tree_sortable_get_sort_column_id( sortable, &column, &order );
839
 
    gtk_tree_sortable_set_sort_column_id( sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order );
 
1067
    gtk_tree_sortable_set_sort_column_id(
 
1068
        sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order );
840
1069
 
841
1070
    /* refresh the model */
842
1071
    gtk_tree_model_foreach( model, update_foreach, NULL );
843
1072
 
844
1073
    /* resume sorting */
845
1074
    gtk_tree_sortable_set_sort_column_id( sortable, column, order );
 
1075
 
 
1076
    /* maybe inhibit hibernation */
 
1077
    maybeInhibitHibernation( self );
846
1078
}
847
1079
 
848
1080
void
849
1081
tr_core_quit( TrCore * core )
850
1082
{
851
 
    g_signal_emit( core, TR_CORE_GET_CLASS(core)->quitsig, 0 );
 
1083
    g_signal_emit( core, TR_CORE_GET_CLASS( core )->quitsig, 0 );
 
1084
}
 
1085
 
 
1086
/**
 
1087
***  Hibernate
 
1088
**/
 
1089
 
 
1090
#ifdef HAVE_DBUS_GLIB
 
1091
 
 
1092
static DBusGProxy*
 
1093
get_hibernation_inhibit_proxy( void )
 
1094
{
 
1095
    GError *          error = NULL;
 
1096
    DBusGConnection * conn;
 
1097
 
 
1098
    conn = dbus_g_bus_get( DBUS_BUS_SESSION, &error );
 
1099
    if( error )
 
1100
    {
 
1101
        g_warning ( "DBUS cannot connect : %s", error->message );
 
1102
        g_error_free ( error );
 
1103
        return NULL;
 
1104
    }
 
1105
 
 
1106
    return dbus_g_proxy_new_for_name (
 
1107
               conn,
 
1108
               "org.freedesktop.PowerManagement",
 
1109
               "/org/freedesktop/PowerManagement/Inhibit",
 
1110
               "org.freedesktop.PowerManagement.Inhibit" );
 
1111
}
 
1112
 
 
1113
static gboolean
 
1114
gtr_inhibit_hibernation( guint * cookie )
 
1115
{
 
1116
    gboolean     success = FALSE;
 
1117
    DBusGProxy * proxy = get_hibernation_inhibit_proxy( );
 
1118
 
 
1119
    if( proxy )
 
1120
    {
 
1121
        GError *     error = NULL;
 
1122
        const char * application = _( "Transmission Bittorrent Client" );
 
1123
        const char * reason = _( "BitTorrent Activity" );
 
1124
        success = dbus_g_proxy_call( proxy, "Inhibit", &error,
 
1125
                                     G_TYPE_STRING, application,
 
1126
                                     G_TYPE_STRING, reason,
 
1127
                                     G_TYPE_INVALID,
 
1128
                                     G_TYPE_UINT, cookie,
 
1129
                                     G_TYPE_INVALID );
 
1130
        if( success )
 
1131
            tr_inf( _( "Disallowing desktop hibernation" ) );
 
1132
        else
 
1133
        {
 
1134
            tr_err( _(
 
1135
                        "Couldn't disable desktop hibernation: %s" ),
 
1136
                    error->message );
 
1137
            g_error_free( error );
 
1138
        }
 
1139
 
 
1140
        g_object_unref( G_OBJECT( proxy ) );
 
1141
    }
 
1142
 
 
1143
    return success != 0;
 
1144
}
 
1145
 
 
1146
static void
 
1147
gtr_uninhibit_hibernation( guint inhibit_cookie )
 
1148
{
 
1149
    DBusGProxy * proxy = get_hibernation_inhibit_proxy( );
 
1150
 
 
1151
    if( proxy )
 
1152
    {
 
1153
        GError * error = NULL;
 
1154
        gboolean success = dbus_g_proxy_call( proxy, "UnInhibit", &error,
 
1155
                                              G_TYPE_UINT, inhibit_cookie,
 
1156
                                              G_TYPE_INVALID,
 
1157
                                              G_TYPE_INVALID );
 
1158
        if( success )
 
1159
            tr_inf( _( "Allowing desktop hibernation" ) );
 
1160
        else
 
1161
        {
 
1162
            g_warning( "Couldn't uninhibit the system from suspending: %s.",
 
1163
                       error->message );
 
1164
            g_error_free( error );
 
1165
        }
 
1166
 
 
1167
        g_object_unref( G_OBJECT( proxy ) );
 
1168
    }
 
1169
}
 
1170
 
 
1171
#endif
 
1172
 
 
1173
static void
 
1174
tr_core_set_hibernation_allowed( TrCore * core,
 
1175
                                 gboolean allowed )
 
1176
{
 
1177
#ifdef HAVE_DBUS_GLIB
 
1178
    g_return_if_fail( core );
 
1179
    g_return_if_fail( core->priv );
 
1180
 
 
1181
    core->priv->inhibit_allowed = allowed != 0;
 
1182
 
 
1183
    if( allowed && core->priv->have_inhibit_cookie )
 
1184
    {
 
1185
        gtr_uninhibit_hibernation( core->priv->inhibit_cookie );
 
1186
        core->priv->have_inhibit_cookie = FALSE;
 
1187
    }
 
1188
 
 
1189
    if( !allowed
 
1190
      && !core->priv->have_inhibit_cookie
 
1191
      && !core->priv->dbus_error )
 
1192
    {
 
1193
        if( gtr_inhibit_hibernation( &core->priv->inhibit_cookie ) )
 
1194
            core->priv->have_inhibit_cookie = TRUE;
 
1195
        else
 
1196
            core->priv->dbus_error = TRUE;
 
1197
    }
 
1198
#endif
 
1199
}
 
1200
 
 
1201
static void
 
1202
maybeInhibitHibernation( TrCore * core )
 
1203
{
 
1204
    gboolean inhibit = pref_flag_get( PREF_KEY_INHIBIT_HIBERNATION );
 
1205
 
 
1206
    /* always allow hibernation when all the torrents are paused */
 
1207
    if( inhibit ) {
 
1208
        tr_session * session = tr_core_session( core );
 
1209
 
 
1210
        if( tr_sessionGetActiveTorrentCount( session ) == 0 )
 
1211
            inhibit = FALSE;
 
1212
    }
 
1213
 
 
1214
    tr_core_set_hibernation_allowed( core, !inhibit );
852
1215
}
853
1216
 
854
1217
/**
856
1219
**/
857
1220
 
858
1221
static void
859
 
commitPrefsChange( TrCore * core, const char * key )
 
1222
commitPrefsChange( TrCore *     core,
 
1223
                   const char * key )
860
1224
{
861
 
    pref_save( NULL );
862
 
    g_signal_emit( core, TR_CORE_GET_CLASS(core)->prefsig, 0, key );
 
1225
    g_signal_emit( core, TR_CORE_GET_CLASS( core )->prefsig, 0, key );
 
1226
    pref_save( tr_core_session( core ) );
863
1227
}
864
1228
 
865
1229
void
866
 
tr_core_set_pref( TrCore * self, const char * key, const char * newval )
 
1230
tr_core_set_pref( TrCore *     self,
 
1231
                  const char * key,
 
1232
                  const char * newval )
867
1233
{
868
 
    char * oldval = pref_string_get( key );
 
1234
    const char * oldval = pref_string_get( key );
 
1235
 
869
1236
    if( tr_strcmp( oldval, newval ) )
870
1237
    {
871
1238
        pref_string_set( key, newval );
872
1239
        commitPrefsChange( self, key );
873
1240
    }
874
 
    g_free( oldval );
875
1241
}
876
1242
 
877
1243
void
878
 
tr_core_set_pref_bool( TrCore * self, const char * key, gboolean newval )
 
1244
tr_core_set_pref_bool( TrCore *     self,
 
1245
                       const char * key,
 
1246
                       gboolean     newval )
879
1247
{
880
1248
    const gboolean oldval = pref_flag_get( key );
 
1249
 
881
1250
    if( oldval != newval )
882
1251
    {
883
1252
        pref_flag_set( key, newval );
886
1255
}
887
1256
 
888
1257
void
889
 
tr_core_set_pref_int( TrCore * self, const char * key, int newval )
 
1258
tr_core_set_pref_int( TrCore *     self,
 
1259
                      const char * key,
 
1260
                      int          newval )
890
1261
{
891
1262
    const int oldval = pref_int_get( key );
 
1263
 
892
1264
    if( oldval != newval )
893
1265
    {
894
1266
        pref_int_set( key, newval );
895
1267
        commitPrefsChange( self, key );
896
1268
    }
897
1269
}
 
1270
 
 
1271
void
 
1272
tr_core_set_pref_double( TrCore *     self,
 
1273
                         const char * key,
 
1274
                         double       newval )
 
1275
{
 
1276
    const double oldval = pref_double_get( key );
 
1277
 
 
1278
    if( oldval != newval )
 
1279
    {
 
1280
        pref_double_set( key, newval );
 
1281
        commitPrefsChange( self, key );
 
1282
    }
 
1283
}
 
1284
 
 
1285
/***
 
1286
****
 
1287
****  RPC Interface
 
1288
****
 
1289
***/
 
1290
 
 
1291
/* #define DEBUG_RPC */
 
1292
 
 
1293
static int nextTag = 1;
 
1294
 
 
1295
typedef void ( server_response_func )( TrCore * core, tr_benc * response, gpointer user_data );
 
1296
 
 
1297
struct pending_request_data
 
1298
{
 
1299
    int tag;
 
1300
    TrCore * core;
 
1301
    server_response_func * responseFunc;
 
1302
    gpointer responseFuncUserData;
 
1303
};
 
1304
 
 
1305
static GHashTable * pendingRequests = NULL;
 
1306
 
 
1307
static gboolean
 
1308
readResponseIdle( void * vresponse )
 
1309
{
 
1310
    GByteArray * response;
 
1311
    tr_benc top;
 
1312
    int64_t intVal;
 
1313
    int tag;
 
1314
    struct pending_request_data * data;
 
1315
 
 
1316
    response = vresponse;
 
1317
    tr_jsonParse( response->data, response->len, &top, NULL );
 
1318
    tr_bencDictFindInt( &top, "tag", &intVal );
 
1319
    tag = (int)intVal;
 
1320
 
 
1321
    data = g_hash_table_lookup( pendingRequests, &tag );
 
1322
    if( data )
 
1323
        (*data->responseFunc)(data->core, &top, data->responseFuncUserData );
 
1324
 
 
1325
    tr_bencFree( &top );
 
1326
    g_hash_table_remove( pendingRequests, &tag );
 
1327
    g_byte_array_free( response, TRUE );
 
1328
    return FALSE;
 
1329
}
 
1330
 
 
1331
static void
 
1332
readResponse( tr_session  * session UNUSED,
 
1333
              const char  * response,
 
1334
              size_t        response_len,
 
1335
              void        * unused UNUSED )
 
1336
{
 
1337
    GByteArray * bytes = g_byte_array_new( );
 
1338
#ifdef DEBUG_RPC
 
1339
    g_message( "response: [%*.*s]", (int)response_len, (int)response_len, response );
 
1340
#endif
 
1341
    g_byte_array_append( bytes, (const uint8_t*)response, response_len );
 
1342
    g_idle_add( readResponseIdle, bytes );
 
1343
}
 
1344
 
 
1345
static void
 
1346
sendRequest( TrCore * core, const char * json, int tag,
 
1347
             server_response_func * responseFunc, void * responseFuncUserData )
 
1348
{
 
1349
    tr_session * session = tr_core_session( core );
 
1350
 
 
1351
    if( pendingRequests == NULL )
 
1352
    {
 
1353
        pendingRequests = g_hash_table_new_full( g_int_hash, g_int_equal, NULL, g_free );
 
1354
    }
 
1355
 
 
1356
    if( session == NULL )
 
1357
    {
 
1358
        g_error( "GTK+ client doesn't support connections to remote servers yet." );
 
1359
    }
 
1360
    else
 
1361
    {
 
1362
        /* remember this request */
 
1363
        struct pending_request_data * data;
 
1364
        data = g_new0( struct pending_request_data, 1 );
 
1365
        data->core = core;
 
1366
        data->tag = tag;
 
1367
        data->responseFunc = responseFunc;
 
1368
        data->responseFuncUserData = responseFuncUserData;
 
1369
        g_hash_table_insert( pendingRequests, &data->tag, data );
 
1370
 
 
1371
        /* make the request */
 
1372
#ifdef DEBUG_RPC
 
1373
        g_message( "request: [%s]", json );
 
1374
#endif
 
1375
        tr_rpc_request_exec_json( session, json, strlen( json ), readResponse, GINT_TO_POINTER(tag) );
 
1376
    }
 
1377
}
 
1378
 
 
1379
/***
 
1380
****  Sending a test-port request via RPC
 
1381
***/
 
1382
 
 
1383
static void
 
1384
portTestResponseFunc( TrCore * core, tr_benc * response, gpointer userData UNUSED )
 
1385
{
 
1386
    tr_benc * args;
 
1387
    tr_bool isOpen = FALSE;
 
1388
 
 
1389
    if( tr_bencDictFindDict( response, "arguments", &args ) )
 
1390
        tr_bencDictFindBool( args, "port-is-open", &isOpen );
 
1391
 
 
1392
    emitPortTested( core, isOpen );
 
1393
}
 
1394
 
 
1395
void
 
1396
tr_core_port_test( TrCore * core )
 
1397
{
 
1398
    char buf[128];
 
1399
    const int tag = nextTag++;
 
1400
    g_snprintf( buf, sizeof( buf ), "{ \"method\": \"port-test\", \"tag\": %d }", tag );
 
1401
    sendRequest( core, buf, tag, portTestResponseFunc, NULL );
 
1402
}
 
1403
 
 
1404
/***
 
1405
****  Updating a blocklist via RPC
 
1406
***/
 
1407
 
 
1408
static void
 
1409
blocklistResponseFunc( TrCore * core, tr_benc * response, gpointer userData UNUSED )
 
1410
{
 
1411
    tr_benc * args;
 
1412
    int64_t ruleCount = 0;
 
1413
 
 
1414
    if( tr_bencDictFindDict( response, "arguments", &args ) )
 
1415
        tr_bencDictFindInt( args, "blocklist-size", &ruleCount );
 
1416
 
 
1417
    if( ruleCount > 0 )
 
1418
        pref_int_set( "blocklist-date", time( NULL ) );
 
1419
 
 
1420
    emitBlocklistUpdated( core, ruleCount );
 
1421
}
 
1422
 
 
1423
void
 
1424
tr_core_blocklist_update( TrCore * core )
 
1425
{
 
1426
    char buf[128];
 
1427
    const int tag = nextTag++;
 
1428
    g_snprintf( buf, sizeof( buf ), "{ \"method\": \"blocklist-update\", \"tag\": %d }", tag );
 
1429
    sendRequest( core, buf, tag, blocklistResponseFunc, NULL );
 
1430
}
 
1431
 
 
1432
/***
 
1433
****
 
1434
***/
 
1435
 
 
1436
void
 
1437
tr_core_exec_json( TrCore * core, const char * json )
 
1438
{
 
1439
    g_message( "executing %s", json );
 
1440
    sendRequest( core, json, 0, NULL, NULL );
 
1441
}
 
1442
 
 
1443
void
 
1444
tr_core_exec( TrCore * core, const tr_benc * top )
 
1445
{
 
1446
    char * json = tr_bencToJSON( top );
 
1447
    tr_core_exec_json( core, json );
 
1448
    tr_free( json );
 
1449
}