mirror of
https://github.com/transmission/transmission.git
synced 2026-02-14 23:19:34 +00:00
Import from 2005-10-26
This commit is contained in:
28
AUTHORS
Normal file
28
AUTHORS
Normal file
@@ -0,0 +1,28 @@
|
||||
Authors:
|
||||
|
||||
Eric Petit <titer@m0k.org>
|
||||
+ About everything until now
|
||||
|
||||
|
||||
Thanks to:
|
||||
|
||||
vi@nwr.jp
|
||||
+ Free SHA1 implementation (sha1.[ch])
|
||||
|
||||
Mike Matas <http://www.mikematas.com/>
|
||||
+ OS X toolbar icons
|
||||
|
||||
Michael, Omar and Adrien
|
||||
+ Beta testing
|
||||
|
||||
Various people
|
||||
+ Writing http://wiki.theory.org/BitTorrentSpecification
|
||||
|
||||
Ahmad M. Afuni
|
||||
+ NetBSD patch
|
||||
|
||||
Jeremy Messenger
|
||||
+ FreeBSD patch
|
||||
|
||||
Martin Stadtmueller
|
||||
+ Icon tweaking
|
||||
46
Jamfile
Normal file
46
Jamfile
Normal file
@@ -0,0 +1,46 @@
|
||||
SubDir TOP ;
|
||||
|
||||
Main transmissioncli : transmissioncli.c ;
|
||||
LinkLibraries transmissioncli : libtransmission.a ;
|
||||
ObjectHdrs transmissioncli.c : $(TOP)/libtransmission ;
|
||||
|
||||
if $(OS) = MACOSX
|
||||
{
|
||||
OSXInfoPlist macosx/Info.plist : macosx/Info.plist.in ;
|
||||
OSXBundle Transmission.app : libtransmission.a
|
||||
macosx/Controller.h
|
||||
macosx/Controller.m
|
||||
macosx/English.lproj/InfoPlist.strings
|
||||
macosx/English.lproj/MainMenu.nib
|
||||
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/Transmission.icns
|
||||
macosx/Images/TransmissionDocument.icns
|
||||
macosx/Info.plist
|
||||
macosx/NameCell.h
|
||||
macosx/NameCell.m
|
||||
macosx/PrefsController.h
|
||||
macosx/PrefsController.m
|
||||
macosx/ProgressCell.h
|
||||
macosx/ProgressCell.m
|
||||
macosx/main.m
|
||||
macosx/Transmission.xcodeproj/project.pbxproj
|
||||
macosx/Transmission_Prefix.pch
|
||||
macosx/Utils.h ;
|
||||
|
||||
OSXPackage Transmission-$(VERSION_STRING)-OSX.dmg :
|
||||
Transmission.app ;
|
||||
NotFile package ;
|
||||
Depends package : Transmission-$(VERSION_STRING)-OSX.dmg ;
|
||||
}
|
||||
|
||||
SubInclude TOP libtransmission ;
|
||||
85
Jamrules
Normal file
85
Jamrules
Normal file
@@ -0,0 +1,85 @@
|
||||
include config.jam ;
|
||||
|
||||
if ! $(DEFINES)
|
||||
{
|
||||
Exit "Please run ./configure first." ;
|
||||
}
|
||||
|
||||
VERSION_MAJOR = 0 ;
|
||||
VERSION_MINOR = 3 ;
|
||||
VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ;
|
||||
|
||||
DEFINES += VERSION_MAJOR=$(VERSION_MAJOR)
|
||||
VERSION_MINOR=$(VERSION_MINOR)
|
||||
VERSION_STRING=\\\"$(VERSION_STRING)\\\" ;
|
||||
CCFLAGS = -g -Wall -W ;
|
||||
OPTIM = -O3 ;
|
||||
RM = rm -Rf ;
|
||||
|
||||
if $(OS) = MACOSX
|
||||
{
|
||||
# Build universal binaries
|
||||
CCFLAGS += -isysroot /Developer/SDKs/MacOSX10.4u.sdk
|
||||
-arch ppc -arch i386 ;
|
||||
LINKFLAGS += -Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk
|
||||
-arch ppc -arch i386 ;
|
||||
|
||||
# Use libtool to build static libraries (ar does not handle
|
||||
# universal binaries)
|
||||
RANLIB = ;
|
||||
NOARUPDATE = 1 ;
|
||||
actions Archive
|
||||
{
|
||||
libtool -static $(>) -o $(<) ;
|
||||
}
|
||||
|
||||
rule OSXInfoPlist
|
||||
{
|
||||
Depends $(1) : $(2) ;
|
||||
Clean clean : $(1) ;
|
||||
}
|
||||
actions OSXInfoPlist
|
||||
{
|
||||
sed "s/%%VERSION%%/$(VERSION_STRING)/" < $(2) > $(1)
|
||||
}
|
||||
|
||||
rule OSXBundle
|
||||
{
|
||||
Depends exe : $(1) ;
|
||||
Depends $(1) : $(2) ;
|
||||
Clean clean : $(1) macosx/build ;
|
||||
}
|
||||
actions OSXBundle
|
||||
{
|
||||
$(RM) $(1) && ( cd macosx && xcodebuild ) && \
|
||||
mv macosx/build/Debug/Transmission.app $(1)
|
||||
}
|
||||
|
||||
rule OSXPackage
|
||||
{
|
||||
Depends $(1) : $(2) ;
|
||||
Clean clean : $(1) ;
|
||||
DoOSXPackage $(1) ;
|
||||
}
|
||||
actions DoOSXPackage
|
||||
{
|
||||
TMP="Transmission $(VERSION_STRING)"
|
||||
rm -f $(1) "$TMP" && mkdir "$TMP" &&
|
||||
mkdir "$TMP/Transmission.app" &&
|
||||
ditto Transmission.app "$TMP/Transmission.app" &&
|
||||
ditto AUTHORS "$TMP/AUTHORS.txt" &&
|
||||
ditto LICENSE "$TMP/LICENSE.txt" &&
|
||||
ditto NEWS "$TMP/NEWS.txt" &&
|
||||
( echo "[InternetShortcut]"; \
|
||||
echo "URL=http://transmission.m0k.org/" ) > \
|
||||
"$TMP/Homepage.url" &&
|
||||
( echo "[InternetShortcut]"; \
|
||||
echo "URL=http://transmission.m0k.org/forum/" ) > \
|
||||
"$TMP/Forums.url" &&
|
||||
( echo "[InternetShortcut]"; \
|
||||
echo "URL=http://transmission.m0k.org/contribute.php" ) > \
|
||||
"$TMP/Contribute.url" &&
|
||||
hdiutil create -format UDZO -srcfolder "$TMP" $(1) &&
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
}
|
||||
23
LICENSE
Normal file
23
LICENSE
Normal file
@@ -0,0 +1,23 @@
|
||||
The Transmission binaries and source code are distributed under the MIT
|
||||
license.
|
||||
|
||||
-----
|
||||
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 SIMON TATHAM 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.
|
||||
-----
|
||||
22
NEWS
Normal file
22
NEWS
Normal file
@@ -0,0 +1,22 @@
|
||||
NEWS file for Transmission <http://transmission.m0k.org/>
|
||||
|
||||
0.3 (2005/10/19)
|
||||
- Fixed "Sometimes sends incorrect messages and looses peers"
|
||||
- Fixed "Crashes with many torrents or torrents with many files"
|
||||
- Enhancements in the "End game" mode
|
||||
- Is nicer to the trackers
|
||||
- Asks for the rarest pieces first
|
||||
- OS X: Universal binary for PPC and x86
|
||||
- OS X: Fixed "Progress increases every time I pause then resume"
|
||||
- OS X: Fixed "Sometimes crashes at exit"
|
||||
- OS X: Cleaner icon
|
||||
- OS X: Show all sizes in human readable form
|
||||
- OS X: Keep downloading in the background when the window is closed
|
||||
- Miscellaneus bugfixes and internal enhancements
|
||||
|
||||
0.2 (2005/09/22)
|
||||
- Bugfixes
|
||||
- OS X: Users can now choose where the downloads are sent
|
||||
|
||||
0.1 (2005/09/15)
|
||||
- First version
|
||||
6
README
Normal file
6
README
Normal file
@@ -0,0 +1,6 @@
|
||||
README for Transmission
|
||||
=======================
|
||||
|
||||
This is Transmission, a free BitTorrent client. For more information
|
||||
(including build instructions), please consult the website:
|
||||
http://transmission.m0k.org/
|
||||
85
configure
vendored
Executable file
85
configure
vendored
Executable file
@@ -0,0 +1,85 @@
|
||||
#! /bin/sh
|
||||
|
||||
# For > 2 GB files
|
||||
DEFINES="_FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE"
|
||||
|
||||
# For asprintf
|
||||
DEFINES="$DEFINES _GNU_SOURCE"
|
||||
|
||||
# System-specific flags
|
||||
SYSTEM=`uname -s`
|
||||
case $SYSTEM in
|
||||
BeOS)
|
||||
DEFINES="$DEFINES SYS_BEOS"
|
||||
|
||||
RELEASE=`uname -r`
|
||||
case $RELEASE in
|
||||
6.0|5.0.4) # Zeta or R5 / BONE beta 7
|
||||
SYSTEM="$SYSTEM / BONE"
|
||||
LINKLIBS="$LINKLIBS -lbind -lsocket"
|
||||
;;
|
||||
5.0*) # R5 / net_server
|
||||
SYSTEM="$SYSTEM / net_server"
|
||||
DEFINES="$DEFINES BEOS_NETSERVER"
|
||||
LINKLIBS="$LINKLIBS -lnet"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported BeOS version"
|
||||
exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
|
||||
Darwin)
|
||||
DEFINES="$DEFINES SYS_DARWIN"
|
||||
LINKLIBS="$LINKLIBS -lpthread"
|
||||
;;
|
||||
|
||||
FreeBSD)
|
||||
DEFINES="$DEFINES SYS_FREEBSD"
|
||||
LINKLIBS="$LINKLIBS -pthread"
|
||||
;;
|
||||
|
||||
NetBSD)
|
||||
DEFINES="$DEFINES SYS_NETBSD"
|
||||
LINKLIBS="$LINKLIBS -lpthread"
|
||||
;;
|
||||
|
||||
Linux)
|
||||
DEFINES="$DEFINES SYS_LINUX"
|
||||
LINKLIBS="$LINKLIBS -lpthread"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unsupported operating system"
|
||||
exit 1 ;;
|
||||
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
|
||||
echo "OpenSSL: no, using built-in SHA1 implementation"
|
||||
fi
|
||||
rm -f testconf.c testconf
|
||||
|
||||
# Generate config.jam
|
||||
rm -f config.jam
|
||||
cat << EOF > config.jam
|
||||
DEFINES = $DEFINES ;
|
||||
LINKLIBS = $LINKLIBS ;
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo "To build Transmission, run 'jam'."
|
||||
8
libtransmission/Jamfile
Normal file
8
libtransmission/Jamfile
Normal file
@@ -0,0 +1,8 @@
|
||||
SubDir TOP libtransmission ;
|
||||
|
||||
LIBTRANSMISSION_SRC =
|
||||
transmission.c bencode.c net.c tracker.c peer.c inout.c
|
||||
metainfo.c sha1.c utils.c upload.c fdlimit.c clients.c ;
|
||||
|
||||
Library libtransmission.a : $(LIBTRANSMISSION_SRC) ;
|
||||
ObjectDefines $(LIBTRANSMISSION_SRC) : __TRANSMISSION__ ;
|
||||
205
libtransmission/bencode.c
Normal file
205
libtransmission/bencode.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#define LIST_SIZE 20
|
||||
|
||||
int _tr_bencLoad( char * buf, benc_val_t * val, char ** end )
|
||||
{
|
||||
char * p, * foo;
|
||||
|
||||
if( !end )
|
||||
{
|
||||
/* So we only have to check once */
|
||||
end = &foo;
|
||||
}
|
||||
|
||||
val->begin = buf;
|
||||
|
||||
if( buf[0] == 'i' )
|
||||
{
|
||||
/* Integer: i1242e */
|
||||
val->type = TYPE_INT;
|
||||
val->val.i = strtoll( &buf[1], &p, 10 );
|
||||
|
||||
if( p == &buf[1] || p[0] != 'e' )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
val->end = p + 1;
|
||||
}
|
||||
else if( buf[0] == 'l' || buf[0] == 'd' )
|
||||
{
|
||||
/* List: l<item1><item2>e
|
||||
Dict: d<string1><item1><string2><item2>e
|
||||
A dictionary is just a special kind of list with an even
|
||||
count of items, and where even items are strings. */
|
||||
char * cur;
|
||||
char is_dict;
|
||||
char str_expected;
|
||||
|
||||
is_dict = ( buf[0] == 'd' );
|
||||
val->type = is_dict ? TYPE_DICT : TYPE_LIST;
|
||||
val->val.l.alloc = LIST_SIZE;
|
||||
val->val.l.count = 0;
|
||||
val->val.l.vals = malloc( LIST_SIZE * sizeof( benc_val_t ) );
|
||||
cur = &buf[1];
|
||||
str_expected = 1;
|
||||
while( cur[0] != 'e' )
|
||||
{
|
||||
if( val->val.l.count == val->val.l.alloc )
|
||||
{
|
||||
/* We need a bigger boat */
|
||||
val->val.l.alloc += LIST_SIZE;
|
||||
val->val.l.vals = realloc( val->val.l.vals,
|
||||
val->val.l.alloc * sizeof( benc_val_t ) );
|
||||
}
|
||||
if( tr_bencLoad( cur, &val->val.l.vals[val->val.l.count], &p ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if( is_dict && str_expected &&
|
||||
val->val.l.vals[val->val.l.count].type != TYPE_STR )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
str_expected = !str_expected;
|
||||
|
||||
val->val.l.count++;
|
||||
cur = p;
|
||||
}
|
||||
|
||||
if( is_dict && ( val->val.l.count & 1 ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
val->end = cur + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* String: 12:whateverword */
|
||||
val->type = TYPE_STR;
|
||||
val->val.s.i = strtol( buf, &p, 10 );
|
||||
|
||||
if( p == buf || p[0] != ':' )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
val->val.s.s = malloc( val->val.s.i + 1 );
|
||||
val->val.s.s[val->val.s.i] = 0;
|
||||
memcpy( val->val.s.s, p + 1, val->val.s.i );
|
||||
|
||||
val->end = p + 1 + val->val.s.i;
|
||||
}
|
||||
|
||||
*end = val->end;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __bencPrint( benc_val_t * val, int space )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < space; i++ )
|
||||
{
|
||||
fprintf( stderr, " " );
|
||||
}
|
||||
|
||||
switch( val->type )
|
||||
{
|
||||
case TYPE_INT:
|
||||
fprintf( stderr, "int: %lld\n", val->val.i );
|
||||
break;
|
||||
|
||||
case TYPE_STR:
|
||||
fprintf( stderr, "%s\n", val->val.s.s );
|
||||
break;
|
||||
|
||||
case TYPE_LIST:
|
||||
fprintf( stderr, "list\n" );
|
||||
for( i = 0; i < val->val.l.count; i++ )
|
||||
__bencPrint( &val->val.l.vals[i], space + 1 );
|
||||
break;
|
||||
|
||||
case TYPE_DICT:
|
||||
fprintf( stderr, "dict\n" );
|
||||
for( i = 0; i < val->val.l.count; i++ )
|
||||
__bencPrint( &val->val.l.vals[i], space + 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void tr_bencPrint( benc_val_t * val )
|
||||
{
|
||||
__bencPrint( val, 0 );
|
||||
}
|
||||
|
||||
void tr_bencFree( benc_val_t * val )
|
||||
{
|
||||
int i;
|
||||
|
||||
switch( val->type )
|
||||
{
|
||||
case TYPE_INT:
|
||||
break;
|
||||
|
||||
case TYPE_STR:
|
||||
if( val->val.s.s )
|
||||
{
|
||||
free( val->val.s.s );
|
||||
}
|
||||
break;
|
||||
|
||||
case TYPE_LIST:
|
||||
case TYPE_DICT:
|
||||
for( i = 0; i < val->val.l.count; i++ )
|
||||
{
|
||||
tr_bencFree( &val->val.l.vals[i] );
|
||||
}
|
||||
free( val->val.l.vals );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
benc_val_t * tr_bencDictFind( benc_val_t * val, char * key )
|
||||
{
|
||||
int i;
|
||||
if( val->type != TYPE_DICT )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for( i = 0; i < val->val.l.count; i += 2 )
|
||||
{
|
||||
if( !strcmp( val->val.l.vals[i].val.s.s, key ) )
|
||||
{
|
||||
return &val->val.l.vals[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
58
libtransmission/bencode.h
Normal file
58
libtransmission/bencode.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_BENCODE_H
|
||||
#define TR_BENCODE_H 1
|
||||
|
||||
typedef struct benc_val_s
|
||||
{
|
||||
char * begin;
|
||||
char * end;
|
||||
#define TYPE_INT 1
|
||||
#define TYPE_STR 2
|
||||
#define TYPE_LIST 4
|
||||
#define TYPE_DICT 8
|
||||
char type;
|
||||
union
|
||||
{
|
||||
int64_t i;
|
||||
struct
|
||||
{
|
||||
int i;
|
||||
char * s;
|
||||
} s;
|
||||
struct
|
||||
{
|
||||
int alloc;
|
||||
int count;
|
||||
struct benc_val_s * vals;
|
||||
} l;
|
||||
} val;
|
||||
} benc_val_t;
|
||||
|
||||
#define tr_bencLoad(b,v,e) _tr_bencLoad((char*)(b),v,(char**)e)
|
||||
int _tr_bencLoad( char * buf, benc_val_t * val, char ** end );
|
||||
void tr_bencPrint( benc_val_t * val );
|
||||
void tr_bencFree( benc_val_t * val );
|
||||
benc_val_t * tr_bencDictFind( benc_val_t * val, char * key );
|
||||
|
||||
#endif
|
||||
88
libtransmission/clients.c
Normal file
88
libtransmission/clients.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
char * tr_clientForId( uint8_t * id )
|
||||
{
|
||||
char * ret = NULL;
|
||||
|
||||
if( id[0] == '-' && id[7] == '-' )
|
||||
{
|
||||
if( !memcmp( &id[1], "TR", 2 ) )
|
||||
{
|
||||
asprintf( &ret, "Transmission %d.%d",
|
||||
( id[3] - '0' ) * 10 + ( id[4] - '0' ),
|
||||
( id[5] - '0' ) * 10 + ( id[6] - '0' ) );
|
||||
}
|
||||
else if( !memcmp( &id[1], "AZ", 2 ) )
|
||||
{
|
||||
asprintf( &ret, "Azureus %c.%c.%c.%c",
|
||||
id[3], id[4], id[5], id[6] );
|
||||
}
|
||||
else if( !memcmp( &id[1], "TS", 2 ) )
|
||||
{
|
||||
asprintf( &ret, "TorrentStorm (%c%c%c%c)",
|
||||
id[3], id[4], id[5], id[6] );
|
||||
}
|
||||
else if( !memcmp( &id[1], "BC", 2 ) )
|
||||
{
|
||||
asprintf( &ret, "BitComet %d.%c%c",
|
||||
( id[3] - '0' ) * 10 + ( id[4] - '0' ),
|
||||
id[5], id[6] );
|
||||
}
|
||||
else if( !memcmp( &id[1], "SZ", 2 ) )
|
||||
{
|
||||
asprintf( &ret, "Shareaza %c.%c.%c.%c",
|
||||
id[3], id[4], id[5], id[6] );
|
||||
}
|
||||
}
|
||||
else if( !memcmp( &id[4], "----", 4 ) )
|
||||
{
|
||||
if( id[0] == 'T' )
|
||||
{
|
||||
asprintf( &ret, "BitTornado (%c%c%c)", id[1], id[2], id[3] );
|
||||
}
|
||||
else if( id[0] == 'A' )
|
||||
{
|
||||
asprintf( &ret, "ABC (%c%c%c)", id[1], id[2], id[3] );
|
||||
}
|
||||
}
|
||||
else if( id[0] == 'M' && id[2] == '-' &&
|
||||
id[4] == '-' && id[6] == '-' &&
|
||||
id[7] == '-' )
|
||||
{
|
||||
asprintf( &ret, "BitTorrent %c.%c.%c", id[1], id[3], id[5] );
|
||||
}
|
||||
else if( !memcmp( id, "exbc", 4 ) )
|
||||
{
|
||||
asprintf( &ret, "BitComet %d.%02d", id[4], id[5] );
|
||||
}
|
||||
|
||||
if( !ret )
|
||||
{
|
||||
asprintf( &ret, "Unknown client (%c%c%c%c%c%c%c%c)",
|
||||
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7] );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
23
libtransmission/clients.h
Normal file
23
libtransmission/clients.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
char * tr_clientForId( uint8_t * );
|
||||
288
libtransmission/fastresume.h
Normal file
288
libtransmission/fastresume.h
Normal file
@@ -0,0 +1,288 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* Fast resume
|
||||
***********************************************************************
|
||||
* Format of the resume file:
|
||||
* - 4 bytes: format version (currently 0)
|
||||
* - 4 bytes * number of files: mtimes of files
|
||||
* - 1 bit * number of blocks: whether we have the block or not
|
||||
* - 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>".
|
||||
*
|
||||
* All values are stored in the native endianness. Moving a
|
||||
* libtransmission resume file from an architecture to another will not
|
||||
* work, although it will not hurt either (the mtimes will be wrong,
|
||||
* 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 )
|
||||
{
|
||||
char * ret, * p;
|
||||
int i;
|
||||
|
||||
p = fastResumeFolderName();
|
||||
asprintf( &ret, "%s/resume.%40d", p, 0 );
|
||||
free( p );
|
||||
|
||||
p = &ret[ strlen( ret ) - 2 * SHA_DIGEST_LENGTH ];
|
||||
for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
|
||||
{
|
||||
sprintf( p, "%02x", io->tor->info.hash[i] );
|
||||
p += 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fastResumeMTimes( tr_io_t * io, int * tab )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i;
|
||||
char * path;
|
||||
struct stat sb;
|
||||
|
||||
for( i = 0; i < inf->fileCount; i++ )
|
||||
{
|
||||
asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
|
||||
if( stat( path, &sb ) )
|
||||
{
|
||||
tr_err( "Could not stat '%s'", path );
|
||||
free( path );
|
||||
return 1;
|
||||
}
|
||||
if( ( sb.st_mode & S_IFMT ) != S_IFREG )
|
||||
{
|
||||
tr_err( "Wrong st_mode for '%s'", path );
|
||||
free( path );
|
||||
return 1;
|
||||
}
|
||||
free( path );
|
||||
|
||||
#ifdef SYS_DARWIN
|
||||
tab[i] = ( sb.st_mtimespec.tv_sec & 0x7FFFFFFF );
|
||||
#else
|
||||
tab[i] = ( sb.st_mtime & 0x7FFFFFFF );
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fastResumeSave( tr_io_t * io )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
FILE * file;
|
||||
int version = 0;
|
||||
char * path;
|
||||
int * fileMTimes;
|
||||
int i;
|
||||
uint8_t * blockBitfield;
|
||||
|
||||
/* Get file sizes */
|
||||
fileMTimes = malloc( inf->fileCount * 4 );
|
||||
if( fastResumeMTimes( io, fileMTimes ) )
|
||||
{
|
||||
free( fileMTimes );
|
||||
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" ) ) )
|
||||
{
|
||||
tr_err( "Could not open '%s' for writing", path );
|
||||
free( fileMTimes );
|
||||
free( path );
|
||||
return;
|
||||
}
|
||||
free( path );
|
||||
|
||||
/* Write format version */
|
||||
fwrite( &version, 4, 1, file );
|
||||
|
||||
/* Write file mtimes */
|
||||
fwrite( fileMTimes, 4, inf->fileCount, file );
|
||||
free( fileMTimes );
|
||||
|
||||
/* Build and write the bitfield for blocks */
|
||||
blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
|
||||
for( i = 0; i < tor->blockCount; i++ )
|
||||
{
|
||||
if( tor->blockHave[i] < 0 )
|
||||
{
|
||||
tr_bitfieldAdd( blockBitfield, i );
|
||||
}
|
||||
}
|
||||
fwrite( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
|
||||
free( blockBitfield );
|
||||
|
||||
/* Write the 'slotPiece' table */
|
||||
fwrite( io->slotPiece, 4, inf->pieceCount, file );
|
||||
|
||||
fclose( file );
|
||||
}
|
||||
|
||||
static int fastResumeLoad( tr_io_t * io )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
FILE * file;
|
||||
int version = 0;
|
||||
char * path;
|
||||
int * fileMTimes1, * fileMTimes2;
|
||||
int i, j;
|
||||
uint8_t * blockBitfield;
|
||||
|
||||
int size;
|
||||
|
||||
/* Open resume file */
|
||||
path = fastResumeFileName( io );
|
||||
if( !( file = fopen( path, "r" ) ) )
|
||||
{
|
||||
tr_inf( "Could not open '%s' for reading", path );
|
||||
free( path );
|
||||
return 1;
|
||||
}
|
||||
free( path );
|
||||
|
||||
/* Check the size */
|
||||
size = 4 + 4 * inf->fileCount + 4 * inf->pieceCount +
|
||||
( tor->blockCount + 7 ) / 8;
|
||||
fseek( file, 0, SEEK_END );
|
||||
if( ftell( file ) != size )
|
||||
{
|
||||
tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
|
||||
ftell( file ), size );
|
||||
fclose( file );
|
||||
return 1;
|
||||
}
|
||||
fseek( file, 0, SEEK_SET );
|
||||
|
||||
/* Check format version */
|
||||
fread( &version, 4, 1, file );
|
||||
if( version != 0 )
|
||||
{
|
||||
tr_inf( "Resume file has version %d, not supported",
|
||||
version );
|
||||
fclose( file );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compare file mtimes */
|
||||
fileMTimes1 = malloc( inf->fileCount * 4 );
|
||||
if( fastResumeMTimes( io, fileMTimes1 ) )
|
||||
{
|
||||
free( fileMTimes1 );
|
||||
fclose( file );
|
||||
return 1;
|
||||
}
|
||||
fileMTimes2 = malloc( inf->fileCount * 4 );
|
||||
fread( fileMTimes2, 4, inf->fileCount, file );
|
||||
if( memcmp( fileMTimes1, fileMTimes2, inf->fileCount * 4 ) )
|
||||
{
|
||||
tr_inf( "File mtimes don't match" );
|
||||
free( fileMTimes1 );
|
||||
free( fileMTimes2 );
|
||||
fclose( file );
|
||||
return 1;
|
||||
}
|
||||
free( fileMTimes1 );
|
||||
free( fileMTimes2 );
|
||||
|
||||
/* Load the bitfield for blocks and fill blockHave */
|
||||
blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
|
||||
fread( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
|
||||
tor->blockHaveCount = 0;
|
||||
for( i = 0; i < tor->blockCount; i++ )
|
||||
{
|
||||
if( tr_bitfieldHas( blockBitfield, i ) )
|
||||
{
|
||||
tor->blockHave[i] = -1;
|
||||
(tor->blockHaveCount)++;
|
||||
}
|
||||
}
|
||||
free( blockBitfield );
|
||||
|
||||
/* Load the 'slotPiece' table */
|
||||
fread( io->slotPiece, 4, inf->pieceCount, file );
|
||||
|
||||
fclose( file );
|
||||
|
||||
/* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
|
||||
io->slotsUsed = 0;
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
{
|
||||
io->pieceSlot[i] = -1;
|
||||
for( j = 0; j < inf->pieceCount; j++ )
|
||||
{
|
||||
if( io->slotPiece[j] == i )
|
||||
{
|
||||
// tr_dbg( "Has piece %d in slot %d", i, j );
|
||||
io->pieceSlot[i] = j;
|
||||
io->slotsUsed = MAX( io->slotsUsed, j + 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for( j = tr_pieceStartBlock( i );
|
||||
j < tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i );
|
||||
j++ )
|
||||
{
|
||||
if( tor->blockHave[j] > -1 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j >= tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i ) )
|
||||
{
|
||||
// tr_dbg( "Piece %d is complete", i );
|
||||
tr_bitfieldAdd( tor->bitfield, i );
|
||||
}
|
||||
}
|
||||
// tr_dbg( "Slot used: %d", io->slotsUsed );
|
||||
|
||||
tr_inf( "Fast resuming successful" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
279
libtransmission/fdlimit.c
Normal file
279
libtransmission/fdlimit.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#define TR_MAX_OPEN_FILES 16 /* That is, real files, not sockets */
|
||||
#define TR_RESERVED_FDS 16 /* Number of sockets reserved for
|
||||
connections to trackers */
|
||||
|
||||
typedef struct tr_openFile_s
|
||||
{
|
||||
char path[MAX_PATH_LENGTH];
|
||||
FILE * file;
|
||||
|
||||
#define STATUS_INVALID 1
|
||||
#define STATUS_UNUSED 2
|
||||
#define STATUS_USED 4
|
||||
int status;
|
||||
|
||||
uint64_t date;
|
||||
|
||||
} tr_openFile_t;
|
||||
|
||||
struct tr_fd_s
|
||||
{
|
||||
tr_lock_t lock;
|
||||
|
||||
int reserved;
|
||||
|
||||
int normal;
|
||||
int normalMax;
|
||||
|
||||
tr_openFile_t open[TR_MAX_OPEN_FILES];
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* tr_fdInit
|
||||
**********************************************************************/
|
||||
tr_fd_t * tr_fdInit()
|
||||
{
|
||||
tr_fd_t * f;
|
||||
int i, j, s[4096];
|
||||
|
||||
f = calloc( sizeof( tr_fd_t ), 1 );
|
||||
|
||||
/* Init lock */
|
||||
tr_lockInit( &f->lock );
|
||||
|
||||
/* Detect the maximum number of open files or sockets */
|
||||
for( i = 0; i < 4096; i++ )
|
||||
{
|
||||
if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for( j = 0; j < i; j++ )
|
||||
{
|
||||
tr_netClose( s[j] );
|
||||
}
|
||||
|
||||
tr_dbg( "%d usable file descriptors", i );
|
||||
|
||||
f->reserved = 0;
|
||||
f->normal = 0;
|
||||
|
||||
f->normalMax = i - TR_RESERVED_FDS - 10;
|
||||
/* To be safe, in case the UI needs to write a preferences file
|
||||
or something */
|
||||
|
||||
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||||
{
|
||||
f->open[i].status = STATUS_INVALID;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_fdFileOpen
|
||||
**********************************************************************/
|
||||
FILE * tr_fdFileOpen( tr_fd_t * f, char * path )
|
||||
{
|
||||
int i, winner;
|
||||
uint64_t date;
|
||||
|
||||
tr_lockLock( f->lock );
|
||||
|
||||
/* Is it already open? */
|
||||
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||||
{
|
||||
if( f->open[i].status > STATUS_INVALID &&
|
||||
!strcmp( path, f->open[i].path ) )
|
||||
{
|
||||
winner = i;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Can we open one more file? */
|
||||
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||||
{
|
||||
if( f->open[i].status & STATUS_INVALID )
|
||||
{
|
||||
winner = i;
|
||||
goto open;
|
||||
}
|
||||
}
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
/* Close the oldest currently unused file */
|
||||
date = tr_date() + 1;
|
||||
winner = -1;
|
||||
|
||||
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||||
{
|
||||
if( f->open[i].status & STATUS_USED )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if( f->open[i].date < date )
|
||||
{
|
||||
winner = i;
|
||||
date = f->open[i].date;
|
||||
}
|
||||
}
|
||||
|
||||
if( winner >= 0 )
|
||||
{
|
||||
tr_dbg( "Closing %s", f->open[winner].path );
|
||||
fclose( f->open[winner].file );
|
||||
goto open;
|
||||
}
|
||||
|
||||
/* All used! Wait a bit and try again */
|
||||
tr_lockUnlock( f->lock );
|
||||
tr_wait( 10 );
|
||||
tr_lockLock( f->lock );
|
||||
}
|
||||
|
||||
open:
|
||||
tr_dbg( "Opening %s", path );
|
||||
snprintf( f->open[winner].path, MAX_PATH_LENGTH, "%s", path );
|
||||
f->open[winner].file = fopen( path, "r+" );
|
||||
|
||||
done:
|
||||
f->open[winner].status = STATUS_USED;
|
||||
f->open[winner].date = tr_date();
|
||||
tr_lockUnlock( f->lock );
|
||||
|
||||
return f->open[winner].file;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_fdFileRelease
|
||||
**********************************************************************/
|
||||
void tr_fdFileRelease( tr_fd_t * f, FILE * file )
|
||||
{
|
||||
int i;
|
||||
tr_lockLock( f->lock );
|
||||
|
||||
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||||
{
|
||||
if( f->open[i].file == file )
|
||||
{
|
||||
f->open[i].status = STATUS_UNUSED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tr_lockUnlock( f->lock );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_fdFileClose
|
||||
**********************************************************************/
|
||||
void tr_fdFileClose( tr_fd_t * f, char * path )
|
||||
{
|
||||
int i;
|
||||
|
||||
tr_lockLock( f->lock );
|
||||
|
||||
/* Is it already open? */
|
||||
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||||
{
|
||||
if( f->open[i].status & STATUS_INVALID )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if( !strcmp( path, f->open[i].path ) )
|
||||
{
|
||||
tr_dbg( "Closing %s", path );
|
||||
fclose( f->open[i].file );
|
||||
f->open[i].status = STATUS_INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tr_lockUnlock( f->lock );
|
||||
}
|
||||
|
||||
int tr_fdSocketWillCreate( tr_fd_t * f, int reserved )
|
||||
{
|
||||
int ret;
|
||||
|
||||
tr_lockLock( f->lock );
|
||||
|
||||
if( reserved )
|
||||
{
|
||||
if( f->reserved < TR_RESERVED_FDS )
|
||||
{
|
||||
ret = 0;
|
||||
(f->reserved)++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( f->normal < f->normalMax )
|
||||
{
|
||||
ret = 0;
|
||||
(f->normal)++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
tr_lockUnlock( f->lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tr_fdSocketClosed( tr_fd_t * f, int reserved )
|
||||
{
|
||||
tr_lockLock( f->lock );
|
||||
|
||||
if( reserved )
|
||||
{
|
||||
(f->reserved)--;
|
||||
}
|
||||
else
|
||||
{
|
||||
(f->normal)--;
|
||||
}
|
||||
|
||||
tr_lockUnlock( f->lock );
|
||||
}
|
||||
|
||||
void tr_fdClose( tr_fd_t * f )
|
||||
{
|
||||
tr_lockClose( f->lock );
|
||||
free( f );
|
||||
}
|
||||
|
||||
34
libtransmission/fdlimit.h
Normal file
34
libtransmission/fdlimit.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
typedef struct tr_fd_s tr_fd_t;
|
||||
|
||||
tr_fd_t * tr_fdInit();
|
||||
|
||||
FILE * tr_fdFileOpen ( tr_fd_t *, char * );
|
||||
void tr_fdFileRelease ( tr_fd_t *, FILE * );
|
||||
void tr_fdFileClose ( tr_fd_t *, char * );
|
||||
|
||||
int tr_fdSocketWillCreate ( tr_fd_t *, int );
|
||||
void tr_fdSocketClosed ( tr_fd_t *, int );
|
||||
|
||||
void tr_fdClose ( tr_fd_t * );
|
||||
601
libtransmission/inout.c
Normal file
601
libtransmission/inout.c
Normal file
@@ -0,0 +1,601 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#ifdef SYS_BEOS
|
||||
# define fseeko _fseek
|
||||
#endif
|
||||
|
||||
struct tr_io_s
|
||||
{
|
||||
tr_torrent_t * tor;
|
||||
|
||||
/* Position of pieces
|
||||
-1 = we haven't started to download this piece yet
|
||||
n = we have started or completed the piece in slot n */
|
||||
int * pieceSlot;
|
||||
|
||||
/* Pieces in slot
|
||||
-1 = unused slot
|
||||
n = piece n */
|
||||
int * slotPiece;
|
||||
|
||||
int slotsUsed;
|
||||
};
|
||||
|
||||
#include "fastresume.h"
|
||||
|
||||
/***********************************************************************
|
||||
* Local prototypes
|
||||
**********************************************************************/
|
||||
static int createFiles( tr_io_t * );
|
||||
static int checkFiles( tr_io_t * );
|
||||
static void closeFiles( tr_io_t * );
|
||||
static int readOrWriteBytes( tr_io_t *, uint64_t, int, uint8_t *, int );
|
||||
static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
|
||||
int * size, int write );
|
||||
static void findSlotForPiece( tr_io_t *, int );
|
||||
|
||||
#define readBytes(io,o,s,b) readOrWriteBytes(io,o,s,b,0)
|
||||
#define writeBytes(io,o,s,b) readOrWriteBytes(io,o,s,b,1)
|
||||
|
||||
#define readSlot(io,sl,b,s) readOrWriteSlot(io,sl,b,s,0)
|
||||
#define writeSlot(io,sl,b,s) readOrWriteSlot(io,sl,b,s,1)
|
||||
|
||||
/***********************************************************************
|
||||
* tr_ioInit
|
||||
***********************************************************************
|
||||
* Open all files we are going to write to
|
||||
**********************************************************************/
|
||||
tr_io_t * tr_ioInit( tr_torrent_t * tor )
|
||||
{
|
||||
tr_io_t * io;
|
||||
|
||||
io = malloc( sizeof( tr_io_t ) );
|
||||
io->tor = tor;
|
||||
|
||||
if( createFiles( io ) || checkFiles( io ) )
|
||||
{
|
||||
free( io );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_ioRead
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
int tr_ioRead( tr_io_t * io, int index, int begin, int length,
|
||||
uint8_t * buf )
|
||||
{
|
||||
uint64_t offset;
|
||||
tr_info_t * inf = &io->tor->info;
|
||||
|
||||
offset = (uint64_t) io->pieceSlot[index] *
|
||||
(uint64_t) inf->pieceSize + (uint64_t) begin;
|
||||
|
||||
return readBytes( io, offset, length, buf );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_ioWrite
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
int tr_ioWrite( tr_io_t * io, int index, int begin, int length,
|
||||
uint8_t * buf )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &io->tor->info;
|
||||
uint64_t offset;
|
||||
int i;
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
uint8_t * pieceBuf;
|
||||
int pieceSize;
|
||||
int startBlock, endBlock;
|
||||
|
||||
if( io->pieceSlot[index] < 0 )
|
||||
{
|
||||
findSlotForPiece( io, index );
|
||||
tr_inf( "Piece %d: starting in slot %d", index,
|
||||
io->pieceSlot[index] );
|
||||
}
|
||||
|
||||
offset = (uint64_t) io->pieceSlot[index] *
|
||||
(uint64_t) inf->pieceSize + (uint64_t) begin;
|
||||
|
||||
if( writeBytes( io, offset, length, buf ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
startBlock = tr_pieceStartBlock( index );
|
||||
endBlock = startBlock + tr_pieceCountBlocks( index );
|
||||
for( i = startBlock; i < endBlock; i++ )
|
||||
{
|
||||
if( tor->blockHave[i] >= 0 )
|
||||
{
|
||||
/* The piece is not complete */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The piece is complete, check the hash */
|
||||
pieceSize = tr_pieceSize( index );
|
||||
pieceBuf = malloc( pieceSize );
|
||||
readBytes( io, (uint64_t) io->pieceSlot[index] *
|
||||
(uint64_t) inf->pieceSize, pieceSize, pieceBuf );
|
||||
SHA1( pieceBuf, pieceSize, hash );
|
||||
free( pieceBuf );
|
||||
|
||||
if( memcmp( hash, &inf->pieces[20*index], SHA_DIGEST_LENGTH ) )
|
||||
{
|
||||
tr_inf( "Piece %d (slot %d): hash FAILED", index,
|
||||
io->pieceSlot[index] );
|
||||
|
||||
/* We will need to reload the whole piece */
|
||||
for( i = startBlock; i < endBlock; i++ )
|
||||
{
|
||||
tor->blockHave[i] = 0;
|
||||
tor->blockHaveCount -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_inf( "Piece %d (slot %d): hash OK", index,
|
||||
io->pieceSlot[index] );
|
||||
tr_bitfieldAdd( tor->bitfield, index );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tr_ioClose( tr_io_t * io )
|
||||
{
|
||||
closeFiles( io );
|
||||
|
||||
fastResumeSave( io );
|
||||
|
||||
free( io->pieceSlot );
|
||||
free( io->slotPiece );
|
||||
free( io );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* createFiles
|
||||
***********************************************************************
|
||||
* Make sure the existing folders/files have correct types and
|
||||
* permissions, and create missing folders and files
|
||||
**********************************************************************/
|
||||
static int createFiles( tr_io_t * io )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i;
|
||||
char * path, * p;
|
||||
struct stat sb;
|
||||
FILE * file;
|
||||
|
||||
tr_dbg( "Creating files..." );
|
||||
|
||||
for( i = 0; i < inf->fileCount; i++ )
|
||||
{
|
||||
asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
|
||||
|
||||
/* Create folders */
|
||||
p = path;
|
||||
while( ( p = strchr( p, '/' ) ) )
|
||||
{
|
||||
*p = '\0';
|
||||
if( stat( path, &sb ) )
|
||||
{
|
||||
/* Folder doesn't exist yet */
|
||||
mkdir( path, 0755 );
|
||||
}
|
||||
else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
|
||||
{
|
||||
/* Node exists but isn't a folder */
|
||||
printf( "Remove %s, it's in the way.\n", path );
|
||||
free( path );
|
||||
return 1;
|
||||
}
|
||||
*p = '/';
|
||||
p++;
|
||||
}
|
||||
|
||||
if( stat( path, &sb ) )
|
||||
{
|
||||
/* File doesn't exist yet */
|
||||
if( !( file = fopen( path, "w" ) ) )
|
||||
{
|
||||
tr_err( "Could not create `%s' (%s)", path,
|
||||
strerror( errno ) );
|
||||
free( path );
|
||||
return 1;
|
||||
}
|
||||
fclose( file );
|
||||
}
|
||||
else if( ( sb.st_mode & S_IFMT ) != S_IFREG )
|
||||
{
|
||||
/* Node exists but isn't a file */
|
||||
printf( "Remove %s, it's in the way.\n", path );
|
||||
free( path );
|
||||
return 1;
|
||||
}
|
||||
|
||||
free( path );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* checkFiles
|
||||
***********************************************************************
|
||||
* Look for pieces
|
||||
**********************************************************************/
|
||||
static int checkFiles( tr_io_t * io )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i;
|
||||
uint8_t * buf;
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
int startBlock, endBlock;
|
||||
|
||||
io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
|
||||
io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
|
||||
|
||||
if( !fastResumeLoad( io ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
tr_dbg( "Checking pieces..." );
|
||||
|
||||
/* Yet we don't have anything */
|
||||
memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
|
||||
memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
|
||||
memset( tor->bitfield, 0, ( inf->pieceCount + 7 ) / 8 );
|
||||
memset( tor->blockHave, 0, tor->blockCount );
|
||||
tor->blockHaveCount = 0;
|
||||
|
||||
/* Check pieces */
|
||||
io->slotsUsed = 0;
|
||||
buf = malloc( inf->pieceSize );
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
{
|
||||
int size, j;
|
||||
|
||||
if( readSlot( io, i, buf, &size ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
io->slotsUsed = i + 1;
|
||||
SHA1( buf, size, hash );
|
||||
|
||||
for( j = i; j < inf->pieceCount - 1; j++ )
|
||||
{
|
||||
if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
|
||||
{
|
||||
int k;
|
||||
io->pieceSlot[j] = i;
|
||||
io->slotPiece[i] = j;
|
||||
tr_bitfieldAdd( tor->bitfield, j );
|
||||
|
||||
startBlock = tr_pieceStartBlock( j );
|
||||
endBlock = startBlock + tr_pieceCountBlocks( j );
|
||||
for( k = startBlock; k < endBlock; k++ )
|
||||
{
|
||||
tor->blockHave[k] = -1;
|
||||
tor->blockHaveCount++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( io->slotPiece[i] > -1 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Special case for the last piece */
|
||||
SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
|
||||
if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
|
||||
SHA_DIGEST_LENGTH ) )
|
||||
{
|
||||
io->pieceSlot[inf->pieceCount - 1] = i;
|
||||
io->slotPiece[i] = inf->pieceCount - 1;
|
||||
tr_bitfieldAdd( tor->bitfield, inf->pieceCount - 1 );
|
||||
|
||||
startBlock = tr_pieceStartBlock( inf->pieceCount - 1 );
|
||||
endBlock = startBlock +
|
||||
tr_pieceCountBlocks( inf->pieceCount - 1 );
|
||||
for( j = startBlock; j < endBlock; j++ )
|
||||
{
|
||||
tor->blockHave[j] = -1;
|
||||
tor->blockHaveCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
free( buf );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* closeFiles
|
||||
**********************************************************************/
|
||||
static void closeFiles( tr_io_t * io )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i;
|
||||
char * path;
|
||||
|
||||
for( i = 0; i < inf->fileCount; i++ )
|
||||
{
|
||||
asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
|
||||
tr_fdFileClose( tor->fdlimit, path );
|
||||
free( path );
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* readOrWriteBytes
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
|
||||
uint8_t * buf, int write )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int piece = offset / inf->pieceSize;
|
||||
int begin = offset % inf->pieceSize;
|
||||
|
||||
int i;
|
||||
uint64_t foo;
|
||||
uint64_t posInFile = 0;
|
||||
int willRead;
|
||||
char * path;
|
||||
FILE * file;
|
||||
|
||||
/* We can't ever read or write more than a piece at a time */
|
||||
if( tr_pieceSize( piece ) < begin + size )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Find which file we shall start reading/writing in */
|
||||
foo = 0;
|
||||
for( i = 0; i < inf->fileCount; i++ )
|
||||
{
|
||||
if( offset < foo + inf->files[i].length )
|
||||
{
|
||||
posInFile = offset - foo;
|
||||
break;
|
||||
}
|
||||
foo += inf->files[i].length;
|
||||
}
|
||||
|
||||
while( size > 0 )
|
||||
{
|
||||
asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
|
||||
file = tr_fdFileOpen( tor->fdlimit, path );
|
||||
free( path );
|
||||
|
||||
if( !file )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
willRead = MIN( inf->files[i].length - posInFile,
|
||||
(uint64_t) size );
|
||||
|
||||
if( fseeko( file, posInFile, SEEK_SET ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if( write )
|
||||
{
|
||||
if( fwrite( buf, willRead, 1, file ) != 1 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( fread( buf, willRead, 1, file ) != 1 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
tr_fdFileRelease( tor->fdlimit, file );
|
||||
|
||||
/* 'willRead' less bytes to do */
|
||||
size -= willRead;
|
||||
buf += willRead;
|
||||
|
||||
/* Go to the beginning of the next file */
|
||||
i += 1;
|
||||
posInFile = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* readSlot
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
|
||||
int * size, int write )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
|
||||
|
||||
*size = 0;
|
||||
if( slot == inf->pieceCount - 1 )
|
||||
{
|
||||
*size = inf->totalSize % inf->pieceSize;
|
||||
}
|
||||
if( !*size )
|
||||
{
|
||||
*size = inf->pieceSize;
|
||||
}
|
||||
|
||||
return readOrWriteBytes( io, offset, *size, buf, write );
|
||||
}
|
||||
|
||||
static void invertSlots( tr_io_t * io, int slot1, int slot2 )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
uint8_t * buf1, * buf2;
|
||||
int piece1, piece2, foo;
|
||||
|
||||
buf1 = calloc( inf->pieceSize, 1 );
|
||||
buf2 = calloc( inf->pieceSize, 1 );
|
||||
|
||||
readSlot( io, slot1, buf1, &foo );
|
||||
readSlot( io, slot2, buf2, &foo );
|
||||
|
||||
writeSlot( io, slot1, buf2, &foo );
|
||||
writeSlot( io, slot2, buf1, &foo );
|
||||
|
||||
free( buf1 );
|
||||
free( buf2 );
|
||||
|
||||
piece1 = io->slotPiece[slot1];
|
||||
piece2 = io->slotPiece[slot2];
|
||||
io->slotPiece[slot1] = piece2;
|
||||
io->slotPiece[slot2] = piece1;
|
||||
if( piece1 >= 0 )
|
||||
{
|
||||
io->pieceSlot[piece1] = slot2;
|
||||
}
|
||||
if( piece2 >= 0 )
|
||||
{
|
||||
io->pieceSlot[piece2] = slot1;
|
||||
}
|
||||
}
|
||||
|
||||
static void reorderPieces( tr_io_t * io )
|
||||
{
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i, didInvert;
|
||||
|
||||
/* Try to move pieces to their final places */
|
||||
do
|
||||
{
|
||||
didInvert = 0;
|
||||
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
{
|
||||
if( io->pieceSlot[i] < 0 )
|
||||
{
|
||||
/* We haven't started this piece yet */
|
||||
continue;
|
||||
}
|
||||
if( io->pieceSlot[i] == i )
|
||||
{
|
||||
/* Already in place */
|
||||
continue;
|
||||
}
|
||||
if( i >= io->slotsUsed )
|
||||
{
|
||||
/* File is not big enough yet */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Move piece i into slot i */
|
||||
tr_inf( "invert %d and %d", io->pieceSlot[i], i );
|
||||
invertSlots( io, i, io->pieceSlot[i] );
|
||||
didInvert = 1;
|
||||
}
|
||||
} while( didInvert );
|
||||
}
|
||||
|
||||
static void findSlotForPiece( tr_io_t * io, int piece )
|
||||
{
|
||||
int i;
|
||||
#if 0
|
||||
tr_torrent_t * tor = io->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
tr_dbg( "Entering findSlotForPiece" );
|
||||
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
printf( "%02d ", io->slotPiece[i] );
|
||||
printf( "\n" );
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
printf( "%02d ", io->pieceSlot[i] );
|
||||
printf( "\n" );
|
||||
#endif
|
||||
|
||||
/* Look for an empty slot somewhere */
|
||||
for( i = 0; i < io->slotsUsed; i++ )
|
||||
{
|
||||
if( io->slotPiece[i] < 0 )
|
||||
{
|
||||
io->pieceSlot[piece] = i;
|
||||
io->slotPiece[i] = piece;
|
||||
goto reorder;
|
||||
}
|
||||
}
|
||||
|
||||
/* No empty slot, extend the file */
|
||||
io->pieceSlot[piece] = io->slotsUsed;
|
||||
io->slotPiece[io->slotsUsed] = piece;
|
||||
(io->slotsUsed)++;
|
||||
|
||||
reorder:
|
||||
reorderPieces( io );
|
||||
|
||||
#if 0
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
printf( "%02d ", io->slotPiece[i] );
|
||||
printf( "\n" );
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
printf( "%02d ", io->pieceSlot[i] );
|
||||
printf( "\n" );
|
||||
|
||||
printf( "Leaving findSlotForPiece\n" );
|
||||
#endif
|
||||
}
|
||||
33
libtransmission/inout.h
Normal file
33
libtransmission/inout.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_IO_H
|
||||
#define TR_IO_H 1
|
||||
|
||||
typedef struct tr_io_s tr_io_t;
|
||||
|
||||
tr_io_t * tr_ioInit ( tr_torrent_t * );
|
||||
int tr_ioRead ( tr_io_t *, int, int, int, uint8_t * );
|
||||
int tr_ioWrite ( tr_io_t *, int, int, int, uint8_t * );
|
||||
void tr_ioClose ( tr_io_t * );
|
||||
|
||||
#endif
|
||||
184
libtransmission/internal.h
Normal file
184
libtransmission/internal.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_INTERNAL_H
|
||||
#define TR_INTERNAL_H 1
|
||||
|
||||
/* Standard headers used here and there.
|
||||
That is probably ugly to put them all here, but it is sooo
|
||||
convenient */
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef BEOS_NETSERVER
|
||||
# define in_port_t uint16_t
|
||||
#else
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
/* We use OpenSSL whenever possible, since it is likely to be more
|
||||
optimized and it is ok to use it with a MIT-licensed application.
|
||||
Otherwise, we use the included implementation by vi@nwr.jp. */
|
||||
#ifdef HAVE_OPENSSL
|
||||
# undef SHA_DIGEST_LENGTH
|
||||
# include <openssl/sha.h>
|
||||
#else
|
||||
# include "sha1.h"
|
||||
# define SHA1(p,i,h) \
|
||||
{ \
|
||||
sha1_state_s pms; \
|
||||
sha1_init( &pms ); \
|
||||
sha1_update( &pms, (sha1_byte_t *) p, i ); \
|
||||
sha1_finish( &pms, (sha1_byte_t *) h ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Convenient macros to perform uint32_t endian conversions with
|
||||
char pointers */
|
||||
#define TR_NTOHL(p,a) (a) = ntohl(*((uint32_t*)(p)))
|
||||
#define TR_HTONL(a,p) *((uint32_t*)(p)) = htonl((a))
|
||||
|
||||
/* Multithreading support: native threads on BeOS, pthreads elsewhere */
|
||||
#ifdef SYS_BEOS
|
||||
# include <kernel/OS.h>
|
||||
# define tr_thread_t thread_id
|
||||
# define tr_threadCreate(pt,f,d) *(pt) = spawn_thread((void*)f,"",10,d); \
|
||||
resume_thread(*(pt));
|
||||
# define tr_threadJoin(t) { long e; wait_for_thread(t,&e); }
|
||||
# define tr_lock_t sem_id
|
||||
# define tr_lockInit(pl) *(pl) = create_sem(1,"")
|
||||
# define tr_lockLock(l) acquire_sem(l)
|
||||
# define tr_lockUnlock(l) release_sem(l)
|
||||
# define tr_lockClose(l) delete_sem(l)
|
||||
#else
|
||||
# include <pthread.h>
|
||||
# define tr_thread_t pthread_t
|
||||
# define tr_threadCreate(pt,f,d) pthread_create(pt,NULL,(void*)f,d)
|
||||
# define tr_threadJoin(t) pthread_join(t,NULL)
|
||||
# define tr_lock_t pthread_mutex_t
|
||||
# define tr_lockInit(pl) pthread_mutex_init(pl,NULL)
|
||||
# define tr_lockLock(l) pthread_mutex_lock(&l)
|
||||
# define tr_lockUnlock(l) pthread_mutex_unlock(&l)
|
||||
# define tr_lockClose(l) pthread_mutex_destroy(&l)
|
||||
#endif
|
||||
|
||||
/* Sometimes the system defines MAX/MIN, sometimes not. In the latter
|
||||
case, define those here since we will use them */
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a)>(b)?(b):(a))
|
||||
#endif
|
||||
|
||||
#define TR_MAX_PEER_COUNT 60
|
||||
|
||||
typedef struct tr_torrent_s tr_torrent_t;
|
||||
|
||||
#include "bencode.h"
|
||||
#include "metainfo.h"
|
||||
#include "tracker.h"
|
||||
#include "peer.h"
|
||||
#include "net.h"
|
||||
#include "inout.h"
|
||||
#include "upload.h"
|
||||
#include "fdlimit.h"
|
||||
#include "clients.h"
|
||||
|
||||
struct tr_torrent_s
|
||||
{
|
||||
tr_info_t info;
|
||||
|
||||
tr_upload_t * upload;
|
||||
tr_fd_t * fdlimit;
|
||||
|
||||
int status;
|
||||
char error[128];
|
||||
|
||||
char * id;
|
||||
|
||||
/* An escaped string used to include the hash in HTTP queries */
|
||||
char hashString[3*SHA_DIGEST_LENGTH+1];
|
||||
|
||||
char scrape[MAX_PATH_LENGTH];
|
||||
|
||||
/* Where to download */
|
||||
char * destination;
|
||||
|
||||
/* How many bytes we ask for per request */
|
||||
int blockSize;
|
||||
int blockCount;
|
||||
|
||||
/* Status for each block
|
||||
-1 = we have it
|
||||
n = we are downloading it from n peers */
|
||||
char * blockHave;
|
||||
int blockHaveCount;
|
||||
uint8_t * bitfield;
|
||||
|
||||
volatile char die;
|
||||
tr_thread_t thread;
|
||||
tr_lock_t lock;
|
||||
|
||||
tr_tracker_t * tracker;
|
||||
tr_io_t * io;
|
||||
|
||||
int bindSocket;
|
||||
int bindPort;
|
||||
int peerCount;
|
||||
tr_peer_t * peers[TR_MAX_PEER_COUNT];
|
||||
|
||||
uint64_t dates[10];
|
||||
uint64_t downloaded[10];
|
||||
uint64_t uploaded[10];
|
||||
};
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
struct tr_handle_s
|
||||
{
|
||||
int torrentCount;
|
||||
tr_torrent_t * torrents[TR_MAX_TORRENT_COUNT];
|
||||
|
||||
tr_upload_t * upload;
|
||||
tr_fd_t * fdlimit;
|
||||
|
||||
int bindPort;
|
||||
|
||||
char id[21];
|
||||
};
|
||||
|
||||
#endif
|
||||
282
libtransmission/metainfo.c
Normal file
282
libtransmission/metainfo.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
/***********************************************************************
|
||||
* Local prototypes
|
||||
**********************************************************************/
|
||||
static void strcatUTF8( char *, char * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_metainfoParse
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
int tr_metainfoParse( tr_info_t * inf, const char * path )
|
||||
{
|
||||
FILE * file;
|
||||
char * buf;
|
||||
benc_val_t meta, * beInfo, * list, * val;
|
||||
char * s, * s2, * s3;
|
||||
int i;
|
||||
struct stat sb;
|
||||
|
||||
snprintf( inf->torrent, MAX_PATH_LENGTH, path );
|
||||
|
||||
if( stat( path, &sb ) )
|
||||
{
|
||||
fprintf( stderr, "Could not stat file (%s)\n", path );
|
||||
return 1;
|
||||
}
|
||||
if( ( sb.st_mode & S_IFMT ) != S_IFREG )
|
||||
{
|
||||
fprintf( stderr, "Not a regular file (%s)\n", path );
|
||||
return 1;
|
||||
}
|
||||
if( sb.st_size > 2097152 )
|
||||
{
|
||||
tr_err( "Torrent file is too big (%d bytes)", sb.st_size );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Load the torrent file into our buffer */
|
||||
file = fopen( path, "rb" );
|
||||
if( !file )
|
||||
{
|
||||
fprintf( stderr, "Could not open file (%s)\n", path );
|
||||
return 1;
|
||||
}
|
||||
buf = malloc( sb.st_size );
|
||||
fseek( file, 0, SEEK_SET );
|
||||
if( fread( buf, sb.st_size, 1, file ) != 1 )
|
||||
{
|
||||
fprintf( stderr, "Read error (%s)\n", path );
|
||||
free( buf );
|
||||
fclose( file );
|
||||
return 1;
|
||||
}
|
||||
fclose( file );
|
||||
|
||||
/* Parse bencoded infos */
|
||||
if( tr_bencLoad( buf, &meta, NULL ) )
|
||||
{
|
||||
fprintf( stderr, "Error while parsing bencoded data\n" );
|
||||
free( buf );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get info hash */
|
||||
if( !( beInfo = tr_bencDictFind( &meta, "info" ) ) )
|
||||
{
|
||||
fprintf( stderr, "Could not find \"info\" dictionary\n" );
|
||||
tr_bencFree( &meta );
|
||||
free( buf );
|
||||
return 1;
|
||||
}
|
||||
SHA1( (uint8_t *) beInfo->begin,
|
||||
(long) beInfo->end - (long) beInfo->begin, inf->hash );
|
||||
|
||||
/* No that we got the hash, we won't need this anymore */
|
||||
free( buf );
|
||||
|
||||
if( !( val = tr_bencDictFind( &meta, "announce" ) ) )
|
||||
{
|
||||
fprintf( stderr, "No \"announce\" entry\n" );
|
||||
tr_bencFree( &meta );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Skip spaces */
|
||||
s3 = val->val.s.s;
|
||||
while( *s3 && *s3 == ' ' )
|
||||
{
|
||||
s3++;
|
||||
}
|
||||
|
||||
/* Parse announce URL */
|
||||
if( strncmp( s3, "http://", 7 ) )
|
||||
{
|
||||
fprintf( stderr, "Invalid announce URL (%s)\n",
|
||||
inf->trackerAddress );
|
||||
tr_bencFree( &meta );
|
||||
return 1;
|
||||
}
|
||||
s = strchr( s3 + 7, ':' );
|
||||
s2 = strchr( s3 + 7, '/' );
|
||||
if( s && s < s2 )
|
||||
{
|
||||
memcpy( inf->trackerAddress, s3 + 7,
|
||||
(long) s - (long) s3 - 7 );
|
||||
inf->trackerPort = atoi( s + 1 );
|
||||
}
|
||||
else if( s2 )
|
||||
{
|
||||
memcpy( inf->trackerAddress, s3 + 7,
|
||||
(long) s2 - (long) s3 - 7 );
|
||||
inf->trackerPort = 80;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Invalid announce URL (%s)\n",
|
||||
inf->trackerAddress );
|
||||
tr_bencFree( &meta );
|
||||
return 1;
|
||||
}
|
||||
snprintf( inf->trackerAnnounce, MAX_PATH_LENGTH, s2 );
|
||||
|
||||
/* Piece length */
|
||||
if( !( val = tr_bencDictFind( beInfo, "piece length" ) ) )
|
||||
{
|
||||
fprintf( stderr, "No \"piece length\" entry\n" );
|
||||
tr_bencFree( &meta );
|
||||
return 1;
|
||||
}
|
||||
inf->pieceSize = val->val.i;
|
||||
|
||||
/* Hashes */
|
||||
val = tr_bencDictFind( beInfo, "pieces" );
|
||||
if( val->val.s.i % SHA_DIGEST_LENGTH )
|
||||
{
|
||||
fprintf( stderr, "Invalid \"piece\" string (size is %d)\n",
|
||||
val->val.s.i );
|
||||
return 1;
|
||||
}
|
||||
inf->pieceCount = val->val.s.i / SHA_DIGEST_LENGTH;
|
||||
inf->pieces = (uint8_t *) val->val.s.s; /* Ugly, but avoids a memcpy */
|
||||
val->val.s.s = NULL;
|
||||
|
||||
/* TODO add more tests so we don't crash on weird files */
|
||||
|
||||
inf->totalSize = 0;
|
||||
if( ( list = tr_bencDictFind( beInfo, "files" ) ) )
|
||||
{
|
||||
/* Multi-file mode */
|
||||
int j;
|
||||
|
||||
val = tr_bencDictFind( beInfo, "name" );
|
||||
strcatUTF8( inf->name, val->val.s.s );
|
||||
|
||||
inf->fileCount = list->val.l.count;
|
||||
inf->files = calloc( inf->fileCount * sizeof( tr_file_t ), 1 );
|
||||
|
||||
for( i = 0; i < list->val.l.count; i++ )
|
||||
{
|
||||
val = tr_bencDictFind( &list->val.l.vals[i], "path" );
|
||||
strcatUTF8( inf->files[i].name, inf->name );
|
||||
for( j = 0; j < val->val.l.count; j++ )
|
||||
{
|
||||
strcatUTF8( inf->files[i].name, "/" );
|
||||
strcatUTF8( inf->files[i].name,
|
||||
val->val.l.vals[j].val.s.s );
|
||||
}
|
||||
val = tr_bencDictFind( &list->val.l.vals[i], "length" );
|
||||
inf->files[i].length = val->val.i;
|
||||
inf->totalSize += val->val.i;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Single-file mode */
|
||||
inf->fileCount = 1;
|
||||
inf->files = calloc( sizeof( tr_file_t ), 1 );
|
||||
|
||||
val = tr_bencDictFind( beInfo, "name" );
|
||||
strcatUTF8( inf->files[0].name, val->val.s.s );
|
||||
strcatUTF8( inf->name, val->val.s.s );
|
||||
|
||||
val = tr_bencDictFind( beInfo, "length" );
|
||||
inf->files[0].length = val->val.i;
|
||||
inf->totalSize += val->val.i;
|
||||
}
|
||||
|
||||
if( (uint64_t) inf->pieceCount !=
|
||||
( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
|
||||
{
|
||||
fprintf( stderr, "Size of hashes and files don't match\n" );
|
||||
tr_bencFree( &meta );
|
||||
return 1;
|
||||
}
|
||||
|
||||
tr_bencFree( &meta );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* strcatUTF8
|
||||
***********************************************************************
|
||||
* According to the official specification, all strings in the torrent
|
||||
* file are supposed to be UTF-8 encoded. However, there are
|
||||
* non-compliant torrents around... If we encounter an invalid UTF-8
|
||||
* character, we assume it is ISO 8859-1 and convert it to UTF-8.
|
||||
**********************************************************************/
|
||||
static void strcatUTF8( char * s, char * append )
|
||||
{
|
||||
char * p;
|
||||
|
||||
/* Go to the end of the destination string */
|
||||
while( s[0] )
|
||||
{
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Now start appending, converting on the fly if necessary */
|
||||
for( p = append; p[0]; )
|
||||
{
|
||||
if( !( p[0] & 0x80 ) )
|
||||
{
|
||||
/* ASCII character */
|
||||
*(s++) = *(p++);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ( p[0] & 0xE0 ) == 0xC0 && ( p[1] & 0xC0 ) == 0x80 )
|
||||
{
|
||||
/* 2-bytes UTF-8 character */
|
||||
*(s++) = *(p++); *(s++) = *(p++);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ( p[0] & 0xF0 ) == 0xE0 && ( p[1] & 0xC0 ) == 0x80 &&
|
||||
( p[2] & 0xC0 ) == 0x80 )
|
||||
{
|
||||
/* 3-bytes UTF-8 character */
|
||||
*(s++) = *(p++); *(s++) = *(p++);
|
||||
*(s++) = *(p++);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ( p[0] & 0xF8 ) == 0xF0 && ( p[1] & 0xC0 ) == 0x80 &&
|
||||
( p[2] & 0xC0 ) == 0x80 && ( p[3] & 0xC0 ) == 0x80 )
|
||||
{
|
||||
/* 4-bytes UTF-8 character */
|
||||
*(s++) = *(p++); *(s++) = *(p++);
|
||||
*(s++) = *(p++); *(s++) = *(p++);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ISO 8859-1 -> UTF-8 conversion */
|
||||
*(s++) = 0xC0 | ( ( *p & 0xFF ) >> 6 );
|
||||
*(s++) = 0x80 | ( *(p++) & 0x3F );
|
||||
}
|
||||
}
|
||||
28
libtransmission/metainfo.h
Normal file
28
libtransmission/metainfo.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_METAINFO_H
|
||||
#define TR_METAINFO_H 1
|
||||
|
||||
int tr_metainfoParse( tr_info_t *, const char * );
|
||||
|
||||
#endif
|
||||
225
libtransmission/net.c
Normal file
225
libtransmission/net.c
Normal file
@@ -0,0 +1,225 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
static int makeSocketNonBlocking( int s )
|
||||
{
|
||||
int flags;
|
||||
|
||||
#ifdef SYS_BEOS
|
||||
flags = 1;
|
||||
if( setsockopt( s, SOL_SOCKET, SO_NONBLOCK,
|
||||
&flags, sizeof( int ) ) < 0 )
|
||||
#else
|
||||
if( ( flags = fcntl( s, F_GETFL, 0 ) ) < 0 ||
|
||||
fcntl( s, F_SETFL, flags | O_NONBLOCK ) < 0 )
|
||||
#endif
|
||||
{
|
||||
tr_err( "Could not set socket to non-blocking mode (%s)",
|
||||
strerror( errno ) );
|
||||
tr_netClose( s );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int createSocket()
|
||||
{
|
||||
int s;
|
||||
|
||||
s = socket( AF_INET, SOCK_STREAM, 0 );
|
||||
if( s < 0 )
|
||||
{
|
||||
tr_err( "Could not create socket (%s)", strerror( errno ) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return makeSocketNonBlocking( s );
|
||||
}
|
||||
|
||||
int tr_netResolve( char * address, struct in_addr * addr )
|
||||
{
|
||||
struct hostent * host;
|
||||
|
||||
addr->s_addr = inet_addr( address );
|
||||
if( addr->s_addr != 0xFFFFFFFF )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !( host = gethostbyname( address ) ) )
|
||||
{
|
||||
tr_err( "Could not resolve (%s)", address );
|
||||
return -1;
|
||||
}
|
||||
memcpy( addr, host->h_addr, host->h_length );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tr_netOpen( struct in_addr addr, in_port_t port )
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_in sock;
|
||||
|
||||
s = createSocket();
|
||||
if( s < 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset( &sock, 0, sizeof( sock ) );
|
||||
sock.sin_family = AF_INET;
|
||||
sock.sin_addr.s_addr = addr.s_addr;
|
||||
sock.sin_port = port;
|
||||
|
||||
if( connect( s, (struct sockaddr *) &sock,
|
||||
sizeof( struct sockaddr_in ) ) < 0 &&
|
||||
errno != EINPROGRESS )
|
||||
{
|
||||
tr_err( "Could not connect socket (%s)", strerror( errno ) );
|
||||
tr_netClose( s );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int tr_netBind( int * port )
|
||||
{
|
||||
int s, i;
|
||||
struct sockaddr_in sock;
|
||||
int minPort, maxPort;
|
||||
|
||||
s = createSocket();
|
||||
if( s < 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
minPort = *port;
|
||||
maxPort = minPort + 1000;
|
||||
maxPort = MIN( maxPort, 65535 );
|
||||
|
||||
for( i = minPort; i <= maxPort; i++ )
|
||||
{
|
||||
memset( &sock, 0, sizeof( sock ) );
|
||||
sock.sin_family = AF_INET;
|
||||
sock.sin_addr.s_addr = INADDR_ANY;
|
||||
sock.sin_port = htons( i );
|
||||
|
||||
if( !bind( s, (struct sockaddr *) &sock,
|
||||
sizeof( struct sockaddr_in ) ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( i > maxPort )
|
||||
{
|
||||
tr_netClose( s );
|
||||
tr_err( "Could not bind any port from %d to %d",
|
||||
minPort, maxPort );
|
||||
return -1;
|
||||
}
|
||||
|
||||
tr_inf( "Binded port %d", i );
|
||||
*port = i;
|
||||
listen( s, 5 );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int tr_netAccept( int s, struct in_addr * addr, in_port_t * port )
|
||||
{
|
||||
int t;
|
||||
unsigned len;
|
||||
struct sockaddr_in sock;
|
||||
|
||||
len = sizeof( sock );
|
||||
t = accept( s, (struct sockaddr *) &sock, &len );
|
||||
|
||||
if( t < 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
*addr = sock.sin_addr;
|
||||
*port = sock.sin_port;
|
||||
|
||||
return makeSocketNonBlocking( t );
|
||||
}
|
||||
|
||||
int tr_netSend( int s, uint8_t * buf, int size )
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = send( s, buf, size, 0 );
|
||||
if( ret < 0 )
|
||||
{
|
||||
if( errno == ENOTCONN || errno == EAGAIN || errno == EWOULDBLOCK )
|
||||
{
|
||||
ret = TR_NET_BLOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = TR_NET_CLOSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tr_netRecv( int s, uint8_t * buf, int size )
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = recv( s, buf, size, 0 );
|
||||
if( ret < 0 )
|
||||
{
|
||||
if( errno == EAGAIN || errno == EWOULDBLOCK )
|
||||
{
|
||||
ret = TR_NET_BLOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = TR_NET_CLOSE;
|
||||
}
|
||||
}
|
||||
if( !ret )
|
||||
{
|
||||
ret = TR_NET_CLOSE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tr_netClose( int s )
|
||||
{
|
||||
#ifdef BEOS_NETSERVER
|
||||
closesocket( s );
|
||||
#else
|
||||
close( s );
|
||||
#endif
|
||||
}
|
||||
32
libtransmission/net.h
Normal file
32
libtransmission/net.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
int tr_netResolve ( char *, struct in_addr * );
|
||||
int tr_netOpen ( struct in_addr addr, in_port_t port );
|
||||
int tr_netBind ( int * );
|
||||
int tr_netAccept ( int s, struct in_addr *, in_port_t * );
|
||||
void tr_netClose ( int s );
|
||||
|
||||
#define TR_NET_BLOCK 0x80000000
|
||||
#define TR_NET_CLOSE 0x40000000
|
||||
int tr_netSend ( int s, uint8_t * buf, int size );
|
||||
int tr_netRecv ( int s, uint8_t * buf, int size );
|
||||
464
libtransmission/peer.c
Normal file
464
libtransmission/peer.c
Normal file
@@ -0,0 +1,464 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#define MAX_REQUEST_COUNT 32
|
||||
#define OUR_REQUEST_COUNT 8 /* TODO: we should detect if we are on a
|
||||
high-speed network and adapt */
|
||||
|
||||
typedef struct tr_request_s
|
||||
{
|
||||
int index;
|
||||
int begin;
|
||||
int length;
|
||||
|
||||
} tr_request_t;
|
||||
|
||||
struct tr_peer_s
|
||||
{
|
||||
struct in_addr addr;
|
||||
in_port_t port;
|
||||
|
||||
#define PEER_STATUS_IDLE 1 /* Need to connect */
|
||||
#define PEER_STATUS_CONNECTING 2 /* Trying to send handshake */
|
||||
#define PEER_STATUS_HANDSHAKE 4 /* Waiting for peer's handshake */
|
||||
#define PEER_STATUS_CONNECTED 8 /* Got peer's handshake */
|
||||
int status;
|
||||
int socket;
|
||||
uint64_t date;
|
||||
uint64_t keepAlive;
|
||||
|
||||
char amChoking;
|
||||
char amInterested;
|
||||
char peerChoking;
|
||||
char peerInterested;
|
||||
|
||||
uint8_t id[20];
|
||||
|
||||
uint8_t * bitfield;
|
||||
|
||||
uint8_t * buf;
|
||||
int size;
|
||||
int pos;
|
||||
|
||||
uint8_t * outMessages;
|
||||
int outMessagesSize;
|
||||
int outMessagesPos;
|
||||
uint8_t outBlock[13+16384];
|
||||
int outBlockSize;
|
||||
int outBlockLoaded;
|
||||
int outBlockSending;
|
||||
|
||||
int inRequestCount;
|
||||
tr_request_t inRequests[OUR_REQUEST_COUNT];
|
||||
int inIndex;
|
||||
int inBegin;
|
||||
int inLength;
|
||||
uint64_t inTotal;
|
||||
|
||||
int outRequestCount;
|
||||
tr_request_t outRequests[MAX_REQUEST_COUNT];
|
||||
uint64_t outTotal;
|
||||
uint64_t outDate;
|
||||
int outSlow;
|
||||
};
|
||||
|
||||
#define peer_dbg( a... ) __peer_dbg( peer, ## a )
|
||||
static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
|
||||
{
|
||||
char string[256];
|
||||
va_list args;
|
||||
|
||||
va_start( args, msg );
|
||||
sprintf( string, "%08x:%04x ",
|
||||
(uint32_t) peer->addr.s_addr, peer->port );
|
||||
vsnprintf( &string[14], sizeof( string ) - 14, msg, args );
|
||||
va_end( args );
|
||||
|
||||
tr_dbg( "%s", string );
|
||||
}
|
||||
|
||||
#include "peermessages.h"
|
||||
#include "peerutils.h"
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerAddOld
|
||||
***********************************************************************
|
||||
* Tries to add a peer given its IP and port (received from a tracker
|
||||
* which doesn't support the "compact" extension).
|
||||
**********************************************************************/
|
||||
void tr_peerAddOld( tr_torrent_t * tor, char * ip, int port )
|
||||
{
|
||||
struct in_addr addr;
|
||||
|
||||
if( tr_netResolve( ip, &addr ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
addWithAddr( tor, addr, htons( port ) );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerAddCompact
|
||||
***********************************************************************
|
||||
* Tries to add a peer. If 's' is a negative value, will use 'addr' and
|
||||
* 'port' to connect to the peer. Otherwise, use the already connected
|
||||
* socket 's'.
|
||||
**********************************************************************/
|
||||
void tr_peerAddCompact( tr_torrent_t * tor, struct in_addr addr,
|
||||
in_port_t port, int s )
|
||||
{
|
||||
tr_peer_t * peer;
|
||||
|
||||
if( s < 0 )
|
||||
{
|
||||
addWithAddr( tor, addr, port );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !( peer = peerInit( tor ) ) )
|
||||
{
|
||||
tr_netClose( s );
|
||||
tr_fdSocketClosed( tor->fdlimit, 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
peer->socket = s;
|
||||
peer->addr = addr;
|
||||
peer->port = port;
|
||||
peer->status = PEER_STATUS_CONNECTING;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerRem
|
||||
***********************************************************************
|
||||
* Frees and closes everything related to the peer at index 'i', and
|
||||
* removes it from the peers list.
|
||||
**********************************************************************/
|
||||
void tr_peerRem( tr_torrent_t * tor, int i )
|
||||
{
|
||||
tr_peer_t * peer = tor->peers[i];
|
||||
int j;
|
||||
|
||||
for( j = 0; j < peer->inRequestCount; j++ )
|
||||
{
|
||||
tr_request_t * r;
|
||||
int block;
|
||||
|
||||
r = &peer->inRequests[j];
|
||||
block = tr_block( r->index,r->begin );
|
||||
if( tor->blockHave[block] > 0 )
|
||||
{
|
||||
(tor->blockHave[block])--;
|
||||
}
|
||||
}
|
||||
if( !peer->amChoking )
|
||||
{
|
||||
tr_uploadChoked( tor->upload );
|
||||
}
|
||||
if( peer->bitfield )
|
||||
{
|
||||
free( peer->bitfield );
|
||||
}
|
||||
if( peer->buf )
|
||||
{
|
||||
free( peer->buf );
|
||||
}
|
||||
if( peer->outMessages )
|
||||
{
|
||||
free( peer->outMessages );
|
||||
}
|
||||
if( peer->status > PEER_STATUS_IDLE )
|
||||
{
|
||||
tr_netClose( peer->socket );
|
||||
tr_fdSocketClosed( tor->fdlimit, 0 );
|
||||
}
|
||||
free( peer );
|
||||
tor->peerCount--;
|
||||
memmove( &tor->peers[i], &tor->peers[i+1],
|
||||
( tor->peerCount - i ) * sizeof( tr_peer_t * ) );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerPulse
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
void tr_peerPulse( tr_torrent_t * tor )
|
||||
{
|
||||
int i, ret, size;
|
||||
uint8_t * p;
|
||||
tr_peer_t * peer;
|
||||
|
||||
tor->dates[9] = tr_date();
|
||||
if( tor->dates[9] > tor->dates[8] + 1000 )
|
||||
{
|
||||
memmove( &tor->downloaded[0], &tor->downloaded[1],
|
||||
9 * sizeof( uint64_t ) );
|
||||
memmove( &tor->uploaded[0], &tor->uploaded[1],
|
||||
9 * sizeof( uint64_t ) );
|
||||
memmove( &tor->dates[0], &tor->dates[1],
|
||||
9 * sizeof( uint64_t ) );
|
||||
|
||||
for( i = 0; i < tor->peerCount; )
|
||||
{
|
||||
if( checkPeer( tor, i ) )
|
||||
{
|
||||
tr_peerRem( tor, i );
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for incoming connections */
|
||||
if( tor->bindSocket > -1 &&
|
||||
tor->peerCount < TR_MAX_PEER_COUNT &&
|
||||
!tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
|
||||
{
|
||||
int s;
|
||||
struct in_addr addr;
|
||||
in_port_t port;
|
||||
s = tr_netAccept( tor->bindSocket, &addr, &port );
|
||||
if( s > -1 )
|
||||
{
|
||||
tr_peerAddCompact( tor, addr, port, s );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_fdSocketClosed( tor->fdlimit, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Shuffle peers */
|
||||
if( tor->peerCount > 1 )
|
||||
{
|
||||
peer = tor->peers[0];
|
||||
memmove( &tor->peers[0], &tor->peers[1],
|
||||
( tor->peerCount - 1 ) * sizeof( void * ) );
|
||||
tor->peers[tor->peerCount - 1] = peer;
|
||||
}
|
||||
|
||||
/* Handle peers */
|
||||
for( i = 0; i < tor->peerCount; )
|
||||
{
|
||||
peer = tor->peers[i];
|
||||
|
||||
/* Connect */
|
||||
if( ( peer->status & PEER_STATUS_IDLE ) &&
|
||||
!tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
|
||||
{
|
||||
peer->socket = tr_netOpen( peer->addr, peer->port );
|
||||
if( peer->socket < 0 )
|
||||
{
|
||||
peer_dbg( "connection failed" );
|
||||
goto dropPeer;
|
||||
}
|
||||
peer->status = PEER_STATUS_CONNECTING;
|
||||
}
|
||||
|
||||
/* Try to send handshake */
|
||||
if( peer->status & PEER_STATUS_CONNECTING )
|
||||
{
|
||||
uint8_t buf[68];
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
buf[0] = 19;
|
||||
memcpy( &buf[1], "BitTorrent protocol", 19 );
|
||||
memset( &buf[20], 0, 8 );
|
||||
memcpy( &buf[28], inf->hash, 20 );
|
||||
memcpy( &buf[48], tor->id, 20 );
|
||||
|
||||
ret = tr_netSend( peer->socket, buf, 68 );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
peer_dbg( "connection closed" );
|
||||
goto dropPeer;
|
||||
}
|
||||
else if( !( ret & TR_NET_BLOCK ) )
|
||||
{
|
||||
peer_dbg( "SEND handshake" );
|
||||
peer->status = PEER_STATUS_HANDSHAKE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to read */
|
||||
if( peer->status >= PEER_STATUS_HANDSHAKE )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
if( peer->size < 1 )
|
||||
{
|
||||
peer->size = 1024;
|
||||
peer->buf = malloc( peer->size );
|
||||
}
|
||||
else if( peer->pos >= peer->size )
|
||||
{
|
||||
peer->size *= 2;
|
||||
peer->buf = realloc( peer->buf, peer->size );
|
||||
}
|
||||
ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
|
||||
peer->size - peer->pos );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
peer_dbg( "connection closed" );
|
||||
goto dropPeer;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
break;
|
||||
}
|
||||
peer->date = tr_date();
|
||||
peer->pos += ret;
|
||||
if( parseMessage( tor, peer, ret ) )
|
||||
{
|
||||
goto dropPeer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to write */
|
||||
writeBegin:
|
||||
|
||||
/* Send all smaller messages regardless of the upload cap */
|
||||
while( ( p = messagesPending( peer, &size ) ) )
|
||||
{
|
||||
ret = tr_netSend( peer->socket, p, size );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
goto dropPeer;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
goto writeEnd;
|
||||
}
|
||||
messagesSent( peer, ret );
|
||||
}
|
||||
|
||||
/* Send pieces if we can */
|
||||
while( ( p = blockPending( tor, peer, &size ) ) )
|
||||
{
|
||||
if( !tr_uploadCanUpload( tor->upload ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ret = tr_netSend( peer->socket, p, size );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
goto dropPeer;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
blockSent( peer, ret );
|
||||
tr_uploadUploaded( tor->upload, ret );
|
||||
|
||||
tor->uploaded[9] += ret;
|
||||
peer->outTotal += ret;
|
||||
peer->outDate = tr_date();
|
||||
|
||||
/* In case this block is done, you may have messages
|
||||
pending. Send them before we start the next block */
|
||||
goto writeBegin;
|
||||
}
|
||||
writeEnd:
|
||||
|
||||
/* Connected peers: ask for a block whenever possible */
|
||||
if( peer->status & PEER_STATUS_CONNECTED )
|
||||
{
|
||||
if( tor->blockHaveCount < tor->blockCount &&
|
||||
!peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
|
||||
{
|
||||
/* This peer is no use to us, and it seems there are
|
||||
more */
|
||||
peer_dbg( "not interesting" );
|
||||
tr_peerRem( tor, i );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( peer->amInterested && !peer->peerChoking )
|
||||
{
|
||||
int block;
|
||||
while( peer->inRequestCount < OUR_REQUEST_COUNT )
|
||||
{
|
||||
block = chooseBlock( tor, peer );
|
||||
if( block < 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
sendRequest( tor, peer, block );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
continue;
|
||||
|
||||
dropPeer:
|
||||
tr_peerRem( tor, i );
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerIsConnected
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
int tr_peerIsConnected( tr_peer_t * peer )
|
||||
{
|
||||
return peer->status & PEER_STATUS_CONNECTED;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerIsUploading
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
int tr_peerIsUploading( tr_peer_t * peer )
|
||||
{
|
||||
return ( peer->inRequestCount > 0 );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerIsDownloading
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
int tr_peerIsDownloading( tr_peer_t * peer )
|
||||
{
|
||||
return peer->outBlockSending;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_peerBitfield
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
uint8_t * tr_peerBitfield( tr_peer_t * peer )
|
||||
{
|
||||
return peer->bitfield;
|
||||
}
|
||||
38
libtransmission/peer.h
Normal file
38
libtransmission/peer.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_PEER_H
|
||||
#define TR_PEER_H 1
|
||||
|
||||
typedef struct tr_peer_s tr_peer_t;
|
||||
|
||||
void tr_peerAddOld ( tr_torrent_t *, char *, int );
|
||||
void tr_peerAddCompact ( tr_torrent_t *, struct in_addr,
|
||||
in_port_t, int );
|
||||
void tr_peerRem ( tr_torrent_t *, int );
|
||||
void tr_peerPulse ( tr_torrent_t * );
|
||||
int tr_peerIsConnected ( tr_peer_t * );
|
||||
int tr_peerIsUploading ( tr_peer_t * );
|
||||
int tr_peerIsDownloading ( tr_peer_t * );
|
||||
uint8_t * tr_peerBitfield ( tr_peer_t * );
|
||||
|
||||
#endif
|
||||
306
libtransmission/peermessages.h
Normal file
306
libtransmission/peermessages.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
static uint8_t * messagesPending( tr_peer_t * peer, int * size )
|
||||
{
|
||||
if( peer->outBlockSending || peer->outMessagesPos < 1 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*size = MIN( peer->outMessagesPos, 1024 );
|
||||
|
||||
return peer->outMessages;
|
||||
}
|
||||
|
||||
static void messagesSent( tr_peer_t * peer, int size )
|
||||
{
|
||||
peer->outMessagesPos -= size;
|
||||
memmove( peer->outMessages, &peer->outMessages[size],
|
||||
peer->outMessagesPos );
|
||||
}
|
||||
|
||||
static uint8_t * blockPending( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
int * size )
|
||||
{
|
||||
if( !peer->outBlockLoaded )
|
||||
{
|
||||
uint8_t * p;
|
||||
tr_request_t * r;
|
||||
|
||||
if( peer->outRequestCount < 1 )
|
||||
{
|
||||
/* No piece to send */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We need to load the block for the next request */
|
||||
r = &peer->outRequests[0];
|
||||
p = (uint8_t *) peer->outBlock;
|
||||
|
||||
TR_HTONL( 9 + r->length, p );
|
||||
p[4] = 7;
|
||||
TR_HTONL( r->index, p + 5 );
|
||||
TR_HTONL( r->begin, p + 9 );
|
||||
|
||||
tr_ioRead( tor->io, r->index, r->begin, r->length, &p[13] );
|
||||
|
||||
peer_dbg( "SEND piece %d/%d (%d bytes)",
|
||||
r->index, r->begin, r->length );
|
||||
|
||||
peer->outBlockSize = 13 + r->length;
|
||||
peer->outBlockLoaded = 1;
|
||||
|
||||
(peer->outRequestCount)--;
|
||||
memmove( &peer->outRequests[0], &peer->outRequests[1],
|
||||
peer->outRequestCount * sizeof( tr_request_t ) );
|
||||
}
|
||||
|
||||
*size = MIN( 1024, peer->outBlockSize );
|
||||
|
||||
return (uint8_t *) peer->outBlock;
|
||||
}
|
||||
|
||||
static void blockSent( tr_peer_t * peer, int size )
|
||||
{
|
||||
peer->outBlockSize -= size;
|
||||
memmove( peer->outBlock, &peer->outBlock[size], peer->outBlockSize );
|
||||
|
||||
if( peer->outBlockSize > 0 )
|
||||
{
|
||||
/* We can't send messages until we are done sending the block */
|
||||
peer->outBlockSending = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Block fully sent */
|
||||
peer->outBlockSending = 0;
|
||||
peer->outBlockLoaded = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t * getPointerForSize( tr_peer_t * peer, int size )
|
||||
{
|
||||
uint8_t * p;
|
||||
|
||||
if( peer->outMessagesPos + size > peer->outMessagesSize )
|
||||
{
|
||||
peer->outMessagesSize = peer->outMessagesPos + size;
|
||||
peer->outMessages = realloc( peer->outMessages,
|
||||
peer->outMessagesSize );
|
||||
}
|
||||
|
||||
p = &peer->outMessages[peer->outMessagesPos];
|
||||
peer->outMessagesPos += size;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* sendKeepAlive
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static void sendKeepAlive( tr_peer_t * peer )
|
||||
{
|
||||
uint8_t * p;
|
||||
|
||||
p = getPointerForSize( peer, 4 );
|
||||
|
||||
TR_HTONL( 0, p );
|
||||
|
||||
peer_dbg( "SEND keep-alive" );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* sendChoke
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static void sendChoke( tr_peer_t * peer, int yes )
|
||||
{
|
||||
uint8_t * p;
|
||||
|
||||
p = getPointerForSize( peer, 5 );
|
||||
|
||||
TR_HTONL( 1, p );
|
||||
p[4] = yes ? 0 : 1;
|
||||
|
||||
peer->amChoking = yes;
|
||||
|
||||
if( yes )
|
||||
{
|
||||
/* Drop all pending requests */
|
||||
peer->outRequestCount = 0;
|
||||
}
|
||||
|
||||
peer_dbg( "SEND %schoke", yes ? "" : "un" );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* sendInterest
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static void sendInterest( tr_peer_t * peer, int yes )
|
||||
{
|
||||
uint8_t * p;
|
||||
|
||||
p = getPointerForSize( peer, 5 );
|
||||
|
||||
TR_HTONL( 1, p );
|
||||
p[4] = yes ? 2 : 3;
|
||||
|
||||
peer->amInterested = yes;
|
||||
|
||||
peer_dbg( "SEND %sinterested", yes ? "" : "un" );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* sendHave
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static void sendHave( tr_peer_t * peer, int piece )
|
||||
{
|
||||
uint8_t * p;
|
||||
|
||||
p = getPointerForSize( peer, 9 );
|
||||
|
||||
TR_HTONL( 5, &p[0] );
|
||||
p[4] = 4;
|
||||
TR_HTONL( piece, &p[5] );
|
||||
|
||||
peer_dbg( "SEND have %d", piece );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* sendBitfield
|
||||
***********************************************************************
|
||||
* Builds a 'bitfield' message:
|
||||
* - size = 5 + X (4 bytes)
|
||||
* - id = 5 (1 byte)
|
||||
* - bitfield (X bytes)
|
||||
**********************************************************************/
|
||||
static void sendBitfield( tr_torrent_t * tor, tr_peer_t * peer )
|
||||
{
|
||||
uint8_t * p;
|
||||
int bitfieldSize = ( tor->info.pieceCount + 7 ) / 8;
|
||||
|
||||
p = getPointerForSize( peer, 5 + bitfieldSize );
|
||||
|
||||
TR_HTONL( 1 + bitfieldSize, p );
|
||||
p[4] = 5;
|
||||
memcpy( &p[5], tor->bitfield, bitfieldSize );
|
||||
|
||||
peer_dbg( "SEND bitfield" );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* sendRequest
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static void sendRequest( tr_torrent_t * tor, tr_peer_t * peer, int block )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
tr_request_t * r;
|
||||
uint8_t * p;
|
||||
|
||||
/* Get the piece the block is a part of, its position in the piece
|
||||
and its size */
|
||||
r = &peer->inRequests[peer->inRequestCount];
|
||||
r->index = block / ( inf->pieceSize / tor->blockSize );
|
||||
r->begin = ( block % ( inf->pieceSize / tor->blockSize ) ) *
|
||||
tor->blockSize;
|
||||
r->length = tor->blockSize;
|
||||
if( block == tor->blockCount - 1 )
|
||||
{
|
||||
int lastSize = inf->totalSize % tor->blockSize;
|
||||
if( lastSize )
|
||||
{
|
||||
r->length = lastSize;
|
||||
}
|
||||
}
|
||||
(peer->inRequestCount)++;
|
||||
|
||||
/* Build the "ask" message */
|
||||
p = getPointerForSize( peer, 17 );
|
||||
|
||||
TR_HTONL( 13, p );
|
||||
p[4] = 6;
|
||||
TR_HTONL( r->index, p + 5 );
|
||||
TR_HTONL( r->begin, p + 9 );
|
||||
TR_HTONL( r->length, p + 13 );
|
||||
|
||||
/* Remember that we have one more uploader for this block */
|
||||
(tor->blockHave[block])++;
|
||||
|
||||
peer_dbg( "SEND request %d/%d (%d bytes)",
|
||||
r->index, r->begin, r->length );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* sendCancel
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static void sendCancel( tr_torrent_t * tor, int block )
|
||||
{
|
||||
int i, j;
|
||||
uint8_t * p;
|
||||
tr_peer_t * peer;
|
||||
tr_request_t * r;
|
||||
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
peer = tor->peers[i];
|
||||
|
||||
for( j = 1; j < peer->inRequestCount; j++ )
|
||||
{
|
||||
r = &peer->inRequests[j];
|
||||
|
||||
if( block != tr_block( r->index, r->begin ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
p = getPointerForSize( peer, 17 );
|
||||
|
||||
/* Build the "cancel" message */
|
||||
TR_HTONL( 13, p );
|
||||
p[4] = 8;
|
||||
TR_HTONL( r->index, p + 5 );
|
||||
TR_HTONL( r->begin, p + 9 );
|
||||
TR_HTONL( r->length, p + 13 );
|
||||
|
||||
peer_dbg( "SEND cancel %d/%d (%d bytes)",
|
||||
r->index, r->begin, r->length );
|
||||
|
||||
(peer->inRequestCount)--;
|
||||
memmove( &peer->inRequests[j], &peer->inRequests[j+1],
|
||||
( peer->inRequestCount - j ) * sizeof( tr_request_t ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
827
libtransmission/peerutils.h
Normal file
827
libtransmission/peerutils.h
Normal file
@@ -0,0 +1,827 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer );
|
||||
|
||||
/***********************************************************************
|
||||
* peerInit
|
||||
***********************************************************************
|
||||
* Returns NULL if we reached the maximum authorized number of peers.
|
||||
* Otherwise, allocates a new tr_peer_t, add it to the peers list and
|
||||
* returns a pointer to it.
|
||||
**********************************************************************/
|
||||
static tr_peer_t * peerInit( tr_torrent_t * tor )
|
||||
{
|
||||
tr_peer_t * peer;
|
||||
|
||||
if( tor->peerCount >= TR_MAX_PEER_COUNT )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer = calloc( sizeof( tr_peer_t ), 1 );
|
||||
peer->amChoking = 1;
|
||||
peer->peerChoking = 1;
|
||||
peer->date = tr_date();
|
||||
peer->keepAlive = peer->date;
|
||||
|
||||
tor->peers[tor->peerCount++] = peer;
|
||||
return peer;
|
||||
}
|
||||
|
||||
static int peerCmp( tr_peer_t * peer1, tr_peer_t * peer2 )
|
||||
{
|
||||
/* Wait until we got the peers' ids */
|
||||
if( peer1->status < PEER_STATUS_CONNECTED ||
|
||||
peer2->status < PEER_STATUS_CONNECTED )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return memcmp( peer1->id, peer2->id, 20 );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* addWithAddr
|
||||
***********************************************************************
|
||||
* Does nothing if we already have a peer matching 'addr' and 'port'.
|
||||
* Otherwise adds such a new peer.
|
||||
**********************************************************************/
|
||||
static void addWithAddr( tr_torrent_t * tor, struct in_addr addr,
|
||||
in_port_t port )
|
||||
{
|
||||
int i;
|
||||
tr_peer_t * peer;
|
||||
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
peer = tor->peers[i];
|
||||
if( peer->addr.s_addr == addr.s_addr &&
|
||||
peer->port == port )
|
||||
{
|
||||
/* We are already connected to this peer */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( !( peer = peerInit( tor ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
peer->addr = addr;
|
||||
peer->port = port;
|
||||
peer->status = PEER_STATUS_IDLE;
|
||||
}
|
||||
|
||||
static int checkPeer( tr_torrent_t * tor, int i )
|
||||
{
|
||||
tr_peer_t * peer = tor->peers[i];
|
||||
|
||||
if( peer->status < PEER_STATUS_CONNECTED &&
|
||||
tr_date() > peer->date + 8000 )
|
||||
{
|
||||
/* If it has been too long, don't wait for the socket
|
||||
to timeout - forget about it now */
|
||||
peer_dbg( "connection timeout" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Drop peers who haven't even sent a keep-alive within the
|
||||
last 3 minutes */
|
||||
if( tr_date() > peer->date + 180000 )
|
||||
{
|
||||
peer_dbg( "read timeout" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Drop peers which are supposed to upload but actually
|
||||
haven't sent anything within the last minute */
|
||||
if( peer->inRequestCount && tr_date() > peer->date + 60000 )
|
||||
{
|
||||
peer_dbg( "bad uploader" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Choke unchoked peers we are not sending anything to */
|
||||
if( !peer->amChoking && tr_date() > peer->outDate + 10000 )
|
||||
{
|
||||
peer_dbg( "not worth the unchoke" );
|
||||
if( sendChoke( peer, 1 ) )
|
||||
{
|
||||
goto dropPeer;
|
||||
}
|
||||
peer->outSlow = 1;
|
||||
tr_uploadChoked( tor->upload );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( peer->status & PEER_STATUS_CONNECTED )
|
||||
{
|
||||
/* Send keep-alive every 2 minutes */
|
||||
if( tr_date() > peer->keepAlive + 120000 )
|
||||
{
|
||||
sendKeepAlive( peer );
|
||||
peer->keepAlive = tr_date();
|
||||
}
|
||||
|
||||
/* Choke or unchoke some people */
|
||||
/* TODO: prefer people who upload to us */
|
||||
if( !peer->amChoking && !peer->peerInterested )
|
||||
{
|
||||
/* He doesn't need us */
|
||||
sendChoke( peer, 1 );
|
||||
tr_uploadChoked( tor->upload );
|
||||
}
|
||||
if( peer->amChoking && peer->peerInterested &&
|
||||
!peer->outSlow && tr_uploadCanUnchoke( tor->upload ) )
|
||||
{
|
||||
sendChoke( peer, 0 );
|
||||
tr_uploadUnchoked( tor->upload );
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
***********************************************************************
|
||||
* Returns 1 if 'peer' has at least one piece that we haven't completed,
|
||||
* or 0 otherwise.
|
||||
**********************************************************************/
|
||||
static int isInteresting( tr_torrent_t * tor, tr_peer_t * peer )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i;
|
||||
int bitfieldSize = ( inf->pieceCount + 7 ) / 8;
|
||||
|
||||
if( !peer->bitfield )
|
||||
{
|
||||
/* We don't know what this peer has */
|
||||
return 0;
|
||||
}
|
||||
|
||||
for( i = 0; i < bitfieldSize; i++ )
|
||||
{
|
||||
if( ( peer->bitfield[i] & ~(tor->bitfield[i]) ) & 0xFF )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer )
|
||||
{
|
||||
int interested = isInteresting( tor, peer );
|
||||
|
||||
if( interested && !peer->amInterested )
|
||||
{
|
||||
sendInterest( peer, 1 );
|
||||
}
|
||||
if( !interested && peer->amInterested )
|
||||
{
|
||||
sendInterest( peer, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* chooseBlock
|
||||
***********************************************************************
|
||||
* At this point, we know the peer has at least one block we have an
|
||||
* interest in. If he has more than one, we choose which one we are
|
||||
* going to ask first.
|
||||
* 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 )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i, j;
|
||||
int startBlock, endBlock, countBlocks;
|
||||
int missingBlocks, minMissing;
|
||||
int poolSize, * pool;
|
||||
int block, minDownloading;
|
||||
|
||||
/* Choose a piece */
|
||||
pool = malloc( inf->pieceCount * sizeof( int ) );
|
||||
poolSize = 0;
|
||||
minMissing = tor->blockCount + 1;
|
||||
for( i = 0; i < inf->pieceCount; i++ )
|
||||
{
|
||||
if( !tr_bitfieldHas( peer->bitfield, i ) )
|
||||
{
|
||||
/* The peer doesn't have this piece */
|
||||
continue;
|
||||
}
|
||||
if( tr_bitfieldHas( tor->bitfield, i ) )
|
||||
{
|
||||
/* We already have it */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Count how many blocks from this piece are missing */
|
||||
startBlock = tr_pieceStartBlock( i );
|
||||
countBlocks = tr_pieceCountBlocks( i );
|
||||
endBlock = startBlock + countBlocks;
|
||||
missingBlocks = countBlocks;
|
||||
for( j = startBlock; j < endBlock; j++ )
|
||||
{
|
||||
/* TODO: optimize */
|
||||
if( tor->blockHave[j] )
|
||||
{
|
||||
missingBlocks--;
|
||||
}
|
||||
if( missingBlocks > minMissing )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( missingBlocks < 1 )
|
||||
{
|
||||
/* We are already downloading all blocks */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We are interested in this piece, remember it */
|
||||
if( missingBlocks < minMissing )
|
||||
{
|
||||
minMissing = missingBlocks;
|
||||
poolSize = 0;
|
||||
}
|
||||
if( missingBlocks <= minMissing )
|
||||
{
|
||||
pool[poolSize++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
if( poolSize )
|
||||
{
|
||||
/* All pieces in 'pool' have 'minMissing' missing blocks. Find
|
||||
the rarest ones. */
|
||||
uint8_t * bitfield;
|
||||
int piece;
|
||||
int min, foo, j;
|
||||
int * pool2;
|
||||
int pool2Size;
|
||||
|
||||
pool2 = malloc( poolSize * sizeof( int ) );
|
||||
pool2Size = 0;
|
||||
min = TR_MAX_PEER_COUNT + 1;
|
||||
for( i = 0; i < poolSize; i++ )
|
||||
{
|
||||
foo = 0;
|
||||
for( j = 0; j < tor->peerCount; j++ )
|
||||
{
|
||||
bitfield = tor->peers[j]->bitfield;
|
||||
if( bitfield && tr_bitfieldHas( bitfield, pool[i] ) )
|
||||
{
|
||||
foo++;
|
||||
}
|
||||
}
|
||||
if( foo < min )
|
||||
{
|
||||
min = foo;
|
||||
pool2Size = 0;
|
||||
}
|
||||
if( foo <= min )
|
||||
{
|
||||
pool2[pool2Size++] = pool[i];
|
||||
}
|
||||
}
|
||||
free( pool );
|
||||
|
||||
if( pool2Size < 1 )
|
||||
{
|
||||
/* Shouldn't happen */
|
||||
free( pool2 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* All pieces in pool2 have the same number of missing blocks,
|
||||
and are availabme from the same number of peers. Pick a
|
||||
random one */
|
||||
piece = pool2[tr_rand(pool2Size)];
|
||||
free( pool2 );
|
||||
|
||||
/* Pick a block in this piece */
|
||||
startBlock = tr_pieceStartBlock( piece );
|
||||
endBlock = startBlock + tr_pieceCountBlocks( piece );
|
||||
for( i = startBlock; i < endBlock; i++ )
|
||||
{
|
||||
if( !tor->blockHave[i] )
|
||||
{
|
||||
block = i;
|
||||
goto check;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shouldn't happen */
|
||||
return -1;
|
||||
}
|
||||
|
||||
free( pool );
|
||||
|
||||
/* "End game" mode */
|
||||
block = -1;
|
||||
minDownloading = TR_MAX_PEER_COUNT + 1;
|
||||
for( i = 0; i < tor->blockCount; i++ )
|
||||
{
|
||||
/* TODO: optimize */
|
||||
if( tor->blockHave[i] > 0 && tor->blockHave[i] < minDownloading )
|
||||
{
|
||||
block = i;
|
||||
minDownloading = tor->blockHave[i];
|
||||
}
|
||||
}
|
||||
|
||||
if( block < 0 )
|
||||
{
|
||||
/* Shouldn't happen */
|
||||
return -1;
|
||||
}
|
||||
|
||||
check:
|
||||
for( i = 0; i < peer->inRequestCount; i++ )
|
||||
{
|
||||
tr_request_t * r;
|
||||
r = &peer->inRequests[i];
|
||||
if( tr_block( r->index, r->begin ) == block )
|
||||
{
|
||||
/* We are already asking this peer for this block */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
235
libtransmission/sha1.c
Normal file
235
libtransmission/sha1.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
sha1.c: Implementation of SHA-1 Secure Hash Algorithm-1
|
||||
|
||||
Based upon: NIST FIPS180-1 Secure Hash Algorithm-1
|
||||
http://www.itl.nist.gov/fipspubs/fip180-1.htm
|
||||
|
||||
Non-official Japanese Translation by HIRATA Yasuyuki:
|
||||
http://yasu.asuka.net/translations/SHA-1.html
|
||||
|
||||
Copyright (C) 2002 vi@nwr.jp. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgement in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as beging the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Note:
|
||||
The copyright notice above is copied from md5.h by L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Thank him since I'm not a good speaker of English. :)
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "sha1.h"
|
||||
|
||||
#define INLINE inline
|
||||
/*
|
||||
* Packing bytes to a word
|
||||
*
|
||||
* Should not assume p is aligned to word boundary
|
||||
*/
|
||||
static INLINE sha1_word_t packup(sha1_byte_t *p)
|
||||
{
|
||||
/* Portable, but slow */
|
||||
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3] << 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unpacking a word to bytes
|
||||
*
|
||||
* Should not assume p is aligned to word boundary
|
||||
*/
|
||||
static void unpackup(sha1_byte_t *p, sha1_word_t q)
|
||||
{
|
||||
p[0] = (q >> 24) & 0xff;
|
||||
p[1] = (q >> 16) & 0xff;
|
||||
p[2] = (q >> 8) & 0xff;
|
||||
p[3] = (q >> 0) & 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processing a block
|
||||
*/
|
||||
static inline void sha1_update_now(sha1_state_s *pms, sha1_byte_t *bp)
|
||||
{
|
||||
sha1_word_t tmp, a, b, c, d, e, w[16+16];
|
||||
int i, s;
|
||||
|
||||
/* pack 64 bytes into 16 words */
|
||||
for(i = 0; i < 16; i++) {
|
||||
w[i] = packup(bp + i * sizeof(sha1_word_t));
|
||||
}
|
||||
memcpy(w + 16, w + 0, sizeof(sha1_word_t) * 16);
|
||||
|
||||
a = pms->sha1_h[0], b = pms->sha1_h[1], c = pms->sha1_h[2], d = pms->sha1_h[3], e = pms->sha1_h[4];
|
||||
|
||||
#define rot(x,n) (((x) << n) | ((x) >> (32-n)))
|
||||
#define f0(b, c, d) ((b&c)|(~b&d))
|
||||
#define f1(b, c, d) (b^c^d)
|
||||
#define f2(b, c, d) ((b&c)|(b&d)|(c&d))
|
||||
#define f3(b, c, d) (b^c^d)
|
||||
#define k0 0x5a827999
|
||||
#define k1 0x6ed9eba1
|
||||
#define k2 0x8f1bbcdc
|
||||
#define k3 0xca62c1d6
|
||||
|
||||
/* t=0-15 */
|
||||
s = 0;
|
||||
for(i = 0; i < 16; i++) {
|
||||
tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=16-19 */
|
||||
for(i = 16; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=20-39 */
|
||||
for(i = 0; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f1(b, c, d) + e + w[s] + k1;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=40-59 */
|
||||
for(i = 0; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f2(b, c, d) + e + w[s] + k2;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=60-79 */
|
||||
for(i = 0; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f3(b, c, d) + e + w[s] + k3;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
pms->sha1_h[0] += a, pms->sha1_h[1] += b, pms->sha1_h[2] += c, pms->sha1_h[3] += d, pms->sha1_h[4] += e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment sha1_size1, sha1_size2 field of sha1_state_s
|
||||
*/
|
||||
static INLINE void incr(sha1_state_s *pms, int v)
|
||||
{
|
||||
sha1_word_t q;
|
||||
|
||||
q = pms->sha1_size1 + v * BITS;
|
||||
if(q < pms->sha1_size1) {
|
||||
pms->sha1_size2++;
|
||||
}
|
||||
pms->sha1_size1 = q;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize sha1_state_s as FIPS specifies
|
||||
*/
|
||||
void sha1_init(sha1_state_s *pms)
|
||||
{
|
||||
memset(pms, 0, sizeof(*pms));
|
||||
pms->sha1_h[0] = 0x67452301; /* Initialize H[0]-H[4] */
|
||||
pms->sha1_h[1] = 0xEFCDAB89;
|
||||
pms->sha1_h[2] = 0x98BADCFE;
|
||||
pms->sha1_h[3] = 0x10325476;
|
||||
pms->sha1_h[4] = 0xC3D2E1F0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill block and update output when needed
|
||||
*/
|
||||
void sha1_update(sha1_state_s *pms, sha1_byte_t *bufp, int length)
|
||||
{
|
||||
/* Is the buffer partially filled? */
|
||||
if(pms->sha1_count != 0) {
|
||||
if(pms->sha1_count + length >= (signed) sizeof(pms->sha1_buf)) { /* buffer is filled enough */
|
||||
int fil = sizeof(pms->sha1_buf) - pms->sha1_count; /* length to copy */
|
||||
|
||||
memcpy(pms->sha1_buf + pms->sha1_count, bufp, fil);
|
||||
sha1_update_now(pms, pms->sha1_buf);
|
||||
length -= fil;
|
||||
bufp += fil;
|
||||
pms->sha1_count = 0;
|
||||
incr(pms, fil);
|
||||
} else {
|
||||
memcpy(pms->sha1_buf + pms->sha1_count, bufp, length);
|
||||
pms->sha1_count += length;
|
||||
incr(pms, length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop to update state */
|
||||
for(;;) {
|
||||
if(length < (signed) sizeof(pms->sha1_buf)) { /* Short to fill up the buffer */
|
||||
if(length) {
|
||||
memcpy(pms->sha1_buf, bufp, length);
|
||||
}
|
||||
pms->sha1_count = length;
|
||||
incr(pms, length);
|
||||
break;
|
||||
}
|
||||
sha1_update_now(pms, bufp);
|
||||
length -= sizeof(pms->sha1_buf);
|
||||
bufp += sizeof(pms->sha1_buf);
|
||||
incr(pms, sizeof(pms->sha1_buf));
|
||||
}
|
||||
}
|
||||
|
||||
void sha1_finish(sha1_state_s *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE])
|
||||
{
|
||||
int i;
|
||||
sha1_byte_t buf[1];
|
||||
|
||||
/* fill a bit */
|
||||
buf[0] = 0x80;
|
||||
sha1_update(pms, buf, 1);
|
||||
|
||||
/* Decrement sha1_size1, sha1_size2 */
|
||||
if((pms->sha1_size1 -= BITS) == 0) {
|
||||
pms->sha1_size2--;
|
||||
}
|
||||
|
||||
/* fill zeros */
|
||||
if(pms->sha1_count > (signed) (sizeof(pms->sha1_buf) - 2 * sizeof(sha1_word_t))) {
|
||||
memset(pms->sha1_buf + pms->sha1_count, 0, sizeof(pms->sha1_buf) - pms->sha1_count);
|
||||
sha1_update_now(pms, pms->sha1_buf);
|
||||
pms->sha1_count = 0;
|
||||
}
|
||||
memset(pms->sha1_buf + pms->sha1_count, 0,
|
||||
sizeof(pms->sha1_buf) - pms->sha1_count - sizeof(sha1_word_t) * 2);
|
||||
|
||||
/* fill last length */
|
||||
unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 2, pms->sha1_size2);
|
||||
unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 1, pms->sha1_size1);
|
||||
|
||||
/* final update */
|
||||
sha1_update_now(pms, pms->sha1_buf);
|
||||
|
||||
/* move hash value to output byte array */
|
||||
for(i = 0; i < (signed) (sizeof(pms->sha1_h)/sizeof(sha1_word_t)); i++) {
|
||||
unpackup(output + i * sizeof(sha1_word_t), pms->sha1_h[i]);
|
||||
}
|
||||
}
|
||||
68
libtransmission/sha1.h
Normal file
68
libtransmission/sha1.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
sha1.h: Implementation of SHA-1 Secure Hash Algorithm-1
|
||||
|
||||
Based upon: NIST FIPS180-1 Secure Hash Algorithm-1
|
||||
http://www.itl.nist.gov/fipspubs/fip180-1.htm
|
||||
|
||||
Non-official Japanese Translation by HIRATA Yasuyuki:
|
||||
http://yasu.asuka.net/translations/SHA-1.html
|
||||
|
||||
Copyright (C) 2002 vi@nwr.jp. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgement in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as beging the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Note:
|
||||
The copyright notice above is copied from md5.h by L. Petet Deutsch
|
||||
<ghost@aladdin.com>. Thank him since I'm not a good speaker of English. :)
|
||||
*/
|
||||
#ifndef SHA1_H
|
||||
#define SHA1_H
|
||||
|
||||
typedef unsigned int sha1_word_t; /* 32bits unsigned integer */
|
||||
typedef unsigned char sha1_byte_t; /* 8bits unsigned integer */
|
||||
#define BITS 8
|
||||
|
||||
/* Define the state of SHA-1 algorithm */
|
||||
typedef struct {
|
||||
sha1_byte_t sha1_buf[64]; /* 512 bits */
|
||||
int sha1_count; /* How many bytes are used */
|
||||
sha1_word_t sha1_size1; /* Length counter Lower Word */
|
||||
sha1_word_t sha1_size2; /* Length counter Upper Word */
|
||||
sha1_word_t sha1_h[5]; /* Hash output */
|
||||
} sha1_state_s;
|
||||
#define SHA1_OUTPUT_SIZE 20 /* in bytes */
|
||||
|
||||
/* External Functions */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initialize SHA-1 algorithm */
|
||||
void sha1_init(sha1_state_s *pms);
|
||||
|
||||
/* Append a string to SHA-1 algorithm */
|
||||
void sha1_update(sha1_state_s *pms, sha1_byte_t *input_buffer, int length);
|
||||
|
||||
/* Finish the SHA-1 algorithm and return the hash */
|
||||
void sha1_finish(sha1_state_s *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
597
libtransmission/tracker.c
Normal file
597
libtransmission/tracker.c
Normal file
@@ -0,0 +1,597 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
struct tr_tracker_s
|
||||
{
|
||||
tr_torrent_t * tor;
|
||||
|
||||
char * id;
|
||||
|
||||
char started;
|
||||
char completed;
|
||||
char stopped;
|
||||
|
||||
int interval;
|
||||
int seeders;
|
||||
int leechers;
|
||||
int hasManyPeers;
|
||||
|
||||
uint64_t dateTry;
|
||||
uint64_t dateOk;
|
||||
|
||||
#define TC_STATUS_IDLE 1
|
||||
#define TC_STATUS_CONNECT 2
|
||||
#define TC_STATUS_RECV 4
|
||||
char status;
|
||||
|
||||
int socket;
|
||||
uint8_t * buf;
|
||||
int size;
|
||||
int pos;
|
||||
};
|
||||
|
||||
static void sendQuery ( tr_tracker_t * tc );
|
||||
static void recvAnswer ( tr_tracker_t * tc );
|
||||
|
||||
tr_tracker_t * tr_trackerInit( tr_handle_t * h, tr_torrent_t * tor )
|
||||
{
|
||||
tr_tracker_t * tc;
|
||||
|
||||
tc = calloc( 1, sizeof( tr_tracker_t ) );
|
||||
tc->tor = tor;
|
||||
tc->id = h->id;
|
||||
|
||||
tc->started = 1;
|
||||
|
||||
tc->seeders = -1;
|
||||
tc->leechers = -1;
|
||||
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
tc->size = 1024;
|
||||
tc->buf = malloc( tc->size );
|
||||
|
||||
return tc;
|
||||
}
|
||||
|
||||
static int shouldConnect( tr_tracker_t * tc )
|
||||
{
|
||||
uint64_t now = tr_date();
|
||||
|
||||
/* In any case, always wait 5 seconds between two requests */
|
||||
if( now < tc->dateTry + 5000 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do we need to send an event? */
|
||||
if( tc->started || tc->completed || tc->stopped )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Should we try and get more peers? */
|
||||
if( now > tc->dateOk + 1000 * tc->interval )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If there is quite a lot of people on this torrent, stress
|
||||
the tracker a bit until we get a decent number of peers */
|
||||
if( tc->hasManyPeers )
|
||||
{
|
||||
if( tc->tor->peerCount < 5 && now > tc->dateOk + 10000 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if( tc->tor->peerCount < 10 && now > tc->dateOk + 20000 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if( tc->tor->peerCount < 15 && now > tc->dateOk + 30000 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tr_trackerPulse( tr_tracker_t * tc )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
uint64_t now = tr_date();
|
||||
|
||||
if( ( tc->status & TC_STATUS_IDLE ) && shouldConnect( tc ) )
|
||||
{
|
||||
struct in_addr addr;
|
||||
|
||||
if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( tr_netResolve( inf->trackerAddress, &addr ) )
|
||||
{
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) );
|
||||
if( tc->socket < 0 )
|
||||
{
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
tr_inf( "Tracker: connecting to %s:%d (%s)",
|
||||
inf->trackerAddress, inf->trackerPort,
|
||||
tc->started ? "sending 'started'" :
|
||||
( tc->completed ? "sending 'completed'" :
|
||||
( tc->stopped ? "sending 'stopped'" :
|
||||
"getting peers" ) ) );
|
||||
|
||||
tc->status = TC_STATUS_CONNECT;
|
||||
tc->dateTry = tr_date();
|
||||
}
|
||||
|
||||
if( tc->status & TC_STATUS_CONNECT )
|
||||
{
|
||||
/* We are connecting to the tracker. Try to send the query */
|
||||
sendQuery( tc );
|
||||
}
|
||||
|
||||
if( tc->status & TC_STATUS_RECV )
|
||||
{
|
||||
/* Try to get something */
|
||||
recvAnswer( tc );
|
||||
}
|
||||
|
||||
if( tc->status > TC_STATUS_IDLE && now > tc->dateTry + 60000 )
|
||||
{
|
||||
/* Give up if the request wasn't successful within 60 seconds */
|
||||
tr_inf( "Tracker: timeout reached (60 s)" );
|
||||
|
||||
tr_netClose( tc->socket );
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
tc->dateTry = tr_date();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tr_trackerCompleted( tr_tracker_t * tc )
|
||||
{
|
||||
tc->started = 0;
|
||||
tc->completed = 1;
|
||||
tc->stopped = 0;
|
||||
}
|
||||
|
||||
void tr_trackerStopped( tr_tracker_t * tc )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
|
||||
if( tc->status > TC_STATUS_CONNECT )
|
||||
{
|
||||
/* If we are already sendy a query at the moment, we need to
|
||||
reconnect */
|
||||
tr_netClose( tc->socket );
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
}
|
||||
|
||||
tc->started = 0;
|
||||
tc->completed = 0;
|
||||
tc->stopped = 1;
|
||||
|
||||
/* Even if we have connected recently, reconnect right now */
|
||||
if( tc->status & TC_STATUS_IDLE )
|
||||
{
|
||||
tc->dateTry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void tr_trackerClose( tr_tracker_t * tc )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
|
||||
if( tc->status > TC_STATUS_IDLE )
|
||||
{
|
||||
tr_netClose( tc->socket );
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
}
|
||||
free( tc->buf );
|
||||
free( tc );
|
||||
}
|
||||
|
||||
static void sendQuery( tr_tracker_t * tc )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
char * event;
|
||||
uint64_t left;
|
||||
int ret;
|
||||
|
||||
if( tc->started )
|
||||
event = "&event=started";
|
||||
else if( tc->completed )
|
||||
event = "&event=completed";
|
||||
else if( tc->stopped )
|
||||
event = "&event=stopped";
|
||||
else
|
||||
event = "";
|
||||
|
||||
left = (uint64_t) ( tor->blockCount - tor->blockHaveCount ) *
|
||||
(uint64_t) tor->blockSize;
|
||||
left = MIN( left, inf->totalSize );
|
||||
|
||||
ret = snprintf( (char *) tc->buf, tc->size,
|
||||
"GET %s?info_hash=%s&peer_id=%s&port=%d&uploaded=%lld&"
|
||||
"downloaded=%lld&left=%lld&compact=1&numwant=50%s "
|
||||
"HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
|
||||
inf->trackerAnnounce, tor->hashString, tc->id,
|
||||
tor->bindPort, tor->uploaded[9], tor->downloaded[9],
|
||||
left, event, inf->trackerAddress );
|
||||
|
||||
ret = tr_netSend( tc->socket, tc->buf, ret );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
tr_inf( "Tracker: connection failed" );
|
||||
tr_netClose( tc->socket );
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
tc->dateTry = tr_date();
|
||||
}
|
||||
else if( !( ret & TR_NET_BLOCK ) )
|
||||
{
|
||||
// printf( "Tracker: sent %s", tc->buf );
|
||||
tc->status = TC_STATUS_RECV;
|
||||
tc->pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void recvAnswer( tr_tracker_t * tc )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
int ret;
|
||||
int i;
|
||||
benc_val_t beAll;
|
||||
benc_val_t * bePeers, * beFoo;
|
||||
|
||||
if( tc->pos == tc->size )
|
||||
{
|
||||
tc->size *= 2;
|
||||
tc->buf = realloc( tc->buf, tc->size );
|
||||
}
|
||||
|
||||
ret = tr_netRecv( tc->socket, &tc->buf[tc->pos],
|
||||
tc->size - tc->pos );
|
||||
|
||||
if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( !( ret & TR_NET_CLOSE ) )
|
||||
{
|
||||
// printf( "got %d bytes\n", ret );
|
||||
tc->pos += ret;
|
||||
return;
|
||||
}
|
||||
|
||||
tr_netClose( tc->socket );
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
// printf( "connection closed, got total %d bytes\n", tc->pos );
|
||||
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
tc->dateTry = tr_date();
|
||||
|
||||
if( tc->pos < 1 )
|
||||
{
|
||||
/* We got nothing */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the beginning of the dictionary */
|
||||
for( i = 0; i < tc->pos - 18; i++ )
|
||||
{
|
||||
/* Hem */
|
||||
if( !memcmp( &tc->buf[i], "d8:interval", 11 ) ||
|
||||
!memcmp( &tc->buf[i], "d8:complete", 11 ) ||
|
||||
!memcmp( &tc->buf[i], "d14:failure reason", 18 ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( i >= tc->pos - 18 )
|
||||
{
|
||||
tr_err( "Tracker: no dictionary in answer" );
|
||||
// printf( "%s\n", tc->buf );
|
||||
return;
|
||||
}
|
||||
|
||||
if( tr_bencLoad( &tc->buf[i], &beAll, NULL ) )
|
||||
{
|
||||
tr_err( "Tracker: error parsing bencoded data" );
|
||||
return;
|
||||
}
|
||||
|
||||
// tr_bencPrint( &beAll );
|
||||
|
||||
if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
|
||||
{
|
||||
tr_err( "Tracker: %s", bePeers->val.s.s );
|
||||
tor->status |= TR_TRACKER_ERROR;
|
||||
snprintf( tor->error, sizeof( tor->error ),
|
||||
bePeers->val.s.s );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
tor->status &= ~TR_TRACKER_ERROR;
|
||||
|
||||
if( !tc->interval )
|
||||
{
|
||||
/* Get the tracker interval, ignore it if it is not between
|
||||
10 sec and 5 mins */
|
||||
if( !( beFoo = tr_bencDictFind( &beAll, "interval" ) ) ||
|
||||
!( beFoo->type & TYPE_INT ) )
|
||||
{
|
||||
tr_err( "Tracker: no 'interval' field" );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
tc->interval = beFoo->val.i;
|
||||
tc->interval = MIN( tc->interval, 300 );
|
||||
tc->interval = MAX( 10, tc->interval );
|
||||
|
||||
tr_inf( "Tracker: interval = %d seconds", tc->interval );
|
||||
}
|
||||
|
||||
if( ( beFoo = tr_bencDictFind( &beAll, "complete" ) ) &&
|
||||
( beFoo->type & TYPE_INT ) )
|
||||
{
|
||||
tc->seeders = beFoo->val.i;
|
||||
}
|
||||
if( ( beFoo = tr_bencDictFind( &beAll, "incomplete" ) ) &&
|
||||
( beFoo->type & TYPE_INT ) )
|
||||
{
|
||||
tc->leechers = beFoo->val.i;
|
||||
}
|
||||
if( tc->seeders + tc->seeders >= 50 )
|
||||
{
|
||||
tc->hasManyPeers = 1;
|
||||
}
|
||||
|
||||
if( !( bePeers = tr_bencDictFind( &beAll, "peers" ) ) )
|
||||
{
|
||||
tr_err( "Tracker: no \"peers\" field" );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if( bePeers->type & TYPE_LIST )
|
||||
{
|
||||
char * ip;
|
||||
int port;
|
||||
|
||||
/* Original protocol */
|
||||
tr_inf( "Tracker: got %d peers", bePeers->val.l.count );
|
||||
|
||||
for( i = 0; i < bePeers->val.l.count; i++ )
|
||||
{
|
||||
beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "ip" );
|
||||
if( !beFoo )
|
||||
continue;
|
||||
ip = beFoo->val.s.s;
|
||||
beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "port" );
|
||||
if( !beFoo )
|
||||
continue;
|
||||
port = beFoo->val.i;
|
||||
|
||||
tr_peerAddOld( tor, ip, port );
|
||||
}
|
||||
|
||||
if( bePeers->val.l.count >= 50 )
|
||||
{
|
||||
tc->hasManyPeers = 1;
|
||||
}
|
||||
}
|
||||
else if( bePeers->type & TYPE_STR )
|
||||
{
|
||||
struct in_addr addr;
|
||||
in_port_t port;
|
||||
|
||||
/* "Compact" extension */
|
||||
if( bePeers->val.s.i % 6 )
|
||||
{
|
||||
tr_err( "Tracker: \"peers\" of size %d",
|
||||
bePeers->val.s.i );
|
||||
tr_lockUnlock( tor->lock );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
tr_inf( "Tracker: got %d peers", bePeers->val.s.i / 6 );
|
||||
for( i = 0; i < bePeers->val.s.i / 6; i++ )
|
||||
{
|
||||
memcpy( &addr, &bePeers->val.s.s[6*i], 4 );
|
||||
memcpy( &port, &bePeers->val.s.s[6*i+4], 2 );
|
||||
|
||||
tr_peerAddCompact( tor, addr, port, -1 );
|
||||
}
|
||||
|
||||
if( bePeers->val.s.i / 6 >= 50 )
|
||||
{
|
||||
tc->hasManyPeers = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Success */
|
||||
tc->started = 0;
|
||||
tc->completed = 0;
|
||||
tc->dateOk = tr_date();
|
||||
|
||||
if( tc->stopped )
|
||||
{
|
||||
tor->status = TR_STATUS_STOPPED;
|
||||
tc->stopped = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
tr_bencFree( &beAll );
|
||||
}
|
||||
|
||||
int tr_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int s, i, ret;
|
||||
uint8_t buf[1024];
|
||||
benc_val_t scrape, * val1, * val2;
|
||||
struct in_addr addr;
|
||||
uint64_t date;
|
||||
int pos, len;
|
||||
|
||||
if( !tor->scrape[0] )
|
||||
{
|
||||
/* scrape not supported */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( tr_netResolve( inf->trackerAddress, &addr ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
s = tr_netOpen( addr, htons( inf->trackerPort ) );
|
||||
if( s < 0 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
len = snprintf( (char *) buf, sizeof( buf ),
|
||||
"GET %s?info_hash=%s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Connection: close\r\n\r\n",
|
||||
tor->scrape, tor->hashString,
|
||||
inf->trackerAddress );
|
||||
|
||||
for( date = tr_date();; )
|
||||
{
|
||||
ret = tr_netSend( s, buf, len );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
fprintf( stderr, "Could not connect to tracker\n" );
|
||||
tr_netClose( s );
|
||||
return 1;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
if( tr_date() > date + 10000 )
|
||||
{
|
||||
fprintf( stderr, "Could not connect to tracker\n" );
|
||||
tr_netClose( s );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
tr_wait( 10 );
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
for( date = tr_date();; )
|
||||
{
|
||||
ret = tr_netRecv( s, &buf[pos], sizeof( buf ) - pos );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
if( tr_date() > date + 10000 )
|
||||
{
|
||||
fprintf( stderr, "Could not read from tracker\n" );
|
||||
tr_netClose( s );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += ret;
|
||||
}
|
||||
tr_wait( 10 );
|
||||
}
|
||||
|
||||
if( pos < 1 )
|
||||
{
|
||||
fprintf( stderr, "Could not read from tracker\n" );
|
||||
tr_netClose( s );
|
||||
return 1;
|
||||
}
|
||||
|
||||
for( i = 0; i < ret - 8; i++ )
|
||||
{
|
||||
if( !memcmp( &buf[i], "d5:files", 8 ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i >= ret - 8 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if( tr_bencLoad( &buf[i], &scrape, NULL ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
val1 = tr_bencDictFind( &scrape, "files" );
|
||||
if( !val1 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
val1 = &val1->val.l.vals[1];
|
||||
if( !val1 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
val2 = tr_bencDictFind( val1, "complete" );
|
||||
if( !val2 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
*seeders = val2->val.i;
|
||||
val2 = tr_bencDictFind( val1, "incomplete" );
|
||||
if( !val2 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
*leechers = val2->val.i;
|
||||
tr_bencFree( &scrape );
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
libtransmission/tracker.h
Normal file
36
libtransmission/tracker.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_TRACKER_H
|
||||
#define TR_TRACKER_H 1
|
||||
|
||||
typedef struct tr_tracker_s tr_tracker_t;
|
||||
|
||||
tr_tracker_t * tr_trackerInit ( tr_handle_t *, tr_torrent_t * );
|
||||
int tr_trackerPulse ( tr_tracker_t * );
|
||||
void tr_trackerCompleted ( tr_tracker_t * );
|
||||
void tr_trackerStopped ( tr_tracker_t * );
|
||||
void tr_trackerClose ( tr_tracker_t * );
|
||||
|
||||
int tr_trackerScrape ( tr_torrent_t *, int *, int * );
|
||||
|
||||
#endif
|
||||
541
libtransmission/transmission.c
Normal file
541
libtransmission/transmission.c
Normal file
@@ -0,0 +1,541 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
/***********************************************************************
|
||||
* Local prototypes
|
||||
**********************************************************************/
|
||||
static void downloadLoop( void * );
|
||||
static float rateDownload( tr_torrent_t * );
|
||||
static float rateUpload( tr_torrent_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_init
|
||||
***********************************************************************
|
||||
* Allocates a tr_handle_t structure and initializes a few things
|
||||
**********************************************************************/
|
||||
tr_handle_t * tr_init()
|
||||
{
|
||||
tr_handle_t * h;
|
||||
int i, r;
|
||||
|
||||
h = calloc( sizeof( tr_handle_t ), 1 );
|
||||
|
||||
/* Generate a peer id : "-TRxxyy-" + 12 random alphanumeric
|
||||
characters, where xx is the major version number and yy the
|
||||
minor version number (Azureus-style) */
|
||||
sprintf( h->id, "-TR%02d%02d-", VERSION_MAJOR, VERSION_MINOR );
|
||||
for( i = 8; i < 20; i++ )
|
||||
{
|
||||
r = tr_rand( 36 );
|
||||
h->id[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
|
||||
}
|
||||
|
||||
/* Don't exit when writing on a broken socket */
|
||||
signal( SIGPIPE, SIG_IGN );
|
||||
|
||||
/* Initialize rate and file descripts controls */
|
||||
h->upload = tr_uploadInit();
|
||||
h->fdlimit = tr_fdInit();
|
||||
|
||||
h->bindPort = 9090;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setBindPort
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
void tr_setBindPort( tr_handle_t * h, int port )
|
||||
{
|
||||
/* FIXME multithread safety */
|
||||
h->bindPort = port;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setUploadLimit
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
void tr_setUploadLimit( tr_handle_t * h, int limit )
|
||||
{
|
||||
tr_uploadSetLimit( h->upload, limit );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentRates
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
|
||||
{
|
||||
int i;
|
||||
tr_torrent_t * tor;
|
||||
|
||||
*dl = 0.0;
|
||||
*ul = 0.0;
|
||||
|
||||
for( i = 0; i < h->torrentCount; i++ )
|
||||
{
|
||||
tor = h->torrents[i];
|
||||
tr_lockLock( tor->lock );
|
||||
*dl += rateDownload( tor );
|
||||
*ul += rateUpload( tor );
|
||||
tr_lockUnlock( tor->lock );
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentInit
|
||||
***********************************************************************
|
||||
* Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
|
||||
* to fill it.
|
||||
**********************************************************************/
|
||||
int tr_torrentInit( tr_handle_t * h, const char * path )
|
||||
{
|
||||
tr_torrent_t * tor;
|
||||
tr_info_t * inf;
|
||||
int i;
|
||||
char * s1, * s2;
|
||||
|
||||
if( h->torrentCount >= TR_MAX_TORRENT_COUNT )
|
||||
{
|
||||
tr_err( "Maximum number of torrents reached" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
tor = calloc( sizeof( tr_torrent_t ), 1 );
|
||||
inf = &tor->info;
|
||||
|
||||
/* Parse torrent file */
|
||||
if( tr_metainfoParse( inf, path ) )
|
||||
{
|
||||
free( tor );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Make sure this torrent is not already open */
|
||||
for( i = 0; i < h->torrentCount; i++ )
|
||||
{
|
||||
if( !memcmp( tor->info.hash, h->torrents[i]->info.hash,
|
||||
SHA_DIGEST_LENGTH ) )
|
||||
{
|
||||
tr_err( "Torrent already open" );
|
||||
free( tor );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
tor->status = TR_STATUS_PAUSE;
|
||||
tor->id = h->id;
|
||||
|
||||
/* Guess scrape URL */
|
||||
s1 = strchr( inf->trackerAnnounce, '/' );
|
||||
while( ( s2 = strchr( s1 + 1, '/' ) ) )
|
||||
{
|
||||
s1 = s2;
|
||||
}
|
||||
s1++;
|
||||
if( !strncmp( s1, "announce", 8 ) )
|
||||
{
|
||||
int pre = (long) s1 - (long) inf->trackerAnnounce;
|
||||
int post = strlen( inf->trackerAnnounce ) - pre - 8;
|
||||
memcpy( tor->scrape, inf->trackerAnnounce, pre );
|
||||
sprintf( &tor->scrape[pre], "scrape" );
|
||||
memcpy( &tor->scrape[pre+6], &inf->trackerAnnounce[pre+8], post );
|
||||
}
|
||||
|
||||
/* Escaped info hash for HTTP queries */
|
||||
for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
|
||||
{
|
||||
sprintf( &tor->hashString[3*i], "%%%02x", inf->hash[i] );
|
||||
}
|
||||
|
||||
/* Block size: usually 16 ko, or less if we have to */
|
||||
tor->blockSize = MIN( inf->pieceSize, 1 << 14 );
|
||||
tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
|
||||
tor->blockSize;
|
||||
tor->blockHave = calloc( tor->blockCount, 1 );
|
||||
tor->bitfield = calloc( ( inf->pieceCount + 7 ) / 8, 1 );
|
||||
|
||||
tr_lockInit( &tor->lock );
|
||||
|
||||
tor->upload = h->upload;
|
||||
tor->fdlimit = h->fdlimit;
|
||||
|
||||
/* We have a new torrent */
|
||||
h->torrents[h->torrentCount] = tor;
|
||||
(h->torrentCount)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentScrape
|
||||
***********************************************************************
|
||||
* Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
|
||||
* to fill it.
|
||||
**********************************************************************/
|
||||
int tr_torrentScrape( tr_handle_t * h, int t, int * s, int * l )
|
||||
{
|
||||
return tr_trackerScrape( h->torrents[t], s, l );
|
||||
}
|
||||
|
||||
void tr_torrentSetFolder( tr_handle_t * h, int t, const char * path )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
tor->destination = strdup( path );
|
||||
}
|
||||
|
||||
char * tr_torrentGetFolder( tr_handle_t * h, int t )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
return tor->destination;
|
||||
}
|
||||
|
||||
void tr_torrentStart( tr_handle_t * h, int t )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
uint64_t now;
|
||||
int i;
|
||||
|
||||
tor->status = TR_STATUS_CHECK;
|
||||
tor->tracker = tr_trackerInit( h, tor );
|
||||
tor->bindPort = h->bindPort;
|
||||
#ifndef BEOS_NETSERVER
|
||||
/* BeOS net_server seems to be unable to set incoming connections to
|
||||
non-blocking. Too bad. */
|
||||
if( !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
|
||||
{
|
||||
tor->bindSocket = tr_netBind( &tor->bindPort );
|
||||
}
|
||||
#endif
|
||||
|
||||
now = tr_date();
|
||||
for( i = 0; i < 10; i++ )
|
||||
{
|
||||
tor->dates[i] = now;
|
||||
}
|
||||
|
||||
tor->die = 0;
|
||||
tr_threadCreate( &tor->thread, downloadLoop, tor );
|
||||
}
|
||||
|
||||
void tr_torrentStop( tr_handle_t * h, int t )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
tr_lockLock( tor->lock );
|
||||
tr_trackerStopped( tor->tracker );
|
||||
tor->status = TR_STATUS_STOPPING;
|
||||
tr_lockUnlock( tor->lock );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* torrentReallyStop
|
||||
***********************************************************************
|
||||
* Joins the download thread and frees/closes everything related to it.
|
||||
**********************************************************************/
|
||||
static void torrentReallyStop( tr_handle_t * h, int t )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
tor->die = 1;
|
||||
tr_threadJoin( tor->thread );
|
||||
tr_dbg( "Thread joined" );
|
||||
|
||||
tr_trackerClose( tor->tracker );
|
||||
|
||||
while( tor->peerCount > 0 )
|
||||
{
|
||||
tr_peerRem( tor, 0 );
|
||||
}
|
||||
if( tor->bindSocket > -1 )
|
||||
{
|
||||
tr_netClose( tor->bindSocket );
|
||||
tr_fdSocketClosed( h->fdlimit, 0 );
|
||||
}
|
||||
|
||||
memset( tor->downloaded, 0, sizeof( tor->downloaded ) );
|
||||
memset( tor->uploaded, 0, sizeof( tor->uploaded ) );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentCount
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
int tr_torrentCount( tr_handle_t * h )
|
||||
{
|
||||
return h->torrentCount;
|
||||
}
|
||||
|
||||
int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
|
||||
{
|
||||
tr_stat_t * s;
|
||||
tr_torrent_t * tor;
|
||||
tr_info_t * inf;
|
||||
int i, j, k, piece;
|
||||
|
||||
if( h->torrentCount < 1 )
|
||||
{
|
||||
*stat = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = malloc( h->torrentCount * sizeof( tr_stat_t ) );
|
||||
|
||||
for( i = 0; i < h->torrentCount; i++ )
|
||||
{
|
||||
tor = h->torrents[i];
|
||||
inf = &tor->info;
|
||||
|
||||
tr_lockLock( tor->lock );
|
||||
|
||||
if( tor->status & TR_STATUS_STOPPED )
|
||||
{
|
||||
torrentReallyStop( h, i );
|
||||
tor->status = TR_STATUS_PAUSE;
|
||||
}
|
||||
|
||||
memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
|
||||
s[i].status = tor->status;
|
||||
memcpy( s[i].error, tor->error, sizeof( s[i].error ) );
|
||||
|
||||
s[i].peersTotal = 0;
|
||||
s[i].peersUploading = 0;
|
||||
s[i].peersDownloading = 0;
|
||||
|
||||
for( j = 0; j < tor->peerCount; j++ )
|
||||
{
|
||||
if( tr_peerIsConnected( tor->peers[j] ) )
|
||||
{
|
||||
(s[i].peersTotal)++;
|
||||
if( tr_peerIsUploading( tor->peers[j] ) )
|
||||
{
|
||||
(s[i].peersUploading)++;
|
||||
}
|
||||
if( tr_peerIsDownloading( tor->peers[j] ) )
|
||||
{
|
||||
(s[i].peersDownloading)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s[i].progress = (float) tor->blockHaveCount / (float) tor->blockCount;
|
||||
|
||||
s[i].rateDownload = rateDownload( tor );
|
||||
s[i].rateUpload = rateUpload( tor );
|
||||
|
||||
if( s[i].rateDownload < 0.1 )
|
||||
{
|
||||
s[i].eta = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s[i].eta = (float) (tor->blockCount - tor->blockHaveCount ) *
|
||||
(float) tor->blockSize / s[i].rateDownload / 1024.0;
|
||||
if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
|
||||
{
|
||||
s[i].eta = -1;
|
||||
}
|
||||
}
|
||||
|
||||
for( j = 0; j < 120; j++ )
|
||||
{
|
||||
piece = j * inf->pieceCount / 120;
|
||||
|
||||
if( tr_bitfieldHas( tor->bitfield, piece ) )
|
||||
{
|
||||
s[i].pieces[j] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
s[i].pieces[j] = 0;
|
||||
|
||||
for( k = 0; k < tor->peerCount; k++ )
|
||||
{
|
||||
if( tr_peerBitfield( tor->peers[k] ) &&
|
||||
tr_bitfieldHas( tr_peerBitfield( tor->peers[k] ), piece ) )
|
||||
{
|
||||
(s[i].pieces[j])++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s[i].downloaded = tor->downloaded[9];
|
||||
s[i].uploaded = tor->uploaded[9];
|
||||
|
||||
s[i].folder = tor->destination;
|
||||
|
||||
tr_lockUnlock( tor->lock );
|
||||
}
|
||||
|
||||
*stat = s;
|
||||
return h->torrentCount;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentClose
|
||||
***********************************************************************
|
||||
* Frees memory allocated by tr_torrentInit.
|
||||
**********************************************************************/
|
||||
void tr_torrentClose( tr_handle_t * h, int t )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
|
||||
{
|
||||
/* Join the thread first */
|
||||
tr_lockLock( tor->lock );
|
||||
torrentReallyStop( h, t );
|
||||
tr_lockUnlock( tor->lock );
|
||||
}
|
||||
|
||||
h->torrentCount--;
|
||||
|
||||
tr_lockClose( tor->lock );
|
||||
|
||||
if( tor->destination )
|
||||
{
|
||||
free( tor->destination );
|
||||
}
|
||||
free( inf->pieces );
|
||||
free( inf->files );
|
||||
free( tor->blockHave );
|
||||
free( tor->bitfield );
|
||||
free( tor );
|
||||
|
||||
memmove( &h->torrents[t], &h->torrents[t+1],
|
||||
( h->torrentCount - t ) * sizeof( void * ) );
|
||||
}
|
||||
|
||||
void tr_close( tr_handle_t * h )
|
||||
{
|
||||
tr_fdClose( h->fdlimit );
|
||||
tr_uploadClose( h->upload );
|
||||
free( h );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* downloadLoop
|
||||
**********************************************************************/
|
||||
static void downloadLoop( void * _tor )
|
||||
{
|
||||
tr_torrent_t * tor = _tor;
|
||||
uint64_t date1, date2;
|
||||
|
||||
tr_dbg( "Thread started" );
|
||||
|
||||
#ifdef SYS_BEOS
|
||||
/* This is required because on BeOS, SIGINT is sent to each thread,
|
||||
which kills them not nicely */
|
||||
signal( SIGINT, SIG_IGN );
|
||||
#endif
|
||||
|
||||
tor->io = tr_ioInit( tor );
|
||||
tor->status = ( tor->blockHaveCount < tor->blockCount ) ?
|
||||
TR_STATUS_DOWNLOAD : TR_STATUS_SEED;
|
||||
|
||||
while( !tor->die )
|
||||
{
|
||||
date1 = tr_date();
|
||||
|
||||
tr_lockLock( tor->lock );
|
||||
|
||||
/* Are we finished ? */
|
||||
if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
|
||||
tor->blockHaveCount >= tor->blockCount )
|
||||
{
|
||||
/* Done */
|
||||
tor->status = TR_STATUS_SEED;
|
||||
tr_trackerCompleted( tor->tracker );
|
||||
}
|
||||
|
||||
/* Receive/send messages */
|
||||
if( !( tor->status & TR_STATUS_STOPPING ) )
|
||||
{
|
||||
tr_peerPulse( tor );
|
||||
}
|
||||
|
||||
/* Try to get new peers or to send a message to the tracker */
|
||||
tr_trackerPulse( tor->tracker );
|
||||
|
||||
tr_lockUnlock( tor->lock );
|
||||
|
||||
if( tor->status & TR_STATUS_STOPPED )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait up to 20 ms */
|
||||
date2 = tr_date();
|
||||
if( date2 < date1 + 20 )
|
||||
{
|
||||
tr_wait( date1 + 20 - date2 );
|
||||
}
|
||||
}
|
||||
|
||||
tr_ioClose( tor->io );
|
||||
|
||||
tor->status = TR_STATUS_STOPPED;
|
||||
|
||||
tr_dbg( "Thread exited" );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* rateDownload, rateUpload
|
||||
**********************************************************************/
|
||||
static float rateGeneric( uint64_t * dates, uint64_t * counts )
|
||||
{
|
||||
float ret;
|
||||
int i;
|
||||
|
||||
ret = 0.0;
|
||||
for( i = 0; i < 9; i++ )
|
||||
{
|
||||
if( dates[i+1] == dates[i] )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ret += (float) ( i + 1 ) * 1000.0 / 1024.0 *
|
||||
(float) ( counts[i+1] - counts[i] ) /
|
||||
(float) ( dates[i+1] - dates[i] );
|
||||
}
|
||||
ret *= 1000.0 / 1024.0 / 45.0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static float rateDownload( tr_torrent_t * tor )
|
||||
{
|
||||
return rateGeneric( tor->dates, tor->downloaded );
|
||||
}
|
||||
static float rateUpload( tr_torrent_t * tor )
|
||||
{
|
||||
return rateGeneric( tor->dates, tor->uploaded );
|
||||
}
|
||||
210
libtransmission/transmission.h
Normal file
210
libtransmission/transmission.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_TRANSMISSION_H
|
||||
#define TR_TRANSMISSION_H 1
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#define MAX_PATH_LENGTH 1024
|
||||
#define TR_MAX_TORRENT_COUNT 50
|
||||
|
||||
/***********************************************************************
|
||||
* tr_init
|
||||
***********************************************************************
|
||||
* Initializes a libtransmission instance. Returns a obscure handle to
|
||||
* be passed to all functions below.
|
||||
**********************************************************************/
|
||||
typedef struct tr_handle_s tr_handle_t;
|
||||
|
||||
tr_handle_t * tr_init();
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setBindPort
|
||||
***********************************************************************
|
||||
* Sets a "start" port: everytime we start a torrent, we try to bind
|
||||
* this port, then the next one and so on until we are successful.
|
||||
**********************************************************************/
|
||||
void tr_setBindPort( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setUploadLimit
|
||||
***********************************************************************
|
||||
* Sets the total upload rate limit in KB/s
|
||||
**********************************************************************/
|
||||
void tr_setUploadLimit( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentRates
|
||||
***********************************************************************
|
||||
* Gets the total download and upload rates
|
||||
**********************************************************************/
|
||||
void tr_torrentRates( tr_handle_t *, float *, float * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentInit
|
||||
***********************************************************************
|
||||
* Opens and parses torrent file at 'path'. If the file exists and is a
|
||||
* valid torrent file, returns 0 and adds it to the list of torrents
|
||||
* (but doesn't start it). Returns a non-zero value otherwise.
|
||||
**********************************************************************/
|
||||
int tr_torrentInit( tr_handle_t *, const char * path );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentScrape
|
||||
***********************************************************************
|
||||
* Asks the tracker for the count of seeders and leechers. Returns 0
|
||||
* and fills 's' and 'l' if successful. Otherwise returns 1 if the
|
||||
* tracker doesn't support the scrape protocol, is unreachable or
|
||||
* replied with some error. tr_torrentScrape may block up to 20 seconds
|
||||
* before returning.
|
||||
**********************************************************************/
|
||||
int tr_torrentScrape( tr_handle_t *, int, int * s, int * l );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentStart
|
||||
***********************************************************************
|
||||
* Starts downloading. The download is launched in a seperate thread,
|
||||
* therefore tr_torrentStart returns immediately.
|
||||
**********************************************************************/
|
||||
void tr_torrentSetFolder( tr_handle_t *, int, const char * );
|
||||
char * tr_torrentGetFolder( tr_handle_t *, int );
|
||||
void tr_torrentStart( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentStop
|
||||
***********************************************************************
|
||||
* Stops downloading and notices the tracker that we are leaving. The
|
||||
* thread keeps running while doing so.
|
||||
* The thread will eventually be joined, either:
|
||||
* - by tr_torrentStat when the tracker has been successfully noticed,
|
||||
* - by tr_torrentStat if the tracker could not be noticed within 60s,
|
||||
* - by tr_torrentClose if you choose to remove the torrent without
|
||||
* waiting any further.
|
||||
**********************************************************************/
|
||||
void tr_torrentStop( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentStat
|
||||
***********************************************************************
|
||||
* Allocates an array of tr_stat_t structures, containing information
|
||||
* about the current status of all open torrents (see the contents
|
||||
* of tr_stat_s below). Returns the count of open torrents and sets
|
||||
* (*s) to the address of the array, or NULL if no torrent is open.
|
||||
* In the former case, the array belongs to the caller who is
|
||||
* responsible of freeing it.
|
||||
* The interface should call this function every 0.5 second or so in
|
||||
* order to update itself.
|
||||
**********************************************************************/
|
||||
typedef struct tr_stat_s tr_stat_t;
|
||||
|
||||
int tr_torrentCount( tr_handle_t * h );
|
||||
int tr_torrentStat( tr_handle_t *, tr_stat_t ** s );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentClose
|
||||
***********************************************************************
|
||||
* Frees memory allocated by tr_torrentInit. If the torrent was running,
|
||||
* you must call tr_torrentStop() before closing it.
|
||||
**********************************************************************/
|
||||
void tr_torrentClose( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_close
|
||||
***********************************************************************
|
||||
* Frees memory allocated by tr_init.
|
||||
**********************************************************************/
|
||||
void tr_close( tr_handle_t * );
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* tr_stat_s
|
||||
**********************************************************************/
|
||||
typedef struct tr_file_s
|
||||
{
|
||||
uint64_t length; /* Length of the file, in bytes */
|
||||
char name[MAX_PATH_LENGTH]; /* Path to the file */
|
||||
}
|
||||
tr_file_t;
|
||||
|
||||
typedef struct tr_info_s
|
||||
{
|
||||
/* Path to torrent */
|
||||
char torrent[MAX_PATH_LENGTH];
|
||||
|
||||
/* General info */
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
char name[MAX_PATH_LENGTH];
|
||||
|
||||
/* Tracker info */
|
||||
char trackerAddress[256];
|
||||
int trackerPort;
|
||||
char trackerAnnounce[MAX_PATH_LENGTH];
|
||||
|
||||
/* Pieces info */
|
||||
int pieceSize;
|
||||
int pieceCount;
|
||||
uint64_t totalSize;
|
||||
uint8_t * pieces;
|
||||
|
||||
/* Files info */
|
||||
int fileCount;
|
||||
tr_file_t * files;
|
||||
}
|
||||
tr_info_t;
|
||||
|
||||
struct tr_stat_s
|
||||
{
|
||||
tr_info_t info;
|
||||
|
||||
#define TR_STATUS_CHECK 0x001 /* Checking files */
|
||||
#define TR_STATUS_DOWNLOAD 0x002 /* Downloading */
|
||||
#define TR_STATUS_SEED 0x004 /* Seeding */
|
||||
#define TR_STATUS_STOPPING 0x008 /* Sending 'stopped' to the tracker */
|
||||
#define TR_STATUS_STOPPED 0x010 /* Sent 'stopped' but thread still
|
||||
running (for internal use only) */
|
||||
#define TR_STATUS_PAUSE 0x020 /* Paused */
|
||||
#define TR_TRACKER_ERROR 0x100
|
||||
int status;
|
||||
char error[128];
|
||||
|
||||
float progress;
|
||||
float rateDownload;
|
||||
float rateUpload;
|
||||
int eta;
|
||||
int peersTotal;
|
||||
int peersUploading;
|
||||
int peersDownloading;
|
||||
char pieces[120];
|
||||
|
||||
uint64_t downloaded;
|
||||
uint64_t uploaded;
|
||||
|
||||
char * folder;
|
||||
};
|
||||
|
||||
#ifdef __TRANSMISSION__
|
||||
# include "internal.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
136
libtransmission/upload.c
Normal file
136
libtransmission/upload.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#define FOO 10
|
||||
|
||||
struct tr_upload_s
|
||||
{
|
||||
tr_lock_t lock;
|
||||
int limit; /* Max upload rate in KB/s */
|
||||
int count; /* Number of peers currently unchoked */
|
||||
uint64_t dates[FOO]; /* The last times we uploaded something */
|
||||
int sizes[FOO]; /* How many bytes we uploaded */
|
||||
};
|
||||
|
||||
tr_upload_t * tr_uploadInit()
|
||||
{
|
||||
tr_upload_t * u;
|
||||
|
||||
u = calloc( sizeof( tr_upload_t ), 1 );
|
||||
tr_lockInit( &u->lock );
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
void tr_uploadSetLimit( tr_upload_t * u, int limit )
|
||||
{
|
||||
tr_lockLock( u->lock );
|
||||
u->limit = limit;
|
||||
tr_lockUnlock( u->lock );
|
||||
}
|
||||
|
||||
int tr_uploadCanUnchoke( tr_upload_t * u )
|
||||
{
|
||||
int ret;
|
||||
|
||||
tr_lockLock( u->lock );
|
||||
if( u->limit < 0 )
|
||||
{
|
||||
/* Infinite number of slots */
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* One slot per 2 KB/s */
|
||||
ret = ( u->count < ( u->limit + 1 ) / 2 );
|
||||
}
|
||||
tr_lockUnlock( u->lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tr_uploadChoked( tr_upload_t * u )
|
||||
{
|
||||
tr_lockLock( u->lock );
|
||||
(u->count)--;
|
||||
tr_lockUnlock( u->lock );
|
||||
}
|
||||
|
||||
void tr_uploadUnchoked( tr_upload_t * u )
|
||||
{
|
||||
tr_lockLock( u->lock );
|
||||
(u->count)++;
|
||||
tr_lockUnlock( u->lock );
|
||||
}
|
||||
|
||||
int tr_uploadCanUpload( tr_upload_t * u )
|
||||
{
|
||||
int ret, i, size;
|
||||
uint64_t now;
|
||||
|
||||
tr_lockLock( u->lock );
|
||||
if( u->limit < 0 )
|
||||
{
|
||||
/* No limit */
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = 0;
|
||||
size = 0;
|
||||
now = tr_date();
|
||||
|
||||
/* Check the last four times we sent something and decide if
|
||||
we must wait */
|
||||
for( i = 0; i < FOO; i++ )
|
||||
{
|
||||
size += u->sizes[i];
|
||||
if( (uint64_t) size < 1024ULL *
|
||||
u->limit * ( now - u->dates[i] ) / 1000 )
|
||||
{
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
tr_lockUnlock( u->lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tr_uploadUploaded( tr_upload_t * u, int size )
|
||||
{
|
||||
tr_lockLock( u->lock );
|
||||
memmove( &u->dates[1], &u->dates[0], (FOO-1) * sizeof( uint64_t ) );
|
||||
memmove( &u->sizes[1], &u->sizes[0], (FOO-1) * sizeof( int ) );
|
||||
u->dates[0] = tr_date();
|
||||
u->sizes[0] = size;
|
||||
tr_lockUnlock( u->lock );
|
||||
}
|
||||
|
||||
void tr_uploadClose( tr_upload_t * u )
|
||||
{
|
||||
tr_lockClose( u->lock );
|
||||
free( u );
|
||||
}
|
||||
32
libtransmission/upload.h
Normal file
32
libtransmission/upload.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
typedef struct tr_upload_s tr_upload_t;
|
||||
|
||||
tr_upload_t * tr_uploadInit();
|
||||
void tr_uploadSetLimit( tr_upload_t *, int );
|
||||
int tr_uploadCanUnchoke( tr_upload_t * );
|
||||
void tr_uploadChoked( tr_upload_t * );
|
||||
void tr_uploadUnchoked( tr_upload_t * );
|
||||
int tr_uploadCanUpload( tr_upload_t * );
|
||||
void tr_uploadUploaded( tr_upload_t *, int );
|
||||
void tr_uploadClose( tr_upload_t * );
|
||||
63
libtransmission/utils.c
Normal file
63
libtransmission/utils.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
void tr_msg( int level, char * msg, ... )
|
||||
{
|
||||
char string[256];
|
||||
va_list args;
|
||||
static int verboseLevel = 0;
|
||||
|
||||
if( !verboseLevel )
|
||||
{
|
||||
char * env;
|
||||
env = getenv( "TR_DEBUG" );
|
||||
verboseLevel = env ? atoi( env ) : -1;
|
||||
verboseLevel = verboseLevel ? verboseLevel : -1;
|
||||
}
|
||||
|
||||
if( verboseLevel < 1 && level > TR_MSG_ERR )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( verboseLevel < 2 && level > TR_MSG_INF )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
va_start( args, msg );
|
||||
vsnprintf( string, sizeof( string ), msg, args );
|
||||
va_end( args );
|
||||
fprintf( stderr, "%s\n", string );
|
||||
}
|
||||
|
||||
int tr_rand( int sup )
|
||||
{
|
||||
static int init = 0;
|
||||
if( !init )
|
||||
{
|
||||
srand( tr_date() );
|
||||
init = 1;
|
||||
}
|
||||
return rand() % sup;
|
||||
}
|
||||
147
libtransmission/utils.h
Normal file
147
libtransmission/utils.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_UTILS_H
|
||||
#define TR_UTILS_H 1
|
||||
|
||||
#define TR_MSG_ERR 1
|
||||
#define TR_MSG_INF 2
|
||||
#define TR_MSG_DBG 4
|
||||
#define tr_err( a... ) tr_msg( TR_MSG_ERR, ## a )
|
||||
#define tr_inf( a... ) tr_msg( TR_MSG_INF, ## a )
|
||||
#define tr_dbg( a... ) tr_msg( TR_MSG_DBG, ## a )
|
||||
void tr_msg ( int level, char * msg, ... );
|
||||
|
||||
int tr_rand ( int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_date
|
||||
***********************************************************************
|
||||
* Returns the current date in milliseconds
|
||||
**********************************************************************/
|
||||
static inline uint64_t tr_date()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday( &tv, NULL );
|
||||
return( (uint64_t) tv.tv_sec * 1000 + (uint64_t) tv.tv_usec / 1000 );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_wait
|
||||
***********************************************************************
|
||||
* Wait 'delay' milliseconds
|
||||
**********************************************************************/
|
||||
static inline void tr_wait( uint64_t delay )
|
||||
{
|
||||
#ifdef SYS_BEOS
|
||||
snooze( 1000 * delay );
|
||||
#else
|
||||
usleep( 1000 * delay );
|
||||
#endif
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_bitfieldHas
|
||||
**********************************************************************/
|
||||
static inline int tr_bitfieldHas( uint8_t * bitfield, int piece )
|
||||
{
|
||||
return ( bitfield[ piece / 8 ] & ( 1 << ( 7 - ( piece % 8 ) ) ) );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_bitfieldAdd
|
||||
**********************************************************************/
|
||||
static inline void tr_bitfieldAdd( uint8_t * bitfield, int piece )
|
||||
{
|
||||
bitfield[ piece / 8 ] |= ( 1 << ( 7 - ( piece % 8 ) ) );
|
||||
}
|
||||
|
||||
#define tr_blockPiece(a) _tr_blockPiece(tor,a)
|
||||
static inline int _tr_blockPiece( tr_torrent_t * tor, int block )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
return block / ( inf->pieceSize / tor->blockSize );
|
||||
}
|
||||
|
||||
#define tr_blockSize(a) _tr_blockSize(tor,a)
|
||||
static inline int _tr_blockSize( tr_torrent_t * tor, int block )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
int dummy;
|
||||
|
||||
if( block != tor->blockCount - 1 ||
|
||||
!( dummy = inf->totalSize % tor->blockSize ) )
|
||||
{
|
||||
return tor->blockSize;
|
||||
}
|
||||
|
||||
return dummy;
|
||||
}
|
||||
|
||||
#define tr_blockPosInPiece(a) _tr_blockPosInPiece(tor,a)
|
||||
static inline int _tr_blockPosInPiece( tr_torrent_t * tor, int block )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
return tor->blockSize *
|
||||
( block % ( inf->pieceSize / tor->blockSize ) );
|
||||
}
|
||||
|
||||
#define tr_pieceCountBlocks(a) _tr_pieceCountBlocks(tor,a)
|
||||
static inline int _tr_pieceCountBlocks( tr_torrent_t * tor, int piece )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
if( piece < inf->pieceCount - 1 ||
|
||||
!( tor->blockCount % ( inf->pieceSize / tor->blockSize ) ) )
|
||||
{
|
||||
return inf->pieceSize / tor->blockSize;
|
||||
}
|
||||
return tor->blockCount % ( inf->pieceSize / tor->blockSize );
|
||||
}
|
||||
|
||||
#define tr_pieceStartBlock(a) _tr_pieceStartBlock(tor,a)
|
||||
static inline int _tr_pieceStartBlock( tr_torrent_t * tor, int piece )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
return piece * ( inf->pieceSize / tor->blockSize );
|
||||
}
|
||||
|
||||
#define tr_pieceSize(a) _tr_pieceSize(tor,a)
|
||||
static inline int _tr_pieceSize( tr_torrent_t * tor, int piece )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
if( piece < inf->pieceCount - 1 ||
|
||||
!( inf->totalSize % inf->pieceSize ) )
|
||||
{
|
||||
return inf->pieceSize;
|
||||
}
|
||||
return inf->totalSize % inf->pieceSize;
|
||||
}
|
||||
|
||||
#define tr_block(a,b) _tr_block(tor,a,b)
|
||||
static inline int _tr_block( tr_torrent_t * tor, int index, int begin )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
return index * ( inf->pieceSize / tor->blockSize ) +
|
||||
begin / tor->blockSize;
|
||||
}
|
||||
|
||||
#endif
|
||||
78
macosx/Controller.h
Normal file
78
macosx/Controller.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <transmission.h>
|
||||
#include "PrefsController.h"
|
||||
|
||||
@interface Controller : NSObject
|
||||
{
|
||||
tr_handle_t * fHandle;
|
||||
int fCount;
|
||||
tr_stat_t * fStat;
|
||||
int fResumeOnWake[TR_MAX_TORRENT_COUNT];
|
||||
|
||||
NSToolbar * fToolbar;
|
||||
|
||||
IBOutlet PrefsController * fPrefsController;
|
||||
|
||||
IBOutlet NSMenuItem * fAdvancedBarItem;
|
||||
|
||||
IBOutlet NSWindow * fWindow;
|
||||
IBOutlet NSTableView * fTableView;
|
||||
IBOutlet NSTextField * fTotalDLField;
|
||||
IBOutlet NSTextField * fTotalULField;
|
||||
|
||||
IBOutlet NSPanel * fInfoPanel;
|
||||
IBOutlet NSTextField * fInfoTitle;
|
||||
IBOutlet NSTextField * fInfoTracker;
|
||||
IBOutlet NSTextField * fInfoAnnounce;
|
||||
IBOutlet NSTextField * fInfoSize;
|
||||
IBOutlet NSTextField * fInfoPieces;
|
||||
IBOutlet NSTextField * fInfoPieceSize;
|
||||
IBOutlet NSTextField * fInfoFolder;
|
||||
IBOutlet NSTextField * fInfoDownloaded;
|
||||
IBOutlet NSTextField * fInfoUploaded;
|
||||
|
||||
io_connect_t fRootPort;
|
||||
NSArray * fFilenames;
|
||||
NSTimer * fTimer;
|
||||
}
|
||||
|
||||
- (void) advancedChanged: (id) sender;
|
||||
- (void) openShowSheet: (id) sender;
|
||||
- (void) openSheetClosed: (NSOpenPanel *) s returnCode: (int) code
|
||||
contextInfo: (void *) info;
|
||||
- (void) stopTorrent: (id) sender;
|
||||
- (void) resumeTorrent: (id) sender;
|
||||
- (void) removeTorrent: (id) sender;
|
||||
- (void) showInfo: (id) sender;
|
||||
|
||||
- (void) updateUI: (NSTimer *) timer;
|
||||
- (void) sleepCallBack: (natural_t) messageType argument:
|
||||
(void *) messageArgument;
|
||||
|
||||
- (void) showMainWindow: (id) sender;
|
||||
- (void) linkHomepage: (id) sender;
|
||||
- (void) linkForums: (id) sender;
|
||||
|
||||
@end
|
||||
705
macosx/Controller.m
Normal file
705
macosx/Controller.m
Normal file
@@ -0,0 +1,705 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#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_INFO @"Toolbar Info"
|
||||
|
||||
static void sleepCallBack( void * controller, io_service_t y,
|
||||
natural_t messageType, void * messageArgument )
|
||||
{
|
||||
Controller * c = controller;
|
||||
[c sleepCallBack: messageType argument: messageArgument];
|
||||
}
|
||||
|
||||
@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];
|
||||
|
||||
[self enableToolbarItem: TOOLBAR_RESUME flag: NO];
|
||||
[self enableToolbarItem: TOOLBAR_STOP flag: NO];
|
||||
[self enableToolbarItem: TOOLBAR_REMOVE flag: NO];
|
||||
|
||||
if( row < 0 )
|
||||
{
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) awakeFromNib
|
||||
{
|
||||
fHandle = tr_init();
|
||||
|
||||
[fPrefsController setHandle: fHandle];
|
||||
|
||||
[fWindow setContentMinSize: NSMakeSize( 400, 120 )];
|
||||
|
||||
/* Check or uncheck menu item in respect to current preferences */
|
||||
[fAdvancedBarItem setState: [[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:@"UseAdvancedBar"] ? NSOnState : NSOffState];
|
||||
|
||||
fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Transmission Toolbar"];
|
||||
[fToolbar setDelegate: self];
|
||||
[fToolbar setAllowsUserCustomization: YES];
|
||||
[fToolbar setAutosavesConfiguration: YES];
|
||||
[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];
|
||||
|
||||
NSTableColumn * tableColumn;
|
||||
NameCell * nameCell;
|
||||
ProgressCell * progressCell;
|
||||
|
||||
nameCell = [[NameCell alloc] init];
|
||||
progressCell = [[ProgressCell alloc] init];
|
||||
tableColumn = [fTableView tableColumnWithIdentifier: @"Name"];
|
||||
[tableColumn setDataCell: nameCell];
|
||||
[tableColumn setMinWidth: 10.0];
|
||||
[tableColumn setMaxWidth: 3000.0];
|
||||
|
||||
tableColumn = [fTableView tableColumnWithIdentifier: @"Progress"];
|
||||
[tableColumn setDataCell: progressCell];
|
||||
[tableColumn setMinWidth: 134.0];
|
||||
[tableColumn setMaxWidth: 134.0];
|
||||
|
||||
[fTableView sizeToFit];
|
||||
|
||||
[fTableView registerForDraggedTypes: [NSArray arrayWithObjects:
|
||||
NSFilenamesPboardType, NULL]];
|
||||
|
||||
IONotificationPortRef notify;
|
||||
io_object_t anIterator;
|
||||
|
||||
/* Register for sleep notifications */
|
||||
fRootPort = IORegisterForSystemPower( self, ¬ify, sleepCallBack,
|
||||
&anIterator);
|
||||
if( !fRootPort )
|
||||
{
|
||||
printf( "Could not IORegisterForSystemPower\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
CFRunLoopAddSource( CFRunLoopGetCurrent(),
|
||||
IONotificationPortGetRunLoopSource( notify ),
|
||||
kCFRunLoopCommonModes );
|
||||
}
|
||||
|
||||
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
NSArray * history = [defaults arrayForKey: @"History"];
|
||||
if( history )
|
||||
{
|
||||
unsigned i;
|
||||
NSDictionary * dic;
|
||||
NSString * torrentPath, * downloadFolder, * paused;
|
||||
|
||||
for( i = 0; i < [history count]; i++ )
|
||||
{
|
||||
dic = [history objectAtIndex: i];
|
||||
|
||||
torrentPath = [dic objectForKey: @"TorrentPath"];
|
||||
downloadFolder = [dic objectForKey: @"DownloadFolder"];
|
||||
paused = [dic objectForKey: @"Paused"];
|
||||
|
||||
if( !torrentPath || !downloadFolder || !paused )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[downloadFolder UTF8String] );
|
||||
|
||||
if( [paused isEqualToString: @"NO"] )
|
||||
{
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the interface every 500 ms */
|
||||
fCount = 0;
|
||||
fStat = NULL;
|
||||
fTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 target: self
|
||||
selector: @selector( updateUI: ) userInfo: NULL repeats: YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer: fTimer
|
||||
forMode: NSModalPanelRunLoopMode];
|
||||
}
|
||||
|
||||
- (BOOL) windowShouldClose: (id) sender
|
||||
{
|
||||
[fWindow orderOut: NULL];
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) applicationShouldHandleReopen: (NSApplication *) app
|
||||
hasVisibleWindows: (BOOL) flag
|
||||
{
|
||||
[self showMainWindow: NULL];
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply) applicationShouldTerminate:
|
||||
(NSApplication *) app
|
||||
{
|
||||
NSMutableArray * history = [NSMutableArray
|
||||
arrayWithCapacity: TR_MAX_TORRENT_COUNT];
|
||||
int i;
|
||||
|
||||
/* Stop updating the interface */
|
||||
[fTimer invalidate];
|
||||
|
||||
/* Save history and stop running torrents */
|
||||
for( i = 0; i < fCount; i++ )
|
||||
{
|
||||
[history addObject: [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithUTF8String: fStat[i].info.torrent],
|
||||
@"TorrentPath",
|
||||
[NSString stringWithUTF8String: tr_torrentGetFolder( fHandle, i )],
|
||||
@"DownloadFolder",
|
||||
( fStat[i].status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD |
|
||||
TR_STATUS_SEED ) ) ? @"NO" : @"YES",
|
||||
@"Paused",
|
||||
NULL]];
|
||||
|
||||
if( fStat[i].status & ( TR_STATUS_CHECK |
|
||||
TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
{
|
||||
tr_torrentStop( fHandle, i );
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for torrents to stop (5 seconds timeout) */
|
||||
NSDate * start = [NSDate date];
|
||||
while( fCount > 0 )
|
||||
{
|
||||
while( [[NSDate date] timeIntervalSinceDate: start] < 5 )
|
||||
{
|
||||
fCount = tr_torrentStat( fHandle, &fStat );
|
||||
if( fStat[0].status & TR_STATUS_PAUSE )
|
||||
{
|
||||
break;
|
||||
}
|
||||
usleep( 500000 );
|
||||
}
|
||||
tr_torrentClose( fHandle, 0 );
|
||||
fCount = tr_torrentStat( fHandle, &fStat );
|
||||
}
|
||||
|
||||
tr_close( fHandle );
|
||||
|
||||
[[NSUserDefaults standardUserDefaults]
|
||||
setObject: history forKey: @"History"];
|
||||
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
- (void) folderChoiceClosed: (NSOpenPanel *) s returnCode: (int) code
|
||||
contextInfo: (void *) info
|
||||
{
|
||||
if( code != NSOKButton )
|
||||
{
|
||||
tr_torrentClose( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
[NSApp stopModal];
|
||||
return;
|
||||
}
|
||||
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[[[s filenames] objectAtIndex: 0] UTF8String] );
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
[NSApp stopModal];
|
||||
}
|
||||
|
||||
|
||||
- (void) application: (NSApplication *) sender
|
||||
openFiles: (NSArray *) filenames
|
||||
{
|
||||
unsigned i;
|
||||
NSUserDefaults * defaults;
|
||||
NSString * downloadChoice, * downloadFolder, * torrentPath;
|
||||
|
||||
defaults = [NSUserDefaults standardUserDefaults];
|
||||
downloadChoice = [defaults stringForKey: @"DownloadChoice"];
|
||||
downloadFolder = [defaults stringForKey: @"DownloadFolder"];
|
||||
|
||||
for( i = 0; i < [filenames count]; i++ )
|
||||
{
|
||||
torrentPath = [filenames objectAtIndex: i];
|
||||
|
||||
if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( [downloadChoice isEqualToString: @"Constant"] )
|
||||
{
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[downloadFolder UTF8String] );
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( [downloadChoice isEqualToString: @"Torrent"] )
|
||||
{
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[[torrentPath stringByDeletingLastPathComponent] UTF8String] );
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
NSOpenPanel * panel;
|
||||
NSString * message;
|
||||
|
||||
panel = [NSOpenPanel openPanel];
|
||||
message = [NSString stringWithFormat:
|
||||
@"Select the download folder for %@",
|
||||
[torrentPath lastPathComponent]];
|
||||
|
||||
[panel setPrompt: @"Select"];
|
||||
[panel setMessage: message];
|
||||
[panel setAllowsMultipleSelection: NO];
|
||||
[panel setCanChooseFiles: NO];
|
||||
[panel setCanChooseDirectories: YES];
|
||||
|
||||
[panel beginSheetForDirectory: NULL file: NULL types: NULL
|
||||
modalForWindow: fWindow modalDelegate: self didEndSelector:
|
||||
@selector( folderChoiceClosed:returnCode:contextInfo: )
|
||||
contextInfo: NULL];
|
||||
[NSApp runModalForWindow: panel];
|
||||
}
|
||||
|
||||
[self updateUI: NULL];
|
||||
}
|
||||
|
||||
- (void) advancedChanged: (id) sender
|
||||
{
|
||||
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
|
||||
if( [fAdvancedBarItem state] == NSOnState )
|
||||
{
|
||||
[fAdvancedBarItem setState: NSOffState];
|
||||
[defaults setObject:@"NO" forKey:@"UseAdvancedBar"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fAdvancedBarItem setState: NSOnState];
|
||||
[defaults setObject:@"YES" forKey:@"UseAdvancedBar"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) openShowSheet: (id) sender
|
||||
{
|
||||
NSOpenPanel * panel;
|
||||
NSArray * fileTypes;
|
||||
|
||||
panel = [NSOpenPanel openPanel];
|
||||
fileTypes = [NSArray arrayWithObject: @"torrent"];
|
||||
|
||||
[panel setAllowsMultipleSelection: YES];
|
||||
[panel setCanChooseFiles: YES];
|
||||
[panel setCanChooseDirectories: NO];
|
||||
|
||||
[panel beginSheetForDirectory: NULL file: NULL types: fileTypes
|
||||
modalForWindow: fWindow modalDelegate: self didEndSelector:
|
||||
@selector( openSheetClosed:returnCode:contextInfo: )
|
||||
contextInfo: NULL];
|
||||
}
|
||||
|
||||
- (void) cantFindAName: (id) sender
|
||||
{
|
||||
[self application: NSApp openFiles: fFilenames];
|
||||
[fFilenames release];
|
||||
}
|
||||
|
||||
- (void) openSheetClosed: (NSOpenPanel *) s returnCode: (int) code
|
||||
contextInfo: (void *) info
|
||||
{
|
||||
if( code != NSOKButton )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fFilenames = [[s filenames] retain];
|
||||
|
||||
[self performSelectorOnMainThread: @selector(cantFindAName:)
|
||||
withObject: NULL waitUntilDone: NO];
|
||||
}
|
||||
|
||||
- (void) resumeTorrent: (id) sender
|
||||
{
|
||||
tr_torrentStart( fHandle, [fTableView selectedRow] );
|
||||
[self updateToolbar];
|
||||
}
|
||||
|
||||
- (void) stopTorrent: (id) sender
|
||||
{
|
||||
tr_torrentStop( fHandle, [fTableView selectedRow] );
|
||||
[self updateToolbar];
|
||||
}
|
||||
|
||||
- (void) removeTorrent: (id) sender
|
||||
{
|
||||
tr_torrentClose( fHandle, [fTableView selectedRow] );
|
||||
[self updateUI: NULL];
|
||||
}
|
||||
|
||||
- (void) showInfo: (id) sender
|
||||
{
|
||||
if( [fInfoPanel isVisible] )
|
||||
{
|
||||
[fInfoPanel close];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fInfoPanel orderFront: sender];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateUI: (NSTimer *) t
|
||||
{
|
||||
float dl, ul;
|
||||
int row;
|
||||
|
||||
/* Update the NSTableView */
|
||||
if( fStat )
|
||||
{
|
||||
free( fStat );
|
||||
}
|
||||
fCount = tr_torrentStat( fHandle, &fStat );
|
||||
[fTableView reloadData];
|
||||
|
||||
/* Update the global DL/UL rates */
|
||||
tr_torrentRates( fHandle, &dl, &ul );
|
||||
[fTotalDLField setStringValue: [NSString stringWithFormat:
|
||||
@"Total DL: %.2f KB/s", dl]];
|
||||
[fTotalULField setStringValue: [NSString stringWithFormat:
|
||||
@"Total UL: %.2f KB/s", ul]];
|
||||
|
||||
/* Update DL/UL totals in the Info panel */
|
||||
row = [fTableView selectedRow];
|
||||
if( row > -1 )
|
||||
{
|
||||
[fInfoDownloaded setStringValue:
|
||||
stringForFileSize( fStat[row].downloaded )];
|
||||
[fInfoUploaded setStringValue:
|
||||
stringForFileSize( fStat[row].uploaded )];
|
||||
}
|
||||
|
||||
/* Must we do this? Can't remember */
|
||||
[self updateToolbar];
|
||||
}
|
||||
|
||||
- (int) numberOfRowsInTableView: (NSTableView *) t
|
||||
{
|
||||
return fCount;
|
||||
}
|
||||
|
||||
- (id) tableView: (NSTableView *) t objectValueForTableColumn:
|
||||
(NSTableColumn *) tableColumn row: (int) rowIndex
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (void) tableView: (NSTableView *) t willDisplayCell: (id) cell
|
||||
forTableColumn: (NSTableColumn *) tableColumn row: (int) rowIndex
|
||||
{
|
||||
if( [[tableColumn identifier] isEqualToString: @"Name"] )
|
||||
{
|
||||
[(NameCell *) cell setStat: &fStat[rowIndex]];
|
||||
}
|
||||
else if( [[tableColumn identifier] isEqualToString: @"Progress"] )
|
||||
{
|
||||
[(ProgressCell *) cell setStat: &fStat[rowIndex]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) tableView: (NSTableView *) t acceptDrop:
|
||||
(id <NSDraggingInfo>) info row: (int) row dropOperation:
|
||||
(NSTableViewDropOperation) operation
|
||||
{
|
||||
NSPasteboard * pasteboard;
|
||||
|
||||
pasteboard = [info draggingPasteboard];
|
||||
if( ![[pasteboard types] containsObject: NSFilenamesPboardType] )
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self application: NSApp openFiles:
|
||||
[pasteboard propertyListForType: NSFilenamesPboardType]];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSDragOperation) tableView: (NSTableView *) t validateDrop:
|
||||
(id <NSDraggingInfo>) info proposedRow: (int) row
|
||||
proposedDropOperation: (NSTableViewDropOperation) operation
|
||||
{
|
||||
return NSDragOperationGeneric;
|
||||
}
|
||||
|
||||
- (void) tableViewSelectionDidChange: (NSNotification *) n
|
||||
{
|
||||
int row = [fTableView selectedRow];
|
||||
|
||||
[self updateToolbar];
|
||||
|
||||
if( row < 0 )
|
||||
{
|
||||
[fInfoTitle setStringValue: @"No torrent selected"];
|
||||
[fInfoTracker setStringValue: @""];
|
||||
[fInfoAnnounce setStringValue: @""];
|
||||
[fInfoSize setStringValue: @""];
|
||||
[fInfoPieces setStringValue: @""];
|
||||
[fInfoPieceSize setStringValue: @""];
|
||||
[fInfoFolder setStringValue: @""];
|
||||
[fInfoDownloaded setStringValue: @""];
|
||||
[fInfoUploaded setStringValue: @""];
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update info window */
|
||||
[fInfoTitle setStringValue: [NSString stringWithCString:
|
||||
fStat[row].info.name]];
|
||||
[fInfoTracker setStringValue: [NSString stringWithFormat:
|
||||
@"%s:%d", fStat[row].info.trackerAddress, fStat[row].info.trackerPort]];
|
||||
[fInfoAnnounce setStringValue: [NSString stringWithCString:
|
||||
fStat[row].info.trackerAnnounce]];
|
||||
[fInfoSize setStringValue:
|
||||
stringForFileSize( fStat[row].info.totalSize )];
|
||||
[fInfoPieces setStringValue: [NSString stringWithFormat: @"%d",
|
||||
fStat[row].info.pieceCount]];
|
||||
[fInfoPieceSize setStringValue:
|
||||
stringForFileSize( fStat[row].info.pieceSize )];
|
||||
[fInfoFolder setStringValue: [[NSString stringWithUTF8String:
|
||||
tr_torrentGetFolder( fHandle, row )] lastPathComponent]];
|
||||
}
|
||||
|
||||
- (NSToolbarItem *) toolbar: (NSToolbar *) t itemForItemIdentifier:
|
||||
(NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
|
||||
{
|
||||
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"]];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_REMOVE] )
|
||||
{
|
||||
[item setLabel: @"Remove"];
|
||||
[item setToolTip: @"Remove torrent from list"];
|
||||
[item setImage: [NSImage imageNamed: @"Remove.tiff"]];
|
||||
}
|
||||
else if( [ident isEqualToString: TOOLBAR_INFO] )
|
||||
{
|
||||
[item setLabel: @"Info"];
|
||||
[item setToolTip: @"Information"];
|
||||
[item setImage: [NSImage imageNamed: @"Info.tiff"]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[item release];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) t
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
TOOLBAR_OPEN, TOOLBAR_RESUME, TOOLBAR_STOP, TOOLBAR_REMOVE,
|
||||
NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_INFO, NULL];
|
||||
}
|
||||
|
||||
- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) t
|
||||
{
|
||||
return [self toolbarAllowedItemIdentifiers: t];
|
||||
}
|
||||
|
||||
- (void) sleepCallBack: (natural_t) messageType argument:
|
||||
(void *) messageArgument
|
||||
{
|
||||
int i;
|
||||
|
||||
switch( messageType )
|
||||
{
|
||||
case kIOMessageSystemWillSleep:
|
||||
/* Close all connections before going to sleep and remember
|
||||
we should resume when we wake up */
|
||||
for( i = 0; i < fCount; i++ )
|
||||
{
|
||||
if( fStat[i].status & ( TR_STATUS_CHECK |
|
||||
TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
{
|
||||
tr_torrentStop( fHandle, i );
|
||||
fResumeOnWake[i] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fResumeOnWake[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: wait a few seconds to let the torrents
|
||||
stop properly */
|
||||
|
||||
IOAllowPowerChange( fRootPort, (long) messageArgument );
|
||||
break;
|
||||
|
||||
case kIOMessageCanSystemSleep:
|
||||
/* Do not prevent idle sleep */
|
||||
/* TODO: prevent it unless there are all paused? */
|
||||
IOAllowPowerChange( fRootPort, (long) messageArgument );
|
||||
break;
|
||||
|
||||
case kIOMessageSystemHasPoweredOn:
|
||||
/* Resume download after we wake up */
|
||||
for( i = 0; i < fCount; i++ )
|
||||
{
|
||||
if( fResumeOnWake[i] )
|
||||
{
|
||||
tr_torrentStart( fHandle, i );
|
||||
}
|
||||
}
|
||||
[self updateToolbar];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSRect) windowWillUseStandardFrame: (NSWindow *) w
|
||||
defaultFrame: (NSRect) defaultFrame
|
||||
{
|
||||
NSRect rectWin, rectView;
|
||||
float foo;
|
||||
|
||||
rectWin = [fWindow frame];
|
||||
rectView = [[fWindow contentView] frame];
|
||||
foo = 68.0 + MAX( 1, tr_torrentCount( fHandle ) ) * 62.0 -
|
||||
rectView.size.height;
|
||||
|
||||
rectWin.size.height += foo;
|
||||
rectWin.origin.y -= foo;
|
||||
|
||||
return rectWin;
|
||||
}
|
||||
|
||||
- (void) showMainWindow: (id) sender
|
||||
{
|
||||
[fWindow makeKeyAndOrderFront: NULL];
|
||||
}
|
||||
|
||||
- (void) linkHomepage: (id) sender
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL: [NSURL
|
||||
URLWithString:@"http://transmission.m0k.org/"]];
|
||||
}
|
||||
|
||||
- (void) linkForums: (id) sender
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL: [NSURL
|
||||
URLWithString:@"http://transmission.m0k.org/forum/"]];
|
||||
}
|
||||
|
||||
@end
|
||||
4
macosx/English.lproj/InfoPlist.strings
Normal file
4
macosx/English.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,4 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
CFBundleName = "Transmission";
|
||||
NSHumanReadableCopyright = "Copyright 2005 Eric Petit";
|
||||
55
macosx/English.lproj/MainMenu.nib/classes.nib
generated
Normal file
55
macosx/English.lproj/MainMenu.nib/classes.nib
generated
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
IBClasses = (
|
||||
{
|
||||
ACTIONS = {
|
||||
advancedChanged = id;
|
||||
linkForums = id;
|
||||
linkHomepage = id;
|
||||
openShowSheet = id;
|
||||
removeTorrent = id;
|
||||
resumeTorrent = id;
|
||||
showInfo = id;
|
||||
showMainWindow = id;
|
||||
stopTorrent = id;
|
||||
};
|
||||
CLASS = Controller;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {
|
||||
fAdvancedBarItem = NSMenuItem;
|
||||
fInfoAnnounce = NSTextField;
|
||||
fInfoDownloaded = NSTextField;
|
||||
fInfoFolder = NSTextField;
|
||||
fInfoPanel = NSPanel;
|
||||
fInfoPieceSize = NSTextField;
|
||||
fInfoPieces = NSTextField;
|
||||
fInfoSize = NSTextField;
|
||||
fInfoTitle = NSTextField;
|
||||
fInfoTracker = NSTextField;
|
||||
fInfoUploaded = NSTextField;
|
||||
fPrefsController = PrefsController;
|
||||
fTableView = NSTableView;
|
||||
fTotalDLField = NSTextField;
|
||||
fTotalULField = NSTextField;
|
||||
fWindow = NSWindow;
|
||||
};
|
||||
SUPERCLASS = NSObject;
|
||||
},
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
|
||||
{
|
||||
ACTIONS = {cancel = id; check = id; ratio = id; save = id; show = id; };
|
||||
CLASS = PrefsController;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {
|
||||
fFolderMatrix = NSMatrix;
|
||||
fFolderPopUp = NSPopUpButton;
|
||||
fPortField = NSTextField;
|
||||
fPrefsWindow = NSWindow;
|
||||
fUploadCheck = NSButton;
|
||||
fUploadField = NSTextField;
|
||||
fWindow = NSWindow;
|
||||
};
|
||||
SUPERCLASS = NSObject;
|
||||
}
|
||||
);
|
||||
IBVersion = 1;
|
||||
}
|
||||
26
macosx/English.lproj/MainMenu.nib/info.nib
generated
Normal file
26
macosx/English.lproj/MainMenu.nib/info.nib
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>204 84 361 432 0 0 1440 878 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>29</key>
|
||||
<string>105 768 371 44 0 0 1280 832 </string>
|
||||
</dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>439.0</string>
|
||||
<key>IBOldestOS</key>
|
||||
<integer>3</integer>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>21</integer>
|
||||
<integer>29</integer>
|
||||
<integer>273</integer>
|
||||
<integer>343</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8C46</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
macosx/English.lproj/MainMenu.nib/keyedobjects.nib
generated
Normal file
BIN
macosx/English.lproj/MainMenu.nib/keyedobjects.nib
generated
Normal file
Binary file not shown.
BIN
macosx/Images/Info.tiff
Normal file
BIN
macosx/Images/Info.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/Open.tiff
Normal file
BIN
macosx/Images/Open.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/Progress.tiff
Normal file
BIN
macosx/Images/Progress.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/Remove.tiff
Normal file
BIN
macosx/Images/Remove.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/Resume.tiff
Normal file
BIN
macosx/Images/Resume.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/RevealOff.tiff
Normal file
BIN
macosx/Images/RevealOff.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/RevealOn.tiff
Normal file
BIN
macosx/Images/RevealOn.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/Stop.tiff
Normal file
BIN
macosx/Images/Stop.tiff
Normal file
Binary file not shown.
BIN
macosx/Images/Transmission.icns
Normal file
BIN
macosx/Images/Transmission.icns
Normal file
Binary file not shown.
BIN
macosx/Images/TransmissionDocument.icns
Normal file
BIN
macosx/Images/TransmissionDocument.icns
Normal file
Binary file not shown.
45
macosx/Info.plist.in
Normal file
45
macosx/Info.plist.in
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>torrent</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>TransmissionDocument</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>BitTorrent Document</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<false/>
|
||||
<key>NSPersistentStoreTypeKey</key>
|
||||
<string>XML</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Transmission</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Transmission</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.m0k.transmission</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>TR##</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>%%VERSION%%</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
33
macosx/NameCell.h
Normal file
33
macosx/NameCell.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <transmission.h>
|
||||
|
||||
@interface NameCell : NSCell
|
||||
{
|
||||
tr_stat_t * fStat;
|
||||
NSRect fRevealRect;
|
||||
NSPoint fClickPoint;
|
||||
}
|
||||
- (void) setStat: (tr_stat_t *) stat;
|
||||
@end
|
||||
167
macosx/NameCell.m
Normal file
167
macosx/NameCell.m
Normal file
@@ -0,0 +1,167 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "NameCell.h"
|
||||
#include "Utils.h"
|
||||
|
||||
@implementation NameCell
|
||||
|
||||
- (void) setStat: (tr_stat_t *) stat;
|
||||
{
|
||||
fStat = stat;
|
||||
}
|
||||
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
|
||||
{
|
||||
if( ![view lockFocusIfCanDraw] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSString * nameString = NULL, * timeString = @"", * peersString = @"";
|
||||
NSMutableDictionary * attributes;
|
||||
attributes = [NSMutableDictionary dictionaryWithCapacity: 1];
|
||||
NSPoint pen = cellFrame.origin;
|
||||
|
||||
NSString * sizeString = [NSString stringWithFormat: @" (%@)",
|
||||
stringForFileSize( fStat->info.totalSize )];
|
||||
|
||||
nameString = [NSString stringWithFormat: @"%@%@",
|
||||
stringFittingInWidth( fStat->info.name, cellFrame.size.width -
|
||||
10 - widthForString( sizeString, 12 ), 12 ),
|
||||
sizeString];
|
||||
|
||||
if( fStat->status & TR_STATUS_PAUSE )
|
||||
{
|
||||
timeString = [NSString stringWithFormat:
|
||||
@"Paused (%.2f %%)", 100 * fStat->progress];
|
||||
peersString = @"";
|
||||
}
|
||||
else if( fStat->status & TR_STATUS_CHECK )
|
||||
{
|
||||
timeString = [NSString stringWithFormat:
|
||||
@"Checking existing files (%.2f %%)", 100 * fStat->progress];
|
||||
peersString = @"";
|
||||
}
|
||||
else if( fStat->status & TR_STATUS_DOWNLOAD )
|
||||
{
|
||||
if( fStat->eta < 0 )
|
||||
{
|
||||
timeString = [NSString stringWithFormat:
|
||||
@"Finishing in --:--:-- (%.2f %%)", 100 * fStat->progress];
|
||||
}
|
||||
else
|
||||
{
|
||||
timeString = [NSString stringWithFormat:
|
||||
@"Finishing in %02d:%02d:%02d (%.2f %%)",
|
||||
fStat->eta / 3600, ( fStat->eta / 60 ) % 60,
|
||||
fStat->eta % 60, 100 * fStat->progress];
|
||||
}
|
||||
peersString = [NSString stringWithFormat:
|
||||
@"Downloading from %d of %d peer%s",
|
||||
fStat->peersUploading, fStat->peersTotal,
|
||||
( fStat->peersTotal == 1 ) ? "" : "s"];
|
||||
}
|
||||
else if( fStat->status & TR_STATUS_SEED )
|
||||
{
|
||||
timeString = [NSString stringWithFormat:
|
||||
@"Seeding, uploading to %d of %d peer%s",
|
||||
fStat->peersDownloading, fStat->peersTotal,
|
||||
( fStat->peersTotal == 1 ) ? "" : "s"];
|
||||
peersString = @"";
|
||||
}
|
||||
else if( fStat->status & TR_STATUS_STOPPING )
|
||||
{
|
||||
timeString = @"Stopping...";
|
||||
peersString = @"";
|
||||
}
|
||||
|
||||
if( ( fStat->status & ( TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) &&
|
||||
( fStat->status & TR_TRACKER_ERROR ) )
|
||||
{
|
||||
peersString = [NSString stringWithFormat: @"%@%@",
|
||||
@"Error: ", stringFittingInWidth( fStat->error,
|
||||
cellFrame.size.width - 15 -
|
||||
widthForString( @"Error: ", 10 ), 10 )];
|
||||
}
|
||||
|
||||
[attributes setObject: [NSFont messageFontOfSize:12.0]
|
||||
forKey: NSFontAttributeName];
|
||||
|
||||
pen.x += 5; pen.y += 5;
|
||||
[nameString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
[attributes setObject: [NSFont messageFontOfSize:10.0]
|
||||
forKey: NSFontAttributeName];
|
||||
|
||||
pen.x += 5; pen.y += 20;
|
||||
[timeString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
pen.x += 0; pen.y += 15;
|
||||
[peersString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
/* "Reveal in Finder" button */
|
||||
fRevealRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19,
|
||||
cellFrame.origin.y + cellFrame.size.height - 19,
|
||||
14, 14 );
|
||||
NSImage * revealImage;
|
||||
if( NSPointInRect( fClickPoint, fRevealRect ) )
|
||||
{
|
||||
revealImage = [NSImage imageNamed: @"RevealOn.tiff"];
|
||||
}
|
||||
else
|
||||
{
|
||||
revealImage = [NSImage imageNamed: @"RevealOff.tiff"];
|
||||
}
|
||||
pen.x = fRevealRect.origin.x;
|
||||
pen.y = fRevealRect.origin.y + 14;
|
||||
[revealImage compositeToPoint: pen operation: NSCompositeSourceOver];
|
||||
|
||||
[view unlockFocus];
|
||||
}
|
||||
|
||||
/* Track mouse as long as button is down */
|
||||
- (BOOL) startTrackingAt: (NSPoint) start inView: (NSView *) v
|
||||
{
|
||||
fClickPoint = start;
|
||||
return YES;
|
||||
}
|
||||
- (BOOL) continueTracking: (NSPoint) last at: (NSPoint) current
|
||||
inView: (NSView *) v
|
||||
{
|
||||
fClickPoint = current;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) stopTracking: (NSPoint) last at:(NSPoint) stop
|
||||
inView: (NSView *) v mouseIsUp: (BOOL) flag
|
||||
{
|
||||
if( flag && NSPointInRect( stop, fRevealRect ) )
|
||||
{
|
||||
/* Reveal in Finder */
|
||||
[[NSWorkspace sharedWorkspace] openFile:
|
||||
[NSString stringWithUTF8String: fStat->folder]];
|
||||
}
|
||||
fClickPoint = NSMakePoint(0,0);
|
||||
}
|
||||
|
||||
@end
|
||||
49
macosx/PrefsController.h
Normal file
49
macosx/PrefsController.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <transmission.h>
|
||||
|
||||
@interface PrefsController : NSObject
|
||||
|
||||
{
|
||||
tr_handle_t * fHandle;
|
||||
|
||||
IBOutlet NSWindow * fWindow;
|
||||
IBOutlet NSWindow * fPrefsWindow;
|
||||
IBOutlet NSMatrix * fFolderMatrix;
|
||||
IBOutlet NSPopUpButton * fFolderPopUp;
|
||||
IBOutlet NSTextField * fPortField;
|
||||
IBOutlet NSButton * fUploadCheck;
|
||||
IBOutlet NSTextField * fUploadField;
|
||||
|
||||
NSString * fDownloadFolder;
|
||||
}
|
||||
|
||||
- (void) setHandle: (tr_handle_t *) handle;
|
||||
- (void) show: (id) sender;
|
||||
- (void) ratio: (id) sender;
|
||||
- (void) check: (id) sender;
|
||||
- (void) cancel: (id) sender;
|
||||
- (void) save: (id) sender;
|
||||
|
||||
@end
|
||||
374
macosx/PrefsController.m
Normal file
374
macosx/PrefsController.m
Normal file
@@ -0,0 +1,374 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "PrefsController.h"
|
||||
|
||||
@interface PrefsController (Private)
|
||||
|
||||
- (void) folderSheetShow: (id) sender;
|
||||
- (void) folderSheetClosed: (NSOpenPanel *) s returnCode: (int) code
|
||||
contextInfo: (void *) info;
|
||||
- (void) loadSettings;
|
||||
- (void) saveSettings;
|
||||
- (void) updatePopUp;
|
||||
|
||||
@end
|
||||
|
||||
@implementation PrefsController
|
||||
|
||||
/***********************************************************************
|
||||
* setHandle
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
- (void) setHandle: (tr_handle_t *) handle
|
||||
{
|
||||
NSUserDefaults * defaults;
|
||||
NSDictionary * appDefaults;
|
||||
|
||||
fHandle = handle;
|
||||
|
||||
/* Register defaults settings:
|
||||
- Simple bar
|
||||
- Always download to Desktop
|
||||
- Port 9090
|
||||
- 20 KB/s upload limit */
|
||||
NSString * desktopPath
|
||||
= [NSString stringWithFormat: @"%@/Desktop",
|
||||
NSHomeDirectory()];
|
||||
|
||||
defaults = [NSUserDefaults standardUserDefaults];
|
||||
appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"UseAdvancedBar",
|
||||
@"Constant", @"DownloadChoice",
|
||||
desktopPath, @"DownloadFolder",
|
||||
@"9090", @"BindPort",
|
||||
@"20", @"UploadLimit",
|
||||
NULL];
|
||||
[defaults registerDefaults: appDefaults];
|
||||
|
||||
/* Apply settings */
|
||||
tr_setBindPort( fHandle, [defaults integerForKey: @"BindPort"] );
|
||||
tr_setUploadLimit( fHandle, [defaults integerForKey: @"UploadLimit"] );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* show
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
- (void) show: (id) sender
|
||||
{
|
||||
NSRect mainFrame;
|
||||
NSRect prefsFrame;
|
||||
NSRect screenRect;
|
||||
NSPoint point;
|
||||
|
||||
[self loadSettings];
|
||||
|
||||
/* Place the window */
|
||||
mainFrame = [fWindow frame];
|
||||
prefsFrame = [fPrefsWindow frame];
|
||||
screenRect = [[NSScreen mainScreen] visibleFrame];
|
||||
point.x = mainFrame.origin.x + mainFrame.size.width / 2 -
|
||||
prefsFrame.size.width / 2;
|
||||
point.y = mainFrame.origin.y + mainFrame.size.height - 30;
|
||||
|
||||
/* Make sure it is in the screen */
|
||||
if( point.x < screenRect.origin.x )
|
||||
{
|
||||
point.x = screenRect.origin.x;
|
||||
}
|
||||
if( point.x + prefsFrame.size.width >
|
||||
screenRect.origin.x + screenRect.size.width )
|
||||
{
|
||||
point.x = screenRect.origin.x +
|
||||
screenRect.size.width - prefsFrame.size.width;
|
||||
}
|
||||
if( point.y - prefsFrame.size.height < screenRect.origin.y )
|
||||
{
|
||||
point.y = screenRect.origin.y + prefsFrame.size.height;
|
||||
}
|
||||
|
||||
[fPrefsWindow setFrameTopLeftPoint: point];
|
||||
[fPrefsWindow makeKeyAndOrderFront: NULL];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* ratio
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
- (void) ratio: (id) sender
|
||||
{
|
||||
[fFolderPopUp setEnabled: ![fFolderMatrix selectedRow]];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* check
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
- (void) check: (id) sender
|
||||
{
|
||||
if( [fUploadCheck state] == NSOnState )
|
||||
{
|
||||
[fUploadField setEnabled: YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fUploadField setEnabled: NO];
|
||||
[fUploadField setStringValue: @""];
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* cancel
|
||||
***********************************************************************
|
||||
* Discards changes and closes the Preferences window
|
||||
**********************************************************************/
|
||||
- (void) cancel: (id) sender
|
||||
{
|
||||
[fDownloadFolder release];
|
||||
[fPrefsWindow close];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* save
|
||||
***********************************************************************
|
||||
* Checks the user-defined options. If they are correct, saves settings
|
||||
* and closes the Preferences window. Otherwise corrects them and leaves
|
||||
* the window open
|
||||
**********************************************************************/
|
||||
- (void) save: (id) sender
|
||||
{
|
||||
int bindPort;
|
||||
int uploadLimit;
|
||||
|
||||
/* Bind port */
|
||||
bindPort = [fPortField intValue];
|
||||
bindPort = MAX( 1, bindPort );
|
||||
bindPort = MIN( bindPort, 65535 );
|
||||
|
||||
if( ![[fPortField stringValue] isEqualToString:
|
||||
[NSString stringWithFormat: @"%d", bindPort]] )
|
||||
{
|
||||
[fPortField setIntValue: bindPort];
|
||||
return;
|
||||
}
|
||||
|
||||
/* Upload limit */
|
||||
if( [fUploadCheck state] == NSOnState )
|
||||
{
|
||||
uploadLimit = [fUploadField intValue];
|
||||
uploadLimit = MAX( 0, uploadLimit );
|
||||
|
||||
if( ![[fUploadField stringValue] isEqualToString:
|
||||
[NSString stringWithFormat: @"%d", uploadLimit]] )
|
||||
{
|
||||
[fUploadField setIntValue: uploadLimit];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self saveSettings];
|
||||
[self cancel: NULL];
|
||||
}
|
||||
|
||||
@end /* @implementation PrefsController */
|
||||
|
||||
@implementation PrefsController (Private)
|
||||
|
||||
- (void) folderSheetShow: (id) sender
|
||||
{
|
||||
NSOpenPanel * panel;
|
||||
|
||||
panel = [NSOpenPanel openPanel];
|
||||
|
||||
[panel setPrompt: @"Select"];
|
||||
[panel setAllowsMultipleSelection: NO];
|
||||
[panel setCanChooseFiles: NO];
|
||||
[panel setCanChooseDirectories: YES];
|
||||
|
||||
[panel beginSheetForDirectory: NULL file: NULL types: NULL
|
||||
modalForWindow: fPrefsWindow modalDelegate: self didEndSelector:
|
||||
@selector( folderSheetClosed:returnCode:contextInfo: )
|
||||
contextInfo: NULL];
|
||||
}
|
||||
|
||||
- (void) folderSheetClosed: (NSOpenPanel *) s returnCode: (int) code
|
||||
contextInfo: (void *) info
|
||||
{
|
||||
[fFolderPopUp selectItemAtIndex: 0];
|
||||
|
||||
if( code != NSOKButton )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[fDownloadFolder release];
|
||||
fDownloadFolder = [[s filenames] objectAtIndex: 0];
|
||||
[fDownloadFolder retain];
|
||||
|
||||
[self updatePopUp];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* loadSettings
|
||||
***********************************************************************
|
||||
* Update the interface with the current settings
|
||||
**********************************************************************/
|
||||
- (void) loadSettings
|
||||
{
|
||||
NSUserDefaults * defaults;
|
||||
NSString * downloadChoice;
|
||||
int uploadLimit;
|
||||
|
||||
/* Fill with current settings */
|
||||
defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
/* Download folder selection */
|
||||
downloadChoice = [defaults stringForKey: @"DownloadChoice"];
|
||||
fDownloadFolder = [defaults stringForKey: @"DownloadFolder"];
|
||||
[fDownloadFolder retain];
|
||||
|
||||
if( [downloadChoice isEqualToString: @"Constant"] )
|
||||
{
|
||||
[fFolderMatrix selectCellAtRow: 0 column: 0];
|
||||
}
|
||||
else if( [downloadChoice isEqualToString: @"Torrent"] )
|
||||
{
|
||||
[fFolderMatrix selectCellAtRow: 1 column: 0];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fFolderMatrix selectCellAtRow: 2 column: 0];
|
||||
}
|
||||
[self ratio: NULL];
|
||||
[self updatePopUp];
|
||||
|
||||
[fPortField setIntValue: [defaults integerForKey: @"BindPort"]];
|
||||
|
||||
uploadLimit = [defaults integerForKey: @"UploadLimit"];
|
||||
if( uploadLimit < 0 )
|
||||
{
|
||||
[fUploadCheck setState: NSOffState];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fUploadCheck setState: NSOnState];
|
||||
[fUploadField setIntValue: uploadLimit];
|
||||
}
|
||||
[self check: NULL];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* saveSettings
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
- (void) saveSettings
|
||||
{
|
||||
NSUserDefaults * defaults;
|
||||
int bindPort;
|
||||
int uploadLimit;
|
||||
|
||||
defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
/* Download folder */
|
||||
switch( [fFolderMatrix selectedRow] )
|
||||
{
|
||||
case 0:
|
||||
[defaults setObject: @"Constant" forKey: @"DownloadChoice"];
|
||||
break;
|
||||
case 1:
|
||||
[defaults setObject: @"Torrent" forKey: @"DownloadChoice"];
|
||||
break;
|
||||
case 2:
|
||||
[defaults setObject: @"Ask" forKey: @"DownloadChoice"];
|
||||
break;
|
||||
}
|
||||
[defaults setObject: fDownloadFolder forKey: @"DownloadFolder"];
|
||||
|
||||
/* Bind port */
|
||||
bindPort = [fPortField intValue];
|
||||
tr_setBindPort( fHandle, bindPort );
|
||||
[defaults setObject: [NSString stringWithFormat: @"%d", bindPort]
|
||||
forKey: @"BindPort"];
|
||||
|
||||
/* Upload limit */
|
||||
if( [fUploadCheck state] == NSOnState )
|
||||
{
|
||||
uploadLimit = [fUploadField intValue];
|
||||
}
|
||||
else
|
||||
{
|
||||
uploadLimit = -1;
|
||||
}
|
||||
tr_setUploadLimit( fHandle, uploadLimit );
|
||||
[defaults setObject: [NSString stringWithFormat: @"%d", uploadLimit]
|
||||
forKey: @"UploadLimit"];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* updatePopUp
|
||||
***********************************************************************
|
||||
* Uses fDownloadFolder to update the displayed folder name and icon
|
||||
**********************************************************************/
|
||||
- (void) updatePopUp
|
||||
{
|
||||
NSMenuItem * menuItem;
|
||||
NSImage * image32, * image16;
|
||||
|
||||
/* Set up the pop up */
|
||||
[fFolderPopUp removeAllItems];
|
||||
[fFolderPopUp addItemWithTitle: @""];
|
||||
[[fFolderPopUp menu] addItem: [NSMenuItem separatorItem]];
|
||||
[fFolderPopUp addItemWithTitle: @"Other..."];
|
||||
|
||||
menuItem = (NSMenuItem *) [fFolderPopUp lastItem];
|
||||
[menuItem setTarget: self];
|
||||
[menuItem setAction: @selector( folderSheetShow: )];
|
||||
|
||||
/* Get the icon for the folder */
|
||||
image32 = [[NSWorkspace sharedWorkspace] iconForFile:
|
||||
fDownloadFolder];
|
||||
image16 = [[NSImage alloc] initWithSize: NSMakeSize(16,16)];
|
||||
|
||||
/* 32x32 -> 16x16 scaling */
|
||||
[image16 lockFocus];
|
||||
[[NSGraphicsContext currentContext]
|
||||
setImageInterpolation: NSImageInterpolationHigh];
|
||||
[image32 drawInRect: NSMakeRect(0,0,16,16)
|
||||
fromRect: NSMakeRect(0,0,32,32) operation: NSCompositeCopy
|
||||
fraction: 1.0];
|
||||
[image16 unlockFocus];
|
||||
|
||||
/* Update the menu item */
|
||||
menuItem = (NSMenuItem *) [fFolderPopUp itemAtIndex: 0];
|
||||
[menuItem setTitle: [fDownloadFolder lastPathComponent]];
|
||||
[menuItem setImage: image16];
|
||||
|
||||
[image16 release];
|
||||
}
|
||||
|
||||
@end /* @implementation PrefsController (Private) */
|
||||
43
macosx/ProgressCell.h
Normal file
43
macosx/ProgressCell.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <transmission.h>
|
||||
|
||||
@interface ProgressCell : NSCell
|
||||
{
|
||||
tr_stat_t * fStat;
|
||||
|
||||
NSString * fProgressString;
|
||||
NSString * fDlString;
|
||||
NSString * fUlString;
|
||||
|
||||
NSBitmapImageRep * fBgBmp;
|
||||
NSImage * fImg;
|
||||
NSBitmapImageRep * fBmp;
|
||||
}
|
||||
- (id) init;
|
||||
- (void) setStat: (tr_stat_t *) stat;
|
||||
- (void) buildSimpleBar;
|
||||
- (void) buildAdvancedBar;
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view;
|
||||
@end
|
||||
257
macosx/ProgressCell.m
Normal file
257
macosx/ProgressCell.m
Normal file
@@ -0,0 +1,257 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "ProgressCell.h"
|
||||
|
||||
#if 0
|
||||
/* Coefficients for the "3D effect" */
|
||||
static float kBarCoeffs[] =
|
||||
{ 0.49, 0.73, 0.84, 0.85, 0.84, 0.79, 0.78,
|
||||
0.82, 0.86, 0.91, 0.93, 0.95, 0.96, 0.71 };
|
||||
#endif
|
||||
|
||||
/* 255, 100, 80 */
|
||||
static uint32_t kRed[] =
|
||||
{ 0x7C3127FF, 0xBA493AFF, 0xD65443FF, 0xD85544FF, 0xD65443FF,
|
||||
0xC94F3FFF, 0xC64E3EFF, 0xD15241FF, 0xDB5644FF, 0xE85B48FF,
|
||||
0xED5D4AFF, 0xF25F4CFF, 0xF4604CFF, 0xB54738FF };
|
||||
|
||||
/* 160, 220, 255 */
|
||||
static uint32_t kBlue1[] =
|
||||
{ 0x4E6B7CFF, 0x74A0BAFF, 0x86B8D6FF, 0x88BBD8FF, 0x86B8D6FF,
|
||||
0x7EADC9FF, 0x7CABC6FF, 0x83B4D1FF, 0x89BDDBFF, 0x91C8E8FF,
|
||||
0x94CCEDFF, 0x98D1F2FF, 0x99D3F4FF, 0x719CB5FF };
|
||||
|
||||
/* 120, 190, 255 */
|
||||
static uint32_t kBlue2[] =
|
||||
{ 0x3A5D7CFF, 0x578ABAFF, 0x649FD6FF, 0x66A1D8FF, 0x649FD6FF,
|
||||
0x5E96C9FF, 0x5D94C6FF, 0x629BD1FF, 0x67A3DBFF, 0x6DACE8FF,
|
||||
0x6FB0EDFF, 0x72B4F2FF, 0x73B6F4FF, 0x5586B5FF };
|
||||
|
||||
/* 80, 160, 255 */
|
||||
static uint32_t kBlue3[] =
|
||||
{ 0x274E7CFF, 0x3A74BAFF, 0x4386D6FF, 0x4488D8FF, 0x4386D6FF,
|
||||
0x3F7EC9FF, 0x3E7CC6FF, 0x4183D1FF, 0x4489DBFF, 0x4891E8FF,
|
||||
0x4A94EDFF, 0x4C98F2FF, 0x4C99F4FF, 0x3871B5FF };
|
||||
|
||||
/* 30, 70, 180 */
|
||||
static uint32_t kBlue4[] =
|
||||
{ 0x0E2258FF, 0x153383FF, 0x193A97FF, 0x193B99FF, 0x193A97FF,
|
||||
0x17378EFF, 0x17368CFF, 0x183993FF, 0x193C9AFF, 0x1B3FA3FF,
|
||||
0x1B41A7FF, 0x1C42ABFF, 0x1C43ACFF, 0x15317FFF };
|
||||
|
||||
/* 130, 130, 130 */
|
||||
static uint32_t kGray[] =
|
||||
{ 0x3F3F3FFF, 0x5E5E5EFF, 0x6D6D6DFF, 0x6E6E6EFF, 0x6D6D6DFF,
|
||||
0x666666FF, 0x656565FF, 0x6A6A6AFF, 0x6F6F6FFF, 0x767676FF,
|
||||
0x787878FF, 0x7B7B7BFF, 0x7C7C7CFF, 0x5C5C5CFF };
|
||||
|
||||
/* 0, 255, 0 */
|
||||
static uint32_t kGreen[] =
|
||||
{ 0x007C00FF, 0x00BA00FF, 0x00D600FF, 0x00D800FF, 0x00D600FF,
|
||||
0x00C900FF, 0x00C600FF, 0x00D100FF, 0x00DB00FF, 0x00E800FF,
|
||||
0x00ED00FF, 0x00F200FF, 0x00F400FF, 0x00B500FF };
|
||||
|
||||
@implementation ProgressCell
|
||||
|
||||
- (id) init
|
||||
{
|
||||
NSImage * bgImg;
|
||||
NSSize size;
|
||||
|
||||
self = [super init];
|
||||
|
||||
/* Have a NSBitmapImageRep ready to draw the progression bar */
|
||||
bgImg = [NSImage imageNamed: @"Progress.tiff"];
|
||||
fBgBmp = [[bgImg representations] objectAtIndex: 0];
|
||||
size = [bgImg size];
|
||||
fImg = [[NSImage alloc] initWithSize: size];
|
||||
fBmp = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes: NULL pixelsWide: size.width
|
||||
pixelsHigh: size.height bitsPerSample: 8 samplesPerPixel: 4
|
||||
hasAlpha: YES isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bytesPerRow: 0 bitsPerPixel: 0];
|
||||
|
||||
[fImg addRepresentation: fBmp];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setStat: (tr_stat_t *) stat
|
||||
{
|
||||
int i;
|
||||
|
||||
fStat = stat;
|
||||
|
||||
fProgressString = [NSString stringWithFormat:
|
||||
@"%.2f %%", 100.0 * fStat->progress];
|
||||
fDlString = [NSString stringWithFormat:
|
||||
@"DL: %.2f KB/s", fStat->rateDownload];
|
||||
fUlString = [NSString stringWithFormat:
|
||||
@"UL: %.2f KB/s", fStat->rateUpload];
|
||||
|
||||
for( i = 0; i < [fImg size].height; i++ )
|
||||
{
|
||||
memcpy( [fBmp bitmapData] + i * [fBmp bytesPerRow],
|
||||
[fBgBmp bitmapData] + i * [fBgBmp bytesPerRow],
|
||||
[fImg size].width * 4 );
|
||||
}
|
||||
|
||||
if( [[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:@"UseAdvancedBar"])
|
||||
{
|
||||
[self buildAdvancedBar];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self buildSimpleBar];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) buildSimpleBar
|
||||
{
|
||||
int h, w;
|
||||
uint32_t * p;
|
||||
|
||||
for( h = 0; h < 14; h++ )
|
||||
{
|
||||
p = (uint32_t *) ( [fBmp bitmapData] +
|
||||
h * [fBmp bytesPerRow] ) + 2;
|
||||
|
||||
for( w = 0; w < 120; w++ )
|
||||
{
|
||||
*p = kBlue2[h];
|
||||
|
||||
if( w >= (int) ( fStat->progress * 120 ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) buildAdvancedBar
|
||||
{
|
||||
int h, w;
|
||||
uint32_t * p;
|
||||
|
||||
if( fStat->status & TR_STATUS_SEED )
|
||||
{
|
||||
for( h = 0; h < 2; h++ )
|
||||
{
|
||||
p = (uint32_t *) ( [fBmp bitmapData] +
|
||||
h * [fBmp bytesPerRow] ) + 2;
|
||||
}
|
||||
}
|
||||
|
||||
for( h = 0; h < 14; h++ )
|
||||
{
|
||||
p = (uint32_t *) ( [fBmp bitmapData] +
|
||||
h * [fBmp bytesPerRow] ) + 2;
|
||||
|
||||
for( w = 0; w < 120; w++ )
|
||||
{
|
||||
if( fStat->status & TR_STATUS_SEED )
|
||||
{
|
||||
*p = kGreen[h];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Download is not finished yet */
|
||||
if( h < 2 )
|
||||
{
|
||||
/* First two lines: dark blue to show progression */
|
||||
*p = kBlue4[h];
|
||||
|
||||
if( w >= (int) ( fStat->progress * 120 ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Lines 2 to X: blue or grey depending on whether
|
||||
we have the piece or not */
|
||||
if( fStat->pieces[w] < 0 )
|
||||
{
|
||||
*p = kGray[h];
|
||||
}
|
||||
else if( fStat->pieces[w] < 1 )
|
||||
{
|
||||
*p = kRed[h];
|
||||
}
|
||||
else if( fStat->pieces[w] < 2 )
|
||||
{
|
||||
*p = kBlue1[h];
|
||||
}
|
||||
else if( fStat->pieces[w] < 3 )
|
||||
{
|
||||
*p = kBlue2[h];
|
||||
}
|
||||
else
|
||||
{
|
||||
*p = kBlue3[h];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
|
||||
{
|
||||
if( ![view lockFocusIfCanDraw] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableDictionary * attributes;
|
||||
NSPoint pen = cellFrame.origin;
|
||||
|
||||
attributes = [NSMutableDictionary dictionaryWithCapacity: 1];
|
||||
[attributes setObject: [NSFont messageFontOfSize:12.0]
|
||||
forKey: NSFontAttributeName];
|
||||
|
||||
pen.x += 5; pen.y += 5;
|
||||
|
||||
pen.y += [fImg size].height;
|
||||
[fImg compositeToPoint: pen operation: NSCompositeSourceOver];
|
||||
pen.y -= [fImg size].height;
|
||||
|
||||
[attributes setObject: [NSFont messageFontOfSize:10.0]
|
||||
forKey: NSFontAttributeName];
|
||||
|
||||
pen.x += 5; pen.y += 20;
|
||||
[fDlString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
pen.x += 0; pen.y += 15;
|
||||
[fUlString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
[view unlockFocus];
|
||||
}
|
||||
|
||||
@end
|
||||
386
macosx/Transmission.xcodeproj/project.pbxproj
Normal file
386
macosx/Transmission.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,386 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 42;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */; };
|
||||
4D096C12089FB4E20091B166 /* NameCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D096C0F089FB4E20091B166 /* NameCell.m */; };
|
||||
4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D096C11089FB4E20091B166 /* ProgressCell.m */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
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 = (); }; };
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXBuildStyle section */
|
||||
4A9504CCFFE6A4B311CA0CBA /* Debug */ = {
|
||||
isa = PBXBuildStyle;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
PREBINDING = NO;
|
||||
ZERO_LINK = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4A9504CDFFE6A4B311CA0CBA /* Release */ = {
|
||||
isa = PBXBuildStyle;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
PREBINDING = NO;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End PBXBuildStyle section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
|
||||
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = "<group>"; };
|
||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
32CA4F630368D1EE00C91783 /* Transmission_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Transmission_Prefix.pch; sourceTree = "<group>"; };
|
||||
4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = TransmissionDocument.icns; path = Images/TransmissionDocument.icns; sourceTree = "<group>"; };
|
||||
4D096C0E089FB4E20091B166 /* NameCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NameCell.h; sourceTree = "<group>"; };
|
||||
4D096C0F089FB4E20091B166 /* NameCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NameCell.m; sourceTree = "<group>"; };
|
||||
4D096C10089FB4E20091B166 /* ProgressCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ProgressCell.h; sourceTree = "<group>"; };
|
||||
4D096C11089FB4E20091B166 /* ProgressCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ProgressCell.m; sourceTree = "<group>"; };
|
||||
4D118E1808CB46B20033958F /* PrefsController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PrefsController.h; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8D11072E0486CEB800E47090 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
|
||||
4DF0C5AE08991C1600DD8943 /* libtransmission.a in Frameworks */,
|
||||
4D3EA0AA08AE13C600EA10C2 /* IOKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4D096C0E089FB4E20091B166 /* NameCell.h */,
|
||||
4D096C0F089FB4E20091B166 /* NameCell.m */,
|
||||
4D096C10089FB4E20091B166 /* ProgressCell.h */,
|
||||
4D096C11089FB4E20091B166 /* ProgressCell.m */,
|
||||
4DF0C5A90899190500DD8943 /* Controller.m */,
|
||||
4DF0C5AA0899190500DD8943 /* Controller.h */,
|
||||
4D118E1808CB46B20033958F /* PrefsController.h */,
|
||||
4D118E1908CB46B20033958F /* PrefsController.m */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
|
||||
);
|
||||
name = "Linked Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4D3EA0A908AE13C600EA10C2 /* IOKit.framework */,
|
||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
|
||||
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
19C28FACFE9D520D11CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8D1107320486CEB800E47090 /* Transmission.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97314FDCFA39411CA2CEA /* Transmission */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4DF0C5AD08991C1600DD8943 /* libtransmission.a */,
|
||||
080E96DDFE201D6D7F000001 /* Classes */,
|
||||
29B97315FDCFA39411CA2CEA /* Other Sources */,
|
||||
29B97317FDCFA39411CA2CEA /* Resources */,
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */,
|
||||
19C28FACFE9D520D11CA2CBB /* Products */,
|
||||
);
|
||||
name = Transmission;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32CA4F630368D1EE00C91783 /* Transmission_Prefix.pch */,
|
||||
29B97316FDCFA39411CA2CEA /* main.m */,
|
||||
);
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
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 */,
|
||||
8D1107310486CEB800E47090 /* Info.plist */,
|
||||
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
|
||||
29B97318FDCFA39411CA2CEA /* MainMenu.nib */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
|
||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8D1107260486CEB800E47090 /* Transmission */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 4DF0C596089918A300DD8943 /* Build configuration list for PBXNativeTarget "Transmission" */;
|
||||
buildPhases = (
|
||||
8D1107290486CEB800E47090 /* Resources */,
|
||||
8D11072C0486CEB800E47090 /* Sources */,
|
||||
8D11072E0486CEB800E47090 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
buildSettings = {
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Transmission_Prefix.pch;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = Transmission;
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
dependencies = (
|
||||
);
|
||||
name = Transmission;
|
||||
productInstallPath = "$(HOME)/Applications";
|
||||
productName = Transmission;
|
||||
productReference = 8D1107320486CEB800E47090 /* Transmission.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 4DF0C59A089918A300DD8943 /* Build configuration list for PBXProject "Transmission" */;
|
||||
buildSettings = {
|
||||
};
|
||||
buildStyles = (
|
||||
4A9504CCFFE6A4B311CA0CBA /* Debug */,
|
||||
4A9504CDFFE6A4B311CA0CBA /* Release */,
|
||||
);
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA /* Transmission */;
|
||||
projectDirPath = "";
|
||||
targets = (
|
||||
8D1107260486CEB800E47090 /* Transmission */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8D1107290486CEB800E47090 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
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 */,
|
||||
4D2784370905709500687951 /* Transmission.icns in Resources */,
|
||||
4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */,
|
||||
4D6DAAC6090CE00500F43C22 /* RevealOff.tiff in Resources */,
|
||||
4D6DAAC7090CE00500F43C22 /* RevealOn.tiff in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8D11072C0486CEB800E47090 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */,
|
||||
4DF0C5AB0899190500DD8943 /* Controller.m in Sources */,
|
||||
4D096C12089FB4E20091B166 /* NameCell.m in Sources */,
|
||||
4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */,
|
||||
4D118E1A08CB46B20033958F /* PrefsController.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
089C165DFE840E0CC02AAC07 /* English */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
29B97319FDCFA39411CA2CEA /* English */,
|
||||
);
|
||||
name = MainMenu.nib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
4DF0C599089918A300DD8943 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 3;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Transmission_Prefix.pch;
|
||||
GCC_TREAT_NONCONFORMANT_CODE_ERRORS_AS_WARNINGS = NO;
|
||||
GCC_UNROLL_LOOPS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
|
||||
GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_MISSING_PARENTHESES = YES;
|
||||
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = NO;
|
||||
GCC_WARN_UNUSED_VALUE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../libtransmission\"";
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../libtransmission\"";
|
||||
OTHER_LDFLAGS = "-lcrypto";
|
||||
PRODUCT_NAME = Transmission;
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4DF0C59D089918A300DD8943 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
COPY_PHASE_STRIP = NO;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.3;
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
4DF0C596089918A300DD8943 /* Build configuration list for PBXNativeTarget "Transmission" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4DF0C599089918A300DD8943 /* Debug */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
4DF0C59A089918A300DD8943 /* Build configuration list for PBXProject "Transmission" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4DF0C59D089918A300DD8943 /* Debug */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
|
||||
}
|
||||
3
macosx/Transmission_Prefix.pch
Normal file
3
macosx/Transmission_Prefix.pch
Normal file
@@ -0,0 +1,3 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
74
macosx/Utils.h
Normal file
74
macosx/Utils.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
static NSString * stringForFileSize( uint64_t size )
|
||||
{
|
||||
if( size < 1024 )
|
||||
{
|
||||
return [NSString stringWithFormat: @"%lld bytes", size];
|
||||
}
|
||||
if( size < 1048576 )
|
||||
{
|
||||
return [NSString stringWithFormat: @"%lld.%lld KB",
|
||||
size / 1024, ( size % 1024 ) / 103];
|
||||
}
|
||||
if( size < 1073741824 )
|
||||
{
|
||||
return [NSString stringWithFormat: @"%lld.%lld MB",
|
||||
size / 1048576, ( size % 1048576 ) / 104858];
|
||||
}
|
||||
return [NSString stringWithFormat: @"%lld.%lld GB",
|
||||
size / 1073741824, ( size % 1073741824 ) / 107374183];
|
||||
}
|
||||
|
||||
static float widthForString( NSString * string, float fontSize )
|
||||
{
|
||||
NSMutableDictionary * attributes =
|
||||
[NSMutableDictionary dictionaryWithCapacity: 1];
|
||||
[attributes setObject: [NSFont messageFontOfSize: fontSize]
|
||||
forKey: NSFontAttributeName];
|
||||
|
||||
return [string sizeWithAttributes: attributes].width;
|
||||
}
|
||||
|
||||
static NSString * stringFittingInWidth( char * string, float width,
|
||||
float fontSize )
|
||||
{
|
||||
NSString * nsString = NULL;
|
||||
char * foo = strdup( string );
|
||||
int i;
|
||||
|
||||
for( i = strlen( string ); i > 0; i-- )
|
||||
{
|
||||
foo[i] = '\0';
|
||||
nsString = [NSString stringWithFormat: @"%s%@",
|
||||
foo, ( i - strlen( string ) ? [NSString
|
||||
stringWithUTF8String:"\xE2\x80\xA6"] : @"" )];
|
||||
|
||||
if( widthForString( nsString, fontSize ) <= width )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
free( foo );
|
||||
return nsString;
|
||||
}
|
||||
36
macosx/main.m
Normal file
36
macosx/main.m
Normal file
@@ -0,0 +1,36 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
if( argc > 1 && !strncmp( argv[1], "-v", 2 ) )
|
||||
{
|
||||
char * env;
|
||||
int debug = atoi( &argv[1][2] );
|
||||
asprintf( &env, "TR_DEBUG=%d", debug );
|
||||
putenv( env );
|
||||
free( env );
|
||||
}
|
||||
return NSApplicationMain( argc, (const char **) argv );
|
||||
}
|
||||
297
transmissioncli.c
Normal file
297
transmissioncli.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <transmission.h>
|
||||
|
||||
#define USAGE \
|
||||
"Usage: %s [options] file.torrent [options]\n\n" \
|
||||
"Options:\n" \
|
||||
" -h, --help Print this help and exit\n" \
|
||||
" -i, --info Print metainfo and exit\n" \
|
||||
" -s, --scrape Print counts of seeders/leechers and exit\n" \
|
||||
" -v, --verbose <int> Verbose level (0 to 2, default = 0)\n" \
|
||||
" -p, --port <int> Port we should listen on (default = 9090)\n" \
|
||||
" -u, --upload <int> Maximum upload rate (-1 = no limit, default = 20)\n"
|
||||
|
||||
static int showHelp = 0;
|
||||
static int showInfo = 0;
|
||||
static int showScrape = 0;
|
||||
static int verboseLevel = 0;
|
||||
static int bindPort = 9090;
|
||||
static int uploadLimit = 20;
|
||||
static char * torrentPath = NULL;
|
||||
static volatile char mustDie = 0;
|
||||
|
||||
static int parseCommandLine ( int argc, char ** argv );
|
||||
static void sigHandler ( int signal );
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
int i, count;
|
||||
tr_handle_t * h;
|
||||
tr_stat_t * s;
|
||||
|
||||
printf( "Transmission %s - http://transmission.m0k.org/\n\n",
|
||||
VERSION_STRING );
|
||||
|
||||
/* Get options */
|
||||
if( parseCommandLine( argc, argv ) )
|
||||
{
|
||||
printf( USAGE, argv[0] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( showHelp )
|
||||
{
|
||||
printf( USAGE, argv[0] );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( verboseLevel < 0 )
|
||||
{
|
||||
verboseLevel = 0;
|
||||
}
|
||||
else if( verboseLevel > 9 )
|
||||
{
|
||||
verboseLevel = 9;
|
||||
}
|
||||
if( verboseLevel )
|
||||
{
|
||||
static char env[11];
|
||||
sprintf( env, "TR_DEBUG=%d", verboseLevel );
|
||||
putenv( env );
|
||||
}
|
||||
|
||||
if( bindPort < 1 || bindPort > 65535 )
|
||||
{
|
||||
printf( "Invalid port '%d'\n", bindPort );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initialize libtransmission */
|
||||
h = tr_init();
|
||||
|
||||
/* Open and parse torrent file */
|
||||
if( tr_torrentInit( h, torrentPath ) )
|
||||
{
|
||||
printf( "Failed opening torrent file `%s'\n", torrentPath );
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if( showInfo )
|
||||
{
|
||||
tr_info_t * info;
|
||||
|
||||
count = tr_torrentStat( h, &s );
|
||||
info = &s[0].info;
|
||||
|
||||
/* Print torrent info (quite à la btshowmetainfo) */
|
||||
printf( "hash: " );
|
||||
for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
|
||||
{
|
||||
printf( "%02x", info->hash[i] );
|
||||
}
|
||||
printf( "\n" );
|
||||
printf( "tracker: %s:%d\n",
|
||||
info->trackerAddress, info->trackerPort );
|
||||
printf( "announce: %s\n", info->trackerAnnounce );
|
||||
printf( "size: %lld (%lld * %d + %lld)\n",
|
||||
info->totalSize, info->totalSize / info->pieceSize,
|
||||
info->pieceSize, info->totalSize % info->pieceSize );
|
||||
printf( "file(s):\n" );
|
||||
for( i = 0; i < info->fileCount; i++ )
|
||||
{
|
||||
printf( " %s (%lld)\n", info->files[i].name,
|
||||
info->files[i].length );
|
||||
}
|
||||
|
||||
free( s );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if( showScrape )
|
||||
{
|
||||
int seeders, leechers;
|
||||
|
||||
if( tr_torrentScrape( h, 0, &seeders, &leechers ) )
|
||||
{
|
||||
printf( "Scrape failed.\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "%d seeder(s), %d leecher(s).\n", seeders, leechers );
|
||||
}
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
signal( SIGINT, sigHandler );
|
||||
|
||||
tr_setBindPort( h, bindPort );
|
||||
tr_setUploadLimit( h, uploadLimit );
|
||||
|
||||
tr_torrentSetFolder( h, 0, "." );
|
||||
tr_torrentStart( h, 0 );
|
||||
|
||||
while( !mustDie )
|
||||
{
|
||||
char string[80];
|
||||
int chars = 0;
|
||||
|
||||
sleep( 1 );
|
||||
|
||||
count = tr_torrentStat( h, &s );
|
||||
|
||||
if( s[0].status & TR_STATUS_CHECK )
|
||||
{
|
||||
chars = snprintf( string, 80,
|
||||
"Checking files... %.2f %%", 100.0 * s[0].progress );
|
||||
}
|
||||
else if( s[0].status & TR_STATUS_DOWNLOAD )
|
||||
{
|
||||
chars = snprintf( string, 80,
|
||||
"Progress: %.2f %%, %d peer%s, dl from %d (%.2f kbps), "
|
||||
"ul to %d (%.2f kbps)", 100.0 * s[0].progress,
|
||||
s[0].peersTotal, ( s[0].peersTotal == 1 ) ? "" : "s",
|
||||
s[0].peersUploading, s[0].rateDownload,
|
||||
s[0].peersDownloading, s[0].rateUpload );
|
||||
}
|
||||
else if( s[0].status & TR_STATUS_SEED )
|
||||
{
|
||||
chars = snprintf( string, 80,
|
||||
"Seeding, uploading to %d of %d peer(s), %.2f kbps",
|
||||
s[0].peersDownloading, s[0].peersTotal,
|
||||
s[0].rateUpload );
|
||||
}
|
||||
memset( &string[chars], ' ', 79 - chars );
|
||||
string[79] = '\0';
|
||||
fprintf( stderr, "\r%s", string );
|
||||
|
||||
if( s[0].status & TR_TRACKER_ERROR )
|
||||
{
|
||||
fprintf( stderr, "\n%s\n", s[0].error );
|
||||
}
|
||||
else if( verboseLevel > 0 )
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
}
|
||||
|
||||
free( s );
|
||||
}
|
||||
fprintf( stderr, "\n" );
|
||||
|
||||
/* Try for 5 seconds to notice the tracker that we are leaving */
|
||||
tr_torrentStop( h, 0 );
|
||||
for( i = 0; i < 10; i++ )
|
||||
{
|
||||
count = tr_torrentStat( h, &s );
|
||||
if( s[0].status & TR_STATUS_PAUSE )
|
||||
{
|
||||
/* The 'stopped' message was sent */
|
||||
free( s );
|
||||
break;
|
||||
}
|
||||
free( s );
|
||||
usleep( 500000 );
|
||||
}
|
||||
|
||||
cleanup:
|
||||
tr_torrentClose( h, 0 );
|
||||
|
||||
failed:
|
||||
tr_close( h );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parseCommandLine( int argc, char ** argv )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
static struct option long_options[] =
|
||||
{ { "help", no_argument, NULL, 'h' },
|
||||
{ "info", no_argument, NULL, 'i' },
|
||||
{ "scrape", no_argument, NULL, 's' },
|
||||
{ "verbose", required_argument, NULL, 'v' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "upload", required_argument, NULL, 'u' },
|
||||
{ 0, 0, 0, 0 } };
|
||||
|
||||
int c, optind = 0;
|
||||
c = getopt_long( argc, argv, "hisv:p:u:", long_options, &optind );
|
||||
if( c < 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
switch( c )
|
||||
{
|
||||
case 'h':
|
||||
showHelp = 1;
|
||||
break;
|
||||
case 'i':
|
||||
showInfo = 1;
|
||||
break;
|
||||
case 's':
|
||||
showScrape = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verboseLevel = atoi( optarg );
|
||||
break;
|
||||
case 'p':
|
||||
bindPort = atoi( optarg );
|
||||
break;
|
||||
case 'u':
|
||||
uploadLimit = atoi( optarg );
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( optind > argc - 1 )
|
||||
{
|
||||
return !showHelp;
|
||||
}
|
||||
|
||||
torrentPath = argv[optind];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sigHandler( int signal )
|
||||
{
|
||||
switch( signal )
|
||||
{
|
||||
case SIGINT:
|
||||
mustDie = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user