Update 2005-11-01

This commit is contained in:
Eric Petit 2006-01-12 18:20:48 +00:00
parent a6aa884776
commit 8adc2d7338
30 changed files with 896 additions and 634 deletions

16
Jamfile
View File

@ -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

View File

@ -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)
}

111
configure vendored
View File

@ -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'."

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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 )

529
libtransmission/peerparse.h Normal file
View File

@ -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;
}

View File

@ -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];

View File

@ -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 );

View File

@ -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
***********************************************************************

View File

@ -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

View File

@ -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

Binary file not shown.

BIN
macosx/Images/Info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
macosx/Images/Open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
macosx/Images/PauseOff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

BIN
macosx/Images/PauseOn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
macosx/Images/Progress.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
macosx/Images/Remove.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
macosx/Images/ResumeOff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
macosx/Images/ResumeOn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
macosx/Images/RevealOff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

BIN
macosx/Images/RevealOn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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];

View File

@ -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;
};