source: trunk/gtk/tr_torrent.c @ 1190

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

Avoid doing some potentially bogus things when stopping and deleting a torrent.

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