Changeset 222


Ignore:
Timestamp:
Apr 8, 2006, 4:10:40 PM (16 years ago)
Author:
titer
Message:

Merge 84:221 from trunk

Location:
branches/simple_http_parsing
Files:
13 deleted
48 edited
16 copied

Legend:

Unmodified
Added
Removed
  • branches/simple_http_parsing

    • Property svn:ignore set to
      Makefile.config
      .depend
      build
  • branches/simple_http_parsing/AUTHORS

    r20 r222  
    99
    1010Josh Elsasser <josh@elsasser.org>
     11 + Back-end
    1112 + GTK+ interface
    12 
    13 Michael Demars <keul@m0k.org>
    14  + OS X interface
    15  + Growl notifications
    16  + AppleScript support
    1713
    1814Bryan Varner <bryan@varnernet.com>
    1915 + BeOS interface
    2016
     17Michael Demars <keul@m0k.org>
     18 + OS X interface (Growl notifications, AppleScript)
     19
    2120
    2221Thanks to:
    2322
    24 vi@nwr.jp
    25  + Free SHA1 implementation (sha1.[ch])
    26 
    27 Mike Matas <http://www.mikematas.com/>
    28  + OS X toolbar icons
     23Mitchell Livingston
     24 + OS X patches (Badging, preferences, check for update and much more)
    2925
    3026Omar and Adrien
     
    3430 + Writing http://wiki.theory.org/BitTorrentSpecification
    3531
     32vi@nwr.jp
     33 + Free SHA1 implementation (sha1.[ch])
     34
     35John Blitch
     36 + OS X patch (Contextual menu)
     37
    3638Ahmad M. Afuni
    3739 + NetBSD patch
     
    4042 + FreeBSD patch
    4143
     44Mike Matas <http://www.mikematas.com/>
     45 + OS X toolbar icons
     46
    4247Martin Stadtmueller
    4348 + Icon tweaking
    4449
    45 John Blitch
    46  + Contextual menu patch
    47 
    48 Mitchell Livingston
    49  + OS X patches
     50FOOOD
     51 + Badging images
  • branches/simple_http_parsing/NEWS

    r32 r222  
    11NEWS file for Transmission <http://transmission.m0k.org/>
     2
     30.5 (2006/02/11)
     4 - Only uses one port for all torrents
     5 - Rewritten choking algorithm
     6 - Remembers download and upload sizes from one launch to another
     7 - New GTK+ interface
     8 - New BeOS interface
     9 - OS X: Dock badging
     10 - OS X: Shows the file icon in the list
     11 - OS X: Shows ratio once download is completed
     12 - OS X: Automatic check for update
     13 - OS X: Fixes a display bug on Intel Macs
    214
    3150.4 (2005/11/18)
  • branches/simple_http_parsing/README

    r1 r222  
    22=======================
    33
    4 This is Transmission, a free BitTorrent client. For more information
    5 (including build instructions), please consult the website:
    6 http://transmission.m0k.org/
     4Transmission is a free, lightweight BitTorrent client. It features a
     5simple, intuitive interface on top on an efficient, cross-platform
     6back-end.
     7
     8Transmission is open source (MIT license) and runs on Mac OS X (Cocoa
     9interface), Linux/NetBSD/FreeBSD/OpenBSD (GTK+ interface) and BeOS
     10(native interface).
     11
     12For more information (including build instructions), please consult the
     13website: http://transmission.m0k.org/
  • branches/simple_http_parsing/beos

    • Property svn:ignore set to
      .depend
      Transmission
  • branches/simple_http_parsing/beos/TRApplication.cpp

    r14 r222  
    201201                }
    202202        }
     203}
     204
     205/**
     206 * Needed for browsers or command line interaction
     207 */
     208void TRApplication::ArgvReceived(int32 _argc, char** _argv)
     209{
     210        entry_ref ref;
     211        BMessage refs(B_REFS_RECEIVED);
     212        for( int32 i = 0; i < _argc; ++i )
     213        {
     214                if( B_OK == get_ref_for_path(_argv[i], &ref) )
     215                {
     216                        refs.AddRef("refs", &ref);
     217                }
     218        }
     219       
     220        be_app_messenger.SendMessage(refs);
    203221}
    204222
  • branches/simple_http_parsing/gtk

    • Property svn:ignore set to
      .depend
      defines.h
      transmission-gtk
  • branches/simple_http_parsing/gtk/conf.c

    r72 r222  
    3737
    3838#include <gtk/gtk.h>
     39#include <glib/gi18n.h>
    3940
    4041#include "conf.h"
     
    4243#include "util.h"
    4344
    44 #define FILE_LOCK               "gtk_lock"
    45 #define FILE_PREFS              "gtk_prefs"
    46 #define FILE_PREFS_TMP          "gtk_prefs.tmp"
    47 #define FILE_STATE              "gtk_state"
    48 #define FILE_STATE_TMP          "gtk_state.tmp"
     45#define CONF_SUBDIR             "gtk"
     46#define FILE_LOCK               "lock"
     47#define FILE_PREFS              "prefs"
     48#define FILE_PREFS_TMP          "prefs.tmp"
     49#define FILE_STATE              "state"
     50#define FILE_STATE_TMP          "state.tmp"
     51#define OLD_FILE_LOCK           "gtk_lock" /* remove this after next release */
     52#define OLD_FILE_PREFS          "gtk_prefs"
     53#define OLD_FILE_STATE          "gtk_state"
    4954#define PREF_SEP_KEYVAL         '\t'
    5055#define PREF_SEP_LINE           '\n'
     
    5964
    6065static char *confdir = NULL;
     66static char *old_confdir = NULL;
    6167static GTree *prefs = NULL;
    6268
     
    7076  if(0 > (fd = open(file, O_RDWR | O_CREAT, 0666))) {
    7177    savederr = errno;
    72     *errstr = g_strdup_printf("Error opening file %s for writing:\n%s",
    73                               file, strerror(errno));
     78    *errstr = g_strdup_printf(_("Failed to open the file %s for writing:\n%s"),
     79      file, strerror(errno));
    7480    errno = savederr;
    7581    return -1;
     
    8490    savederr = errno;
    8591    if(EAGAIN == errno)
    86       *errstr = g_strdup_printf("Another copy of %s is already running.",
     92      *errstr = g_strdup_printf(_("Another copy of %s is already running."),
    8793                                g_get_application_name());
    8894    else
    89       *errstr = g_strdup_printf("Error obtaining lock on file %s:\n%s",
    90                                 file, strerror(errno));
     95      *errstr = g_strdup_printf(_("Failed to lock the file %s:\n%s"),
     96        file, strerror(errno));
    9197    close(fd);
    9298    errno = savederr;
     
    99105gboolean
    100106cf_init(const char *dir, char **errstr) {
    101   struct stat sb;
    102 
    103107  *errstr = NULL;
    104   confdir = g_strdup(dir);
    105 
    106   if(0 > stat(dir, &sb)) {
    107     if(ENOENT != errno)
    108       *errstr = g_strdup_printf("Failed to check directory %s:\n%s",
    109                                 dir, strerror(errno));
    110     else {
    111       if(0 == mkdir(dir, 0777))
    112         return TRUE;
    113       else
    114         *errstr = g_strdup_printf("Failed to create directory %s:\n%s",
    115                                   dir, strerror(errno));
    116     }
    117     return FALSE;
    118   }
    119 
    120   if(S_IFDIR & sb.st_mode)
     108  old_confdir = g_strdup(dir);
     109  confdir = g_build_filename(dir, CONF_SUBDIR, NULL);
     110
     111  if(mkdir_p(confdir, 0777))
    121112    return TRUE;
    122113
    123   *errstr = g_strdup_printf("%s is not a directory", dir);
     114  *errstr = g_strdup_printf(_("Failed to create the directory %s:\n%s"),
     115                            confdir, strerror(errno));
    124116  return FALSE;
    125117}
     
    127119gboolean
    128120cf_lock(char **errstr) {
    129   char *path = g_build_filename(confdir, FILE_LOCK, NULL);
     121  char *path = g_build_filename(old_confdir, OLD_FILE_LOCK, NULL);
    130122  int fd = lockfile(path, errstr);
     123
     124  if(0 <= fd) {
     125    g_free(path);
     126    path = g_build_filename(confdir, FILE_LOCK, NULL);
     127    fd = lockfile(path, errstr);
     128  }
    131129
    132130  g_free(path);
     
    137135cf_loadprefs(char **errstr) {
    138136  char *path = g_build_filename(confdir, FILE_PREFS, NULL);
     137  char *oldpath;
    139138  GIOChannel *io;
    140139  GError *err;
     
    155154  if(NULL != err) {
    156155    if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
    157       *errstr = g_strdup_printf("Error opening file %s for reading:\n%s",
    158                                 path, err->message);
    159     goto done;
     156      *errstr = g_strdup_printf(
     157        _("Failed to open the file %s for reading:\n%s"), path, err->message);
     158    else {
     159      g_error_free(err);
     160      err = NULL;
     161      oldpath = g_build_filename(old_confdir, OLD_FILE_PREFS, NULL);
     162      io = g_io_channel_new_file(oldpath, "r", &err);
     163      g_free(oldpath);
     164    }
     165    if(NULL != err)
     166      goto done;
    160167  }
    161168  g_io_channel_set_line_term(io, &term, 1);
     
    166173    switch(g_io_channel_read_line(io, &line, &len, &termpos, &err)) {
    167174      case G_IO_STATUS_ERROR:
    168         *errstr = g_strdup_printf("Error reading file %s:\n%s",
    169                                   path, err->message);
     175        *errstr = g_strdup_printf(
     176          _("Error while reading from the file %s:\n%s"), path, err->message);
    170177        goto done;
    171178      case G_IO_STATUS_NORMAL:
     
    193200  if(NULL != io) 
    194201    g_io_channel_unref(io);
     202  g_free(path);
    195203  return NULL == *errstr;
    196204}
     
    230238  if(0 > (fd = lockfile(tmpfile, errstr))) {
    231239    g_free(errstr);
    232     *errstr = g_strdup_printf("Error opening or locking file %s:\n%s",
     240    *errstr = g_strdup_printf(_("Failed to open or lock the file %s:\n%s"),
    233241                              tmpfile, strerror(errno));
    234242    goto done;
     
    250258  if(NULL != info.err ||
    251259     G_IO_STATUS_ERROR == g_io_channel_shutdown(io, TRUE, &info.err)) {
    252     *errstr = g_strdup_printf("Error writing to file %s:\n%s",
     260    *errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
    253261                              tmpfile, info.err->message);
    254262    g_error_free(info.err);
     
    257265
    258266  if(0 > rename(tmpfile, file)) {
    259     *errstr = g_strdup_printf("Error renaming %s to %s:\n%s",
     267    *errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
    260268                              tmpfile, file, strerror(errno));
    261269    goto done;
     
    306314cf_loadstate(char **errstr) {
    307315  char *path = g_build_filename(confdir, FILE_STATE, NULL);
     316  char *oldpath;
    308317  GIOChannel *io;
    309318  GError *err;
     
    318327  if(NULL != err) {
    319328    if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
    320       *errstr = g_strdup_printf("Error opening file %s for reading:\n%s",
    321                                 path, err->message);
    322     goto done;
     329      *errstr = g_strdup_printf(
     330        _("Failed to open the file %s for reading:\n%s"), path, err->message);
     331    else {
     332      g_error_free(err);
     333      err = NULL;
     334      oldpath = g_build_filename(old_confdir, OLD_FILE_STATE, NULL);
     335      io = g_io_channel_new_file(oldpath, "r", &err);
     336      g_free(oldpath);
     337    }
     338    if(NULL != err)
     339      goto done;
    323340  }
    324341  g_io_channel_set_line_term(io, &term, 1);
     
    329346    switch(g_io_channel_read_line(io, &line, &len, &termpos, &err)) {
    330347      case G_IO_STATUS_ERROR:
    331         *errstr = g_strdup_printf("Error reading file %s:\n%s",
    332                                   path, err->message);
     348        *errstr = g_strdup_printf(
     349          _("Error while reading from the file %s:\n%s"), path, err->message);
    333350        goto done;
    334351      case G_IO_STATUS_NORMAL:
     
    365382    ret = NULL;
    366383  }
     384  g_free(path);
    367385  return ret;
    368386}
     
    373391
    374392  /* skip any leading whitespace */
    375   while(isspace(*line))
     393  while(g_ascii_isspace(*line))
    376394    line++;
    377395
    378396  /* walk over the key, which may be alphanumerics as well as - or _ */
    379   for(start = line; isalnum(*start) || '_' == *start || '-' == *start; start++)
     397  for(start = line; g_ascii_isalnum(*start)
     398        || '_' == *start || '-' == *start; start++)
    380399    ;
    381400
     
    413432
    414433gboolean
    415 cf_savestate(int count, tr_stat_t *torrents, char **errstr) {
     434cf_savestate(GList *torrents, char **errstr) {
    416435  char *file = g_build_filename(confdir, FILE_STATE, NULL);
    417436  char *tmpfile = g_build_filename(confdir, FILE_STATE_TMP, NULL);
    418437  GIOChannel *io = NULL;
    419438  GError *err;
    420   int fd, ii;
     439  int fd;
    421440  char *torrentfile, *torrentdir, *line;
    422441  gsize written;
    423442  gboolean paused;
    424443  GIOStatus res;
     444  tr_stat_t *sb;
     445  tr_info_t *in;
    425446
    426447  *errstr = NULL;
     
    428449  if(0 > (fd = lockfile(tmpfile, errstr))) {
    429450    g_free(errstr);
    430     *errstr = g_strdup_printf("Error opening or locking file %s:\n%s",
     451    *errstr = g_strdup_printf(_("Failed to open or lock the file %s:\n%s"),
    431452                              tmpfile, strerror(errno));
    432453    goto done;
     
    443464
    444465  err = NULL;
    445   for(ii = 0; ii < count; ii++) {
    446     /* XXX need a better way to query running/stopped state */
    447     paused = ((TR_STATUS_STOPPING | TR_STATUS_PAUSE) & torrents[ii].status);
    448     torrentfile = g_strescape(torrents[ii].info.torrent, "");
    449     torrentdir = g_strescape(torrents[ii].folder, "");
     466  while(NULL != torrents) {
     467    sb = tr_torrentStat(torrents->data);
     468    in = tr_torrentInfo(torrents->data);
     469    paused = (TR_STATUS_INACTIVE & sb->status);
     470    torrentfile = g_strescape(in->torrent, "");
     471    torrentdir = g_strescape(tr_torrentGetFolder(torrents->data), "");
    450472    /* g_strcompress */
    451473    line = g_strdup_printf("torrent=\"%s\" dir=\"%s\" paused=\"%s\"%c",
     
    465487        goto done;
    466488    }
     489    torrents = torrents->next;
    467490  }
    468491  if(NULL != err ||
    469492     G_IO_STATUS_ERROR == g_io_channel_shutdown(io, TRUE, &err)) {
    470     *errstr = g_strdup_printf("Error writing to file %s:\n%s",
     493    *errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
    471494                              tmpfile, err->message);
    472495    g_error_free(err);
     
    475498
    476499  if(0 > rename(tmpfile, file)) {
    477     *errstr = g_strdup_printf("Error renaming %s to %s:\n%s",
     500    *errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
    478501                              tmpfile, file, strerror(errno));
    479502    goto done;
  • branches/simple_http_parsing/gtk/conf.h

    r51 r222  
    5151cf_loadstate(char **errstr);
    5252gboolean
    53 cf_savestate(int count, tr_stat_t *torrents, char **errstr);
     53cf_savestate(GList *torrents, char **errstr);
    5454void
    5555cf_freestate(struct cf_torrentstate *state);
  • branches/simple_http_parsing/gtk/dialogs.c

    r70 r222  
    3131
    3232#include <gtk/gtk.h>
     33#include <glib/gi18n.h>
    3334
    3435#include "conf.h"
     
    3940struct prefdata {
    4041  GtkSpinButton *port;
    41   GtkCheckButton *uselimit;
    42   GtkSpinButton *limit;
     42  GtkCheckButton *use_dlimit;
     43  GtkSpinButton *dlimit;
     44  GtkCheckButton *use_ulimit;
     45  GtkSpinButton *ulimit;
    4346  GtkFileChooser *dir;
    4447  GtkWindow *parent;
     
    4952  add_torrent_func_t addfunc;
    5053  GtkWindow *parent;
    51   tr_handle_t *tr;
    5254  torrents_added_func_t donefunc;
    53   void *donedata;
     55  void *data;
    5456  gboolean autostart;
    5557  gboolean usingaltdir;
     
    5961
    6062static void
     63windclosed(GtkWidget *widget SHUTUP, gpointer gdata);
     64static void
    6165clicklimitbox(GtkWidget *widget, gpointer gdata);
    6266static void
     
    7074
    7175void
    72 makeprefwindow(GtkWindow *parent, tr_handle_t *tr) {
    73   char *title = g_strconcat(g_get_application_name(), " Preferences", NULL);
     76makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened) {
     77  char *title = g_strdup_printf(_("%s Preferences"), g_get_application_name());
    7478  GtkWidget *wind = gtk_dialog_new_with_buttons(title, parent,
    7579   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
    7680   GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    7781   GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
    78   GtkWidget *table = gtk_table_new(4, 2, FALSE);
     82  const unsigned int rowcount = 6;
     83  GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
    7984  GtkWidget *portnum = gtk_spin_button_new_with_range(1, 0xffff, 1);
    80   GtkWidget *limitbox = gtk_check_button_new_with_label("Limit upload speed");
    81   GtkWidget *limitnum = gtk_spin_button_new_with_range(0, G_MAXLONG, 1);
    82   GtkWidget *dirstr = gtk_file_chooser_button_new("Choose download directory",
     85  GtkWidget *dirstr = gtk_file_chooser_button_new(
     86    _("Choose download directory"),
    8387    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    8488  GtkWidget *label;
     
    8690  const char *pref;
    8791  struct prefdata *data = g_new0(struct prefdata, 1);
     92  struct { GtkWidget *on; GtkWidget *num; GtkWidget *label; gboolean first;
     93    const char *usepref; const char *numpref; long def; } lim[] = {
     94    { gtk_check_button_new_with_mnemonic(_("_Limit download speed")),
     95      gtk_spin_button_new_with_range(0, G_MAXLONG, 1),
     96      gtk_label_new_with_mnemonic(_("Maximum _download speed:")),
     97      FALSE, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT, DEFAULT_DOWNLIMIT, },
     98    { gtk_check_button_new_with_mnemonic(_("Li_mit upload speed")),
     99      gtk_spin_button_new_with_range(0, G_MAXLONG, 1),
     100      gtk_label_new_with_mnemonic(_("Maximum _upload speed:")),
     101      TRUE, PREF_USEUPLIMIT, PREF_UPLIMIT, DEFAULT_UPLIMIT, },
     102  };
     103  unsigned int ii;
     104
     105  *opened = TRUE;
    88106
    89107  g_free(title);
     108  gtk_widget_set_name(wind, "TransmissionDialog");
    90109  gtk_table_set_col_spacings(GTK_TABLE(table), 12);
    91110  gtk_table_set_row_spacings(GTK_TABLE(table), 12);
    92111  gtk_dialog_set_default_response(GTK_DIALOG(wind), GTK_RESPONSE_OK);
    93   gtk_container_set_border_width(GTK_CONTAINER(table), 12);
     112  gtk_container_set_border_width(GTK_CONTAINER(table), 6);
     113  gtk_window_set_resizable(GTK_WINDOW(wind), FALSE);
    94114
    95115  data->port = GTK_SPIN_BUTTON(portnum);
    96   data->uselimit = GTK_CHECK_BUTTON(limitbox);
    97   data->limit = GTK_SPIN_BUTTON(limitnum);
     116  data->use_dlimit = GTK_CHECK_BUTTON(lim[0].on);
     117  data->dlimit = GTK_SPIN_BUTTON(lim[0].num);
     118  data->use_ulimit = GTK_CHECK_BUTTON(lim[1].on);
     119  data->ulimit = GTK_SPIN_BUTTON(lim[1].num);
    98120  data->dir = GTK_FILE_CHOOSER(dirstr);
    99121  data->parent = parent;
    100122  data->tr = tr;
    101123
    102   /* port label and entry */
    103   label = gtk_label_new("Listening port:");
    104   gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
    105   pref = cf_getpref(PREF_PORT);
    106   gtk_spin_button_set_value(GTK_SPIN_BUTTON(portnum),
    107     (NULL == pref ? TR_DEFAULT_PORT : strtol(pref, NULL, 10)));
    108   gtk_table_attach_defaults(GTK_TABLE(table), label,            0, 1, 0, 1);
    109   gtk_table_attach_defaults(GTK_TABLE(table), portnum,          1, 2, 0, 1);
    110 
    111   /* limit checkbox */
    112   pref = cf_getpref(PREF_USELIMIT);
    113   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(limitbox),
    114     (NULL == pref ? TRUE : strbool(pref)));
    115   array = g_new(GtkWidget*, 2);
    116   g_signal_connect_data(limitbox, "clicked", G_CALLBACK(clicklimitbox),
    117                         array, (GClosureNotify)g_free, 0);
    118   gtk_table_attach_defaults(GTK_TABLE(table), limitbox,         0, 2, 1, 2);
    119 
    120   /* limit label and entry */
    121   label = gtk_label_new("Maximum upload speed:");
    122   gtk_misc_set_alignment(GTK_MISC(label), 0, 1.0/3.0);
    123   pref = cf_getpref(PREF_LIMIT);
    124   gtk_spin_button_set_value(GTK_SPIN_BUTTON(limitnum),
    125     (NULL == pref ? DEFAULT_UPLIMIT : strtol(pref,NULL,10)));
    126   gtk_table_attach_defaults(GTK_TABLE(table), label,            0, 1, 2, 3);
    127   gtk_table_attach_defaults(GTK_TABLE(table), limitnum,         1, 2, 2, 3);
    128   array[0] = label;
    129   array[1] = limitnum;
    130   clicklimitbox(limitbox, array);
     124#define RN(n) (n), (n) + 1
     125
     126  for(ii = 0; ii < ALEN(lim); ii++) {
     127    /* limit checkbox */
     128    pref = cf_getpref(lim[ii].usepref);
     129    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lim[ii].on),
     130      (NULL == pref ? lim[ii].first : strbool(pref)));
     131    array = g_new(GtkWidget*, 2);
     132    g_signal_connect_data(lim[ii].on, "clicked", G_CALLBACK(clicklimitbox),
     133                          array, (GClosureNotify)g_free, 0);
     134    gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].on,    0, 2, RN(ii*2));
     135
     136    /* limit label and entry */
     137    gtk_label_set_mnemonic_widget(GTK_LABEL(lim[ii].label), lim[ii].num);
     138    gtk_misc_set_alignment(GTK_MISC(lim[ii].label), 0, .5);
     139    pref = cf_getpref(lim[ii].numpref);
     140    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(lim[ii].num), TRUE);
     141    gtk_spin_button_set_value(GTK_SPIN_BUTTON(lim[ii].num),
     142      (NULL == pref ? lim[ii].def : strtol(pref, NULL, 10)));
     143    gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].label, 0,1,RN(ii*2+1));
     144    gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].num,   1,2,RN(ii*2+1));
     145    array[0] = lim[ii].label;
     146    array[1] = lim[ii].num;
     147    clicklimitbox(lim[ii].on, array);
     148  }
     149  ii *= 2;
    131150
    132151  /* directory label and chooser */
    133   label = gtk_label_new("Download Directory:");
     152  label = gtk_label_new_with_mnemonic(_("Download di_rectory:"));
     153  gtk_label_set_mnemonic_widget(GTK_LABEL(label), dirstr);
    134154  gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
    135155  if(NULL != (pref = cf_getpref(PREF_DIR)))
    136156    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirstr), pref);
    137   gtk_table_attach_defaults(GTK_TABLE(table), label,            0, 1, 3, 4);
    138   gtk_table_attach_defaults(GTK_TABLE(table), dirstr,           1, 2, 3, 4);
     157  gtk_table_attach_defaults(GTK_TABLE(table), label,           0, 1, RN(ii));
     158  gtk_table_attach_defaults(GTK_TABLE(table), dirstr,          1, 2, RN(ii));
     159  ii++;
     160
     161  /* port label and entry */
     162  label = gtk_label_new_with_mnemonic(_("Listening _port:"));
     163  gtk_label_set_mnemonic_widget(GTK_LABEL(label), portnum);
     164  gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
     165  pref = cf_getpref(PREF_PORT);
     166  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(portnum), TRUE);
     167  gtk_spin_button_set_value(GTK_SPIN_BUTTON(portnum),
     168    (NULL == pref ? TR_DEFAULT_PORT : strtol(pref, NULL, 10)));
     169  gtk_table_attach_defaults(GTK_TABLE(table), label,           0, 1, RN(ii));
     170  gtk_table_attach_defaults(GTK_TABLE(table), portnum,         1, 2, RN(ii));
     171  ii++;
     172
     173#undef RN
     174  assert(rowcount == ii);
    139175
    140176  gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
    141177  g_signal_connect_data(wind, "response", G_CALLBACK(clickdialog),
    142178                        data, (GClosureNotify)g_free, 0);
     179  g_signal_connect(wind, "destroy", G_CALLBACK(windclosed), opened);
    143180  gtk_widget_show_all(wind);
     181}
     182
     183static void
     184windclosed(GtkWidget *widget SHUTUP, gpointer gdata) {
     185  gboolean *preachy_gcc = gdata;
     186 
     187  *preachy_gcc = FALSE;
    144188}
    145189
     
    160204  const char *strval;
    161205  char *strnum, *errstr;
    162   gboolean boolval;
    163 
    164   if(GTK_RESPONSE_OK == resp) {
     206  gboolean bval;
     207
     208  if(GTK_RESPONSE_APPLY == resp || GTK_RESPONSE_OK == resp) {
    165209    /* check directory */
    166210    if(NULL != (strval = gtk_file_chooser_get_current_folder(data->dir))) {
    167211      if(!mkdir_p(strval, 0777)) {
    168         errmsg(data->parent, "Failed to create directory %s:\n%s",
     212        errmsg(data->parent,
     213               _("Failed to create the directory %s:\n%s"),
    169214               strval, strerror(errno));
    170215        return;
     
    181226    g_free(strnum);
    182227
    183     /* save uselimit pref */
    184     boolval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->uselimit));
    185     cf_setpref(PREF_USELIMIT, (boolval ? "yes" : "no"));
    186 
    187     /* save limit pref */
    188     intval = gtk_spin_button_get_value_as_int(data->limit);
     228    /* save usedownlimit pref */
     229    bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->use_dlimit));
     230    cf_setpref(PREF_USEDOWNLIMIT, (bval ? "yes" : "no"));
     231
     232    /* save downlimit pref */
     233    intval = gtk_spin_button_get_value_as_int(data->dlimit);
    189234    strnum = g_strdup_printf("%i", intval);
    190     cf_setpref(PREF_LIMIT, strnum);
     235    cf_setpref(PREF_DOWNLIMIT, strnum);
     236
     237    /* save useuplimit pref */
     238    bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->use_ulimit));
     239    cf_setpref(PREF_USEUPLIMIT, (bval ? "yes" : "no"));
     240
     241    /* save downlimit pref */
     242    intval = gtk_spin_button_get_value_as_int(data->ulimit);
     243    strnum = g_strdup_printf("%i", intval);
     244    cf_setpref(PREF_UPLIMIT, strnum);
    191245
    192246    /* save prefs */
     
    197251    }
    198252
     253    /* XXX would be nice to have errno strings, are they printed to stdout? */
    199254    tr_setBindPort(data->tr, gtk_spin_button_get_value_as_int(data->port));
    200255    setlimit(data->tr);
    201256  }
    202257
    203   gtk_widget_destroy(widget);
     258  if(GTK_RESPONSE_APPLY != resp)
     259    gtk_widget_destroy(widget);
    204260}
    205261
     
    207263setlimit(tr_handle_t *tr) {
    208264  const char *pref;
    209 
    210   if(NULL != (pref = cf_getpref(PREF_USELIMIT)) && !strbool(pref))
    211     tr_setUploadLimit(tr, -1);
    212   else if(NULL != (pref = cf_getpref(PREF_LIMIT)))
    213     tr_setUploadLimit(tr, strtol(pref, NULL, 10));
    214   else
    215     tr_setUploadLimit(tr, DEFAULT_UPLIMIT);
     265  struct { void (*func)(tr_handle_t*, int);
     266    const char *use; const char *num; long def; } lim[] = {
     267    {tr_setDownloadLimit, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT,DEFAULT_DOWNLIMIT},
     268    {tr_setUploadLimit,   PREF_USEUPLIMIT,   PREF_UPLIMIT,  DEFAULT_UPLIMIT},
     269  };
     270  unsigned int ii;
     271
     272  for(ii = 0; ii < ALEN(lim); ii++) {
     273    if(NULL != (pref = cf_getpref(lim[ii].use)) && !strbool(pref))
     274      lim[ii].func(tr, -1);
     275    else if(NULL != (pref = cf_getpref(lim[ii].num)))
     276      lim[ii].func(tr, strtol(pref, NULL, 10));
     277    else
     278      lim[ii].func(tr, lim[ii].def);
     279  }
    216280}
    217281
    218282void
    219 makeaddwind(add_torrent_func_t addfunc, GtkWindow *parent, tr_handle_t *tr,
    220             torrents_added_func_t donefunc, void *donedata) {
    221   GtkWidget *wind = gtk_file_chooser_dialog_new("Add a Torrent", parent,
     283makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
     284            torrents_added_func_t donefunc, void *cbdata) {
     285  GtkWidget *wind = gtk_file_chooser_dialog_new(_("Add a Torrent"), parent,
    222286    GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    223287    GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
     
    225289  GtkWidget *vbox = gtk_vbox_new(FALSE, 3);
    226290  GtkWidget *bbox = gtk_hbutton_box_new();
    227   GtkWidget *autocheck = gtk_check_button_new_with_label(
    228     "Automatically start torrent");
    229   GtkWidget *dircheck = gtk_check_button_new_with_label(
    230     "Use alternate download directory");
     291  GtkWidget *autocheck = gtk_check_button_new_with_mnemonic(
     292    _("Automatically _start torrent"));
     293  GtkWidget *dircheck = gtk_check_button_new_with_mnemonic(
     294    _("Use alternate _download directory"));
    231295  GtkFileFilter *filter = gtk_file_filter_new();
    232296  GtkFileFilter *unfilter = gtk_file_filter_new();
    233297  GtkWidget *getdir = gtk_file_chooser_button_new(
    234     "Choose a download directory", GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
     298    _("Choose a download directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    235299  const char *pref;
    236300
    237301  data->addfunc = addfunc;
    238302  data->parent = parent;
    239   data->tr = tr;
    240303  data->donefunc = donefunc;
    241   data->donedata = donedata;
     304  data->data = cbdata;
    242305  data->autostart = TRUE;
    243306  data->usingaltdir = FALSE;
     
    245308  data->altbox = GTK_BUTTON_BOX(bbox);
    246309
    247   gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
     310  gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_EDGE);
    248311  gtk_box_pack_start_defaults(GTK_BOX(bbox), dircheck);
    249312  gtk_box_pack_start_defaults(GTK_BOX(bbox), getdir);
     
    257320
    258321  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dircheck), FALSE);
    259 
    260   gtk_file_filter_set_name(filter, "Torrent files");
     322  gtk_widget_set_sensitive(getdir, FALSE);
     323
     324  gtk_file_filter_set_name(filter, _("Torrent files"));
    261325  gtk_file_filter_add_pattern(filter, "*.torrent");
    262   gtk_file_filter_set_name(unfilter, "All files");
     326  gtk_file_filter_set_name(unfilter, _("All files"));
    263327  gtk_file_filter_add_pattern(unfilter, "*");
    264328
     
    273337
    274338  gtk_widget_show_all(wind);
    275   gtk_widget_hide(getdir);
    276339}
    277340
     
    288351
    289352  data->usingaltdir = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
    290   gtk_button_box_set_layout(data->altbox,
    291     (data->usingaltdir ? GTK_BUTTONBOX_EDGE : GTK_BUTTONBOX_START));
    292   if(data->usingaltdir)
    293     gtk_widget_show(GTK_WIDGET(data->altdir));
    294   else
    295     gtk_widget_hide(GTK_WIDGET(data->altdir));
     353  gtk_widget_set_sensitive(GTK_WIDGET(data->altdir), data->usingaltdir);
    296354}
    297355
     
    308366    files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
    309367    for(ii = files; NULL != ii; ii = ii->next)
    310       if(data->addfunc(data->tr, data->parent, ii->data, dir,
    311                        !data->autostart))
     368      if(data->addfunc(data->data, ii->data, dir,
     369                       /* XXX need to group errors here */
     370                       !data->autostart, NULL))
    312371        added = TRUE;
    313372    if(added)
    314       data->donefunc(data->donedata);
     373      data->donefunc(data->data);
    315374    if(NULL != dir)
    316375      g_free(dir);
     
    322381#define INFOLINE(tab, ii, nam, val) \
    323382  do { \
     383    char *txt = g_markup_printf_escaped("<b>%s</b>", nam); \
    324384    GtkWidget *wid = gtk_label_new(NULL); \
    325385    gtk_misc_set_alignment(GTK_MISC(wid), 1, .5); \
    326     gtk_label_set_markup(GTK_LABEL(wid), "<b>" nam ":</b>"); \
     386    gtk_label_set_markup(GTK_LABEL(wid), txt); \
    327387    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 0, 1, ii, ii + 1); \
    328388    wid = gtk_label_new(val); \
     389    gtk_label_set_selectable(GTK_LABEL(wid), TRUE); \
    329390    gtk_misc_set_alignment(GTK_MISC(wid), 0, .5); \
    330391    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 1, 2, ii, ii + 1); \
    331392    ii++; \
    332   } while(0);
     393    g_free(txt); \
     394  } while(0)
    333395
    334396#define INFOLINEF(tab, ii, fmt, nam, val) \
     
    337399    INFOLINE(tab, ii, nam, buf); \
    338400    g_free(buf); \
    339   } while(0);
     401  } while(0)
    340402
    341403#define INFOLINEA(tab, ii, nam, val) \
     
    344406    INFOLINE(tab, ii, nam, buf); \
    345407    g_free(buf); \
    346   } while(0);
     408  } while(0)
    347409
    348410#define INFOSEP(tab, ii) \
     
    351413    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 0, 2, ii, ii + 1); \
    352414    ii++; \
    353   } while(0);
     415  } while(0)
    354416
    355417void
    356 makeinfowind(GtkWindow *parent, tr_handle_t *tr, int id) {
     418makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
    357419  tr_stat_t *sb;
    358   GtkWidget *wind, *table, *label;
     420  tr_info_t *in;
     421  GtkWidget *wind, *label;
    359422  int ii;
    360423  char *str;
    361   const int rowcount = 12;
    362 
    363   /* XXX would be nice to be able to stat just one */
    364   if(id >= tr_torrentStat(tr, &sb))
    365     assert(!"XXX ");
    366   str = g_strconcat(sb[id].info.name, " Properties", NULL);
     424  const int rowcount = 14;
     425  GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
     426
     427  sb = tr_torrentStat(tor);
     428  in = tr_torrentInfo(tor);
     429  str = g_strdup_printf(_("%s Properties"), in->name);
    367430  wind = gtk_dialog_new_with_buttons(str, parent,
    368431    GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
     
    370433  g_free(str);
    371434
    372   table = gtk_table_new(rowcount, 2, FALSE);
     435  gtk_widget_set_name(wind, "TransmissionDialog");
    373436  gtk_table_set_col_spacings(GTK_TABLE(table), 12);
    374437  gtk_table_set_row_spacings(GTK_TABLE(table), 12);
     438  gtk_dialog_set_default_response(GTK_DIALOG(wind), GTK_RESPONSE_ACCEPT);
     439  gtk_container_set_border_width(GTK_CONTAINER(table), 6);
     440  gtk_window_set_resizable(GTK_WINDOW(wind), FALSE);
    375441
    376442  label = gtk_label_new(NULL);
    377   str = g_markup_printf_escaped("<big>%s</big>", sb[id].info.name);
     443  gtk_label_set_selectable(GTK_LABEL(label), TRUE);
     444  str = g_markup_printf_escaped("<big>%s</big>", in->name);
    378445  gtk_label_set_markup(GTK_LABEL(label), str);
    379446  g_free(str);
     
    384451  INFOSEP(table, ii);
    385452
    386   INFOLINEA(table, ii, "Tracker", g_strdup_printf("http://%s:%i",
    387             sb[id].info.trackerAddress, sb[id].info.trackerPort));
    388   INFOLINE(table, ii, "Announce", sb[id].info.trackerAnnounce);
    389   INFOLINEA(table, ii, "Piece Size", readablesize(sb[id].info.pieceSize, 1));
    390   INFOLINEF(table, ii, "%i", "Pieces", sb[id].info.pieceCount);
    391   INFOLINEA(table, ii, "Total Size", readablesize(sb[id].info.totalSize, 1));
     453  if(80 == in->trackerPort)
     454    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s",
     455              in->trackerAddress));
     456  else
     457    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s:%i",
     458              in->trackerAddress, in->trackerPort));
     459  INFOLINE(table, ii, _("Announce:"), in->trackerAnnounce);
     460  INFOLINEA(table, ii, _("Piece Size:"), readablesize(in->pieceSize));
     461  INFOLINEF(table, ii, "%i", _("Pieces:"), in->pieceCount);
     462  INFOLINEA(table, ii, _("Total Size:"), readablesize(in->totalSize));
     463  if(0 > sb->seeders)
     464    INFOLINE(table, ii, _("Seeders:"), _("?"));
     465  else
     466    INFOLINEF(table, ii, "%i", _("Seeders:"), sb->seeders);
     467  if(0 > sb->leechers)
     468    INFOLINE(table, ii, _("Leechers:"), _("?"));
     469  else
     470    INFOLINEF(table, ii, "%i", _("Leechers:"), sb->leechers);
    392471
    393472  INFOSEP(table, ii);
    394473
    395   INFOLINE(table, ii, "Directory", sb[id].folder);
    396   INFOLINEA(table, ii, "Downloaded", readablesize(sb[id].downloaded, 1));
    397   INFOLINEA(table, ii, "Uploaded", readablesize(sb[id].uploaded, 1));
     474  INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tor));
     475  INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb->downloaded));
     476  INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb->uploaded));
    398477
    399478  INFOSEP(table, ii);
     
    401480  assert(rowcount == ii);
    402481
    403   gtk_container_set_border_width(GTK_CONTAINER(table), 12);
    404482  gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
    405483  g_signal_connect(G_OBJECT(wind), "response",
    406484                   G_CALLBACK(gtk_widget_destroy), NULL);
    407485  gtk_widget_show_all(wind);
    408   free(sb);
    409 }
     486}
  • branches/simple_http_parsing/gtk/dialogs.h

    r70 r222  
    11/*
    2   Copyright (c) 2005 Joshua Elsasser. All rights reserved.
     2  Copyright (c) 2005-2006 Joshua Elsasser. All rights reserved.
    33   
    44  Redistribution and use in source and binary forms, with or without
     
    3232/* macros for names of prefs we use */
    3333#define PREF_PORT               "listening-port"
    34 #define PREF_USELIMIT           "use-upload-limit"
    35 #define PREF_LIMIT              "upload-limit"
     34#define PREF_USEDOWNLIMIT       "use-download-limit"
     35#define PREF_DOWNLIMIT          "download-limit"
     36#define PREF_USEUPLIMIT         "use-upload-limit"
     37#define PREF_UPLIMIT            "upload-limit"
    3638#define PREF_DIR                "download-directory"
    3739
     40#define DEFAULT_DOWNLIMIT       100
    3841#define DEFAULT_UPLIMIT         20
    3942
    40 typedef gboolean (*add_torrent_func_t)(tr_handle_t*, GtkWindow*, const char*, const char *, gboolean);
     43typedef gboolean (*add_torrent_func_t)(void *, const char *, const char *, gboolean, GList **);
    4144typedef void (*torrents_added_func_t)(void *);
    4245
    4346void
    44 makeprefwindow(GtkWindow *parent, tr_handle_t *tr);
     47makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened);
    4548
    4649/* set the upload limit based on saved prefs */
     
    5053/* show the "add a torrent" dialog */
    5154void
    52 makeaddwind(add_torrent_func_t addfunc, GtkWindow *parent, tr_handle_t *tr,
    53             torrents_added_func_t donefunc, void *donedata);
     55makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
     56            torrents_added_func_t donefunc, void *cbdata);
    5457
    5558/* show the info window for a torrent */
    5659void
    57 makeinfowind(GtkWindow *parent, tr_handle_t *tr, int index);
     60makeinfowind(GtkWindow *parent, tr_torrent_t *tor);
    5861
    5962#endif /* TG_PREFS_H */
  • branches/simple_http_parsing/gtk/main.c

    r70 r222  
    2828#include <assert.h>
    2929#include <errno.h>
     30#include <signal.h>
    3031#include <string.h>
    3132#include <stdio.h>
     
    3536
    3637#include <gtk/gtk.h>
     38#include <glib/gi18n.h>
     39#include <glib/gstdio.h>
     40
     41#include "defines.h"
    3742
    3843#include "conf.h"
     
    4752  tr_handle_t *tr;
    4853  GtkWindow *wind;
    49   GtkListStore *model;
     54  GtkTreeModel *model;
    5055  GtkTreeView *view;
    5156  GtkStatusbar *bar;
    5257  GtkWidget **buttons;
    5358  guint timer;
     59  gboolean prefsopen;
    5460};
    5561
     
    7379void
    7480makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved);
    75 gboolean
    76 winclose(GtkWidget *widget, GdkEvent *event, gpointer gdata);
    77 gboolean
    78 exitcheck(gpointer gdata);
    79 void
    80 stoptransmission(void *tr);
    8181GtkWidget *
    8282makewind_toolbar(struct cbdata *data);
    8383GtkWidget *
    8484makewind_list(struct cbdata *data);
     85gboolean
     86winclose(GtkWidget *widget, GdkEvent *event, gpointer gdata);
     87gboolean
     88exitcheck(gpointer gdata);
     89void
     90stoptransmission(tr_handle_t *tr);
     91void
     92setupdrag(GtkWidget *widget, struct cbdata *data);
     93void
     94gotdrag(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
     95        GtkSelectionData *sel, guint info, guint time, gpointer gdata);
    8596static void
    8697stylekludge(GObject *obj, GParamSpec *spec, gpointer gdata);
     
    104115            GList *ids, int status);
    105116void
    106 killmenu(GtkWidget *menu, gpointer *gdata SHUTUP);
     117killmenu(GtkWidget *menu, gpointer *gdata);
    107118void
    108119actionclick(GtkWidget *widget, gpointer gdata);
     120void
     121findtorrent(GtkTreeModel *model, tr_torrent_t *tor, GtkTreeIter *iter);
    109122gint
    110123intrevcmp(gconstpointer a, gconstpointer b);
     
    114127
    115128gboolean
    116 addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
    117            const char *dir, gboolean paused);
     129addtorrent(void *vdata, const char *torrent, const char *dir, gboolean paused,
     130           GList **errs);
    118131void
    119132addedtorrents(void *vdata);
    120133gboolean
    121 savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat);
     134savetorrents(tr_handle_t *tr, GtkWindow *wind);
    122135void
    123136orstatus(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
     
    126139makeidlist(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
    127140           gpointer gdata);
     141void
     142maketorrentlist(tr_torrent_t *tor, void *data);
     143void
     144setupsighandlers(void);
     145void
     146fatalsig(int sig);
    128147
    129148#define TR_TYPE_PIECES_NAME     "tr-type-pieces"
     
    137156enum listfrom { FROM_BUTTON, FROM_POPUP };
    138157
    139 #define LIST_INDEX            "torrent-list-indexes"
     158#define LIST_IDS              "torrent-list-ids"
    140159#define LIST_MENU_WIDGET      "torrent-list-popup-menu-widget"
    141160
     
    143162  int avail; const char *ttext; const char *tpriv; }
    144163actionitems[] = {
    145   {"Add",         GTK_STOCK_ADD,          ACT_OPEN,   FALSE,  0,
    146    "Add a new torrent file", "XXX"},
    147   {"Start",       GTK_STOCK_EXECUTE,      ACT_START,  FALSE,
    148    (TR_STATUS_STOPPING | TR_STATUS_PAUSE),
    149    "Resume a torrent that has been paused", "XXX"},
    150   {"Stop",        GTK_STOCK_STOP,         ACT_STOP,   FALSE,
    151    ~(TR_STATUS_STOPPING | TR_STATUS_PAUSE),
    152    "Pause a torrent", "XXX"},
    153   {"Remove",      GTK_STOCK_REMOVE,       ACT_DELETE, FALSE, ~0,
    154    "Remove a torrent from the list", "XXX"},
    155   {"Properties",  GTK_STOCK_PROPERTIES,   ACT_INFO,   FALSE, ~0,
    156    "Get additional information for a torrent", "XXX"},
    157   {"Preferences", GTK_STOCK_PREFERENCES,  ACT_PREF,   TRUE,   0,
    158    "Open preferences dialog", "XXX"},
     164  {N_("Add"),         GTK_STOCK_ADD,          ACT_OPEN,   FALSE,  0,
     165   N_("Add a new torrent"), "XXX"},
     166  {N_("Start"),       GTK_STOCK_EXECUTE,      ACT_START,  FALSE,
     167   TR_STATUS_INACTIVE,
     168   N_("Start a torrent that is not running"), "XXX"},
     169  {N_("Stop"),        GTK_STOCK_STOP,         ACT_STOP,   FALSE,
     170   TR_STATUS_ACTIVE,
     171   N_("Stop a torrent that is running"), "XXX"},
     172  {N_("Remove"),      GTK_STOCK_REMOVE,       ACT_DELETE, FALSE, ~0,
     173   N_("Remove a torrent"), "XXX"},
     174  {N_("Properties"),  GTK_STOCK_PROPERTIES,   ACT_INFO,   FALSE, ~0,
     175   N_("Show additional information about a torrent"), "XXX"},
     176  {N_("Preferences"), GTK_STOCK_PREFERENCES,  ACT_PREF,   TRUE,   0,
     177   N_("Customize application behavior"), "XXX"},
    159178};
    160179
    161180#define CBDATA_PTR              "callback-data-pointer"
     181
     182#define SIGCOUNT_MAX            3
     183
     184static sig_atomic_t global_sigcount = 0;
     185static int global_lastsig = 0;
     186
    162187int
    163188main(int argc, char **argv) {
     
    169194  long intval;
    170195
     196  setupsighandlers();
     197
    171198  gtk_init(&argc, &argv);
    172199
    173   g_set_application_name("Transmission");
     200  bindtextdomain("transmission-gtk", LOCALEDIR);
     201  textdomain("transmission-gtk");
     202
     203  g_set_application_name(_("Transmission"));
    174204
    175205  tr = tr_init();
    176206
    177   setuphandlers(stoptransmission, tr);
     207  gtk_rc_parse_string(
     208    "style \"transmission-standard\" {\n"
     209    " GtkDialog::action-area-border = 6\n"
     210    " GtkDialog::button-spacing = 12\n"
     211    " GtkDialog::content-area-border = 6\n"
     212    "}\n"
     213    "widget \"TransmissionDialog\" style \"transmission-standard\"\n");
    178214
    179215  if(cf_init(tr_getPrefsDirectory(), &err)) {
     
    249285  GtkWidget *list;
    250286  GtkRequisition req;
    251   GList *ii;
     287  GList *loaderrs, *ii;
    252288  struct cf_torrentstate *ts;
    253289  gint height;
     290  char *str;
    254291
    255292  data->tr = tr;
     
    261298  data->bar = GTK_STATUSBAR(status);
    262299  data->buttons = NULL;
     300  data->prefsopen = FALSE;
    263301
    264302  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER,
     
    278316  g_signal_connect(G_OBJECT(wind), "delete_event", G_CALLBACK(winclose), data);
    279317
     318  setupdrag(list, data);
     319
     320  loaderrs = NULL;
    280321  for(ii = g_list_first(saved); NULL != ii; ii = ii->next) {
    281322    ts = ii->data;
    282     addtorrent(tr, GTK_WINDOW(wind),
    283                ts->ts_torrent, ts->ts_directory, ts->ts_paused);
     323    addtorrent(data, ts->ts_torrent, ts->ts_directory, ts->ts_paused,
     324               &loaderrs);
    284325    cf_freestate(ts);
    285326  }
    286327  g_list_free(saved);
     328
     329  if(NULL != loaderrs) {
     330    str = joinstrlist(loaderrs, "\n");
     331    errmsg(GTK_WINDOW(wind), ngettext("Failed to load the torrent file %s",
     332                                      "Failed to load the torrent files:\n%s",
     333                                      g_list_length(loaderrs)), str);
     334    g_list_foreach(loaderrs, (GFunc)g_free, NULL);
     335    g_list_free(loaderrs);
     336    g_free(str);
     337    savetorrents(tr, GTK_WINDOW(wind));
     338  }
    287339
    288340  data->timer = g_timeout_add(500, updatemodel, data);
     
    304356}
    305357
    306 /* XXX is this the right thing to do? */
    307 #define TR_TORRENT_NEEDS_STOP(t) \
    308   ((t) & TR_STATUS_CHECK || (t) & TR_STATUS_DOWNLOAD || (t) & TR_STATUS_SEED)
    309 
    310 gboolean
    311 winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
    312   struct cbdata *data = gdata;
    313   struct exitdata *edata;
    314   tr_stat_t *st;
    315   int ii;
    316 
    317   if(0 >= data->timer)
    318     g_source_remove(data->timer);
    319   data->timer = -1;
    320 
    321   blocksigs();
    322 
    323   for(ii = tr_torrentStat(data->tr, &st); 0 < ii; ii--) {
    324     if(TR_TORRENT_NEEDS_STOP(st[ii-1].status)) {
    325       /*fprintf(stderr, "quit: stopping %i %s\n", ii, st[ii-1].info.name);*/
    326       tr_torrentStop(data->tr, ii - 1);
    327     } else {
    328       /*fprintf(stderr, "quit: closing %i %s\n", ii, st[ii-1].info.name);*/
    329       tr_torrentClose(data->tr, ii - 1);
    330     }
    331   }
    332   free(st);
    333 
    334   unblocksigs();
    335 
    336   /* XXX should disable widgets or something */
    337 
    338   /* try to wait until torrents stop before exiting */
    339   edata = g_new0(struct exitdata, 1);
    340   edata->cbdata = data;
    341   edata->started = time(NULL);
    342   edata->timer = g_timeout_add(500, exitcheck, edata);
    343 
    344   /*fprintf(stderr, "quit: starting timeout at %i\n", edata->started);*/
    345 
    346   /* returning FALSE means to destroy the window */
    347   return TRUE;
    348 }
    349 
    350 gboolean
    351 exitcheck(gpointer gdata) {
    352   struct exitdata *data = gdata;
    353   tr_stat_t *st;
    354   int ii;
    355 
    356   blocksigs();
    357 
    358   for(ii = tr_torrentStat(data->cbdata->tr, &st); 0 < ii; ii--) {
    359     if(TR_STATUS_PAUSE & st[ii-1].status) {
    360       /*fprintf(stderr, "quit: closing %i %s\n", ii, st[ii-1].info.name);*/
    361       tr_torrentClose(data->cbdata->tr, ii - 1);
    362     }
    363   }
    364   free(st);
    365 
    366   /*fprintf(stderr, "quit: %i torrents left at %i\n",
    367     tr_torrentCount(data->cbdata->tr), time(NULL));*/
    368   /* keep going if we still have torrents and haven't hit the exit timeout */
    369   if(0 < tr_torrentCount(data->cbdata->tr) &&
    370      time(NULL) - data->started < TRACKER_EXIT_TIMEOUT) {
    371     updatemodel(data->cbdata);
    372     unblocksigs();
    373     return TRUE;
    374   }
    375 
    376   /* exit otherwise */
    377 
    378   if(0 >= data->timer)
    379     g_source_remove(data->timer);
    380   data->timer = -1;
    381 
    382   /*fprintf(stderr, "quit: giving up on %i torrents\n",
    383     tr_torrentCount(data->cbdata->tr));*/
    384   stoptransmission(data->cbdata->tr);
    385   clearhandlers();
    386   unblocksigs();
    387 
    388   gtk_widget_destroy(GTK_WIDGET(data->cbdata->wind));
    389   g_free(data->cbdata);
    390   g_free(data);
    391   gtk_main_quit();
    392 
    393   return FALSE;
    394 }
    395 
    396 void
    397 stoptransmission(void *tr) {
    398   while(0 < tr_torrentCount(tr))
    399     tr_torrentClose(tr, 0);
    400   tr_close(tr);
    401 }
    402 
    403358GtkWidget *
    404359makewind_toolbar(struct cbdata *data) {
     
    416371    item = gtk_tool_button_new_from_stock(actionitems[ii].id);
    417372    data->buttons[ii] = GTK_WIDGET(item);
    418     gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), actionitems[ii].name);
     373    gtk_tool_button_set_label(GTK_TOOL_BUTTON(item),
     374                              gettext(actionitems[ii].name));
    419375    gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
    420                               actionitems[ii].ttext, actionitems[ii].tpriv);
     376                              gettext(actionitems[ii].ttext),
     377                              actionitems[ii].tpriv);
    421378    g_object_set_data(G_OBJECT(item), LIST_ACTION,
    422379                      GINT_TO_POINTER(actionitems[ii].act));
     
    431388
    432389/* XXX check for unused data in model */
    433 enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_PROG, MC_DRATE, MC_URATE,
     390enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_TERR, MC_PROG, MC_DRATE, MC_URATE,
    434391      MC_ETA, MC_PEERS, MC_UPEERS, MC_DPEERS, MC_PIECES, MC_DOWN, MC_UP,
    435       MC_ROW_INDEX, MC_ROW_COUNT};
     392      MC_TORRENT, MC_ROW_COUNT};
    436393
    437394GtkWidget *
    438395makewind_list(struct cbdata *data) {
    439396  GType types[] = {
    440     /* info->name, info->totalSize, status,     error,         progress */
    441     G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,
    442     /* rateDownload, rateUpload,   eta,        peersTotal, peersUploading */
    443     G_TYPE_FLOAT,    G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
    444     /* peersDownloading, pieces,         downloaded,    uploaded */
    445     G_TYPE_INT,          TR_TYPE_PIECES, G_TYPE_UINT64, G_TYPE_UINT64,
    446     /* index into the torrent array */
    447     G_TYPE_INT};
    448   GtkListStore *model;
     397    /* info->name, info->totalSize, status,     error,      trackerError, */
     398    G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING,
     399    /* progress,  rateDownload, rateUpload,   eta,        peersTotal, */
     400    G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT,
     401    /* peersUploading, peersDownloading, pieces,         downloaded, */
     402    G_TYPE_INT,        G_TYPE_INT,       TR_TYPE_PIECES, G_TYPE_UINT64,
     403    /* uploaded,   the handle for the torrent */
     404    G_TYPE_UINT64, G_TYPE_POINTER};
     405  GtkListStore *store;
    449406  GtkWidget *view;
    450407  GtkTreeViewColumn *col;
    451408  GtkTreeSelection *sel;
    452409  GtkCellRenderer *namerend, *progrend;
     410  char *str;
    453411
    454412  assert(MC_ROW_COUNT == ALEN(types));
    455413
    456   model = gtk_list_store_newv(MC_ROW_COUNT, types);
    457   view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
     414  store = gtk_list_store_newv(MC_ROW_COUNT, types);
     415  view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
    458416  /* XXX do I need to worry about reference counts anywhere else? */
    459   g_object_unref(G_OBJECT(model));
    460   data->model = model;
     417  g_object_unref(G_OBJECT(store));
     418  data->model = GTK_TREE_MODEL(store);
    461419  data->view = GTK_TREE_VIEW(view);
    462420
    463421  namerend = gtk_cell_renderer_text_new();
    464   col = gtk_tree_view_column_new_with_attributes("Name", namerend, NULL);
     422  col = gtk_tree_view_column_new_with_attributes(_("Name"), namerend, NULL);
    465423  gtk_tree_view_column_set_cell_data_func(col, namerend, dfname, NULL, NULL);
    466424  gtk_tree_view_column_set_expand(col, TRUE);
     
    468426
    469427  progrend = tr_cell_renderer_torrent_new();
    470   g_object_set(progrend, "label", "<big>  fnord    fnord  </big>", NULL);
    471   col = gtk_tree_view_column_new_with_attributes("Progress", progrend, NULL);
     428  /* this string is only used to determing the size of the progress bar */
     429  str = g_markup_printf_escaped("<big>%s</big>", _("  fnord    fnord  "));
     430  g_object_set(progrend, "label", str, NULL);
     431  g_free(str);
     432  col = gtk_tree_view_column_new_with_attributes(_("Progress"), progrend, NULL);
    472433  gtk_tree_view_column_set_cell_data_func(col, progrend, dfprog, NULL, NULL);
    473434  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
     
    490451}
    491452
     453gboolean
     454winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
     455  struct cbdata *data = gdata;
     456  struct exitdata *edata;
     457  tr_stat_t *st;
     458  GtkTreeIter iter;
     459  tr_torrent_t *tor;
     460  gboolean going;
     461
     462  if(0 >= data->timer)
     463    g_source_remove(data->timer);
     464  data->timer = -1;
     465
     466  going = gtk_tree_model_get_iter_first(data->model, &iter);
     467  while(going) {
     468    gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
     469    st = tr_torrentStat(tor);
     470    if(TR_STATUS_ACTIVE & st->status) {
     471      tr_torrentStop(tor);
     472      going = gtk_tree_model_iter_next(data->model, &iter);
     473    } else {
     474      tr_torrentClose(data->tr, tor);
     475      going = gtk_list_store_remove(GTK_LIST_STORE(data->model), &iter);
     476    }
     477  }
     478
     479  /* XXX should disable widgets or something */
     480
     481  /* try to wait until torrents stop before exiting */
     482  edata = g_new0(struct exitdata, 1);
     483  edata->cbdata = data;
     484  edata->started = time(NULL);
     485  edata->timer = g_timeout_add(500, exitcheck, edata);
     486
     487  /* returning FALSE means to destroy the window */
     488  return TRUE;
     489}
     490
     491gboolean
     492exitcheck(gpointer gdata) {
     493  struct exitdata *data = gdata;
     494  tr_stat_t *st;
     495  GtkTreeIter iter;
     496  tr_torrent_t *tor;
     497  gboolean go;
     498
     499  go = gtk_tree_model_get_iter_first(data->cbdata->model, &iter);
     500  while(go) {
     501    gtk_tree_model_get(data->cbdata->model, &iter, MC_TORRENT, &tor, -1);
     502    st = tr_torrentStat(tor);
     503    if(!(TR_STATUS_PAUSE & st->status))
     504      go = gtk_tree_model_iter_next(data->cbdata->model, &iter);
     505    else {
     506      tr_torrentClose(data->cbdata->tr, tor);
     507      go = gtk_list_store_remove(GTK_LIST_STORE(data->cbdata->model), &iter);
     508    }
     509  }
     510
     511  /* keep going if we still have torrents and haven't hit the exit timeout */
     512  if(0 < tr_torrentCount(data->cbdata->tr) &&
     513     time(NULL) - data->started < TRACKER_EXIT_TIMEOUT) {
     514    assert(gtk_tree_model_get_iter_first(data->cbdata->model, &iter));
     515    updatemodel(data->cbdata);
     516    return TRUE;
     517  }
     518
     519  /* exit otherwise */
     520
     521  if(0 >= data->timer)
     522    g_source_remove(data->timer);
     523  data->timer = -1;
     524
     525  stoptransmission(data->cbdata->tr);
     526
     527  gtk_widget_destroy(GTK_WIDGET(data->cbdata->wind));
     528  g_free(data->cbdata);
     529  g_free(data);
     530  gtk_main_quit();
     531
     532  return FALSE;
     533}
     534
     535void
     536stoptransmission(tr_handle_t *tr) {
     537  GList *list, *ii;
     538
     539  list = NULL;
     540  tr_torrentIterate(tr, maketorrentlist, &list);
     541  for(ii = g_list_first(list); NULL != ii; ii = ii->next)
     542    tr_torrentClose(tr, ii->data);
     543  g_list_free(list);
     544
     545  tr_close(tr);
     546}
     547
     548void
     549gotdrag(GtkWidget *widget SHUTUP, GdkDragContext *dc, gint x SHUTUP,
     550        gint y SHUTUP, GtkSelectionData *sel, guint info SHUTUP, guint time,
     551        gpointer gdata) {
     552  struct cbdata *data = gdata;
     553  char prefix[] = "file:";
     554  char *files, *decoded, *deslashed, *hostless;
     555  int ii, len;
     556  GList *errs;
     557  gboolean gotfile;
     558  struct stat sb;
     559  int prelen = strlen(prefix);
     560
     561#ifdef DND_DEBUG
     562  char *sele = gdk_atom_name(sel->selection);
     563  char *targ = gdk_atom_name(sel->target);
     564  char *type = gdk_atom_name(sel->type);
     565
     566  fprintf(stderr, "dropped file: sel=%s targ=%s type=%s fmt=%i len=%i\n",
     567          sele, targ, type, sel->format, sel->length);
     568  g_free(sele);
     569  g_free(targ);
     570  g_free(type);
     571  if(8 == sel->format) {
     572    for(ii = 0; ii < sel->length; ii++)
     573      fprintf(stderr, "%02X ", sel->data[ii]);
     574    fprintf(stderr, "\n");
     575  }
     576#endif
     577
     578  errs = NULL;
     579  gotfile = FALSE;
     580  if(gdk_atom_intern("XdndSelection", FALSE) == sel->selection &&
     581     8 == sel->format) {
     582    /* split file list on carriage returns and linefeeds */
     583    files = g_new(char, sel->length + 1);
     584    memcpy(files, sel->data, sel->length);
     585    files[sel->length] = '\0';
     586    for(ii = 0; '\0' != files[ii]; ii++)
     587      if('\015' == files[ii] || '\012' == files[ii])
     588        files[ii] = '\0';
     589
     590    /* try to get a usable filename out of the URI supplied and add it */
     591    for(ii = 0; ii < sel->length; ii += len + 1) {
     592      if('\0' == files[ii])
     593        len = 0;
     594      else {
     595        len = strlen(files + ii);
     596        /* de-urlencode the URI */
     597        decoded = urldecode(files + ii, len);
     598        if(g_utf8_validate(decoded, -1, NULL)) {
     599          /* remove the file: prefix */
     600          if(prelen < len && 0 == strncmp(prefix, decoded, prelen)) {
     601            deslashed = decoded + prelen;
     602            /* trim excess / characters from the beginning */
     603            while('/' == deslashed[0] && '/' == deslashed[1])
     604              deslashed++;
     605            /* if the file doesn't exist, the first part might be a hostname */
     606            if(0 > g_stat(deslashed, &sb) &&
     607               NULL != (hostless = strchr(deslashed + 1, '/')) &&
     608               0 == g_stat(hostless, &sb))
     609              deslashed = hostless;
     610            /* finally, try to add it as a torrent */
     611            if(addtorrent(data, deslashed, NULL, FALSE, &errs))
     612              gotfile = TRUE;
     613          }
     614        }
     615        g_free(decoded);
     616      }
     617    }
     618
     619    g_free(files);
     620    if(gotfile)
     621      addedtorrents(data);
     622
     623    /* print any errors */
     624    if(NULL != errs) {
     625      files = joinstrlist(errs, "\n");
     626      errmsg(data->wind, ngettext("Failed to load the torrent file %s",
     627                                  "Failed to load the torrent files:\n%s",
     628                                  g_list_length(errs)), files);
     629      g_list_foreach(errs, (GFunc)g_free, NULL);
     630      g_list_free(errs);
     631      g_free(files);
     632    }
     633  }
     634
     635  gtk_drag_finish(dc, gotfile, FALSE, time);
     636}
     637
     638void
     639setupdrag(GtkWidget *widget, struct cbdata *data) {
     640  GtkTargetEntry targets[] = {
     641    { "STRING",     0, 0 },
     642    { "text/plain", 0, 0 },
     643    { "text/uri-list", 0, 0 },
     644  };
     645
     646  g_signal_connect(widget, "drag_data_received", G_CALLBACK(gotdrag), data);
     647
     648  gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, targets,
     649                    ALEN(targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
     650}
     651
    492652/* kludge to have the progress bars notice theme changes */
    493653static void
     
    522682dfname(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
    523683       GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
    524   char *name, *mb, *err, *str, *top, *bottom;
     684  char *name, *mb, *terr, *str, *top, *bottom;
    525685  guint64 size;
    526686  gfloat prog;
    527   int status, eta, tpeers, upeers, dpeers;
    528 
    529   /* XXX should I worry about gtk_tree_model_get failing? */
     687  int status, err, eta, tpeers, upeers, dpeers;
     688
    530689  gtk_tree_model_get(model, iter, MC_NAME, &name, MC_STAT, &status,
    531     MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta, MC_PEERS, &tpeers,
    532     MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
    533 
    534   if(0 > eta)
    535     eta = 0;
     690    MC_ERR, &err, MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta,
     691    MC_PEERS, &tpeers, MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
     692
    536693  if(0 > tpeers)
    537694    tpeers = 0;
     
    540697  if(0 > dpeers)
    541698    dpeers = 0;
    542   mb = readablesize(size, 1);
     699  mb = readablesize(size);
    543700  prog *= 100;
    544701
    545702  if(status & TR_STATUS_CHECK)
    546     top = g_strdup_printf("Checking existing files (%.1f%%)", prog);
    547   else if(status & TR_STATUS_DOWNLOAD)
    548     top = g_strdup_printf("Finishing in %02i:%02i:%02i (%.1f%%)",
    549                            eta / 60 / 60, eta / 60 % 60, eta % 60, prog);
     703    top = g_strdup_printf(_("Checking existing files (%.1f%%)"), prog);
     704  else if(status & TR_STATUS_DOWNLOAD) {
     705    if(0 > eta)
     706      top = g_strdup_printf(_("Finishing in --:--:-- (%.1f%%)"), prog);
     707    else
     708      top = g_strdup_printf(_("Finishing in %02i:%02i:%02i (%.1f%%)"),
     709                            eta / 60 / 60, eta / 60 % 60, eta % 60, prog);
     710  }
    550711  else if(status & TR_STATUS_SEED)
    551     top = g_strdup_printf("Seeding, uploading to %d of %d peer%s",
    552                            dpeers, tpeers, (1 == tpeers ? "" : "s"));
     712    top = g_strdup_printf(ngettext("Seeding, uploading to %d of %d peer",
     713                                   "Seeding, uploading to %d of %d peers",
     714                                   tpeers), dpeers, tpeers);
    553715  else if(status & TR_STATUS_STOPPING)
    554     top = g_strdup("Stopping...");
     716    top = g_strdup(_("Stopping..."));
    555717  else if(status & TR_STATUS_PAUSE)
    556     top = g_strdup_printf("Paused (%.1f%%)", prog);
     718    top = g_strdup_printf(_("Stopped (%.1f%%)"), prog);
    557719  else {
    558720    top = g_strdup("");
     
    560722  }
    561723
    562   if(status & TR_TRACKER_ERROR) {
    563     gtk_tree_model_get(model, iter, MC_ERR, &err, -1);
    564     bottom = g_strconcat("Error: ", err, NULL);
    565     g_free(err);
     724  if(TR_NOERROR != err) {
     725    gtk_tree_model_get(model, iter, MC_TERR, &terr, -1);
     726    bottom = g_strconcat(_("Error: "), terr, NULL);
     727    g_free(terr);
    566728  }
    567729  else if(status & TR_STATUS_DOWNLOAD)
    568     bottom = g_strdup_printf("Downloading from %i of %i peer%s",
    569                              upeers, tpeers, (1 == tpeers ? "" : "s"));
     730    bottom = g_strdup_printf(ngettext("Downloading from %i of %i peer",
     731                                      "Downloading from %i of %i peers",
     732                                      tpeers), upeers, tpeers);
    570733  else
    571734    bottom = NULL;
     
    584747dfprog(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
    585748       GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
    586   char *dlstr, *ulstr, *str;
     749  char *dlstr, *ulstr, *str, *marked;
    587750  gfloat prog, dl, ul;
    588 
    589   /* XXX should I worry about gtk_tree_model_get failing? */
    590   gtk_tree_model_get(model, iter, MC_PROG, &prog,
    591                      MC_DRATE, &dl, MC_URATE, &ul, -1);
     751  guint64 down, up;
     752
     753  gtk_tree_model_get(model, iter, MC_PROG, &prog, MC_DRATE, &dl, MC_URATE, &ul,
     754                     MC_DOWN, &down, MC_UP, &up, -1);
    592755  if(0.0 > prog)
    593756    prog = 0.0;
     
    595758    prog = 1.0;
    596759
    597   dlstr = readablesize(dl * 1024.0, 2);
    598   ulstr = readablesize(ul * 1024.0, 2);
    599   str = g_strdup_printf("<small>DL: %s/s\nUL: %s/s</small>", dlstr, ulstr);
     760  ulstr = readablesize(ul * 1024.0);
     761  if(1.0 == prog) {
     762    dlstr = ratiostr(down, up);
     763    str = g_strdup_printf(_("Ratio: %s\nUL: %s/s"), dlstr, ulstr);
     764  } else {
     765    dlstr = readablesize(dl * 1024.0);
     766    str = g_strdup_printf(_("DL: %s/s\nUL: %s/s"), dlstr, ulstr);
     767  }
     768  marked = g_markup_printf_escaped("<small>%s</small>", str);
    600769  g_object_set(rend, "text", str, "value", prog, NULL);
    601770  g_free(dlstr);
    602771  g_free(ulstr);
    603772  g_free(str);
     773  g_free(marked);
    604774}
    605775
     
    607777updatemodel(gpointer gdata) {
    608778  struct cbdata *data = gdata;
     779  tr_torrent_t *tor;
    609780  tr_stat_t *st;
    610   int ii, max;
     781  tr_info_t *in;
    611782  GtkTreeIter iter;
    612783  float up, down;
    613784  char *upstr, *downstr, *str;
    614785
    615   blocksigs();
    616 
    617   max = tr_torrentStat(data->tr, &st);
    618   for(ii = 0; ii < max; ii++) {
    619     if(!(ii ? gtk_tree_model_iter_next(GTK_TREE_MODEL(data->model), &iter) :
    620          gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter)))
    621       gtk_list_store_append(data->model, &iter);
    622     /* XXX find out if setting the same data emits changed signal */
    623     gtk_list_store_set(
    624       data->model, &iter, MC_ROW_INDEX, ii,
    625       MC_NAME, st[ii].info.name, MC_SIZE, st[ii].info.totalSize,
    626       MC_STAT, st[ii].status, MC_ERR, st[ii].error, MC_PROG, st[ii].progress,
    627       MC_DRATE, st[ii].rateDownload, MC_URATE, st[ii].rateUpload,
    628       MC_ETA, st[ii].eta, MC_PEERS, st[ii].peersTotal,
    629       MC_UPEERS, st[ii].peersUploading, MC_DPEERS, st[ii].peersDownloading,
    630       MC_DOWN, st[ii].downloaded, MC_UP, st[ii].uploaded, -1);
    631   }
    632   free(st);
    633 
    634   /* remove any excess rows */
    635   if(ii ? gtk_tree_model_iter_next(GTK_TREE_MODEL(data->model), &iter) :
    636      gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter))
    637     while(gtk_list_store_remove(data->model, &iter))
    638       ;
     786  if(0 < global_sigcount) {
     787    stoptransmission(data->tr);
     788    global_sigcount = SIGCOUNT_MAX;
     789    raise(global_lastsig);
     790  }
     791
     792  if(gtk_tree_model_get_iter_first(data->model, &iter)) {
     793    do {
     794      gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
     795      st = tr_torrentStat(tor);
     796      in = tr_torrentInfo(tor);
     797      /* XXX find out if setting the same data emits changed signal */
     798      gtk_list_store_set(GTK_LIST_STORE(data->model), &iter, MC_NAME, in->name,
     799        MC_SIZE, in->totalSize, MC_STAT, st->status, MC_ERR, st->error,
     800        MC_TERR, st->trackerError, MC_PROG, st->progress,
     801        MC_DRATE, st->rateDownload, MC_URATE, st->rateUpload, MC_ETA, st->eta,
     802        MC_PEERS, st->peersTotal, MC_UPEERS, st->peersUploading,
     803        MC_DPEERS, st->peersDownloading, MC_DOWN, st->downloaded,
     804        MC_UP, st->uploaded, -1);
     805    } while(gtk_tree_model_iter_next(data->model, &iter));
     806  }
    639807
    640808  /* update the status bar */
    641809  tr_torrentRates(data->tr, &up, &down);
    642   downstr = readablesize(down * 1024.0, 2);
    643   upstr = readablesize(up * 1024.0, 2);
    644   str = g_strdup_printf("     Total DL: %s/s     Total UL: %s/s", upstr, downstr);
     810  downstr = readablesize(down * 1024.0);
     811  upstr = readablesize(up * 1024.0);
     812  str = g_strdup_printf(_("     Total DL: %s/s     Total UL: %s/s"),
     813                        upstr, downstr);
    645814  gtk_statusbar_pop(data->bar, 0);
    646815  gtk_statusbar_push(data->bar, 0, str);
     
    652821  fixbuttons(NULL, data);
    653822
    654   unblocksigs();
    655 
    656823  return TRUE;
    657824}
     
    664831  GtkTreePath *path;
    665832  GtkTreeIter iter;
    666   int index, status;
     833  int status;
     834  gpointer tor;
    667835  GList *ids;
    668836
     
    674842      dopopupmenu(event, data, NULL, 0);
    675843    else {
    676       if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path)) {
     844      if(gtk_tree_model_get_iter(data->model, &iter, path)) {
    677845        /* get ID and status for the right-clicked row */
    678         gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter,
    679                            MC_ROW_INDEX, &index, MC_STAT, &status, -1);
     846        gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor,
     847                           MC_STAT, &status, -1);
    680848        /* get a list of selected IDs */
    681849        ids = NULL;
    682850        gtk_tree_selection_selected_foreach(sel, makeidlist, &ids);
    683851        /* is the clicked row selected? */
    684         if(NULL == g_list_find(ids, GINT_TO_POINTER(index))) {
     852        if(NULL == g_list_find(ids, tor)) {
    685853          /* no, do the popup for just the clicked row */
    686854          g_list_free(ids);
    687           dopopupmenu(event, data, g_list_append(NULL, GINT_TO_POINTER(index)),
    688                       status);
     855          dopopupmenu(event, data, g_list_append(NULL, tor), status);
    689856        } else {
    690857          /* yes, do the popup for all the selected rows */
     
    705872  struct cbdata *data = gdata;
    706873  GtkTreeSelection *sel = gtk_tree_view_get_selection(data->view);
    707   GtkTreeModel *model;
    708874  GList *ids;
    709875  int status;
     
    712878    dopopupmenu(NULL, data, NULL, 0);
    713879  else {
    714     assert(model == GTK_TREE_MODEL(data->model));
    715880    status = 0;
    716881    gtk_tree_selection_selected_foreach(sel, orstatus, &status);
     
    735900        (NULL == ids || !(actionitems[ii].avail & status))))
    736901      continue;
    737     item = gtk_menu_item_new_with_label(actionitems[ii].name);
     902    item = gtk_menu_item_new_with_label(gettext(actionitems[ii].name));
    738903    /* set the action for the menu item */
    739904    g_object_set_data(G_OBJECT(item), LIST_ACTION,
     
    743908                      GINT_TO_POINTER(FROM_POPUP));
    744909    /* set a glist of selected torrent's IDs */
    745     g_object_set_data(G_OBJECT(item), LIST_INDEX, ids);
     910    g_object_set_data(G_OBJECT(item), LIST_IDS, ids);
    746911    /* set the menu widget, so the activate handler can destroy it */
    747912    g_object_set_data(G_OBJECT(item), LIST_MENU_WIDGET, menu);
     
    752917
    753918  /* set up the glist to be freed when the menu is destroyed */
    754   g_object_set_data_full(G_OBJECT(menu), LIST_INDEX, ids,
     919  g_object_set_data_full(G_OBJECT(menu), LIST_IDS, ids,
    755920                         (GDestroyNotify)g_list_free);
    756921
     
    778943  enum listfrom from =
    779944    GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), LIST_ACTION_FROM));
    780   int index, count;
    781945  unsigned int actindex;
    782946  tr_stat_t *sb;
    783947  GList *ids, *ii;
     948  tr_torrent_t *tor;
    784949  gboolean updatesave;
     950  GtkTreeIter iter;
    785951
    786952  /* destroy the popup menu, if any */
     
    790956  switch(act) {
    791957    case ACT_OPEN:
    792       makeaddwind(addtorrent, data->wind, data->tr, addedtorrents, data);
     958      makeaddwind(data->wind, addtorrent, addedtorrents, data);
    793959      return;
    794960    case ACT_PREF:
    795       makeprefwindow(data->wind, data->tr);
     961      if(!data->prefsopen)
     962        makeprefwindow(data->wind, data->tr, &data->prefsopen);
    796963      return;
    797964    default:
     
    803970      ids = NULL;
    804971      gtk_tree_selection_selected_foreach(sel, makeidlist, &ids);
    805       /* XXX should I assert(0 <= index) to insure a row was selected? */
    806972      break;
    807973    case FROM_POPUP:
    808       ids = g_object_get_data(G_OBJECT(widget), LIST_INDEX);
     974      ids = g_object_get_data(G_OBJECT(widget), LIST_IDS);
    809975      break;
    810976    default:
     
    818984  assert(actindex < ALEN(actionitems));
    819985
    820   blocksigs();
    821986  updatesave = FALSE;
    822   count = tr_torrentStat(data->tr, &sb);
    823987
    824988  for(ii = g_list_sort(ids, intrevcmp); NULL != ii; ii = ii->next) {
    825     index = GPOINTER_TO_INT(ii->data);
    826     if(index >= count) {
    827       assert(!"illegal torrent id");
    828       continue;
    829     }
     989    tor = ii->data;
     990    sb = tr_torrentStat(tor);
     991
    830992    /* check if this action is valid for this torrent */
    831993    if(actionitems[actindex].nomenu ||
    832994       (actionitems[actindex].avail &&
    833         !(actionitems[actindex].avail & sb[index].status)))
     995        !(actionitems[actindex].avail & sb->status)))
    834996      continue;
    835997
    836998    switch(act) {
    837999      case ACT_START:
    838         tr_torrentStart(data->tr, index);
     1000        tr_torrentStart(tor);
    8391001        updatesave = TRUE;
    8401002        break;
    8411003      case ACT_STOP:
    842         tr_torrentStop(data->tr, index);
     1004        tr_torrentStop(tor);
    8431005        updatesave = TRUE;
    8441006        break;
    8451007      case ACT_DELETE:
    846         if(TR_TORRENT_NEEDS_STOP(sb[index].status))
    847           tr_torrentStop(data->tr, index);
    848         tr_torrentClose(data->tr, index);
     1008        if(TR_STATUS_ACTIVE & sb->status)
     1009          tr_torrentStop(tor);
     1010        tr_torrentClose(data->tr, tor);
     1011        findtorrent(data->model, tor, &iter);
     1012        gtk_list_store_remove(GTK_LIST_STORE(data->model), &iter);
    8491013        updatesave = TRUE;
    8501014        /* XXX should only unselect deleted rows */
    851         gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(data->view));
     1015        gtk_tree_selection_unselect_all(
     1016          gtk_tree_view_get_selection(data->view));
    8521017        break;
    8531018      case ACT_INFO:
    854         makeinfowind(data->wind, data->tr, index);
     1019        makeinfowind(data->wind, tor);
    8551020        break;
    8561021      default:
     
    8591024    }
    8601025  }
    861   free(sb);
    8621026
    8631027  if(updatesave) {
    864     savetorrents(data->tr, data->wind, -1, NULL);
     1028    savetorrents(data->tr, data->wind);
    8651029    updatemodel(data);
    8661030  }
    867 
    868   unblocksigs();
    8691031
    8701032  if(FROM_BUTTON == from)
    8711033    g_list_free(ids);
     1034}
     1035
     1036void
     1037findtorrent(GtkTreeModel *model, tr_torrent_t *tor, GtkTreeIter *iter) {
     1038  gpointer ptr;
     1039
     1040  if(gtk_tree_model_get_iter_first(model, iter)) {
     1041    do {
     1042      gtk_tree_model_get(model, iter, MC_TORRENT, &ptr, -1);
     1043      if(tor == ptr)
     1044        return;
     1045    } while(gtk_tree_model_iter_next(model, iter));
     1046  }
     1047
     1048  assert(!"torrent not found");
    8721049}
    8731050
     
    8901067  struct cbdata *data = gdata;
    8911068  GtkTreeIter iter;
    892   int index;
    893 
    894   if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path)) {
    895     gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter,
    896                        MC_ROW_INDEX, &index, -1);
    897     makeinfowind(data->wind, data->tr, index);
    898   }
    899 }
    900 
    901 gboolean
    902 addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
    903            const char *dir, gboolean paused) {
     1069  tr_torrent_t *tor;
     1070
     1071  if(gtk_tree_model_get_iter(data->model, &iter, path)) {
     1072    gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
     1073    makeinfowind(data->wind, tor);
     1074  }
     1075}
     1076
     1077gboolean
     1078addtorrent(void *vdata, const char *torrent, const char *dir, gboolean paused,
     1079           GList **errs) {
     1080  const struct { const int err; const char *msg; } errstrs[] = {
     1081    {TR_EINVALID,       N_("not a valid torrent file")},
     1082    {TR_EDUPLICATE,     N_("torrent is already open")},
     1083  };
     1084  struct cbdata *data = vdata;
     1085  tr_torrent_t *new;
    9041086  char *wd;
     1087  int err;
     1088  unsigned int ii;
     1089  GtkTreeIter iter;
    9051090
    9061091  if(NULL == dir && NULL != (dir = cf_getpref(PREF_DIR))) {
    9071092    if(!mkdir_p(dir, 0777)) {
    908       errmsg(parentwind, "Failed to create download directory %s:\n%s",
     1093      errmsg(data->wind, _("Failed to create the directory %s:\n%s"),
    9091094             dir, strerror(errno));
    9101095      return FALSE;
     
    9121097  }
    9131098
    914   blocksigs();
    915 
    916   if(0 != tr_torrentInit(tr, torrent)) {
    917     unblocksigs();
    918     /* XXX would be nice to have errno strings, are they printed to stdout? */
    919     errmsg(parentwind, "Failed to open torrent file %s", torrent);
     1099  if(NULL == (new = tr_torrentInit(data->tr, torrent, &err))) {
     1100    for(ii = 0; ii < ALEN(errstrs); ii++)
     1101      if(err == errstrs[ii].err)
     1102        break;
     1103    if(NULL == errs) {
     1104      if(ii == ALEN(errstrs))
     1105        errmsg(data->wind, _("Failed to load the torrent file %s"), torrent);
     1106      else
     1107        errmsg(data->wind, _("Failed to load the torrent file %s: %s"),
     1108               torrent, gettext(errstrs[ii].msg));
     1109    } else {
     1110      if(ii == ALEN(errstrs))
     1111        *errs = g_list_append(*errs, g_strdup(torrent));
     1112      else
     1113        *errs = g_list_append(*errs, g_strdup_printf(_("%s (%s)"),
     1114                              torrent, gettext(errstrs[ii].msg)));
     1115    }
    9201116    return FALSE;
    9211117  }
    9221118
     1119  gtk_list_store_append(GTK_LIST_STORE(data->model), &iter);
     1120  gtk_list_store_set(GTK_LIST_STORE(data->model), &iter, MC_TORRENT, new, -1);
     1121
    9231122  if(NULL != dir)
    924     tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, dir);
     1123    tr_torrentSetFolder(new, dir);
    9251124  else {
    9261125    wd = g_new(char, MAXPATHLEN + 1);
    9271126    if(NULL == getcwd(wd, MAXPATHLEN + 1))
    928       tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, ".");
     1127      tr_torrentSetFolder(new, ".");
    9291128    else {
    930       tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, wd);
     1129      tr_torrentSetFolder(new, wd);
    9311130      free(wd);
    9321131    }
     
    9341133
    9351134  if(!paused)
    936     tr_torrentStart(tr, tr_torrentCount(tr) - 1);
    937 
    938   unblocksigs();
     1135    tr_torrentStart(new);
    9391136
    9401137  return TRUE;
     
    9461143
    9471144  updatemodel(data);
    948   savetorrents(data->tr, data->wind, -1, NULL);
    949 }
    950 
    951 gboolean
    952 savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat) {
     1145  savetorrents(data->tr, data->wind);
     1146}
     1147
     1148gboolean
     1149savetorrents(tr_handle_t *tr, GtkWindow *wind) {
     1150  GList *torrents;
    9531151  char *errstr;
    954   tr_stat_t *st;
    9551152  gboolean ret;
    9561153
    957   assert(NULL != tr || 0 <= count);
    958 
    959   if(0 <= count)
    960     ret = cf_savestate(count, stat, &errstr);
    961   else {
    962     blocksigs();
    963     count = tr_torrentStat(tr, &st);
    964     unblocksigs();
    965     ret = cf_savestate(count, st, &errstr);
    966     free(st);
    967   }
    968 
    969   if(!ret) {
     1154  torrents = NULL;
     1155  tr_torrentIterate(tr, maketorrentlist, &torrents);
     1156
     1157  if(!(ret = cf_savestate(torrents, &errstr))) {
    9701158    errmsg(wind, "%s", errstr);
    9711159    g_free(errstr);
    9721160  }
     1161
     1162  g_list_free(torrents);
    9731163
    9741164  return ret;
     
    9901180           gpointer gdata) {
    9911181  GList **ids = gdata;
    992   int index;
    993 
    994   gtk_tree_model_get(model, iter, MC_ROW_INDEX, &index, -1);
    995   *ids = g_list_append(*ids, GINT_TO_POINTER(index));
    996 }
     1182  gpointer ptr;
     1183
     1184  gtk_tree_model_get(model, iter, MC_TORRENT, &ptr, -1);
     1185  *ids = g_list_append(*ids, ptr);
     1186}
     1187
     1188void
     1189maketorrentlist(tr_torrent_t *tor, void *data) {
     1190  GList **list = data;
     1191
     1192  *list = g_list_append(*list, tor);
     1193}
     1194
     1195void
     1196setupsighandlers(void) {
     1197  int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
     1198  struct sigaction sa;
     1199  unsigned int ii;
     1200
     1201  bzero(&sa, sizeof(sa));
     1202  sa.sa_handler = fatalsig;
     1203  for(ii = 0; ii < ALEN(sigs); ii++)
     1204    sigaction(sigs[ii], &sa, NULL);
     1205}
     1206
     1207void
     1208fatalsig(int sig) {
     1209  struct sigaction sa;
     1210
     1211  global_lastsig = sig;
     1212
     1213  if(SIGCOUNT_MAX <= ++global_sigcount) {
     1214    bzero(&sa, sizeof(sa));
     1215    sa.sa_handler = SIG_DFL;
     1216    sigaction(sig, &sa, NULL);
     1217    raise(sig);
     1218  }
     1219}
  • branches/simple_http_parsing/gtk/util.c

    r24 r222  
    11/*
    2   Copyright (c) 2005 Joshua Elsasser. All rights reserved.
     2  Copyright (c) 2005-2006 Joshua Elsasser. All rights reserved.
    33   
    44  Redistribution and use in source and binary forms, with or without
     
    3333
    3434#include <gtk/gtk.h>
     35#include <glib/gi18n.h>
    3536
    3637#include "util.h"
    3738
    38 static void
    39 sigexithandler(int sig);
     39#define BESTDECIMAL(d)          (10.0 > (d) ? 2 : (100.0 > (d) ? 1 : 0))
     40
    4041static void
    4142errcb(GtkWidget *wind, int resp, gpointer data);
     
    5960}
    6061
    61 char *
    62 readablesize(guint64 size, int decimals) {
    63   const char *sizes[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
     62static const char *sizestrs[] = {
     63  N_("B"), N_("KiB"), N_("MiB"), N_("GiB"), N_("TiB"), N_("PiB"), N_("EiB"),
     64};
     65
     66char *
     67readablesize(guint64 size) {
    6468  unsigned int ii;
    6569  double small = size;
    6670
    67   for(ii = 0; ii + 1 < ALEN(sizes) && 1024.0 <= small / 1024.0; ii++)
     71  for(ii = 0; ii + 1 < ALEN(sizestrs) && 1024.0 <= small / 1024.0; ii++)
    6872    small /= 1024.0;
    6973
     
    7377  }
    7478
    75   return g_strdup_printf("%.*f %s", decimals, small, sizes[ii]);
     79  return g_strdup_printf("%.*f %s", BESTDECIMAL(small), small,
     80                         gettext(sizestrs[ii]));
     81}
     82
     83char *
     84ratiostr(guint64 down, guint64 up) {
     85  double ratio;
     86
     87  if(0 == up && 0 == down)
     88    return g_strdup(_("N/A"));
     89
     90  if(0 == down)
     91    /* this is a UTF-8 infinity symbol */
     92    return g_strdup(_("\xE2\x88\x9E"));
     93
     94  ratio = (double)up / (double)down;
     95
     96  return g_strdup_printf("%.*f", (10.0 > ratio ? 2 : (100.0 > ratio ? 1 : 0)),
     97                         ratio);
    7698}
    7799
     
    102124}
    103125
    104 static int exit_sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
    105 static callbackfunc_t exit_func = NULL;
    106 static void *exit_data = NULL;
    107 static int exit_block_level = 0;
    108 
    109 void
    110 setuphandlers(callbackfunc_t func, void *data) {
    111   struct sigaction sa;
    112   unsigned int ii;
    113 
    114   exit_data = data;
    115   exit_func = func;
    116 
    117   bzero(&sa, sizeof(sa));
    118   sa.sa_handler = sigexithandler;
    119   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    120     sigaction(exit_sigs[ii], &sa, NULL);
    121 }
    122 
    123 void
    124 clearhandlers(void) {
    125   struct sigaction sa;
    126   unsigned int ii;
    127 
    128   bzero(&sa, sizeof(sa));
    129   sa.sa_handler = SIG_DFL;
    130   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    131     sigaction(exit_sigs[ii], &sa, NULL);
    132 }
    133 
    134 static void
    135 sigexithandler(int sig) {
    136   exit_func(exit_data);
    137   clearhandlers();
    138   raise(sig);
    139 }
    140 
    141 void
    142 blocksigs(void) {
    143   sigset_t mask;
    144   unsigned int ii;
    145 
    146   if(0 < (exit_block_level++))
    147     return;
    148 
    149   sigemptyset(&mask);
    150   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    151     sigaddset(&mask, exit_sigs[ii]);
    152   sigprocmask(SIG_BLOCK, &mask, NULL);
    153 }
    154 
    155 void
    156 unblocksigs(void) {
    157   sigset_t mask;
    158   unsigned int ii;
    159 
    160   if(0 < (--exit_block_level))
    161     return;
    162 
    163   sigemptyset(&mask);
    164   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    165     sigaddset(&mask, exit_sigs[ii]);
    166   sigprocmask(SIG_UNBLOCK, &mask, NULL);
     126char *
     127joinstrlist(GList *list, char *sep) {
     128  GList *ii;
     129  int len;
     130  char *ret, *dest;
     131
     132  if(0 > (len = strlen(sep) * (g_list_length(list) - 1)))
     133    return NULL;
     134
     135  for(ii = g_list_first(list); NULL != ii; ii = ii->next)
     136    len += strlen(ii->data);
     137
     138  dest = ret = g_new(char, len + 1);
     139
     140  for(ii = g_list_first(list); NULL != ii; ii = ii->next) {
     141    dest = g_stpcpy(dest, ii->data);
     142    if(NULL != ii->next)
     143      dest = g_stpcpy(dest, sep);
     144  }
     145
     146  return ret;
     147}
     148
     149char *
     150urldecode(const char *str, int len) {
     151  int ii, jj;
     152  char *ret;
     153  char buf[3];
     154
     155  if(0 >= len)
     156    len = strlen(str);
     157
     158  for(ii = jj = 0; ii < len; ii++, jj++)
     159    if('%' == str[ii])
     160      ii += 2;
     161
     162  ret = g_new(char, jj + 1);
     163
     164  buf[2] = '\0';
     165  for(ii = jj = 0; ii < len; ii++, jj++) {
     166    switch(str[ii]) {
     167      case '%':
     168        if(ii + 2 < len) {
     169          buf[0] = str[ii+1];
     170          buf[1] = str[ii+2];
     171          ret[jj] = g_ascii_strtoull(buf, NULL, 16);
     172        }
     173        ii += 2;
     174        break;
     175      case '+':
     176        ret[jj] = ' ';
     177      default:
     178        ret[jj] = str[ii];
     179    }
     180  }
     181  ret[jj] = '\0';
     182
     183  return ret;
    167184}
    168185
  • branches/simple_http_parsing/gtk/util.h

    r24 r222  
    11/*
    2   Copyright (c) 2005 Joshua Elsasser. All rights reserved.
     2  Copyright (c) 2005-2006 Joshua Elsasser. All rights reserved.
    33   
    44  Redistribution and use in source and binary forms, with or without
     
    4545
    4646/* try to interpret a string as a textual representation of a boolean */
     47/* note that this isn't localized */
    4748gboolean
    4849strbool(const char *str);
    4950
    50 /* return a human-readable string for the size given in bytes with the
    51    requested number of decimal places.  the string must be g_free()d */
     51/* return a human-readable string for the size given in bytes.
     52   the string must be g_free()d */
    5253char *
    53 readablesize(guint64 size, int decimals);
     54readablesize(guint64 size);
     55
     56/* returns a string representing the download ratio.
     57   the string must be g_free()d */
     58char *
     59ratiostr(guint64 down, guint64 up);
    5460
    5561/* create a directory and any missing parent directories */
     
    5763mkdir_p(const char *name, mode_t mode);
    5864
    59 /* set up a handler for various fatal signals */
    60 void
    61 setuphandlers(callbackfunc_t func, void *data);
     65/* joins a GList of strings into one string using an optional separator */
     66char *
     67joinstrlist(GList *list, char *sep);
    6268
    63 /* clear the handlers for fatal signals */
    64 void
    65 clearhandlers(void);
    66 
    67 /* blocks and unblocks delivery of fatal signals. calls to these
    68    functions can be nested as long as unblocksigs() is called exactly
    69    as many times as blocksigs().  only the first blocksigs() will
    70    block signals and only the last unblocksigs() will unblock them. */
    71 void
    72 blocksigs(void);
    73 void
    74 unblocksigs(void);
     69/* decodes a string that has been urlencoded */
     70char *
     71urldecode(const char *str, int len);
    7572
    7673/* if wind is NULL then you must call gtk_widget_show on the returned widget */
  • branches/simple_http_parsing/libtransmission

    • Property svn:ignore set to
      *.a
      .depend
  • branches/simple_http_parsing/libtransmission/choking.c

    r76 r222  
    2424#include "transmission.h"
    2525
     26#ifndef HAVE_LRINTF
     27#  define lrintf(a) ((int)(0.5+(a)))
     28#endif
     29
     30/* We may try to allocate and free tables of size 0. Quick and dirty
     31   way to handle it... */
     32void * __malloc( int size )
     33{
     34    if( !size )
     35        return NULL;
     36    return malloc( size );
     37}
     38void __free( void * p )
     39{
     40    if( p )
     41        free( p );
     42}
     43#define malloc __malloc
     44#define free   __free
     45
    2646struct tr_choking_s
    2747{
     
    4969        c->slots = 4242;
    5070    else
     71        /* Reckon a number of slots from the upload limit. There is no
     72           official right way to do this, the formula below e.g. gives:
     73            10  KB/s -> 4  * 2.50 KB/s
     74            20  KB/s -> 6  * 3.33 KB/s
     75            50  KB/s -> 10 * 5.00 KB/s
     76            100 KB/s -> 14 * 7.14 KB/s */
    5177        c->slots = lrintf( sqrt( 2 * limit ) );
    5278    tr_lockUnlock( &c->lock );
    5379}
    5480
    55 static inline void sortPeers( tr_peer_t ** peers, int count )
    56 {
    57     int i, j;
    58     tr_peer_t * tmp;
    59 
    60     for( i = count - 1; i > 0; i-- )
     81#define sortPeersAscending(a,ac,z,zc,n,nc)  sortPeers(a,ac,z,zc,n,nc,0)
     82#define sortPeersDescending(a,ac,z,zc,n,nc) sortPeers(a,ac,z,zc,n,nc,1)
     83static inline void sortPeers( tr_peer_t ** all, int allCount,
     84                              tr_peer_t ** zero, int * zeroCount,
     85                              tr_peer_t ** nonZero, int * nonZeroCount,
     86                              int order )
     87{
     88    int i, shuffle;
     89
     90    /* Seperate uploaders from non-uploaders */
     91    *zeroCount    = 0;
     92    *nonZeroCount = 0;
     93    for( i = 0; i < allCount; i++ )
     94    {
     95        if( tr_peerDownloadRate( all[i] ) < 0.1 )
     96            zero[(*zeroCount)++] = all[i];
     97        else
     98            nonZero[(*nonZeroCount)++] = all[i];
     99    }
     100
     101    /* Randomly shuffle non-uploaders, so they are treated equally */
     102    if( *zeroCount && ( shuffle = tr_rand( *zeroCount ) ) )
     103    {
     104        tr_peer_t ** bak;
     105        bak = malloc( shuffle * sizeof( tr_peer_t * ) );
     106        memcpy( bak, zero, shuffle * sizeof( tr_peer_t * ) );
     107        memmove( zero, &zero[shuffle],
     108                 ( *zeroCount - shuffle ) * sizeof( tr_peer_t * ) );
     109        memcpy( &zero[*zeroCount - shuffle], bak,
     110                 shuffle * sizeof( tr_peer_t * ) );
     111        free( bak );
     112    }
     113
     114    /* Sort uploaders by download rate */
     115    for( i = *nonZeroCount - 1; i > 0; i-- )
     116    {
     117        float rate1, rate2;
     118        tr_peer_t * tmp;
     119        int j, sorted;
     120
     121        sorted = 1;
    61122        for( j = 0; j < i; j++ )
    62123        {
    63             if( tr_peerDownloadRate( peers[j] ) >
    64                     tr_peerDownloadRate( peers[j+1] ) )
     124            rate1 = tr_peerDownloadRate( nonZero[j] );
     125            rate2 = tr_peerDownloadRate( nonZero[j+1] );
     126            if( order ? ( rate1 < rate2 ) : ( rate1 > rate2 ) )
    65127            {
    66                 tmp        = peers[j];
    67                 peers[j]   = peers[j+1];
    68                 peers[j+1] = tmp;
     128                tmp          = nonZero[j];
     129                nonZero[j]   = nonZero[j+1];
     130                nonZero[j+1] = tmp;
     131                sorted       = 0;
    69132            }
    70133        }
     134        if( sorted )
     135            break;
     136    }
    71137}
    72138
    73139void tr_chokingPulse( tr_choking_t * c )
    74140{
    75     int i, j, peersTotalCount;
    76     tr_peer_t * peer;
    77     tr_peer_t ** peersCanChoke, ** peersCanUnchoke;
    78     int peersCanChokeCount, peersCanUnchokeCount, unchokedCount;
     141    int peersTotalCount, unchoked, mustOptimistic = 1;
     142    tr_peer_t ** canChoke, ** canUnchoke;
     143    tr_peer_t ** canChokeZero, ** canUnchokeZero;
     144    tr_peer_t ** canChokeNonZero, ** canUnchokeNonZero;
     145    int canChokeCount, canUnchokeCount;
     146    int canChokeZeroCount, canUnchokeZeroCount;
     147    int canChokeNonZeroCount, canUnchokeNonZeroCount;
    79148    tr_torrent_t * tor;
    80149    uint64_t now = tr_date();
     
    84153    /* Lock all torrents and get the total number of peers */
    85154    peersTotalCount = 0;
    86     for( i = 0; i < c->h->torrentCount; i++ )
    87     {
    88         tor = c->h->torrents[i];
     155    for( tor = c->h->torrentList; tor; tor = tor->next )
     156    {
    89157        tr_lockLock( &tor->lock );
    90158        peersTotalCount += tor->peerCount;
    91159    }
    92160
    93     peersCanChoke   = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
    94     peersCanUnchoke = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
    95     peersCanChokeCount   = 0;
    96     peersCanUnchokeCount = 0;
    97     unchokedCount        = 0;
    98 
    99     /* Build two lists of interested peers: those who may choke,
    100        those who may unchoke */
    101     for( i = 0; i < c->h->torrentCount; i++ )
    102     {
    103         tor = c->h->torrents[i];
    104         for( j = 0; j < tor->peerCount; j++ )
     161    canChoke   = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
     162    canUnchoke = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
     163    canChokeCount   = 0;
     164    canUnchokeCount = 0;
     165    unchoked        = 0;
     166
     167    for( tor = c->h->torrentList; tor; tor = tor->next )
     168    {
     169        tr_peer_t * peer;
     170        int i;
     171
     172        for( i = 0; i < tor->peerCount; i++ )
    105173        {
    106             peer = tor->peers[j];
     174            peer = tor->peers[i];
    107175
    108176            if( !tr_peerIsConnected( peer ) )
    109177                continue;
    110178
     179            /* Choke peers who have lost their interest in us */
    111180            if( !tr_peerIsInterested( peer ) )
    112181            {
    113182                if( tr_peerIsUnchoked( peer ) )
     183                {
    114184                    tr_peerChoke( peer );
     185                    tr_peerSetOptimistic( peer, 0 );
     186                }
    115187                continue;
    116188            }
    117189
     190            /* Build two lists of interested peers: those we may choke,
     191               those we may unchoke. Whatever happens, we never choke a
     192               peer less than 10 seconds after the time we unchoked him
     193               (or the other way around). */
    118194            if( tr_peerIsUnchoked( peer ) )
    119195            {
    120                 unchokedCount++;
     196                if( tr_peerIsOptimistic( peer ) )
     197                {
     198                    if( tr_peerLastChoke( peer ) + 30000 < now )
     199                    {
     200                        /* He got his 30 seconds, now we see him like
     201                           any other unchoked peer */
     202                        tr_peerSetOptimistic( peer, 0 );
     203                    }
     204                    else
     205                    {
     206                        /* Keep him unchoked for 30 seconds */
     207                        mustOptimistic = 0;
     208                        continue;
     209                    }
     210                }
     211
     212                unchoked++;
    121213                if( tr_peerLastChoke( peer ) + 10000 < now )
    122                     peersCanChoke[peersCanChokeCount++] = peer;
     214                    canChoke[canChokeCount++] = peer;
    123215            }
    124216            else
    125217            {
    126218                if( tr_peerLastChoke( peer ) + 10000 < now )
    127                     peersCanUnchoke[peersCanUnchokeCount++] = peer;
     219                    canUnchoke[canUnchokeCount++] = peer;
    128220            }
    129221        }
    130222    }
    131223
    132     sortPeers( peersCanChoke, peersCanChokeCount );
    133     sortPeers( peersCanUnchoke, peersCanUnchokeCount );
    134 
    135     if( unchokedCount > c->slots && peersCanChokeCount > 0 )
    136     {
    137         int willChoke;
    138         willChoke = MIN( peersCanChokeCount, unchokedCount - c->slots );
    139         for( i = 0; i < willChoke; i++ )
    140             tr_peerChoke( peersCanChoke[i] );
    141         peersCanChokeCount -= willChoke;
    142         memmove( &peersCanChoke[0], &peersCanChoke[willChoke],
    143                  peersCanChokeCount * sizeof( tr_peer_t * ) );
    144     }
    145     else if( unchokedCount < c->slots && peersCanUnchokeCount > 0 )
    146     {
    147         int willUnchoke;
    148         willUnchoke = MIN( peersCanUnchokeCount, c->slots - unchokedCount );
    149         for( i = 0; i < willUnchoke; i++ )
    150             tr_peerUnchoke( peersCanUnchoke[peersCanUnchokeCount - i - 1] );
    151         peersCanUnchokeCount -= willUnchoke;
    152     }
    153 
    154     while( peersCanChokeCount > 0 && peersCanUnchokeCount > 0 )
    155     {
    156         if( tr_peerDownloadRate( peersCanUnchoke[peersCanUnchokeCount - 1] )
    157                 < tr_peerDownloadRate( peersCanChoke[0] ) )
     224    canChokeZero      = malloc( canChokeCount * sizeof( tr_peer_t * ) );
     225    canChokeNonZero   = malloc( canChokeCount * sizeof( tr_peer_t * ) );
     226    canUnchokeZero    = malloc( canUnchokeCount * sizeof( tr_peer_t * ) );
     227    canUnchokeNonZero = malloc( canUnchokeCount * sizeof( tr_peer_t * ) );
     228
     229    sortPeersDescending( canChoke, canChokeCount,
     230                         canChokeZero, &canChokeZeroCount,
     231                         canChokeNonZero, &canChokeNonZeroCount);
     232    sortPeersAscending( canUnchoke, canUnchokeCount,
     233                        canUnchokeZero, &canUnchokeZeroCount,
     234                        canUnchokeNonZero, &canUnchokeNonZeroCount);
     235
     236    free( canChoke );
     237    free( canUnchoke );
     238
     239    if( mustOptimistic )
     240    {
     241        tr_peer_t * peer;
     242
     243        /* Open an extra slot for optimistic choking */
     244        if( canUnchokeZeroCount )
     245        {
     246            /* TODO: prefer peers with no pieces at all */
     247            peer = canUnchokeZero[--canUnchokeZeroCount];
     248            tr_peerUnchoke( peer );
     249            tr_peerSetOptimistic( peer, 1 );
     250        }
     251        else if( canUnchokeNonZeroCount )
     252        {
     253            peer = canUnchokeNonZero[--canUnchokeNonZeroCount];
     254            tr_peerUnchoke( peer );
     255            tr_peerSetOptimistic( peer, 1 );
     256        }
     257    }
     258
     259    /* If we have more open slots than what we should have (the user has
     260       just lowered his upload limit), we need to choke some of the
     261       peers we are uploading to. We start with the peers who aren't
     262       uploading to us, then those we upload the least. */
     263    while( unchoked > c->slots && canChokeZeroCount > 0 )
     264    {
     265        tr_peerChoke( canChokeZero[--canChokeZeroCount] );
     266        unchoked--;
     267    }
     268    while( unchoked > c->slots && canChokeNonZeroCount > 0 )
     269    {
     270        tr_peerChoke( canChokeNonZero[--canChokeNonZeroCount] );
     271        unchoked--;
     272    }
     273
     274    /* If we have unused open slots, let's unchoke some people. We start
     275       with the peers who are uploading to us the most. */
     276    while( unchoked < c->slots && canUnchokeNonZeroCount > 0 )
     277    {
     278        tr_peerUnchoke( canUnchokeNonZero[--canUnchokeNonZeroCount] );
     279        unchoked++;
     280    }
     281    while( unchoked < c->slots && canUnchokeZeroCount > 0 )
     282    {
     283        tr_peerUnchoke( canUnchokeZero[--canUnchokeZeroCount] );
     284        unchoked++;
     285    }
     286
     287    /* Choke peers who aren't uploading if there are good peers waiting
     288       for an unchoke */
     289    while( canChokeZeroCount > 0 && canUnchokeNonZeroCount > 0 )
     290    {
     291        tr_peerChoke( canChokeZero[--canChokeZeroCount] );
     292        tr_peerUnchoke( canUnchokeNonZero[--canUnchokeNonZeroCount] );
     293    }
     294
     295    /* Choke peers who aren't uploading that much if there are choked
     296       peers who are uploading more */
     297    while( canChokeNonZeroCount > 0 && canUnchokeNonZeroCount > 0 )
     298    {
     299        if( tr_peerDownloadRate( canUnchokeNonZero[canUnchokeNonZeroCount - 1] )
     300            < tr_peerDownloadRate( canChokeNonZero[canChokeNonZeroCount - 1] ) )
    158301            break;
    159302
    160         tr_peerChoke( peersCanChoke[0] );
    161         tr_peerUnchoke( peersCanUnchoke[peersCanUnchokeCount - 1] );
    162         peersCanChokeCount--;
    163         peersCanUnchokeCount--;
    164         memmove( &peersCanChoke[0], &peersCanChoke[1],
    165                  peersCanChokeCount * sizeof( tr_peer_t * ) );
    166     }
    167 
    168     free( peersCanChoke );
    169     free( peersCanUnchoke );
     303        tr_peerChoke( canChokeNonZero[--canChokeNonZeroCount] );
     304        tr_peerUnchoke( canUnchokeNonZero[--canUnchokeNonZeroCount] );
     305    }
     306
     307    /* Some unchoked peers still aren't uploading to us, let's give a
     308       chance to other non-uploaders */
     309    while( canChokeZeroCount > 0 && canUnchokeZeroCount > 0 )
     310    {
     311        tr_peerChoke( canChokeZero[--canChokeZeroCount] );
     312        tr_peerUnchoke( canUnchokeZero[--canUnchokeZeroCount] );
     313    }
     314
     315    free( canChokeZero );
     316    free( canChokeNonZero );
     317    free( canUnchokeZero );
     318    free( canUnchokeNonZero );
    170319
    171320    /* Unlock all torrents */
    172     for( i = 0; i < c->h->torrentCount; i++ )
    173     {
    174         tr_lockUnlock( &c->h->torrents[i]->lock );
     321    for( tor = c->h->torrentList; tor; tor = tor->next )
     322    {
     323        tr_lockUnlock( &tor->lock );
    175324    }
    176325
  • branches/simple_http_parsing/libtransmission/fastresume.h

    r20 r222  
    2424 * Fast resume
    2525 ***********************************************************************
    26  * Format of the resume file:
    27  *  - 4 bytes: format version (currently 0)
     26 * The format of the resume file is a 4 byte format version (currently 1),
     27 * followed by several variable-sized blocks of data.  Each block is
     28 * preceded by a 1 byte ID and a 4 byte length.  The currently recognized
     29 * IDs are defined below by the FR_ID_* macros.  The length does not include
     30 * the 5 bytes for the ID and length.
     31 *
     32 * The name of the resume file is "resume.<hash>".
     33 *
     34 * All values are stored in the native endianness. Moving a
     35 * libtransmission resume file from an architecture to another will not
     36 * work, although it will not hurt either (the version will be wrong,
     37 * so the resume file will not be read).
     38 **********************************************************************/
     39
     40/* progress data:
    2841 *  - 4 bytes * number of files: mtimes of files
    2942 *  - 1 bit * number of blocks: whether we have the block or not
    3043 *  - 4 bytes * number of pieces (byte aligned): the pieces that have
    3144 *    been completed or started in each slot
    32  *
    33  * The name of the resume file is "resume.<hash>".
    34  *
    35  * All values are stored in the native endianness. Moving a
    36  * libtransmission resume file from an architecture to another will not
    37  * work, although it will not hurt either (the mtimes will be wrong,
    38  * so the files will be scanned).
    39  **********************************************************************/
     45 */
     46#define FR_ID_PROGRESS          0x01
     47/* number of bytes downloaded */
     48#define FR_ID_DOWNLOADED        0x02
     49/* number of bytes uploaded */
     50#define FR_ID_UPLOADED          0x03
     51
     52/* macros for the length of various pieces of the progress data */
     53#define FR_MTIME_LEN( t ) \
     54  ( 4 * (t)->info.fileCount )
     55#define FR_BLOCK_BITFIELD_LEN( t ) \
     56  ( ( (t)->blockCount + 7 ) / 8 )
     57#define FR_SLOTPIECE_LEN( t ) \
     58  ( 4 * (t)->info.pieceCount )
     59#define FR_PROGRESS_LEN( t ) \
     60  ( FR_MTIME_LEN( t ) + FR_BLOCK_BITFIELD_LEN( t ) + FR_SLOTPIECE_LEN( t ) )
    4061
    4162static char * fastResumeFileName( tr_io_t * io )
     
    92113}
    93114
     115static inline void fastResumeWriteData( uint8_t id, void * data, uint32_t size,
     116                                        uint32_t count, FILE * file )
     117{
     118    uint32_t  datalen = size * count;
     119
     120    fwrite( &id, 1, 1, file );
     121    fwrite( &datalen, 4, 1, file );
     122    fwrite( data, size, count, file );
     123}
     124
    94125static void fastResumeSave( tr_io_t * io )
    95126{
    96127    tr_torrent_t * tor = io->tor;
    97     tr_info_t    * inf = &tor->info;
    98128   
    99129    FILE    * file;
    100     int       version = 0;
     130    int       version = 1;
    101131    char    * path;
    102     int     * fileMTimes;
    103     uint8_t * blockBitfield;
     132    uint8_t * buf;
     133
     134    buf = malloc( FR_PROGRESS_LEN( tor ) );
    104135
    105136    /* Get file sizes */
    106     fileMTimes = malloc( inf->fileCount * 4 );
    107     if( fastResumeMTimes( io, fileMTimes ) )
    108     {
    109         free( fileMTimes );
     137    if( fastResumeMTimes( io, (int*)buf ) )
     138    {
     139        free( buf );
    110140        return;
    111141    }
     
    116146    {
    117147        tr_err( "Could not open '%s' for writing", path );
    118         free( fileMTimes );
     148        free( buf );
    119149        free( path );
    120150        return;
     
    124154    fwrite( &version, 4, 1, file );
    125155
    126     /* Write file mtimes */
    127     fwrite( fileMTimes, 4, inf->fileCount, file );
    128     free( fileMTimes );
    129 
    130     /* Build and write the bitfield for blocks */
    131     blockBitfield = tr_cpBlockBitfield( tor->completion );
    132     fwrite( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
    133 
    134     /* Write the 'slotPiece' table */
    135     fwrite( io->slotPiece, 4, inf->pieceCount, file );
     156    /* Build and copy the bitfield for blocks */
     157    memcpy(buf + FR_MTIME_LEN( tor ), tr_cpBlockBitfield( tor->completion ),
     158           FR_BLOCK_BITFIELD_LEN( tor ) );
     159
     160    /* Copy the 'slotPiece' table */
     161    memcpy(buf + FR_MTIME_LEN( tor ) + FR_BLOCK_BITFIELD_LEN( tor ),
     162           io->slotPiece, FR_SLOTPIECE_LEN( tor ) );
     163
     164    /* Write progress data */
     165    fastResumeWriteData( FR_ID_PROGRESS, buf, 1, FR_PROGRESS_LEN( tor ), file );
     166    free( buf );
     167
     168    /* Write download and upload totals */
     169    fastResumeWriteData( FR_ID_DOWNLOADED, &tor->downloaded, 8, 1, file );
     170    fastResumeWriteData( FR_ID_UPLOADED, &tor->uploaded, 8, 1, file );
    136171
    137172    fclose( file );
     
    141176}
    142177
    143 static int fastResumeLoad( tr_io_t * io )
     178static int fastResumeLoadProgress( tr_io_t * io, FILE * file )
    144179{
    145180    tr_torrent_t * tor = io->tor;
    146181    tr_info_t    * inf = &tor->info;
    147    
    148     FILE    * file;
    149     int       version = 0;
    150     char    * path;
    151     int     * fileMTimes1, * fileMTimes2;
     182
     183    int     * fileMTimes;
    152184    int       i, j;
    153     uint8_t * blockBitfield;
    154 
    155     int size;
    156 
    157     /* Open resume file */
    158     path = fastResumeFileName( io );
    159     if( !( file = fopen( path, "r" ) ) )
    160     {
    161         tr_inf( "Could not open '%s' for reading", path );
    162         free( path );
    163         return 1;
    164     }
    165     tr_dbg( "Resume file '%s' loaded", path );
    166     free( path );
    167 
    168     /* Check the size */
    169     size = 4 + 4 * inf->fileCount + 4 * inf->pieceCount +
    170         ( tor->blockCount + 7 ) / 8;
    171     fseek( file, 0, SEEK_END );
    172     if( ftell( file ) != size )
    173     {
    174         tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
    175                 ftell( file ), size );
    176         fclose( file );
    177         return 1;
    178     }
    179     fseek( file, 0, SEEK_SET );
    180 
    181     /* Check format version */
    182     fread( &version, 4, 1, file );
    183     if( version != 0 )
    184     {
    185         tr_inf( "Resume file has version %d, not supported",
    186                 version );
    187         fclose( file );
     185    uint8_t * buf;
     186    size_t    len;
     187
     188    len = FR_PROGRESS_LEN( tor );
     189    buf = calloc( len, 1 );
     190    if( len != fread( buf, 1, len, file ) )
     191    {
     192        tr_inf( "Could not read from resume file" );
     193        free( buf );
    188194        return 1;
    189195    }
    190196
    191197    /* Compare file mtimes */
    192     fileMTimes1 = malloc( inf->fileCount * 4 );
    193     if( fastResumeMTimes( io, fileMTimes1 ) )
    194     {
    195         free( fileMTimes1 );
    196         fclose( file );
    197         return 1;
    198     }
    199     fileMTimes2 = malloc( inf->fileCount * 4 );
    200     fread( fileMTimes2, 4, inf->fileCount, file );
    201     if( memcmp( fileMTimes1, fileMTimes2, inf->fileCount * 4 ) )
     198    fileMTimes = malloc( FR_MTIME_LEN( tor ) );
     199    if( fastResumeMTimes( io, fileMTimes ) )
     200    {
     201        free( buf );
     202        free( fileMTimes );
     203        return 1;
     204    }
     205    if( memcmp( fileMTimes, buf, FR_MTIME_LEN( tor ) ) )
    202206    {
    203207        tr_inf( "File mtimes don't match" );
    204         free( fileMTimes1 );
    205         free( fileMTimes2 );
    206         fclose( file );
    207         return 1;
    208     }
    209     free( fileMTimes1 );
    210     free( fileMTimes2 );
    211 
    212     /* Load the bitfield for blocks and fill blockHave */
    213     blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
    214     fread( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
    215     tr_cpBlockBitfieldSet( tor->completion, blockBitfield );
    216     free( blockBitfield );
    217 
    218     /* Load the 'slotPiece' table */
    219     fread( io->slotPiece, 4, inf->pieceCount, file );
    220 
    221     fclose( file );
     208        free( buf );
     209        free( fileMTimes );
     210        return 1;
     211    }
     212    free( fileMTimes );
     213
     214    /* Copy the bitfield for blocks and fill blockHave */
     215    tr_cpBlockBitfieldSet( tor->completion, buf + FR_MTIME_LEN( tor ) );
     216
     217    /* Copy the 'slotPiece' table */
     218    memcpy( io->slotPiece, buf + FR_MTIME_LEN( tor ) +
     219            FR_BLOCK_BITFIELD_LEN( tor ), FR_SLOTPIECE_LEN( tor ) );
     220
     221    free( buf );
    222222
    223223    /* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
     
    239239    // tr_dbg( "Slot used: %d", io->slotsUsed );
    240240
    241     tr_inf( "Fast resuming successful" );
    242    
    243241    return 0;
    244242}
     243
     244static int fastResumeLoadOld( tr_io_t * io, FILE * file )
     245{
     246    tr_torrent_t * tor = io->tor;
     247   
     248    int size;
     249
     250    /* Check the size */
     251    size = 4 + FR_PROGRESS_LEN( tor );
     252    fseek( file, 0, SEEK_END );
     253    if( ftell( file ) != size )
     254    {
     255        tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
     256                ftell( file ), size );
     257        fclose( file );
     258        return 1;
     259    }
     260
     261    /* load progress information */
     262    fseek( file, 4, SEEK_SET );
     263    if( fastResumeLoadProgress( io, file ) )
     264    {
     265        fclose( file );
     266        return 1;
     267    }
     268
     269    fclose( file );
     270
     271    tr_inf( "Fast resuming successful (version 0)" );
     272   
     273    return 0;
     274}
     275
     276static int fastResumeLoad( tr_io_t * io )
     277{
     278    tr_torrent_t * tor = io->tor;
     279   
     280    FILE    * file;
     281    int       version = 0;
     282    char    * path;
     283    uint8_t   id;
     284    uint32_t  len;
     285    int       ret;
     286
     287    /* Open resume file */
     288    path = fastResumeFileName( io );
     289    if( !( file = fopen( path, "r" ) ) )
     290    {
     291        tr_inf( "Could not open '%s' for reading", path );
     292        free( path );
     293        return 1;
     294    }
     295    tr_dbg( "Resume file '%s' loaded", path );
     296    free( path );
     297
     298    /* Check format version */
     299    fread( &version, 4, 1, file );
     300    if( 0 == version )
     301    {
     302        return fastResumeLoadOld( io, file );
     303    }
     304    if( 1 != version )
     305    {
     306        tr_inf( "Resume file has version %d, not supported", version );
     307        fclose( file );
     308        return 1;
     309    }
     310
     311    ret = 1;
     312    /* read each block of data */
     313    while( 1 == fread( &id, 1, 1, file ) && 1 == fread( &len, 4, 1, file ) )
     314    {
     315        switch( id )
     316        {
     317            case FR_ID_PROGRESS:
     318                /* read progress data */
     319                if( (uint32_t)FR_PROGRESS_LEN( tor ) == len )
     320                {
     321                    if( fastResumeLoadProgress( io, file ) )
     322                    {
     323                        fclose( file );
     324                        return 1;
     325                    }
     326                    ret = 0;
     327                    continue;
     328                }
     329                break;
     330
     331            case FR_ID_DOWNLOADED:
     332                /* read download total */
     333                if( 8 == len)
     334                {
     335                    if( 1 != fread( &tor->downloaded, 8, 1, file ) )
     336                    {
     337                      fclose( file );
     338                      return 1;
     339                    }
     340                    continue;
     341                }
     342                break;
     343
     344            case FR_ID_UPLOADED:
     345                /* read upload total */
     346                if( 8 == len)
     347                {
     348                    if( 1 != fread( &tor->uploaded, 8, 1, file ) )
     349                    {
     350                      fclose( file );
     351                      return 1;
     352                    }
     353                    continue;
     354                }
     355                break;
     356
     357            default:
     358                break;
     359        }
     360
     361        /* if we didn't read the data, seek past it */
     362        tr_inf( "Skipping resume data type %02x, %u bytes", id, len );
     363        fseek( file, len, SEEK_CUR );
     364    }
     365
     366    fclose( file );
     367
     368    if( !ret )
     369    {
     370        tr_inf( "Fast resuming successful" );
     371    }
     372   
     373    return ret;
     374}
  • branches/simple_http_parsing/libtransmission/inout.c

    r54 r222  
    184184}
    185185
     186void tr_ioSaveResume( tr_io_t * io )
     187{
     188    fastResumeSave( io );
     189}
     190
    186191/***********************************************************************
    187192 * createFiles
  • branches/simple_http_parsing/libtransmission/inout.h

    r1 r222  
    3030int       tr_ioWrite       ( tr_io_t *, int, int, int, uint8_t * );
    3131void      tr_ioClose       ( tr_io_t * );
     32void      tr_ioSaveResume  ( tr_io_t * );
    3233
    3334#endif
  • branches/simple_http_parsing/libtransmission/internal.h

    r65 r222  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    8383#define TR_MAX_PEER_COUNT 60
    8484
    85 typedef struct tr_torrent_s tr_torrent_t;
    8685typedef struct tr_completion_s tr_completion_t;
    8786
     
    109108
    110109    int               status;
    111         int                               finished;
    112     char              error[128];
     110    int               error;
     111    char              trackerError[128];
     112    int               finished;
    113113
    114114    char            * id;
    115115    char            * key;
     116    int             * bindPort;
    116117
    117118    /* An escaped string used to include the hash in HTTP queries */
     
    127128    int               blockCount;
    128129   
    129 #if 0
    130     /* Status for each block
    131        -1 = we have it
    132         n = we are downloading it from n peers */
    133     char            * blockHave;
    134     int               blockHaveCount;
    135     uint8_t         * bitfield;
    136 #endif
    137130    tr_completion_t * completion;
    138131
     
    151144    uint64_t          downloaded;
    152145    uint64_t          uploaded;
     146
     147    tr_stat_t         stats[2];
     148    int               statCur;
     149
     150    tr_torrent_t    * prev;
     151    tr_torrent_t    * next;
    153152};
    154153
     
    159158{
    160159    int            torrentCount;
    161     tr_torrent_t * torrents[TR_MAX_TORRENT_COUNT];
     160    tr_torrent_t * torrentList;
    162161
    163162    tr_ratecontrol_t * upload;
  • branches/simple_http_parsing/libtransmission/net.c

    r26 r222  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    5959}
    6060
     61struct tr_resolve_s
     62{
     63    int            status;
     64    char           * address;
     65    struct in_addr addr;
     66
     67    tr_lock_t      lock;
     68    tr_thread_t    thread;
     69    int            orphan;
     70};
     71
     72/* Hem, global variable. Initialized from tr_init(). */
     73tr_lock_t gethostbynameLock;
     74
     75static void resolveFunc( void * _r )
     76{
     77    tr_resolve_t * r = _r;
     78    struct hostent * host;
     79
     80    tr_lockLock( &r->lock );
     81
     82    r->addr.s_addr = inet_addr( r->address );
     83    if( r->addr.s_addr != 0xFFFFFFFF )
     84    {
     85        /* This was an IP address, no resolving required */
     86        r->status = TR_RESOLVE_OK;
     87        goto resolveDone;
     88    }
     89
     90    tr_lockLock( &gethostbynameLock );
     91    tr_lockUnlock( &r->lock );
     92    host = gethostbyname( r->address );
     93    tr_lockLock( &r->lock );
     94    if( host )
     95    {
     96        memcpy( &r->addr, host->h_addr, host->h_length );
     97        r->status = TR_RESOLVE_OK;
     98    }
     99    else
     100    {
     101        r->status = TR_RESOLVE_ERROR;
     102    }
     103    tr_lockUnlock( &gethostbynameLock );
     104
     105resolveDone:
     106    if( r->orphan )
     107    {
     108        /* tr_netResolveClose was closed already. Free memory */
     109        tr_lockUnlock( &r->lock );
     110        tr_lockClose( &r->lock );
     111        free( r );
     112    }
     113    else
     114    {
     115        tr_lockUnlock( &r->lock );
     116    }
     117}
     118
     119tr_resolve_t * tr_netResolveInit( char * address )
     120{
     121    tr_resolve_t * r = malloc( sizeof( tr_resolve_t ) );
     122
     123    r->status  = TR_RESOLVE_WAIT;
     124    r->address = address;
     125
     126    tr_lockInit( &r->lock );
     127    tr_threadCreate( &r->thread, resolveFunc, r );
     128    r->orphan = 0;
     129
     130    return r;
     131}
     132
     133int tr_netResolvePulse( tr_resolve_t * r, struct in_addr * addr )
     134{
     135    int ret;
     136
     137    tr_lockLock( &r->lock );
     138    ret = r->status;
     139    if( ret == TR_RESOLVE_OK )
     140    {
     141        *addr = r->addr;
     142    }
     143    tr_lockUnlock( &r->lock );
     144
     145    return ret;
     146}
     147
     148void tr_netResolveClose( tr_resolve_t * r )
     149{
     150    tr_lockLock( &r->lock );
     151    if( r->status == TR_RESOLVE_WAIT )
     152    {
     153        /* Let the thread die */
     154        r->orphan = 1;
     155        tr_lockUnlock( &r->lock );
     156        return;
     157    }
     158    tr_lockUnlock( &r->lock );
     159
     160    /* Clean up */
     161    tr_threadJoin( &r->thread );
     162    tr_lockClose( &r->lock );
     163    free( r );
     164}
     165
     166/* Blocking version */
    61167int tr_netResolve( char * address, struct in_addr * addr )
    62168{
    63     struct hostent * host;
    64 
    65     addr->s_addr = inet_addr( address );
    66     if( addr->s_addr != 0xFFFFFFFF )
    67     {
    68         return 0;
    69     }
    70 
    71     if( !( host = gethostbyname( address ) ) )
    72     {
    73         tr_err( "Could not resolve (%s)", address );
    74         return -1;
    75     }
    76     memcpy( addr, host->h_addr, host->h_length );
    77 
    78     return 0;
     169    tr_resolve_t * r = tr_netResolveInit( address );
     170    int ret;
     171
     172    for( ;; )
     173    {
     174        ret = tr_netResolvePulse( r, addr );
     175        if( ret != TR_RESOLVE_WAIT )
     176        {
     177            break;
     178        }
     179        tr_wait( 20 );
     180    }
     181
     182    tr_netResolveClose( r );
     183    return ( ret != TR_RESOLVE_OK );
    79184}
    80185
  • branches/simple_http_parsing/libtransmission/net.h

    r26 r222  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    2121 *****************************************************************************/
    2222
     23#define TR_RESOLVE_WAIT  0
     24#define TR_RESOLVE_ERROR 1
     25#define TR_RESOLVE_OK    2
     26typedef struct tr_resolve_s tr_resolve_t;
     27tr_resolve_t * tr_netResolveInit( char * );
     28int            tr_netResolvePulse( tr_resolve_t *, struct in_addr * );
     29void           tr_netResolveClose( tr_resolve_t * );
     30
    2331int  tr_netResolve ( char *, struct in_addr * );
    2432int  tr_netOpen    ( struct in_addr addr, in_port_t port );
  • branches/simple_http_parsing/libtransmission/peer.c

    r65 r222  
    5454    char           peerInterested;
    5555
     56    int            optimistic;
    5657    uint64_t       lastChoke;
    5758
     
    219220    for( ;; )
    220221    {
     222        if( tor && !tr_rcCanTransfer( tor->globalDownload ) )
     223        {
     224            break;
     225        }
     226
    221227        if( peer->size < 1 )
    222228        {
     
    229235            peer->buf   = realloc( peer->buf, peer->size );
    230236        }
     237        /* Never read more than 1K each time, otherwise the rate
     238           control is no use */
    231239        ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
    232                           peer->size - peer->pos );
     240                          MIN( 1024, peer->size - peer->pos ) );
    233241        if( ret & TR_NET_CLOSE )
    234242        {
     
    262270
    263271    return 0;
     272}
     273
     274uint64_t tr_peerDate( tr_peer_t * peer )
     275{
     276    return peer->date;
    264277}
    265278
     
    490503    return peer->lastChoke;
    491504}
     505
     506void tr_peerSetOptimistic( tr_peer_t * peer, int o )
     507{
     508    peer->optimistic = o;
     509}
     510
     511int tr_peerIsOptimistic( tr_peer_t * peer )
     512{
     513    return peer->optimistic;
     514}
  • branches/simple_http_parsing/libtransmission/peer.h

    r65 r222  
    3333void        tr_peerRem           ( tr_torrent_t *, int );
    3434int         tr_peerRead          ( tr_torrent_t *, tr_peer_t * );
     35uint64_t    tr_peerDate          ( tr_peer_t * );
    3536uint8_t *   tr_peerHash          ( tr_peer_t * );
    3637void        tr_peerPulse         ( tr_torrent_t * );
     
    4546void        tr_peerUnchoke       ( tr_peer_t * );
    4647uint64_t    tr_peerLastChoke     ( tr_peer_t * );
     48void        tr_peerSetOptimistic ( tr_peer_t *, int );
     49int         tr_peerIsOptimistic  ( tr_peer_t * );
    4750
    4851#endif
  • branches/simple_http_parsing/libtransmission/ratecontrol.c

    r80 r222  
    101101
    102102    tr_lockLock( &r->lock );
    103     ret = ( r->limit < -1 ) || ( rateForInterval( r, 1000 ) < r->limit );
     103    ret = ( r->limit < 0 ) || ( rateForInterval( r, 1000 ) < r->limit );
    104104    tr_lockUnlock( &r->lock );
    105105
     
    145145}
    146146
    147 void tr_rcClose( tr_ratecontrol_t * r )
     147void tr_rcReset( tr_ratecontrol_t * r )
    148148{
    149149    tr_transfer_t * t, * next;
     150
     151    tr_lockLock( &r->lock );
    150152    for( t = r->first; t; )
    151153    {
     
    154156        t = next;
    155157    }
     158    r->first = NULL;
     159    r->last  = NULL;
     160    tr_lockUnlock( &r->lock );
     161}
     162
     163void tr_rcClose( tr_ratecontrol_t * r )
     164{
     165    tr_rcReset( r );
    156166    tr_lockClose( &r->lock );
    157167    free( r );
  • branches/simple_http_parsing/libtransmission/ratecontrol.h

    r63 r222  
    2828void               tr_rcTransferred( tr_ratecontrol_t *, int );
    2929float              tr_rcRate( tr_ratecontrol_t * );
     30void               tr_rcReset( tr_ratecontrol_t * );
    3031void               tr_rcClose( tr_ratecontrol_t * );
  • branches/simple_http_parsing/libtransmission/tracker.c

    r84 r222  
    4242
    4343#define TC_STATUS_IDLE    1
    44 #define TC_STATUS_CONNECT 2
    45 #define TC_STATUS_RECV    4
     44#define TC_STATUS_RESOLVE 2
     45#define TC_STATUS_CONNECT 4
     46#define TC_STATUS_RECV    8
    4647    char           status;
    4748
     49    tr_resolve_t * resolve;
    4850    int            socket;
    4951    uint8_t      * buf;
     
    5355    int            bindPort;
    5456    int            newPort;
     57
     58    uint64_t       download;
     59    uint64_t       upload;
    5560};
    5661
     
    5863static void recvAnswer ( tr_tracker_t * tc );
    5964
    60 tr_tracker_t * tr_trackerInit( tr_handle_t * h, tr_torrent_t * tor )
     65tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
    6166{
    6267    tr_tracker_t * tc;
     
    6469    tc           = calloc( 1, sizeof( tr_tracker_t ) );
    6570    tc->tor      = tor;
    66     tc->id       = h->id;
     71    tc->id       = tor->id;
    6772
    6873    tc->started  = 1;
     
    7580    tc->buf      = malloc( tc->size );
    7681
    77     tc->bindPort = h->bindPort;
     82    tc->bindPort = *(tor->bindPort);
    7883    tc->newPort  = -1;
     84
     85    tc->download = tor->downloaded;
     86    tc->upload   = tor->uploaded;
    7987
    8088    return tc;
     
    137145    if( ( tc->status & TC_STATUS_IDLE ) && shouldConnect( tc ) )
    138146    {
    139         struct in_addr addr;
    140 
    141         if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
    142         {
    143             return 0;
    144         }
    145 
    146         if( tr_netResolve( inf->trackerAddress, &addr ) )
    147         {
    148             tr_fdSocketClosed( tor->fdlimit, 1 );
    149             return 0;
    150         }
    151 
    152         tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) );
    153         if( tc->socket < 0 )
    154         {
    155             tr_fdSocketClosed( tor->fdlimit, 1 );
    156             return 0;
    157         }
     147        tc->resolve = tr_netResolveInit( inf->trackerAddress );
    158148
    159149        tr_inf( "Tracker: connecting to %s:%d (%s)",
     
    165155                      "getting peers" ) ) ) );
    166156
    167         tc->status  = TC_STATUS_CONNECT;
     157        tc->status  = TC_STATUS_RESOLVE;
    168158        tc->dateTry = tr_date();
     159    }
     160
     161    if( tc->status & TC_STATUS_RESOLVE )
     162    {
     163        int ret;
     164        struct in_addr addr;
     165
     166        ret = tr_netResolvePulse( tc->resolve, &addr );
     167        if( ret == TR_RESOLVE_WAIT )
     168        {
     169            return 0;
     170        }
     171        else
     172        {
     173            tr_netResolveClose( tc->resolve );
     174        }
     175       
     176        if( ret == TR_RESOLVE_ERROR )
     177        {
     178            tc->status = TC_STATUS_IDLE;
     179            return 0;
     180        }
     181
     182        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
     183        {
     184            tc->status = TC_STATUS_IDLE;
     185            return 0;
     186        }
     187
     188        tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) );
     189        if( tc->socket < 0 )
     190        {
     191            tr_fdSocketClosed( tor->fdlimit, 1 );
     192            tc->status = TC_STATUS_IDLE;
     193            return 0;
     194        }
     195
     196        tc->status = TC_STATUS_CONNECT;
    169197    }
    170198
     
    231259    tr_torrent_t * tor = tc->tor;
    232260
    233     if( tc->status > TC_STATUS_IDLE )
     261    if( tc->status == TC_STATUS_RESOLVE )
     262    {
     263        tr_netResolveClose( tc->resolve );
     264    }
     265    else if( tc->status > TC_STATUS_RESOLVE )
    234266    {
    235267        tr_netClose( tc->socket );
     
    248280    uint64_t   left;
    249281    int        ret;
    250 
    251     if( tc->started && 0 < tc->newPort )
    252     {
    253         tc->bindPort = tc->newPort;
    254         tc->newPort = -1;
    255     }
    256 
     282    uint64_t   down;
     283    uint64_t   up;
     284
     285    down = tor->downloaded - tc->download;
     286    up = tor->uploaded - tc->upload;
    257287    if( tc->started )
     288    {
    258289        event = "&event=started";
     290        down = up = 0;
     291       
     292        if( 0 < tc->newPort )
     293        {
     294            tc->bindPort = tc->newPort;
     295            tc->newPort = -1;
     296        }
     297    }
    259298    else if( tc->completed )
     299    {
    260300        event = "&event=completed";
     301    }
    261302    else if( tc->stopped || 0 < tc->newPort )
     303    {
    262304        event = "&event=stopped";
     305    }
    263306    else
     307    {
    264308        event = "";
     309    }
    265310
    266311    left = tr_cpLeftBytes( tor->completion );
     
    283328            "Connection: close\r\n\r\n",
    284329            inf->trackerAnnounce, tor->hashString, tc->id,
    285             tc->bindPort, tor->uploaded, tor->downloaded,
     330            tc->bindPort, up, down,
    286331            left, tor->key, event, inf->trackerAddress,
    287332            VERSION_MAJOR, VERSION_MINOR );
     
    410455    {
    411456        tr_err( "Tracker: %s", bePeers->val.s.s );
    412         tor->status |= TR_TRACKER_ERROR;
    413         snprintf( tor->error, sizeof( tor->error ),
     457        tor->error |= TR_ETRACKER;
     458        snprintf( tor->trackerError, sizeof( tor->trackerError ),
    414459                  "%s", bePeers->val.s.s );
    415460        goto cleanup;
    416461    }
    417 
    418     tor->status &= ~TR_TRACKER_ERROR;
     462    tor->error &= ~TR_ETRACKER;
    419463
    420464    if( !tc->interval )
     
    666710}
    667711
    668 int tr_trackerSeeders( tr_torrent_t * tor)
    669 {
    670         if (tor->status != TR_STATUS_PAUSE)
    671         {
    672                 return (tor->tracker)->seeders;
    673         }
    674         return 0;
    675 }
    676 
    677 int tr_trackerLeechers( tr_torrent_t * tor)
    678 {
    679         if (tor->status != TR_STATUS_PAUSE)
    680         {
    681                 return (tor->tracker)->leechers;
    682         }
    683         return 0;
    684 }
     712int tr_trackerSeeders( tr_tracker_t * tc )
     713{
     714    if( !tc )
     715    {
     716        return -1;
     717    }
     718    return tc->seeders;
     719}
     720
     721int tr_trackerLeechers( tr_tracker_t * tc )
     722{
     723    if( !tc )
     724    {
     725        return -1;
     726    }
     727    return tc->leechers;
     728}
  • branches/simple_http_parsing/libtransmission/tracker.h

    r26 r222  
    2626typedef struct tr_tracker_s tr_tracker_t;
    2727
    28 tr_tracker_t * tr_trackerInit      ( tr_handle_t *, tr_torrent_t * );
     28tr_tracker_t * tr_trackerInit      ( tr_torrent_t * );
    2929void           tr_trackerChangePort( tr_tracker_t *, int );
    3030int            tr_trackerPulse     ( tr_tracker_t * );
     
    3838 * Looks for the seeders/leechers as returned by the tracker.
    3939 **********************************************************************/
    40 int                         tr_trackerSeeders  ( tr_torrent_t * );
     40int                         tr_trackerSeeders  ( tr_tracker_t * );
    4141
    4242/***********************************************************************
     
    4545 * Looks for the seeders/leechers as returned by the tracker.
    4646 **********************************************************************/
    47 int                         tr_trackerLeechers ( tr_torrent_t * );
     47int                         tr_trackerLeechers ( tr_tracker_t * );
    4848
    4949int            tr_trackerScrape    ( tr_torrent_t *, int *, int * );
  • branches/simple_http_parsing/libtransmission/transmission.c

    r67 r222  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    2626 * Local prototypes
    2727 **********************************************************************/
    28 static void torrentReallyStop( tr_handle_t * h, int t );
     28static void torrentReallyStop( tr_torrent_t * );
    2929static void  downloadLoop( void * );
    3030static void  acceptLoop( void * );
    3131static void acceptStop( tr_handle_t * h );
    3232
     33/* Used in tr_netResolve */
     34extern tr_lock_t gethostbynameLock;
     35
    3336/***********************************************************************
    3437 * tr_init
     
    4043    tr_handle_t * h;
    4144    int           i, r;
     45
     46    tr_lockInit( &gethostbynameLock );
    4247
    4348    h = calloc( sizeof( tr_handle_t ), 1 );
     
    6974    h->choking  = tr_chokingInit( h );
    7075
    71     h->bindPort = TR_DEFAULT_PORT;
     76    h->bindPort = -1;
    7277    h->bindSocket = -1;
     78
     79    h->acceptDie = 0;
     80    tr_lockInit( &h->acceptLock );
     81    tr_threadCreate( &h->acceptThread, acceptLoop, h );
     82
     83    return h;
     84}
     85
     86/***********************************************************************
     87 * tr_setBindPort
     88 ***********************************************************************
     89 *
     90 **********************************************************************/
     91void tr_setBindPort( tr_handle_t * h, int port )
     92{
     93    int sock = -1;
     94    tr_torrent_t * tor;
     95
     96    if( h->bindPort == port )
     97      return;
    7398
    7499#ifndef BEOS_NETSERVER
     
    78103    {
    79104        /* XXX should handle failure here in a better way */
    80         h->bindSocket = tr_netBind( h->bindPort );
    81     }
    82 #endif
    83 
    84 
    85     h->acceptDie = 0;
    86     tr_lockInit( &h->acceptLock );
    87     tr_threadCreate( &h->acceptThread, acceptLoop, h );
    88 
    89     return h;
    90 }
    91 
    92 /***********************************************************************
    93  * tr_setBindPort
    94  ***********************************************************************
    95  *
    96  **********************************************************************/
    97 void tr_setBindPort( tr_handle_t * h, int port )
    98 {
    99     int ii, sock;
    100 
    101     if( h->bindPort == port )
    102       return;
    103 
    104 #ifndef BEOS_NETSERVER
    105     /* BeOS net_server seems to be unable to set incoming connections to
    106        non-blocking. Too bad. */
    107     if( !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
    108     {
    109         /* XXX should handle failure here in a better way */
    110105        sock = tr_netBind( port );
    111106    }
     
    118113    h->bindPort = port;
    119114
    120     for( ii = 0; ii < h->torrentCount; ii++ )
    121     {
    122         tr_lockLock( &h->torrents[ii]->lock );
    123         if( NULL != h->torrents[ii]->tracker )
    124         {
    125             tr_trackerChangePort( h->torrents[ii]->tracker, port );
    126         }
    127         tr_lockUnlock( &h->torrents[ii]->lock );
     115    for( tor = h->torrentList; tor; tor = tor->next )
     116    {
     117        tr_lockLock( &tor->lock );
     118        if( NULL != tor->tracker )
     119        {
     120            tr_trackerChangePort( tor->tracker, port );
     121        }
     122        tr_lockUnlock( &tor->lock );
    128123    }
    129124
     
    151146
    152147/***********************************************************************
     148 * tr_setDownloadLimit
     149 ***********************************************************************
     150 *
     151 **********************************************************************/
     152void tr_setDownloadLimit( tr_handle_t * h, int limit )
     153{
     154    tr_rcSetLimit( h->download, limit );
     155}
     156
     157/***********************************************************************
    153158 * tr_torrentRates
    154159 ***********************************************************************
     
    157162void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
    158163{
    159     *dl = tr_rcRate( h->download );
    160     *ul = tr_rcRate( h->upload );
     164    tr_torrent_t * tor;
     165
     166    *dl = 0.0;
     167    *ul = 0.0;
     168    for( tor = h->torrentList; tor; tor = tor->next )
     169    {
     170        tr_lockLock( &tor->lock );
     171        if( tor->status & TR_STATUS_DOWNLOAD )
     172            *dl += tr_rcRate( tor->download );
     173        *ul += tr_rcRate( tor->upload );
     174        tr_lockUnlock( &tor->lock );
     175    }
    161176}
    162177
     
    167182 * to fill it.
    168183 **********************************************************************/
    169 int tr_torrentInit( tr_handle_t * h, const char * path )
    170 {
    171     tr_torrent_t  * tor;
     184tr_torrent_t * tr_torrentInit( tr_handle_t * h, const char * path,
     185                               int * error )
     186{
     187    tr_torrent_t  * tor, * tor_tmp;
    172188    tr_info_t     * inf;
    173189    int             i;
    174190    char          * s1, * s2;
    175191
    176     if( h->torrentCount >= TR_MAX_TORRENT_COUNT )
    177     {
    178         tr_err( "Maximum number of torrents reached" );
    179         return 1;
    180     }
    181 
    182192    tor = calloc( sizeof( tr_torrent_t ), 1 );
    183193    inf = &tor->info;
     
    186196    if( tr_metainfoParse( inf, path ) )
    187197    {
     198        *error = TR_EINVALID;
    188199        free( tor );
    189         return 1;
     200        return NULL;
    190201    }
    191202
    192203    /* Make sure this torrent is not already open */
    193     for( i = 0; i < h->torrentCount; i++ )
    194     {
    195         if( !memcmp( tor->info.hash, h->torrents[i]->info.hash,
     204    for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
     205    {
     206        if( !memcmp( tor->info.hash, tor_tmp->info.hash,
    196207                     SHA_DIGEST_LENGTH ) )
    197208        {
    198             tr_err( "Torrent already open" );
     209            *error = TR_EDUPLICATE;
    199210            free( tor );
    200             return 1;
     211            return NULL;
    201212        }
    202213    }
     
    205216    tor->id     = h->id;
    206217    tor->key    = h->key;
     218    tor->bindPort = &h->bindPort;
    207219        tor->finished = 0;
    208220
     
    246258    /* We have a new torrent */
    247259    tr_lockLock( &h->acceptLock );
    248     h->torrents[h->torrentCount] = tor;
     260    tor->prev = NULL;
     261    tor->next = h->torrentList;
     262    if( tor->next )
     263    {
     264        tor->next->prev = tor;
     265    }
     266    h->torrentList = tor;
    249267    (h->torrentCount)++;
    250268    tr_lockUnlock( &h->acceptLock );
    251269
    252     return 0;
     270    if( 0 > h->bindPort )
     271    {
     272        tr_setBindPort( h, TR_DEFAULT_PORT );
     273    }
     274
     275    return tor;
     276}
     277
     278tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
     279{
     280    return &tor->info;
    253281}
    254282
    255283/***********************************************************************
    256284 * tr_torrentScrape
    257  ***********************************************************************
    258  * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
    259  * to fill it.
    260  **********************************************************************/
    261 int tr_torrentScrape( tr_handle_t * h, int t, int * s, int * l )
    262 {
    263     return tr_trackerScrape( h->torrents[t], s, l );
    264 }
    265 
    266 void tr_torrentSetFolder( tr_handle_t * h, int t, const char * path )
    267 {
    268     tr_torrent_t * tor = h->torrents[t];
    269 
     285 **********************************************************************/
     286int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l )
     287{
     288    return tr_trackerScrape( tor, s, l );
     289}
     290
     291void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
     292{
    270293    tor->destination = strdup( path );
    271294}
    272295
    273 char * tr_torrentGetFolder( tr_handle_t * h, int t )
    274 {
    275     tr_torrent_t * tor = h->torrents[t];
    276 
     296char * tr_torrentGetFolder( tr_torrent_t * tor )
     297{
    277298    return tor->destination;
    278299}
    279300
    280 void tr_torrentStart( tr_handle_t * h, int t )
    281 {
    282     tr_torrent_t * tor = h->torrents[t];
    283 
     301void tr_torrentStart( tr_torrent_t * tor )
     302{
    284303    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
    285304    {
    286305        /* Join the thread first */
    287         torrentReallyStop( h, t );
    288     }
    289 
    290     tor->status   = TR_STATUS_CHECK;
    291     tor->tracker  = tr_trackerInit( h, tor );
     306        torrentReallyStop( tor );
     307    }
     308
     309    tor->status  = TR_STATUS_CHECK;
    292310
    293311    tor->date = tr_date();
     
    296314}
    297315
    298 void tr_torrentStop( tr_handle_t * h, int t )
    299 {
    300     tr_torrent_t * tor = h->torrents[t];
    301 
     316void tr_torrentStop( tr_torrent_t * tor )
     317{
    302318    tr_lockLock( &tor->lock );
    303319    tr_trackerStopped( tor->tracker );
     320    tr_rcReset( tor->download );
     321    tr_rcReset( tor->upload );
    304322    tor->status = TR_STATUS_STOPPING;
    305323    tor->stopDate = tr_date();
     
    312330 * Joins the download thread and frees/closes everything related to it.
    313331 **********************************************************************/
    314 static void torrentReallyStop( tr_handle_t * h, int t )
    315 {
    316     tr_torrent_t * tor = h->torrents[t];
    317 
     332static void torrentReallyStop( tr_torrent_t * tor )
     333{
    318334    tor->die = 1;
    319335    tr_threadJoin( &tor->thread );
     
    321337
    322338    tr_trackerClose( tor->tracker );
     339    tor->tracker = NULL;
    323340
    324341    while( tor->peerCount > 0 )
     
    326343        tr_peerRem( tor, 0 );
    327344    }
    328 
    329     tor->downloaded = 0;
    330     tor->uploaded   = 0;
    331345}
    332346
     
    341355}
    342356
    343 int tr_getFinished( tr_handle_t * h, int i)
    344 {
    345         return h->torrents[i]->finished;
    346 }
    347 void tr_setFinished( tr_handle_t * h, int i, int val)
    348 {
    349         h->torrents[i]->finished = val;
    350 }
    351 
    352 int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
     357void tr_torrentIterate( tr_handle_t * h, tr_callback_t func, void * d )
     358{
     359    tr_torrent_t * tor;
     360
     361    for( tor = h->torrentList; tor; tor = tor->next )
     362    {
     363        func( tor, d );
     364    }
     365}
     366
     367int tr_getFinished( tr_torrent_t * tor )
     368{
     369    if( tor->finished )
     370    {
     371        tor->finished = 0;
     372        return 1;
     373    }
     374    return 0;
     375}
     376
     377tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
    353378{
    354379    tr_stat_t * s;
    355     tr_torrent_t * tor;
    356     tr_info_t * inf;
    357     int i, j, k, piece;
    358 
    359     if( h->torrentCount < 1 )
    360     {
    361         *stat = NULL;
    362         return 0;
    363     }
    364 
    365     s = malloc( h->torrentCount * sizeof( tr_stat_t ) );
    366 
    367     for( i = 0; i < h->torrentCount; i++ )
    368     {
    369         tor = h->torrents[i];
    370         inf = &tor->info;
    371 
    372         if( ( tor->status & TR_STATUS_STOPPED ) ||
    373             ( ( tor->status & TR_STATUS_STOPPING ) &&
    374               tr_date() > tor->stopDate + 60000 ) )
    375         {
    376             torrentReallyStop( h, i );
    377             tor->status = TR_STATUS_PAUSE;
    378         }
    379 
    380         tr_lockLock( &tor->lock );
    381 
    382         memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
    383         s[i].status = tor->status;
    384         memcpy( s[i].error, tor->error, sizeof( s[i].error ) );
    385 
    386         s[i].peersTotal       = 0;
    387         s[i].peersUploading   = 0;
    388         s[i].peersDownloading = 0;
    389 
     380    tr_info_t * inf = &tor->info;
     381    int i;
     382
     383    tor->statCur = ( tor->statCur + 1 ) % 2;
     384    s = &tor->stats[tor->statCur];
     385
     386    if( ( tor->status & TR_STATUS_STOPPED ) ||
     387        ( ( tor->status & TR_STATUS_STOPPING ) &&
     388          tr_date() > tor->stopDate + 60000 ) )
     389    {
     390        torrentReallyStop( tor );
     391        tor->status = TR_STATUS_PAUSE;
     392    }
     393
     394    tr_lockLock( &tor->lock );
     395
     396    s->status = tor->status;
     397    s->error  = tor->error;
     398    memcpy( s->trackerError, tor->trackerError,
     399            sizeof( s->trackerError ) );
     400
     401    s->peersTotal       = 0;
     402    s->peersUploading   = 0;
     403    s->peersDownloading = 0;
     404
     405    for( i = 0; i < tor->peerCount; i++ )
     406    {
     407        if( tr_peerIsConnected( tor->peers[i] ) )
     408        {
     409            (s->peersTotal)++;
     410            if( tr_peerIsUploading( tor->peers[i] ) )
     411            {
     412                (s->peersUploading)++;
     413            }
     414            if( tr_peerIsDownloading( tor->peers[i] ) )
     415            {
     416                (s->peersDownloading)++;
     417            }
     418        }
     419    }
     420
     421    s->progress = tr_cpCompletionAsFloat( tor->completion );
     422    if( tor->status & TR_STATUS_DOWNLOAD )
     423        s->rateDownload = tr_rcRate( tor->download );
     424    else
     425        /* tr_rcRate() doesn't make the difference between 'piece'
     426           messages and other messages, which causes a non-zero
     427           download rate even tough we are not downloading. So we
     428           force it to zero not to confuse the user. */
     429        s->rateDownload = 0.0;
     430    s->rateUpload = tr_rcRate( tor->upload );
     431   
     432    s->seeders  = tr_trackerSeeders(tor->tracker);
     433        s->leechers = tr_trackerLeechers(tor->tracker);
     434
     435    if( s->rateDownload < 0.1 )
     436    {
     437        s->eta = -1;
     438    }
     439    else
     440    {
     441        s->eta = (float) ( 1.0 - s->progress ) *
     442            (float) inf->totalSize / s->rateDownload / 1024.0;
     443        if( s->eta > 99 * 3600 + 59 * 60 + 59 )
     444        {
     445            s->eta = -1;
     446        }
     447    }
     448
     449    s->downloaded = tor->downloaded;
     450    s->uploaded   = tor->uploaded;
     451
     452    tr_lockUnlock( &tor->lock );
     453
     454    return s;
     455}
     456
     457void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
     458{
     459    int i, j, piece;
     460
     461    tr_lockLock( &tor->lock );
     462    for( i = 0; i < size; i++ )
     463    {
     464        piece = i * tor->info.pieceCount / size;
     465
     466        if( tr_cpPieceIsComplete( tor->completion, piece ) )
     467        {
     468            tab[i] = -1;
     469            continue;
     470        }
     471
     472        tab[i] = 0;
    390473        for( j = 0; j < tor->peerCount; j++ )
    391474        {
    392             if( tr_peerIsConnected( tor->peers[j] ) )
     475            if( tr_peerBitfield( tor->peers[j] ) &&
     476                tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
    393477            {
    394                 (s[i].peersTotal)++;
    395                 if( tr_peerIsUploading( tor->peers[j] ) )
    396                 {
    397                     (s[i].peersUploading)++;
    398                 }
    399                 if( tr_peerIsDownloading( tor->peers[j] ) )
    400                 {
    401                     (s[i].peersDownloading)++;
    402                 }
     478                (tab[i])++;
    403479            }
    404480        }
    405 
    406         s[i].progress     = tr_cpCompletionAsFloat( tor->completion );
    407         s[i].rateDownload = tr_rcRate( tor->download );
    408         s[i].rateUpload   = tr_rcRate( tor->upload );
    409        
    410         s[i].seeders      = tr_trackerSeeders(tor);
    411                 s[i].leechers     = tr_trackerLeechers(tor);
    412 
    413         if( s[i].rateDownload < 0.1 )
    414         {
    415             s[i].eta = -1;
    416         }
    417         else
    418         {
    419             s[i].eta = (float) ( 1.0 - s[i].progress ) *
    420                 (float) inf->totalSize / s[i].rateDownload / 1024.0;
    421             if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
    422             {
    423                 s[i].eta = -1;
    424             }
    425         }
    426 
    427         for( j = 0; j < 120; j++ )
    428         {
    429             piece = j * inf->pieceCount / 120;
    430 
    431             if( tr_cpPieceIsComplete( tor->completion, piece ) )
    432             {
    433                 s[i].pieces[j] = -1;
    434                 continue;
    435             }
    436 
    437             s[i].pieces[j] = 0;
    438            
    439             for( k = 0; k < tor->peerCount; k++ )
    440             {
    441                 if( tr_peerBitfield( tor->peers[k] ) &&
    442                     tr_bitfieldHas( tr_peerBitfield( tor->peers[k] ), piece ) )
    443                 {
    444                     (s[i].pieces[j])++;
    445                 }
    446             }
    447         }
    448 
    449         s[i].downloaded = tor->downloaded;
    450         s[i].uploaded   = tor->uploaded;
    451 
    452         s[i].folder = tor->destination;
    453 
    454         tr_lockUnlock( &tor->lock );
    455     }
    456 
    457     *stat = s;
    458     return h->torrentCount;
     481    }
     482    tr_lockUnlock( &tor->lock );
    459483}
    460484
     
    464488 * Frees memory allocated by tr_torrentInit.
    465489 **********************************************************************/
    466 void tr_torrentClose( tr_handle_t * h, int t )
    467 {
    468     tr_torrent_t * tor = h->torrents[t];
    469     tr_info_t    * inf = &tor->info;
     490void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
     491{
     492    tr_info_t * inf = &tor->info;
    470493
    471494    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
    472495    {
    473496        /* Join the thread first */
    474         torrentReallyStop( h, t );
     497        torrentReallyStop( tor );
    475498    }
    476499
     
    491514    free( inf->pieces );
    492515    free( inf->files );
     516
     517    if( tor->prev )
     518    {
     519        tor->prev->next = tor->next;
     520    }
     521    else
     522    {
     523        h->torrentList = tor->next;
     524    }
     525    if( tor->next )
     526    {
     527        tor->next->prev = tor->prev;
     528    }
    493529    free( tor );
    494 
    495     memmove( &h->torrents[t], &h->torrents[t+1],
    496              ( h->torrentCount - t ) * sizeof( void * ) );
    497530
    498531    tr_lockUnlock( &h->acceptLock );
     
    507540    tr_rcClose( h->download );
    508541    free( h );
     542
     543    tr_lockClose( &gethostbynameLock );
    509544}
    510545
     
    531566    tor->status = tr_cpIsSeeding( tor->completion ) ?
    532567                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
     568    tor->tracker = tr_trackerInit( tor );
    533569
    534570    while( !tor->die )
     
    544580                        tor->finished = 1;
    545581            tr_trackerCompleted( tor->tracker );
     582            tr_ioSaveResume( tor->io );
    546583        }
    547584
     
    583620    tr_handle_t * h = _h;
    584621    uint64_t      date1, date2, lastchoke = 0;
    585     int           ii, jj;
     622    int           ii;
    586623    uint8_t     * hash;
     624    tr_torrent_t * tor;
    587625
    588626    tr_dbg( "Accept thread started" );
     
    628666            if( NULL != ( hash = tr_peerHash( h->acceptPeers[ii] ) ) )
    629667            {
    630                 for( jj = 0; jj < h->torrentCount; jj++ )
     668                for( tor = h->torrentList; tor; tor = tor->next )
    631669                {
    632                     tr_lockLock( &h->torrents[jj]->lock );
    633                     if( 0 == memcmp( h->torrents[jj]->info.hash, hash,
     670                    tr_lockLock( &tor->lock );
     671                    if( 0 == memcmp( tor->info.hash, hash,
    634672                                     SHA_DIGEST_LENGTH ) )
    635673                    {
    636                       tr_peerAttach( h->torrents[jj], h->acceptPeers[ii] );
    637                       tr_lockUnlock( &h->torrents[jj]->lock );
     674                      tr_peerAttach( tor, h->acceptPeers[ii] );
     675                      tr_lockUnlock( &tor->lock );
    638676                      goto removePeer;
    639677                    }
    640                     tr_lockUnlock( &h->torrents[jj]->lock );
     678                    tr_lockUnlock( &tor->lock );
    641679                }
     680                tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
     681                goto removePeer;
     682            }
     683            if( date1 > tr_peerDate( h->acceptPeers[ii] ) + 10000 )
     684            {
     685                /* Give them 10 seconds to send the handshake */
    642686                tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
    643687                goto removePeer;
  • branches/simple_http_parsing/libtransmission/transmission.h

    r20 r222  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    3030#include <inttypes.h>
    3131
    32 #define SHA_DIGEST_LENGTH    20
     32#define SHA_DIGEST_LENGTH 20
    3333#ifdef __BEOS__
    3434# include <StorageDefs.h>
    35 # define MAX_PATH_LENGTH B_FILE_NAME_LENGTH
     35# define MAX_PATH_LENGTH  B_FILE_NAME_LENGTH
    3636#else
    37 # define MAX_PATH_LENGTH      1024
    38 #endif
    39 #define TR_MAX_TORRENT_COUNT 50
    40 
    41 #define TR_DEFAULT_PORT      9090
     37# define MAX_PATH_LENGTH  1024
     38#endif
     39
     40#define TR_DEFAULT_PORT   9090
     41#define TR_NOERROR        0
    4242
    4343/***********************************************************************
     
    4848 **********************************************************************/
    4949typedef struct tr_handle_s tr_handle_t;
    50 
    5150tr_handle_t * tr_init();
    5251
     
    7675
    7776/***********************************************************************
     77 * tr_setDownloadLimit
     78 ***********************************************************************
     79 * Sets the total download rate limit in KB/s
     80 **********************************************************************/
     81void tr_setDownloadLimit( tr_handle_t *, int );
     82
     83/***********************************************************************
     84 * tr_torrentCount
     85 ***********************************************************************
     86 * Returns the count of open torrents
     87 **********************************************************************/
     88int tr_torrentCount( tr_handle_t * h );
     89
     90/***********************************************************************
     91 * tr_torrentIterate
     92 ***********************************************************************
     93 * Iterates on open torrents
     94 **********************************************************************/
     95typedef struct tr_torrent_s tr_torrent_t;
     96typedef void (*tr_callback_t) ( tr_torrent_t *, void * );
     97void tr_torrentIterate( tr_handle_t *, tr_callback_t, void * );
     98
     99/***********************************************************************
    78100 * tr_torrentRates
    79101 ***********************************************************************
     
    83105
    84106/***********************************************************************
    85  * tr_getFinished
    86  ***********************************************************************
    87  * Tests to see if torrent is finished
    88  **********************************************************************/
    89 int tr_getFinished( tr_handle_t *, int );
    90 
    91 /***********************************************************************
    92  * tr_setFinished
    93  ***********************************************************************
    94  * Sets the boolean value finished in the torrent back to false
    95  **********************************************************************/
    96 void tr_setFinished( tr_handle_t *, int, int );
     107 * tr_close
     108 ***********************************************************************
     109 * Frees memory allocated by tr_init.
     110 **********************************************************************/
     111void tr_close( tr_handle_t * );
    97112
    98113/***********************************************************************
     
    100115 ***********************************************************************
    101116 * Opens and parses torrent file at 'path'. If the file exists and is a
    102  * valid torrent file, returns 0 and adds it to the list of torrents
    103  * (but doesn't start it). Returns a non-zero value otherwise.
    104  **********************************************************************/
    105 int tr_torrentInit( tr_handle_t *, const char * path );
     117 * valid torrent file, returns an handle and adds it to the list of
     118 * torrents (but doesn't start it). Returns NULL and sets *error
     119 * otherwise.
     120 **********************************************************************/
     121#define TR_EINVALID     1
     122#define TR_EUNSUPPORTED 2
     123#define TR_EDUPLICATE   3
     124#define TR_EOTHER       666
     125tr_torrent_t * tr_torrentInit( tr_handle_t *, const char * path,
     126                               int * error );
     127
     128typedef struct tr_info_s tr_info_t;
     129tr_info_t * tr_torrentInfo( tr_torrent_t * );
    106130
    107131/***********************************************************************
     
    114138 * before returning.
    115139 **********************************************************************/
    116 int tr_torrentScrape( tr_handle_t *, int, int * s, int * l );
     140int tr_torrentScrape( tr_torrent_t *, int * s, int * l );
    117141
    118142/***********************************************************************
     
    122146 * therefore tr_torrentStart returns immediately.
    123147 **********************************************************************/
    124 void   tr_torrentSetFolder( tr_handle_t *, int, const char * );
    125 char * tr_torrentGetFolder( tr_handle_t *, int );
    126 void   tr_torrentStart( tr_handle_t *, int );
     148void   tr_torrentSetFolder( tr_torrent_t *, const char * );
     149char * tr_torrentGetFolder( tr_torrent_t * );
     150void   tr_torrentStart( tr_torrent_t * );
    127151
    128152/***********************************************************************
     
    137161 *   waiting any further.
    138162 **********************************************************************/
    139 void tr_torrentStop( tr_handle_t *, int );
     163void tr_torrentStop( tr_torrent_t * );
     164
     165/***********************************************************************
     166 * tr_getFinished
     167 ***********************************************************************
     168 * The first call after a torrent is completed returns 1. Returns 0
     169 * in other cases.
     170 **********************************************************************/
     171int tr_getFinished( tr_torrent_t * );
    140172
    141173/***********************************************************************
    142174 * tr_torrentStat
    143175 ***********************************************************************
    144  * Allocates an array of tr_stat_t structures, containing information
    145  * about the current status of all open torrents (see the contents
    146  * of tr_stat_s below). Returns the count of open torrents and sets
    147  * (*s) to the address of the array, or NULL if no torrent is open.
    148  * In the former case, the array belongs to the caller who is
    149  * responsible of freeing it.
    150  * The interface should call this function every 0.5 second or so in
    151  * order to update itself.
     176 * Returns a pointer to an tr_stat_t structure with updated information
     177 * on the torrent. The structure belongs to libtransmission (do not
     178 * free it) and is guaranteed to be unchanged until the next call to
     179 * tr_torrentStat.
     180 * The interface should call this function every second or so in order
     181 * to update itself.
    152182 **********************************************************************/
    153183typedef struct tr_stat_s tr_stat_t;
    154 
    155 int tr_torrentCount( tr_handle_t * h );
    156 int tr_torrentStat( tr_handle_t *, tr_stat_t ** s );
     184tr_stat_t * tr_torrentStat( tr_torrent_t * );
     185
     186/***********************************************************************
     187 * tr_torrentAvailability
     188 ***********************************************************************
     189 * Use this to draw an advanced progress bar which is 'size' pixels
     190 * wide. Fills 'tab' which you must have allocated: each byte is set
     191 * to either -1 if we have the piece, otherwise it is set to the number
     192 * of connected peers who have the piece.
     193 **********************************************************************/
     194void tr_torrentAvailability( tr_torrent_t *, int8_t * tab, int size );
    157195
    158196/***********************************************************************
     
    162200 * you must call tr_torrentStop() before closing it.
    163201 **********************************************************************/
    164 void tr_torrentClose( tr_handle_t *, int );
    165 
    166 /***********************************************************************
    167  * tr_close
    168  ***********************************************************************
    169  * Frees memory allocated by tr_init.
    170  **********************************************************************/
    171 void tr_close( tr_handle_t * );
    172 
    173 
    174 /***********************************************************************
    175  * tr_stat_s
     202void tr_torrentClose( tr_handle_t *, tr_torrent_t * );
     203
     204
     205/***********************************************************************
     206 * tr_info_s
    176207 **********************************************************************/
    177208typedef struct tr_file_s
     
    181212}
    182213tr_file_t;
    183 
    184 typedef struct tr_info_s
     214struct tr_info_s
    185215{
    186216    /* Path to torrent */
     
    205235    int         fileCount;
    206236    tr_file_t * files;
    207 }
    208 tr_info_t;
    209 
     237};
     238
     239/***********************************************************************
     240 * tr_stat_s
     241 **********************************************************************/
    210242struct tr_stat_s
    211243{
    212     tr_info_t   info;
    213 
    214244#define TR_STATUS_CHECK    0x001 /* Checking files */
    215245#define TR_STATUS_DOWNLOAD 0x002 /* Downloading */
     
    219249                                    running (for internal use only) */
    220250#define TR_STATUS_PAUSE    0x020 /* Paused */
    221 #define TR_TRACKER_ERROR   0x100
     251
     252#define TR_STATUS_ACTIVE   (TR_STATUS_CHECK|TR_STATUS_DOWNLOAD|TR_STATUS_SEED)
     253#define TR_STATUS_INACTIVE (TR_STATUS_STOPPING|TR_STATUS_STOPPED|TR_STATUS_PAUSE)
    222254    int         status;
    223     char        error[128];
     255
     256#define TR_ETRACKER 1
     257#define TR_EINOUT   2
     258    int         error;
     259    char        trackerError[128];
    224260
    225261    float       progress;
     
    230266    int         peersUploading;
    231267    int         peersDownloading;
    232     char        pieces[120];
    233268    int         seeders;
    234269    int         leechers;
     
    236271    uint64_t    downloaded;
    237272    uint64_t    uploaded;
    238 
    239     char      * folder;
    240273};
    241274
  • branches/simple_http_parsing/macosx

    • Property svn:ignore set to
      Info.plist
      Transmission.app
  • branches/simple_http_parsing/macosx/Badger.h

    r36 r222  
    1313@interface Badger : NSObject {
    1414
    15         NSImage                 * fBadge, * fDockIcon, * fBadgedDockIcon;
    16                                        
    17         NSDictionary    * fBadgeAttributes, * fStringAttributes;
    18                                        
    19         NSColor                 * fUploadingColor, * fDownloadingColor;
    20        
    21         int                             fCompleted;
     15    NSImage             * fDockIcon, * fBadgedDockIcon,
     16                        * fBadge, * fUploadBadge, * fDownloadBadge;
     17                   
     18    NSMutableDictionary * fAttributes;
     19   
     20    int                 fCompleted;
     21    BOOL                fSpeedShown;
    2222}
    2323
    2424- (void) updateBadgeWithCompleted: (int) completed
    25                                         uploadRate: (NSString *) uploadRate
    26                                         downloadRate: (NSString *) downloadRate;
     25                    uploadRate: (NSString *) uploadRate
     26                    downloadRate: (NSString *) downloadRate;
    2727- (void) clearBadge;
    2828
  • branches/simple_http_parsing/macosx/Badger.m

    r36 r222  
    1 //
    2 //  Badger.m
    3 //  Transmission
    4 //
    5 //  Created by Mitchell Livingston on 12/21/05.
    6 //
     1/******************************************************************************
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
     3 *
     4 * Permission is hereby granted, free of charge, to any person obtaining a
     5 * copy of this software and associated documentation files (the "Software"),
     6 * to deal in the Software without restriction, including without limitation
     7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     8 * and/or sell copies of the Software, and to permit persons to whom the
     9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     20 * DEALINGS IN THE SOFTWARE.
     21 *****************************************************************************/
    722
    823#import "Badger.h"
     24#import "StringAdditions.h"
     25#import "Utils.h"
    926
    1027@interface Badger (Private)
    1128
    12 - (NSImage *) badgeWithNum: (int) num;
     29- (void) badgeString: (NSString *) string forRect: (NSRect) rect;
    1330
    1431@end
     
    1835- (id) init
    1936{
    20         if ((self = [super init]))
    21         {
    22                 fBadge = [NSImage imageNamed: @"Badge"];
    23                 fDockIcon = [[NSApp applicationIconImage] copy];
    24                 fBadgedDockIcon = [fDockIcon copy];
    25                
    26                 fBadgeAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:
    27                                                                 [NSColor whiteColor], NSForegroundColorAttributeName,
    28                                                                 [NSFont fontWithName: @"Helvetica-Bold" size: 24], NSFontAttributeName,
    29                                                                 nil] retain];
    30                
    31                 fStringAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:
    32                                                                 [NSColor whiteColor], NSForegroundColorAttributeName,
    33                                                                 [NSFont fontWithName: @"Helvetica-Bold" size: 20], NSFontAttributeName,
    34                                                                 nil] retain];
    35                                                                
    36                 fUploadingColor = [[[NSColor greenColor] colorWithAlphaComponent: 0.65] retain];
    37                 fDownloadingColor = [[[NSColor blueColor] colorWithAlphaComponent: 0.65] retain];
    38                
    39                 fCompleted = 0;
    40         }
    41        
    42         return self;
     37    if ((self = [super init]))
     38    {
     39        fBadge = [NSImage imageNamed: @"Badge"];
     40        fDockIcon = [[NSApp applicationIconImage] copy];
     41        fBadgedDockIcon = [fDockIcon copy];
     42        fUploadBadge = [NSImage imageNamed: @"UploadBadge"];
     43        fDownloadBadge = [NSImage imageNamed: @"DownloadBadge"];
     44       
     45       
     46        fAttributes = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
     47            [NSColor whiteColor], NSForegroundColorAttributeName,
     48            [NSFont fontWithName: @"Helvetica-Bold" size: 28], NSFontAttributeName,
     49            nil] retain];
     50
     51        if( OSX_VERSION >= 10.3 )
     52        {
     53            NSShadow * stringShadow = [[NSShadow alloc] init];
     54            [stringShadow setShadowOffset: NSMakeSize(2, -2)];
     55            [stringShadow setShadowBlurRadius: 4];
     56            [fAttributes setObject: stringShadow
     57                forKey: NSShadowAttributeName];
     58            [stringShadow release];
     59        }
     60       
     61        fCompleted = 0;
     62        fSpeedShown = NO;
     63    }
     64   
     65    return self;
    4366}
    4467
    4568- (void) dealloc
    4669{
    47         [fDockIcon release];
    48         [fBadgedDockIcon release];
    49 
    50         [fBadgeAttributes release];
    51         [fStringAttributes release];
    52        
    53         [fUploadingColor release];
    54         [fDownloadingColor release];
    55 
    56         [super dealloc];
     70    [fDockIcon release];
     71    [fBadgedDockIcon release];
     72
     73    [fAttributes release];
     74
     75    [super dealloc];
    5776}
    5877
    5978- (void) updateBadgeWithCompleted: (int) completed
    60                                         uploadRate: (NSString *) uploadRate
    61                                         downloadRate: (NSString *) downloadRate
    62 {
    63         NSImage * dockIcon;
    64         NSSize iconSize = [fDockIcon size];
    65                        
    66         //set seeding and downloading badges if there was a change
    67         if (completed != fCompleted)
    68         {
    69                 fCompleted = completed;
    70                
    71                 dockIcon = [fDockIcon copy];
    72                 [dockIcon lockFocus];
    73                
    74                 //set completed badge to top right
    75                 if (completed > 0)
    76                 {
    77                         NSSize badgeSize = [fBadge size];
    78                         [[self badgeWithNum: completed]
    79                                         compositeToPoint: NSMakePoint(iconSize.width - badgeSize.width,
    80                                                                                 iconSize.height - badgeSize.height)
    81                                                         operation: NSCompositeSourceOver];
    82                 }
    83 
    84                 [dockIcon unlockFocus];
    85                
    86                 [fBadgedDockIcon release];
    87                 fBadgedDockIcon = [dockIcon copy];
    88         }
    89         else
    90                 dockIcon = [fBadgedDockIcon copy];
    91        
    92         if (uploadRate || downloadRate)
    93         {
    94                 //upload rate at bottom
    95                 float mainY = 5,
    96                         mainHeight = 25;
    97                 NSRect shapeRect = NSMakeRect(12.5, mainY, iconSize.width - 25, mainHeight);
    98                
    99                 NSRect leftRect, rightRect;
    100                 leftRect.origin.x = 0;
    101                 leftRect.origin.y = mainY;
    102                 leftRect.size.width = shapeRect.origin.x * 2.0;
    103                 leftRect.size.height = mainHeight;
    104                
    105                 rightRect = leftRect;
    106                 rightRect.origin.x = iconSize.width - rightRect.size.width;
    107                
    108                 NSRect textRect;
    109                 textRect.origin.y = mainY;
    110                 textRect.size.height = mainHeight;
    111                
    112                 [dockIcon lockFocus];
    113                
    114                 if (uploadRate)
    115                 {
    116                         float width = [uploadRate sizeWithAttributes: fStringAttributes].width;
    117                         textRect.origin.x = (iconSize.width - width) * 0.5;
    118                         textRect.size.width = width;
    119                
    120                         NSBezierPath * uploadOval = [NSBezierPath bezierPathWithRect: shapeRect];
    121                         [uploadOval appendBezierPathWithOvalInRect: leftRect];
    122                         [uploadOval appendBezierPathWithOvalInRect: rightRect];
    123                
    124                         [fUploadingColor set];
    125                         [uploadOval fill];
    126                         [uploadRate drawInRect: textRect withAttributes: fStringAttributes];
    127                        
    128                         //shift up for download rate if there is an upload rate
    129                         float heightDiff = 27;
    130                         shapeRect.origin.y += heightDiff;
    131                         leftRect.origin.y += heightDiff;
    132                         rightRect.origin.y += heightDiff;
    133                         textRect.origin.y += heightDiff;
    134                 }
    135                
    136                 //download rate above upload rate
    137                 if (downloadRate)
    138                 {
    139                         float width = [downloadRate sizeWithAttributes: fStringAttributes].width;
    140                         textRect.origin.x = (iconSize.width - width) * 0.5;
    141                         textRect.size.width = width;
    142                
    143                         NSBezierPath * downloadOval = [NSBezierPath bezierPathWithRect: shapeRect];
    144                         [downloadOval appendBezierPathWithOvalInRect: leftRect];
    145                         [downloadOval appendBezierPathWithOvalInRect: rightRect];
    146                
    147                         [fDownloadingColor set];
    148                         [downloadOval fill];
    149                         [downloadRate drawInRect: textRect withAttributes: fStringAttributes];
    150                 }
    151                
    152                 [dockIcon unlockFocus];
    153         }
    154        
    155         [NSApp setApplicationIconImage: dockIcon];
    156         [dockIcon release];
     79                    uploadRate: (NSString *) uploadRate
     80                    downloadRate: (NSString *) downloadRate
     81{
     82    NSImage * dockIcon = nil;
     83    NSSize iconSize = [fDockIcon size];
     84
     85    //set seeding and downloading badges if there was a change
     86    if (completed != fCompleted)
     87    {
     88        fCompleted = completed;
     89       
     90        dockIcon = [fDockIcon copy];
     91       
     92        //set completed badge to top right
     93        if (completed > 0)
     94        {
     95            NSRect badgeRect;
     96            badgeRect.size = [fBadge size];
     97            badgeRect.origin.x = iconSize.width - badgeRect.size.width;
     98            badgeRect.origin.y = iconSize.height - badgeRect.size.height;
     99                                       
     100            [dockIcon lockFocus];
     101           
     102            //place badge
     103            [fBadge compositeToPoint: badgeRect.origin
     104                        operation: NSCompositeSourceOver];
     105           
     106            //ignore shadow of badge when placing string
     107            float badgeBottomExtra = 5.0;
     108            badgeRect.size.height -= badgeBottomExtra;
     109            badgeRect.origin.y += badgeBottomExtra;
     110           
     111            //place badge text
     112            [self badgeString: [NSString stringWithInt: completed]
     113                        forRect: badgeRect];
     114                       
     115            [dockIcon unlockFocus];
     116        }
     117       
     118        [fBadgedDockIcon release];
     119        fBadgedDockIcon = [dockIcon copy];
     120    }
     121
     122    //display upload and download rates
     123    BOOL speedShown = NO;
     124    if (uploadRate || downloadRate)
     125    {
     126        speedShown = YES;
     127   
     128        NSRect badgeRect, stringRect;
     129        badgeRect.size = [fUploadBadge size];
     130        badgeRect.origin = NSZeroPoint;
     131       
     132        //ignore shadow of badge when placing string
     133        float badgeBottomExtra = 2.0;
     134        stringRect = badgeRect;
     135        stringRect.size.height -= badgeBottomExtra;
     136        stringRect.origin.y += badgeBottomExtra;
     137
     138        if (!dockIcon)
     139            dockIcon = [fBadgedDockIcon copy];
     140       
     141        [dockIcon lockFocus];
     142       
     143        if (uploadRate)
     144        {
     145            //place badge
     146            [fUploadBadge compositeToPoint: badgeRect.origin
     147                        operation: NSCompositeSourceOver];
     148           
     149            //place badge text
     150            [self badgeString: uploadRate forRect: stringRect];
     151        }
     152       
     153        if (downloadRate)
     154        {
     155            //download rate above upload rate
     156            if (uploadRate)
     157            {
     158                float spaceBetween = badgeRect.size.height + 2.0;
     159                badgeRect.origin.y += spaceBetween;
     160                stringRect.origin.y += spaceBetween;
     161            }
     162       
     163            //place badge
     164            [fDownloadBadge compositeToPoint: badgeRect.origin
     165                        operation: NSCompositeSourceOver];
     166           
     167            //place badge text
     168            [self badgeString: downloadRate forRect: stringRect];
     169        }
     170       
     171        [dockIcon unlockFocus];
     172    }
     173
     174    if (dockIcon || fSpeedShown)
     175    {
     176        if (!dockIcon)
     177            dockIcon = [fBadgedDockIcon copy];
     178           
     179        [NSApp setApplicationIconImage: dockIcon];
     180       
     181        [dockIcon release];
     182    }
     183   
     184    fSpeedShown = speedShown;
    157185}
    158186
    159187- (void) clearBadge
    160188{
    161         [fBadgedDockIcon release];
    162         fBadgedDockIcon = [fDockIcon copy];
    163 
    164         [NSApp setApplicationIconImage: fDockIcon];
     189    [fBadgedDockIcon release];
     190    fBadgedDockIcon = [fDockIcon copy];
     191
     192    [NSApp setApplicationIconImage: fDockIcon];
    165193}
    166194
     
    169197@implementation Badger (Private)
    170198
    171 - (NSImage *) badgeWithNum: (int) num
    172 {       
    173         NSImage * badge = [[fBadge copy] autorelease];
    174         NSString * numString = [NSString stringWithFormat: @"%d", num];
    175        
    176         //number is in center of image
    177         NSRect badgeRect;
    178         NSSize numSize = [numString sizeWithAttributes: fBadgeAttributes];
    179         badgeRect.size = [badge size];
    180         badgeRect.origin.x = (badgeRect.size.width - numSize.width) * 0.5;
    181         badgeRect.origin.y = badgeRect.size.height * 0.5 - numSize.height * 1.2;
    182 
    183         [badge lockFocus];
    184         [numString drawInRect: badgeRect withAttributes: fBadgeAttributes];
    185         [badge unlockFocus];
    186        
    187         return badge;
     199//dock icon must have locked focus
     200- (void) badgeString: (NSString *) string forRect: (NSRect) rect
     201{
     202    NSSize stringSize = [string sizeWithAttributes: fAttributes];
     203   
     204    //string is in center of image
     205    rect.origin.x += (rect.size.width - stringSize.width) * 0.5;
     206    rect.origin.y += (rect.size.height - stringSize.height) * 0.5;
     207                       
     208    [string drawAtPoint: rect.origin withAttributes: fAttributes];
    188209}
    189210
  • branches/simple_http_parsing/macosx/Controller.h

    r59 r222  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    3333@interface Controller : NSObject
    3434{
    35     tr_handle_t                 * fHandle;
    36     int                         fCount, fSeeding, fDownloading, fCompleted;
    37     tr_stat_t                   * fStat;
    38     int                         fResumeOnWake[TR_MAX_TORRENT_COUNT];
     35    tr_handle_t                 * fLib;
     36    int                         fCompleted;
     37    NSMutableArray              * fTorrents;
    3938
    4039    NSToolbar                   * fToolbar;
     
    5049
    5150    IBOutlet NSWindow           * fWindow;
     51    IBOutlet NSScrollView       * fScrollView;
    5252    IBOutlet TorrentTableView   * fTableView;
    5353    IBOutlet NSTextField        * fTotalDLField;
     
    5555
    5656    IBOutlet NSPanel            * fInfoPanel;
    57     IBOutlet NSTextField        * fInfoTitle;
     57    IBOutlet NSImageView        * fInfoImageView;
     58    IBOutlet NSTextField        * fInfoName;
     59    IBOutlet NSTextField        * fInfoSize;
    5860    IBOutlet NSTextField        * fInfoTracker;
    5961    IBOutlet NSTextField        * fInfoAnnounce;
    60     IBOutlet NSTextField        * fInfoSize;
     62    IBOutlet NSTextField        * fInfoPieceSize;
    6163    IBOutlet NSTextField        * fInfoPieces;
    62     IBOutlet NSTextField        * fInfoPieceSize;
     64    IBOutlet NSTextField        * fInfoHash1;
     65    IBOutlet NSTextField        * fInfoHash2;
    6366    IBOutlet NSTextField        * fInfoSeeders;
    6467    IBOutlet NSTextField        * fInfoLeechers;
    65     IBOutlet NSTextField        * fInfoFolder;
    6668    IBOutlet NSTextField        * fInfoDownloaded;
    6769    IBOutlet NSTextField        * fInfoUploaded;
     70    NSImage                     * fAppIcon;
    6871
    6972    io_connect_t                fRootPort;
    70     NSArray                     * fFilenames;
    7173    NSTimer                     * fTimer;
    7274    NSTimer                     * fUpdateTimer;
     
    8789
    8890- (void) quitSheetDidEnd:   (NSWindow *)sheet returnCode:(int)returnCode
    89                             contextInfo:(void  *)contextInfo;
     91                            contextInfo:(void *)contextInfo;
    9092                       
     93- (void) resumeTorrent:             (id) sender;
     94- (void) resumeAllTorrents:         (id) sender;
     95- (void) resumeTorrentWithIndex:    (NSIndexSet *) indexSet;
    9196- (void) stopTorrent:               (id) sender;
    9297- (void) stopAllTorrents:           (id) sender;
    93 - (void) stopTorrentWithIndex:      (int) index;
    94 - (void) resumeTorrent:             (id) sender;
    95 - (void) resumeAllTorrents:         (id) sender;
    96 - (void) resumeTorrentWithIndex:    (int) index;
     98- (void) stopTorrentWithIndex:      (NSIndexSet *) indexSet;
    9799
    98100- (void) removeTorrent:             (id) sender;
     
    100102- (void) removeTorrentDeleteData:   (id) sender;
    101103- (void) removeTorrentDeleteBoth:   (id) sender;
    102 - (void) removeTorrentWithIndex:    (int) idx
     104- (void) removeTorrentWithIndex:    (NSIndexSet *) indexSet
    103105                deleteTorrent:      (BOOL) deleteTorrent
    104106                deleteData:         (BOOL) deleteData;
    105107               
    106108- (void) removeSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode
    107                         contextInfo:(NSDictionary  *)dict;
    108 - (void) confirmRemoveTorrentWithIndex: (int) idx
     109                        contextInfo:(NSDictionary *)dict;
     110- (void) confirmRemoveTorrentWithIndex: (NSIndexSet *) indexSet
    109111            deleteTorrent: (BOOL) deleteTorrent
    110112            deleteData: (BOOL) deleteData;
     
    113115
    114116- (void) updateUI:        (NSTimer *) timer;
     117- (void) updateTorrentHistory;
    115118- (void) sleepCallBack:   (natural_t) messageType argument:
    116119                        (void *) messageArgument;
     
    126129- (void) notifyGrowl:       (NSString *) file;
    127130- (void) revealFromMenu:    (id) sender;
    128 - (void) finderReveal:      (NSString *) path;
    129 - (void) finderTrash:       (NSString *) path;
    130131- (void) growlRegister:     (id) sender;
    131132
  • branches/simple_http_parsing/macosx/Controller.m

    r78 r222  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    2323#import <IOKit/IOMessage.h>
    2424
    25 #import "NameCell.h"
    26 #import "ProgressCell.h"
     25#import "Controller.h"
     26#import "Torrent.h"
     27#import "TorrentCell.h"
    2728#import "StringAdditions.h"
    2829#import "Utils.h"
     
    5354@implementation Controller
    5455
     56- (id) init
     57{
     58    fLib      = tr_init();
     59    fTorrents = [[NSMutableArray alloc] initWithCapacity: 10];
     60    return self;
     61}
     62
     63- (void) dealloc
     64{
     65    [fTorrents release];
     66    tr_close( fLib );
     67    [fAppIcon release];
     68    [super dealloc];
     69}
     70
    5571- (void) awakeFromNib
    5672{
    57     [fWindow setContentMinSize: NSMakeSize( 400, 120 )];
    58    
    59     fHandle = tr_init();
    60 
    61     [fPrefsController setPrefsWindow: fHandle];
     73    fAppIcon = [[NSApp applicationIconImage] copy];
     74
     75    [fPrefsController setPrefsWindow: fLib];
    6276    fDefaults = [NSUserDefaults standardUserDefaults];
    63    
    64     [fInfoPanel setFrameAutosaveName:@"InfoPanel"];
    6577
    6678    //check advanced bar menu item
    6779    [fAdvancedBarItem setState: [fDefaults
    6880        boolForKey:@"UseAdvancedBar"] ? NSOnState : NSOffState];
    69    
     81
    7082    fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Transmission Toolbar"];
    7183    [fToolbar setDelegate: self];
     
    7587    [fWindow setDelegate: self];
    7688
    77     NSTableColumn * tableColumn;
    78     NameCell * nameCell = [[NameCell alloc] init];
    79     ProgressCell * progressCell = [[ProgressCell alloc] init];
    80 
    81     tableColumn  = [fTableView tableColumnWithIdentifier: @"Name"];
    82     [tableColumn setDataCell: nameCell];
    83 
    84     tableColumn  = [fTableView tableColumnWithIdentifier: @"Progress"];
    85     [tableColumn setDataCell: progressCell];
    86 
    87     [fTableView setAutosaveTableColumns: YES];
    88     //[fTableView sizeToFit];
     89    [fTableView setTorrents: fTorrents];
     90    [[fTableView tableColumnWithIdentifier: @"Torrent"] setDataCell:
     91        [[TorrentCell alloc] init]];
    8992
    9093    [fTableView registerForDraggedTypes: [NSArray arrayWithObjects:
     
    109112    NSDictionary * dic;
    110113
     114    Torrent * torrent;
    111115    NSEnumerator * enumerator = [[fDefaults arrayForKey: @"History"] objectEnumerator];
    112116    while ((dic = [enumerator nextObject]))
     
    115119        downloadFolder = [dic objectForKey: @"DownloadFolder"];
    116120        paused         = [dic objectForKey: @"Paused"];
    117            
     121
    118122        if (!torrentPath || !downloadFolder || !paused)
    119123            continue;
    120124
    121         if (tr_torrentInit(fHandle, [torrentPath UTF8String]))
     125        torrent = [[Torrent alloc] initWithPath: torrentPath lib: fLib];
     126        if( !torrent )
    122127            continue;
    123128
    124         tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    125                                 [downloadFolder UTF8String] );
     129        [fTorrents addObject: torrent];
     130
     131        [torrent setFolder: downloadFolder];
    126132
    127133        if ([paused isEqualToString: @"NO"])
    128             tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     134            [torrent start];
    129135    }
    130136
     
    138144    //initialize badging
    139145    fBadger = [[Badger alloc] init];
    140    
     146
    141147    //update the interface every 500 ms
    142     fCount = 0;
    143     fDownloading = 0;
    144     fSeeding = 0;
    145148    fCompleted = 0;
    146     fStat  = nil;
    147     fTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 target: self
    148         selector: @selector( updateUI: ) userInfo: NULL repeats: YES];
     149    [self updateUI: nil];
     150    fTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self
     151        selector: @selector( updateUI: ) userInfo: nil repeats: YES];
    149152    [[NSRunLoop currentRunLoop] addTimer: fTimer
    150153        forMode: NSModalPanelRunLoopMode];
     
    155158    fUpdateTimer = [NSTimer scheduledTimerWithTimeInterval: 60.0
    156159        target: self selector: @selector( checkForUpdateTimer: )
    157         userInfo: NULL repeats: YES];
     160        userInfo: nil repeats: YES];
    158161}
    159162
     
    171174- (BOOL) windowShouldClose: (id) sender
    172175{
    173     [fWindow orderOut: NULL];
     176    [fWindow orderOut: nil];
    174177    return NO;
    175178}
     
    178181    hasVisibleWindows: (BOOL) flag
    179182{
    180     [self showMainWindow: NULL];
     183    [self showMainWindow: nil];
    181184    return NO;
    182185}
     
    184187- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
    185188{
    186     int active = fDownloading + fSeeding;
    187     if (active > 0 && [[NSUserDefaults standardUserDefaults] boolForKey: @"CheckQuit"])
     189    int active = 0;
     190    Torrent * torrent;
     191    NSEnumerator * enumerator = [fTorrents objectEnumerator];
     192    while( ( torrent = [enumerator nextObject] ) )
     193    {
     194        if( [torrent isActive] )
     195        {
     196            active++;
     197        }
     198    }
     199
     200    if (active > 0 && [fDefaults boolForKey: @"CheckQuit"])
    188201    {
    189202        NSString * message = active == 1
     
    199212                            nil, nil, message);
    200213        return NSTerminateLater;
    201     }                                                                           
    202    
     214    }
     215
    203216    return NSTerminateNow;
    204217}
     
    214227- (void) applicationWillTerminate: (NSNotification *) notification
    215228{
    216     int i;
    217    
     229    NSEnumerator * enumerator;
     230    Torrent * torrent;
     231
    218232    // Stop updating the interface
    219233    [fTimer invalidate];
     
    222236    //clear badge
    223237    [fBadger clearBadge];
    224     [fBadger release];                                                         
    225 
    226     // Save history and stop running torrents
    227     NSMutableArray * history = [NSMutableArray arrayWithCapacity: fCount];
    228     BOOL active;
    229     for( i = 0; i < fCount; i++ )
    230     {
    231         active = fStat[i].status &
    232             ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED );
    233 
    234         [history addObject: [NSDictionary dictionaryWithObjectsAndKeys:
    235             [NSString stringWithUTF8String: fStat[i].info.torrent],
    236             @"TorrentPath",
    237             [NSString stringWithUTF8String: tr_torrentGetFolder( fHandle, i )],
    238             @"DownloadFolder",
    239             active ? @"NO" : @"YES",
    240             @"Paused",
    241             NULL]];
    242 
    243         if( active )
    244         {
    245             tr_torrentStop( fHandle, i );
    246         }
    247     }
    248     [fDefaults setObject: history forKey: @"History"];
     238    [fBadger release];
     239
     240    // Save history
     241    [self updateTorrentHistory];
     242
     243    // Stop running torrents
     244    enumerator = [fTorrents objectEnumerator];
     245    while( ( torrent = [enumerator nextObject] ) )
     246    {
     247        [torrent stop];
     248    }
    249249
    250250    // Wait for torrents to stop (5 seconds timeout)
    251251    NSDate * start = [NSDate date];
    252     while( fCount > 0 )
    253     {
     252    while( [fTorrents count] )
     253    {
     254        torrent = [fTorrents objectAtIndex: 0];
    254255        while( [[NSDate date] timeIntervalSinceDate: start] < 5 &&
    255                 !( fStat[0].status & TR_STATUS_PAUSE ) )
     256                ![torrent isPaused] )
    256257        {
    257258            usleep( 100000 );
    258             tr_torrentStat( fHandle, &fStat );
    259         }
    260         tr_torrentClose( fHandle, 0 );
    261         fCount = tr_torrentStat( fHandle, &fStat );
    262     }
    263 
    264     tr_close( fHandle );
     259            [torrent update];
     260        }
     261        [fTorrents removeObject: torrent];
     262        [torrent release];
     263    }
    265264}
    266265
     
    270269    if (![fPrefsWindow isVisible])
    271270    {
    272         NSRect  prefsFrame, screenRect;
    273         NSPoint point;
    274 
    275         prefsFrame = [fPrefsWindow frame];
    276         screenRect = [[NSScreen mainScreen] visibleFrame];
    277         point.x    = (screenRect.size.width - prefsFrame.size.width) * 0.5;
    278         point.y    = screenRect.origin.y + screenRect.size.height * 0.67 +
    279                      prefsFrame.size.height * 0.33;
    280 
    281         [fPrefsWindow setFrameTopLeftPoint: point];
    282     }
    283    
     271        [fPrefsWindow center];
     272    }
     273
    284274    [fPrefsWindow makeKeyAndOrderFront:NULL];
    285275}
    286276
    287277- (void) folderChoiceClosed: (NSOpenPanel *) s returnCode: (int) code
    288     contextInfo: (void *) info
     278    contextInfo: (Torrent *) torrent
    289279{
    290280    if (code == NSOKButton)
    291281    {
    292         tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    293                          [[[s filenames] objectAtIndex: 0] UTF8String] );
    294         tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     282        [fTorrents addObject: torrent];
     283        [torrent setFolder: [[s filenames] objectAtIndex: 0]];
     284        [torrent start];
    295285    }
    296286    else
    297287    {
    298         tr_torrentClose( fHandle, tr_torrentCount( fHandle ) - 1 );
     288        [torrent release];
    299289    }
    300290    [NSApp stopModal];
     
    305295{
    306296    NSString * downloadChoice, * downloadFolder, * torrentPath;
     297    Torrent * torrent;
    307298
    308299    downloadChoice = [fDefaults stringForKey: @"DownloadChoice"];
     
    312303    while ((torrentPath = [enumerator nextObject]))
    313304    {
    314         if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) )
     305        torrent = [[Torrent alloc] initWithPath: torrentPath lib: fLib];
     306        if( !torrent )
    315307            continue;
    316308
     
    321313        if( [downloadChoice isEqualToString: @"Constant"] )
    322314        {
    323             tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    324                                  [[downloadFolder stringByExpandingTildeInPath] UTF8String] );
    325             tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     315            [fTorrents addObject: torrent];
     316            [torrent setFolder: [downloadFolder stringByExpandingTildeInPath]];
     317            [torrent start];
    326318        }
    327319        else if( [downloadChoice isEqualToString: @"Torrent"] )
    328320        {
    329             tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    330                 [[torrentPath stringByDeletingLastPathComponent] UTF8String] );
    331             tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     321            [fTorrents addObject: torrent];
     322            [torrent setFolder: [torrentPath stringByDeletingLastPathComponent]];
     323            [torrent start];
    332324        }
    333325        else
    334326        {
    335327            NSOpenPanel * panel = [NSOpenPanel openPanel];
    336            
     328
    337329            [panel setPrompt: @"Select Download Folder"];
    338             [panel setMessage: [NSString stringWithFormat:
    339                                 @"Select the download folder for %@",
    340                                 [torrentPath lastPathComponent]]];
    341330            [panel setAllowsMultipleSelection: NO];
    342331            [panel setCanChooseFiles: NO];
    343332            [panel setCanChooseDirectories: YES];
    344333
     334            if( [panel respondsToSelector: @selector(setMessage:)] )
     335                /* >= 10.3 */
     336                [panel setMessage: [NSString stringWithFormat:
     337                                    @"Select the download folder for %@",
     338                                    [torrentPath lastPathComponent]]];
     339
    345340            [panel beginSheetForDirectory: NULL file: NULL types: NULL
    346341                modalForWindow: fWindow modalDelegate: self didEndSelector:
    347342                @selector( folderChoiceClosed:returnCode:contextInfo: )
    348                 contextInfo: NULL];
     343                contextInfo: torrent];
    349344            [NSApp runModalForWindow: panel];
    350345        }
    351346    }
    352347
    353     [self updateUI: NULL];
     348    [self updateUI: nil];
     349    [self updateTorrentHistory];
    354350}
    355351
     
    366362- (void) open: (NSArray *) files
    367363{
    368     fFilenames = [files retain];
    369364    [self performSelectorOnMainThread: @selector(cantFindAName:)
    370                 withObject: NULL waitUntilDone: NO];
     365                withObject: files waitUntilDone: NO];
    371366}
    372367
     
    378373    panel     = [NSOpenPanel openPanel];
    379374    fileTypes = [NSArray arrayWithObject: @"torrent"];
    380    
     375
    381376    [panel setAllowsMultipleSelection: YES];
    382377    [panel setCanChooseFiles:          YES];
     
    389384}
    390385
    391 - (void) cantFindAName: (id) sender
    392 {
    393     [self application: NSApp openFiles: fFilenames];
    394     [fFilenames release];
     386- (void) cantFindAName: (NSArray *) filenames
     387{
     388    [self application: NSApp openFiles: filenames];
    395389}
    396390
     
    398392    contextInfo: (void *) info
    399393{
    400     if( code != NSOKButton )
    401     {
    402         return;
    403     }
    404 
    405     fFilenames = [[s filenames] retain];
    406 
    407     [self performSelectorOnMainThread: @selector(cantFindAName:)
    408                 withObject: NULL waitUntilDone: NO];
     394    if( code == NSOKButton )
     395        [self performSelectorOnMainThread: @selector(cantFindAName:)
     396                    withObject: [s filenames] waitUntilDone: NO];
    409397}
    410398
    411399- (void) resumeTorrent: (id) sender
    412400{
    413     [self resumeTorrentWithIndex: [fTableView selectedRow]];
     401    [self resumeTorrentWithIndex: [fTableView selectedRowIndexes]];
    414402}
    415403
    416404- (void) resumeAllTorrents: (id) sender
    417405{
    418     int i;
    419     for ( i = 0; i < fCount; i++)
    420     {
    421         if ( fStat[i].status & ( TR_STATUS_STOPPING
    422         | TR_STATUS_PAUSE | TR_STATUS_STOPPED ) )
    423         {
    424             [self resumeTorrentWithIndex: i];
    425         }
    426     }
    427 }
    428 
    429 - (void) resumeTorrentWithIndex: (int) idx
    430 {
    431     tr_torrentStart( fHandle, idx );
    432     [self updateUI: NULL];
     406    [self resumeTorrentWithIndex: [NSIndexSet indexSetWithIndexesInRange:
     407                                                                        NSMakeRange(0, [fTorrents count])]];
     408}
     409
     410- (void) resumeTorrentWithIndex: (NSIndexSet *) indexSet
     411{
     412        Torrent * torrent;
     413    unsigned int i;
     414       
     415    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
     416        {
     417                torrent = [fTorrents objectAtIndex: i];
     418                [torrent start];
     419        }
     420       
     421    [self updateUI: nil];
     422    [self updateTorrentHistory];