source: trunk/gtk/tr_torrent.c @ 3111

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

remove the backwards compatability typedefs at BentMyWookie?'s suggestion. update libT, gtk, daemon, and cli accordingly...

  • Property svn:keywords set to Date Rev Author Id
File size: 15.5 KB
Line 
1/******************************************************************************
2 * $Id: tr_torrent.c 3111 2007-09-20 20:14:13Z charles $
3 *
4 * Copyright (c) 2006-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 <string.h>
26#include <unistd.h>
27
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
30
31#include <libtransmission/transmission.h>
32#include <libtransmission/bencode.h>
33
34#include "tr_prefs.h"
35#include "tr_torrent.h"
36#include "util.h"
37
38enum {
39  TR_TORRENT_HANDLE = 1,
40  TR_TORRENT_DIR,
41};
42
43static void
44tr_torrent_init(GTypeInstance *instance, gpointer g_class);
45static void
46tr_torrent_set_property(GObject *object, guint property_id,
47                        const GValue *value, GParamSpec *pspec);
48static void
49tr_torrent_get_property(GObject *object, guint property_id,
50                        GValue *value, GParamSpec *pspec);
51static void
52tr_torrent_class_init(gpointer g_class, gpointer g_class_data);
53static void
54tr_torrent_dispose(GObject *obj);
55static void
56tr_torrent_set_folder(TrTorrent *tor);
57
58static gpointer
59tracker_boxed_fake_copy( gpointer boxed )
60{
61    return boxed;
62}
63
64static void
65tracker_boxed_fake_free( gpointer boxed SHUTUP )
66{
67}
68
69GType
70tr_tracker_boxed_get_type( void )
71{
72    static GType type = 0;
73
74    if( 0 == type )
75    {
76        type = g_boxed_type_register_static( "TrTrackerBoxed",
77                                             tracker_boxed_fake_copy,
78                                             tracker_boxed_fake_free );
79    }
80
81    return type;
82}
83
84GType
85tr_torrent_get_type(void) {
86  static GType type = 0;
87
88  if(0 == type) {
89    static const GTypeInfo info = {
90      sizeof (TrTorrentClass),
91      NULL,   /* base_init */
92      NULL,   /* base_finalize */
93      tr_torrent_class_init,   /* class_init */
94      NULL,   /* class_finalize */
95      NULL,   /* class_data */
96      sizeof (TrTorrent),
97      0,      /* n_preallocs */
98      tr_torrent_init, /* instance_init */
99      NULL,
100    };
101    type = g_type_register_static(G_TYPE_OBJECT, "TrTorrent", &info, 0);
102  }
103  return type;
104}
105
106static void
107tr_torrent_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
108  GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
109  GParamSpec *pspec;
110
111  gobject_class->set_property = tr_torrent_set_property;
112  gobject_class->get_property = tr_torrent_get_property;
113  gobject_class->dispose = tr_torrent_dispose;
114
115  pspec = g_param_spec_pointer("torrent-handle", "Torrent handle",
116                               "Torrent handle from libtransmission",
117                               G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
118  g_object_class_install_property(gobject_class, TR_TORRENT_HANDLE, pspec);
119
120  pspec = g_param_spec_string("download-directory", "Download directory",
121                              "Directory to download files to", NULL,
122                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
123  g_object_class_install_property(gobject_class, TR_TORRENT_DIR, pspec);
124}
125
126static void
127tr_torrent_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
128  TrTorrent *self = (TrTorrent *)instance;
129
130#ifdef REFDBG
131  fprintf( stderr, "torrent %p init\n", self );
132#endif
133
134  self->handle = NULL;
135  self->lastStatTime = 0;
136  self->dir = NULL;
137  self->delfile = NULL;
138  self->severed = FALSE;
139  self->disposed = FALSE;
140}
141
142static void
143tr_torrent_set_property(GObject *object, guint property_id,
144                        const GValue *value, GParamSpec *pspec) {
145  TrTorrent *self = (TrTorrent*)object;
146
147  if(self->severed)
148    return;
149
150  switch(property_id) {
151    case TR_TORRENT_HANDLE:
152      g_assert(NULL == self->handle);
153      self->handle = g_value_get_pointer(value);
154      if(NULL != self->handle && NULL != self->dir)
155        tr_torrent_set_folder(self);
156      break;
157    case TR_TORRENT_DIR:
158      g_assert(NULL == self->dir);
159      self->dir = g_value_dup_string(value);
160      if(NULL != self->handle && NULL != self->dir)
161        tr_torrent_set_folder(self);
162      break;
163    default:
164      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
165      break;
166  }
167}
168
169static void
170tr_torrent_get_property(GObject *object, guint property_id,
171                        GValue *value, GParamSpec *pspec) {
172  TrTorrent *self = (TrTorrent*)object;
173
174  if(self->severed)
175    return;
176
177  switch(property_id) {
178    case TR_TORRENT_HANDLE:
179      g_value_set_pointer(value, self->handle);
180      break;
181    case TR_TORRENT_DIR:
182      g_value_set_string(value, (NULL != self->dir ? self->dir :
183                                 tr_torrentGetFolder(self->handle)));
184      break;
185    default:
186      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
187      break;
188  }
189}
190
191static void
192tr_torrent_dispose(GObject *obj) {
193  GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
194  TrTorrent *self = (TrTorrent*)obj;
195
196  if(self->disposed)
197    return;
198  self->disposed = TRUE;
199
200#ifdef REFDBG
201  fprintf( stderr, "torrent %p dispose\n", self );
202#endif
203
204  if( !self->severed )
205      tr_torrent_sever( self );
206
207  g_free (self->delfile);
208  g_free (self->dir);
209
210  /* Chain up to the parent class */
211  parent->dispose(obj);
212}
213
214void
215tr_torrent_sever( TrTorrent * self )
216{
217    g_return_if_fail (TR_IS_TORRENT( self ));
218
219    if( !self->severed )
220    {
221        self->severed = TRUE;
222
223        if( self->handle )
224            tr_torrentClose( self->handle );
225    }
226}
227
228tr_torrent *
229tr_torrent_handle(TrTorrent *tor)
230{
231    g_assert( TR_IS_TORRENT(tor) );
232
233    return tor->severed ? NULL : tor->handle;
234}
235
236static tr_stat*
237refreshStat( TrTorrent * tor )
238{
239    tor->lastStatTime= time( NULL );
240    tor->stat = *tr_torrentStat( tor->handle );
241    return &tor->stat;
242}
243
244const tr_stat *
245tr_torrent_stat(TrTorrent *tor)
246{
247    g_assert( TR_IS_TORRENT(tor) );
248
249    if( !tor->severed && tor->lastStatTime!=time(NULL) )
250        refreshStat( tor );
251
252    return &tor->stat;
253}
254
255const tr_info *
256tr_torrent_info(TrTorrent *tor) {
257  TR_IS_TORRENT(tor);
258
259  if(tor->severed)
260    return NULL;
261
262  return tr_torrentInfo(tor->handle);
263}
264
265void
266tr_torrent_start( TrTorrent * self )
267{
268    TR_IS_TORRENT( self );
269
270    if( !self->severed )
271        tr_torrentStart( self->handle );
272}
273
274void
275tr_torrent_stop( TrTorrent * self )
276{
277    TR_IS_TORRENT( self );
278
279    if( !self->severed )
280        tr_torrentStop( self->handle );
281}
282
283static TrTorrent *
284maketorrent( tr_torrent * handle )
285{
286    tr_torrentDisablePex( handle,
287                          !tr_prefs_get_bool_with_default( PREF_ID_PEX ) );
288
289    return g_object_new( TR_TORRENT_TYPE,
290                         "torrent-handle", handle,
291                         NULL);
292}
293
294TrTorrent*
295tr_torrent_new_preexisting( tr_torrent * tor )
296{
297    return maketorrent( tor );
298}
299
300
301TrTorrent *
302tr_torrent_new( tr_handle * back, const char *torrent, const char *dir,
303                enum tr_torrent_action act, gboolean paused, char **err )
304{
305  TrTorrent *ret;
306  tr_torrent *handle;
307  int errcode, flags;
308
309  g_assert(NULL != dir);
310
311  *err = NULL;
312
313  errcode = -1;
314
315  flags = ( TR_TOR_COPY == act || TR_TOR_MOVE == act ? TR_FLAG_SAVE : 0 );
316  if( paused )
317      flags |= TR_FLAG_PAUSED;
318
319  handle = tr_torrentInit( back, torrent, dir, flags, &errcode );
320
321  if(NULL == handle) {
322    switch(errcode) {
323      case TR_EINVALID:
324        *err = g_strdup_printf(_("%s: not a valid torrent file"), torrent);
325        break;
326      case TR_EDUPLICATE:
327        *err = g_strdup_printf(_("%s: torrent is already open"), torrent);
328        break;
329      default:
330        *err = g_strdup(torrent);
331        break;
332    }
333    return NULL;
334  }
335
336  ret = maketorrent( handle );
337
338  if( TR_TOR_MOVE == act )
339    ret->delfile = g_strdup(torrent);
340
341  return ret;
342}
343
344TrTorrent *
345tr_torrent_new_with_data( tr_handle * back, uint8_t * data, size_t size,
346                          const char * dir, gboolean paused, char ** err )
347{
348    tr_torrent * handle;
349    int          errcode;
350    int          flags;
351
352    g_assert( NULL != dir );
353
354    *err = NULL;
355
356    flags = TR_FLAG_SAVE;
357    if( paused )
358        flags |= TR_FLAG_PAUSED;
359
360    errcode = -1;
361    handle  = tr_torrentInitData( back, data, size, dir, flags, &errcode );
362
363    if( NULL == handle )
364    {
365        switch( errcode )
366        {
367            case TR_EINVALID:
368                *err = g_strdup( _("not a valid torrent file") );
369                break;
370            case TR_EDUPLICATE:
371                *err = g_strdup( _("torrent is already open") );
372                break;
373            default:
374                *err = g_strdup( "" );
375                break;
376        }
377        return NULL;
378    }
379
380    return maketorrent( handle );
381}
382
383TrTorrent *
384tr_torrent_new_with_state( tr_handle * back, benc_val_t * state,
385                           gboolean forcedpause, char ** err )
386{
387  TrTorrent * ret;
388  tr_torrent * handle;
389  int ii, errcode;
390  int flags;
391  benc_val_t *name, *data;
392  char *torrent, *hash, *dir;
393  gboolean paused = FALSE;
394  gboolean seeding_cap_enabled = FALSE;
395  gdouble seeding_cap = 0.0;
396
397  *err = NULL;
398
399  if(TYPE_DICT != state->type)
400    return NULL;
401
402  torrent = hash = dir = NULL;
403
404  for(ii = 0; ii + 1 < state->val.l.count; ii += 2) {
405    name = state->val.l.vals + ii;
406    data = state->val.l.vals + ii + 1;
407    if(TYPE_STR == name->type &&
408       (TYPE_STR == data->type || TYPE_INT == data->type)) {
409      char * key = name->val.s.s;
410      char * val = data->val.s.s;
411           if (!strcmp (key, "torrent")) torrent = val;
412      else if (!strcmp (key, "hash")) hash = val;
413      else if (!strcmp (key, "dir")) dir = val;
414      else if (!strcmp (key, "paused")) paused = !!data->val.i;
415      else if (!strcmp (key, "seeding-cap-ratio")) seeding_cap = (data->val.i / 100.0);
416      else if (!strcmp (key, "seeding-cap-enabled")) seeding_cap_enabled = !!data->val.i;
417    }
418  }
419
420  if((NULL != torrent && NULL != hash) ||
421     (NULL == torrent && NULL == hash) || NULL == dir)
422    return NULL;
423
424  flags = 0;
425  if( paused || forcedpause )
426      flags |= TR_FLAG_PAUSED;
427
428  if( NULL != hash )
429    handle = tr_torrentInitSaved(back, hash, dir, flags, &errcode);
430  else
431    handle = tr_torrentInit(back, torrent, dir, flags, &errcode);
432
433  if(NULL == handle) {
434    torrent = ( NULL == hash ? torrent : hash );
435    switch(errcode) {
436      case TR_EINVALID:
437        *err = g_strdup_printf(_("%s: not a valid torrent file"), torrent);
438        break;
439      case TR_EDUPLICATE:
440        *err = g_strdup_printf(_("%s: torrent is already open"), torrent);
441        break;
442      default:
443        *err = g_strdup(torrent);
444        break;
445    }
446    return NULL;
447  }
448
449  ret = maketorrent( handle );
450  ret->seeding_cap = seeding_cap;
451  ret->seeding_cap_enabled = seeding_cap_enabled;
452  return ret;
453}
454
455gboolean
456tr_torrent_get_state( TrTorrent * tor, benc_val_t * state )
457{
458    const tr_info * inf;
459
460    TR_IS_TORRENT( tor );
461
462    if( tor->severed )
463    {
464        return FALSE;
465    }
466
467    inf = tr_torrentInfo( tor->handle );
468
469    tr_bencInit( state, TYPE_DICT );
470    if( tr_bencDictReserve( state, 3 ) )
471    {
472        return FALSE;
473    }
474
475    if( TR_FLAG_SAVE & inf->flags )
476    {
477        tr_bencInitStr( tr_bencDictAdd( state, "hash" ),
478                        inf->hashString, -1, 1 );
479    }
480    else
481    {
482        tr_bencInitStr( tr_bencDictAdd( state, "torrent" ),
483                        inf->torrent, -1, 1 );
484    }
485    tr_bencInitStr( tr_bencDictAdd( state, "dir" ),
486                    tr_torrentGetFolder( tor->handle ), -1, 1 );
487    tr_bencInitInt( tr_bencDictAdd( state, "paused" ),
488                    (refreshStat(tor)->status & TR_STATUS_INACTIVE) ? 1 : 0);
489    tr_bencInitInt( tr_bencDictAdd( state, "seeding-cap-ratio" ),
490                    (int)(tor->seeding_cap * 100.0)); /* two decimal places */
491    tr_bencInitInt( tr_bencDictAdd( state, "seeding-cap-enabled" ),
492                    tor->seeding_cap_enabled ? 1 : 0);
493
494    return TRUE;
495}
496
497/* XXX this should probably be done with a signal */
498void
499tr_torrent_state_saved(TrTorrent *tor) {
500  TR_IS_TORRENT(tor);
501
502  if(tor->severed)
503    return;
504
505  if(NULL != tor->delfile) {
506    unlink(tor->delfile);
507    g_free(tor->delfile);
508    tor->delfile = NULL;
509  }
510}
511
512static void
513tr_torrent_set_folder(TrTorrent *tor) {
514  char *wd;
515
516  if(NULL != tor->dir)
517    tr_torrentSetFolder(tor->handle, tor->dir);
518  else {
519    wd = g_new(char, MAX_PATH_LENGTH + 1);
520    tr_torrentSetFolder(tor->handle,
521                        (NULL == getcwd(wd, MAX_PATH_LENGTH + 1) ? "." : wd));
522    g_free(wd);
523  }
524}
525
526void
527tr_torrent_check_seeding_cap ( TrTorrent *gtor)
528{
529  const tr_stat * st = tr_torrent_stat( gtor );
530  if ((gtor->seeding_cap_enabled) && (st->ratio >= gtor->seeding_cap))
531    tr_torrent_stop (gtor);
532}
533void
534tr_torrent_set_seeding_cap_ratio ( TrTorrent *gtor, gdouble ratio )
535{
536  gtor->seeding_cap = ratio;
537  tr_torrent_check_seeding_cap (gtor);
538}
539void
540tr_torrent_set_seeding_cap_enabled ( TrTorrent *gtor, gboolean b )
541{
542  if ((gtor->seeding_cap_enabled = b))
543    tr_torrent_check_seeding_cap (gtor);
544}
545
546char *
547tr_torrent_status_str ( TrTorrent * gtor )
548{
549    char * top = 0;
550
551    const tr_stat * st = tr_torrent_stat( gtor );
552
553    const int tpeers = MAX (st->peersConnected, 0);
554    const int upeers = MAX (st->peersGettingFromUs, 0);
555    const int eta = st->eta;
556    double prog = st->percentDone * 100.0; /* [0...100] */
557
558    switch( st->status )
559    {
560        case TR_STATUS_CHECK_WAIT:
561            prog = st->recheckProgress * 100.0; /* [0...100] */
562            top = g_strdup_printf( _("Waiting to check existing files (checked %.1f%%)"), prog );
563            break;
564
565        case TR_STATUS_CHECK:
566            prog = st->recheckProgress * 100.0; /* [0...100] */
567            top = g_strdup_printf( _("Checking existing files (checked %.1f%%)"), prog );
568            break;
569
570        case TR_STATUS_DOWNLOAD:
571            if( eta < 0 )
572                top = g_strdup_printf( _("Stalled (%.1f%%)"), prog );
573            else {
574                char * timestr = readabletime(eta);
575                top = g_strdup_printf( _("%s remaining (%.1f%%)"), timestr, prog );
576                g_free(timestr);
577            }
578            break;
579
580        case TR_STATUS_DONE:
581            top = g_strdup_printf(
582                ngettext( "Uploading to %d of %d peer",
583                          "Uploading to %d of %d peers", tpeers ),
584                          upeers, tpeers );
585            break;
586
587        case TR_STATUS_SEED:
588            top = g_strdup_printf(
589                ngettext( "Seeding to %d of %d peer",
590                          "Seeding to %d of %d peers", tpeers ),
591                          upeers, tpeers );
592            break;
593
594        case TR_STATUS_STOPPING:
595            top = g_strdup( _("Stopping...") );
596            break;
597
598        case TR_STATUS_STOPPED:
599            top = g_strdup_printf( _("Stopped (%.1f%%)"), prog );
600            break;
601
602        default:
603            top = g_strdup_printf("Unrecognized state: %d", st->status );
604            break;
605
606    }
607
608    return top;
609}
Note: See TracBrowser for help on using the repository browser.