source: trunk/gtk/tr_torrent.c @ 269

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

Wait and try to send a stopped event when removing a torrent.

  • Property svn:keywords set to Date Rev Author Id
File size: 11.6 KB
Line 
1/*
2  $Id: tr_torrent.c 269 2006-05-31 23:20:59Z 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->disposed = FALSE;
137}
138
139static void
140tr_torrent_set_property(GObject *object, guint property_id,
141                        const GValue *value, GParamSpec *pspec) {
142  TrTorrent *self = (TrTorrent*)object;
143
144  if(self->disposed)
145    return;
146
147  switch(property_id) {
148    case TR_TORRENT_HANDLE:
149      g_assert(NULL == self->handle);
150      self->handle = g_value_get_pointer(value);
151      if(NULL != self->handle && NULL != self->dir)
152        tr_torrent_set_folder(self);
153      break;
154    case TR_TORRENT_BACKEND:
155      g_assert(NULL == self->back);
156      self->back = g_object_ref(g_value_get_object(value));
157      break;
158    case TR_TORRENT_DIR:
159      g_assert(NULL == self->dir);
160      self->dir = g_value_dup_string(value);
161      if(NULL != self->handle && NULL != self->dir)
162        tr_torrent_set_folder(self);
163      break;
164    case TR_TORRENT_PAUSED:
165      g_assert(NULL != self->handle);
166      if(tr_torrent_paused(self) != g_value_get_boolean(value))
167        (g_value_get_boolean(value) ? tr_torrentStop : tr_torrentStart)
168          (self->handle);
169      break;
170    default:
171      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
172      break;
173  }
174}
175
176static void
177tr_torrent_get_property(GObject *object, guint property_id,
178                        GValue *value, GParamSpec *pspec) {
179  TrTorrent *self = (TrTorrent*)object;
180
181  if(self->disposed)
182    return;
183
184  switch(property_id) {
185    case TR_TORRENT_HANDLE:
186      g_value_set_pointer(value, self->handle);
187      break;
188    case TR_TORRENT_BACKEND:
189      g_value_set_object(value, self->back);
190      break;
191    case TR_TORRENT_DIR:
192      g_value_set_string(value, (NULL != self->dir ? self->dir :
193                                 tr_torrentGetFolder(self->handle)));
194      break;
195    case TR_TORRENT_PAUSED:
196      g_value_set_boolean(value, tr_torrent_paused(self));
197      break;
198    default:
199      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
200      break;
201  }
202}
203
204static void
205tr_torrent_dispose(GObject *obj) {
206  GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
207  TrTorrent *self = (TrTorrent*)obj;
208
209  if(self->disposed)
210    return;
211  self->disposed = TRUE;
212
213  if(NULL != self->handle) {
214    if(!tr_torrent_paused(self))
215      tr_torrentStop(self->handle);
216    tr_torrentClose(tr_backend_handle(TR_BACKEND(self->back)), self->handle);
217    self->handle = NULL;
218  }
219
220  if(NULL != self->back) {
221    g_object_unref(self->back);
222    self->back = NULL;
223  }
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               gboolean *paused, char **err) {
262  TrTorrent *ret;
263  tr_torrent_t *handle;
264  int errcode;
265
266  TR_IS_BACKEND(backend);
267  g_assert(NULL != dir);
268
269  *err = NULL;
270
271  errcode = -1;
272  handle = tr_torrentInit(tr_backend_handle(TR_BACKEND(backend)),
273                          torrent, &errcode);
274  if(NULL == handle) {
275    switch(errcode) {
276      case TR_EINVALID:
277        *err = g_strdup_printf(_("%s: not a valid torrent file"), torrent);
278        break;
279      case TR_EDUPLICATE:
280        *err = g_strdup_printf(_("%s: torrent is already open"), torrent);
281        break;
282      default:
283        *err = g_strdup(torrent);
284        break;
285    }
286    return NULL;
287  }
288
289  ret = g_object_new(TR_TORRENT_TYPE, "torrent-handle", handle,
290                     "backend", backend, "download-directory", dir, NULL);
291  tr_backend_add_torrent(TR_BACKEND(backend), G_OBJECT(ret));
292
293  g_object_set(ret, "paused", (NULL == paused ? FALSE : *paused), NULL);
294
295  return ret;
296}
297
298TrTorrent *
299tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err) {
300  int ii;
301  benc_val_t *name, *data;
302  char *torrent, *dir;
303  gboolean hadpaused, paused;
304
305  *err = NULL;
306
307  if(TYPE_DICT != state->type)
308    return NULL;
309
310  torrent = dir = NULL;
311  hadpaused = FALSE;
312
313  for(ii = 0; ii + 1 < state->val.l.count; ii += 2) {
314    name = state->val.l.vals + ii;
315    data = state->val.l.vals + ii + 1;
316    if(TYPE_STR == name->type &&
317       (TYPE_STR == data->type || TYPE_INT == data->type)) {
318      if(0 == strcmp("torrent", name->val.s.s))
319        torrent = data->val.s.s;
320      else if(0 == strcmp("dir", name->val.s.s))
321        dir = data->val.s.s;
322      else if(0 == strcmp("paused", name->val.s.s)) {
323        hadpaused = TRUE;
324        paused = (data->val.i ? TRUE : FALSE);
325      }
326    }
327  }
328
329  if(NULL == torrent || NULL == dir)
330    return NULL;
331
332  return tr_torrent_new(backend, torrent, dir,
333                        (hadpaused ? &paused : NULL), err);
334}
335
336void
337tr_torrent_get_state(TrTorrent *tor, benc_val_t *state) {
338  tr_info_t *in = tr_torrentInfo(tor->handle);
339  const char *strs[] = {
340    "torrent", in->torrent, "dir", tr_torrentGetFolder(tor->handle), "paused", 
341  };
342  unsigned int ii;
343  const unsigned int len = 6;
344
345  TR_IS_TORRENT(tor);
346
347  if(tor->disposed)
348    return;
349
350  if(tor->closing)
351    return;
352
353  state->type = TYPE_DICT;
354  state->val.l.vals = g_new0(benc_val_t, len);
355  state->val.l.alloc = state->val.l.count = len;
356
357  g_assert(len > ALEN(strs));
358  for(ii = 0; ii < ALEN(strs); ii++) {
359    state->val.l.vals[ii].type = TYPE_STR;
360    state->val.l.vals[ii].val.s.s = g_strdup(strs[ii]);
361    state->val.l.vals[ii].val.s.i = strlen(strs[ii]);
362  }
363
364  state->val.l.vals[ii].type = TYPE_INT;
365  state->val.l.vals[ii].val.i = tr_torrent_paused(tor);
366  ii++;
367
368  g_assert(len == ii);
369}
370
371static void
372tr_torrent_set_folder(TrTorrent *tor) {
373  char *wd;
374
375  if(NULL != tor->dir)
376    tr_torrentSetFolder(tor->handle, tor->dir);
377  else {
378    wd = g_new(char, MAX_PATH_LENGTH + 1);
379    tr_torrentSetFolder(tor->handle,
380                        (NULL == getcwd(wd, MAX_PATH_LENGTH + 1) ? "." : wd));
381    g_free(wd);
382  }
383}
384
385static gboolean
386tr_torrent_paused(TrTorrent *tor) {
387  tr_stat_t *st = tr_torrentStat(tor->handle);
388
389  return (TR_STATUS_INACTIVE & st->status ? TRUE : FALSE);
390}
391
392void
393tr_torrent_stop_politely(TrTorrent *tor) {
394  tr_stat_t *st;
395
396  TR_IS_TORRENT(tor);
397
398  if(tor->disposed)
399    return;
400
401  if(!tor->closing) {
402    st = tr_torrent_stat(tor);
403    tor->closing = TRUE;
404    if(TR_STATUS_ACTIVE & st->status)
405      tr_torrentStop(tor->handle);
406  }
407}
408
409tr_stat_t *
410tr_torrent_stat_polite(TrTorrent *tor) {
411  TrTorrentClass *klass;
412  tr_stat_t *st = tr_torrentStat(tor->handle);
413
414  if(tor->disposed)
415    return st;
416
417  if(tor->closing && TR_STATUS_PAUSE & st->status) {
418    tor->closing = FALSE;
419    klass = g_type_class_peek(TR_TORRENT_TYPE);
420    g_signal_emit(tor, klass->paused_signal_id, 0, NULL);
421  }
422
423  return st;
424}
Note: See TracBrowser for help on using the repository browser.