source: trunk/gtk/tr_core.c @ 3178

Last change on this file since 3178 was 3178, checked in by charles, 15 years ago

modify the tr_stat struct as hashed out by charles_ and BentMyWookie?. sync gtk, ipc, and wx clients.

  • Property svn:keywords set to Date Rev Author Id
File size: 20.1 KB
Line 
1/******************************************************************************
2 * $Id: tr_core.c 3178 2007-09-26 01:55:04Z charles $
3 *
4 * Copyright (c) 2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <inttypes.h>
26#include <stdio.h>
27#include <string.h>
28
29#include <gtk/gtk.h>
30#include <glib/gi18n.h>
31
32#include <libtransmission/bencode.h>
33#include <libtransmission/transmission.h>
34#include <libtransmission/utils.h>
35
36#include "conf.h"
37#include "tr_core.h"
38#include "tr_prefs.h"
39#include "tr_torrent.h"
40#include "util.h"
41
42static void
43tr_core_init( GTypeInstance * instance, gpointer g_class );
44static void
45tr_core_class_init( gpointer g_class, gpointer g_class_data );
46static void
47tr_core_marshal_err( GClosure * closure, GValue * ret, guint count,
48                     const GValue * vals, gpointer hint, gpointer marshal );
49void
50tr_core_marshal_prompt( GClosure * closure, GValue * ret, guint count,
51                        const GValue * vals, gpointer hint, gpointer marshal );
52void
53tr_core_marshal_data( GClosure * closure, GValue * ret, guint count,
54                      const GValue * vals, gpointer hint, gpointer marshal );
55static void
56tr_core_dispose( GObject * obj );
57static void
58tr_core_insert( TrCore * self, TrTorrent * tor );
59static void
60tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg );
61
62GType
63tr_core_get_type( void )
64{
65    static GType type = 0;
66
67    if( 0 == type )
68    {
69        static const GTypeInfo info =
70        {
71            sizeof( TrCoreClass ),
72            NULL,                       /* base_init */
73            NULL,                       /* base_finalize */
74            tr_core_class_init,         /* class_init */
75            NULL,                       /* class_finalize */
76            NULL,                       /* class_data */
77            sizeof( TrCore ),
78            0,                          /* n_preallocs */
79            tr_core_init,               /* instance_init */
80            NULL,
81        };
82        type = g_type_register_static( G_TYPE_OBJECT, "TrCore", &info, 0 );
83    }
84
85    return type;
86}
87
88void
89tr_core_class_init( gpointer g_class, gpointer g_class_data SHUTUP )
90{
91    GObjectClass * gobject_class;
92    TrCoreClass  * core_class;
93
94    gobject_class = G_OBJECT_CLASS( g_class );
95    gobject_class->dispose = tr_core_dispose;
96
97    core_class = TR_CORE_CLASS( g_class );
98    core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ),
99                                       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
100                                       tr_core_marshal_err, G_TYPE_NONE,
101                                       2, G_TYPE_INT, G_TYPE_STRING );
102    core_class->promptsig = g_signal_new( "directory-prompt",
103                                          G_TYPE_FROM_CLASS( g_class ),
104                                          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
105                                          tr_core_marshal_prompt, G_TYPE_NONE,
106                                          3, G_TYPE_POINTER, G_TYPE_INT,
107                                          G_TYPE_BOOLEAN );
108    core_class->promptdatasig = g_signal_new( "directory-prompt-data",
109                                              G_TYPE_FROM_CLASS( g_class ),
110                                              G_SIGNAL_RUN_LAST, 0, NULL, NULL,
111                                              tr_core_marshal_data,
112                                              G_TYPE_NONE, 3, G_TYPE_STRING,
113                                              G_TYPE_UINT, G_TYPE_BOOLEAN );
114    core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ),
115                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
116                                        g_cclosure_marshal_VOID__VOID,
117                                        G_TYPE_NONE, 0 );
118    core_class->prefsig = g_signal_new( "prefs-changed",
119                                        G_TYPE_FROM_CLASS( g_class ),
120                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
121                                        g_cclosure_marshal_VOID__INT,
122                                        G_TYPE_NONE, 1, G_TYPE_INT );
123}
124
125void
126tr_core_marshal_err( GClosure * closure, GValue * ret SHUTUP, guint count,
127                     const GValue * vals, gpointer hint SHUTUP,
128                     gpointer marshal )
129{
130    typedef void (*TRMarshalErr)
131        ( gpointer, enum tr_core_err, const char *, gpointer );
132    TRMarshalErr     callback;
133    GCClosure      * cclosure = (GCClosure*) closure;
134    enum tr_core_err errcode;
135    const char     * errstr;
136    gpointer         inst, gdata;
137
138    g_return_if_fail( 3 == count );
139
140    inst    = g_value_peek_pointer( vals );
141    errcode = g_value_get_int( vals + 1 );
142    errstr  = g_value_get_string( vals + 2 );
143    gdata   = closure->data;
144
145    callback = (TRMarshalErr) ( NULL == marshal ?
146                                cclosure->callback : marshal );
147    callback( inst, errcode, errstr, gdata );
148}
149
150void
151tr_core_marshal_prompt( GClosure * closure, GValue * ret SHUTUP, guint count,
152                        const GValue * vals, gpointer hint SHUTUP,
153                        gpointer marshal )
154{
155    typedef void (*TRMarshalPrompt)
156        ( gpointer, GList *, enum tr_torrent_action, gboolean, gpointer );
157    TRMarshalPrompt        callback;
158    GCClosure            * cclosure = (GCClosure*) closure;
159    GList                * paths;
160    enum tr_torrent_action action;
161    gboolean               paused;
162    gpointer               inst, gdata;
163
164    g_return_if_fail( 4 == count );
165
166    inst    = g_value_peek_pointer( vals );
167    paths   = g_value_get_pointer( vals + 1 );
168    action  = g_value_get_int( vals + 2 );
169    paused  = g_value_get_boolean( vals + 3 );
170    gdata   = closure->data;
171
172    callback = (TRMarshalPrompt) ( NULL == marshal ?
173                                   cclosure->callback : marshal );
174    callback( inst, paths, action, paused, gdata );
175}
176
177void
178tr_core_marshal_data( GClosure * closure, GValue * ret SHUTUP, guint count,
179                      const GValue * vals, gpointer hint SHUTUP,
180                      gpointer marshal )
181{
182    typedef void (*TRMarshalPrompt)
183        ( gpointer, uint8_t *, size_t, gboolean, gpointer );
184    TRMarshalPrompt        callback;
185    GCClosure            * cclosure = (GCClosure*) closure;
186    uint8_t              * data;
187    size_t                 size;
188    gboolean               paused;
189    gpointer               inst, gdata;
190
191    g_return_if_fail( 4 == count );
192
193    inst    = g_value_peek_pointer( vals );
194    data    = (uint8_t *) g_value_get_string( vals + 1 );
195    size    = g_value_get_uint( vals + 2 );
196    paused  = g_value_get_boolean( vals + 3 );
197    gdata   = closure->data;
198
199    callback = (TRMarshalPrompt) ( NULL == marshal ?
200                                   cclosure->callback : marshal );
201    callback( inst, data, size, paused, gdata );
202}
203
204void
205tr_core_init( GTypeInstance * instance, gpointer g_class SHUTUP )
206{
207    TrCore * self = (TrCore *) instance;
208    GtkListStore * store;
209
210    /* column types for the model used to store torrent information */
211    /* keep this in sync with the enum near the bottom of tr_core.h */
212    GType types[] =
213    {
214        /* info->name, info->totalSize, info->hashString, status, */
215        G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_STRING,    G_TYPE_INT,
216        /* error,   errorString,   percentComplete, percentDone,  rateDownload, rateUpload, */
217        G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,    G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
218        /* eta,     peersConnected, peersUploading, peersDownloading, seeders, */
219        G_TYPE_INT, G_TYPE_INT,     G_TYPE_INT,     G_TYPE_INT,       G_TYPE_INT,
220        /* leechers, completedFromTracker, downloaded,    uploaded */
221        G_TYPE_INT,  G_TYPE_INT,           G_TYPE_UINT64, G_TYPE_UINT64,
222        /* left,       tracker,               TrTorrent object, ID for IPC */
223        G_TYPE_UINT64, TR_TRACKER_BOXED_TYPE, TR_TORRENT_TYPE,  G_TYPE_INT,
224    };
225
226#ifdef REFDBG
227    fprintf( stderr, "core    %p init\n", self );
228#endif
229
230    /* create the model used to store torrent data */
231    g_assert( ALEN( types ) == MC_ROW_COUNT );
232    store = gtk_list_store_newv( MC_ROW_COUNT, types );
233
234    self->model    = GTK_TREE_MODEL( store );
235    self->handle   = tr_init( "gtk" );
236    self->nextid   = 1;
237    self->quitting = FALSE;
238    self->disposed = FALSE;
239}
240
241void
242tr_core_dispose( GObject * obj )
243{
244    TrCore       * self = (TrCore *) obj;
245    GObjectClass * parent;
246    GtkTreeIter    iter;
247    TrTorrent    * tor;
248
249    if( self->disposed )
250    {
251        return;
252    }
253    self->disposed = TRUE;
254
255#ifdef REFDBG
256    fprintf( stderr, "core    %p dispose\n", self );
257#endif
258
259    /* sever all remaining torrents in the model */
260    if( gtk_tree_model_get_iter_first( self->model, &iter ) ) do
261    {
262        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
263        tr_torrent_sever( tor );
264        g_object_unref( tor );
265    }
266    while( gtk_tree_model_iter_next( self->model, &iter ) );
267    g_object_unref( self->model );
268
269#ifdef REFDBG
270    fprintf( stderr, "core    %p dead\n", self );
271#endif
272
273    /* close the libtransmission instance */
274    tr_close( self->handle );
275
276    /* Chain up to the parent class */
277    parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) );
278    parent->dispose( obj );
279}
280
281TrCore *
282tr_core_new( void )
283{
284    return g_object_new( TR_CORE_TYPE, NULL );
285}
286
287GtkTreeModel *
288tr_core_model( TrCore * self )
289{
290    g_return_val_if_fail (TR_IS_CORE(self), NULL);
291
292    return self->disposed ? NULL : self->model;
293}
294
295tr_handle *
296tr_core_handle( TrCore * self )
297{
298    g_return_val_if_fail (TR_IS_CORE(self), NULL);
299
300    return self->disposed ? NULL : self->handle;
301}
302
303void
304tr_core_shutdown( TrCore * self )
305{
306    GtkTreeIter iter;
307
308    TR_IS_CORE( self );
309
310    if( self->disposed )
311        return;
312
313    g_assert( !self->quitting );
314    self->quitting = TRUE;
315
316    /* try to stop all the torrents nicely */
317    if ( gtk_tree_model_get_iter_first( self->model, &iter) ) do {
318        TrTorrent * tor;
319        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
320        tr_torrent_sever( tor );
321        g_object_unref( tor );
322    } while( gtk_list_store_remove( GTK_LIST_STORE(self->model), &iter ) );
323
324    /* shut down nat traversal */
325    tr_natTraversalEnable( self->handle, 0 );
326}
327
328gboolean
329tr_core_quiescent( TrCore * self )
330{
331    const tr_handle_status * hstat;
332
333    TR_IS_CORE( self );
334    g_assert( self->quitting );
335
336    if( self->disposed )
337        return TRUE;
338
339    if ( tr_torrentCount( self->handle ) != 0 )
340        return FALSE;
341
342    hstat = tr_handleStatus( self->handle );
343    return TR_NAT_TRAVERSAL_DISABLED == hstat->natTraversalStatus;
344}
345
346void
347tr_core_save( TrCore * self )
348{
349    benc_val_t  state;
350    int         count;
351    GtkTreeIter iter;
352    TrTorrent * tor;
353    char      * errstr;
354    GList     * saved, * ii;
355
356    TR_IS_CORE( self );
357
358    count = gtk_tree_model_iter_n_children( self->model, NULL );
359
360    tr_bencInit( &state, TYPE_LIST );
361    if( tr_bencListReserve( &state, count ) )
362    {
363        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, "malloc failure" );
364        return;
365    }
366
367    saved = NULL;
368    if( gtk_tree_model_get_iter_first( self->model, &iter) ) do
369    {
370        benc_val_t * item = tr_bencListAdd( &state );
371        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
372        if( tr_torrent_get_state( tor, item ) )
373        {
374            saved = g_list_append( saved, tor );
375        }
376        else
377        {
378            tr_bencFree( item );
379            tr_bencInitStr( item, NULL, 0, 1 );
380        }
381        g_object_unref( tor );
382    }
383    while( gtk_tree_model_iter_next( self->model, &iter ) );
384
385    errstr = NULL;
386    cf_savestate( &state, &errstr );
387    tr_bencFree( &state );
388    if( NULL != errstr )
389    {
390        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, errstr );
391        g_free( errstr );
392    }
393    else
394    {
395        for( ii = saved; NULL != ii; ii = ii->next )
396        {
397            tr_torrent_state_saved( ii->data );
398        }
399    }
400
401    g_list_free( saved );
402}
403
404int
405tr_core_load( TrCore * self, gboolean forcepaused )
406{
407    int i;
408    int flags;
409    int count = 0;
410    tr_torrent ** torrents;
411    const char * destination;
412
413    TR_IS_CORE( self );
414
415    destination = getdownloaddir( );
416
417    flags = 0;
418    if( forcepaused )
419         flags |= TR_FLAG_PAUSED;
420
421    torrents = tr_loadTorrents ( self->handle, destination, flags, &count );
422    for( i=0; i<count; ++i )
423        tr_core_insert( self, tr_torrent_new_preexisting( torrents[i] ) );
424    tr_free( torrents );
425
426    return count;
427}
428
429gboolean
430tr_core_add( TrCore * self, const char * path, enum tr_torrent_action act,
431             gboolean paused )
432{
433    GList * list;
434    int     ret;
435
436    TR_IS_CORE( self );
437
438    list = g_list_append( NULL, (void *) path );
439    ret  = tr_core_add_list( self, list, act, paused );
440    g_list_free( list );
441
442    return 1 == ret;
443}
444
445gboolean
446tr_core_add_dir( TrCore * self, const char * path, const char * dir,
447                 enum tr_torrent_action act, gboolean paused )
448{
449    TrTorrent * tor;
450    char      * errstr;
451
452    TR_IS_CORE( self );
453
454    errstr = NULL;
455    tor = tr_torrent_new( self->handle, path, dir, act, paused, &errstr );
456    if( NULL == tor )
457    {
458        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
459        g_free( errstr );
460        return FALSE;
461    }
462    g_assert( NULL == errstr );
463
464    tr_core_insert( self, tor );
465
466    return TRUE;
467}
468
469int
470tr_core_add_list( TrCore * self, GList * paths, enum tr_torrent_action act,
471                  gboolean paused )
472{
473    const char  * pref = tr_prefs_get( PREF_ID_ASKDIR );
474    TrCoreClass * class;
475    int           count;
476
477    TR_IS_CORE( self );
478
479    if( strbool( pref ) )
480    {
481        class = g_type_class_peek( TR_CORE_TYPE );
482        g_signal_emit( self, class->promptsig, 0, paths, act, paused );
483        return 0;
484    }
485
486    pref = getdownloaddir();
487    count = 0;
488    for( ; paths; paths=paths->next )
489        if( tr_core_add_dir( self, paths->data, pref, act, paused ) )
490            count++;
491
492    return count;
493}
494
495gboolean
496tr_core_add_data( TrCore * self, uint8_t * data, size_t size, gboolean paused )
497{
498    const char  * pref = tr_prefs_get( PREF_ID_ASKDIR );
499
500    TR_IS_CORE( self );
501
502    if( strbool( pref ) )
503    {
504        TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
505        g_signal_emit( self, class->promptdatasig, 0, data, size, paused );
506        return FALSE;
507    }
508
509    return tr_core_add_data_dir( self, data, size, getdownloaddir(), paused );
510}
511
512gboolean
513tr_core_add_data_dir( TrCore * self, uint8_t * data, size_t size,
514                      const char * dir, gboolean paused )
515{
516    TrTorrent * tor;
517    char      * errstr = NULL;
518
519    TR_IS_CORE( self );
520
521    tor = tr_torrent_new_with_data( self->handle, data, size, dir,
522                                    paused, &errstr );
523    if( NULL == tor )
524    {
525        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
526        g_free( errstr );
527        return FALSE;
528    }
529    g_assert( NULL == errstr );
530
531    tr_core_insert( self, tor );
532
533    return TRUE;
534}
535
536void
537tr_core_torrents_added( TrCore * self )
538{
539    TR_IS_CORE( self );
540
541    tr_core_update( self );
542    tr_core_save( self );
543    tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL );
544}
545
546void
547tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter )
548{
549    TrTorrent * tor;
550
551    TR_IS_CORE( self );
552
553    gtk_tree_model_get( self->model, iter, MC_TORRENT, &tor, -1 );
554    gtk_list_store_remove( GTK_LIST_STORE( self->model ), iter );
555    tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
556
557    tr_torrent_sever( tor );
558}
559
560void
561tr_core_insert( TrCore * self, TrTorrent * tor )
562{
563    GtkTreeIter iter;
564    const tr_info * inf;
565
566    gtk_list_store_append( GTK_LIST_STORE( self->model ), &iter );
567    inf = tr_torrent_info( tor );
568    gtk_list_store_set( GTK_LIST_STORE( self->model ), &iter,
569                        MC_NAME,    inf->name,
570                        MC_SIZE,    inf->totalSize,
571                        MC_HASH,    inf->hashString,
572                        MC_TORRENT, tor,
573                        MC_ID,      self->nextid,
574                        -1);
575    g_object_unref( tor );
576    self->nextid++;
577}
578
579void
580tr_core_update( TrCore * self )
581{
582    GtkTreeIter iter;
583    TrTorrent * tor;
584    const tr_stat * st;
585
586    TR_IS_CORE( self );
587
588    if( gtk_tree_model_get_iter_first( self->model, &iter ) ) do
589    {
590        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
591        st = tr_torrent_stat( tor );
592        g_object_unref( tor );
593        tr_torrent_check_seeding_cap ( tor );
594
595        /* XXX find out if setting the same data emits changed signal */
596        gtk_list_store_set( GTK_LIST_STORE( self->model ), &iter,
597                            MC_STAT,        st->status,
598                            MC_ERR,         st->error,
599                            MC_TERR,        st->errorString,
600                            MC_PROG_C,      st->percentComplete,
601                            MC_PROG_D,      st->percentDone,
602                            MC_DRATE,       st->rateDownload,
603                            MC_URATE,       st->rateUpload,
604                            MC_ETA,         st->eta,
605                            MC_PEERS,       st->peersConnected,
606                            MC_UPEERS,      st->peersGettingFromUs,
607                            MC_DPEERS,      st->peersSendingToUs,
608                            MC_SEED,        st->seeders,
609                            MC_LEECH,       st->leechers,
610                            MC_DONE,        st->completedFromTracker,
611                            MC_TRACKER,     st->tracker,
612                            MC_DOWN,        st->downloadedEver,
613                            MC_UP,          st->uploadedEver,
614                            MC_LEFT,        st->leftUntilDone,
615                            -1 );
616    }
617    while( gtk_tree_model_iter_next( self->model, &iter ) );
618}
619
620void
621tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg )
622{
623    TrCoreClass * class;
624
625    class = g_type_class_peek( TR_CORE_TYPE );
626    g_signal_emit( self, class->errsig, 0, type, msg );
627}
628
629void
630tr_core_quit( TrCore * self )
631{
632    TrCoreClass * class;
633
634    TR_IS_CORE( self );
635
636    class = g_type_class_peek( TR_CORE_TYPE );
637    g_signal_emit( self, class->quitsig, 0 );
638}
639
640void
641tr_core_set_pref( TrCore * self, int id, const char * val )
642{
643    const char  * name, * old;
644    char        * errstr;
645    TrCoreClass * class;
646
647    TR_IS_CORE( self );
648
649    /* don't change anything if the new value is the same as the old one */
650    name = tr_prefs_name( id );
651    old = cf_getpref( name );
652    if( !tr_strcmp( old, val ) )
653        return;
654
655    cf_setpref( name, val );
656
657    /* write prefs to disk */
658    cf_saveprefs( &errstr );
659    if( NULL != errstr )
660    {
661        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, errstr );
662        g_free( errstr );
663    }
664
665    /* signal a pref change */
666    class = g_type_class_peek( TR_CORE_TYPE );
667    g_signal_emit( self, class->prefsig, 0, id );
668}
669
670void
671tr_core_set_pref_bool( TrCore * self, int id, gboolean val )
672{
673    tr_core_set_pref( self, id, ( val ? "yes" : "no" ) );
674}
675
676void
677tr_core_set_pref_int( TrCore * self, int id, int val )
678{
679    char buf[32];
680
681    g_snprintf( buf, sizeof buf, "%i", val );
682
683    tr_core_set_pref( self, id, buf );
684}
Note: See TracBrowser for help on using the repository browser.