Update 2005-11-01
16
Jamfile
|
@ -15,14 +15,14 @@ if $(OS) = MACOSX
|
|||
macosx/English.lproj/MainMenu.nib/classes.nib
|
||||
macosx/English.lproj/MainMenu.nib/info.nib
|
||||
macosx/English.lproj/MainMenu.nib/keyedobjects.nib
|
||||
macosx/Images/Info.tiff
|
||||
macosx/Images/Open.tiff
|
||||
macosx/Images/Progress.tiff
|
||||
macosx/Images/Remove.tiff
|
||||
macosx/Images/Resume.tiff
|
||||
macosx/Images/RevealOff.tiff
|
||||
macosx/Images/RevealOn.tiff
|
||||
macosx/Images/Stop.tiff
|
||||
macosx/Images/Info.png
|
||||
macosx/Images/Open.png
|
||||
macosx/Images/PauseOff.png
|
||||
macosx/Images/PauseOn.png
|
||||
macosx/Images/Progress.png
|
||||
macosx/Images/Remove.png
|
||||
macosx/Images/RevealOff.png
|
||||
macosx/Images/RevealOn.png
|
||||
macosx/Images/Transmission.icns
|
||||
macosx/Images/TransmissionDocument.icns
|
||||
macosx/Info.plist
|
||||
|
|
7
Jamrules
|
@ -7,12 +7,12 @@ if ! $(DEFINES)
|
|||
|
||||
VERSION_MAJOR = 0 ;
|
||||
VERSION_MINOR = 3 ;
|
||||
VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ;
|
||||
# VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ;
|
||||
VERSION_STRING = 0.4-cvs ;
|
||||
|
||||
DEFINES += VERSION_MAJOR=$(VERSION_MAJOR)
|
||||
VERSION_MINOR=$(VERSION_MINOR)
|
||||
VERSION_STRING=\\\"$(VERSION_STRING)\\\" ;
|
||||
CCFLAGS = -g -Wall -W ;
|
||||
OPTIM = -O3 ;
|
||||
RM = rm -Rf ;
|
||||
|
||||
|
@ -27,6 +27,7 @@ if $(OS) = MACOSX
|
|||
# Use libtool to build static libraries (ar does not handle
|
||||
# universal binaries)
|
||||
RANLIB = ;
|
||||
NOARSCAN = 1 ;
|
||||
NOARUPDATE = 1 ;
|
||||
actions Archive
|
||||
{
|
||||
|
@ -36,10 +37,12 @@ if $(OS) = MACOSX
|
|||
rule OSXInfoPlist
|
||||
{
|
||||
Depends $(1) : $(2) ;
|
||||
Depends $(1) : Jamrules ;
|
||||
Clean clean : $(1) ;
|
||||
}
|
||||
actions OSXInfoPlist
|
||||
{
|
||||
$(RM) $(1)
|
||||
sed "s/%%VERSION%%/$(VERSION_STRING)/" < $(2) > $(1)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,82 @@
|
|||
#! /bin/sh
|
||||
|
||||
#
|
||||
# Functions
|
||||
#
|
||||
usage()
|
||||
{
|
||||
cat << EOF
|
||||
|
||||
OpenSSL options:
|
||||
--disable-openssl Disable OpenSSL, use built-in SHA1 implementation
|
||||
--openssl-prefix=PATH Location of OpenSSL headers and library
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command (default "cc")
|
||||
CFLAGS C compiler flags (default "-g -Wall -W")
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
openssl_test()
|
||||
{
|
||||
cat > testconf.c << EOF
|
||||
#include <stdio.h>
|
||||
#include <openssl/sha.h>
|
||||
int main()
|
||||
{
|
||||
SHA1( 0, 0, 0 );
|
||||
}
|
||||
EOF
|
||||
if [ -n "$PREFIX" ]; then
|
||||
TMPFLAGS="-I$PREFIX/include -L$PREFIX/lib"
|
||||
fi
|
||||
if $CC $TMPFLAGS -o testconf testconf.c -lcrypto > /dev/null 2>&1
|
||||
then
|
||||
echo "OpenSSL: yes"
|
||||
DEFINES="$DEFINES HAVE_OPENSSL"
|
||||
LINKLIBS="$LINKLIBS -lcrypto"
|
||||
else
|
||||
echo "OpenSSL: no, using built-in SHA1 implementation"
|
||||
fi
|
||||
rm -f testconf.c testconf
|
||||
}
|
||||
|
||||
#
|
||||
# Defaults settings
|
||||
#
|
||||
CC="${CC-cc}"
|
||||
CFLAGS="${CFLAGS--g -Wall -W}"
|
||||
# For > 2 GB files
|
||||
DEFINES="_FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE"
|
||||
|
||||
# For asprintf
|
||||
DEFINES="$DEFINES _GNU_SOURCE"
|
||||
openssl_disable=0
|
||||
|
||||
#
|
||||
# Parse options
|
||||
#
|
||||
while [ $# -ne 0 ]; do
|
||||
param=`expr "opt$1" : 'opt[^=]*=\(.*\)'`
|
||||
|
||||
case "x$1" in
|
||||
x--disable-openssl)
|
||||
openssl_disable="1";
|
||||
;;
|
||||
x--openssl-prefix=*)
|
||||
OPENSSL_PREFIX="$param";
|
||||
;;
|
||||
x--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
#
|
||||
# System-specific flags
|
||||
#
|
||||
SYSTEM=`uname -s`
|
||||
case $SYSTEM in
|
||||
BeOS)
|
||||
|
@ -55,31 +125,32 @@ case $SYSTEM in
|
|||
esac
|
||||
echo "System: $SYSTEM"
|
||||
|
||||
# Check for OpenSSL
|
||||
cat > testconf.c << EOF
|
||||
#include <stdio.h>
|
||||
#include <openssl/sha.h>
|
||||
int main()
|
||||
{
|
||||
SHA1( 0, 0, 0 );
|
||||
}
|
||||
EOF
|
||||
if cc -o testconf testconf.c -lcrypto > /dev/null 2>&1
|
||||
then
|
||||
echo "OpenSSL: yes"
|
||||
DEFINES="$DEFINES HAVE_OPENSSL"
|
||||
LINKLIBS="$LINKLIBS -lcrypto"
|
||||
else
|
||||
#
|
||||
# OpenSSL settings
|
||||
#
|
||||
if [ ${openssl_disable} = 1 ]; then
|
||||
echo "OpenSSL: no, using built-in SHA1 implementation"
|
||||
else
|
||||
openssl_test
|
||||
fi
|
||||
rm -f testconf.c testconf
|
||||
|
||||
#
|
||||
# Generate config.jam
|
||||
#
|
||||
rm -f config.jam
|
||||
cat << EOF > config.jam
|
||||
DEFINES = $DEFINES ;
|
||||
LINKLIBS = $LINKLIBS ;
|
||||
cat > config.jam << EOF
|
||||
CC = $CC ;
|
||||
LINK = $CC ;
|
||||
CCFLAGS = $CFLAGS ;
|
||||
DEFINES = $DEFINES ;
|
||||
LINKLIBS = $LINKLIBS ;
|
||||
EOF
|
||||
if [ -n "$OPENSSL_PREFIX" ]; then
|
||||
cat >> config.jam << EOF
|
||||
HDRS += $OPENSSL_PREFIX/include ;
|
||||
LINKFLAGS += -L$OPENSSL_PREFIX/lib ;
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "To build Transmission, run 'jam'."
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
* - 4 bytes * number of pieces (byte aligned): the pieces that have
|
||||
* been completed or started in each slot
|
||||
*
|
||||
* The resume file is located in ~/.transmission/. Its name is
|
||||
* "resume.<hash>".
|
||||
* The name of the resume file is "resume.<hash>".
|
||||
*
|
||||
* All values are stored in the native endianness. Moving a
|
||||
* libtransmission resume file from an architecture to another will not
|
||||
|
@ -39,21 +38,13 @@
|
|||
* so the files will be scanned).
|
||||
**********************************************************************/
|
||||
|
||||
static char * fastResumeFolderName()
|
||||
{
|
||||
char * ret;
|
||||
asprintf( &ret, "%s/.transmission", getenv( "HOME" ) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char * fastResumeFileName( tr_io_t * io )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
char * ret, * p;
|
||||
int i;
|
||||
|
||||
p = fastResumeFolderName();
|
||||
asprintf( &ret, "%s/resume.%40d", p, 0 );
|
||||
free( p );
|
||||
asprintf( &ret, "%s/resume.%40d", tor->prefsDirectory, 0 );
|
||||
|
||||
p = &ret[ strlen( ret ) - 2 * SHA_DIGEST_LENGTH ];
|
||||
for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
|
||||
|
@ -121,11 +112,6 @@ static void fastResumeSave( tr_io_t * io )
|
|||
return;
|
||||
}
|
||||
|
||||
/* Create folder if missing */
|
||||
path = fastResumeFolderName();
|
||||
mkdir( path, 0755 );
|
||||
free( path );
|
||||
|
||||
/* Create/overwrite the resume file */
|
||||
path = fastResumeFileName( io );
|
||||
if( !( file = fopen( path, "w" ) ) )
|
||||
|
@ -135,7 +121,6 @@ static void fastResumeSave( tr_io_t * io )
|
|||
free( path );
|
||||
return;
|
||||
}
|
||||
free( path );
|
||||
|
||||
/* Write format version */
|
||||
fwrite( &version, 4, 1, file );
|
||||
|
@ -160,6 +145,9 @@ static void fastResumeSave( tr_io_t * io )
|
|||
fwrite( io->slotPiece, 4, inf->pieceCount, file );
|
||||
|
||||
fclose( file );
|
||||
|
||||
tr_dbg( "Resume file '%s' written", path );
|
||||
free( path );
|
||||
}
|
||||
|
||||
static int fastResumeLoad( tr_io_t * io )
|
||||
|
@ -184,6 +172,7 @@ static int fastResumeLoad( tr_io_t * io )
|
|||
free( path );
|
||||
return 1;
|
||||
}
|
||||
tr_dbg( "Resume file '%s' loaded", path );
|
||||
free( path );
|
||||
|
||||
/* Check the size */
|
||||
|
|
|
@ -155,6 +155,7 @@ struct tr_torrent_s
|
|||
|
||||
tr_tracker_t * tracker;
|
||||
tr_io_t * io;
|
||||
uint64_t stopDate;
|
||||
|
||||
int bindSocket;
|
||||
int bindPort;
|
||||
|
@ -164,6 +165,8 @@ struct tr_torrent_s
|
|||
uint64_t dates[10];
|
||||
uint64_t downloaded[10];
|
||||
uint64_t uploaded[10];
|
||||
|
||||
char * prefsDirectory;
|
||||
};
|
||||
|
||||
#include "utils.h"
|
||||
|
@ -179,6 +182,7 @@ struct tr_handle_s
|
|||
int bindPort;
|
||||
|
||||
char id[21];
|
||||
char prefsDirectory[256];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -100,6 +100,7 @@ static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
|
|||
|
||||
#include "peermessages.h"
|
||||
#include "peerutils.h"
|
||||
#include "peerparse.h"
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerAddOld
|
||||
|
@ -232,6 +233,11 @@ void tr_peerPulse( tr_torrent_t * tor )
|
|||
}
|
||||
}
|
||||
|
||||
if( tor->status & TR_STATUS_STOPPING )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for incoming connections */
|
||||
if( tor->bindSocket > -1 &&
|
||||
tor->peerCount < TR_MAX_PEER_COUNT &&
|
||||
|
@ -331,7 +337,7 @@ void tr_peerPulse( tr_torrent_t * tor )
|
|||
}
|
||||
peer->date = tr_date();
|
||||
peer->pos += ret;
|
||||
if( parseMessage( tor, peer, ret ) )
|
||||
if( parseBuf( tor, peer, ret ) )
|
||||
{
|
||||
goto dropPeer;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* This file handles all outgoing messages
|
||||
**********************************************************************/
|
||||
|
||||
static uint8_t * messagesPending( tr_peer_t * peer, int * size )
|
||||
{
|
||||
if( peer->outBlockSending || peer->outMessagesPos < 1 )
|
||||
|
|
|
@ -0,0 +1,529 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* This file handles all incoming messages
|
||||
**********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* parseChoke
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static inline int parseChoke( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
int len, int choking )
|
||||
{
|
||||
tr_request_t * r;
|
||||
int i;
|
||||
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET %schoke, invalid", choking ? "" : "un" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer_dbg( "GET %schoke", choking ? "" : "un" );
|
||||
|
||||
peer->peerChoking = choking;
|
||||
|
||||
if( choking )
|
||||
{
|
||||
/* Discard all pending requests */
|
||||
for( i = 0; i < peer->inRequestCount; i++ )
|
||||
{
|
||||
r = &peer->inRequests[i];
|
||||
if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
|
||||
{
|
||||
tor->blockHave[tr_block(r->index,r->begin)]--;
|
||||
}
|
||||
}
|
||||
peer->inRequestCount = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* parseInterested
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static inline int parseInterested( tr_peer_t * peer, int len,
|
||||
int interested )
|
||||
{
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET %sinterested, invalid", interested ? "" : "un" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer_dbg( "GET %sinterested", interested ? "" : "un" );
|
||||
|
||||
peer->peerInterested = interested;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* parseHave
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static inline int parseHave( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
uint32_t piece;
|
||||
|
||||
if( len != 5 )
|
||||
{
|
||||
peer_dbg( "GET have, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
TR_NTOHL( p, piece );
|
||||
|
||||
peer_dbg( "GET have %d", piece );
|
||||
|
||||
if( !peer->bitfield )
|
||||
{
|
||||
peer->bitfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
|
||||
}
|
||||
tr_bitfieldAdd( peer->bitfield, piece );
|
||||
updateInterest( tor, peer );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
int bitfieldSize;
|
||||
|
||||
bitfieldSize = ( inf->pieceCount + 7 ) / 8;
|
||||
|
||||
if( len != 1 + bitfieldSize )
|
||||
{
|
||||
peer_dbg( "GET bitfield, wrong size" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Make sure the spare bits are unset */
|
||||
if( ( inf->pieceCount & 0x7 ) )
|
||||
{
|
||||
uint8_t lastByte;
|
||||
|
||||
lastByte = p[bitfieldSize-1];
|
||||
lastByte <<= inf->pieceCount & 0x7;
|
||||
lastByte &= 0xFF;
|
||||
|
||||
if( lastByte )
|
||||
{
|
||||
peer_dbg( "GET bitfield, spare bits set" );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
peer_dbg( "GET bitfield, ok" );
|
||||
|
||||
if( !peer->bitfield )
|
||||
{
|
||||
peer->bitfield = malloc( bitfieldSize );
|
||||
}
|
||||
memcpy( peer->bitfield, p, bitfieldSize );
|
||||
updateInterest( tor, peer );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len )
|
||||
{
|
||||
int index, begin, length;
|
||||
tr_request_t * r;
|
||||
|
||||
if( len != 13 )
|
||||
{
|
||||
peer_dbg( "GET request, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( peer->amChoking )
|
||||
{
|
||||
/* Didn't he get it? */
|
||||
sendChoke( peer, 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
TR_NTOHL( p, index );
|
||||
TR_NTOHL( &p[4], begin );
|
||||
TR_NTOHL( &p[8], length );
|
||||
|
||||
peer_dbg( "GET request %d/%d (%d bytes)",
|
||||
index, begin, length );
|
||||
|
||||
/* TODO sanity checks (do we have the piece, etc) */
|
||||
|
||||
if( length > 16384 )
|
||||
{
|
||||
/* Sorry mate */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( peer->outRequestCount >= MAX_REQUEST_COUNT )
|
||||
{
|
||||
tr_err( "Too many requests" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = &peer->outRequests[peer->outRequestCount];
|
||||
r->index = index;
|
||||
r->begin = begin;
|
||||
r->length = length;
|
||||
|
||||
(peer->outRequestCount)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
int index, begin, block, i, j;
|
||||
tr_request_t * r;
|
||||
|
||||
TR_NTOHL( p, index );
|
||||
TR_NTOHL( &p[4], begin );
|
||||
|
||||
peer_dbg( "GET piece %d/%d (%d bytes)",
|
||||
index, begin, len - 9 );
|
||||
|
||||
if( peer->inRequestCount < 1 )
|
||||
{
|
||||
/* Our "cancel" was probably late */
|
||||
peer_dbg( "not expecting a block" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = &peer->inRequests[0];
|
||||
if( index != r->index || begin != r->begin )
|
||||
{
|
||||
int suckyClient;
|
||||
|
||||
/* Either our "cancel" was late, or this is a sucky
|
||||
client that cannot deal with multiple requests */
|
||||
suckyClient = 0;
|
||||
for( i = 0; i < peer->inRequestCount; i++ )
|
||||
{
|
||||
r = &peer->inRequests[i];
|
||||
|
||||
if( index != r->index || begin != r->begin )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Sucky client, he dropped the previous requests */
|
||||
peer_dbg( "block was expected later" );
|
||||
for( j = 0; j < i; j++ )
|
||||
{
|
||||
r = &peer->inRequests[j];
|
||||
if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
|
||||
{
|
||||
tor->blockHave[tr_block(r->index,r->begin)]--;
|
||||
}
|
||||
}
|
||||
suckyClient = 1;
|
||||
peer->inRequestCount -= i;
|
||||
memmove( &peer->inRequests[0], &peer->inRequests[i],
|
||||
peer->inRequestCount * sizeof( tr_request_t ) );
|
||||
r = &peer->inRequests[0];
|
||||
break;
|
||||
}
|
||||
|
||||
if( !suckyClient )
|
||||
{
|
||||
r = &peer->inRequests[0];
|
||||
peer_dbg( "wrong block (expecting %d/%d)",
|
||||
r->index, r->begin );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( len - 9 != r->length )
|
||||
{
|
||||
peer_dbg( "wrong size (expecting %d)", r->length );
|
||||
return 1;
|
||||
}
|
||||
|
||||
block = tr_block( r->index, r->begin );
|
||||
if( tor->blockHave[block] < 0 )
|
||||
{
|
||||
peer_dbg( "have this block already" );
|
||||
(peer->inRequestCount)--;
|
||||
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
||||
peer->inRequestCount * sizeof( tr_request_t ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
tor->blockHave[block] = -1;
|
||||
tor->blockHaveCount += 1;
|
||||
tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
|
||||
|
||||
sendCancel( tor, block );
|
||||
|
||||
if( tr_bitfieldHas( tor->bitfield, index ) )
|
||||
{
|
||||
tr_peer_t * otherPeer;
|
||||
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
otherPeer = tor->peers[i];
|
||||
|
||||
if( otherPeer->status < PEER_STATUS_CONNECTED )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sendHave( otherPeer, index );
|
||||
updateInterest( tor, otherPeer );
|
||||
}
|
||||
}
|
||||
|
||||
(peer->inRequestCount)--;
|
||||
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
||||
peer->inRequestCount * sizeof( tr_request_t ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len )
|
||||
{
|
||||
int index, begin, length;
|
||||
int i;
|
||||
tr_request_t * r;
|
||||
|
||||
if( len != 13 )
|
||||
{
|
||||
peer_dbg( "GET cancel, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
TR_NTOHL( p, index );
|
||||
TR_NTOHL( &p[4], begin );
|
||||
TR_NTOHL( &p[8], length );
|
||||
|
||||
peer_dbg( "GET cancel %d/%d (%d bytes)",
|
||||
index, begin, length );
|
||||
|
||||
for( i = 0; i < peer->outRequestCount; i++ )
|
||||
{
|
||||
r = &peer->outRequests[i];
|
||||
if( r->index == index && r->begin == begin &&
|
||||
r->length == length )
|
||||
{
|
||||
(peer->outRequestCount)--;
|
||||
memmove( &r[0], &r[1], sizeof( tr_request_t ) *
|
||||
( peer->outRequestCount - i ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len )
|
||||
{
|
||||
in_port_t port;
|
||||
|
||||
if( len != 3 )
|
||||
{
|
||||
peer_dbg( "GET port, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
port = *( (in_port_t *) p );
|
||||
peer_dbg( "GET port %d", ntohs( port ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
char id;
|
||||
|
||||
/* Type of the message */
|
||||
id = *(p++);
|
||||
|
||||
switch( id )
|
||||
{
|
||||
case 0:
|
||||
return parseChoke( tor, peer, len, 1 );
|
||||
case 1:
|
||||
return parseChoke( tor, peer, len, 0 );
|
||||
case 2:
|
||||
return parseInterested( peer, len, 1 );
|
||||
case 3:
|
||||
return parseInterested( peer, len, 0 );
|
||||
case 4:
|
||||
return parseHave( tor, peer, p, len );
|
||||
case 5:
|
||||
return parseBitfield( tor, peer, p, len );
|
||||
case 6:
|
||||
return parseRequest( peer, p, len );
|
||||
case 7:
|
||||
return parsePiece( tor, peer, p, len );
|
||||
case 8:
|
||||
return parseCancel( peer, p, len );
|
||||
case 9:
|
||||
return parsePort( peer, p, len );
|
||||
}
|
||||
|
||||
peer_dbg( "Unknown message '%d'", id );
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
int newBytes )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i;
|
||||
int len;
|
||||
uint8_t * p = peer->buf;
|
||||
uint8_t * end = &p[peer->pos];
|
||||
|
||||
while( peer->pos >= 4 )
|
||||
{
|
||||
if( peer->status & PEER_STATUS_HANDSHAKE )
|
||||
{
|
||||
char * client;
|
||||
|
||||
if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
|
||||
{
|
||||
/* Don't wait until we get 68 bytes, this is wrong
|
||||
already */
|
||||
peer_dbg( "GET handshake, invalid" );
|
||||
tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( peer->pos < 68 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( memcmp( &p[4], "Torrent protocol", 16 ) )
|
||||
{
|
||||
peer_dbg( "GET handshake, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( memcmp( &p[28], inf->hash, 20 ) )
|
||||
{
|
||||
peer_dbg( "GET handshake, wrong torrent hash" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !memcmp( &p[48], tor->id, 20 ) )
|
||||
{
|
||||
/* We are connected to ourselves... */
|
||||
peer_dbg( "GET handshake, that is us" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer->status = PEER_STATUS_CONNECTED;
|
||||
memcpy( peer->id, &p[48], 20 );
|
||||
p += 68;
|
||||
peer->pos -= 68;
|
||||
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
if( tor->peers[i] == peer )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if( !peerCmp( peer, tor->peers[i] ) )
|
||||
{
|
||||
peer_dbg( "GET handshake, duplicate" );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
client = tr_clientForId( (uint8_t *) peer->id );
|
||||
peer_dbg( "GET handshake, ok (%s)", client );
|
||||
free( client );
|
||||
|
||||
sendBitfield( tor, peer );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get payload size */
|
||||
TR_NTOHL( p, len );
|
||||
p += 4;
|
||||
|
||||
if( len > 9 + tor->blockSize )
|
||||
{
|
||||
/* This should never happen. Drop that peer */
|
||||
peer_dbg( "message too large (%d bytes)", len );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !len )
|
||||
{
|
||||
/* keep-alive */
|
||||
peer_dbg( "GET keep-alive" );
|
||||
peer->pos -= 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* That's a piece coming */
|
||||
if( p < end && *p == 7 )
|
||||
{
|
||||
/* XXX */
|
||||
tor->downloaded[9] += newBytes;
|
||||
peer->inTotal += newBytes;
|
||||
newBytes = 0;
|
||||
}
|
||||
|
||||
if( &p[len] > end )
|
||||
{
|
||||
/* We do not have the entire message */
|
||||
p -= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Remaining data after this message */
|
||||
peer->pos -= 4 + len;
|
||||
|
||||
if( parseMessage( tor, peer, p, len ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
p += len;
|
||||
}
|
||||
|
||||
memmove( peer->buf, p, peer->pos );
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -122,6 +122,8 @@ static int checkPeer( tr_torrent_t * tor, int i )
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* TODO: check for bad downloaders */
|
||||
|
||||
#if 0
|
||||
/* Choke unchoked peers we are not sending anything to */
|
||||
if( !peer->amChoking && tr_date() > peer->outDate + 10000 )
|
||||
|
@ -164,452 +166,6 @@ static int checkPeer( tr_torrent_t * tor, int i )
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
int newBytes )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i, j;
|
||||
int len;
|
||||
char id;
|
||||
uint8_t * p = peer->buf;
|
||||
uint8_t * end = &p[peer->pos];
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
if( peer->pos < 4 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( peer->status & PEER_STATUS_HANDSHAKE )
|
||||
{
|
||||
char * client;
|
||||
|
||||
if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
|
||||
{
|
||||
/* Don't wait until we get 68 bytes, this is wrong
|
||||
already */
|
||||
peer_dbg( "GET handshake, invalid" );
|
||||
tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( peer->pos < 68 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( memcmp( &p[4], "Torrent protocol", 16 ) )
|
||||
{
|
||||
peer_dbg( "GET handshake, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( memcmp( &p[28], inf->hash, 20 ) )
|
||||
{
|
||||
peer_dbg( "GET handshake, wrong torrent hash" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !memcmp( &p[48], tor->id, 20 ) )
|
||||
{
|
||||
/* We are connected to ourselves... */
|
||||
peer_dbg( "GET handshake, that is us" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer->status = PEER_STATUS_CONNECTED;
|
||||
memcpy( peer->id, &p[48], 20 );
|
||||
p += 68;
|
||||
peer->pos -= 68;
|
||||
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
if( tor->peers[i] == peer )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if( !peerCmp( peer, tor->peers[i] ) )
|
||||
{
|
||||
peer_dbg( "GET handshake, duplicate" );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
client = tr_clientForId( (uint8_t *) peer->id );
|
||||
peer_dbg( "GET handshake, ok (%s)", client );
|
||||
free( client );
|
||||
|
||||
sendBitfield( tor, peer );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get payload size */
|
||||
TR_NTOHL( p, len );
|
||||
p += 4;
|
||||
|
||||
if( len > 9 + tor->blockSize )
|
||||
{
|
||||
/* This shouldn't happen. Forget about that peer */
|
||||
peer_dbg( "message too large" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !len )
|
||||
{
|
||||
/* keep-alive */
|
||||
peer_dbg( "GET keep-alive" );
|
||||
peer->pos -= 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* That's a piece coming */
|
||||
if( p < end && *p == 7 )
|
||||
{
|
||||
/* XXX */
|
||||
tor->downloaded[9] += newBytes;
|
||||
peer->inTotal += newBytes;
|
||||
newBytes = 0;
|
||||
}
|
||||
|
||||
if( &p[len] > end )
|
||||
{
|
||||
/* We do not have the entire message */
|
||||
p -= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Remaining data after this message */
|
||||
peer->pos -= 4 + len;
|
||||
|
||||
/* Type of the message */
|
||||
id = *(p++);
|
||||
|
||||
switch( id )
|
||||
{
|
||||
case 0: /* choke */
|
||||
{
|
||||
tr_request_t * r;
|
||||
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET choke, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer_dbg( "GET choke" );
|
||||
peer->peerChoking = 1;
|
||||
|
||||
for( i = 0; i < peer->inRequestCount; i++ )
|
||||
{
|
||||
r = &peer->inRequests[i];
|
||||
if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
|
||||
{
|
||||
tor->blockHave[tr_block(r->index,r->begin)]--;
|
||||
}
|
||||
}
|
||||
peer->inRequestCount = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case 1: /* unchoke */
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET unchoke, invalid" );
|
||||
return 1;
|
||||
}
|
||||
peer_dbg( "GET unchoke" );
|
||||
peer->peerChoking = 0;
|
||||
break;
|
||||
case 2: /* interested */
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET interested, invalid" );
|
||||
return 1;
|
||||
}
|
||||
peer_dbg( "GET interested" );
|
||||
peer->peerInterested = 1;
|
||||
break;
|
||||
case 3: /* uninterested */
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET uninterested, invalid" );
|
||||
return 1;
|
||||
}
|
||||
peer_dbg( "GET uninterested" );
|
||||
peer->peerInterested = 0;
|
||||
break;
|
||||
case 4: /* have */
|
||||
{
|
||||
uint32_t piece;
|
||||
if( len != 5 )
|
||||
{
|
||||
peer_dbg( "GET have, invalid" );
|
||||
return 1;
|
||||
}
|
||||
TR_NTOHL( p, piece );
|
||||
if( !peer->bitfield )
|
||||
{
|
||||
peer->bitfield = calloc( ( inf->pieceCount + 7 ) / 8, 1 );
|
||||
}
|
||||
tr_bitfieldAdd( peer->bitfield, piece );
|
||||
|
||||
updateInterest( tor, peer );
|
||||
|
||||
peer_dbg( "GET have %d", piece );
|
||||
break;
|
||||
}
|
||||
case 5: /* bitfield */
|
||||
{
|
||||
int bitfieldSize;
|
||||
|
||||
bitfieldSize = ( inf->pieceCount + 7 ) / 8;
|
||||
|
||||
if( len != 1 + bitfieldSize )
|
||||
{
|
||||
peer_dbg( "GET bitfield, wrong size" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Make sure the spare bits are unset */
|
||||
if( ( inf->pieceCount & 0x7 ) )
|
||||
{
|
||||
uint8_t lastByte;
|
||||
|
||||
lastByte = p[bitfieldSize-1];
|
||||
lastByte <<= inf->pieceCount & 0x7;
|
||||
lastByte &= 0xFF;
|
||||
|
||||
if( lastByte )
|
||||
{
|
||||
peer_dbg( "GET bitfield, spare bits set" );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( !peer->bitfield )
|
||||
{
|
||||
peer->bitfield = malloc( bitfieldSize );
|
||||
}
|
||||
memcpy( peer->bitfield, p, bitfieldSize );
|
||||
|
||||
updateInterest( tor, peer );
|
||||
|
||||
peer_dbg( "GET bitfield, ok" );
|
||||
break;
|
||||
}
|
||||
case 6: /* request */
|
||||
{
|
||||
int index, begin, length;
|
||||
|
||||
if( peer->amChoking )
|
||||
{
|
||||
/* Didn't he get it? */
|
||||
sendChoke( peer, 1 );
|
||||
break;
|
||||
}
|
||||
|
||||
TR_NTOHL( p, index );
|
||||
TR_NTOHL( &p[4], begin );
|
||||
TR_NTOHL( &p[8], length );
|
||||
|
||||
peer_dbg( "GET request %d/%d (%d bytes)",
|
||||
index, begin, length );
|
||||
|
||||
/* TODO sanity checks (do we have the piece, etc) */
|
||||
|
||||
if( length > 16384 )
|
||||
{
|
||||
/* Sorry mate */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( peer->outRequestCount < MAX_REQUEST_COUNT )
|
||||
{
|
||||
tr_request_t * r;
|
||||
|
||||
r = &peer->outRequests[peer->outRequestCount];
|
||||
r->index = index;
|
||||
r->begin = begin;
|
||||
r->length = length;
|
||||
|
||||
(peer->outRequestCount)++;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_err( "Too many requests" );
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 7: /* piece */
|
||||
{
|
||||
int index, begin;
|
||||
int block;
|
||||
tr_request_t * r;
|
||||
|
||||
TR_NTOHL( p, index );
|
||||
TR_NTOHL( &p[4], begin );
|
||||
|
||||
peer_dbg( "GET piece %d/%d (%d bytes)",
|
||||
index, begin, len - 9 );
|
||||
|
||||
if( peer->inRequestCount < 1 )
|
||||
{
|
||||
/* Our "cancel" was probably late */
|
||||
peer_dbg( "not expecting a block" );
|
||||
break;
|
||||
}
|
||||
|
||||
r = &peer->inRequests[0];
|
||||
if( index != r->index || begin != r->begin )
|
||||
{
|
||||
int suckyClient;
|
||||
|
||||
/* Either our "cancel" was late, or this is a sucky
|
||||
client that cannot deal with multiple requests */
|
||||
suckyClient = 0;
|
||||
for( i = 0; i < peer->inRequestCount; i++ )
|
||||
{
|
||||
r = &peer->inRequests[i];
|
||||
|
||||
if( index != r->index || begin != r->begin )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Sucky client, he dropped the previous requests */
|
||||
peer_dbg( "block was expected later" );
|
||||
for( j = 0; j < i; j++ )
|
||||
{
|
||||
r = &peer->inRequests[j];
|
||||
if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
|
||||
{
|
||||
tor->blockHave[tr_block(r->index,r->begin)]--;
|
||||
}
|
||||
}
|
||||
suckyClient = 1;
|
||||
peer->inRequestCount -= i;
|
||||
memmove( &peer->inRequests[0], &peer->inRequests[i],
|
||||
peer->inRequestCount * sizeof( tr_request_t ) );
|
||||
r = &peer->inRequests[0];
|
||||
break;
|
||||
}
|
||||
|
||||
if( !suckyClient )
|
||||
{
|
||||
r = &peer->inRequests[0];
|
||||
peer_dbg( "wrong block (expecting %d/%d)",
|
||||
r->index, r->begin );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( len - 9 != r->length )
|
||||
{
|
||||
peer_dbg( "wrong size (expecting %d)", r->length );
|
||||
return 1;
|
||||
}
|
||||
|
||||
block = tr_block( r->index, r->begin );
|
||||
if( tor->blockHave[block] < 0 )
|
||||
{
|
||||
peer_dbg( "have this block already" );
|
||||
(peer->inRequestCount)--;
|
||||
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
||||
peer->inRequestCount * sizeof( tr_request_t ) );
|
||||
break;
|
||||
}
|
||||
|
||||
tor->blockHave[block] = -1;
|
||||
tor->blockHaveCount += 1;
|
||||
tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
|
||||
|
||||
sendCancel( tor, block );
|
||||
|
||||
if( tr_bitfieldHas( tor->bitfield, index ) )
|
||||
{
|
||||
tr_peer_t * otherPeer;
|
||||
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
otherPeer = tor->peers[i];
|
||||
|
||||
if( otherPeer->status < PEER_STATUS_CONNECTED )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sendHave( otherPeer, index );
|
||||
updateInterest( tor, otherPeer );
|
||||
}
|
||||
}
|
||||
|
||||
(peer->inRequestCount)--;
|
||||
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
||||
peer->inRequestCount * sizeof( tr_request_t ) );
|
||||
break;
|
||||
}
|
||||
case 8: /* cancel */
|
||||
{
|
||||
int index, begin, length;
|
||||
int i;
|
||||
tr_request_t * r;
|
||||
|
||||
TR_NTOHL( p, index );
|
||||
TR_NTOHL( &p[4], begin );
|
||||
TR_NTOHL( &p[8], length );
|
||||
|
||||
peer_dbg( "GET cancel %d/%d (%d bytes)",
|
||||
index, begin, length );
|
||||
|
||||
for( i = 0; i < peer->outRequestCount; i++ )
|
||||
{
|
||||
r = &peer->outRequests[i];
|
||||
if( r->index == index && r->begin == begin &&
|
||||
r->length == length )
|
||||
{
|
||||
(peer->outRequestCount)--;
|
||||
memmove( &r[0], &r[1], sizeof( tr_request_t ) *
|
||||
( peer->outRequestCount - i ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 9:
|
||||
{
|
||||
in_port_t port;
|
||||
|
||||
if( len != 3 )
|
||||
{
|
||||
peer_dbg( "GET port, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
port = *( (in_port_t *) p );
|
||||
peer_dbg( "GET port %d", ntohs( port ) );
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
peer_dbg( "Unknown message '%d'", id );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
p += len - 1;
|
||||
}
|
||||
|
||||
memmove( peer->buf, p, peer->pos );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* isInteresting
|
||||
***********************************************************************
|
||||
|
@ -662,7 +218,7 @@ static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer )
|
|||
* Our main goal is to complete pieces, so we look the pieces which are
|
||||
* missing less blocks.
|
||||
**********************************************************************/
|
||||
static int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer )
|
||||
static inline int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
|
@ -798,7 +354,8 @@ static int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer )
|
|||
for( i = 0; i < tor->blockCount; i++ )
|
||||
{
|
||||
/* TODO: optimize */
|
||||
if( tor->blockHave[i] > 0 && tor->blockHave[i] < minDownloading )
|
||||
if( tr_bitfieldHas( peer->bitfield, tr_blockPiece( i ) ) &&
|
||||
tor->blockHave[i] >= 0 && tor->blockHave[i] < minDownloading )
|
||||
{
|
||||
block = i;
|
||||
minDownloading = tor->blockHave[i];
|
||||
|
|
|
@ -59,10 +59,24 @@ tr_handle_t * tr_init()
|
|||
h->fdlimit = tr_fdInit();
|
||||
|
||||
h->bindPort = 9090;
|
||||
|
||||
snprintf( h->prefsDirectory, sizeof( h->prefsDirectory ),
|
||||
"%s/.transmission", getenv( "HOME" ) );
|
||||
mkdir( h->prefsDirectory, 0755 );
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_getPrefsDirectory
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
char * tr_getPrefsDirectory( tr_handle_t * h )
|
||||
{
|
||||
return (char *) h->prefsDirectory;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setBindPort
|
||||
***********************************************************************
|
||||
|
@ -184,6 +198,7 @@ int tr_torrentInit( tr_handle_t * h, const char * path )
|
|||
|
||||
tor->upload = h->upload;
|
||||
tor->fdlimit = h->fdlimit;
|
||||
tor->prefsDirectory = (char *) h->prefsDirectory;
|
||||
|
||||
/* We have a new torrent */
|
||||
h->torrents[h->torrentCount] = tor;
|
||||
|
@ -252,6 +267,7 @@ void tr_torrentStop( tr_handle_t * h, int t )
|
|||
tr_lockLock( tor->lock );
|
||||
tr_trackerStopped( tor->tracker );
|
||||
tor->status = TR_STATUS_STOPPING;
|
||||
tor->stopDate = tr_date();
|
||||
tr_lockUnlock( tor->lock );
|
||||
}
|
||||
|
||||
|
@ -316,7 +332,9 @@ int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
|
|||
|
||||
tr_lockLock( tor->lock );
|
||||
|
||||
if( tor->status & TR_STATUS_STOPPED )
|
||||
if( ( tor->status & TR_STATUS_STOPPED ) ||
|
||||
( ( tor->status & TR_STATUS_STOPPING ) &&
|
||||
tr_date() > tor->stopDate + 60000 ) )
|
||||
{
|
||||
torrentReallyStop( h, i );
|
||||
tor->status = TR_STATUS_PAUSE;
|
||||
|
@ -478,10 +496,7 @@ static void downloadLoop( void * _tor )
|
|||
}
|
||||
|
||||
/* Receive/send messages */
|
||||
if( !( tor->status & TR_STATUS_STOPPING ) )
|
||||
{
|
||||
tr_peerPulse( tor );
|
||||
}
|
||||
tr_peerPulse( tor );
|
||||
|
||||
/* Try to get new peers or to send a message to the tracker */
|
||||
tr_trackerPulse( tor->tracker );
|
||||
|
|
|
@ -39,6 +39,15 @@ typedef struct tr_handle_s tr_handle_t;
|
|||
|
||||
tr_handle_t * tr_init();
|
||||
|
||||
/***********************************************************************
|
||||
* tr_getPrefsDirectory
|
||||
***********************************************************************
|
||||
* Returns the full path to the directory used by libtransmission to
|
||||
* store the resume files. The string belongs to libtransmission, do
|
||||
* not free it.
|
||||
**********************************************************************/
|
||||
char * tr_getPrefsDirectory( tr_handle_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setBindPort
|
||||
***********************************************************************
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CONTROLLER_H
|
||||
#define CONTROLLER_H
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <transmission.h>
|
||||
#include "PrefsController.h"
|
||||
|
@ -63,7 +66,9 @@
|
|||
- (void) openSheetClosed: (NSOpenPanel *) s returnCode: (int) code
|
||||
contextInfo: (void *) info;
|
||||
- (void) stopTorrent: (id) sender;
|
||||
- (void) stopTorrentWithIndex: (int) index;
|
||||
- (void) resumeTorrent: (id) sender;
|
||||
- (void) resumeTorrentWithIndex: (int) index;
|
||||
- (void) removeTorrent: (id) sender;
|
||||
- (void) showInfo: (id) sender;
|
||||
|
||||
|
@ -76,3 +81,5 @@
|
|||
- (void) linkForums: (id) sender;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,15 +22,13 @@
|
|||
|
||||
#include <IOKit/IOMessage.h>
|
||||
|
||||
#include "Controller.h"
|
||||
#include "NameCell.h"
|
||||
#include "ProgressCell.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#define TOOLBAR_OPEN @"Toolbar Open"
|
||||
#define TOOLBAR_RESUME @"Toolbar Resume"
|
||||
#define TOOLBAR_STOP @"Toolbar Stop"
|
||||
#define TOOLBAR_REMOVE @"Toolbar Remove"
|
||||
#define TOOLBAR_PREFS @"Toolbar Preferences"
|
||||
#define TOOLBAR_INFO @"Toolbar Info"
|
||||
|
||||
static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
@ -42,60 +40,26 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
|
||||
@implementation Controller
|
||||
|
||||
- (void) enableToolbarItem: (NSString *) ident flag: (BOOL) e
|
||||
{
|
||||
NSArray * array = [fToolbar items];
|
||||
NSToolbarItem * item;
|
||||
|
||||
if( [ident isEqualToString: TOOLBAR_OPEN] )
|
||||
{
|
||||
item = [array objectAtIndex: 0];
|
||||
[item setAction: e ? @selector( openShowSheet: ) : NULL];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_RESUME] )
|
||||
{
|
||||
item = [array objectAtIndex: 1];
|
||||
[item setAction: e ? @selector( resumeTorrent: ) : NULL];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_STOP] )
|
||||
{
|
||||
item = [array objectAtIndex: 2];
|
||||
[item setAction: e ? @selector( stopTorrent: ) : NULL];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_REMOVE] )
|
||||
{
|
||||
item = [array objectAtIndex: 3];
|
||||
[item setAction: e ? @selector( removeTorrent: ) : NULL];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_INFO] )
|
||||
{
|
||||
item = [array objectAtIndex: 5];
|
||||
[item setAction: e ? @selector( showInfo: ) : NULL];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateToolbar
|
||||
{
|
||||
int row = [fTableView selectedRow];
|
||||
NSArray * items;
|
||||
NSToolbarItem * item;
|
||||
BOOL enable;
|
||||
int row;
|
||||
unsigned i;
|
||||
|
||||
[self enableToolbarItem: TOOLBAR_RESUME flag: NO];
|
||||
[self enableToolbarItem: TOOLBAR_STOP flag: NO];
|
||||
[self enableToolbarItem: TOOLBAR_REMOVE flag: NO];
|
||||
row = [fTableView selectedRow];
|
||||
enable = ( row >= 0 ) && ( fStat[row].status &
|
||||
( TR_STATUS_STOPPING | TR_STATUS_PAUSE ) );
|
||||
|
||||
if( row < 0 )
|
||||
items = [fToolbar items];
|
||||
for( i = 0; i < [items count]; i++ )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( fStat[row].status &
|
||||
( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
{
|
||||
[self enableToolbarItem: TOOLBAR_STOP flag: YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self enableToolbarItem: TOOLBAR_RESUME flag: YES];
|
||||
[self enableToolbarItem: TOOLBAR_REMOVE flag: YES];
|
||||
item = [items objectAtIndex: i];
|
||||
if( [[item itemIdentifier] isEqualToString: TOOLBAR_REMOVE] )
|
||||
{
|
||||
[item setAction: enable ? @selector( removeTorrent: ) : NULL];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,12 +82,6 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
[fWindow setToolbar: fToolbar];
|
||||
[fWindow setDelegate: self];
|
||||
|
||||
[self enableToolbarItem: TOOLBAR_OPEN flag: YES];
|
||||
[self enableToolbarItem: TOOLBAR_RESUME flag: NO];
|
||||
[self enableToolbarItem: TOOLBAR_STOP flag: NO];
|
||||
[self enableToolbarItem: TOOLBAR_REMOVE flag: NO];
|
||||
[self enableToolbarItem: TOOLBAR_INFO flag: YES];
|
||||
|
||||
[fTableView setDataSource: self];
|
||||
[fTableView setDelegate: self];
|
||||
|
||||
|
@ -134,6 +92,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
nameCell = [[NameCell alloc] init];
|
||||
progressCell = [[ProgressCell alloc] init];
|
||||
tableColumn = [fTableView tableColumnWithIdentifier: @"Name"];
|
||||
[nameCell setController: self];
|
||||
[tableColumn setDataCell: nameCell];
|
||||
[tableColumn setMinWidth: 10.0];
|
||||
[tableColumn setMaxWidth: 3000.0];
|
||||
|
@ -143,7 +102,8 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
[tableColumn setMinWidth: 134.0];
|
||||
[tableColumn setMaxWidth: 134.0];
|
||||
|
||||
[fTableView sizeToFit];
|
||||
[fTableView setAutosaveTableColumns: YES];
|
||||
[fTableView sizeToFit];
|
||||
|
||||
[fTableView registerForDraggedTypes: [NSArray arrayWithObjects:
|
||||
NSFilenamesPboardType, NULL]];
|
||||
|
@ -208,7 +168,12 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
fTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 target: self
|
||||
selector: @selector( updateUI: ) userInfo: NULL repeats: YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer: fTimer
|
||||
forMode: NSModalPanelRunLoopMode];
|
||||
forMode: NSEventTrackingRunLoopMode];
|
||||
}
|
||||
|
||||
- (void) windowDidResize: (NSNotification *) n
|
||||
{
|
||||
[fTableView sizeToFit];
|
||||
}
|
||||
|
||||
- (BOOL) windowShouldClose: (id) sender
|
||||
|
@ -411,14 +376,24 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
|
||||
- (void) resumeTorrent: (id) sender
|
||||
{
|
||||
tr_torrentStart( fHandle, [fTableView selectedRow] );
|
||||
[self updateToolbar];
|
||||
[self resumeTorrentWithIndex: [fTableView selectedRow]];
|
||||
}
|
||||
|
||||
- (void) resumeTorrentWithIndex: (int) idx
|
||||
{
|
||||
tr_torrentStart( fHandle, idx );
|
||||
[self updateUI: NULL];
|
||||
}
|
||||
|
||||
- (void) stopTorrent: (id) sender
|
||||
{
|
||||
tr_torrentStop( fHandle, [fTableView selectedRow] );
|
||||
[self updateToolbar];
|
||||
[self stopTorrentWithIndex: [fTableView selectedRow]];
|
||||
}
|
||||
|
||||
- (void) stopTorrentWithIndex: (int) idx
|
||||
{
|
||||
tr_torrentStop( fHandle, idx );
|
||||
[self updateUI: NULL];
|
||||
}
|
||||
|
||||
- (void) removeTorrent: (id) sender
|
||||
|
@ -489,7 +464,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
{
|
||||
if( [[tableColumn identifier] isEqualToString: @"Name"] )
|
||||
{
|
||||
[(NameCell *) cell setStat: &fStat[rowIndex]];
|
||||
[(NameCell *) cell setStat: &fStat[rowIndex] index: rowIndex];
|
||||
}
|
||||
else if( [[tableColumn identifier] isEqualToString: @"Progress"] )
|
||||
{
|
||||
|
@ -565,37 +540,37 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
NSToolbarItem * item;
|
||||
item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
|
||||
|
||||
[item setTarget: self];
|
||||
|
||||
if( [ident isEqualToString: TOOLBAR_OPEN] )
|
||||
{
|
||||
[item setLabel: @"Open"];
|
||||
[item setToolTip: @"Open a torrent"];
|
||||
[item setImage: [NSImage imageNamed: @"Open.tiff"]];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_RESUME] )
|
||||
{
|
||||
[item setLabel: @"Resume"];
|
||||
[item setToolTip: @"Resume download"];
|
||||
[item setImage: [NSImage imageNamed: @"Resume.tiff"]];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_STOP] )
|
||||
{
|
||||
[item setLabel: @"Stop"];
|
||||
[item setToolTip: @"Stop download"];
|
||||
[item setImage: [NSImage imageNamed: @"Stop.tiff"]];
|
||||
[item setImage: [NSImage imageNamed: @"Open.png"]];
|
||||
[item setTarget: self];
|
||||
[item setAction: @selector( openShowSheet: )];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_REMOVE] )
|
||||
{
|
||||
[item setLabel: @"Remove"];
|
||||
[item setToolTip: @"Remove torrent from list"];
|
||||
[item setImage: [NSImage imageNamed: @"Remove.tiff"]];
|
||||
[item setImage: [NSImage imageNamed: @"Remove.png"]];
|
||||
[item setTarget: self];
|
||||
/* We set the selector in updateToolbar: */
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_PREFS] )
|
||||
{
|
||||
[item setLabel: @"Preferences"];
|
||||
[item setToolTip: @"Show the Preferences panel"];
|
||||
[item setImage: [NSImage imageNamed: @"Preferences.png"]];
|
||||
[item setTarget: fPrefsController];
|
||||
[item setAction: @selector( show: )];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_INFO] )
|
||||
{
|
||||
[item setLabel: @"Info"];
|
||||
[item setToolTip: @"Information"];
|
||||
[item setImage: [NSImage imageNamed: @"Info.tiff"]];
|
||||
[item setImage: [NSImage imageNamed: @"Info.png"]];
|
||||
[item setTarget: self];
|
||||
[item setAction: @selector( showInfo: )];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -608,9 +583,9 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
|
||||
- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) t
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
TOOLBAR_OPEN, TOOLBAR_RESUME, TOOLBAR_STOP, TOOLBAR_REMOVE,
|
||||
NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_INFO, NULL];
|
||||
return [NSArray arrayWithObjects: TOOLBAR_OPEN, TOOLBAR_REMOVE,
|
||||
NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_PREFS,
|
||||
TOOLBAR_INFO, NULL];
|
||||
}
|
||||
|
||||
- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) t
|
||||
|
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 651 B |
After Width: | Height: | Size: 677 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 553 B |
After Width: | Height: | Size: 492 B |
|
@ -20,14 +20,24 @@
|
|||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef NAMECELL_H
|
||||
#define NAMECELL_H
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <transmission.h>
|
||||
#include "Controller.h"
|
||||
|
||||
@interface NameCell : NSCell
|
||||
{
|
||||
tr_stat_t * fStat;
|
||||
NSRect fRevealRect;
|
||||
NSPoint fClickPoint;
|
||||
Controller * fController;
|
||||
tr_stat_t * fStat;
|
||||
int fIndex;
|
||||
NSRect fPauseRect;
|
||||
NSRect fRevealRect;
|
||||
NSPoint fClickPoint;
|
||||
}
|
||||
- (void) setStat: (tr_stat_t *) stat;
|
||||
- (void) setController: (Controller *) controller;
|
||||
- (void) setStat: (tr_stat_t *) stat index: (int) index;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,9 +25,15 @@
|
|||
|
||||
@implementation NameCell
|
||||
|
||||
- (void) setStat: (tr_stat_t *) stat;
|
||||
- (void) setController: (Controller *) controller
|
||||
{
|
||||
fStat = stat;
|
||||
fController = controller;
|
||||
}
|
||||
|
||||
- (void) setStat: (tr_stat_t *) stat index: (int) idx
|
||||
{
|
||||
fStat = stat;
|
||||
fIndex = idx;
|
||||
}
|
||||
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
|
||||
|
@ -47,7 +53,7 @@
|
|||
|
||||
nameString = [NSString stringWithFormat: @"%@%@",
|
||||
stringFittingInWidth( fStat->info.name, cellFrame.size.width -
|
||||
10 - widthForString( sizeString, 12 ), 12 ),
|
||||
35 - widthForString( sizeString, 12 ), 12 ),
|
||||
sizeString];
|
||||
|
||||
if( fStat->status & TR_STATUS_PAUSE )
|
||||
|
@ -100,7 +106,7 @@
|
|||
{
|
||||
peersString = [NSString stringWithFormat: @"%@%@",
|
||||
@"Error: ", stringFittingInWidth( fStat->error,
|
||||
cellFrame.size.width - 15 -
|
||||
cellFrame.size.width - 40 -
|
||||
widthForString( @"Error: ", 10 ), 10 )];
|
||||
}
|
||||
|
||||
|
@ -119,6 +125,41 @@
|
|||
pen.x += 0; pen.y += 15;
|
||||
[peersString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
/* "Pause" button */
|
||||
fPauseRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19,
|
||||
cellFrame.origin.y + cellFrame.size.height - 38,
|
||||
14, 14 );
|
||||
NSImage * pauseImage = NULL;
|
||||
if( fStat->status & TR_STATUS_PAUSE )
|
||||
{
|
||||
if( NSPointInRect( fClickPoint, fPauseRect ) )
|
||||
{
|
||||
pauseImage = [NSImage imageNamed: @"ResumeOn.png"];
|
||||
}
|
||||
else
|
||||
{
|
||||
pauseImage = [NSImage imageNamed: @"ResumeOff.png"];
|
||||
}
|
||||
}
|
||||
else if( fStat->status &
|
||||
( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
{
|
||||
if( NSPointInRect( fClickPoint, fPauseRect ) )
|
||||
{
|
||||
pauseImage = [NSImage imageNamed: @"PauseOn.png"];
|
||||
}
|
||||
else
|
||||
{
|
||||
pauseImage = [NSImage imageNamed: @"PauseOff.png"];
|
||||
}
|
||||
}
|
||||
if( pauseImage )
|
||||
{
|
||||
pen.x = fPauseRect.origin.x;
|
||||
pen.y = fPauseRect.origin.y + 14;
|
||||
[pauseImage compositeToPoint: pen operation: NSCompositeSourceOver];
|
||||
}
|
||||
|
||||
/* "Reveal in Finder" button */
|
||||
fRevealRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19,
|
||||
cellFrame.origin.y + cellFrame.size.height - 19,
|
||||
|
@ -126,11 +167,11 @@
|
|||
NSImage * revealImage;
|
||||
if( NSPointInRect( fClickPoint, fRevealRect ) )
|
||||
{
|
||||
revealImage = [NSImage imageNamed: @"RevealOn.tiff"];
|
||||
revealImage = [NSImage imageNamed: @"RevealOn.png"];
|
||||
}
|
||||
else
|
||||
{
|
||||
revealImage = [NSImage imageNamed: @"RevealOff.tiff"];
|
||||
revealImage = [NSImage imageNamed: @"RevealOff.png"];
|
||||
}
|
||||
pen.x = fRevealRect.origin.x;
|
||||
pen.y = fRevealRect.origin.y + 14;
|
||||
|
@ -155,11 +196,36 @@
|
|||
- (void) stopTracking: (NSPoint) last at:(NSPoint) stop
|
||||
inView: (NSView *) v mouseIsUp: (BOOL) flag
|
||||
{
|
||||
if( flag && NSPointInRect( stop, fRevealRect ) )
|
||||
if( flag )
|
||||
{
|
||||
/* Reveal in Finder */
|
||||
[[NSWorkspace sharedWorkspace] openFile:
|
||||
[NSString stringWithUTF8String: fStat->folder]];
|
||||
if( NSPointInRect( stop, fRevealRect ) )
|
||||
{
|
||||
/* Reveal in Finder */
|
||||
NSString * string = [NSString stringWithFormat:
|
||||
@"tell application \"Finder\"\nactivate\nreveal (POSIX file \"%s/%s\")\nend tell",
|
||||
fStat->folder, fStat->info.name];
|
||||
NSAppleScript * appleScript;
|
||||
appleScript = [[NSAppleScript alloc] initWithSource: string];
|
||||
NSDictionary * error;
|
||||
if( ![appleScript executeAndReturnError: &error] )
|
||||
{
|
||||
printf( "Reveal in Finder: AppleScript failed\n" );
|
||||
}
|
||||
[appleScript release];
|
||||
}
|
||||
else if( NSPointInRect( stop, fPauseRect ) )
|
||||
{
|
||||
/* Pause, resume */
|
||||
if( fStat->status & TR_STATUS_PAUSE )
|
||||
{
|
||||
[fController resumeTorrentWithIndex: fIndex];
|
||||
}
|
||||
else if( fStat->status & ( TR_STATUS_CHECK |
|
||||
TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
{
|
||||
[fController stopTorrentWithIndex: fIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
fClickPoint = NSMakePoint(0,0);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROGRESSCELL_H
|
||||
#define PROGRESSCELL_H
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <transmission.h>
|
||||
|
||||
|
@ -41,3 +44,5 @@
|
|||
- (void) buildAdvancedBar;
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -81,7 +81,7 @@ static uint32_t kGreen[] =
|
|||
self = [super init];
|
||||
|
||||
/* Have a NSBitmapImageRep ready to draw the progression bar */
|
||||
bgImg = [NSImage imageNamed: @"Progress.tiff"];
|
||||
bgImg = [NSImage imageNamed: @"Progress.png"];
|
||||
fBgBmp = [[bgImg representations] objectAtIndex: 0];
|
||||
size = [bgImg size];
|
||||
fImg = [[NSImage alloc] initWithSize: size];
|
||||
|
|
|
@ -13,16 +13,19 @@
|
|||
4D118E1A08CB46B20033958F /* PrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D118E1908CB46B20033958F /* PrefsController.m */; };
|
||||
4D2784370905709500687951 /* Transmission.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D2784360905709500687951 /* Transmission.icns */; };
|
||||
4D3EA0AA08AE13C600EA10C2 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D3EA0A908AE13C600EA10C2 /* IOKit.framework */; };
|
||||
4D6DAAC6090CE00500F43C22 /* RevealOff.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */; };
|
||||
4D6DAAC7090CE00500F43C22 /* RevealOn.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */; };
|
||||
4D813EB508AA43AC00191DB4 /* Progress.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.tiff */; };
|
||||
4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.png */; };
|
||||
4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.png */; };
|
||||
4D752E930913C949008EAAD4 /* Preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D752E920913C949008EAAD4 /* Preferences.png */; };
|
||||
4D813EB508AA43AC00191DB4 /* Progress.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.png */; };
|
||||
4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB80911233800450CB1 /* PauseOn.png */; };
|
||||
4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB90911233800450CB1 /* PauseOff.png */; };
|
||||
4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */; };
|
||||
4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */; };
|
||||
4DF0C5AB0899190500DD8943 /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DF0C5A90899190500DD8943 /* Controller.m */; };
|
||||
4DF0C5AE08991C1600DD8943 /* libtransmission.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DF0C5AD08991C1600DD8943 /* libtransmission.a */; };
|
||||
4DF7500C08A103AD007B0D70 /* Open.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.tiff */; };
|
||||
4DF7500D08A103AD007B0D70 /* Info.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.tiff */; };
|
||||
4DF7500E08A103AD007B0D70 /* Remove.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.tiff */; };
|
||||
4DF7500F08A103AD007B0D70 /* Resume.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500A08A103AD007B0D70 /* Resume.tiff */; };
|
||||
4DF7501008A103AD007B0D70 /* Stop.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500B08A103AD007B0D70 /* Stop.tiff */; };
|
||||
4DF7500C08A103AD007B0D70 /* Open.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.png */; };
|
||||
4DF7500D08A103AD007B0D70 /* Info.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.png */; };
|
||||
4DF7500E08A103AD007B0D70 /* Remove.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.png */; };
|
||||
8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
||||
|
@ -74,17 +77,20 @@
|
|||
4D118E1908CB46B20033958F /* PrefsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PrefsController.m; sourceTree = "<group>"; };
|
||||
4D2784360905709500687951 /* Transmission.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Transmission.icns; path = Images/Transmission.icns; sourceTree = "<group>"; };
|
||||
4D3EA0A908AE13C600EA10C2 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
|
||||
4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = RevealOff.tiff; path = Images/RevealOff.tiff; sourceTree = "<group>"; };
|
||||
4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = RevealOn.tiff; path = Images/RevealOn.tiff; sourceTree = "<group>"; };
|
||||
4D813EB408AA43AC00191DB4 /* Progress.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Progress.tiff; path = Images/Progress.tiff; sourceTree = "<group>"; };
|
||||
4D6DAAC4090CE00500F43C22 /* RevealOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOff.png; path = Images/RevealOff.png; sourceTree = "<group>"; };
|
||||
4D6DAAC5090CE00500F43C22 /* RevealOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOn.png; path = Images/RevealOn.png; sourceTree = "<group>"; };
|
||||
4D752E920913C949008EAAD4 /* Preferences.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Preferences.png; path = Images/Preferences.png; sourceTree = "<group>"; };
|
||||
4D813EB408AA43AC00191DB4 /* Progress.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Progress.png; path = Images/Progress.png; sourceTree = "<group>"; };
|
||||
4DA6FDB80911233800450CB1 /* PauseOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOn.png; path = Images/PauseOn.png; sourceTree = "<group>"; };
|
||||
4DA6FDB90911233800450CB1 /* PauseOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOff.png; path = Images/PauseOff.png; sourceTree = "<group>"; };
|
||||
4DA6FDC3091141AD00450CB1 /* ResumeOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOff.png; path = Images/ResumeOff.png; sourceTree = "<group>"; };
|
||||
4DA6FDC4091141AD00450CB1 /* ResumeOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOn.png; path = Images/ResumeOn.png; sourceTree = "<group>"; };
|
||||
4DF0C5A90899190500DD8943 /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Controller.m; sourceTree = "<group>"; };
|
||||
4DF0C5AA0899190500DD8943 /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Controller.h; sourceTree = "<group>"; };
|
||||
4DF0C5AD08991C1600DD8943 /* libtransmission.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtransmission.a; path = ../libtransmission/libtransmission.a; sourceTree = SOURCE_ROOT; };
|
||||
4DF7500708A103AD007B0D70 /* Open.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Open.tiff; path = Images/Open.tiff; sourceTree = "<group>"; };
|
||||
4DF7500808A103AD007B0D70 /* Info.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Info.tiff; path = Images/Info.tiff; sourceTree = "<group>"; };
|
||||
4DF7500908A103AD007B0D70 /* Remove.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Remove.tiff; path = Images/Remove.tiff; sourceTree = "<group>"; };
|
||||
4DF7500A08A103AD007B0D70 /* Resume.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Resume.tiff; path = Images/Resume.tiff; sourceTree = "<group>"; };
|
||||
4DF7500B08A103AD007B0D70 /* Stop.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Stop.tiff; path = Images/Stop.tiff; sourceTree = "<group>"; };
|
||||
4DF7500708A103AD007B0D70 /* Open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Open.png; path = Images/Open.png; sourceTree = "<group>"; };
|
||||
4DF7500808A103AD007B0D70 /* Info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Info.png; path = Images/Info.png; sourceTree = "<group>"; };
|
||||
4DF7500908A103AD007B0D70 /* Remove.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Remove.png; path = Images/Remove.png; sourceTree = "<group>"; };
|
||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
@ -172,14 +178,17 @@
|
|||
children = (
|
||||
4D2784360905709500687951 /* Transmission.icns */,
|
||||
4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */,
|
||||
4DF7500808A103AD007B0D70 /* Info.tiff */,
|
||||
4D813EB408AA43AC00191DB4 /* Progress.tiff */,
|
||||
4DF7500708A103AD007B0D70 /* Open.tiff */,
|
||||
4DF7500908A103AD007B0D70 /* Remove.tiff */,
|
||||
4DF7500A08A103AD007B0D70 /* Resume.tiff */,
|
||||
4DF7500B08A103AD007B0D70 /* Stop.tiff */,
|
||||
4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */,
|
||||
4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */,
|
||||
4DF7500808A103AD007B0D70 /* Info.png */,
|
||||
4D813EB408AA43AC00191DB4 /* Progress.png */,
|
||||
4DF7500708A103AD007B0D70 /* Open.png */,
|
||||
4DF7500908A103AD007B0D70 /* Remove.png */,
|
||||
4D6DAAC4090CE00500F43C22 /* RevealOff.png */,
|
||||
4D6DAAC5090CE00500F43C22 /* RevealOn.png */,
|
||||
4DA6FDB80911233800450CB1 /* PauseOn.png */,
|
||||
4DA6FDB90911233800450CB1 /* PauseOff.png */,
|
||||
4DA6FDC3091141AD00450CB1 /* ResumeOff.png */,
|
||||
4DA6FDC4091141AD00450CB1 /* ResumeOn.png */,
|
||||
4D752E920913C949008EAAD4 /* Preferences.png */,
|
||||
8D1107310486CEB800E47090 /* Info.plist */,
|
||||
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
|
||||
29B97318FDCFA39411CA2CEA /* MainMenu.nib */,
|
||||
|
@ -256,16 +265,19 @@
|
|||
files = (
|
||||
8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */,
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
|
||||
4DF7500C08A103AD007B0D70 /* Open.tiff in Resources */,
|
||||
4DF7500D08A103AD007B0D70 /* Info.tiff in Resources */,
|
||||
4DF7500E08A103AD007B0D70 /* Remove.tiff in Resources */,
|
||||
4DF7500F08A103AD007B0D70 /* Resume.tiff in Resources */,
|
||||
4DF7501008A103AD007B0D70 /* Stop.tiff in Resources */,
|
||||
4D813EB508AA43AC00191DB4 /* Progress.tiff in Resources */,
|
||||
4DF7500C08A103AD007B0D70 /* Open.png in Resources */,
|
||||
4DF7500D08A103AD007B0D70 /* Info.png in Resources */,
|
||||
4DF7500E08A103AD007B0D70 /* Remove.png in Resources */,
|
||||
4D813EB508AA43AC00191DB4 /* Progress.png in Resources */,
|
||||
4D2784370905709500687951 /* Transmission.icns in Resources */,
|
||||
4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */,
|
||||
4D6DAAC6090CE00500F43C22 /* RevealOff.tiff in Resources */,
|
||||
4D6DAAC7090CE00500F43C22 /* RevealOn.tiff in Resources */,
|
||||
4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */,
|
||||
4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */,
|
||||
4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */,
|
||||
4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */,
|
||||
4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */,
|
||||
4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */,
|
||||
4D752E930913C949008EAAD4 /* Preferences.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|