source: trunk/gtk/tr_torrent.c @ 320

Last change on this file since 320 was 320, checked in by joshe, 16 years ago

Add support to the GTK GUI for saving private copies of torrent files.
The prefs dialog for this sucks, but it should work.

  • Property svn:keywords set to Date Rev Author Id
File size: 12.7 KB
Line 
1/*
2  $Id: tr_torrent.c 320 2006-06-10 06:53:20Z joshe $
3
4  Copyright (c) 2006 Joshua Elsasser. All rights reserved.
5   
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions
8  are met:
9   
10   1. Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15   
16  THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND
17  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include <string.h>
30#include <unistd.h>
31
32#include <gtk/gtk.h>
33#include <glib/gi18n.h>
34
35#define TR_WANT_BACKEND_PRIVATE
36
37#include "transmission.h"
38#include "bencode.h"
39
40#include "tr_backend.h"
41#include "tr_torrent.h"
42#include "util.h"
43
44enum {
45  TR_TORRENT_HANDLE = 1,
46  TR_TORRENT_BACKEND,
47  TR_TORRENT_DIR,
48  TR_TORRENT_PAUSED,
49};
50
51static void
52tr_torrent_init(GTypeInstance *instance, gpointer g_class);
53static void
54tr_torrent_set_property(GObject *object, guint property_id,
55                        const GValue *value, GParamSpec *pspec);
56static void
57tr_torrent_get_property(GObject *object, guint property_id,
58                        GValue *value, GParamSpec *pspec);
59static void
60tr_torrent_class_init(gpointer g_class, gpointer g_class_data);
61static void
62tr_torrent_dispose(GObject *obj);
63static void
64tr_torrent_set_folder(TrTorrent *tor);
65static gboolean
66tr_torrent_paused(TrTorrent *tor);
67
68GType
69tr_torrent_get_type(void) {
70  static GType type = 0;
71
72  if(0 == type) {
73    static const GTypeInfo info = {
74      sizeof (TrTorrentClass),
75      NULL,   /* base_init */
76      NULL,   /* base_finalize */
77      tr_torrent_class_init,   /* class_init */
78      NULL,   /* class_finalize */
79      NULL,   /* class_data */
80      sizeof (TrTorrent),
81      0,      /* n_preallocs */
82      tr_torrent_init, /* instance_init */
83      NULL,
84    };
85    type = g_type_register_static(G_TYPE_OBJECT, "TrTorrentType", &info, 0);
86  }
87  return type;
88}
89
90static void
91tr_torrent_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
92  GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
93  TrTorrentClass *klass = TR_TORRENT_CLASS(g_class);
94  GParamSpec *pspec;
95
96  gobject_class->set_property = tr_torrent_set_property;
97  gobject_class->get_property = tr_torrent_get_property;
98  gobject_class->dispose = tr_torrent_dispose;
99
100  pspec = g_param_spec_pointer("torrent-handle", "Torrent handle",
101                               "Torrent handle from libtransmission",
102                               G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
103  g_object_class_install_property(gobject_class, TR_TORRENT_HANDLE, pspec);
104
105  pspec = g_param_spec_object("backend", "Backend",
106                              "Libtransmission backend object",
107                              TR_BACKEND_TYPE,
108                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
109  g_object_class_install_property(gobject_class, TR_TORRENT_BACKEND, pspec);
110
111  pspec = g_param_spec_string("download-directory", "Download directory",
112                              "Directory to download files to", NULL,
113                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
114  g_object_class_install_property(gobject_class, TR_TORRENT_DIR, pspec);
115
116  pspec = g_param_spec_boolean("paused", "Paused",
117                               "Is the torrent paused or running", TRUE,
118                               G_PARAM_READWRITE);
119  g_object_class_install_property(gobject_class, TR_TORRENT_PAUSED, pspec);
120
121  klass->paused_signal_id = g_signal_newv("politely-stopped",
122                                          G_TYPE_FROM_CLASS(g_class),
123                                          G_SIGNAL_RUN_LAST, NULL, NULL, NULL,
124                                          g_cclosure_marshal_VOID__VOID,
125                                          G_TYPE_NONE, 0, NULL);
126}
127
128static void
129tr_torrent_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
130  TrTorrent *self = (TrTorrent *)instance;
131
132  self->handle = NULL;
133  self->back = NULL;
134  self->dir = NULL;
135  self->closing = FALSE;
136  self->delfile = NULL;
137  self->disposed = FALSE;
138}
139
140static void
141tr_torrent_set_property(GObject *object, guint property_id,
142                        const GValue *value, GParamSpec *pspec) {
143  TrTorrent *self = (TrTorrent*)object;
144
145  if(self->disposed)
146    return;
147
148  switch(property_id) {
149    case TR_TORRENT_HANDLE:
150      g_assert(NULL == self->handle);
151      self->handle = g_value_get_pointer(value);
152      if(NULL != self->handle && NULL != self->dir)
153        tr_torrent_set_folder(self);
154      break;
155    case TR_TORRENT_BACKEND:
156      g_assert(NULL == self->back);
157      self->back = g_object_ref(g_value_get_object(value));
158      break;
159    case TR_TORRENT_DIR:
160      g_assert(NULL == self->dir);
161      self->dir = g_value_dup_string(value);
162      if(NULL != self->handle && NULL != self->dir)
163        tr_torrent_set_folder(self);
164      break;
165    case TR_TORRENT_PAUSED:
166      g_assert(NULL != self->handle);
167      if(tr_torrent_paused(self) != g_value_get_boolean(value))
168        (g_value_get_boolean(value) ? tr_torrentStop : tr_torrentStart)
169          (self->handle);
170      break;
171    default:
172      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
173      break;
174  }
175}
176
177static void
178tr_torrent_get_property(GObject *object, guint property_id,
179                        GValue *value, GParamSpec *pspec) {
180  TrTorrent *self = (TrTorrent*)object;
181
182  if(self->disposed)
183    return;
184
185  switch(property_id) {
186    case TR_TORRENT_HANDLE:
187      g_value_set_pointer(value, self->handle);
188      break;
189    case TR_TORRENT_BACKEND:
190      g_value_set_object(value, self->back);
191      break;
192    case TR_TORRENT_DIR:
193      g_value_set_string(value, (NULL != self->dir ? self->dir :
194                                 tr_torrentGetFolder(self->handle)));
195      break;
196    case TR_TORRENT_PAUSED:
197      g_value_set_boolean(value, tr_torrent_paused(self));
198      break;
199    default:
200      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
201      break;
202  }
203}
204
205static void
206tr_torrent_dispose(GObject *obj) {
207  GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
208  TrTorrent *self = (TrTorrent*)obj;
209
210  if(self->disposed)
211    return;
212  self->disposed = TRUE;
213
214  if(NULL != self->handle) {
215    if(!tr_torrent_paused(self))
216      tr_torrentStop(self->handle);
217    tr_torrentClose(tr_backend_handle(TR_BACKEND(self->back)), self->handle);
218    self->handle = NULL;
219  }
220
221  if(NULL != self->back) {
222    g_object_unref(self->back);
223    self->back = NULL;
224  }
225
226  if(NULL != self->delfile)
227    g_free(self->delfile);
228
229  /* Chain up to the parent class */
230  parent->dispose(obj);
231}
232
233tr_torrent_t *
234tr_torrent_handle(TrTorrent *tor) {
235  TR_IS_TORRENT(tor);
236
237  if(tor->disposed)
238    return NULL;
239
240  return tor->handle;
241}
242
243tr_stat_t *
244tr_torrent_stat(TrTorrent *tor) {
245  TR_IS_TORRENT(tor);
246
247  if(tor->disposed)
248    return NULL;
249
250  return tr_torrentStat(tor->handle);
251}
252
253tr_info_t *
254tr_torrent_info(TrTorrent *tor) {
255  TR_IS_TORRENT(tor);
256
257  if(tor->disposed)
258    return NULL;
259
260  return tr_torrentInfo(tor->handle);
261}
262
263TrTorrent *
264tr_torrent_new(GObject *backend, const char *torrent, const char *dir,
265               guint flags, char **err) {
266  TrTorrent *ret;
267  tr_torrent_t *handle;
268  tr_handle_t *back;
269  int errcode, trflags;
270
271  TR_IS_BACKEND(backend);
272  g_assert(NULL != dir);
273
274  *err = NULL;
275
276  back = tr_backend_handle(TR_BACKEND(backend));
277  trflags = 0;
278  if((TR_TORNEW_SAVE_COPY|TR_TORNEW_SAVE_MOVE) & flags)
279    trflags |= TR_FSAVEPRIVATE;
280  errcode = -1;
281
282  if(TR_TORNEW_LOAD_SAVED & flags)
283    handle = tr_torrentInitSaved(back, torrent, 0, &errcode);
284  else
285    handle = tr_torrentInit(back, torrent, trflags, &errcode);
286
287  if(NULL == handle) {
288    switch(errcode) {
289      case TR_EINVALID:
290        *err = g_strdup_printf(_("%s: not a valid torrent file"), torrent);
291        break;
292      case TR_EDUPLICATE:
293        *err = g_strdup_printf(_("%s: torrent is already open"), torrent);
294        break;
295      default:
296        *err = g_strdup(torrent);
297        break;
298    }
299    return NULL;
300  }
301
302  ret = g_object_new(TR_TORRENT_TYPE, "torrent-handle", handle,
303                     "backend", backend, "download-directory", dir, NULL);
304  tr_backend_add_torrent(TR_BACKEND(backend), G_OBJECT(ret));
305 
306  g_object_set(ret, "paused", (TR_TORNEW_PAUSED & flags ? TRUE : FALSE), NULL);
307
308  if(TR_TORNEW_SAVE_MOVE & flags)
309    ret->delfile = g_strdup(torrent);
310
311  return ret;
312}
313
314TrTorrent *
315tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err) {
316  int ii;
317  benc_val_t *name, *data;
318  char *torrent, *hash, *dir;
319  gboolean hadpaused, paused;
320  guint flags;
321
322  *err = NULL;
323
324  if(TYPE_DICT != state->type)
325    return NULL;
326
327  torrent = hash = dir = NULL;
328  hadpaused = FALSE;
329  paused = FALSE;               /* silence stupid compiler warning */
330
331  for(ii = 0; ii + 1 < state->val.l.count; ii += 2) {
332    name = state->val.l.vals + ii;
333    data = state->val.l.vals + ii + 1;
334    if(TYPE_STR == name->type &&
335       (TYPE_STR == data->type || TYPE_INT == data->type)) {
336      if(0 == strcmp("torrent", name->val.s.s))
337        torrent = data->val.s.s;
338      if(0 == strcmp("hash", name->val.s.s))
339        hash = data->val.s.s;
340      else if(0 == strcmp("dir", name->val.s.s))
341        dir = data->val.s.s;
342      else if(0 == strcmp("paused", name->val.s.s)) {
343        hadpaused = TRUE;
344        paused = (data->val.i ? TRUE : FALSE);
345      }
346    }
347  }
348
349  if((NULL != torrent && NULL != hash) ||
350     (NULL == torrent && NULL == hash) || NULL == dir)
351    return NULL;
352
353  flags = 0;
354  if(hadpaused)
355    flags |= (paused ? TR_TORNEW_PAUSED : TR_TORNEW_RUNNING);
356  if(NULL != hash) {
357    flags |= TR_TORNEW_LOAD_SAVED;
358    torrent = hash;
359  }
360
361  return tr_torrent_new(backend, torrent, dir, flags, err);
362}
363
364#define SETSTRVAL(vv, ss) \
365  do { \
366    (vv)->type = TYPE_STR; \
367    (vv)->val.s.s = g_strdup((ss)); \
368    (vv)->val.s.i = strlen((ss)); \
369  } while(0)
370
371void
372tr_torrent_get_state(TrTorrent *tor, benc_val_t *state) {
373  tr_info_t *in = tr_torrentInfo(tor->handle);
374
375  TR_IS_TORRENT(tor);
376
377  if(tor->disposed)
378    return;
379
380  if(tor->closing)
381    return;
382
383  state->type = TYPE_DICT;
384  state->val.l.vals = g_new0(benc_val_t, 6);
385  state->val.l.alloc = state->val.l.count = 6;
386
387  if(TR_FSAVEPRIVATE & in->flags) {
388    SETSTRVAL(state->val.l.vals + 0, "hash");
389    SETSTRVAL(state->val.l.vals + 1, in->hashString);
390  } else {
391    SETSTRVAL(state->val.l.vals + 0, "torrent");
392    SETSTRVAL(state->val.l.vals + 1, in->torrent);
393  }
394  SETSTRVAL(state->val.l.vals + 2, "dir");
395  SETSTRVAL(state->val.l.vals + 3, tr_torrentGetFolder(tor->handle));
396  SETSTRVAL(state->val.l.vals + 4, "paused");
397  state->val.l.vals[5].type = TYPE_INT;
398  state->val.l.vals[5].val.i = tr_torrent_paused(tor);
399}
400
401/* XXX this should probably be done with a signal */
402void
403tr_torrent_state_saved(TrTorrent *tor) {
404  TR_IS_TORRENT(tor);
405
406  if(tor->disposed)
407    return;
408
409  if(NULL != tor->delfile) {
410    unlink(tor->delfile);
411    g_free(tor->delfile);
412    tor->delfile = NULL;
413  }
414}
415
416static void
417tr_torrent_set_folder(TrTorrent *tor) {
418  char *wd;
419
420  if(NULL != tor->dir)
421    tr_torrentSetFolder(tor->handle, tor->dir);
422  else {
423    wd = g_new(char, MAX_PATH_LENGTH + 1);
424    tr_torrentSetFolder(tor->handle,
425                        (NULL == getcwd(wd, MAX_PATH_LENGTH + 1) ? "." : wd));
426    g_free(wd);
427  }
428}
429
430static gboolean
431tr_torrent_paused(TrTorrent *tor) {
432  tr_stat_t *st = tr_torrentStat(tor->handle);
433
434  return (TR_STATUS_INACTIVE & st->status ? TRUE : FALSE);
435}
436
437void
438tr_torrent_stop_politely(TrTorrent *tor) {
439  tr_stat_t *st;
440
441  TR_IS_TORRENT(tor);
442
443  if(tor->disposed)
444    return;
445
446  if(!tor->closing) {
447    st = tr_torrent_stat(tor);
448    tor->closing = TRUE;
449    if(TR_STATUS_ACTIVE & st->status)
450      tr_torrentStop(tor->handle);
451  }
452}
453
454tr_stat_t *
455tr_torrent_stat_polite(TrTorrent *tor) {
456  TrTorrentClass *klass;
457  tr_stat_t *st = tr_torrentStat(tor->handle);
458
459  if(tor->disposed)
460    return st;
461
462  if(tor->closing && TR_STATUS_PAUSE & st->status) {
463    tor->closing = FALSE;
464    klass = g_type_class_peek(TR_TORRENT_TYPE);
465    g_signal_emit(tor, klass->paused_signal_id, 0, NULL);
466  }
467
468  return st;
469}
Note: See TracBrowser for help on using the repository browser.