Esempio n. 1
0
static int
testMerge( void )
{
    tr_benc dest, src;
    int64_t i;
    const char * s;

    /* initial dictionary (default values)  */
    tr_bencInitDict( &dest, 10 );
    tr_bencDictAddInt( &dest, "i1", 1 );
    tr_bencDictAddInt( &dest, "i2", 2 );
    tr_bencDictAddInt( &dest, "i4", -35 ); /* remains untouched */
    tr_bencDictAddStr( &dest, "s5", "abc" );
    tr_bencDictAddStr( &dest, "s6", "def" );
    tr_bencDictAddStr( &dest, "s7", "127.0.0.1" ); /* remains untouched */

    /* new dictionary, will overwrite items in dest  */
    tr_bencInitDict( &src, 10 );
    tr_bencDictAddInt( &src, "i1", 1 );     /* same value */
    tr_bencDictAddInt( &src, "i2", 4 );     /* new value */
    tr_bencDictAddInt( &src, "i3", 3 );     /* new key:value */
    tr_bencDictAddStr( &src, "s5", "abc" ); /* same value */
    tr_bencDictAddStr( &src, "s6", "xyz" ); /* new value */
    tr_bencDictAddStr( &src, "s8", "ghi" ); /* new key:value */

    tr_bencMergeDicts( &dest, /*const*/ &src );

    check( tr_bencDictFindInt( &dest, "i1", &i ));
    check_int_eq (1, i);
    check( tr_bencDictFindInt( &dest, "i2", &i ));
    check_int_eq (4, i);
    check( tr_bencDictFindInt( &dest, "i3", &i ));
    check_int_eq (3, i);
    check( tr_bencDictFindInt( &dest, "i4", &i ));
    check_int_eq (-35, i);
    check( tr_bencDictFindStr( &dest, "s5", &s ));
    check_streq ("abc", s);
    check( tr_bencDictFindStr( &dest, "s6", &s ));
    check_streq ("xyz", s);
    check( tr_bencDictFindStr( &dest, "s7", &s ));
    check_streq ("127.0.0.1", s);
    check( tr_bencDictFindStr( &dest, "s8", &s ));
    check_streq ("ghi", s);

    tr_bencFree( &dest );
    tr_bencFree( &src );
    return 0;
}
Esempio n. 2
0
const char*
gtr_pref_string_get( const char * key )
{
    const char * str = NULL;
    tr_bencDictFindStr( getPrefs( ), key, &str );
    return str;
}
Esempio n. 3
0
int
tr_ctorSetMetainfoFromFile( tr_ctor *    ctor,
                            const char * filename )
{
    uint8_t * metainfo;
    size_t    len;
    int       err;

    metainfo = tr_loadFile( filename, &len );
    if( metainfo && len )
        err = tr_ctorSetMetainfo( ctor, metainfo, len );
    else
    {
        clearMetainfo( ctor );
        err = 1;
    }

    setSourceFile( ctor, filename );

    /* if no `name' field was set, then set it from the filename */
    if( ctor->isSet_metainfo )
    {
        tr_benc * info;
        if( tr_bencDictFindDict( &ctor->metainfo, "info", &info ) )
        {
            const char * name;
            if( !tr_bencDictFindStr( info, "name.utf-8", &name ) )
                if( !tr_bencDictFindStr( info, "name", &name ) )
                    name = NULL;
            if( !name || !*name )
            {
                char * base = tr_basename( filename );
                tr_bencDictAddStr( info, "name", base );
                tr_free( base );
            }
        }
    }

    tr_free( metainfo );
    return err;
}
Esempio n. 4
0
static tr_bool
addURL( tr_benc * metainfo, const char * url )
{
    const char * str;
    tr_benc * announce_list;
    tr_bool changed = FALSE;
    tr_bool match = FALSE;

    /* maybe add it to "announce" */
    if( !tr_bencDictFindStr( metainfo, "announce", &str ) )
    {
        printf( "\tAdded \"%s\" in \"announce\"\n", url );
        tr_bencDictAddStr( metainfo, "announce", url );
        changed = TRUE;
    }

    /* see if it's already in announce-list */
    if( tr_bencDictFindList( metainfo, "announce-list", &announce_list ) ) {
        tr_benc * tier;
        int tierCount = 0;
        while(( tier = tr_bencListChild( announce_list, tierCount++ ))) {
            tr_benc * node;
            int nodeCount = 0;
            while(( node = tr_bencListChild( tier, nodeCount++ )))
                if( tr_bencGetStr( node, &str ) && !strcmp( str, url ) )
                    match = TRUE;
        }
    }

    /* if it's not in announce-list, add it now */
    if( !match )
    {
        tr_benc * tier;

        if( !tr_bencDictFindList( metainfo, "announce-list", &announce_list ) )
            announce_list = tr_bencDictAddList( metainfo, "announce-list", 1 );

        tier = tr_bencListAddList( announce_list, 1 );
        tr_bencListAddStr( tier, url );
        printf( "\tAdded \"%s\" to \"announce-list\" tier %zu\n", url, tr_bencListSize( announce_list ) );
        changed = TRUE;
    }

    return changed;
}
Esempio n. 5
0
static bool
addURL (tr_benc * metainfo, const char * url)
{
  const char * announce = NULL;
  tr_benc * announce_list = NULL;
  bool changed = false;
  const bool had_announce = tr_bencDictFindStr (metainfo, "announce", &announce);
  const bool had_announce_list = tr_bencDictFindList (metainfo, "announce-list", &announce_list);

  if (!had_announce && !had_announce_list)
    {
      /* this new tracker is the only one, so add it to "announce"... */
      printf ("\tAdded \"%s\" in \"announce\"\n", url);
      tr_bencDictAddStr (metainfo, "announce", url);
      changed = true;
    }
  else
    {
      if (!had_announce_list)
        {
          announce_list = tr_bencDictAddList (metainfo, "announce-list", 2);

          if (had_announce)
            {
              /* we're moving from an 'announce' to an 'announce-list',
               * so copy the old announce URL to the list */
              tr_benc * tier = tr_bencListAddList (announce_list, 1);
              tr_bencListAddStr (tier, announce);
              changed = true;
            }
        }

      /* If the user-specified URL isn't in the announce list yet, add it */
      if (!announce_list_has_url (announce_list, url))
        {
          tr_benc * tier = tr_bencListAddList (announce_list, 1);
          tr_bencListAddStr (tier, url);
          printf ("\tAdded \"%s\" to \"announce-list\" tier %zu\n", url, tr_bencListSize (announce_list));
          changed = true;
        }
    }

  return changed;
}
Esempio n. 6
0
static bool
replaceURL (tr_benc * metainfo, const char * in, const char * out)
{
  const char * str;
  tr_benc * announce_list;
  bool changed = false;

  if (tr_bencDictFindStr (metainfo, "announce", &str) && strstr (str, in))
    {
      char * newstr = replaceSubstr (str, in, out);
      printf ("\tReplaced in \"announce\": \"%s\" --> \"%s\"\n", str, newstr);
      tr_bencDictAddStr (metainfo, "announce", newstr);
      tr_free (newstr);
      changed = true;
    }

  if (tr_bencDictFindList (metainfo, "announce-list", &announce_list))
    {
      tr_benc * tier;
      int tierCount = 0;
      while ((tier = tr_bencListChild (announce_list, tierCount++)))
        {
          tr_benc * node;
          int nodeCount = 0;
          while ((node = tr_bencListChild (tier, nodeCount++)))
            {
              if (tr_bencGetStr (node, &str) && strstr (str, in))
                {
                  char * newstr = replaceSubstr (str, in, out);
                  printf ("\tReplaced in \"announce-list\" tier %d: \"%s\" --> \"%s\"\n", tierCount, str, newstr);
                  tr_bencFree (node);
                  tr_bencInitStr (node, newstr, -1);
                  tr_free (newstr);
                  changed = true;
                }
            }
        }
    }

  return changed;
}
Esempio n. 7
0
/* Convert to compact form */
static uint8_t *
parseOldPeers( tr_benc * bePeers,
               size_t *  byteCount )
{
    int       i;
    uint8_t * compact, *walk;
    const int peerCount = bePeers->val.l.count;

    assert( bePeers->type == TYPE_LIST );

    compact = tr_new( uint8_t, peerCount * 6 );

    for( i = 0, walk = compact; i < peerCount; ++i )
    {
        const char *   s;
        int64_t        itmp;
        struct in_addr addr;
        tr_port_t      port;
        tr_benc *      peer = &bePeers->val.l.vals[i];

        if( !tr_bencDictFindStr( peer, "ip",
                                 &s ) || tr_netResolve( s, &addr ) )
            continue;

        memcpy( walk, &addr, 4 );
        walk += 4;

        if( !tr_bencDictFindInt( peer, "port",
                                 &itmp ) || itmp < 0 || itmp > 0xffff )
            continue;

        port = htons( itmp );
        memcpy( walk, &port, 2 );
        walk += 2;
    }

    *byteCount = peerCount * 6;
    return compact;
}
Esempio n. 8
0
static int
testParse2( void )
{
    tr_benc top;
    tr_benc top2;
    int64_t intVal;
    const char * strVal;
    double realVal;
    bool boolVal;
    int len;
    char * benc;
    const uint8_t * end;

    tr_bencInitDict( &top, 0 );
    tr_bencDictAddBool( &top, "this-is-a-bool", true );
    tr_bencDictAddInt( &top, "this-is-an-int", 1234 );
    tr_bencDictAddReal( &top, "this-is-a-real", 0.5 );
    tr_bencDictAddStr( &top, "this-is-a-string", "this-is-a-string" );

    benc = tr_bencToStr( &top, TR_FMT_BENC, &len );
    check_streq( "d14:this-is-a-booli1e14:this-is-a-real8:0.50000016:this-is-a-string16:this-is-a-string14:this-is-an-inti1234ee", benc );
    check( !tr_bencParse( benc, benc+len, &top2, &end ) );
    check( (char*)end == benc + len );
    check( tr_bencIsDict( &top2 ) );
    check( tr_bencDictFindInt( &top, "this-is-an-int", &intVal ) );
    check_int_eq (1234, intVal);
    check( tr_bencDictFindBool( &top, "this-is-a-bool", &boolVal ) );
    check( boolVal == true );
    check( tr_bencDictFindStr( &top, "this-is-a-string", &strVal ) );
    check_streq ("this-is-a-string", strVal);
    check( tr_bencDictFindReal( &top, "this-is-a-real", &realVal ) );
    check_int_eq (50, (int)(realVal*100));

    tr_bencFree( &top2 );
    tr_free( benc );
    tr_bencFree( &top );

    return 0;
}
Esempio n. 9
0
static tr_pex*
listToPex( tr_benc * peerList, size_t * setme_len )
{
    size_t i;
    size_t n;
    const size_t len = tr_bencListSize( peerList );
    tr_pex * pex = tr_new0( tr_pex, len );

    for( i=n=0; i<len; ++i )
    {
        int64_t port;
        const char * ip;
        tr_address addr;
        tr_benc * peer = tr_bencListChild( peerList, i );

        if( peer == NULL )
            continue;
        if( !tr_bencDictFindStr( peer, "ip", &ip ) )
            continue;
        if( !tr_address_from_string( &addr, ip ) )
            continue;
        if( !tr_bencDictFindInt( peer, "port", &port ) )
            continue;
        if( ( port < 0 ) || ( port > USHRT_MAX ) )
            continue;
        if( !tr_address_is_valid_for_peers( &addr, port ) )
            continue;

        pex[n].addr = addr;
        pex[n].port = htons( (uint16_t)port );
        ++n;
    }

    *setme_len = n;
    return pex;
}
Esempio n. 10
0
static bool
removeURL (tr_benc * metainfo, const char * url)
{
  const char * str;
  tr_benc * announce_list;
  bool changed = false;

  if (tr_bencDictFindStr (metainfo, "announce", &str) && !strcmp (str, url))
    {
      printf ("\tRemoved \"%s\" from \"announce\"\n", str);
      tr_bencDictRemove (metainfo, "announce");
      changed = true;
    }

  if (tr_bencDictFindList (metainfo, "announce-list", &announce_list))
    {
      tr_benc * tier;
      int tierIndex = 0;
      while ((tier = tr_bencListChild (announce_list, tierIndex)))
        {
          tr_benc * node;
          int nodeIndex = 0;
          while ((node = tr_bencListChild (tier, nodeIndex)))
            {
              if (tr_bencGetStr (node, &str) && !strcmp (str, url))
                {
                  printf ("\tRemoved \"%s\" from \"announce-list\" tier #%d\n", str, (tierIndex+1));
                  tr_bencListRemove (tier, nodeIndex);
                  changed = true;
                }
              else ++nodeIndex;
            }

          if (tr_bencListSize (tier) == 0)
            {
              printf ("\tNo URLs left in tier #%d... removing tier\n", (tierIndex+1));
              tr_bencListRemove (announce_list, tierIndex);
            }
          else
            {
              ++tierIndex;
            }
        }

      if (tr_bencListSize (announce_list) == 0)
        {
          printf ("\tNo tiers left... removing announce-list\n");
          tr_bencDictRemove (metainfo, "announce-list");
        }
    }

  /* if we removed the "announce" field and there's still another track left,
   * use it as the "announce" field */
  if (changed && !tr_bencDictFindStr (metainfo, "announce", &str))
    {
      tr_benc * tier;
      tr_benc * node;

      if ((tier = tr_bencListChild (announce_list, 0)))
        {
          if ((node = tr_bencListChild (tier, 0)))
            {
              if (tr_bencGetStr (node, &str))
                {
                  tr_bencDictAddStr (metainfo, "announce", str);
                  printf ("\tAdded \"%s\" to announce\n", str);
                }
            }
        }
    }

  return changed;
}
Esempio n. 11
0
static uint64_t
loadProgress( tr_benc * dict, tr_torrent * tor )
{
    size_t i, n;
    uint64_t ret = 0;
    tr_benc * prog;
    const tr_info * inf = tr_torrentInfo( tor );

    for( i=0, n=inf->pieceCount; i<n; ++i )
        inf->pieces[i].timeChecked = 0;

    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &prog ) )
    {
        const char * err;
        const char * str;
        const uint8_t * raw;
        size_t rawlen;
        tr_benc * l;
        tr_benc * b;
        struct tr_bitset bitset = TR_BITSET_INIT;

        if( tr_bencDictFindList( prog, KEY_PROGRESS_CHECKTIME, &l ) )
        {
            /* per-piece timestamps were added in 2.20.
              
               If some of a file's pieces have been checked more recently than
               the file's mtime, and some lest recently, then that file will
               have a list containing timestamps for each piece.
              
               However, the most common use case is that the file doesn't change
               after it's downloaded. To reduce overhead in the .resume file,
               only a single timestamp is saved for the file if *all* or *none*
               of the pieces were tested more recently than the file's mtime. */

            tr_file_index_t fi;

            for( fi=0; fi<inf->fileCount; ++fi )
            {
                tr_benc * b = tr_bencListChild( l, fi );
                const tr_file * f = &inf->files[fi];
                tr_piece * p = &inf->pieces[f->firstPiece];
                const tr_piece * pend = &inf->pieces[f->lastPiece]+1;

                if( tr_bencIsInt( b ) )
                {
                    int64_t t;
                    tr_bencGetInt( b, &t );
                    for( ; p!=pend; ++p )
                        p->timeChecked = (time_t)t;
                }
                else if( tr_bencIsList( b ) )
                {
                    int i = 0;
                    int64_t offset = 0;
                    const int pieces = f->lastPiece + 1 - f->firstPiece;

                    tr_bencGetInt( tr_bencListChild( b, 0 ), &offset );

                    for( i=0; i<pieces; ++i )
                    {
                        int64_t t = 0;
                        tr_bencGetInt( tr_bencListChild( b, i+1 ), &t );
                        inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0);
                    }
                }
            }
        }
        else if( tr_bencDictFindList( prog, KEY_PROGRESS_MTIMES, &l ) )
        {
            tr_file_index_t fi;

            /* Before 2.20, we stored the files' mtimes in the .resume file.
               When loading the .resume file, a torrent's file would be flagged
               as untested if its stored mtime didn't match its real mtime. */

            for( fi=0; fi<inf->fileCount; ++fi )
            {
                int64_t t;

                if( tr_bencGetInt( tr_bencListChild( l, fi ), &t ) )
                {
                    const tr_file * f = &inf->files[fi];
                    tr_piece * p = &inf->pieces[f->firstPiece];
                    const tr_piece * pend = &inf->pieces[f->lastPiece];
                    const time_t mtime = tr_torrentGetFileMTime( tor, fi );
                    const time_t timeChecked = mtime==t ? mtime : 0;

                    for( ; p!=pend; ++p )
                        p->timeChecked = timeChecked;
                }
            }
        }

        err = NULL;

        if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS )))
        {
            if( !tr_bitsetFromBenc( &bitset, b ) )
                err = "Invalid value for PIECES";
        }
        else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) )
        {
            if( !strcmp( str, "all" ) )
                tr_bitsetSetHaveAll( &bitset );
            else
                err = "Invalid value for HAVE";
        }
        else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) )
        {
            bitset.bitfield.bits = (void*) raw;
            bitset.bitfield.byteCount = rawlen;
            bitset.bitfield.bitCount = rawlen * 8;
        }
        else err = "Couldn't find 'pieces' or 'have' or 'bitfield'";

        if( !err && !tr_cpBlockBitsetInit( &tor->completion, &bitset ) )
            err = "Error loading bitfield";
        if( err != NULL )
            tr_tordbg( tor, "Torrent needs to be verified - %s", err );

        ret = TR_FR_PROGRESS;
    }

    return ret;
}
Esempio n. 12
0
static const char*
getannounce( tr_info * inf, tr_benc * meta )
{
    const char *      str;
    tr_tracker_info * trackers = NULL;
    int               trackerCount = 0;
    tr_benc *         tiers;

    /* Announce-list */
    if( tr_bencDictFindList( meta, "announce-list", &tiers ) )
    {
        int       n;
        int       i, j, validTiers;
        const int numTiers = tr_bencListSize( tiers );

        n = 0;
        for( i = 0; i < numTiers; ++i )
            n += tr_bencListSize( tr_bencListChild( tiers, i ) );

        trackers = tr_new0( tr_tracker_info, n );

        for( i = 0, validTiers = 0; i < numTiers; ++i )
        {
            tr_benc * tier = tr_bencListChild( tiers, i );
            const int tierSize = tr_bencListSize( tier );
            tr_bool anyAdded = FALSE;
            for( j = 0; j < tierSize; ++j )
            {
                if( tr_bencGetStr( tr_bencListChild( tier, j ), &str ) )
                {
                    char * url = tr_strstrip( tr_strdup( str ) );
                    if( tr_urlIsValidTracker( url ) )
                    {
                        tr_tracker_info * t = trackers + trackerCount;
                        t->tier = validTiers;
                        t->announce = tr_strdup( url );
                        t->scrape = tr_convertAnnounceToScrape( url );
                        t->id = trackerCount;

                        anyAdded = TRUE;
                        ++trackerCount;
                    }
                    tr_free( url );
                }
            }

            if( anyAdded )
                ++validTiers;
        }

        /* did we use any of the tiers? */
        if( !trackerCount )
        {
            tr_free( trackers );
            trackers = NULL;
        }
    }

    /* Regular announce value */
    if( !trackerCount
      && tr_bencDictFindStr( meta, "announce", &str ) )
    {
        char * url = tr_strstrip( tr_strdup( str ) );
        if( tr_urlIsValidTracker( url ) )
        {
            trackers = tr_new0( tr_tracker_info, 1 );
            trackers[trackerCount].tier = 0;
            trackers[trackerCount].announce = tr_strdup( url );
            trackers[trackerCount].scrape = tr_convertAnnounceToScrape( url );
            trackers[trackerCount].id = 0;
            trackerCount++;
            /*fprintf( stderr, "single announce: [%s]\n", url );*/
        }
        tr_free( url );
    }

    inf->trackers = trackers;
    inf->trackerCount = trackerCount;

    return NULL;
}
Esempio n. 13
0
static uint64_t
loadFromFile( tr_torrent * tor,
              uint64_t     fieldsToLoad )
{
    int64_t      i;
    const char * str;
    uint64_t     fieldsLoaded = 0;
    char *       filename;
    tr_benc      top;

    filename = getResumeFilename( tor );

    if( tr_bencLoadFile( filename, &top ) )
    {
        tr_tordbg( tor, "Couldn't read \"%s\"; trying old format.",
                   filename );
        fieldsLoaded = tr_fastResumeLoad( tor, fieldsToLoad );

        if( ( fieldsLoaded != 0 ) && ( fieldsToLoad == ~(uint64_t)0 ) )
        {
            tr_torrentSaveResume( tor );
            tr_fastResumeRemove( tor );
            tr_tordbg( tor, "Migrated resume file to \"%s\"", filename );
        }

        tr_free( filename );
        return fieldsLoaded;
    }

    tr_tordbg( tor, "Read resume file \"%s\"", filename );

    if( ( fieldsToLoad & TR_FR_CORRUPT )
      && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) )
    {
        tor->corruptPrev = i;
        fieldsLoaded |= TR_FR_CORRUPT;
    }

    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) )
      && tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) )
    {
        tr_free( tor->downloadDir );
        tor->downloadDir = tr_strdup( str );
        fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
    }

    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
      && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) )
    {
        tor->downloadedPrev = i;
        fieldsLoaded |= TR_FR_DOWNLOADED;
    }

    if( ( fieldsToLoad & TR_FR_UPLOADED )
      && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) )
    {
        tor->uploadedPrev = i;
        fieldsLoaded |= TR_FR_UPLOADED;
    }

    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
      && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) )
    {
        tor->maxConnectedPeers = i;
        fieldsLoaded |= TR_FR_MAX_PEERS;
    }

    if( ( fieldsToLoad & TR_FR_RUN )
      && tr_bencDictFindInt( &top, KEY_PAUSED, &i ) )
    {
        tor->isRunning = i ? 0 : 1;
        fieldsLoaded |= TR_FR_RUN;
    }

    if( ( fieldsToLoad & TR_FR_ADDED_DATE )
      && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) )
    {
        tor->addedDate = i;
        fieldsLoaded |= TR_FR_ADDED_DATE;
    }

    if( ( fieldsToLoad & TR_FR_DONE_DATE )
      && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) )
    {
        tor->doneDate = i;
        fieldsLoaded |= TR_FR_DONE_DATE;
    }

    if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE )
      && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) )
    {
        tor->activityDate = i;
        fieldsLoaded |= TR_FR_ACTIVITY_DATE;
    }

    if( fieldsToLoad & TR_FR_PEERS )
        fieldsLoaded |= loadPeers( &top, tor );

    if( fieldsToLoad & TR_FR_PRIORITY )
        fieldsLoaded |= loadPriorities( &top, tor );

    if( fieldsToLoad & TR_FR_PROGRESS )
        fieldsLoaded |= loadProgress( &top, tor );

    if( fieldsToLoad & TR_FR_DND )
        fieldsLoaded |= loadDND( &top, tor );

    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
        fieldsLoaded |= loadSpeedLimits( &top, tor );

    tr_bencFree( &top );
    tr_free( filename );
    return fieldsLoaded;
}
Esempio n. 14
0
static const char*
getannounce( tr_info * inf,
             tr_benc * meta )
{
    const char *      str;
    tr_tracker_info * trackers = NULL;
    int               trackerCount = 0;
    tr_benc *         tiers;

    /* Announce-list */
    if( tr_bencDictFindList( meta, "announce-list", &tiers ) )
    {
        int       n;
        int       i, j;
        const int numTiers = tr_bencListSize( tiers );

        n = 0;
        for( i = 0; i < numTiers; ++i )
            n += tr_bencListSize( tr_bencListChild( tiers, i ) );

        trackers = tr_new0( tr_tracker_info, n );
        trackerCount = 0;

        for( i = 0; i < numTiers; ++i )
        {
            tr_benc * tier = tr_bencListChild( tiers, i );
            const int tierSize = tr_bencListSize( tier );
            for( j = 0; j < tierSize; ++j )
            {
                if( tr_bencGetStr( tr_bencListChild( tier, j ), &str ) )
                {
                    char * url = tr_strstrip( tr_strdup( str ) );
                    if( tr_httpIsValidURL( url ) )
                    {
                        tr_tracker_info * t = trackers + trackerCount++;
                        t->tier = i;
                        t->announce = tr_strdup( url );
                        t->scrape = announceToScrape( url );
                    }
                    tr_free( url );
                }
            }
        }

        /* did we use any of the tiers? */
        if( !trackerCount )
        {
            tr_free( trackers );
            trackers = NULL;
        }
    }

    /* Regular announce value */
    if( !trackerCount
      && tr_bencDictFindStr( meta, "announce", &str ) )
    {
        char * url = tr_strstrip( tr_strdup( str ) );
        if( tr_httpIsValidURL( url ) )
        {
            trackers = tr_new0( tr_tracker_info, 1 );
            trackers[trackerCount].tier = 0;
            trackers[trackerCount].announce = tr_strdup( url );
            trackers[trackerCount++].scrape = announceToScrape( url );
            /*fprintf( stderr, "single announce: [%s]\n", url );*/
        }
        tr_free( url );
    }

    inf->trackers = trackers;
    inf->trackerCount = trackerCount;

    return inf->trackerCount ? NULL : "announce";
}
Esempio n. 15
0
static void
on_announce_done( tr_session   * session,
                  bool           did_connect,
                  bool           did_timeout,
                  long           response_code,
                  const void   * msg,
                  size_t         msglen,
                  void         * vdata )
{
    tr_announce_response * response;
    struct announce_data * data = vdata;

    response = &data->response;
    response->did_connect = did_connect;
    response->did_timeout = did_timeout;
    dbgmsg( data->log_name, "Got announce response" );

    if( response_code != HTTP_OK )
    {
        const char * fmt = _( "Tracker gave HTTP response code %1$ld (%2$s)" );
        const char * response_str = tr_webGetResponseStr( response_code );
        response->errmsg = tr_strdup_printf( fmt, response_code, response_str );
    }
    else
    {
        tr_benc benc;
        const int benc_loaded = !tr_bencLoad( msg, msglen, &benc, NULL );

        if( getenv( "TR_CURL_VERBOSE" ) != NULL )
        {
            struct evbuffer * buf = tr_bencToBuf( &benc, TR_FMT_JSON );
            fprintf( stderr, "Announce response:\n< %s\n", evbuffer_pullup( buf, -1 ) );
            tr_free( buf );
        }

        if( benc_loaded && tr_bencIsDict( &benc ) )
        {
            int64_t i;
            size_t rawlen;
            tr_benc * tmp;
            const char * str;
            const uint8_t * raw;

            if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
                response->errmsg = tr_strdup( str );

            if( tr_bencDictFindStr( &benc, "warning message", &str ) )
                response->warning = tr_strdup( str );

            if( tr_bencDictFindInt( &benc, "interval", &i ) )
                response->interval = i;

            if( tr_bencDictFindInt( &benc, "min interval", &i ) )
                response->min_interval = i;

            if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
                response->tracker_id_str = tr_strdup( str );

            if( tr_bencDictFindInt( &benc, "complete", &i ) )
                response->seeders = i;

            if( tr_bencDictFindInt( &benc, "incomplete", &i ) )
                response->leechers = i;

            if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
                response->downloads = i;

            if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) ) {
                dbgmsg( data->log_name, "got a peers6 length of %zu", rawlen );
                response->pex6 = tr_peerMgrCompact6ToPex( raw, rawlen,
                                              NULL, 0, &response->pex6_count );
            }

            if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) ) {
                dbgmsg( data->log_name, "got a compact peers length of %zu", rawlen );
                response->pex = tr_peerMgrCompactToPex( raw, rawlen,
                                               NULL, 0, &response->pex_count );
            } else if( tr_bencDictFindList( &benc, "peers", &tmp ) ) {
                response->pex = listToPex( tmp, &response->pex_count );
                dbgmsg( data->log_name, "got a peers list with %zu entries",
                        response->pex_count );
            }
        }

        if( benc_loaded )
            tr_bencFree( &benc );
    }

    tr_runInEventThread( session, on_announce_done_eventthread, data );
}
Esempio n. 16
0
static void
on_scrape_done( tr_session   * session,
                bool           did_connect,
                bool           did_timeout,
                long           response_code,
                const void   * msg,
                size_t         msglen,
                void         * vdata )
{
    tr_scrape_response * response;
    struct scrape_data * data = vdata;

    response = &data->response;
    response->did_connect = did_connect;
    response->did_timeout = did_timeout;
    dbgmsg( data->log_name, "Got scrape response for \"%s\"", response->url );

    if( response_code != HTTP_OK )
    {
        const char * fmt = _( "Tracker gave HTTP response code %1$ld (%2$s)" );
        const char * response_str = tr_webGetResponseStr( response_code );
        response->errmsg = tr_strdup_printf( fmt, response_code, response_str );
    }
    else
    {
        tr_benc top;
        int64_t intVal;
        tr_benc * files;
        const char * str;
        const int benc_loaded = !tr_bencLoad( msg, msglen, &top, NULL );
        if( benc_loaded )
        {
            if( tr_bencDictFindStr( &top, "failure reason", &str ) )
                response->errmsg = tr_strdup( str );

            if( tr_bencDictFindInt( &top, "min_request_interval", &intVal ) )
                response->min_request_interval = intVal;

            if( tr_bencDictFindDict( &top, "files", &files ) )
            {
                int i = 0;

                for( ;; )
                {
                    int j;
                    tr_benc * val;
                    const char * key;

                    /* get the next "file" */
                    if( !tr_bencDictChild( files, i++, &key, &val ) )
                        break;

                    /* populate the corresponding row in our response array */
                    for( j=0; j<response->row_count; ++j )
                    {
                        struct tr_scrape_response_row * row = &response->rows[j];
                        if( !memcmp( key, row->info_hash, SHA_DIGEST_LENGTH ) )
                        {
                            if( tr_bencDictFindInt( val, "complete", &intVal ) )
                                row->seeders = intVal;
                            if( tr_bencDictFindInt( val, "incomplete", &intVal ) )
                                row->leechers = intVal;
                            if( tr_bencDictFindInt( val, "downloaded", &intVal ) )
                                row->downloads = intVal;
                            if( tr_bencDictFindInt( val, "downloaders", &intVal ) )
                                row->downloaders = intVal;
                            break;
                        }
                    }
                }
            }

            tr_bencFree( &top );
        }
    }

    tr_runInEventThread( session, on_scrape_done_eventthread, data );
}
Esempio n. 17
0
static void
tr_sessionInitImpl( void * vdata )
{
    int64_t i;
    int64_t j;
    double  d;
    tr_bool found;
    const char * str;
    tr_benc settings;
    char * filename;
    struct init_data * data = vdata;
    tr_benc * clientSettings = data->clientSettings;
    tr_session * session = data->session;

    assert( tr_amInEventThread( session ) );
    assert( tr_bencIsDict( clientSettings ) );

    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p", session->bandwidth );

    tr_bencInitDict( &settings, 0 );
    tr_sessionGetDefaultSettings( &settings );
    tr_bencMergeDicts( &settings, clientSettings );

#ifndef WIN32
    /* Don't exit when writing on a broken socket */
    signal( SIGPIPE, SIG_IGN );
#endif

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i );
    assert( found );
    session->peerLimitPerTorrent = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_MSGLEVEL, &i );
    assert( found );
    tr_setMessageLevel( i );
    tr_setMessageQueuing( data->messageQueuingEnabled );


    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEX_ENABLED, &i );
    assert( found );
    session->isPexEnabled = i != 0;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ENCRYPTION, &i );
    assert( found );
    assert( tr_isEncryptionMode( i ) );
    session->encryptionMode = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PREALLOCATION, &i );
    assert( found );
    assert( tr_isPreallocationMode( i ) );
    session->preallocationMode = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &i );
    assert( found );
    session->peerSocketTOS = i;

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str );
    assert( found );
    session->downloadDir = tr_strdup( str );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_ENABLED, &i );
    assert( found );
    session->isProxyEnabled = i != 0;

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY, &str );
    assert( found );
    session->proxy = tr_strdup( str );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_PORT, &i );
    assert( found );
    session->proxyPort = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_TYPE, &i );
    assert( found );
    session->proxyType = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_AUTH_ENABLED, &i );
    assert( found );
    session->isProxyAuthEnabled = i != 0;

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_USERNAME, &str );
    assert( found );
    session->proxyUsername = tr_strdup( str );

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_PASSWORD, &str );
    assert( found );
    session->proxyPassword = tr_strdup( str );

    session->so_sndbuf = 1500 * 3; /* 3x MTU for most ethernet/wireless */
    session->so_rcvbuf = 8192;

    tr_setConfigDir( session, data->configDir );

    tr_trackerSessionInit( session );
    assert( session->tracker != NULL );

    session->peerMgr = tr_peerMgrNew( session );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_LAZY_BITFIELD, &i );
    assert( found );
    session->useLazyBitfield = i != 0;

    /* Initialize rate and file descripts controls */

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i );
    assert( found );
    session->openFileLimit = i;
    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &j );
    assert( found );
    tr_fdInit( session->openFileLimit, j );

    /**
    *** random port
    **/

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ENABLED, &i );
    assert( found );
    session->isPortRandom = i != 0;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i );
    assert( found );
    session->randomPortLow = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i );
    assert( found );
    session->randomPortHigh = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PORT_FORWARDING, &i )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT, &j );
    assert( found );
    session->peerPort = session->isPortRandom ? getRandomPort( session ) : j;
    session->shared = tr_sharedInit( session, i, session->peerPort );
    session->isPortSet = session->isPortRandom || j>0;

    /**
    **/

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, &i );
    assert( found );
    session->uploadSlotsPerTorrent = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED, &i )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED_ENABLED, &j );
    assert( found );
    tr_sessionSetSpeedLimit( session, TR_UP, i );
    tr_sessionSetSpeedLimitEnabled( session, TR_UP, j );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED, &i )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED_ENABLED, &j );
    assert( found );
    tr_sessionSetSpeedLimit( session, TR_DOWN, i );
    tr_sessionSetSpeedLimitEnabled( session, TR_DOWN, j );

    found = tr_bencDictFindDouble( &settings, TR_PREFS_KEY_RATIO, &d )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_RATIO_ENABLED, &j );
    assert( found );
    tr_sessionSetRatioLimit( session, d );
    tr_sessionSetRatioLimited( session, j );

    /* initialize the blocklist */
    filename = tr_buildPath( session->configDir, "blocklists", NULL );
    tr_mkdirp( filename, 0777 );
    tr_free( filename );
    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &i );
    assert( found );
    session->isBlocklistEnabled = i;
    loadBlocklists( session );

    session->rpcServer = tr_rpcInit( session, &settings );

    tr_bencFree( &settings );

    assert( tr_isSession( session ) );

    /* first %s is the application name
       second %s is the version number */
    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );

    tr_statsInit( session );
    session->web = tr_webInit( session );
    metainfoLookupRescan( session );
    session->isWaiting = FALSE;
    dbgmsg( "returning session %p; session->tracker is %p", session, session->tracker );
}
Esempio n. 18
0
static uint64_t
loadFromFile( tr_torrent * tor,
              uint64_t     fieldsToLoad )
{
    int64_t  i;
    const char * str;
    uint64_t fieldsLoaded = 0;
    char * filename;
    tr_benc top;
    tr_bool boolVal;
    const tr_bool  wasDirty = tor->isDirty;

    assert( tr_isTorrent( tor ) );

    filename = getResumeFilename( tor );

    if( tr_bencLoadFile( &top, TR_FMT_BENC, filename ) )
    {
        tr_tordbg( tor, "Couldn't read \"%s\"", filename );

        tr_free( filename );
        return fieldsLoaded;
    }

    tr_tordbg( tor, "Read resume file \"%s\"", filename );

    if( ( fieldsToLoad & TR_FR_CORRUPT )
      && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) )
    {
        tor->corruptPrev = i;
        fieldsLoaded |= TR_FR_CORRUPT;
    }

    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) )
      && ( tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) )
      && ( str && *str ) )
    {
        tr_free( tor->downloadDir );
        tor->downloadDir = tr_strdup( str );
        fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
    }

    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_INCOMPLETE_DIR ) )
      && ( tr_bencDictFindStr( &top, KEY_INCOMPLETE_DIR, &str ) )
      && ( str && *str ) )
    {
        tr_free( tor->incompleteDir );
        tor->incompleteDir = tr_strdup( str );
        fieldsLoaded |= TR_FR_INCOMPLETE_DIR;
    }

    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
      && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) )
    {
        tor->downloadedPrev = i;
        fieldsLoaded |= TR_FR_DOWNLOADED;
    }

    if( ( fieldsToLoad & TR_FR_UPLOADED )
      && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) )
    {
        tor->uploadedPrev = i;
        fieldsLoaded |= TR_FR_UPLOADED;
    }

    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
      && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) )
    {
        tor->maxConnectedPeers = i;
        fieldsLoaded |= TR_FR_MAX_PEERS;
    }

    if( ( fieldsToLoad & TR_FR_RUN )
      && tr_bencDictFindBool( &top, KEY_PAUSED, &boolVal ) )
    {
        tor->isRunning = !boolVal;
        fieldsLoaded |= TR_FR_RUN;
    }

    if( ( fieldsToLoad & TR_FR_ADDED_DATE )
      && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) )
    {
        tor->addedDate = i;
        fieldsLoaded |= TR_FR_ADDED_DATE;
    }

    if( ( fieldsToLoad & TR_FR_DONE_DATE )
      && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) )
    {
        tor->doneDate = i;
        fieldsLoaded |= TR_FR_DONE_DATE;
    }

    if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE )
      && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) )
    {
        tr_torrentSetActivityDate( tor, i );
        fieldsLoaded |= TR_FR_ACTIVITY_DATE;
    }

    if( ( fieldsToLoad & TR_FR_TIME_SEEDING )
      && tr_bencDictFindInt( &top, KEY_TIME_SEEDING, &i ) )
    {
        tor->secondsSeeding = i;
        fieldsLoaded |= TR_FR_TIME_SEEDING;
    }

    if( ( fieldsToLoad & TR_FR_TIME_DOWNLOADING )
      && tr_bencDictFindInt( &top, KEY_TIME_DOWNLOADING, &i ) )
    {
        tor->secondsDownloading = i;
        fieldsLoaded |= TR_FR_TIME_DOWNLOADING;
    }

    if( ( fieldsToLoad & TR_FR_BANDWIDTH_PRIORITY )
      && tr_bencDictFindInt( &top, KEY_BANDWIDTH_PRIORITY, &i )
      && tr_isPriority( i ) )
    {
        tr_torrentSetPriority( tor, i );
        fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY;
    }

    if( fieldsToLoad & TR_FR_PEERS )
        fieldsLoaded |= loadPeers( &top, tor );

    if( fieldsToLoad & TR_FR_FILE_PRIORITIES )
        fieldsLoaded |= loadFilePriorities( &top, tor );

    if( fieldsToLoad & TR_FR_PROGRESS )
        fieldsLoaded |= loadProgress( &top, tor );

    if( fieldsToLoad & TR_FR_DND )
        fieldsLoaded |= loadDND( &top, tor );

    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
        fieldsLoaded |= loadSpeedLimits( &top, tor );

    if( fieldsToLoad & TR_FR_RATIOLIMIT )
        fieldsLoaded |= loadRatioLimits( &top, tor );

    if( fieldsToLoad & TR_FR_IDLELIMIT )
        fieldsLoaded |= loadIdleLimits( &top, tor );

    /* loading the resume file triggers of a lot of changes,
     * but none of them needs to trigger a re-saving of the
     * same resume information... */
    tor->isDirty = wasDirty;

    tr_bencFree( &top );
    tr_free( filename );
    return fieldsLoaded;
}
Esempio n. 19
0
int
main( int argc, char ** argv )
{
    int c;
    const char * optarg;
    tr_benc settings;
    tr_bool boolVal;
    tr_bool loaded;
    tr_bool foreground = FALSE;
    tr_bool dumpSettings = FALSE;
    const char * configDir = NULL;
    const char * pid_filename;
    dtr_watchdir * watchdir = NULL;
    FILE * logfile = NULL;
    tr_bool pidfile_created = FALSE;

    signal( SIGINT, gotsig );
    signal( SIGTERM, gotsig );
#ifndef WIN32
    signal( SIGHUP, gotsig );
#endif

    /* load settings from defaults + config file */
    tr_bencInitDict( &settings, 0 );
    tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_ENABLED, TRUE );
    configDir = getConfigDir( argc, (const char**)argv );
    loaded = tr_sessionLoadSettings( &settings, configDir, MY_NAME );

    /* overwrite settings from the comamndline */
    tr_optind = 1;
    while(( c = tr_getopt( getUsage(), argc, (const char**)argv, options, &optarg ))) {
        switch( c ) {
            case 'a': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, optarg );
                      tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, TRUE );
                      break;
            case 'b': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE );
                      break;
            case 'B': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE );
                      break;
            case 'c': tr_bencDictAddStr( &settings, PREF_KEY_DIR_WATCH, optarg );
                      tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, TRUE );
                      break;
            case 'C': tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, FALSE );
                      break;
            case 941: tr_bencDictAddStr( &settings, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
                      tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE );
                      break;
            case 942: tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE );
                      break;
            case 'd': dumpSettings = TRUE;
                      break;
            case 'e': logfile = fopen( optarg, "a+" );
                      if( logfile == NULL )
                          fprintf( stderr, "Couldn't open \"%s\": %s\n", optarg, tr_strerror( errno ) );
                      break;
            case 'f': foreground = TRUE;
                      break;
            case 'g': /* handled above */
                      break;
            case 'V': /* version */
                      fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
                      exit( 0 );
            case 'o': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, TRUE );
                      break;
            case 'O': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, FALSE );
                      break;
            case 'p': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_PORT, atoi( optarg ) );
                      break;
            case 't': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, TRUE );
                      break;
            case 'T': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, FALSE );
                      break;
            case 'u': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_USERNAME, optarg );
                      break;
            case 'v': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, optarg );
                      break;
            case 'w': tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, optarg );
                      break;
            case 'P': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
                      break;
            case 'm': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
                      break;
            case 'M': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
                      break;
            case 'L': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( optarg ) );
                      break;
            case 'l': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( optarg ) );
                      break;
            case 800: paused = TRUE;
                      break;
            case 910: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED );
                      break;
            case 911: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
                      break;
            case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED );
                      break;
            case 'i': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, optarg );
                      break;
            case 'I': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, optarg );
                      break;
            case 'r': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, optarg );
                      break;
            case 953: tr_bencDictAddReal( &settings, TR_PREFS_KEY_RATIO, atof(optarg) );
                      tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, TRUE );
                      break;
            case 954: tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, FALSE );
                      break;
            case 'x': tr_bencDictAddStr( &settings, PREF_KEY_PIDFILE, optarg );
                      break;
            case 'y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, TRUE );
                      break;
            case 'Y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, FALSE );
                      break;
            case 810: tr_bencDictAddInt( &settings,  TR_PREFS_KEY_MSGLEVEL, TR_MSG_ERR );
                      break;
            case 811: tr_bencDictAddInt( &settings,  TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF );
                      break;
            case 812: tr_bencDictAddInt( &settings,  TR_PREFS_KEY_MSGLEVEL, TR_MSG_DBG );
                      break;
            default:  showUsage( );
                      break;
        }
    }

    if( foreground && !logfile )
        logfile = stderr;

    if( !loaded )
    {
        printMessage( logfile, TR_MSG_ERR, MY_NAME, "Error loading config file -- exiting.", __FILE__, __LINE__ );
        return -1;
    }

    if( dumpSettings )
    {
        char * str = tr_bencToStr( &settings, TR_FMT_JSON, NULL );
        fprintf( stderr, "%s", str );
        tr_free( str );
        return 0;
    }

    if( !foreground && tr_daemon( TRUE, FALSE ) < 0 )
    {
        char buf[256];
        tr_snprintf( buf, sizeof( buf ), "Failed to daemonize: %s", tr_strerror( errno ) );
        printMessage( logfile, TR_MSG_ERR, MY_NAME, buf, __FILE__, __LINE__ );
        exit( 1 );
    }

    /* start the session */
    tr_formatter_mem_init( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR );
    tr_formatter_size_init( DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR );
    tr_formatter_speed_init( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR );
    mySession = tr_sessionInit( "daemon", configDir, TRUE, &settings );
    tr_ninf( NULL, "Using settings from \"%s\"", configDir );
    tr_sessionSaveSettings( mySession, configDir, &settings );

    pid_filename = NULL;
    tr_bencDictFindStr( &settings, PREF_KEY_PIDFILE, &pid_filename );
    if( pid_filename && *pid_filename )
    {
        FILE * fp = fopen( pid_filename, "w+" );
        if( fp != NULL )
        {
            fprintf( fp, "%d", (int)getpid() );
            fclose( fp );
            tr_inf( "Saved pidfile \"%s\"", pid_filename );
            pidfile_created = TRUE;
        }
        else
            tr_err( "Unable to save pidfile \"%s\": %s", pid_filename, strerror( errno ) );
    }

    if( tr_bencDictFindBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &boolVal ) && boolVal )
        tr_ninf( MY_NAME, "requiring authentication" );

    /* maybe add a watchdir */
    {
        const char * dir;

        if( tr_bencDictFindBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, &boolVal )
            && boolVal
            && tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir )
            && dir
            && *dir )
        {
            tr_inf( "Watching \"%s\" for new .torrent files", dir );
            watchdir = dtr_watchdir_new( mySession, dir, onFileAdded );
        }
    }

    /* load the torrents */
    {
        tr_torrent ** torrents;
        tr_ctor * ctor = tr_ctorNew( mySession );
        if( paused )
            tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
        torrents = tr_sessionLoadTorrents( mySession, ctor, NULL );
        tr_free( torrents );
        tr_ctorFree( ctor );
    }

#ifdef HAVE_SYSLOG
    if( !foreground )
        openlog( MY_NAME, LOG_CONS|LOG_PID, LOG_DAEMON );
#endif

    while( !closing ) {
        tr_wait_msec( 1000 ); /* sleep one second */
        dtr_watchdir_update( watchdir );
        pumpLogMessages( logfile );
    }

    /* shutdown */
#if HAVE_SYSLOG
    if( !foreground )
    {
        syslog( LOG_INFO, "%s", "Closing session" );
        closelog( );
    }
#endif

    printf( "Closing transmission session..." );
    tr_sessionSaveSettings( mySession, configDir, &settings );
    dtr_watchdir_free( watchdir );
    tr_sessionClose( mySession );
    printf( " done.\n" );

    /* cleanup */
    if( pidfile_created )
        remove( pid_filename );
    tr_bencFree( &settings );
    return 0;
}