Merge nat-traversal branch to trunk.
This commit is contained in:
parent
bec163be16
commit
0257761670
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// PiecesWindowController.h
|
||||
// Transmission
|
||||
//
|
||||
// Created by Livingston on 9/23/06.
|
||||
// Copyright 2006 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Torrent.h"
|
||||
|
||||
@interface PiecesWindowController : NSWindowController
|
||||
{
|
||||
int8_t * fPieces;
|
||||
|
||||
NSImage * fExistingImage, * fBack, * fWhitePiece, * fGreenPiece,
|
||||
* fBlue1Piece, * fBlue2Piece, * fBlue3Piece;
|
||||
|
||||
Torrent * fTorrent;
|
||||
int fNumPieces, fAcross, fWidth, fExtraBorder;
|
||||
|
||||
IBOutlet NSImageView * fImageView;
|
||||
}
|
||||
|
||||
- (void) setTorrent: (Torrent *) torrent;
|
||||
- (void) updateView: (BOOL) first;
|
||||
|
||||
@end
|
|
@ -0,0 +1,214 @@
|
|||
//
|
||||
// PiecesWindowController.m
|
||||
// Transmission
|
||||
//
|
||||
// Created by Livingston on 9/23/06.
|
||||
// Copyright 2006 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PiecesWindowController.h"
|
||||
|
||||
#define MAX_ACROSS 20
|
||||
#define BETWEEN 1.0
|
||||
|
||||
#define BLANK -99
|
||||
|
||||
@implementation PiecesWindowController
|
||||
|
||||
- (id) initWithWindowNibName: (NSString *) name
|
||||
{
|
||||
if ((self = [super initWithWindowNibName: name]))
|
||||
{
|
||||
fTorrent = nil;
|
||||
int numPieces = MAX_ACROSS * MAX_ACROSS;
|
||||
fPieces = malloc(numPieces);
|
||||
int i;
|
||||
for (i = 0; i < numPieces; i++)
|
||||
fPieces[i] = BLANK;
|
||||
|
||||
fBack = [NSImage imageNamed: @"PiecesBack.tiff"];
|
||||
NSSize size = [fBack size];
|
||||
|
||||
fWhitePiece = [NSImage imageNamed: @"BoxWhite.tiff"];
|
||||
[fWhitePiece setScalesWhenResized: YES];
|
||||
[fWhitePiece setSize: size];
|
||||
|
||||
fGreenPiece = [NSImage imageNamed: @"BoxGreen.tiff"];
|
||||
[fGreenPiece setScalesWhenResized: YES];
|
||||
[fGreenPiece setSize: size];
|
||||
|
||||
fBlue1Piece = [NSImage imageNamed: @"BoxBlue1.tiff"];
|
||||
[fBlue1Piece setScalesWhenResized: YES];
|
||||
[fBlue1Piece setSize: size];
|
||||
|
||||
fBlue2Piece = [NSImage imageNamed: @"BoxBlue2.tiff"];
|
||||
[fBlue2Piece setScalesWhenResized: YES];
|
||||
[fBlue2Piece setSize: size];
|
||||
|
||||
fBlue3Piece = [NSImage imageNamed: @"BoxBlue3.tiff"];
|
||||
[fBlue3Piece setScalesWhenResized: YES];
|
||||
[fBlue3Piece setSize: size];
|
||||
|
||||
fExistingImage = [fBack copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) awakeFromNib
|
||||
{
|
||||
//window location and size
|
||||
NSPanel * window = (NSPanel *)[self window];
|
||||
|
||||
[window setBecomesKeyOnlyIfNeeded: YES];
|
||||
|
||||
[window setFrameAutosaveName: @"PiecesWindowFrame"];
|
||||
[window setFrameUsingName: @"PiecesWindowFrame"];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
free(fPieces);
|
||||
|
||||
if (fTorrent)
|
||||
[fTorrent release];
|
||||
[fExistingImage release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setTorrent: (Torrent *) torrent
|
||||
{
|
||||
if (fTorrent)
|
||||
{
|
||||
[fTorrent release];
|
||||
|
||||
if (!torrent)
|
||||
{
|
||||
fTorrent = nil;
|
||||
[fImageView setImage: fBack];
|
||||
}
|
||||
}
|
||||
|
||||
if (torrent)
|
||||
{
|
||||
fTorrent = [torrent retain];
|
||||
|
||||
//determine relevant values
|
||||
fNumPieces = MAX_ACROSS * MAX_ACROSS;
|
||||
if ([fTorrent pieceCount] < fNumPieces)
|
||||
{
|
||||
fNumPieces = [fTorrent pieceCount];
|
||||
|
||||
fAcross = sqrt(fNumPieces);
|
||||
if (fAcross * fAcross < fNumPieces)
|
||||
fAcross++;
|
||||
}
|
||||
else
|
||||
fAcross = MAX_ACROSS;
|
||||
|
||||
fWidth = ([fExistingImage size].width - (fAcross + 1) * BETWEEN) / fAcross;
|
||||
fExtraBorder = ([fExistingImage size].width - ((fWidth + BETWEEN) * fAcross + BETWEEN)) / 2;
|
||||
|
||||
[self updateView: YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateView: (BOOL) first
|
||||
{
|
||||
if (!fTorrent)
|
||||
return;
|
||||
|
||||
if (first)
|
||||
{
|
||||
[fExistingImage release];
|
||||
fExistingImage = [fBack copy];
|
||||
}
|
||||
|
||||
int8_t * pieces = malloc(fNumPieces);
|
||||
[fTorrent getAvailability: pieces size: fNumPieces];
|
||||
|
||||
int i, j, piece, index = -1;
|
||||
NSPoint point;
|
||||
NSRect rect = NSMakeRect(0, 0, fWidth, fWidth);
|
||||
NSImage * pieceImage;
|
||||
BOOL change = NO;
|
||||
|
||||
for (i = 0; i < fAcross; i++)
|
||||
for (j = 0; j < fAcross; j++)
|
||||
{
|
||||
index++;
|
||||
if (index >= fNumPieces)
|
||||
break;
|
||||
|
||||
pieceImage = nil;
|
||||
|
||||
piece = pieces[index];
|
||||
if (piece < 0)
|
||||
{
|
||||
if (first || fPieces[index] != -1)
|
||||
{
|
||||
fPieces[index] = -1;
|
||||
pieceImage = fGreenPiece;
|
||||
}
|
||||
}
|
||||
else if (piece == 0)
|
||||
{
|
||||
if (first || fPieces[index] != 0)
|
||||
{
|
||||
fPieces[index] = 0;
|
||||
pieceImage = fWhitePiece;
|
||||
}
|
||||
}
|
||||
else if (piece == 1)
|
||||
{
|
||||
if (first || fPieces[index] != 1)
|
||||
{
|
||||
fPieces[index] = 1;
|
||||
pieceImage = fBlue1Piece;
|
||||
}
|
||||
}
|
||||
else if (piece == 2)
|
||||
{
|
||||
if (first || fPieces[index] != 2)
|
||||
{
|
||||
fPieces[index] = 2;
|
||||
pieceImage = fBlue2Piece;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (first || fPieces[index] != 3)
|
||||
{
|
||||
fPieces[index] = 3;
|
||||
pieceImage = fBlue3Piece;
|
||||
}
|
||||
}
|
||||
|
||||
if (pieceImage)
|
||||
{
|
||||
//drawing actually will occur, so figure out values
|
||||
if (!change)
|
||||
{
|
||||
[fExistingImage lockFocus];
|
||||
change = YES;
|
||||
}
|
||||
|
||||
point = NSMakePoint(j * (fWidth + BETWEEN) + BETWEEN + fExtraBorder,
|
||||
[fExistingImage size].width - (i + 1) * (fWidth + BETWEEN) - fExtraBorder);
|
||||
|
||||
[pieceImage compositeToPoint: point fromRect: rect operation: NSCompositeSourceOver];
|
||||
}
|
||||
}
|
||||
|
||||
if (change)
|
||||
{
|
||||
[fExistingImage unlockFocus];
|
||||
|
||||
[fImageView setImage: nil];
|
||||
[fImageView setImage: fExistingImage];
|
||||
}
|
||||
|
||||
free(pieces);
|
||||
}
|
||||
|
||||
@end
|
|
@ -7,6 +7,28 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
3518E4A50AC620FC002ED3A2 /* PiecesWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3518E4A30AC620FC002ED3A2 /* PiecesWindowController.h */; };
|
||||
3518E4A60AC620FC002ED3A2 /* PiecesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3518E4A40AC620FC002ED3A2 /* PiecesWindowController.m */; };
|
||||
3518E4D10AC62517002ED3A2 /* PiecesBack.tiff in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3518E4CD0AC62517002ED3A2 /* PiecesBack.tiff */; };
|
||||
3518E4D30AC62517002ED3A2 /* BoxBlue1.tiff in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3518E4CF0AC62517002ED3A2 /* BoxBlue1.tiff */; };
|
||||
3518E4D40AC62517002ED3A2 /* BoxBlue2.tiff in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3518E4D00AC62517002ED3A2 /* BoxBlue2.tiff */; };
|
||||
3518E4D70AC6253F002ED3A2 /* PiecesWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3518E4D50AC6253F002ED3A2 /* PiecesWindow.nib */; };
|
||||
3518E4FB0AC62832002ED3A2 /* PiecesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3518E4A40AC620FC002ED3A2 /* PiecesWindowController.m */; };
|
||||
3518E5210AC62A29002ED3A2 /* PiecesBack.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 3518E4CD0AC62517002ED3A2 /* PiecesBack.tiff */; };
|
||||
3518E5230AC62A2A002ED3A2 /* BoxBlue2.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 3518E4D00AC62517002ED3A2 /* BoxBlue2.tiff */; };
|
||||
3518E5240AC62A2B002ED3A2 /* BoxBlue1.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 3518E4CF0AC62517002ED3A2 /* BoxBlue1.tiff */; };
|
||||
3518E5290AC62A55002ED3A2 /* BoxBlue3.tiff in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3518E5270AC62A55002ED3A2 /* BoxBlue3.tiff */; };
|
||||
3518E52A0AC62A55002ED3A2 /* BoxWhite.tiff in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3518E5280AC62A55002ED3A2 /* BoxWhite.tiff */; };
|
||||
3518E52B0AC62A57002ED3A2 /* BoxWhite.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 3518E5280AC62A55002ED3A2 /* BoxWhite.tiff */; };
|
||||
3518E52C0AC62A57002ED3A2 /* BoxBlue3.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 3518E5270AC62A55002ED3A2 /* BoxBlue3.tiff */; };
|
||||
3518E5770AC63262002ED3A2 /* BoxGreen.tiff in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3518E5760AC63262002ED3A2 /* BoxGreen.tiff */; };
|
||||
3518E57B0AC632EA002ED3A2 /* BoxGreen.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 3518E5760AC63262002ED3A2 /* BoxGreen.tiff */; };
|
||||
35B037A70AC59BC600A10FDF /* Check.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 35B037A60AC59BC600A10FDF /* Check.png */; };
|
||||
35B037B60AC59C4000A10FDF /* Check.png in Resources */ = {isa = PBXBuildFile; fileRef = 35B037A60AC59BC600A10FDF /* Check.png */; };
|
||||
35B037FB0AC5B53800A10FDF /* ResumeNoWaitOn.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 35B037F90AC5B53800A10FDF /* ResumeNoWaitOn.png */; };
|
||||
35B037FC0AC5B53800A10FDF /* ResumeNoWaitOff.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 35B037FA0AC5B53800A10FDF /* ResumeNoWaitOff.png */; };
|
||||
35B038130AC5B6EB00A10FDF /* ResumeNoWaitOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 35B037F90AC5B53800A10FDF /* ResumeNoWaitOn.png */; };
|
||||
35B038140AC5B6EC00A10FDF /* ResumeNoWaitOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 35B037FA0AC5B53800A10FDF /* ResumeNoWaitOff.png */; };
|
||||
4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */; };
|
||||
4D118E1A08CB46B20033958F /* PrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D118E1908CB46B20033958F /* PrefsController.m */; };
|
||||
4D1838BC09DEC0430047D688 /* internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D18389D09DEC0430047D688 /* internal.h */; };
|
||||
|
@ -56,6 +78,14 @@
|
|||
4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB90911233800450CB1 /* PauseOff.png */; };
|
||||
4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */; };
|
||||
4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */; };
|
||||
4DAB87C50ABE1F730081CF7E /* xml.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DAB87BD0ABE1F730081CF7E /* xml.h */; };
|
||||
4DAB87C60ABE1F730081CF7E /* xml.c in Sources */ = {isa = PBXBuildFile; fileRef = 4DAB87BE0ABE1F730081CF7E /* xml.c */; };
|
||||
4DAB87C70ABE1F730081CF7E /* upnp.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DAB87BF0ABE1F730081CF7E /* upnp.h */; };
|
||||
4DAB87C80ABE1F730081CF7E /* upnp.c in Sources */ = {isa = PBXBuildFile; fileRef = 4DAB87C00ABE1F730081CF7E /* upnp.c */; };
|
||||
4DAB87C90ABE1F730081CF7E /* natpmp.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DAB87C10ABE1F730081CF7E /* natpmp.h */; };
|
||||
4DAB87CA0ABE1F730081CF7E /* natpmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 4DAB87C20ABE1F730081CF7E /* natpmp.c */; };
|
||||
4DAB87CB0ABE1F730081CF7E /* http.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DAB87C30ABE1F730081CF7E /* http.h */; };
|
||||
4DAB87CC0ABE1F730081CF7E /* http.c in Sources */ = {isa = PBXBuildFile; fileRef = 4DAB87C40ABE1F730081CF7E /* http.c */; };
|
||||
4DCCBB3E09C3D71100D3CABF /* TorrentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */; };
|
||||
4DDBB71C09E16BF100284745 /* transmissioncli.c in Sources */ = {isa = PBXBuildFile; fileRef = 4DDBB71B09E16BF100284745 /* transmissioncli.c */; };
|
||||
4DDFDD22099A5D8E00189D81 /* DownloadBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DDFDD20099A5D8E00189D81 /* DownloadBadge.png */; };
|
||||
|
@ -178,6 +208,15 @@
|
|||
files = (
|
||||
A25FCDDF0A37695F002BCBBE /* PauseSelected.png in CopyFiles */,
|
||||
A25FCDE00A37695F002BCBBE /* ResumeSelected.png in CopyFiles */,
|
||||
35B037A70AC59BC600A10FDF /* Check.png in CopyFiles */,
|
||||
35B037FB0AC5B53800A10FDF /* ResumeNoWaitOn.png in CopyFiles */,
|
||||
35B037FC0AC5B53800A10FDF /* ResumeNoWaitOff.png in CopyFiles */,
|
||||
3518E4D10AC62517002ED3A2 /* PiecesBack.tiff in CopyFiles */,
|
||||
3518E4D30AC62517002ED3A2 /* BoxBlue1.tiff in CopyFiles */,
|
||||
3518E4D40AC62517002ED3A2 /* BoxBlue2.tiff in CopyFiles */,
|
||||
3518E5290AC62A55002ED3A2 /* BoxBlue3.tiff in CopyFiles */,
|
||||
3518E52A0AC62A55002ED3A2 /* BoxWhite.tiff in CopyFiles */,
|
||||
3518E5770AC63262002ED3A2 /* BoxGreen.tiff in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -210,6 +249,18 @@
|
|||
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; name = Transmission_Prefix.pch; path = macosx/Transmission_Prefix.pch; sourceTree = "<group>"; };
|
||||
3518E4A30AC620FC002ED3A2 /* PiecesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PiecesWindowController.h; sourceTree = "<group>"; };
|
||||
3518E4A40AC620FC002ED3A2 /* PiecesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PiecesWindowController.m; sourceTree = "<group>"; };
|
||||
3518E4CD0AC62517002ED3A2 /* PiecesBack.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = PiecesBack.tiff; path = macosx/Images/PiecesBack.tiff; sourceTree = "<group>"; };
|
||||
3518E4CF0AC62517002ED3A2 /* BoxBlue1.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = BoxBlue1.tiff; path = macosx/Images/BoxBlue1.tiff; sourceTree = "<group>"; };
|
||||
3518E4D00AC62517002ED3A2 /* BoxBlue2.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = BoxBlue2.tiff; path = macosx/Images/BoxBlue2.tiff; sourceTree = "<group>"; };
|
||||
3518E4D60AC6253F002ED3A2 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = macosx/English.lproj/PiecesWindow.nib; sourceTree = "<group>"; };
|
||||
3518E5270AC62A55002ED3A2 /* BoxBlue3.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = BoxBlue3.tiff; path = macosx/Images/BoxBlue3.tiff; sourceTree = "<group>"; };
|
||||
3518E5280AC62A55002ED3A2 /* BoxWhite.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = BoxWhite.tiff; path = macosx/Images/BoxWhite.tiff; sourceTree = "<group>"; };
|
||||
3518E5760AC63262002ED3A2 /* BoxGreen.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = BoxGreen.tiff; path = macosx/Images/BoxGreen.tiff; sourceTree = "<group>"; };
|
||||
35B037A60AC59BC600A10FDF /* Check.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Check.png; path = macosx/Images/Check.png; sourceTree = "<group>"; };
|
||||
35B037F90AC5B53800A10FDF /* ResumeNoWaitOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeNoWaitOn.png; path = macosx/Images/ResumeNoWaitOn.png; sourceTree = "<group>"; };
|
||||
35B037FA0AC5B53800A10FDF /* ResumeNoWaitOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeNoWaitOff.png; path = macosx/Images/ResumeNoWaitOff.png; sourceTree = "<group>"; };
|
||||
4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = TransmissionDocument.icns; path = macosx/Images/TransmissionDocument.icns; sourceTree = "<group>"; };
|
||||
4D118E1808CB46B20033958F /* PrefsController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PrefsController.h; path = macosx/PrefsController.h; sourceTree = "<group>"; };
|
||||
4D118E1908CB46B20033958F /* PrefsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = PrefsController.m; path = macosx/PrefsController.m; sourceTree = "<group>"; };
|
||||
|
@ -262,9 +313,17 @@
|
|||
4DA6FDB90911233800450CB1 /* PauseOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOff.png; path = macosx/Images/PauseOff.png; sourceTree = "<group>"; };
|
||||
4DA6FDC3091141AD00450CB1 /* ResumeOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOff.png; path = macosx/Images/ResumeOff.png; sourceTree = "<group>"; };
|
||||
4DA6FDC4091141AD00450CB1 /* ResumeOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOn.png; path = macosx/Images/ResumeOn.png; sourceTree = "<group>"; };
|
||||
4DAB87BD0ABE1F730081CF7E /* xml.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = xml.h; path = libtransmission/xml.h; sourceTree = "<group>"; };
|
||||
4DAB87BE0ABE1F730081CF7E /* xml.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = xml.c; path = libtransmission/xml.c; sourceTree = "<group>"; };
|
||||
4DAB87BF0ABE1F730081CF7E /* upnp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = upnp.h; path = libtransmission/upnp.h; sourceTree = "<group>"; };
|
||||
4DAB87C00ABE1F730081CF7E /* upnp.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = upnp.c; path = libtransmission/upnp.c; sourceTree = "<group>"; };
|
||||
4DAB87C10ABE1F730081CF7E /* natpmp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = natpmp.h; path = libtransmission/natpmp.h; sourceTree = "<group>"; };
|
||||
4DAB87C20ABE1F730081CF7E /* natpmp.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = natpmp.c; path = libtransmission/natpmp.c; sourceTree = "<group>"; };
|
||||
4DAB87C30ABE1F730081CF7E /* http.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = http.h; path = libtransmission/http.h; sourceTree = "<group>"; };
|
||||
4DAB87C40ABE1F730081CF7E /* http.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = http.c; path = libtransmission/http.c; sourceTree = "<group>"; };
|
||||
4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = TorrentCell.m; path = macosx/TorrentCell.m; sourceTree = "<group>"; };
|
||||
4DCCBB3D09C3D71100D3CABF /* TorrentCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = TorrentCell.h; path = macosx/TorrentCell.h; sourceTree = "<group>"; };
|
||||
4DDBB71909E16BAE00284745 /* transmissioncli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = transmissioncli; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4DDBB71909E16BAE00284745 /* transmissioncli */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "compiled.mach-o.executable"; path = transmissioncli; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4DDBB71B09E16BF100284745 /* transmissioncli.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = transmissioncli.c; path = cli/transmissioncli.c; sourceTree = "<group>"; };
|
||||
4DDFDD20099A5D8E00189D81 /* DownloadBadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = DownloadBadge.png; path = macosx/Images/DownloadBadge.png; sourceTree = "<group>"; };
|
||||
4DDFDD21099A5D8E00189D81 /* UploadBadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = UploadBadge.png; path = macosx/Images/UploadBadge.png; sourceTree = "<group>"; };
|
||||
|
@ -393,6 +452,8 @@
|
|||
080E96DDFE201D6D7F000001 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3518E4A30AC620FC002ED3A2 /* PiecesWindowController.h */,
|
||||
3518E4A40AC620FC002ED3A2 /* PiecesWindowController.m */,
|
||||
A2A306530AAD24A80049E2AC /* UKFileWatcher.h */,
|
||||
A2A306540AAD24A80049E2AC /* UKFileWatcher.m */,
|
||||
A2A306550AAD24A80049E2AC /* UKFNSubscribeFileWatcher.h */,
|
||||
|
@ -463,6 +524,12 @@
|
|||
children = (
|
||||
A259316A0A73B2CC002F4FE7 /* Transmission Help */,
|
||||
A2F8951E0A2D4BA500ED2127 /* Credits.rtf */,
|
||||
3518E4CD0AC62517002ED3A2 /* PiecesBack.tiff */,
|
||||
3518E4CF0AC62517002ED3A2 /* BoxBlue1.tiff */,
|
||||
3518E4D00AC62517002ED3A2 /* BoxBlue2.tiff */,
|
||||
3518E5270AC62A55002ED3A2 /* BoxBlue3.tiff */,
|
||||
3518E5280AC62A55002ED3A2 /* BoxWhite.tiff */,
|
||||
3518E5760AC63262002ED3A2 /* BoxGreen.tiff */,
|
||||
A2305AA40A3DCCEF00AB2D77 /* ProgressBarEndAdvanced.png */,
|
||||
A2305AA50A3DCCEF00AB2D77 /* ProgressBarEndBlue.png */,
|
||||
A2305AA60A3DCCEF00AB2D77 /* ProgressBarEndWhite.png */,
|
||||
|
@ -473,6 +540,7 @@
|
|||
A2305A7D0A3DC9E400AB2D77 /* ProgressBarBlue.png */,
|
||||
A2305A7E0A3DC9E400AB2D77 /* ProgressBarGray.png */,
|
||||
A2305A7F0A3DC9E400AB2D77 /* ProgressBarGreen.png */,
|
||||
35B037A60AC59BC600A10FDF /* Check.png */,
|
||||
A260C9AB0AA3B8D700FDC1B7 /* Error.tiff */,
|
||||
A2D4F0840A915F7200890C32 /* GreenDot.tiff */,
|
||||
A2D4F0820A915F6600890C32 /* RedDot.tiff */,
|
||||
|
@ -486,6 +554,8 @@
|
|||
4DF7500808A103AD007B0D70 /* Info.png */,
|
||||
4DF7500708A103AD007B0D70 /* Open.png */,
|
||||
4DF7500908A103AD007B0D70 /* Remove.png */,
|
||||
35B037F90AC5B53800A10FDF /* ResumeNoWaitOn.png */,
|
||||
35B037FA0AC5B53800A10FDF /* ResumeNoWaitOff.png */,
|
||||
4D6DAAC4090CE00500F43C22 /* RevealOff.png */,
|
||||
4D6DAAC5090CE00500F43C22 /* RevealOn.png */,
|
||||
4DA6FDB80911233800450CB1 /* PauseOn.png */,
|
||||
|
@ -529,6 +599,7 @@
|
|||
A253F7280A699373008EE24F /* FilterButtonSelectedMain.png */,
|
||||
A253F7290A699373008EE24F /* FilterButtonSelectedRight.png */,
|
||||
A2912C520A2956E80097A0CA /* PrefsWindow.nib */,
|
||||
3518E4D50AC6253F002ED3A2 /* PiecesWindow.nib */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
|
@ -583,6 +654,14 @@
|
|||
4D1838B909DEC0430047D688 /* completion.h */,
|
||||
4D1838BA09DEC0430047D688 /* completion.c */,
|
||||
4D1838BB09DEC0430047D688 /* clients.h */,
|
||||
4DAB87BD0ABE1F730081CF7E /* xml.h */,
|
||||
4DAB87BE0ABE1F730081CF7E /* xml.c */,
|
||||
4DAB87BF0ABE1F730081CF7E /* upnp.h */,
|
||||
4DAB87C00ABE1F730081CF7E /* upnp.c */,
|
||||
4DAB87C10ABE1F730081CF7E /* natpmp.h */,
|
||||
4DAB87C20ABE1F730081CF7E /* natpmp.c */,
|
||||
4DAB87C30ABE1F730081CF7E /* http.h */,
|
||||
4DAB87C40ABE1F730081CF7E /* http.c */,
|
||||
);
|
||||
name = libtransmission;
|
||||
sourceTree = "<group>";
|
||||
|
@ -649,6 +728,11 @@
|
|||
4D1838FC09DEC4380047D688 /* transmission.h in Headers */,
|
||||
A26D450B0A0503AC00A10BB3 /* peermessages.h in Headers */,
|
||||
A2BD40070A09BBEA008CCE96 /* bencode.h in Headers */,
|
||||
4DAB87C50ABE1F730081CF7E /* xml.h in Headers */,
|
||||
4DAB87C70ABE1F730081CF7E /* upnp.h in Headers */,
|
||||
4DAB87C90ABE1F730081CF7E /* natpmp.h in Headers */,
|
||||
4DAB87CB0ABE1F730081CF7E /* http.h in Headers */,
|
||||
3518E4A50AC620FC002ED3A2 /* PiecesWindowController.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -799,6 +883,16 @@
|
|||
A2D4F0850A915F7200890C32 /* GreenDot.tiff in Resources */,
|
||||
A21567ED0A9A5034004DECD6 /* MessageWindow.nib in Resources */,
|
||||
A260C9AC0AA3B8D700FDC1B7 /* Error.tiff in Resources */,
|
||||
35B037B60AC59C4000A10FDF /* Check.png in Resources */,
|
||||
35B038130AC5B6EB00A10FDF /* ResumeNoWaitOn.png in Resources */,
|
||||
35B038140AC5B6EC00A10FDF /* ResumeNoWaitOff.png in Resources */,
|
||||
3518E4D70AC6253F002ED3A2 /* PiecesWindow.nib in Resources */,
|
||||
3518E5210AC62A29002ED3A2 /* PiecesBack.tiff in Resources */,
|
||||
3518E5230AC62A2A002ED3A2 /* BoxBlue2.tiff in Resources */,
|
||||
3518E5240AC62A2B002ED3A2 /* BoxBlue1.tiff in Resources */,
|
||||
3518E52B0AC62A57002ED3A2 /* BoxWhite.tiff in Resources */,
|
||||
3518E52C0AC62A57002ED3A2 /* BoxBlue3.tiff in Resources */,
|
||||
3518E57B0AC632EA002ED3A2 /* BoxGreen.tiff in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -824,6 +918,11 @@
|
|||
4D1838D909DEC0430047D688 /* completion.c in Sources */,
|
||||
4D1838FB09DEC4380047D688 /* utils.c in Sources */,
|
||||
4D1838FD09DEC4380047D688 /* transmission.c in Sources */,
|
||||
4DAB87C60ABE1F730081CF7E /* xml.c in Sources */,
|
||||
4DAB87C80ABE1F730081CF7E /* upnp.c in Sources */,
|
||||
4DAB87CA0ABE1F730081CF7E /* natpmp.c in Sources */,
|
||||
4DAB87CC0ABE1F730081CF7E /* http.c in Sources */,
|
||||
3518E4A60AC620FC002ED3A2 /* PiecesWindowController.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -858,6 +957,7 @@
|
|||
A2A3065E0AAD24A80049E2AC /* UKFNSubscribeFileWatcher.m in Sources */,
|
||||
A2A306600AAD24A80049E2AC /* UKKQueue.m in Sources */,
|
||||
A2A306620AAD24A80049E2AC /* UKMainThreadProxy.m in Sources */,
|
||||
3518E4FB0AC62832002ED3A2 /* PiecesWindowController.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -893,6 +993,14 @@
|
|||
name = MainMenu.nib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3518E4D50AC6253F002ED3A2 /* PiecesWindow.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3518E4D60AC6253F002ED3A2 /* English */,
|
||||
);
|
||||
name = PiecesWindow.nib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A200B9620A227FD0007BBB1E /* InfoWindow.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
.Op Fl i Ar torrent-file
|
||||
.Op Fl s Ar torrent-file
|
||||
.Op Fl v Ar level
|
||||
.Op Fl n
|
||||
.Op Fl p Ar port
|
||||
.Op Fl u Ar upload-rate
|
||||
.Op Fl u Ar download-rate
|
||||
|
@ -50,6 +51,9 @@ file, and exits.
|
|||
.It Fl v, -verbose Ar level
|
||||
Sets debugging options. The current range is 0-2, with the highest
|
||||
level producing the most output. The default is 0.
|
||||
.It Fl n, Fl -nat-traversal
|
||||
Attempt to use the NAT-PMP and UPnP IGD protocols to establish a port
|
||||
mapping for allowing incoming peer connections.
|
||||
.It Fl p, -port Ar port
|
||||
Specifies an alternate port for the client to listen on. The default is
|
||||
9090.
|
||||
|
|
|
@ -37,14 +37,15 @@
|
|||
#define USAGE \
|
||||
"Usage: %s [options] file.torrent [options]\n\n" \
|
||||
"Options:\n" \
|
||||
" -d, --download <int> Maximum download rate (-1 = no limit, default = -1)\n"\
|
||||
" -f, --finish <shell script> Command you wish to run on completion\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" \
|
||||
" -n --nat-traversal Attempt NAT traversal using NAT-PMP or UPnP IGD\n" \
|
||||
" -p, --port <int> Port we should listen on (default = %d)\n" \
|
||||
" -s, --scrape Print counts of seeders/leechers and exit\n" \
|
||||
" -u, --upload <int> Maximum upload rate (-1 = no limit, default = 20)\n" \
|
||||
" -d, --download <int> Maximum download rate (-1 = no limit, default = -1)\n" \
|
||||
" -f, --finish <shell script> Command you wish to run on completion\n"
|
||||
" -v, --verbose <int> Verbose level (0 to 2, default = 0)\n"
|
||||
|
||||
static int showHelp = 0;
|
||||
static int showInfo = 0;
|
||||
|
@ -55,6 +56,7 @@ static int uploadLimit = 20;
|
|||
static int downloadLimit = -1;
|
||||
static char * torrentPath = NULL;
|
||||
static volatile char mustDie = 0;
|
||||
static int natTraversal = 0;
|
||||
|
||||
static char * finishCall = NULL;
|
||||
|
||||
|
@ -63,7 +65,7 @@ static void sigHandler ( int signal );
|
|||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
int i, error;
|
||||
int i, error, nat;
|
||||
tr_handle_t * h;
|
||||
tr_torrent_t * tor;
|
||||
tr_stat_t * s;
|
||||
|
@ -163,6 +165,15 @@ int main( int argc, char ** argv )
|
|||
tr_setBindPort( h, bindPort );
|
||||
tr_setUploadLimit( h, uploadLimit );
|
||||
tr_setDownloadLimit( h, downloadLimit );
|
||||
|
||||
if( natTraversal )
|
||||
{
|
||||
tr_natTraversalEnable( h );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_natTraversalDisable( h );
|
||||
}
|
||||
|
||||
tr_torrentSetFolder( tor, "." );
|
||||
tr_torrentStart( tor );
|
||||
|
@ -200,7 +211,7 @@ int main( int argc, char ** argv )
|
|||
}
|
||||
memset( &string[chars], ' ', 79 - chars );
|
||||
string[79] = '\0';
|
||||
fprintf( stderr, "\r%s", string );
|
||||
//fprintf( stderr, "\r%s", string );
|
||||
|
||||
if( s->error & TR_ETRACKER )
|
||||
{
|
||||
|
@ -218,14 +229,18 @@ int main( int argc, char ** argv )
|
|||
}
|
||||
fprintf( stderr, "\n" );
|
||||
|
||||
/* Try for 5 seconds to notice the tracker that we are leaving */
|
||||
/* Try for 5 seconds to notify the tracker that we are leaving
|
||||
and to delete any port mappings for nat traversal */
|
||||
tr_torrentStop( tor );
|
||||
tr_natTraversalDisable( h );
|
||||
for( i = 0; i < 10; i++ )
|
||||
{
|
||||
s = tr_torrentStat( tor );
|
||||
if( s->status & TR_STATUS_PAUSE )
|
||||
nat = tr_natTraversalStatus( h );
|
||||
if( s->status & TR_STATUS_PAUSE && TR_NAT_TRAVERSAL_DISABLED == nat )
|
||||
{
|
||||
/* The 'stopped' message was sent */
|
||||
/* The 'stopped' tracker message was sent
|
||||
and port mappings were deleted */
|
||||
break;
|
||||
}
|
||||
usleep( 500000 );
|
||||
|
@ -253,10 +268,11 @@ static int parseCommandLine( int argc, char ** argv )
|
|||
{ "upload", required_argument, NULL, 'u' },
|
||||
{ "download", required_argument, NULL, 'd' },
|
||||
{ "finish", required_argument, NULL, 'f' },
|
||||
{ "nat-traversal", no_argument, NULL, 'n' },
|
||||
{ 0, 0, 0, 0} };
|
||||
|
||||
int c, optind = 0;
|
||||
c = getopt_long( argc, argv, "hisv:p:u:d:f:", long_options, &optind );
|
||||
c = getopt_long( argc, argv, "hisv:p:u:d:f:n", long_options, &optind );
|
||||
if( c < 0 )
|
||||
{
|
||||
break;
|
||||
|
@ -287,6 +303,9 @@ static int parseCommandLine( int argc, char ** argv )
|
|||
case 'f':
|
||||
finishCall = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
natTraversal = 1;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -358,7 +358,7 @@ cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
|
|||
GIOChannel *io = NULL;
|
||||
GError *err;
|
||||
char *datastr;
|
||||
size_t len;
|
||||
int len;
|
||||
gsize written;
|
||||
|
||||
*errstr = NULL;
|
||||
|
|
|
@ -59,5 +59,6 @@ cf_freestate(benc_val_t *state);
|
|||
#define PREF_ADDSTD "add-behavior-standard"
|
||||
#define PREF_ADDIPC "add-behavior-ipc"
|
||||
#define PREF_MSGLEVEL "message-level"
|
||||
#define PREF_NAT "use-nat-traversal"
|
||||
|
||||
#endif /* TG_CONF_H */
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#define DEF_USEDOWNLIMIT FALSE
|
||||
#define DEF_UPLIMIT 20
|
||||
#define DEF_USEUPLIMIT TRUE
|
||||
#define DEF_NAT TRUE
|
||||
|
||||
struct prefdata {
|
||||
GList *prefwidgets;
|
||||
|
@ -170,9 +171,11 @@ makeprefwindow(GtkWindow *parent, TrBackend *back) {
|
|||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
|
||||
GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
|
||||
const unsigned int rowcount = 8;
|
||||
const unsigned int rowcount = 9;
|
||||
GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
|
||||
GtkWidget *portnum = gtk_spin_button_new_with_range(1, 0xffff, 1);
|
||||
GtkWidget *natcheck = gtk_check_button_new_with_mnemonic(
|
||||
_("Use NAT _Traversal (NAT-PMP and UPnP)"));
|
||||
GtkWidget *dirstr = gtk_file_chooser_button_new(
|
||||
_("Choose a download directory"),
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
|
||||
|
@ -196,6 +199,8 @@ makeprefwindow(GtkWindow *parent, TrBackend *back) {
|
|||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkCellRenderer *rend;
|
||||
gboolean boolval;
|
||||
int intval;
|
||||
|
||||
g_free(title);
|
||||
gtk_widget_set_name(wind, "TransmissionDialog");
|
||||
|
@ -206,7 +211,7 @@ makeprefwindow(GtkWindow *parent, TrBackend *back) {
|
|||
gtk_window_set_resizable(GTK_WINDOW(wind), FALSE);
|
||||
|
||||
data->prefwidgets = makeglist(portnum, lim[0].on, lim[0].num, lim[1].on,
|
||||
lim[1].num, dirstr, addstd, addipc, NULL);
|
||||
lim[1].num, dirstr, addstd, addipc, natcheck, NULL);
|
||||
data->parent = parent;
|
||||
data->back = back;
|
||||
g_object_ref(G_OBJECT(back));
|
||||
|
@ -253,6 +258,13 @@ makeprefwindow(GtkWindow *parent, TrBackend *back) {
|
|||
gtk_table_attach_defaults(GTK_TABLE(table), portnum, 1, 2, RN(ii));
|
||||
ii++;
|
||||
|
||||
/* NAT traversal checkbox */
|
||||
intval = tr_natTraversalStatus(tr_backend_handle(back));
|
||||
boolval = !TR_NAT_TRAVERSAL_IS_DISABLED( intval );
|
||||
setupprefwidget(natcheck, PREF_NAT, boolval);
|
||||
gtk_table_attach_defaults(GTK_TABLE(table), natcheck, 0, 2, RN(ii));
|
||||
ii++;
|
||||
|
||||
/* create the model used by the three popup menus */
|
||||
model = GTK_TREE_MODEL(gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT));
|
||||
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
|
||||
|
@ -340,11 +352,8 @@ clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
|
|||
g_free(errstr);
|
||||
}
|
||||
|
||||
applyprefs(data->back);
|
||||
/* XXX would be nice to have errno strings, are they printed to stdout? */
|
||||
|
||||
tr_setBindPort(tr_backend_handle(data->back),
|
||||
strtol(cf_getpref(PREF_PORT), NULL, 10));
|
||||
setlimit(data->back);
|
||||
}
|
||||
|
||||
if(GTK_RESPONSE_APPLY != resp)
|
||||
|
@ -352,7 +361,7 @@ clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
|
|||
}
|
||||
|
||||
void
|
||||
setlimit(TrBackend *back) {
|
||||
applyprefs(TrBackend *back) {
|
||||
struct { void (*func)(tr_handle_t*, int);
|
||||
const char *use; const char *num; gboolean defuse; long def; } lim[] = {
|
||||
{tr_setDownloadLimit, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT,
|
||||
|
@ -361,10 +370,12 @@ setlimit(TrBackend *back) {
|
|||
DEF_USEUPLIMIT, DEF_UPLIMIT},
|
||||
};
|
||||
const char *pref;
|
||||
unsigned int ii;
|
||||
int ii;
|
||||
tr_handle_t *tr = tr_backend_handle(back);
|
||||
gboolean boolval;
|
||||
|
||||
for(ii = 0; ii < ALEN(lim); ii++) {
|
||||
/* set upload and download limits */
|
||||
for(ii = 0; ii < (int)ALEN(lim); ii++) {
|
||||
pref = cf_getpref(lim[ii].use);
|
||||
if(!(NULL == pref ? lim[ii].defuse : strbool(pref)))
|
||||
lim[ii].func(tr, -1);
|
||||
|
@ -373,6 +384,18 @@ setlimit(TrBackend *back) {
|
|||
lim[ii].func(tr, (NULL == pref ? lim[ii].def : strtol(pref, NULL, 10)));
|
||||
}
|
||||
}
|
||||
|
||||
/* set the listening port */
|
||||
if(NULL != (pref = cf_getpref(PREF_PORT)) &&
|
||||
0 < (ii = strtol(pref, NULL, 10)) && 0xffff >= ii)
|
||||
tr_setBindPort(tr, ii);
|
||||
|
||||
/* enable/disable NAT traversal */
|
||||
boolval = (NULL == (pref = cf_getpref(PREF_NAT)) ? DEF_NAT : strbool(pref));
|
||||
if( boolval )
|
||||
tr_natTraversalEnable(tr);
|
||||
else
|
||||
tr_natTraversalDisable(tr);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
GtkWidget *
|
||||
makeprefwindow(GtkWindow *parent, TrBackend *back);
|
||||
|
||||
/* set the upload limit based on saved prefs */
|
||||
/* set various things based on saved prefs */
|
||||
void
|
||||
setlimit(TrBackend *back);
|
||||
applyprefs(TrBackend *back);
|
||||
|
||||
/* show the "add a torrent" dialog */
|
||||
void
|
||||
|
|
|
@ -263,7 +263,7 @@ srv_io_accept(GSource *source SHUTUP, int fd, struct sockaddr *sa SHUTUP,
|
|||
static int
|
||||
send_msg(struct constate *con, const char *name, benc_val_t *val) {
|
||||
char *buf;
|
||||
size_t used, total;
|
||||
int used, total;
|
||||
benc_val_t dict;
|
||||
char stupid;
|
||||
|
||||
|
|
23
gtk/main.c
23
gtk/main.c
|
@ -177,8 +177,6 @@ main(int argc, char **argv) {
|
|||
char *err;
|
||||
TrBackend *back;
|
||||
benc_val_t *state;
|
||||
const char *pref;
|
||||
long intval;
|
||||
GList *argfiles;
|
||||
gboolean didinit, didlock;
|
||||
|
||||
|
@ -233,13 +231,8 @@ main(int argc, char **argv) {
|
|||
|
||||
back = tr_backend_new();
|
||||
|
||||
/* set the upload limit */
|
||||
setlimit(back);
|
||||
|
||||
/* set the listening port */
|
||||
if(NULL != (pref = cf_getpref(PREF_PORT)) &&
|
||||
0 < (intval = strtol(pref, NULL, 10)) && 0xffff >= intval)
|
||||
tr_setBindPort(tr_backend_handle(back), intval);
|
||||
/* apply a few prefs */
|
||||
applyprefs(back);
|
||||
|
||||
makewind(mainwind, back, state, argfiles);
|
||||
|
||||
|
@ -523,6 +516,9 @@ winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
|
|||
/* try to politely stop all the torrents */
|
||||
tr_backend_stop_torrents(data->back);
|
||||
|
||||
/* shut down nat traversal */
|
||||
tr_natTraversalDisable(tr_backend_handle(data->back));
|
||||
|
||||
/* set things up to wait for torrents to stop */
|
||||
edata = g_new0(struct exitdata, 1);
|
||||
edata->cbdata = data;
|
||||
|
@ -544,10 +540,13 @@ winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
|
|||
gboolean
|
||||
exitcheck(gpointer gdata) {
|
||||
struct exitdata *data = gdata;
|
||||
int natstat = tr_natTraversalStatus(tr_backend_handle(data->cbdata->back));
|
||||
|
||||
/* keep going if we still have torrents and haven't hit the exit timeout */
|
||||
if(time(NULL) - data->started < TRACKER_EXIT_TIMEOUT &&
|
||||
!tr_backend_torrents_stopped(data->cbdata->back)) {
|
||||
/* keep going if we haven't hit the exit timeout and
|
||||
we either have torrents left or nat traversal is stopping */
|
||||
if( time( NULL ) - data->started < TRACKER_EXIT_TIMEOUT &&
|
||||
( !tr_backend_torrents_stopped( data->cbdata->back ) &&
|
||||
TR_NAT_TRAVERSAL_DISABLED != natstat ) ) {
|
||||
updatemodel(data->cbdata);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -25,16 +25,8 @@
|
|||
#include "transmission.h"
|
||||
|
||||
#define LIST_SIZE 20
|
||||
#define OUTBUF_SIZE 100
|
||||
|
||||
static int tr_bencSprintf( char ** buf, size_t * used, size_t * max,
|
||||
char * format, ... )
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format (printf, 4, 5)))
|
||||
#endif
|
||||
;
|
||||
|
||||
int _tr_bencLoad( char * buf, size_t len, benc_val_t * val, char ** end )
|
||||
int _tr_bencLoad( char * buf, int len, benc_val_t * val, char ** end )
|
||||
{
|
||||
char * p, * e, * foo;
|
||||
|
||||
|
@ -89,7 +81,7 @@ int _tr_bencLoad( char * buf, size_t len, benc_val_t * val, char ** end )
|
|||
val->val.l.vals = malloc( LIST_SIZE * sizeof( benc_val_t ) );
|
||||
cur = &buf[1];
|
||||
str_expected = 1;
|
||||
while( (size_t)(cur - buf) < len && cur[0] != 'e' )
|
||||
while( cur - buf < len && cur[0] != 'e' )
|
||||
{
|
||||
if( val->val.l.count == val->val.l.alloc )
|
||||
{
|
||||
|
@ -139,7 +131,7 @@ int _tr_bencLoad( char * buf, size_t len, benc_val_t * val, char ** end )
|
|||
e[0] = ':';
|
||||
|
||||
if( p != e || 0 > val->val.s.i ||
|
||||
(size_t)(val->val.s.i) > len - ((p + 1) - buf) )
|
||||
val->val.s.i > len - ((p + 1) - buf) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -240,10 +232,10 @@ benc_val_t * tr_bencDictFind( benc_val_t * val, char * key )
|
|||
return NULL;
|
||||
}
|
||||
|
||||
char * tr_bencSaveMalloc( benc_val_t * val, size_t * len )
|
||||
char * tr_bencSaveMalloc( benc_val_t * val, int * len )
|
||||
{
|
||||
char * buf = NULL;
|
||||
size_t alloc = 0;
|
||||
int alloc = 0;
|
||||
|
||||
*len = 0;
|
||||
if( tr_bencSave( val, &buf, len, &alloc ) )
|
||||
|
@ -259,14 +251,14 @@ char * tr_bencSaveMalloc( benc_val_t * val, size_t * len )
|
|||
return buf;
|
||||
}
|
||||
|
||||
int tr_bencSave( benc_val_t * val, char ** buf, size_t * used, size_t * max )
|
||||
int tr_bencSave( benc_val_t * val, char ** buf, int * used, int * max )
|
||||
{
|
||||
int ii;
|
||||
|
||||
switch( val->type )
|
||||
{
|
||||
case TYPE_INT:
|
||||
if( tr_bencSprintf( buf, used, max, "i%"PRIu64"e", val->val.i ) )
|
||||
if( tr_sprintf( buf, used, max, "i%"PRIu64"e", val->val.i ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -277,8 +269,8 @@ int tr_bencSave( benc_val_t * val, char ** buf, size_t * used, size_t * max )
|
|||
{
|
||||
return 1;
|
||||
}
|
||||
if( tr_bencSprintf( buf, used, max, "%i:%s",
|
||||
val->val.s.i, val->val.s.s ) )
|
||||
if( tr_sprintf( buf, used, max, "%i:%s",
|
||||
val->val.s.i, val->val.s.s ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -286,8 +278,8 @@ int tr_bencSave( benc_val_t * val, char ** buf, size_t * used, size_t * max )
|
|||
|
||||
case TYPE_LIST:
|
||||
case TYPE_DICT:
|
||||
if( tr_bencSprintf( buf, used, max,
|
||||
(TYPE_LIST == val->type ? "l" : "d") ) )
|
||||
if( tr_sprintf( buf, used, max,
|
||||
(TYPE_LIST == val->type ? "l" : "d") ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -298,7 +290,7 @@ int tr_bencSave( benc_val_t * val, char ** buf, size_t * used, size_t * max )
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
if( tr_bencSprintf( buf, used, max, "e" ) )
|
||||
if( tr_sprintf( buf, used, max, "e" ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -307,32 +299,3 @@ int tr_bencSave( benc_val_t * val, char ** buf, size_t * used, size_t * max )
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tr_bencSprintf( char ** buf, size_t * used, size_t * max,
|
||||
char * format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int want;
|
||||
char * newbuf;
|
||||
|
||||
va_start( ap, format );
|
||||
want = vsnprintf( NULL, 0, format, ap );
|
||||
va_end(ap);
|
||||
|
||||
while( *used + want + 1 > *max )
|
||||
{
|
||||
*max += OUTBUF_SIZE;
|
||||
newbuf = realloc( *buf, *max );
|
||||
if( NULL == newbuf )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
*buf = newbuf;
|
||||
}
|
||||
|
||||
va_start( ap, format );
|
||||
*used += vsnprintf( *buf + *used, *max - *used, format, ap );
|
||||
va_end( ap );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -52,13 +52,13 @@ typedef struct benc_val_s
|
|||
} benc_val_t;
|
||||
|
||||
#define tr_bencLoad(b,l,v,e) _tr_bencLoad((char*)(b),(l),(v),(char**)(e))
|
||||
int _tr_bencLoad( char * buf, size_t len, benc_val_t * val,
|
||||
int _tr_bencLoad( char * buf, int len, 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 );
|
||||
char * tr_bencSaveMalloc( benc_val_t * val, size_t * len );
|
||||
char * tr_bencSaveMalloc( benc_val_t * val, int * len );
|
||||
int tr_bencSave( benc_val_t * val, char ** buf,
|
||||
size_t * used, size_t * max );
|
||||
int * used, int * max );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -248,7 +248,7 @@ static int fastResumeLoadOld( tr_io_t * io, FILE * file )
|
|||
if( ftell( file ) != size )
|
||||
{
|
||||
tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
|
||||
ftell( file ), size );
|
||||
(int)ftell( file ), size );
|
||||
fclose( file );
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,829 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* 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 HTTP_PORT 80 /* default http port 80 */
|
||||
#define HTTP_TIMEOUT 60000 /* one minute http timeout */
|
||||
#define HTTP_BUFSIZE 1500 /* 1.5K buffer size increment */
|
||||
#define LF "\012"
|
||||
#define CR "\015"
|
||||
#define SP( cc ) ( ' ' == (cc) || '\t' == (cc) )
|
||||
#define NL( cc ) ( '\015' == (cc) || '\012' == (cc) )
|
||||
#define NUM( cc ) ( '0' <= (cc) && '9' >= (cc) )
|
||||
#define ALEN( aa ) ( (int)(sizeof( (aa) ) / sizeof( (aa)[0] ) ) )
|
||||
#define SKIP( off, len, done ) \
|
||||
while( (off) < (len) && (done) ) { (off)++; };
|
||||
|
||||
static const char *
|
||||
slice( const char * data, int * len, const char * delim );
|
||||
static tr_tristate_t
|
||||
sendrequest( tr_http_t * http );
|
||||
static tr_tristate_t
|
||||
receiveresponse( tr_http_t * http );
|
||||
static int
|
||||
checklength( tr_http_t * http );
|
||||
static int
|
||||
learnlength( tr_http_t * http );
|
||||
|
||||
#define EXPANDBUF( bs ) &(bs).buf, &(bs).used, &(bs).size
|
||||
struct buf {
|
||||
char * buf;
|
||||
int size;
|
||||
int used;
|
||||
};
|
||||
|
||||
struct tr_http_s {
|
||||
#define HTTP_STATE_CONSTRUCT 1
|
||||
#define HTTP_STATE_RESOLVE 2
|
||||
#define HTTP_STATE_CONNECT 3
|
||||
#define HTTP_STATE_RECEIVE 4
|
||||
#define HTTP_STATE_DONE 5
|
||||
#define HTTP_STATE_ERROR 6
|
||||
char state;
|
||||
#define HTTP_LENGTH_UNKNOWN 1
|
||||
#define HTTP_LENGTH_EOF 2
|
||||
#define HTTP_LENGTH_FIXED 3
|
||||
#define HTTP_LENGTH_CHUNKED 4
|
||||
char lengthtype;
|
||||
tr_resolve_t * resolve;
|
||||
char * host;
|
||||
int port;
|
||||
int sock;
|
||||
struct buf header;
|
||||
struct buf body;
|
||||
uint64_t date;
|
||||
/*
|
||||
eof: unused
|
||||
fixed: lenptr is the start of the body, lenint is the body length
|
||||
chunked: lenptr is start of chunk (after length), lenint is chunk size
|
||||
*/
|
||||
int chunkoff;
|
||||
int chunklen;
|
||||
};
|
||||
|
||||
int
|
||||
tr_httpRequestType( const char * data, int len, char ** method, char ** uri )
|
||||
{
|
||||
const char * words[6];
|
||||
int ii, ret;
|
||||
const char * end;
|
||||
|
||||
/* find the end of the line */
|
||||
for( end = data; data + len > end; end++ )
|
||||
{
|
||||
if( NL( *data) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* find the first three "words" in the line */
|
||||
for( ii = 0; ALEN( words ) > ii && data < end; ii++ )
|
||||
{
|
||||
/* find the next space or non-space */
|
||||
while( data < end && ( ii % 2 ? !SP( *data ) : SP( *data ) ) )
|
||||
{
|
||||
data++;
|
||||
}
|
||||
|
||||
/* save the beginning of the word */
|
||||
words[ii] = data;
|
||||
}
|
||||
|
||||
/* check for missing words */
|
||||
if( ALEN( words) > ii )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse HTTP version */
|
||||
ret = -1;
|
||||
if( 8 <= words[5] - words[4] )
|
||||
{
|
||||
if( 0 == tr_strncasecmp( words[4], "HTTP/1.1", 8 ) )
|
||||
{
|
||||
ret = 11;
|
||||
}
|
||||
else if( 0 == tr_strncasecmp( words[4], "HTTP/1.0", 8 ) )
|
||||
{
|
||||
ret = 10;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy the method */
|
||||
if( 0 <= ret && NULL != method )
|
||||
{
|
||||
*method = tr_dupstr( words[0], words[1] - words[0] );
|
||||
if( NULL == *method )
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
/* copy uri */
|
||||
if( 0 <= ret && NULL != uri )
|
||||
{
|
||||
*uri = tr_dupstr( words[2], words[3] - words[2] );
|
||||
if( NULL == *uri )
|
||||
{
|
||||
free( *method );
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
tr_httpResponseCode( const char * data, int len )
|
||||
{
|
||||
char code[4];
|
||||
int ret;
|
||||
|
||||
/* check for the minimum legal length */
|
||||
if( 12 > len ||
|
||||
/* check for valid http version */
|
||||
0 != tr_strncasecmp( data, "HTTP/1.", 7 ) ||
|
||||
( '1' != data[7] && '0' != data[7] ) ||
|
||||
/* there should be at least one space after the version */
|
||||
!SP( data[8] ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* skip any extra spaces */
|
||||
data += 9;
|
||||
len -= 9;
|
||||
while( 0 < len && SP( *data ) )
|
||||
{
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* check for a valid three-digit code */
|
||||
if( 3 > len || !NUM( data[0] ) || !NUM( data[1] ) || !NUM( data[2] ) ||
|
||||
( 3 < len && NUM( data[3] ) ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse and return the code */
|
||||
memcpy( code, data, 3 );
|
||||
code[3] = '\0';
|
||||
ret = strtol( code, NULL, 10 );
|
||||
if( 100 > ret )
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_httpParse( const char * data, int len, tr_http_header_t *headers )
|
||||
{
|
||||
const char * body, * begin;
|
||||
int ii, jj, full;
|
||||
|
||||
/* find the end of the http headers */
|
||||
body = slice( data, &len, CR LF CR LF );
|
||||
if( NULL == body )
|
||||
{
|
||||
body = slice( data, &len, LF LF );
|
||||
if( NULL == body )
|
||||
{
|
||||
body = slice( data, &len, CR CR );
|
||||
if( NULL == body )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return if no headers were requested */
|
||||
if( NULL == headers || NULL == headers[0].name )
|
||||
{
|
||||
return (char*) body;
|
||||
}
|
||||
|
||||
/* NULL out all the header's data pointers */
|
||||
for( ii = 0; NULL != headers[ii].name; ii++ )
|
||||
{
|
||||
headers[ii].data = NULL;
|
||||
headers[ii].len = 0;
|
||||
}
|
||||
|
||||
/* skip the http request or response line */
|
||||
ii = 0;
|
||||
SKIP( ii, len, !NL( data[ii] ) );
|
||||
SKIP( ii, len, NL( data[ii] ) );
|
||||
|
||||
/* find the requested headers */
|
||||
while(ii < len )
|
||||
{
|
||||
/* skip leading spaces and find the header name */
|
||||
SKIP( ii, len, SP( data[ii] ) );
|
||||
begin = data + ii;
|
||||
SKIP( ii, len, ':' != data[ii] && !NL( data[ii] ) );
|
||||
|
||||
if( ':' == data[ii] )
|
||||
{
|
||||
full = 1;
|
||||
|
||||
/* try to match the found header with one of the requested */
|
||||
for( jj = 0; NULL != headers[jj].name; jj++ )
|
||||
{
|
||||
if( NULL == headers[jj].data )
|
||||
{
|
||||
full = 0;
|
||||
if( 0 == tr_strncasecmp( headers[jj].name, begin,
|
||||
( data + ii ) - begin ) )
|
||||
{
|
||||
ii++;
|
||||
/* skip leading whitespace and save the header value */
|
||||
SKIP( ii, len, SP( data[ii] ) );
|
||||
headers[jj].data = data + ii;
|
||||
SKIP( ii, len, !NL( data[ii] ) );
|
||||
headers[jj].len = ( data + ii ) - headers[jj].data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( full )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip to the end of the header */
|
||||
SKIP( ii, len, !NL( data[ii] ) );
|
||||
}
|
||||
|
||||
/* skip past the newline */
|
||||
SKIP( ii, len, NL( data[ii] ) );
|
||||
}
|
||||
|
||||
return (char*)body;
|
||||
}
|
||||
|
||||
static const char *
|
||||
slice( const char * data, int * len, const char * delim )
|
||||
{
|
||||
const char *body;
|
||||
int dlen;
|
||||
|
||||
dlen = strlen( delim );
|
||||
body = tr_memmem( data, *len, delim, dlen );
|
||||
|
||||
if( NULL != body )
|
||||
{
|
||||
*len = body - data;
|
||||
body += dlen;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
int
|
||||
tr_httpParseUrl( const char * url, int len,
|
||||
char ** host, int * port, char ** path )
|
||||
{
|
||||
const char * pathstart, * hostend;
|
||||
int ii, colon, portnum;
|
||||
char str[6];
|
||||
|
||||
if( 0 > len )
|
||||
{
|
||||
len = strlen( url );
|
||||
}
|
||||
|
||||
/* check for protocol */
|
||||
if( 7 > len || 0 != tr_strncasecmp( url, "http://", 7 ) )
|
||||
{
|
||||
tr_err( "Invalid HTTP URL" );
|
||||
return 1;
|
||||
}
|
||||
url += 7;
|
||||
len -= 7;
|
||||
|
||||
/* find the hostname and port */
|
||||
colon = -1;
|
||||
for( ii = 0; len > ii && '/' != url[ii]; ii++ )
|
||||
{
|
||||
if( ':' == url[ii] )
|
||||
{
|
||||
colon = ii;
|
||||
}
|
||||
}
|
||||
hostend = url + ( 0 > colon ? ii : colon );
|
||||
pathstart = url + ii;
|
||||
|
||||
/* parse the port number */
|
||||
portnum = HTTP_PORT;
|
||||
if( 0 <= colon )
|
||||
{
|
||||
colon++;
|
||||
memset( str, 0, sizeof( str ) );
|
||||
memcpy( str, url + colon, MIN( (int) sizeof( str) - 1, ii - colon ) );
|
||||
portnum = strtol( str, NULL, 10 );
|
||||
if( 0 >= portnum || 0xffff <= portnum )
|
||||
{
|
||||
tr_err( "Invalid port (%i)", portnum );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( NULL != host )
|
||||
{
|
||||
*host = tr_dupstr( url, hostend - url );
|
||||
}
|
||||
if( NULL != port )
|
||||
{
|
||||
*port = portnum;
|
||||
}
|
||||
if( NULL != path )
|
||||
{
|
||||
if( 0 < len - ( pathstart - url ) )
|
||||
{
|
||||
*path = tr_dupstr( pathstart, len - ( pathstart - url ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
*path = strdup( "/" );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
tr_http_t *
|
||||
tr_httpClient( int method, const char * host, int port, const char * fmt, ... )
|
||||
{
|
||||
tr_http_t * http;
|
||||
va_list ap1, ap2;
|
||||
char * methodstr;
|
||||
|
||||
http = malloc( sizeof( *http ) );
|
||||
if( NULL == http )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset( http, 0, sizeof( *http ) );
|
||||
http->state = HTTP_STATE_CONSTRUCT;
|
||||
http->lengthtype = HTTP_LENGTH_UNKNOWN;
|
||||
http->host = strdup( host );
|
||||
http->port = port;
|
||||
http->sock = -1;
|
||||
|
||||
if( NULL == http->host || NULL == fmt )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch( method )
|
||||
{
|
||||
case TR_HTTP_GET:
|
||||
methodstr = "GET";
|
||||
break;
|
||||
case TR_HTTP_POST:
|
||||
methodstr = "POST";
|
||||
break;
|
||||
case TR_HTTP_M_POST:
|
||||
methodstr = "M-POST";
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
if( tr_sprintf( EXPANDBUF( http->header ), "%s ", methodstr ) )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
va_start( ap1, fmt );
|
||||
va_start( ap2, fmt );
|
||||
if( tr_vsprintf( EXPANDBUF( http->header ), fmt, ap1, ap2 ) )
|
||||
{
|
||||
va_end( ap2 );
|
||||
va_end( ap1 );
|
||||
goto err;
|
||||
}
|
||||
va_end( ap2 );
|
||||
va_end( ap1 );
|
||||
|
||||
if( tr_sprintf( EXPANDBUF( http->header ), " HTTP/1.1" CR LF
|
||||
"Host: %s" CR LF
|
||||
"User-Agent: Transmission/%d.%d" CR LF
|
||||
"Connection: close" CR LF,
|
||||
http->host, VERSION_MAJOR, VERSION_MINOR ) )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
return http;
|
||||
|
||||
err:
|
||||
tr_httpClose( http );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tr_httpAddHeader( tr_http_t * http, const char * name, const char * value )
|
||||
{
|
||||
if( HTTP_STATE_CONSTRUCT == http->state )
|
||||
{
|
||||
if( tr_sprintf( EXPANDBUF( http->header ),
|
||||
"%s: %s" CR LF, name, value ) )
|
||||
{
|
||||
http->state = HTTP_STATE_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( HTTP_STATE_ERROR == http->state );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tr_httpAddBody( tr_http_t * http , const char * fmt , ... )
|
||||
{
|
||||
va_list ap1, ap2;
|
||||
|
||||
if( HTTP_STATE_CONSTRUCT == http->state )
|
||||
{
|
||||
va_start( ap1, fmt );
|
||||
va_start( ap2, fmt );
|
||||
if( tr_vsprintf( EXPANDBUF( http->body ), fmt, ap1, ap2 ) )
|
||||
{
|
||||
http->state = HTTP_STATE_ERROR;
|
||||
}
|
||||
va_end( ap2 );
|
||||
va_end( ap1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( HTTP_STATE_ERROR == http->state );
|
||||
}
|
||||
}
|
||||
|
||||
tr_tristate_t
|
||||
tr_httpPulse( tr_http_t * http, const char ** data, int * len )
|
||||
{
|
||||
struct in_addr addr;
|
||||
|
||||
switch( http->state )
|
||||
{
|
||||
case HTTP_STATE_CONSTRUCT:
|
||||
if( tr_sprintf( EXPANDBUF( http->header ), "Content-length: %i"
|
||||
CR LF CR LF, http->body.used ) )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
if( !tr_netResolve( http->host, &addr ) )
|
||||
{
|
||||
http->sock = tr_netOpenTCP( addr, htons( http->port ) );
|
||||
http->state = HTTP_STATE_CONNECT;
|
||||
break;
|
||||
}
|
||||
http->resolve = tr_netResolveInit( http->host );
|
||||
if( NULL == http->resolve )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
http->state = HTTP_STATE_RESOLVE;
|
||||
/* fallthrough */
|
||||
|
||||
case HTTP_STATE_RESOLVE:
|
||||
switch( tr_netResolvePulse( http->resolve, &addr ) )
|
||||
{
|
||||
case TR_WAIT:
|
||||
return TR_WAIT;
|
||||
case TR_ERROR:
|
||||
goto err;
|
||||
case TR_OK:
|
||||
tr_netResolveClose( http->resolve );
|
||||
http->resolve = NULL;
|
||||
http->sock = tr_netOpenTCP( addr, htons( http->port ) );
|
||||
http->state = HTTP_STATE_CONNECT;
|
||||
}
|
||||
/* fallthrough */
|
||||
|
||||
case HTTP_STATE_CONNECT:
|
||||
switch( sendrequest( http ) )
|
||||
{
|
||||
case TR_WAIT:
|
||||
return TR_WAIT;
|
||||
case TR_ERROR:
|
||||
goto err;
|
||||
case TR_OK:
|
||||
http->state = HTTP_STATE_RECEIVE;
|
||||
}
|
||||
/* fallthrough */
|
||||
|
||||
case HTTP_STATE_RECEIVE:
|
||||
switch( receiveresponse( http ) )
|
||||
{
|
||||
case TR_WAIT:
|
||||
return TR_WAIT;
|
||||
case TR_ERROR:
|
||||
goto err;
|
||||
case TR_OK:
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_STATE_DONE:
|
||||
goto ok;
|
||||
|
||||
case HTTP_STATE_ERROR:
|
||||
goto err;
|
||||
}
|
||||
|
||||
return TR_WAIT;
|
||||
|
||||
err:
|
||||
http->state = HTTP_STATE_ERROR;
|
||||
return TR_ERROR;
|
||||
|
||||
ok:
|
||||
http->state = HTTP_STATE_DONE;
|
||||
if( NULL != data )
|
||||
{
|
||||
*data = http->header.buf;
|
||||
}
|
||||
if( NULL != len )
|
||||
{
|
||||
*len = http->header.used;
|
||||
}
|
||||
return TR_OK;
|
||||
}
|
||||
|
||||
static tr_tristate_t
|
||||
sendrequest( tr_http_t * http )
|
||||
{
|
||||
struct buf * buf;
|
||||
int ret;
|
||||
|
||||
if( 0 == http->date )
|
||||
{
|
||||
http->date = tr_date();
|
||||
}
|
||||
|
||||
if( 0 > http->sock || tr_date() > http->date + HTTP_TIMEOUT )
|
||||
{
|
||||
return TR_ERROR;
|
||||
}
|
||||
|
||||
buf = ( 0 < http->header.used ? &http->header : &http->body );
|
||||
while( 0 < buf->used )
|
||||
{
|
||||
ret = tr_netSend( http->sock, (uint8_t *) buf->buf, buf->used );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
return TR_ERROR;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
return TR_WAIT;
|
||||
}
|
||||
buf->used = 0;
|
||||
buf = &http->body;
|
||||
}
|
||||
|
||||
free( http->body.buf );
|
||||
http->body.buf = NULL;
|
||||
http->body.size = 0;
|
||||
http->date = 0;
|
||||
|
||||
return TR_OK;
|
||||
}
|
||||
|
||||
static tr_tristate_t
|
||||
receiveresponse( tr_http_t * http )
|
||||
{
|
||||
int ret, before;
|
||||
void * newbuf;
|
||||
|
||||
if( 0 == http->date )
|
||||
{
|
||||
http->date = tr_date();
|
||||
}
|
||||
|
||||
before = http->header.used;
|
||||
for(;;)
|
||||
{
|
||||
if( http->header.size - http->header.used < HTTP_BUFSIZE )
|
||||
{
|
||||
newbuf = realloc( http->header.buf,
|
||||
http->header.size + HTTP_BUFSIZE );
|
||||
if( NULL == newbuf )
|
||||
{
|
||||
return TR_ERROR;
|
||||
}
|
||||
http->header.buf = newbuf;
|
||||
http->header.size += HTTP_BUFSIZE;
|
||||
}
|
||||
|
||||
ret = tr_netRecv( http->sock,
|
||||
(uint8_t *) ( http->header.buf + http->header.used ),
|
||||
http->header.size - http->header.used );
|
||||
if( ret & TR_NET_CLOSE )
|
||||
{
|
||||
checklength( http );
|
||||
return TR_OK;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
http->header.used += ret;
|
||||
}
|
||||
}
|
||||
|
||||
if( before < http->header.used && checklength( http ) )
|
||||
{
|
||||
return TR_OK;
|
||||
}
|
||||
|
||||
if( tr_date() > HTTP_TIMEOUT + http->date )
|
||||
{
|
||||
return TR_ERROR;
|
||||
}
|
||||
|
||||
return TR_WAIT;
|
||||
}
|
||||
|
||||
static int
|
||||
checklength( tr_http_t * http )
|
||||
{
|
||||
char * buf;
|
||||
int num, ii, len;
|
||||
|
||||
switch( http->lengthtype )
|
||||
{
|
||||
case HTTP_LENGTH_UNKNOWN:
|
||||
if( learnlength( http ) )
|
||||
{
|
||||
return checklength( http );
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_LENGTH_EOF:
|
||||
break;
|
||||
|
||||
case HTTP_LENGTH_FIXED:
|
||||
if( http->header.used >= http->chunkoff + http->chunklen )
|
||||
{
|
||||
http->header.used = http->chunkoff + http->chunklen;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_LENGTH_CHUNKED:
|
||||
buf = http->header.buf;
|
||||
while( http->header.used > http->chunkoff + http->chunklen )
|
||||
{
|
||||
num = http->chunkoff + http->chunklen;
|
||||
while( http->header.used > num && NL( buf[num] ) )
|
||||
{
|
||||
num++;
|
||||
}
|
||||
ii = num;
|
||||
while( http->header.used > ii && !NL( buf[ii] ) )
|
||||
{
|
||||
ii++;
|
||||
}
|
||||
if( http->header.used > ii )
|
||||
{
|
||||
/* strtol should stop at the newline */
|
||||
len = strtol( buf + num, NULL, 16 );
|
||||
if( 0 == len )
|
||||
{
|
||||
http->header.used = http->chunkoff + http->chunklen;
|
||||
return 1;
|
||||
}
|
||||
if( http->header.used > ii + 1 )
|
||||
{
|
||||
ii += ( 0 == memcmp( buf + ii, CR LF, 2 ) ? 2 : 1 );
|
||||
if( http->header.used > ii )
|
||||
{
|
||||
memmove( buf + http->chunkoff + http->chunklen,
|
||||
buf + ii, http->header.used - ii );
|
||||
}
|
||||
http->header.used -=
|
||||
ii - ( http->chunkoff + http->chunklen );
|
||||
http->chunkoff += http->chunklen;
|
||||
http->chunklen = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
learnlength( tr_http_t * http )
|
||||
{
|
||||
tr_http_header_t hdr[] = {
|
||||
{ "Content-Length", NULL, 0 },
|
||||
/*
|
||||
XXX this probably doesn't handle multiple encodings correctly
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41
|
||||
*/
|
||||
{ "Transfer-Encoding", NULL, 0 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
const char * body;
|
||||
char * duped;
|
||||
|
||||
body = tr_httpParse( http->header.buf, http->header.used, hdr );
|
||||
if( NULL != body )
|
||||
{
|
||||
if( 0 < hdr[1].len &&
|
||||
0 == tr_strncasecmp( "chunked", hdr[1].data, hdr[1].len ) )
|
||||
{
|
||||
http->lengthtype = HTTP_LENGTH_CHUNKED;
|
||||
http->chunkoff = body - http->header.buf;
|
||||
http->chunklen = 0;
|
||||
}
|
||||
else if( 0 < hdr[0].len )
|
||||
{
|
||||
http->lengthtype = HTTP_LENGTH_FIXED;
|
||||
http->chunkoff = body - http->header.buf;
|
||||
duped = tr_dupstr( hdr[0].data, hdr[0].len );
|
||||
http->chunklen = strtol( duped, NULL, 10 );
|
||||
free( duped );
|
||||
}
|
||||
else
|
||||
{
|
||||
http->lengthtype = HTTP_LENGTH_EOF;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_httpWhatsMyAddress( tr_http_t * http )
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
socklen_t size;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
|
||||
if( 0 > http->sock )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = sizeof( sin );
|
||||
if( 0 > getsockname( http->sock, (struct sockaddr *) &sin, &size ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tr_netNtop( &sin.sin_addr, buf, sizeof( buf ) );
|
||||
|
||||
return strdup( buf );
|
||||
}
|
||||
|
||||
void
|
||||
tr_httpClose( tr_http_t * http )
|
||||
{
|
||||
if( NULL != http->resolve )
|
||||
{
|
||||
tr_netResolveClose( http->resolve );
|
||||
}
|
||||
free( http->host );
|
||||
if( 0 <= http->sock )
|
||||
{
|
||||
tr_netClose( http->sock );
|
||||
}
|
||||
free( http->header.buf );
|
||||
free( http->body.buf );
|
||||
free( http );
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* 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_HTTP_H
|
||||
#define TR_HTTP_H 1
|
||||
|
||||
/*
|
||||
Parse an HTTP request header to find the method, uri, and version.
|
||||
The version will be 11, 10, or -1 on parse error. The method and/or
|
||||
uri pointers may be NULL if the caller is not interested.
|
||||
*/
|
||||
int tr_httpRequestType( const char * data, int len,
|
||||
char ** method, char ** uri );
|
||||
|
||||
/* Return the HTTP status code for the response, or -1 for parse error */
|
||||
int tr_httpResponseCode( const char * data, int len );
|
||||
|
||||
#define TR_HTTP_STATUS_OK( st ) ( 200 <= (st) && 299 >= (st) )
|
||||
#define TR_HTTP_STATUS_REDIRECT( st ) ( 300 <= (st) && 399 >= (st) )
|
||||
#define TR_HTTP_STATUS_FAIL( st ) ( 400 <= (st) && 599 >= (st) )
|
||||
|
||||
/*
|
||||
Parse an HTTP request or response, locating specified headers and
|
||||
the body. The length of the body will be len - ( body - data ).
|
||||
*/
|
||||
typedef struct { const char * name; const char * data; int len; }
|
||||
tr_http_header_t;
|
||||
char * tr_httpParse( const char * data, int len, tr_http_header_t *headers );
|
||||
|
||||
int tr_httpParseUrl( const char *, int, char **, int *, char ** );
|
||||
|
||||
/* fetch a file via HTTP from a standard http:// url */
|
||||
typedef struct tr_http_s tr_http_t;
|
||||
#define TR_HTTP_GET 1
|
||||
#define TR_HTTP_POST 2
|
||||
#define TR_HTTP_M_POST 3
|
||||
tr_http_t * tr_httpClient( int, const char *, int, const char *, ... )
|
||||
PRINTF( 4, 5 );
|
||||
/* only add headers or body before first pulse */
|
||||
void tr_httpAddHeader( tr_http_t *, const char *, const char * );
|
||||
void tr_httpAddBody( tr_http_t *, const char *, ... ) PRINTF( 2, 3 );
|
||||
tr_tristate_t tr_httpPulse( tr_http_t *, const char **, int * );
|
||||
char * tr_httpWhatsMyAddress( tr_http_t * );
|
||||
void tr_httpClose( tr_http_t * );
|
||||
|
||||
#endif
|
|
@ -416,7 +416,7 @@ static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
|
|||
if( i >= inf->fileCount )
|
||||
{
|
||||
/* Should not happen */
|
||||
tr_err( "readOrWriteBytes: offset out of range (%lld, %d, %d)",
|
||||
tr_err( "readOrWriteBytes: offset out of range (%"PRIu64", %d, %d)",
|
||||
offset, size, isWrite );
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
/* Standard headers used here and there.
|
||||
That is probably ugly to put them all here, but it is sooo
|
||||
convenient */
|
||||
#if ( defined( __unix__ ) || defined( unix ) ) && !defined( USG )
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -50,14 +53,21 @@
|
|||
#include <assert.h>
|
||||
#ifdef BEOS_NETSERVER
|
||||
# define in_port_t uint16_t
|
||||
# define socklen_t uint32_t
|
||||
#else
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
#define INADDR_NONE 0xffffffff
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define UNUSED __attribute__((unused))
|
||||
# define PRINTF( fmt, args ) __attribute__((format (printf, fmt, args)))
|
||||
#else
|
||||
# define UNUSED
|
||||
# define PRINTF( fmt, args )
|
||||
#endif
|
||||
|
||||
/* We use OpenSSL whenever possible, since it is likely to be more
|
||||
|
@ -107,6 +117,8 @@ static inline void tr_htonl( uint32_t a, uint8_t * p )
|
|||
|
||||
typedef struct tr_completion_s tr_completion_t;
|
||||
|
||||
typedef enum { TR_OK, TR_ERROR, TR_WAIT } tr_tristate_t;
|
||||
|
||||
#include "platform.h"
|
||||
#include "bencode.h"
|
||||
#include "metainfo.h"
|
||||
|
@ -118,6 +130,10 @@ typedef struct tr_completion_s tr_completion_t;
|
|||
#include "ratecontrol.h"
|
||||
#include "clients.h"
|
||||
#include "choking.h"
|
||||
#include "natpmp.h"
|
||||
#include "upnp.h"
|
||||
#include "http.h"
|
||||
#include "xml.h"
|
||||
|
||||
struct tr_torrent_s
|
||||
{
|
||||
|
@ -187,6 +203,8 @@ struct tr_handle_s
|
|||
tr_ratecontrol_t * download;
|
||||
tr_fd_t * fdlimit;
|
||||
tr_choking_t * choking;
|
||||
tr_natpmp_t * natpmp;
|
||||
tr_upnp_t * upnp;
|
||||
|
||||
int bindPort;
|
||||
int bindSocket;
|
||||
|
|
|
@ -69,7 +69,7 @@ int tr_metainfoParse( tr_info_t * inf, const char * path,
|
|||
}
|
||||
if( sb.st_size > TORRENT_MAX_SIZE )
|
||||
{
|
||||
tr_err( "Torrent file is too big (%d bytes)", sb.st_size );
|
||||
tr_err( "Torrent file is too big (%d bytes)", (int)sb.st_size );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,793 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* 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 PMP_PORT 5351
|
||||
#define PMP_MCAST_ADDR "224.0.0.1"
|
||||
#define PMP_INITIAL_DELAY 250 /* ms, 1/4 second */
|
||||
#define PMP_TOTAL_DELAY 120000 /* ms, 2 minutes */
|
||||
#define PMP_VERSION 0
|
||||
#define PMP_OPCODE_GETIP 0
|
||||
#define PMP_OPCODE_ADDUDP 1
|
||||
#define PMP_OPCODE_ADDTCP 2
|
||||
#define PMP_LIFETIME 3600 /* secs, one hour */
|
||||
#define PMP_RESPCODE_OK 0
|
||||
#define PMP_RESPCODE_BADVERS 1
|
||||
#define PMP_RESPCODE_REFUSED 2
|
||||
#define PMP_RESPCODE_NETDOWN 3
|
||||
#define PMP_RESPCODE_NOMEM 4
|
||||
#define PMP_RESPCODE_BADOPCODE 5
|
||||
|
||||
#define PMP_OPCODE_FROM_RESPONSE( op ) ( 0x80 ^ (op) )
|
||||
#define PMP_OPCODE_TO_RESPONSE( op ) ( 0x80 | (op) )
|
||||
#define PMP_OPCODE_IS_RESPONSE( op ) ( 0x80 & (op) )
|
||||
#define PMP_TOBUF16( buf, num ) ( *( (uint16_t *) (buf) ) = htons( (num) ) )
|
||||
#define PMP_TOBUF32( buf, num ) ( *( (uint32_t *) (buf) ) = htonl( (num) ) )
|
||||
#define PMP_FROMBUF16( buf ) ( htons( *( (uint16_t *) (buf) ) ) )
|
||||
#define PMP_FROMBUF32( buf ) ( htonl( *( (uint32_t *) (buf) ) ) )
|
||||
|
||||
typedef struct tr_natpmp_uptime_s
|
||||
{
|
||||
time_t when;
|
||||
uint32_t uptime;
|
||||
} tr_natpmp_uptime_t;
|
||||
|
||||
typedef struct tr_natpmp_req_s
|
||||
{
|
||||
unsigned int adding : 1;
|
||||
unsigned int nobodyhome : 1;
|
||||
unsigned int tmpfail : 1;
|
||||
int fd;
|
||||
int delay;
|
||||
uint64_t retry;
|
||||
uint64_t timeout;
|
||||
int port;
|
||||
tr_fd_t * fdlimit;
|
||||
tr_natpmp_uptime_t * uptime;
|
||||
} tr_natpmp_req_t;
|
||||
|
||||
struct tr_natpmp_s
|
||||
{
|
||||
#define PMP_STATE_IDLE 1
|
||||
#define PMP_STATE_ADDING 2
|
||||
#define PMP_STATE_DELETING 3
|
||||
#define PMP_STATE_MAPPED 4
|
||||
#define PMP_STATE_FAILED 5
|
||||
#define PMP_STATE_NOBODYHOME 6
|
||||
#define PMP_STATE_TMPFAIL 7
|
||||
char state;
|
||||
unsigned int active : 1;
|
||||
struct in_addr dest;
|
||||
int newport;
|
||||
int mappedport;
|
||||
tr_fd_t * fdlimit;
|
||||
tr_lock_t lock;
|
||||
uint64_t renew;
|
||||
tr_natpmp_req_t * req;
|
||||
tr_natpmp_uptime_t uptime;
|
||||
int mcastfd;
|
||||
};
|
||||
|
||||
static int
|
||||
checktime( tr_natpmp_uptime_t * uptime, uint32_t seen );
|
||||
static void
|
||||
killsock( int * fd, tr_fd_t * fdlimit );
|
||||
static tr_natpmp_req_t *
|
||||
newreq( int adding, struct in_addr addr, int port, tr_fd_t * fdlimit,
|
||||
tr_natpmp_uptime_t * uptime );
|
||||
static tr_tristate_t
|
||||
pulsereq( tr_natpmp_req_t * req, uint64_t * renew );
|
||||
static int
|
||||
mcastsetup( tr_fd_t * fdlimit );
|
||||
static void
|
||||
mcastpulse( tr_natpmp_t * pmp );
|
||||
static void
|
||||
killreq( tr_natpmp_req_t ** req );
|
||||
static int
|
||||
sendrequest( int adding, int fd, int port );
|
||||
static tr_tristate_t
|
||||
readrequest( uint8_t * buf, int len, int adding, int port,
|
||||
tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail );
|
||||
|
||||
tr_natpmp_t *
|
||||
tr_natpmpInit( tr_fd_t * fdlimit )
|
||||
{
|
||||
tr_natpmp_t * pmp;
|
||||
|
||||
pmp = calloc( 1, sizeof( *pmp ) );
|
||||
if( NULL == pmp )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
pmp->fdlimit = fdlimit;
|
||||
pmp->mcastfd = -1;
|
||||
|
||||
if( tr_getDefaultRoute( &pmp->dest ) || INADDR_ANY == pmp->dest.s_addr )
|
||||
{
|
||||
pmp->dest.s_addr = INADDR_NONE;
|
||||
}
|
||||
|
||||
if( INADDR_NONE == pmp->dest.s_addr )
|
||||
{
|
||||
tr_dbg( "nat-pmp device is unknown" );
|
||||
}
|
||||
else
|
||||
{
|
||||
char addrstr[INET_ADDRSTRLEN];
|
||||
tr_netNtop( &pmp->dest, addrstr, sizeof( addrstr ) );
|
||||
tr_dbg( "nat-pmp device is %s", addrstr );
|
||||
}
|
||||
|
||||
tr_lockInit( &pmp->lock );
|
||||
|
||||
return pmp;
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpStart( tr_natpmp_t * pmp )
|
||||
{
|
||||
tr_lockLock( &pmp->lock );
|
||||
|
||||
if( !pmp->active )
|
||||
{
|
||||
tr_inf( "starting nat-pmp" );
|
||||
pmp->active = 1;
|
||||
if( 0 > pmp->mcastfd )
|
||||
{
|
||||
pmp->mcastfd = mcastsetup( pmp->fdlimit );
|
||||
}
|
||||
/* XXX should I change state? */
|
||||
}
|
||||
|
||||
tr_lockUnlock( &pmp->lock );
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpStop( tr_natpmp_t * pmp )
|
||||
{
|
||||
tr_lockLock( &pmp->lock );
|
||||
|
||||
if( pmp->active )
|
||||
{
|
||||
tr_inf( "stopping nat-pmp" );
|
||||
pmp->active = 0;
|
||||
killsock( &pmp->mcastfd, pmp->fdlimit );
|
||||
switch( pmp->state )
|
||||
{
|
||||
case PMP_STATE_IDLE:
|
||||
break;
|
||||
case PMP_STATE_ADDING:
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
tr_dbg( "nat-pmp state add -> idle" );
|
||||
if( NULL != pmp->req )
|
||||
{
|
||||
killreq( &pmp->req );
|
||||
pmp->mappedport = pmp->req->port;
|
||||
pmp->state = PMP_STATE_DELETING;
|
||||
tr_dbg( "nat-pmp state idle -> del" );
|
||||
}
|
||||
break;
|
||||
case PMP_STATE_DELETING:
|
||||
break;
|
||||
case PMP_STATE_MAPPED:
|
||||
pmp->state = PMP_STATE_DELETING;
|
||||
tr_dbg( "nat-pmp state mapped -> del" );
|
||||
break;
|
||||
case PMP_STATE_FAILED:
|
||||
case PMP_STATE_NOBODYHOME:
|
||||
case PMP_STATE_TMPFAIL:
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tr_lockUnlock( &pmp->lock );
|
||||
}
|
||||
|
||||
int
|
||||
tr_natpmpStatus( tr_natpmp_t * pmp )
|
||||
{
|
||||
int ret;
|
||||
|
||||
tr_lockLock( &pmp->lock );
|
||||
|
||||
|
||||
if( !pmp->active )
|
||||
{
|
||||
ret = ( PMP_STATE_DELETING == pmp->state ?
|
||||
TR_NAT_TRAVERSAL_UNMAPPING : TR_NAT_TRAVERSAL_DISABLED );
|
||||
}
|
||||
else if( 0 < pmp->mappedport )
|
||||
{
|
||||
ret = TR_NAT_TRAVERSAL_MAPPED;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( pmp->state )
|
||||
{
|
||||
case PMP_STATE_IDLE:
|
||||
case PMP_STATE_ADDING:
|
||||
case PMP_STATE_DELETING:
|
||||
ret = TR_NAT_TRAVERSAL_MAPPING;
|
||||
break;
|
||||
case PMP_STATE_FAILED:
|
||||
case PMP_STATE_TMPFAIL:
|
||||
ret = TR_NAT_TRAVERSAL_ERROR;
|
||||
break;
|
||||
case PMP_STATE_NOBODYHOME:
|
||||
ret = TR_NAT_TRAVERSAL_NOTFOUND;
|
||||
break;
|
||||
case PMP_STATE_MAPPED:
|
||||
default:
|
||||
assert( 0 );
|
||||
ret = TR_NAT_TRAVERSAL_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tr_lockUnlock( &pmp->lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpForwardPort( tr_natpmp_t * pmp, int port )
|
||||
{
|
||||
tr_lockLock( &pmp->lock );
|
||||
tr_inf( "nat-pmp set port %i", port );
|
||||
pmp->newport = port;
|
||||
tr_lockUnlock( &pmp->lock );
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpClose( tr_natpmp_t * pmp )
|
||||
{
|
||||
/* try to send at least one delete request if we have a port mapping */
|
||||
tr_natpmpStop( pmp );
|
||||
tr_natpmpPulse( pmp );
|
||||
|
||||
tr_lockLock( &pmp->lock );
|
||||
killreq( &pmp->req );
|
||||
tr_lockClose( &pmp->lock );
|
||||
free( pmp );
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpPulse( tr_natpmp_t * pmp )
|
||||
{
|
||||
tr_lockLock( &pmp->lock );
|
||||
|
||||
if( 0 <= pmp->mcastfd )
|
||||
{
|
||||
mcastpulse( pmp );
|
||||
}
|
||||
|
||||
if( pmp->active || PMP_STATE_DELETING == pmp->state )
|
||||
{
|
||||
switch( pmp->state )
|
||||
{
|
||||
case PMP_STATE_IDLE:
|
||||
case PMP_STATE_TMPFAIL:
|
||||
if( 0 < pmp->newport )
|
||||
{
|
||||
tr_dbg( "nat-pmp state %s -> add with port %i",
|
||||
( PMP_STATE_IDLE == pmp->state ? "idle" : "err" ),
|
||||
pmp->newport );
|
||||
pmp->state = PMP_STATE_ADDING;
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_ADDING:
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
if( 0 >= pmp->newport )
|
||||
{
|
||||
tr_dbg( "nat-pmp state add -> idle, no port" );
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
}
|
||||
else if( INADDR_NONE == pmp->dest.s_addr )
|
||||
{
|
||||
tr_dbg( "nat-pmp state add -> fail, no default route" );
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmp->req = newreq( 1, pmp->dest, pmp->newport,
|
||||
pmp->fdlimit, &pmp->uptime );
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state add -> fail on req init" );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( PMP_STATE_ADDING == pmp->state )
|
||||
{
|
||||
switch( pulsereq( pmp->req, &pmp->renew ) )
|
||||
{
|
||||
case TR_ERROR:
|
||||
if( pmp->req->nobodyhome )
|
||||
{
|
||||
pmp->state = PMP_STATE_NOBODYHOME;
|
||||
tr_dbg( "nat-pmp state add -> nobodyhome on pulse" );
|
||||
}
|
||||
else if( pmp->req->tmpfail )
|
||||
{
|
||||
pmp->state = PMP_STATE_TMPFAIL;
|
||||
tr_dbg( "nat-pmp state add -> err on pulse" );
|
||||
if( pmp->req->port == pmp->newport )
|
||||
{
|
||||
pmp->newport = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state add -> fail on pulse" );
|
||||
}
|
||||
killreq( &pmp->req );
|
||||
break;
|
||||
case TR_OK:
|
||||
pmp->mappedport = pmp->req->port;
|
||||
killreq( &pmp->req );
|
||||
pmp->state = PMP_STATE_MAPPED;
|
||||
tr_dbg( "nat-pmp state add -> mapped with port %i",
|
||||
pmp->mappedport);
|
||||
tr_inf( "nat-pmp mapped port %i", pmp->mappedport );
|
||||
break;
|
||||
case TR_WAIT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_DELETING:
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
assert( 0 < pmp->mappedport );
|
||||
pmp->req = newreq( 0, pmp->dest, pmp->mappedport,
|
||||
pmp->fdlimit, &pmp->uptime );
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state del -> fail on req init" );
|
||||
}
|
||||
}
|
||||
if( PMP_STATE_DELETING == pmp->state )
|
||||
{
|
||||
switch( pulsereq( pmp->req, &pmp->renew ) )
|
||||
{
|
||||
case TR_ERROR:
|
||||
if( pmp->req->nobodyhome )
|
||||
{
|
||||
pmp->state = PMP_STATE_NOBODYHOME;
|
||||
tr_dbg( "nat-pmp state del -> nobodyhome on pulse" );
|
||||
}
|
||||
else if( pmp->req->tmpfail )
|
||||
{
|
||||
pmp->state = PMP_STATE_TMPFAIL;
|
||||
tr_dbg( "nat-pmp state del -> err on pulse" );
|
||||
pmp->mappedport = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state del -> fail on pulse" );
|
||||
}
|
||||
killreq( &pmp->req );
|
||||
break;
|
||||
case TR_OK:
|
||||
tr_dbg( "nat-pmp state del -> idle with port %i",
|
||||
pmp->req->port);
|
||||
tr_inf( "nat-pmp unmapped port %i", pmp->req->port );
|
||||
pmp->mappedport = -1;
|
||||
killreq( &pmp->req );
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
break;
|
||||
case TR_WAIT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_MAPPED:
|
||||
if( pmp->newport != pmp->mappedport )
|
||||
{
|
||||
tr_dbg( "nat-pmp state mapped -> del, port from %i to %i",
|
||||
pmp->mappedport, pmp->newport );
|
||||
pmp->state = PMP_STATE_DELETING;
|
||||
}
|
||||
else if( tr_date() > pmp->renew )
|
||||
{
|
||||
pmp->state = PMP_STATE_ADDING;
|
||||
tr_dbg( "nat-pmp state mapped -> add for renewal" );
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_FAILED:
|
||||
case PMP_STATE_NOBODYHOME:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tr_lockUnlock( &pmp->lock );
|
||||
}
|
||||
|
||||
static int
|
||||
checktime( tr_natpmp_uptime_t * uptime, uint32_t cursecs )
|
||||
{
|
||||
time_t now;
|
||||
int ret;
|
||||
uint32_t estimated;
|
||||
|
||||
now = time( NULL );
|
||||
ret = 0;
|
||||
if( 0 < uptime->when )
|
||||
{
|
||||
estimated = ( ( now - uptime->when ) * 7 / 8 ) + uptime->uptime;
|
||||
if( estimated > cursecs )
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
uptime->when = now;
|
||||
uptime->uptime = cursecs;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
killsock( int * fd, tr_fd_t * fdlimit )
|
||||
{
|
||||
if( 0 <= *fd )
|
||||
{
|
||||
tr_netClose( *fd );
|
||||
*fd = -1;
|
||||
tr_fdSocketClosed( fdlimit, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
static tr_natpmp_req_t *
|
||||
newreq( int adding, struct in_addr addr, int port, tr_fd_t * fdlimit,
|
||||
tr_natpmp_uptime_t * uptime )
|
||||
{
|
||||
tr_natpmp_req_t * ret;
|
||||
uint64_t now;
|
||||
|
||||
ret = calloc( 1, sizeof( *ret ) );
|
||||
if( NULL == ret )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
ret->fd = -1;
|
||||
if( tr_fdSocketWillCreate( fdlimit, 0 ) )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
ret->fd = tr_netOpenUDP( addr, htons( PMP_PORT ) );
|
||||
if( 0 > ret->fd )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
if( sendrequest( adding, ret->fd, port ) )
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
now = tr_date();
|
||||
ret->adding = adding;
|
||||
ret->delay = PMP_INITIAL_DELAY;
|
||||
ret->retry = now + PMP_INITIAL_DELAY;
|
||||
ret->timeout = now + PMP_TOTAL_DELAY;
|
||||
ret->port = port;
|
||||
ret->fdlimit = fdlimit;
|
||||
ret->uptime = uptime;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if( NULL != ret )
|
||||
{
|
||||
killsock( &ret->fd, fdlimit );
|
||||
}
|
||||
free( ret );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static tr_tristate_t
|
||||
pulsereq( tr_natpmp_req_t * req, uint64_t * renew )
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
uint8_t buf[16];
|
||||
int res, tmpfail;
|
||||
uint64_t now;
|
||||
tr_tristate_t ret;
|
||||
|
||||
now = tr_date();
|
||||
|
||||
if( now >= req->timeout )
|
||||
{
|
||||
tr_dbg( "nat-pmp request timed out" );
|
||||
req->nobodyhome = 1;
|
||||
return TR_ERROR;
|
||||
}
|
||||
|
||||
if( now >= req->retry )
|
||||
{
|
||||
if( sendrequest( req->adding, req->fd, req->port ) )
|
||||
{
|
||||
return TR_ERROR;
|
||||
}
|
||||
req->delay *= 2;
|
||||
req->timeout = now + req->delay;
|
||||
}
|
||||
|
||||
res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin );
|
||||
if( TR_NET_BLOCK & res )
|
||||
{
|
||||
return TR_WAIT;
|
||||
}
|
||||
else if( TR_NET_CLOSE & res )
|
||||
{
|
||||
if( ECONNRESET == errno || ECONNREFUSED == errno )
|
||||
{
|
||||
tr_dbg( "nat-pmp not supported by device" );
|
||||
req->nobodyhome = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_inf( "error reading nat-pmp response (%s)", strerror( errno ) );
|
||||
}
|
||||
return TR_ERROR;
|
||||
}
|
||||
|
||||
tr_dbg( "nat-pmp read %i byte response", res );
|
||||
|
||||
ret = readrequest( buf, res, req->adding, req->port, req->uptime, renew,
|
||||
&tmpfail );
|
||||
req->tmpfail = ( tmpfail ? 1 : 0 );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mcastsetup( tr_fd_t * fdlimit )
|
||||
{
|
||||
int fd;
|
||||
struct in_addr addr;
|
||||
|
||||
if( tr_fdSocketWillCreate( fdlimit, 0 ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.s_addr = inet_addr( PMP_MCAST_ADDR );
|
||||
fd = tr_netMcastOpen( PMP_PORT, addr );
|
||||
if( 0 > fd )
|
||||
{
|
||||
tr_fdSocketClosed( fdlimit, 0 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
tr_dbg( "nat-pmp create multicast socket %i", fd );
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
mcastpulse( tr_natpmp_t * pmp )
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
uint8_t buf[16];
|
||||
int res;
|
||||
char dbgstr[INET_ADDRSTRLEN];
|
||||
|
||||
res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin );
|
||||
if( TR_NET_BLOCK & res )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if( TR_NET_CLOSE & res )
|
||||
{
|
||||
tr_err( "error reading nat-pmp multicast message" );
|
||||
killsock( &pmp->mcastfd, pmp->fdlimit );
|
||||
return;
|
||||
}
|
||||
|
||||
tr_netNtop( &sin.sin_addr, dbgstr, sizeof( dbgstr ) );
|
||||
tr_dbg( "nat-pmp read %i byte multicast packet from %s", res, dbgstr );
|
||||
|
||||
if( pmp->dest.s_addr != sin.sin_addr.s_addr )
|
||||
{
|
||||
tr_dbg( "nat-pmp ignoring multicast packet from unknown host %s",
|
||||
dbgstr );
|
||||
return;
|
||||
}
|
||||
|
||||
if( TR_OK == readrequest( buf, res, 0, -1, &pmp->uptime, &pmp->renew, NULL ) &&
|
||||
PMP_STATE_NOBODYHOME == pmp->state )
|
||||
{
|
||||
tr_dbg( "nat-pmp state notfound -> idle" );
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
killreq( tr_natpmp_req_t ** req )
|
||||
{
|
||||
if( NULL != *req )
|
||||
{
|
||||
killsock( &(*req)->fd, (*req)->fdlimit );
|
||||
free( *req );
|
||||
*req = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sendrequest( int adding, int fd, int port )
|
||||
{
|
||||
uint8_t buf[12];
|
||||
int res;
|
||||
|
||||
buf[0] = PMP_VERSION;
|
||||
buf[1] = PMP_OPCODE_ADDTCP;
|
||||
buf[2] = 0;
|
||||
buf[3] = 0;
|
||||
PMP_TOBUF16( buf + 4, port );
|
||||
if( adding )
|
||||
{
|
||||
PMP_TOBUF16( buf + 6, port );
|
||||
PMP_TOBUF32( buf + 8, PMP_LIFETIME );
|
||||
}
|
||||
else
|
||||
{
|
||||
PMP_TOBUF16( buf + 6, 0 );
|
||||
PMP_TOBUF32( buf + 8, 0 );
|
||||
}
|
||||
|
||||
res = tr_netSend( fd, buf, sizeof( buf ) );
|
||||
/* XXX is it all right to assume the entire thing is written? */
|
||||
|
||||
/* XXX I should handle blocking here */
|
||||
|
||||
return ( ( TR_NET_CLOSE | TR_NET_BLOCK ) & res ? 1 : 0 );
|
||||
}
|
||||
|
||||
static tr_tristate_t
|
||||
readrequest( uint8_t * buf, int len, int adding, int port,
|
||||
tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail )
|
||||
{
|
||||
uint8_t version, opcode, wantedopcode;
|
||||
uint16_t rescode, privport, pubport;
|
||||
uint32_t seconds, lifetime;
|
||||
|
||||
assert( !adding || NULL != tmpfail );
|
||||
if( NULL != tmpfail )
|
||||
{
|
||||
*tmpfail = 0;
|
||||
}
|
||||
if( 4 > len )
|
||||
{
|
||||
tr_err( "read truncated %i byte nat-pmp response packet", len );
|
||||
return TR_ERROR;
|
||||
}
|
||||
version = buf[0];
|
||||
opcode = buf[1];
|
||||
rescode = PMP_FROMBUF16( buf + 2 );
|
||||
wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP );
|
||||
|
||||
if( !PMP_OPCODE_IS_RESPONSE( opcode ) )
|
||||
{
|
||||
tr_dbg( "nat-pmp ignoring request packet" );
|
||||
return TR_WAIT;
|
||||
}
|
||||
opcode = PMP_OPCODE_FROM_RESPONSE( opcode );
|
||||
|
||||
if( PMP_VERSION != version )
|
||||
{
|
||||
tr_err( "bad nat-pmp version %hhu", buf[0] );
|
||||
return TR_ERROR;
|
||||
}
|
||||
if( wantedopcode != opcode )
|
||||
{
|
||||
tr_err( "bad nat-pmp opcode %hhu", opcode );
|
||||
return TR_ERROR;
|
||||
}
|
||||
switch( rescode )
|
||||
{
|
||||
case PMP_RESPCODE_OK:
|
||||
break;
|
||||
case PMP_RESPCODE_REFUSED:
|
||||
case PMP_RESPCODE_NETDOWN:
|
||||
case PMP_RESPCODE_NOMEM:
|
||||
if( NULL != tmpfail )
|
||||
{
|
||||
*tmpfail = 1;
|
||||
}
|
||||
/* fallthrough */
|
||||
default:
|
||||
tr_err( "bad nat-pmp result code %hu", rescode );
|
||||
return TR_ERROR;
|
||||
}
|
||||
|
||||
if( 8 > len )
|
||||
{
|
||||
tr_err( "read truncated %i byte nat-pmp response packet", len );
|
||||
return TR_ERROR;
|
||||
}
|
||||
seconds = PMP_FROMBUF32( buf + 4 );
|
||||
|
||||
if( checktime( uptime, seconds ) )
|
||||
{
|
||||
*renew = 0;
|
||||
tr_inf( "detected nat-pmp device reset" );
|
||||
/* XXX should reset retry counter here */
|
||||
return TR_WAIT;
|
||||
}
|
||||
|
||||
if( 0 <= port )
|
||||
{
|
||||
assert( PMP_OPCODE_ADDTCP == wantedopcode );
|
||||
if( 16 > len )
|
||||
{
|
||||
tr_err( "read truncated %i byte nat-pmp response packet", len );
|
||||
return TR_ERROR;
|
||||
}
|
||||
privport = PMP_FROMBUF16( buf + 8 );
|
||||
pubport = PMP_FROMBUF16( buf + 10 );
|
||||
lifetime = PMP_FROMBUF32( buf + 12 );
|
||||
|
||||
if( port != privport )
|
||||
{
|
||||
/* private port doesn't match, ignore it */
|
||||
tr_dbg( "nat-pmp ignoring message for port %i, expected port %i",
|
||||
privport, port );
|
||||
return TR_WAIT;
|
||||
}
|
||||
|
||||
if( adding )
|
||||
{
|
||||
if( port != pubport )
|
||||
{
|
||||
*tmpfail = 1;
|
||||
/* XXX should just start announcing the pub port we're given */
|
||||
return TR_ERROR;
|
||||
}
|
||||
tr_dbg( "nat-pmp set renew to half of %u", lifetime );
|
||||
*renew = tr_date() + ( lifetime / 2 * 1000 );
|
||||
}
|
||||
}
|
||||
|
||||
return TR_OK;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* 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_NATPMP_H
|
||||
#define TR_NATPMP_H 1
|
||||
|
||||
typedef struct tr_natpmp_s tr_natpmp_t;
|
||||
|
||||
tr_natpmp_t * tr_natpmpInit( tr_fd_t * );
|
||||
void tr_natpmpStart( tr_natpmp_t * );
|
||||
void tr_natpmpStop( tr_natpmp_t * );
|
||||
int tr_natpmpStatus( tr_natpmp_t * );
|
||||
void tr_natpmpForwardPort( tr_natpmp_t *, int );
|
||||
void tr_natpmpPulse( tr_natpmp_t * );
|
||||
void tr_natpmpClose( tr_natpmp_t * );
|
||||
|
||||
#endif
|
|
@ -35,7 +35,7 @@
|
|||
* representing numbers expressed in the Internet standard `.' notation.
|
||||
* Returns a non-zero value if an error occurs.
|
||||
**********************************************************************/
|
||||
int tr_netResolve( char * address, struct in_addr * addr )
|
||||
int tr_netResolve( const char * address, struct in_addr * addr )
|
||||
{
|
||||
addr->s_addr = inet_addr( address );
|
||||
return ( addr->s_addr == 0xFFFFFFFF );
|
||||
|
@ -52,7 +52,7 @@ static void resolveFunc ( void * );
|
|||
|
||||
struct tr_resolve_s
|
||||
{
|
||||
int status;
|
||||
tr_tristate_t status;
|
||||
char * address;
|
||||
struct in_addr addr;
|
||||
|
||||
|
@ -94,12 +94,12 @@ void tr_netResolveThreadClose()
|
|||
***********************************************************************
|
||||
* Adds an address to the resolution queue.
|
||||
**********************************************************************/
|
||||
tr_resolve_t * tr_netResolveInit( char * address )
|
||||
tr_resolve_t * tr_netResolveInit( const char * address )
|
||||
{
|
||||
tr_resolve_t * r;
|
||||
|
||||
r = malloc( sizeof( tr_resolve_t ) );
|
||||
r->status = TR_RESOLVE_WAIT;
|
||||
r->status = TR_WAIT;
|
||||
r->address = strdup( address );
|
||||
r->refcount = 2;
|
||||
r->next = NULL;
|
||||
|
@ -125,13 +125,13 @@ tr_resolve_t * tr_netResolveInit( char * address )
|
|||
***********************************************************************
|
||||
* Checks the current status of a resolution.
|
||||
**********************************************************************/
|
||||
int tr_netResolvePulse( tr_resolve_t * r, struct in_addr * addr )
|
||||
tr_tristate_t tr_netResolvePulse( tr_resolve_t * r, struct in_addr * addr )
|
||||
{
|
||||
int ret;
|
||||
tr_tristate_t ret;
|
||||
|
||||
tr_lockLock( &resolveLock );
|
||||
ret = r->status;
|
||||
if( ret == TR_RESOLVE_OK )
|
||||
if( ret == TR_OK )
|
||||
{
|
||||
*addr = r->addr;
|
||||
}
|
||||
|
@ -201,11 +201,11 @@ static void resolveFunc( void * arg UNUSED )
|
|||
if( host )
|
||||
{
|
||||
memcpy( &r->addr, host->h_addr, host->h_length );
|
||||
r->status = TR_RESOLVE_OK;
|
||||
r->status = TR_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
r->status = TR_RESOLVE_ERROR;
|
||||
r->status = TR_ERROR;
|
||||
}
|
||||
|
||||
resolveQueue = r->next;
|
||||
|
@ -251,11 +251,11 @@ static int makeSocketNonBlocking( int s )
|
|||
return s;
|
||||
}
|
||||
|
||||
static int createSocket()
|
||||
static int createSocket( int type )
|
||||
{
|
||||
int s;
|
||||
|
||||
s = socket( AF_INET, SOCK_STREAM, 0 );
|
||||
s = socket( AF_INET, type, 0 );
|
||||
if( s < 0 )
|
||||
{
|
||||
tr_err( "Could not create socket (%s)", strerror( errno ) );
|
||||
|
@ -265,12 +265,12 @@ static int createSocket()
|
|||
return makeSocketNonBlocking( s );
|
||||
}
|
||||
|
||||
int tr_netOpen( struct in_addr addr, in_port_t port )
|
||||
int tr_netOpen( struct in_addr addr, in_port_t port, int type )
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_in sock;
|
||||
|
||||
s = createSocket();
|
||||
s = createSocket( type );
|
||||
if( s < 0 )
|
||||
{
|
||||
return -1;
|
||||
|
@ -293,15 +293,46 @@ int tr_netOpen( struct in_addr addr, in_port_t port )
|
|||
return s;
|
||||
}
|
||||
|
||||
int tr_netBind( int port )
|
||||
#ifdef IP_ADD_MEMBERSHIP
|
||||
int tr_netMcastOpen( int port, struct in_addr addr )
|
||||
{
|
||||
int fd;
|
||||
struct ip_mreq req;
|
||||
|
||||
fd = tr_netBindUDP( port );
|
||||
if( 0 > fd )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset( &req, 0, sizeof( req ) );
|
||||
req.imr_multiaddr.s_addr = addr.s_addr;
|
||||
req.imr_interface.s_addr = htonl( INADDR_ANY );
|
||||
if( setsockopt( fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof ( req ) ) )
|
||||
{
|
||||
tr_err( "Could not join multicast group (%s)", strerror( errno ) );
|
||||
tr_netClose( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
#else /* IP_ADD_MEMBERSHIP */
|
||||
int tr_netMcastOpen( int port UNUSED, struct in_addr addr UNUSED )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif /* IP_ADD_MEMBERSHIP */
|
||||
|
||||
int tr_netBind( int port, int type )
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_in sock;
|
||||
#ifdef SO_REUSEADDR
|
||||
#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
|
||||
int optval;
|
||||
#endif
|
||||
|
||||
s = createSocket();
|
||||
s = createSocket( type );
|
||||
if( s < 0 )
|
||||
{
|
||||
return -1;
|
||||
|
@ -312,6 +343,14 @@ int tr_netBind( int port )
|
|||
setsockopt( s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof( optval ) );
|
||||
#endif
|
||||
|
||||
#ifdef SO_REUSEPORT
|
||||
if( SOCK_DGRAM == type )
|
||||
{
|
||||
optval = 1;
|
||||
setsockopt( s, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof( optval ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
memset( &sock, 0, sizeof( sock ) );
|
||||
sock.sin_family = AF_INET;
|
||||
sock.sin_addr.s_addr = INADDR_ANY;
|
||||
|
@ -324,9 +363,6 @@ int tr_netBind( int port )
|
|||
tr_netClose( s );
|
||||
return -1;
|
||||
}
|
||||
|
||||
tr_inf( "Binded port %d", port );
|
||||
listen( s, 5 );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -371,11 +407,13 @@ int tr_netSend( int s, uint8_t * buf, int size )
|
|||
return ret;
|
||||
}
|
||||
|
||||
int tr_netRecv( int s, uint8_t * buf, int size )
|
||||
int tr_netRecvFrom( int s, uint8_t * buf, int size, struct sockaddr_in * addr )
|
||||
{
|
||||
int ret;
|
||||
socklen_t len;
|
||||
int ret;
|
||||
|
||||
ret = recv( s, buf, size, 0 );
|
||||
len = ( NULL == addr ? 0 : sizeof( *addr ) );
|
||||
ret = recvfrom( s, buf, size, 0, ( struct sockaddr * ) addr, &len );
|
||||
if( ret < 0 )
|
||||
{
|
||||
if( errno == EAGAIN || errno == EWOULDBLOCK )
|
||||
|
|
|
@ -26,30 +26,33 @@
|
|||
/***********************************************************************
|
||||
* DNS resolution
|
||||
**********************************************************************/
|
||||
int tr_netResolve( char *, struct in_addr * );
|
||||
int tr_netResolve( const char *, struct in_addr * );
|
||||
|
||||
#define TR_RESOLVE_WAIT 0
|
||||
#define TR_RESOLVE_ERROR 1
|
||||
#define TR_RESOLVE_OK 2
|
||||
typedef struct tr_resolve_s tr_resolve_t;
|
||||
void tr_netResolveThreadInit();
|
||||
void tr_netResolveThreadClose();
|
||||
tr_resolve_t * tr_netResolveInit( char * );
|
||||
int tr_netResolvePulse( tr_resolve_t *, struct in_addr * );
|
||||
tr_resolve_t * tr_netResolveInit( const char * );
|
||||
tr_tristate_t tr_netResolvePulse( tr_resolve_t *, struct in_addr * );
|
||||
void tr_netResolveClose( tr_resolve_t * );
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* TCP sockets
|
||||
**********************************************************************/
|
||||
int tr_netOpen ( struct in_addr addr, in_port_t port );
|
||||
int tr_netBind ( int );
|
||||
#define tr_netOpenTCP( addr, port ) tr_netOpen( (addr), (port), SOCK_STREAM )
|
||||
#define tr_netOpenUDP( addr, port ) tr_netOpen( (addr), (port), SOCK_DGRAM )
|
||||
int tr_netOpen ( struct in_addr addr, in_port_t port, int type );
|
||||
int tr_netMcastOpen( int port, struct in_addr addr );
|
||||
#define tr_netBindTCP( port ) tr_netBind( (port), SOCK_STREAM )
|
||||
#define tr_netBindUDP( port ) tr_netBind( (port), SOCK_DGRAM )
|
||||
int tr_netBind ( int port, int type );
|
||||
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 );
|
||||
#define tr_netRecv( s, buf, size ) tr_netRecvFrom( (s), (buf), (size), NULL )
|
||||
int tr_netRecvFrom( int s, uint8_t * buf, int size, struct sockaddr_in * );
|
||||
|
||||
void tr_netNtop( const struct in_addr * addr, char * buf, int len );
|
||||
|
|
|
@ -100,6 +100,8 @@ struct tr_peer_s
|
|||
};
|
||||
|
||||
#define peer_dbg( a... ) __peer_dbg( peer, ## a )
|
||||
static void __peer_dbg( tr_peer_t * peer, char * msg, ... ) PRINTF( 2, 3 );
|
||||
|
||||
static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
|
||||
{
|
||||
char string[256];
|
||||
|
|
|
@ -151,7 +151,7 @@ static int checkPeer( tr_torrent_t * tor, int i )
|
|||
if( ( peer->status & PEER_STATUS_IDLE ) &&
|
||||
!tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
|
||||
{
|
||||
peer->socket = tr_netOpen( peer->addr, peer->port );
|
||||
peer->socket = tr_netOpenTCP( peer->addr, peer->port );
|
||||
if( peer->socket < 0 )
|
||||
{
|
||||
peer_dbg( "connection failed" );
|
||||
|
|
|
@ -196,3 +196,371 @@ void tr_lockClose( tr_lock_t * l )
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined( BSD )
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <net/route.h>
|
||||
|
||||
static uint8_t *
|
||||
getroute( int * buflen );
|
||||
static int
|
||||
parseroutes( uint8_t * buf, int len, struct in_addr * addr );
|
||||
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr )
|
||||
{
|
||||
uint8_t * buf;
|
||||
int len;
|
||||
|
||||
buf = getroute( &len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
tr_err( "failed to get default route (BSD)" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
len = parseroutes( buf, len, addr );
|
||||
free( buf );
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifndef SA_SIZE
|
||||
#define ROUNDUP( a, size ) \
|
||||
( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
|
||||
#define SA_SIZE( sap ) \
|
||||
( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
|
||||
sizeof( u_long ) )
|
||||
#endif /* !SA_SIZE */
|
||||
#define NEXT_SA( sap ) \
|
||||
(struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
|
||||
|
||||
static uint8_t *
|
||||
getroute( int * buflen )
|
||||
{
|
||||
int mib[6];
|
||||
size_t len;
|
||||
uint8_t * buf;
|
||||
|
||||
mib[0] = CTL_NET;
|
||||
mib[1] = PF_ROUTE;
|
||||
mib[2] = 0;
|
||||
mib[3] = AF_INET;
|
||||
mib[4] = NET_RT_FLAGS;
|
||||
mib[5] = RTF_GATEWAY;
|
||||
|
||||
if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
|
||||
{
|
||||
if( ENOENT != errno )
|
||||
{
|
||||
tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
|
||||
strerror( errno ) );
|
||||
}
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = malloc( len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
|
||||
{
|
||||
tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
|
||||
strerror( errno ) );
|
||||
free( buf );
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*buflen = len;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
parseroutes( uint8_t * buf, int len, struct in_addr * addr )
|
||||
{
|
||||
uint8_t * end;
|
||||
struct rt_msghdr * rtm;
|
||||
struct sockaddr * sa;
|
||||
struct sockaddr_in * sin;
|
||||
int ii;
|
||||
struct in_addr dest, gw;
|
||||
|
||||
end = buf + len;
|
||||
while( end > buf + sizeof( *rtm ) )
|
||||
{
|
||||
rtm = (struct rt_msghdr *) buf;
|
||||
buf += rtm->rtm_msglen;
|
||||
if( end >= buf )
|
||||
{
|
||||
dest.s_addr = INADDR_NONE;
|
||||
gw.s_addr = INADDR_NONE;
|
||||
sa = (struct sockaddr *) ( rtm + 1 );
|
||||
|
||||
for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
|
||||
{
|
||||
if( buf < (uint8_t *) NEXT_SA( sa ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( rtm->rtm_addrs & ( 1 << ii ) )
|
||||
{
|
||||
if( AF_INET == sa->sa_family )
|
||||
{
|
||||
sin = (struct sockaddr_in *) sa;
|
||||
switch( ii )
|
||||
{
|
||||
case RTAX_DST:
|
||||
dest = sin->sin_addr;
|
||||
break;
|
||||
case RTAX_GATEWAY:
|
||||
gw = sin->sin_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sa = NEXT_SA( sa );
|
||||
}
|
||||
}
|
||||
|
||||
if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
|
||||
{
|
||||
*addr = gw;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#define SEQNUM 195909
|
||||
|
||||
static int
|
||||
getsock( void );
|
||||
static uint8_t *
|
||||
getroute( int fd, unsigned int * buflen );
|
||||
static int
|
||||
parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
|
||||
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr )
|
||||
{
|
||||
int fd, ret;
|
||||
unsigned int len;
|
||||
uint8_t * buf;
|
||||
|
||||
ret = 1;
|
||||
fd = getsock();
|
||||
if( 0 <= fd )
|
||||
{
|
||||
while( ret )
|
||||
{
|
||||
buf = getroute( fd, &len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
break;
|
||||
}
|
||||
ret = parseroutes( buf, len, addr );
|
||||
free( buf );
|
||||
}
|
||||
close( fd );
|
||||
}
|
||||
|
||||
if( ret )
|
||||
{
|
||||
tr_err( "failed to get default route (Linux)" );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
getsock( void )
|
||||
{
|
||||
int fd, flags;
|
||||
struct
|
||||
{
|
||||
struct nlmsghdr nlh;
|
||||
struct rtgenmsg rtg;
|
||||
} req;
|
||||
struct sockaddr_nl snl;
|
||||
|
||||
fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
|
||||
if( 0 > fd )
|
||||
{
|
||||
tr_err( "failed to create routing socket (%s)", strerror( errno ) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
flags = fcntl( fd, F_GETFL );
|
||||
if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
|
||||
{
|
||||
tr_err( "failed to set socket nonblocking (%s)", strerror( errno ) );
|
||||
close( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero( &snl, sizeof( snl ) );
|
||||
snl.nl_family = AF_NETLINK;
|
||||
|
||||
bzero( &req, sizeof( req ) );
|
||||
req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
|
||||
req.nlh.nlmsg_type = RTM_GETROUTE;
|
||||
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
req.nlh.nlmsg_seq = SEQNUM;
|
||||
req.nlh.nlmsg_pid = 0;
|
||||
req.rtg.rtgen_family = AF_INET;
|
||||
|
||||
if( 0 > sendto( fd, &req, sizeof( req ), 0,
|
||||
(struct sockaddr *) &snl, sizeof( snl ) ) )
|
||||
{
|
||||
tr_err( "failed to write to routing socket (%s)", strerror( errno ) );
|
||||
close( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
getroute( int fd, unsigned int * buflen )
|
||||
{
|
||||
void * buf;
|
||||
unsigned int len;
|
||||
ssize_t res;
|
||||
struct sockaddr_nl snl;
|
||||
socklen_t slen;
|
||||
|
||||
len = 8192;
|
||||
buf = calloc( 1, len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
bzero( &snl, sizeof( snl ) );
|
||||
slen = sizeof( snl );
|
||||
res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
|
||||
if( 0 > res )
|
||||
{
|
||||
if( EAGAIN != errno )
|
||||
{
|
||||
tr_err( "failed to read from routing socket (%s)",
|
||||
strerror( errno ) );
|
||||
}
|
||||
free( buf );
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
|
||||
{
|
||||
tr_err( "bad address" );
|
||||
free( buf );
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( 0 == snl.nl_pid )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*buflen = res;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
|
||||
{
|
||||
struct nlmsghdr * nlm;
|
||||
struct nlmsgerr * nle;
|
||||
struct rtmsg * rtm;
|
||||
struct rtattr * rta;
|
||||
int rtalen;
|
||||
struct in_addr gw, dst;
|
||||
|
||||
nlm = ( struct nlmsghdr * ) buf;
|
||||
while( NLMSG_OK( nlm, len ) )
|
||||
{
|
||||
gw.s_addr = INADDR_ANY;
|
||||
dst.s_addr = INADDR_ANY;
|
||||
if( NLMSG_ERROR == nlm->nlmsg_type )
|
||||
{
|
||||
nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
|
||||
if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
|
||||
nlm->nlmsg_len )
|
||||
{
|
||||
tr_err( "truncated netlink error" );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_err( "netlink error (%s)", strerror( nle->error ) );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
|
||||
getpid() == (pid_t) nlm->nlmsg_pid &&
|
||||
NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
|
||||
{
|
||||
rtm = NLMSG_DATA( nlm );
|
||||
rta = RTM_RTA( rtm );
|
||||
rtalen = RTM_PAYLOAD( nlm );
|
||||
|
||||
while( RTA_OK( rta, rtalen ) )
|
||||
{
|
||||
if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
|
||||
{
|
||||
switch( rta->rta_type )
|
||||
{
|
||||
case RTA_GATEWAY:
|
||||
memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
|
||||
break;
|
||||
case RTA_DST:
|
||||
memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
rta = RTA_NEXT( rta, rtalen );
|
||||
}
|
||||
}
|
||||
|
||||
if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
|
||||
INADDR_ANY == dst.s_addr )
|
||||
{
|
||||
*addr = gw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nlm = NLMSG_NEXT( nlm, len );
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else /* not BSD or Linux */
|
||||
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr UNUSED )
|
||||
{
|
||||
tr_inf( "don't know how to get default route on this platform" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -60,4 +60,7 @@ static inline void tr_lockUnlock( tr_lock_t * l )
|
|||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -42,22 +42,12 @@ struct tr_tracker_s
|
|||
uint64_t dateTry;
|
||||
uint64_t dateOk;
|
||||
|
||||
#define TC_STATUS_IDLE 1
|
||||
#define TC_STATUS_RESOLVE 2
|
||||
#define TC_STATUS_CONNECT 4
|
||||
#define TC_STATUS_RECV 8
|
||||
char status;
|
||||
|
||||
#define TC_ATTEMPT_NOREACH 1
|
||||
#define TC_ATTEMPT_ERROR 2
|
||||
#define TC_ATTEMPT_OK 4
|
||||
char lastAttempt;
|
||||
|
||||
tr_resolve_t * resolve;
|
||||
int socket;
|
||||
uint8_t * buf;
|
||||
int size;
|
||||
int pos;
|
||||
tr_http_t * http;
|
||||
|
||||
int bindPort;
|
||||
int newPort;
|
||||
|
@ -66,8 +56,8 @@ struct tr_tracker_s
|
|||
uint64_t upload;
|
||||
};
|
||||
|
||||
static void sendQuery ( tr_tracker_t * tc );
|
||||
static void recvAnswer ( tr_tracker_t * tc );
|
||||
static tr_http_t * getQuery ( tr_tracker_t * tc );
|
||||
static void readAnswer ( tr_tracker_t * tc, const char *, int );
|
||||
|
||||
tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
|
||||
{
|
||||
|
@ -83,10 +73,7 @@ tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
|
|||
tc->seeders = -1;
|
||||
tc->leechers = -1;
|
||||
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
tc->lastAttempt = TC_ATTEMPT_NOREACH;
|
||||
tc->size = 1024;
|
||||
tc->buf = malloc( tc->size );
|
||||
|
||||
tc->bindPort = *(tor->bindPort);
|
||||
tc->newPort = -1;
|
||||
|
@ -159,11 +146,17 @@ int tr_trackerPulse( tr_tracker_t * tc )
|
|||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
uint64_t now = tr_date();
|
||||
const char * data;
|
||||
int len;
|
||||
|
||||
if( ( tc->status & TC_STATUS_IDLE ) && shouldConnect( tc ) )
|
||||
if( ( NULL == tc->http ) && shouldConnect( tc ) )
|
||||
{
|
||||
tc->resolve = tr_netResolveInit( inf->trackerAddress );
|
||||
if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
tc->dateTry = tr_date();
|
||||
tc->http = getQuery( tc );
|
||||
|
||||
tr_inf( "Tracker: connecting to %s:%d (%s)",
|
||||
inf->trackerAddress, inf->trackerPort,
|
||||
|
@ -172,71 +165,29 @@ int tr_trackerPulse( tr_tracker_t * tc )
|
|||
( tc->stopped ? "sending 'stopped'" :
|
||||
( 0 < tc->newPort ? "sending 'stopped' to change port" :
|
||||
"getting peers" ) ) ) );
|
||||
|
||||
tc->status = TC_STATUS_RESOLVE;
|
||||
tc->dateTry = tr_date();
|
||||
}
|
||||
|
||||
if( tc->status & TC_STATUS_RESOLVE )
|
||||
if( NULL != tc->http )
|
||||
{
|
||||
int ret;
|
||||
struct in_addr addr;
|
||||
|
||||
ret = tr_netResolvePulse( tc->resolve, &addr );
|
||||
if( ret == TR_RESOLVE_WAIT )
|
||||
switch( tr_httpPulse( tc->http, &data, &len ) )
|
||||
{
|
||||
return 0;
|
||||
case TR_WAIT:
|
||||
return 0;
|
||||
|
||||
case TR_ERROR:
|
||||
tr_httpClose( tc->http );
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
tc->http = NULL;
|
||||
tc->dateTry = tr_date();
|
||||
return 0;
|
||||
|
||||
case TR_OK:
|
||||
readAnswer( tc, data, len );
|
||||
tr_httpClose( tc->http );
|
||||
tc->http = NULL;
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_netResolveClose( tc->resolve );
|
||||
}
|
||||
|
||||
if( ret == TR_RESOLVE_ERROR )
|
||||
{
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
|
||||
{
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) );
|
||||
if( tc->socket < 0 )
|
||||
{
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tc->status = TC_STATUS_CONNECT;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -253,13 +204,13 @@ void tr_trackerStopped( tr_tracker_t * tc )
|
|||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
|
||||
if( tc->status > TC_STATUS_CONNECT )
|
||||
if( NULL != tc->http )
|
||||
{
|
||||
/* If we are already sendy a query at the moment, we need to
|
||||
reconnect */
|
||||
tr_netClose( tc->socket );
|
||||
tr_httpClose( tc->http );
|
||||
tc->http = NULL;
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
tc->status = TC_STATUS_IDLE;
|
||||
}
|
||||
|
||||
tc->started = 0;
|
||||
|
@ -267,39 +218,30 @@ void tr_trackerStopped( tr_tracker_t * tc )
|
|||
tc->stopped = 1;
|
||||
|
||||
/* Even if we have connected recently, reconnect right now */
|
||||
if( tc->status & TC_STATUS_IDLE )
|
||||
{
|
||||
tc->dateTry = 0;
|
||||
}
|
||||
tc->dateTry = 0;
|
||||
}
|
||||
|
||||
void tr_trackerClose( tr_tracker_t * tc )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
|
||||
if( tc->status == TC_STATUS_RESOLVE )
|
||||
if( NULL != tc->http )
|
||||
{
|
||||
tr_netResolveClose( tc->resolve );
|
||||
}
|
||||
else if( tc->status > TC_STATUS_RESOLVE )
|
||||
{
|
||||
tr_netClose( tc->socket );
|
||||
tr_httpClose( tc->http );
|
||||
tr_fdSocketClosed( tor->fdlimit, 1 );
|
||||
}
|
||||
free( tc->buf );
|
||||
free( tc );
|
||||
}
|
||||
|
||||
static void sendQuery( tr_tracker_t * tc )
|
||||
static tr_http_t * getQuery( tr_tracker_t * tc )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
char * event;
|
||||
uint64_t left;
|
||||
int ret;
|
||||
uint64_t down;
|
||||
uint64_t up;
|
||||
char * event;
|
||||
uint64_t left;
|
||||
uint64_t down;
|
||||
uint64_t up;
|
||||
|
||||
assert( tor->downloaded >= tc->download && tor->uploaded >= tc->upload );
|
||||
down = tor->downloaded - tc->download;
|
||||
|
@ -330,119 +272,61 @@ static void sendQuery( tr_tracker_t * tc )
|
|||
|
||||
left = tr_cpLeftBytes( tor->completion );
|
||||
|
||||
ret = snprintf( (char *) tc->buf, tc->size,
|
||||
"GET %s?"
|
||||
"info_hash=%s&"
|
||||
"peer_id=%s&"
|
||||
"port=%d&"
|
||||
"uploaded=%"PRIu64"&"
|
||||
"downloaded=%"PRIu64"&"
|
||||
"left=%"PRIu64"&"
|
||||
"compact=1&"
|
||||
"numwant=50&"
|
||||
"key=%s"
|
||||
"%s "
|
||||
"HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"User-Agent: Transmission/%d.%d\r\n"
|
||||
"Connection: close\r\n\r\n",
|
||||
inf->trackerAnnounce, tor->hashString, tc->id,
|
||||
tc->bindPort, up, down,
|
||||
left, tor->key, event, inf->trackerAddress,
|
||||
VERSION_MAJOR, VERSION_MINOR );
|
||||
|
||||
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;
|
||||
}
|
||||
return tr_httpClient( TR_HTTP_GET, inf->trackerAddress,
|
||||
inf->trackerPort,
|
||||
"%s?"
|
||||
"info_hash=%s&"
|
||||
"peer_id=%s&"
|
||||
"port=%d&"
|
||||
"uploaded=%"PRIu64"&"
|
||||
"downloaded=%"PRIu64"&"
|
||||
"left=%"PRIu64"&"
|
||||
"compact=1&"
|
||||
"numwant=50&"
|
||||
"key=%s"
|
||||
"%s ",
|
||||
inf->trackerAnnounce, tor->hashString, tc->id,
|
||||
tc->bindPort, up, down, left, tor->key, event );
|
||||
}
|
||||
|
||||
static void recvAnswer( tr_tracker_t * tc )
|
||||
static void readAnswer( tr_tracker_t * tc, const char * data, int len )
|
||||
{
|
||||
tr_torrent_t * tor = tc->tor;
|
||||
int ret;
|
||||
int i;
|
||||
int code;
|
||||
benc_val_t beAll;
|
||||
benc_val_t * bePeers, * beFoo;
|
||||
uint8_t * body;
|
||||
const uint8_t * body;
|
||||
int bodylen;
|
||||
int shouldfree;
|
||||
|
||||
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 < 12 || ( 0 != memcmp( tc->buf, "HTTP/1.0 ", 9 ) &&
|
||||
0 != memcmp( tc->buf, "HTTP/1.1 ", 9 ) ) )
|
||||
code = tr_httpResponseCode( data, len );
|
||||
if( 0 > code )
|
||||
{
|
||||
/* We don't have a complete HTTP status line */
|
||||
tr_inf( "Tracker: incomplete HTTP status line" );
|
||||
/* We don't have a valid HTTP status line */
|
||||
tr_inf( "Tracker: invalid HTTP status line" );
|
||||
tc->lastAttempt = TC_ATTEMPT_NOREACH;
|
||||
return;
|
||||
}
|
||||
|
||||
if( '2' != tc->buf[9] )
|
||||
if( !TR_HTTP_STATUS_OK( code ) )
|
||||
{
|
||||
/* we didn't get a 2xx status code */
|
||||
tr_err( "Tracker: invalid HTTP status code: %c%c%c",
|
||||
tc->buf[9], tc->buf[10], tc->buf[11] );
|
||||
tr_err( "Tracker: invalid HTTP status code: %i", code );
|
||||
tc->lastAttempt = TC_ATTEMPT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
/* find the end of the http headers */
|
||||
body = tr_memmem( tc->buf, tc->pos, "\015\012\015\012", 4 );
|
||||
if( NULL != body )
|
||||
{
|
||||
body += 4;
|
||||
}
|
||||
/* hooray for trackers that violate the HTTP spec */
|
||||
else if( NULL != ( body = tr_memmem( tc->buf, tc->pos, "\015\015", 2 ) ) ||
|
||||
NULL != ( body = tr_memmem( tc->buf, tc->pos, "\012\012", 2 ) ) )
|
||||
{
|
||||
body += 2;
|
||||
}
|
||||
else
|
||||
body = (uint8_t *) tr_httpParse( data, len, NULL );
|
||||
if( NULL == body )
|
||||
{
|
||||
tr_err( "Tracker: could not find end of HTTP headers" );
|
||||
tc->lastAttempt = TC_ATTEMPT_NOREACH;
|
||||
return;
|
||||
}
|
||||
bodylen = tc->pos - (body - tc->buf);
|
||||
bodylen = len - (body - (const uint8_t*)data);
|
||||
|
||||
/* Find and load the dictionary */
|
||||
shouldfree = 0;
|
||||
|
@ -610,13 +494,11 @@ 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;
|
||||
tr_resolve_t * resolve;
|
||||
tr_http_t * http;
|
||||
const char * data, * body;
|
||||
int datalen, bodylen;
|
||||
int code, ii;
|
||||
benc_val_t scrape, * val1, * val2;
|
||||
|
||||
if( !tor->scrape[0] )
|
||||
{
|
||||
|
@ -624,133 +506,97 @@ int tr_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers )
|
|||
return 1;
|
||||
}
|
||||
|
||||
resolve = tr_netResolveInit( inf->trackerAddress );
|
||||
for( date = tr_date();; )
|
||||
{
|
||||
ret = tr_netResolvePulse( resolve, &addr );
|
||||
if( ret == TR_RESOLVE_OK )
|
||||
{
|
||||
tr_netResolveClose( resolve );
|
||||
break;
|
||||
}
|
||||
if( ret == TR_RESOLVE_ERROR ||
|
||||
( ret == TR_RESOLVE_WAIT && tr_date() > date + 10000 ) )
|
||||
{
|
||||
tr_err( "Could not resolve %s", inf->trackerAddress );
|
||||
tr_netResolveClose( resolve );
|
||||
return 1;
|
||||
}
|
||||
tr_wait( 10 );
|
||||
}
|
||||
http = tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
|
||||
"%s?info_hash=%s", tor->scrape, tor->hashString );
|
||||
|
||||
s = tr_netOpen( addr, htons( inf->trackerPort ) );
|
||||
if( s < 0 )
|
||||
data = NULL;
|
||||
while( NULL == data )
|
||||
{
|
||||
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 )
|
||||
switch( tr_httpPulse( http, &data, &datalen ) )
|
||||
{
|
||||
tr_err( "Could not connect to tracker" );
|
||||
tr_netClose( s );
|
||||
return 1;
|
||||
}
|
||||
else if( ret & TR_NET_BLOCK )
|
||||
{
|
||||
if( tr_date() > date + 10000 )
|
||||
{
|
||||
tr_err( "Could not connect to tracker" );
|
||||
tr_netClose( s );
|
||||
case TR_WAIT:
|
||||
break;
|
||||
|
||||
case TR_ERROR:
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
|
||||
case TR_OK:
|
||||
if( NULL == data || 0 >= datalen )
|
||||
{
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
tr_wait( 10 );
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
for( date = tr_date();; )
|
||||
code = tr_httpResponseCode( data, datalen );
|
||||
if( !TR_HTTP_STATUS_OK( code ) )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
tr_err( "Could not read from tracker" );
|
||||
tr_netClose( s );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += ret;
|
||||
}
|
||||
tr_wait( 10 );
|
||||
}
|
||||
|
||||
if( pos < 1 )
|
||||
{
|
||||
tr_err( "Could not read from tracker" );
|
||||
tr_netClose( s );
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
|
||||
for( i = 0; i < pos - 8; i++ )
|
||||
body = tr_httpParse( data, datalen , NULL );
|
||||
if( NULL == body )
|
||||
{
|
||||
if( !memcmp( &buf[i], "d5:files", 8 ) )
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
bodylen = datalen - ( body - data );
|
||||
|
||||
for( ii = 0; ii < bodylen - 8; ii++ )
|
||||
{
|
||||
if( !memcmp( body + ii, "d5:files", 8 ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i >= pos - 8 )
|
||||
if( ii >= bodylen - 8 )
|
||||
{
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
if( tr_bencLoad( &buf[i], pos - i, &scrape, NULL ) )
|
||||
if( tr_bencLoad( body + ii, bodylen - ii, &scrape, NULL ) )
|
||||
{
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
|
||||
val1 = tr_bencDictFind( &scrape, "files" );
|
||||
if( !val1 )
|
||||
{
|
||||
tr_bencFree( &scrape );
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
val1 = &val1->val.l.vals[1];
|
||||
if( !val1 )
|
||||
{
|
||||
tr_bencFree( &scrape );
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
val2 = tr_bencDictFind( val1, "complete" );
|
||||
if( !val2 )
|
||||
{
|
||||
tr_bencFree( &scrape );
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
*seeders = val2->val.i;
|
||||
val2 = tr_bencDictFind( val1, "incomplete" );
|
||||
if( !val2 )
|
||||
{
|
||||
tr_bencFree( &scrape );
|
||||
tr_httpClose( http );
|
||||
return 1;
|
||||
}
|
||||
*leechers = val2->val.i;
|
||||
tr_bencFree( &scrape );
|
||||
tr_httpClose( http );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ tr_handle_t * tr_init()
|
|||
h->download = tr_rcInit();
|
||||
h->fdlimit = tr_fdInit();
|
||||
h->choking = tr_chokingInit( h );
|
||||
h->natpmp = tr_natpmpInit( h->fdlimit );
|
||||
h->upnp = tr_upnpInit( h->fdlimit );
|
||||
|
||||
h->bindPort = -1;
|
||||
h->bindSocket = -1;
|
||||
|
@ -104,7 +106,16 @@ void tr_setBindPort( tr_handle_t * h, int port )
|
|||
if( !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
|
||||
{
|
||||
/* XXX should handle failure here in a better way */
|
||||
sock = tr_netBind( port );
|
||||
sock = tr_netBindTCP( port );
|
||||
if( 0 > sock)
|
||||
{
|
||||
tr_fdSocketClosed( h->fdlimit, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_inf( "Bound listening port %d", port );
|
||||
listen( sock, 5 );
|
||||
}
|
||||
}
|
||||
#else
|
||||
return;
|
||||
|
@ -132,9 +143,53 @@ void tr_setBindPort( tr_handle_t * h, int port )
|
|||
|
||||
h->bindSocket = sock;
|
||||
|
||||
tr_natpmpForwardPort( h->natpmp, port );
|
||||
tr_upnpForwardPort( h->upnp, port );
|
||||
|
||||
tr_lockUnlock( &h->acceptLock );
|
||||
}
|
||||
|
||||
void tr_natTraversalEnable( tr_handle_t * h )
|
||||
{
|
||||
tr_natpmpStart( h->natpmp );
|
||||
tr_upnpStart( h->upnp );
|
||||
}
|
||||
|
||||
void tr_natTraversalDisable( tr_handle_t * h )
|
||||
{
|
||||
tr_natpmpStop( h->natpmp );
|
||||
tr_upnpStop( h->upnp );
|
||||
}
|
||||
|
||||
int tr_natTraversalStatus( tr_handle_t * h )
|
||||
{
|
||||
int statuses[] = {
|
||||
TR_NAT_TRAVERSAL_MAPPED,
|
||||
TR_NAT_TRAVERSAL_MAPPING,
|
||||
TR_NAT_TRAVERSAL_UNMAPPING,
|
||||
TR_NAT_TRAVERSAL_ERROR,
|
||||
TR_NAT_TRAVERSAL_NOTFOUND,
|
||||
TR_NAT_TRAVERSAL_DISABLED,
|
||||
-1,
|
||||
};
|
||||
int natpmp, upnp, ii;
|
||||
|
||||
natpmp = tr_natpmpStatus( h->natpmp );
|
||||
upnp = tr_upnpStatus( h->upnp );
|
||||
|
||||
for( ii = 0; 0 <= statuses[ii]; ii++ )
|
||||
{
|
||||
if( statuses[ii] == natpmp || statuses[ii] == upnp )
|
||||
{
|
||||
return statuses[ii];
|
||||
}
|
||||
}
|
||||
|
||||
assert( 0 );
|
||||
|
||||
return TR_NAT_TRAVERSAL_ERROR;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setUploadLimit
|
||||
***********************************************************************
|
||||
|
@ -629,6 +684,8 @@ void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
|
|||
void tr_close( tr_handle_t * h )
|
||||
{
|
||||
acceptStop( h );
|
||||
tr_natpmpClose( h->natpmp );
|
||||
tr_upnpClose( h->upnp );
|
||||
tr_chokingClose( h->choking );
|
||||
tr_fdClose( h->fdlimit );
|
||||
tr_rcClose( h->upload );
|
||||
|
@ -735,6 +792,10 @@ static void acceptLoop( void * _h )
|
|||
{
|
||||
date1 = tr_date();
|
||||
|
||||
/* do NAT-PMP and UPnP pulses here since there's nowhere better */
|
||||
tr_natpmpPulse( h->natpmp );
|
||||
tr_upnpPulse( h->upnp );
|
||||
|
||||
/* Check for incoming connections */
|
||||
if( h->bindSocket > -1 &&
|
||||
h->acceptPeerCount < TR_MAX_PEER_COUNT &&
|
||||
|
|
|
@ -99,10 +99,35 @@ char * tr_getPrefsDirectory();
|
|||
/***********************************************************************
|
||||
* tr_setBindPort
|
||||
***********************************************************************
|
||||
* Sets the port to listen for incoming peer connections
|
||||
* Sets the port to listen for incoming peer connections.
|
||||
* This can be safely called even with active torrents.
|
||||
**********************************************************************/
|
||||
void tr_setBindPort( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_natTraversalEnable
|
||||
* tr_natTraversalDisable
|
||||
***********************************************************************
|
||||
* Enable or disable NAT traversal using NAT-PMP or UPnP IGD.
|
||||
**********************************************************************/
|
||||
void tr_natTraversalEnable( tr_handle_t * );
|
||||
void tr_natTraversalDisable( tr_handle_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_natTraversalStatus
|
||||
***********************************************************************
|
||||
* Return the status of NAT traversal
|
||||
**********************************************************************/
|
||||
#define TR_NAT_TRAVERSAL_MAPPING 1
|
||||
#define TR_NAT_TRAVERSAL_MAPPED 2
|
||||
#define TR_NAT_TRAVERSAL_NOTFOUND 3
|
||||
#define TR_NAT_TRAVERSAL_ERROR 4
|
||||
#define TR_NAT_TRAVERSAL_UNMAPPING 5
|
||||
#define TR_NAT_TRAVERSAL_DISABLED 6
|
||||
#define TR_NAT_TRAVERSAL_IS_DISABLED( st ) \
|
||||
( TR_NAT_TRAVERSAL_DISABLED == (st) || TR_NAT_TRAVERSAL_UNMAPPING == (st) )
|
||||
int tr_natTraversalStatus( tr_handle_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setUploadLimit
|
||||
***********************************************************************
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,38 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* 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_UPNP_H
|
||||
#define TR_UPNP_H 1
|
||||
|
||||
typedef struct tr_upnp_s tr_upnp_t;
|
||||
|
||||
tr_upnp_t * tr_upnpInit( tr_fd_t * );
|
||||
void tr_upnpStart( tr_upnp_t * );
|
||||
void tr_upnpStop( tr_upnp_t * );
|
||||
int tr_upnpStatus( tr_upnp_t * );
|
||||
void tr_upnpForwardPort( tr_upnp_t *, int );
|
||||
void tr_upnpPulse( tr_upnp_t * );
|
||||
void tr_upnpClose( tr_upnp_t * );
|
||||
|
||||
#endif
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include "transmission.h"
|
||||
|
||||
#define SPRINTF_BUFSIZE 100
|
||||
|
||||
static tr_lock_t * messageLock = NULL;
|
||||
static int messageLevel = 0;
|
||||
static int messageQueuing = 0;
|
||||
|
@ -96,8 +98,9 @@ void tr_freeMessageList( tr_msg_list_t * list )
|
|||
|
||||
void tr_msg( int level, char * msg, ... )
|
||||
{
|
||||
va_list args;
|
||||
va_list args1, args2;
|
||||
tr_msg_list_t * newmsg;
|
||||
int len1, len2;
|
||||
|
||||
assert( NULL != messageLock );
|
||||
tr_lockLock( messageLock );
|
||||
|
@ -112,7 +115,7 @@ void tr_msg( int level, char * msg, ... )
|
|||
|
||||
if( messageLevel >= level )
|
||||
{
|
||||
va_start( args, msg );
|
||||
va_start( args1, msg );
|
||||
if( messageQueuing )
|
||||
{
|
||||
newmsg = calloc( 1, sizeof( *newmsg ) );
|
||||
|
@ -120,7 +123,11 @@ void tr_msg( int level, char * msg, ... )
|
|||
{
|
||||
newmsg->level = level;
|
||||
newmsg->when = time( NULL );
|
||||
vasprintf( &newmsg->message, msg, args );
|
||||
len1 = len2 = 0;
|
||||
va_start( args2, msg );
|
||||
tr_vsprintf( &newmsg->message, &len1, &len2, msg,
|
||||
args1, args2 );
|
||||
va_end( args2 );
|
||||
if( NULL == newmsg->message )
|
||||
{
|
||||
free( newmsg );
|
||||
|
@ -134,10 +141,10 @@ void tr_msg( int level, char * msg, ... )
|
|||
}
|
||||
else
|
||||
{
|
||||
vfprintf( stderr, msg, args );
|
||||
vfprintf( stderr, msg, args1 );
|
||||
fputc( '\n', stderr );
|
||||
}
|
||||
va_end( args );
|
||||
va_end( args1 );
|
||||
}
|
||||
|
||||
tr_lockUnlock( messageLock );
|
||||
|
@ -234,3 +241,96 @@ int tr_mkdir( char * path )
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define UPPER( cc ) \
|
||||
( 'a' <= (cc) && 'z' >= (cc) ? (cc) - ( 'a' - 'A' ) : (cc) )
|
||||
|
||||
int tr_strncasecmp( const char * first, const char * second, int len )
|
||||
{
|
||||
int ii;
|
||||
char firstchar, secondchar;
|
||||
|
||||
if( 0 > len )
|
||||
{
|
||||
len = strlen( first );
|
||||
ii = strlen( second );
|
||||
len = MIN( len, ii );
|
||||
}
|
||||
|
||||
for( ii = 0; ii < len; ii++ )
|
||||
{
|
||||
if( first[ii] != second[ii] )
|
||||
{
|
||||
firstchar = UPPER( first[ii] );
|
||||
secondchar = UPPER( second[ii] );
|
||||
if( firstchar > secondchar )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if( firstchar < secondchar )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if( '\0' == first[ii] )
|
||||
{
|
||||
/* if first[ii] is '\0' then second[ii] is too */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tr_sprintf( char ** buf, int * used, int * max, const char * format, ... )
|
||||
{
|
||||
va_list ap1, ap2;
|
||||
int ret;
|
||||
|
||||
va_start( ap1, format );
|
||||
va_start( ap2, format );
|
||||
ret = tr_vsprintf( buf, used, max, format, ap1, ap2 );
|
||||
va_end( ap2 );
|
||||
va_end( ap1 );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tr_vsprintf( char ** buf, int * used, int * max, const char * fmt,
|
||||
va_list ap1, va_list ap2 )
|
||||
{
|
||||
int want;
|
||||
char * newbuf;
|
||||
|
||||
want = vsnprintf( NULL, 0, fmt, ap1 );
|
||||
|
||||
while( *used + want + 1 > *max )
|
||||
{
|
||||
*max += SPRINTF_BUFSIZE;
|
||||
newbuf = realloc( *buf, *max );
|
||||
if( NULL == newbuf )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
*buf = newbuf;
|
||||
}
|
||||
|
||||
*used += vsnprintf( *buf + *used, *max - *used, fmt, ap2 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_dupstr( const char * base, int len )
|
||||
{
|
||||
char * ret;
|
||||
|
||||
ret = malloc( len + 1 );
|
||||
if( NULL != ret )
|
||||
{
|
||||
memcpy( ret, base, len );
|
||||
ret[len] = '\0';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ void tr_msgInit( void );
|
|||
#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, ... );
|
||||
void tr_msg ( int level, char * msg, ... ) PRINTF( 2, 3 );
|
||||
|
||||
int tr_rand ( int );
|
||||
|
||||
|
@ -44,6 +44,32 @@ void * tr_memmem( const void *, size_t, const void *, size_t );
|
|||
**********************************************************************/
|
||||
int tr_mkdir( char * path );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_strcasecmp
|
||||
***********************************************************************
|
||||
* A case-insensitive strncmp()
|
||||
**********************************************************************/
|
||||
#define tr_strcasecmp( ff, ss ) ( tr_strncasecmp( (ff), (ss), -1 ) )
|
||||
int tr_strncasecmp( const char * first, const char * second, int len );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sprintf
|
||||
***********************************************************************
|
||||
* Appends to the end of a buffer using printf formatting,
|
||||
* growing the buffer if needed
|
||||
**********************************************************************/
|
||||
int tr_sprintf( char ** buf, int * used, int * max,
|
||||
const char * format, ... ) PRINTF( 4, 5 );
|
||||
/* gee, it sure would be nice if BeOS had va_copy() */
|
||||
int tr_vsprintf( char **, int *, int *, const char *, va_list, va_list );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_dupstr
|
||||
***********************************************************************
|
||||
* Creates a nul-terminated string
|
||||
**********************************************************************/
|
||||
char * tr_dupstr( const char * base, int len );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_date
|
||||
***********************************************************************
|
||||
|
|
|
@ -0,0 +1,424 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* http://www.w3.org/TR/2004/REC-xml-20040204/ */
|
||||
|
||||
#define WS( cc ) \
|
||||
( ' ' == (cc) || '\t' == (cc) || '\n' == (cc) || '\r' == (cc) )
|
||||
#define TAGBEGIN '<'
|
||||
#define TAGEND '>'
|
||||
#define TAGCLOSE '/'
|
||||
#define NAMESPACESEP ':'
|
||||
#define SQUOTEC '\''
|
||||
#define DQUOTEC '"'
|
||||
#define SQUOTES "'"
|
||||
#define DQUOTES "\""
|
||||
#define COMMENTBEGIN "<!--"
|
||||
#define COMMENTEND "-->"
|
||||
#define PROCINSTBEGIN "<?"
|
||||
#define PROCINSTEND "?>"
|
||||
#define CDATABEGIN "<![CDATA["
|
||||
#define CDATAEND "]]>"
|
||||
#define BANGBEGIN "<!"
|
||||
#define BANGEND ">"
|
||||
#define CHECKNULL( bb, ee, rr ) \
|
||||
{ if( NULL == (bb) || (ee) <= (bb) ) return (rr); }
|
||||
#define justskip( bb, ee, ap, ot, ct ) \
|
||||
( skipthingy( (bb), (ee), (ap), (ot), (ct), NULL, NULL ) )
|
||||
|
||||
static char *
|
||||
catrange( char * str, const char * begin, const char * end );
|
||||
static int
|
||||
skipall( const char * begin, const char * end, const char ** afterpos );
|
||||
static int
|
||||
nexttag( const char * begin, const char * end, const char ** tagpos );
|
||||
static int
|
||||
overtag( const char * begin, const char * end, const char ** overpos );
|
||||
static int
|
||||
tagname( const char * begin, const char * end,
|
||||
const char ** tagstart, const char ** namestart, int * namelen );
|
||||
static int
|
||||
skipthingy( const char * begin, const char * end, const char ** afterpos,
|
||||
const char * openthingy, const char * closethingy,
|
||||
const char ** databegin, const char ** dataend );
|
||||
|
||||
/* XXX check document charset, in http headers and/or <?xml> tag */
|
||||
|
||||
const char *
|
||||
tr_xmlFindTag( const char * begin, const char * end, const char * tag )
|
||||
{
|
||||
const char * name;
|
||||
int len;
|
||||
|
||||
CHECKNULL( begin, end, NULL );
|
||||
|
||||
while( tagname( begin, end, &begin, &name, &len ) )
|
||||
{
|
||||
assert( NULL != begin && NULL != name && 0 < len );
|
||||
if( 0 == tr_strncasecmp( tag, name, len ) )
|
||||
{
|
||||
return begin;
|
||||
}
|
||||
begin = tr_xmlSkipTag( begin, end );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
tr_xmlTagName( const char * begin, const char * end, int * len )
|
||||
{
|
||||
CHECKNULL( begin, end, NULL );
|
||||
|
||||
if( tagname( begin, end, NULL, &begin, len ) )
|
||||
{
|
||||
return begin;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
tr_xmlTagContents( const char * begin, const char * end )
|
||||
{
|
||||
CHECKNULL( begin, end, NULL );
|
||||
|
||||
if( nexttag( begin, end, &begin ) && overtag( begin, end, &begin ) )
|
||||
{
|
||||
begin = NULL;
|
||||
}
|
||||
|
||||
return begin;
|
||||
}
|
||||
|
||||
int
|
||||
tr_xmlVerifyContents( const char * begin, const char * end, const char * data,
|
||||
int ignorecase )
|
||||
{
|
||||
int len;
|
||||
|
||||
CHECKNULL( begin, end, 1 );
|
||||
len = strlen( data );
|
||||
|
||||
while( end > begin && WS( *begin ) )
|
||||
{
|
||||
begin++;
|
||||
}
|
||||
if( end - begin > len )
|
||||
{
|
||||
if( ignorecase )
|
||||
{
|
||||
return ( 0 != tr_strncasecmp( begin, data, len ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return ( 0 != memcmp( begin, data, len ) );
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *
|
||||
tr_xmlSkipTag( const char * begin, const char * end )
|
||||
{
|
||||
CHECKNULL( begin, end, NULL );
|
||||
|
||||
if( nexttag( begin, end, &begin ) )
|
||||
{
|
||||
if( overtag( begin, end, &begin ) )
|
||||
{
|
||||
return begin;
|
||||
}
|
||||
while( NULL != begin )
|
||||
{
|
||||
if( nexttag( begin, end, &begin ) )
|
||||
{
|
||||
begin = tr_xmlSkipTag( begin, end );
|
||||
}
|
||||
else
|
||||
{
|
||||
overtag( begin, end, &begin );
|
||||
return begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_xmlDupContents( const char * begin, const char * end )
|
||||
{
|
||||
const char * ii, * cbegin, * cend;
|
||||
char * ret;
|
||||
int len;
|
||||
|
||||
CHECKNULL( begin, end, NULL );
|
||||
|
||||
ret = NULL;
|
||||
len = strlen( CDATABEGIN );
|
||||
|
||||
while( end > begin )
|
||||
{
|
||||
ii = memchr( begin, TAGBEGIN, end - begin );
|
||||
if( NULL == ii )
|
||||
{
|
||||
free( ret );
|
||||
return NULL;
|
||||
}
|
||||
/* XXX expand entity references and such here */
|
||||
ret = catrange( ret, begin, ii );
|
||||
if( NULL == ret )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if( !skipthingy( ii, end, &begin, CDATABEGIN, CDATAEND,
|
||||
&cbegin, &cend ) )
|
||||
{
|
||||
ret = catrange( ret, cbegin, cend );
|
||||
if( NULL == ret )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if( skipall( ii, end, &begin ) )
|
||||
{
|
||||
if( end > ii + 1 && TAGCLOSE == ii[1] )
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
begin = tr_xmlSkipTag( ii, end );
|
||||
}
|
||||
}
|
||||
|
||||
free( ret );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *
|
||||
catrange( char * str, const char * begin, const char * end )
|
||||
{
|
||||
int len;
|
||||
char * ret;
|
||||
|
||||
if( NULL == str )
|
||||
{
|
||||
return tr_dupstr( begin, end - begin );
|
||||
}
|
||||
|
||||
len = strlen( str );
|
||||
ret = realloc( str, len + end - begin + 1 );
|
||||
if( NULL == ret )
|
||||
{
|
||||
free( str );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy( ret + len, begin, end - begin );
|
||||
ret[len + end - begin] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
skipall( const char * begin, const char * end, const char ** afterpos )
|
||||
{
|
||||
return ( justskip( begin, end, afterpos, COMMENTBEGIN, COMMENTEND ) &&
|
||||
justskip( begin, end, afterpos, CDATABEGIN, CDATAEND ) &&
|
||||
justskip( begin, end, afterpos, PROCINSTBEGIN, PROCINSTEND ) &&
|
||||
justskip( begin, end, afterpos, BANGBEGIN, BANGEND ) );
|
||||
}
|
||||
|
||||
/* returns true if a tag was found and it's a start or empty element tag */
|
||||
static int
|
||||
nexttag( const char * begin, const char * end, const char ** tagpos )
|
||||
{
|
||||
CHECKNULL( begin, end, 0 );
|
||||
|
||||
while( end > begin )
|
||||
{
|
||||
begin = memchr( begin, TAGBEGIN, end - begin );
|
||||
CHECKNULL( begin, end, 0 );
|
||||
if( justskip( begin, end, &begin, COMMENTBEGIN, COMMENTEND ) &&
|
||||
justskip( begin, end, &begin, CDATABEGIN, CDATAEND ) &&
|
||||
justskip( begin, end, &begin, PROCINSTBEGIN, PROCINSTEND ) &&
|
||||
justskip( begin, end, &begin, BANGBEGIN, BANGEND ) )
|
||||
{
|
||||
*tagpos = begin;
|
||||
begin++;
|
||||
if( end > begin )
|
||||
{
|
||||
return ( TAGCLOSE != *begin );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*tagpos = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns true if the tag is an empty element such as <foo/> */
|
||||
static int
|
||||
overtag( const char * begin, const char * end, const char ** overpos )
|
||||
{
|
||||
const char * ii;
|
||||
|
||||
assert( NULL != begin && end > begin && TAGBEGIN == *begin );
|
||||
|
||||
ii = begin + 1;
|
||||
while( end > ii )
|
||||
{
|
||||
switch( *ii )
|
||||
{
|
||||
case DQUOTEC:
|
||||
justskip( ii, end, &ii, DQUOTES, DQUOTES );
|
||||
break;
|
||||
case SQUOTEC:
|
||||
justskip( ii, end, &ii, SQUOTES, SQUOTES );
|
||||
break;
|
||||
case TAGEND:
|
||||
*overpos = ii + 1;
|
||||
for( ii--; begin < ii; ii-- )
|
||||
{
|
||||
if( TAGCLOSE == *ii )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if( !WS( *ii ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
ii++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*overpos = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tagname( const char * begin, const char * end,
|
||||
const char ** tagstart, const char ** namestart, int * namelen )
|
||||
{
|
||||
const char * name, * ii;
|
||||
|
||||
CHECKNULL( begin, end, 0 );
|
||||
|
||||
if( nexttag( begin, end, &begin ) )
|
||||
{
|
||||
assert( NULL != begin && TAGBEGIN == *begin );
|
||||
ii = begin + 1;
|
||||
while( end > ii && WS( *ii ) )
|
||||
{
|
||||
ii++;
|
||||
}
|
||||
name = ii;
|
||||
while( end > ii && TAGEND != *ii && !WS( *ii ) )
|
||||
{
|
||||
if( NAMESPACESEP == *ii )
|
||||
{
|
||||
name = ii + 1;
|
||||
}
|
||||
ii++;
|
||||
}
|
||||
if( end > ii && ii > name )
|
||||
{
|
||||
if( NULL != tagstart )
|
||||
{
|
||||
*tagstart = begin;
|
||||
}
|
||||
if( NULL != namestart )
|
||||
{
|
||||
*namestart = name;
|
||||
}
|
||||
if( NULL != namelen )
|
||||
{
|
||||
*namelen = ii - name;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
skipthingy( const char * begin, const char * end, const char ** afterpos,
|
||||
const char * openthingy, const char * closethingy,
|
||||
const char ** databegin, const char ** dataend )
|
||||
{
|
||||
int len;
|
||||
|
||||
CHECKNULL( begin, end, 1 );
|
||||
len = strlen( openthingy );
|
||||
if( 0 != memcmp( begin, openthingy, MIN( end - begin, len ) ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( NULL != afterpos )
|
||||
{
|
||||
*afterpos = NULL;
|
||||
}
|
||||
if( NULL != databegin )
|
||||
{
|
||||
*databegin = NULL;
|
||||
}
|
||||
if( NULL != dataend )
|
||||
{
|
||||
*dataend = NULL;
|
||||
}
|
||||
if( end - begin <= len )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
begin += len;
|
||||
if( NULL != databegin )
|
||||
{
|
||||
*databegin = begin;
|
||||
}
|
||||
|
||||
len = strlen( closethingy );
|
||||
begin = tr_memmem( begin, end - begin, closethingy, len );
|
||||
if( NULL != dataend )
|
||||
{
|
||||
*dataend = begin;
|
||||
}
|
||||
if( NULL != afterpos && NULL != begin )
|
||||
{
|
||||
*afterpos = ( begin + len >= end ? NULL : begin + len );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* 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_XML_H
|
||||
#define TR_XML_H 1
|
||||
|
||||
const char *
|
||||
tr_xmlFindTag( const char * begin, const char * end, const char * tag );
|
||||
|
||||
const char *
|
||||
tr_xmlTagName( const char * begin, const char * end, int * len );
|
||||
|
||||
const char *
|
||||
tr_xmlTagContents( const char * begin, const char * end );
|
||||
|
||||
#define tr_xmlFindTagContents( bb, ee, tt ) \
|
||||
( tr_xmlTagContents( tr_xmlFindTag( (bb), (ee), (tt) ), (ee) ) )
|
||||
|
||||
int
|
||||
tr_xmlVerifyContents( const char * begin, const char * end, const char * data,
|
||||
int ignorecase );
|
||||
|
||||
#define tr_xmlFindTagVerifyContents( bb, ee, tt, dd, ic ) \
|
||||
( tr_xmlVerifyContents( tr_xmlFindTagContents( (bb), (ee), (tt) ), \
|
||||
(ee), (dd), (ic) ) )
|
||||
|
||||
const char *
|
||||
tr_xmlSkipTag( const char * begin, const char * end );
|
||||
|
||||
char *
|
||||
tr_xmlDupContents( const char * begin, const char * end );
|
||||
|
||||
#define tr_xmlDupTagContents( bb, ee, tt ) \
|
||||
( tr_xmlDupContents( tr_xmlFindTagContents( (bb), (ee), (tt) ), (ee) ) )
|
||||
|
||||
#endif
|
|
@ -30,6 +30,7 @@
|
|||
#import "PrefsController.h"
|
||||
#import "InfoWindowController.h"
|
||||
#import "MessageWindowController.h"
|
||||
#import "PiecesWindowController.h"
|
||||
#import "Badger.h"
|
||||
#import "ImageBackgroundView.h"
|
||||
#import "BarButton.h"
|
||||
|
@ -49,6 +50,7 @@
|
|||
NSUserDefaults * fDefaults;
|
||||
InfoWindowController * fInfoController;
|
||||
MessageWindowController * fMessageController;
|
||||
PiecesWindowController * fPiecesWindowController;
|
||||
|
||||
IBOutlet NSWindow * fWindow;
|
||||
IBOutlet NSScrollView * fScrollView;
|
||||
|
@ -105,9 +107,12 @@
|
|||
|
||||
- (void) resumeSelectedTorrents: (id) sender;
|
||||
- (void) resumeAllTorrents: (id) sender;
|
||||
- (void) resumeWaitingTorrents: (id) sender;
|
||||
- (void) resumeTorrents: (NSArray *) torrents;
|
||||
|
||||
- (void) resumeSelectedTorrentsNoWait: (id) sender;
|
||||
- (void) resumeWaitingTorrents: (id) sender;
|
||||
- (void) resumeTorrentsNoWait: (NSArray *) torrents;
|
||||
|
||||
- (void) stopSelectedTorrents: (id) sender;
|
||||
- (void) stopAllTorrents: (id) sender;
|
||||
- (void) stopTorrents: (NSArray *) torrents;
|
||||
|
@ -136,6 +141,7 @@
|
|||
- (void) setInfoTab: (id) sender;
|
||||
|
||||
- (void) showMessageWindow: (id) sender;
|
||||
- (void) showPiecesView: (id) sender;
|
||||
|
||||
- (void) updateControlTint: (NSNotification *) notification;
|
||||
|
||||
|
@ -172,8 +178,6 @@
|
|||
- (void) attemptToStartAuto: (Torrent *) torrent;
|
||||
- (void) attemptToStartMultipleAuto: (NSArray *) torrents;
|
||||
|
||||
- (void) reloadInspectorSettings: (NSNotification *) notification;
|
||||
|
||||
- (void) checkAutoImportDirectory;
|
||||
|
||||
- (void) sleepCallBack: (natural_t) messageType argument: (void *) messageArgument;
|
||||
|
|
|
@ -86,6 +86,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
fMessageController = [[MessageWindowController alloc] initWithWindowNibName: @"MessageWindow"];
|
||||
fInfoController = [[InfoWindowController alloc] initWithWindowNibName: @"InfoWindow"];
|
||||
fPiecesWindowController = [[PiecesWindowController alloc] initWithWindowNibName: @"PiecesWindow"];
|
||||
fPrefsController = [[PrefsController alloc] initWithWindowNibName: @"PrefsWindow" handle: fLib];
|
||||
|
||||
fBadger = [[Badger alloc] init];
|
||||
|
@ -100,13 +101,15 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
- (void) dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
||||
|
||||
[fTorrents release];
|
||||
[fDisplayedTorrents release];
|
||||
|
||||
[fInfoController release];
|
||||
[fMessageController release];
|
||||
[fPiecesWindowController release];
|
||||
[fPrefsController release];
|
||||
|
||||
[fToolbar release];
|
||||
[fInfoController release];
|
||||
[fPrefsController release];
|
||||
[fTorrents release];
|
||||
[fDisplayedTorrents release];
|
||||
[fBadger release];
|
||||
|
||||
[fSortType release];
|
||||
|
@ -332,19 +335,11 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
//check all torrents for starting
|
||||
[nc addObserver: self selector: @selector(globalStartSettingChange:)
|
||||
name: @"GlobalStartSettingChange" object: nil];
|
||||
|
||||
//check if torrent should now start
|
||||
[nc addObserver: self selector: @selector(torrentStartSettingChange:)
|
||||
name: @"TorrentStartSettingChange" object: nil];
|
||||
|
||||
//check if torrent should now start
|
||||
[nc addObserver: self selector: @selector(torrentStoppedForRatio:)
|
||||
name: @"TorrentStoppedForRatio" object: nil];
|
||||
|
||||
//change that just impacts the inspector
|
||||
[nc addObserver: self selector: @selector(reloadInspectorSettings:)
|
||||
name: @"TorrentSettingChange" object: nil];
|
||||
|
||||
//change that just impacts the dock badge
|
||||
[nc addObserver: self selector: @selector(resetDockBadge:)
|
||||
name: @"DockBadgeChange" object: nil];
|
||||
|
@ -364,6 +359,9 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
if ([fDefaults boolForKey: @"InfoVisible"])
|
||||
[self showInfo: nil];
|
||||
|
||||
if ([fDefaults boolForKey: @"PiecesViewerVisible"])
|
||||
[self showPiecesView: nil];
|
||||
|
||||
//timer to auto toggle speed limit
|
||||
[self autoSpeedLimitChange: nil];
|
||||
fSpeedLimitTimer = [NSTimer scheduledTimerWithTimeInterval: AUTO_SPEED_LIMIT_SECONDS target: self
|
||||
|
@ -425,8 +423,12 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
[self updateTorrentHistory];
|
||||
[fTorrents makeObjectsPerformSelector: @selector(stopTransferForQuit)];
|
||||
|
||||
//disable NAT traversal
|
||||
tr_natTraversalDisable(fLib);
|
||||
|
||||
//remember window states and close all windows
|
||||
[fDefaults setBool: [[fInfoController window] isVisible] forKey: @"InfoVisible"];
|
||||
[fDefaults setBool: [[fPiecesWindowController window] isVisible] forKey: @"PiecesViewerVisible"];
|
||||
[[NSApp windows] makeObjectsPerformSelector: @selector(close)];
|
||||
[self showStatusBar: NO animate: NO];
|
||||
[self showFilterBar: NO animate: NO];
|
||||
|
@ -438,13 +440,13 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
if (fUpdateInProgress)
|
||||
return;
|
||||
|
||||
//wait for running transfers to stop (5 second timeout)
|
||||
//wait for running transfers to stop (5 second timeout) and for NAT to be disabled
|
||||
NSDate * start = [NSDate date];
|
||||
BOOL timeUp = NO;
|
||||
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
Torrent * torrent;
|
||||
while (!timeUp && (torrent = [enumerator nextObject]))
|
||||
while (!timeUp && ((torrent = [enumerator nextObject]) || tr_natTraversalStatus(fLib) != TR_NAT_TRAVERSAL_DISABLED))
|
||||
while (![torrent isPaused] && !(timeUp = [start timeIntervalSinceNow] < -5.0))
|
||||
{
|
||||
usleep(100000);
|
||||
|
@ -543,8 +545,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
[panel setMessage: [NSString stringWithFormat: @"Select the download folder for \"%@\"", [torrent name]]];
|
||||
|
||||
NSDictionary * dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
|
||||
torrent, @"Torrent", files, @"Files", nil];
|
||||
NSDictionary * dictionary = [[NSDictionary alloc] initWithObjectsAndKeys: torrent, @"Torrent", files, @"Files", nil];
|
||||
|
||||
[panel beginSheetForDirectory: nil file: nil types: nil modalForWindow: fWindow modalDelegate: self
|
||||
didEndSelector: @selector(folderChoiceClosed:returnCode:contextInfo:) contextInfo: dictionary];
|
||||
|
@ -626,6 +627,25 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
[self resumeTorrents: fTorrents];
|
||||
}
|
||||
|
||||
- (void) resumeTorrents: (NSArray *) torrents
|
||||
{
|
||||
NSEnumerator * enumerator = [torrents objectEnumerator];
|
||||
Torrent * torrent;
|
||||
while ((torrent = [enumerator nextObject]))
|
||||
[torrent setWaitToStart: YES];
|
||||
|
||||
[self attemptToStartMultipleAuto: torrents];
|
||||
|
||||
[self updateUI: nil];
|
||||
[self applyFilter: nil];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
- (void) resumeSelectedTorrentsNoWait: (id) sender
|
||||
{
|
||||
[self resumeTorrentsNoWait: [self torrentsAtIndexes: [fTableView selectedRowIndexes]]];
|
||||
}
|
||||
|
||||
- (void) resumeWaitingTorrents: (id) sender
|
||||
{
|
||||
NSMutableArray * torrents = [NSMutableArray arrayWithCapacity: [fTorrents count]];
|
||||
|
@ -636,16 +656,15 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
if ([torrent waitingToStart])
|
||||
[torrents addObject: torrent];
|
||||
|
||||
[self resumeTorrents: torrents];
|
||||
[self resumeTorrentsNoWait: torrents];
|
||||
}
|
||||
|
||||
- (void) resumeTorrents: (NSArray *) torrents
|
||||
- (void) resumeTorrentsNoWait: (NSArray *) torrents
|
||||
{
|
||||
[torrents makeObjectsPerformSelector: @selector(startTransfer)];
|
||||
|
||||
[self updateUI: nil];
|
||||
[self applyFilter: nil];
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
|
@ -671,7 +690,6 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
[self updateUI: nil];
|
||||
[self applyFilter: nil];
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
|
@ -918,6 +936,17 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
[fMessageController showWindow: nil];
|
||||
}
|
||||
|
||||
- (void) showPiecesView: (id) sender
|
||||
{
|
||||
if ([[fPiecesWindowController window] isVisible])
|
||||
[fPiecesWindowController close];
|
||||
else
|
||||
{
|
||||
[fPiecesWindowController updateView: NO];
|
||||
[[fPiecesWindowController window] orderFront: nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateControlTint: (NSNotification *) notification
|
||||
{
|
||||
if (fSpeedLimitEnabled)
|
||||
|
@ -947,6 +976,10 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
//update non-constant parts of info window
|
||||
if ([[fInfoController window] isVisible])
|
||||
[fInfoController updateInfoStats];
|
||||
|
||||
//update pieces viewer
|
||||
if ([[fPiecesWindowController window] isVisible])
|
||||
[fPiecesWindowController updateView: NO];
|
||||
|
||||
//badge dock
|
||||
[fBadger updateBadgeWithCompleted: fCompleted uploadRate: uploadRate downloadRate: downloadRate];
|
||||
|
@ -1387,17 +1420,16 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
[self updateUI: nil];
|
||||
[self applyFilter: nil];
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
- (void) checkToStartWaiting: (Torrent *) finishedTorrent
|
||||
{
|
||||
//don't try to start a transfer if there should be none waiting
|
||||
if (![[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Wait"])
|
||||
if (![fDefaults boolForKey: @"Queue"])
|
||||
return;
|
||||
|
||||
int desiredActive = [fDefaults integerForKey: @"WaitToStartNumber"];
|
||||
int desiredActive = [fDefaults integerForKey: @"QueueDownloadNumber"];
|
||||
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
Torrent * torrent, * torrentToStart = nil;
|
||||
|
@ -1432,7 +1464,6 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
[self updateUI: nil];
|
||||
[self applyFilter: nil];
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
}
|
||||
|
@ -1443,7 +1474,6 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
[self updateUI: nil];
|
||||
[self applyFilter: nil];
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
|
@ -1453,14 +1483,14 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
[self updateUI: nil];
|
||||
[self applyFilter: nil];
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
- (void) torrentStoppedForRatio: (NSNotification *) notification
|
||||
{
|
||||
[self applyFilter: nil];
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
[fInfoController updateInfoStats];
|
||||
[fInfoController updateInfoSettings];
|
||||
|
||||
if ([fDefaults boolForKey: @"PlaySeedingSound"])
|
||||
{
|
||||
|
@ -1481,8 +1511,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
//will try to start, taking into consideration the start preference
|
||||
- (void) attemptToStartMultipleAuto: (NSArray *) torrents
|
||||
{
|
||||
NSString * startSetting = [fDefaults stringForKey: @"StartSetting"];
|
||||
if ([startSetting isEqualToString: @"Start"])
|
||||
if (![fDefaults boolForKey: @"Queue"])
|
||||
{
|
||||
NSEnumerator * enumerator = [torrents objectEnumerator];
|
||||
Torrent * torrent;
|
||||
|
@ -1492,12 +1521,9 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
return;
|
||||
}
|
||||
else if (![startSetting isEqualToString: @"Wait"])
|
||||
return;
|
||||
else;
|
||||
|
||||
//determine the number of downloads needed to start
|
||||
int desiredActive = [fDefaults integerForKey: @"WaitToStartNumber"];
|
||||
int desiredActive = [fDefaults integerForKey: @"QueueDownloadNumber"];
|
||||
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
Torrent * torrent;
|
||||
|
@ -1543,11 +1569,6 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
}
|
||||
}
|
||||
|
||||
- (void) reloadInspectorSettings: (NSNotification *) notification
|
||||
{
|
||||
[fInfoController updateInfoStatsAndSettings];
|
||||
}
|
||||
|
||||
-(void) watcher: (id<UKFileWatcher>) watcher receivedNotification: (NSString *) notification forPath: (NSString *) path
|
||||
{
|
||||
if ([notification isEqualToString: UKFileWatcherWriteNotification])
|
||||
|
@ -1586,12 +1607,6 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
return [fDisplayedTorrents count];
|
||||
}
|
||||
|
||||
/*- (void) tableView: (NSTableView *) t willDisplayCell: (id) cell
|
||||
forTableColumn: (NSTableColumn *) tableColumn row: (int) row
|
||||
{
|
||||
[cell setTorrent: [fDisplayedTorrents objectAtIndex: row]];
|
||||
}*/
|
||||
|
||||
- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (int) row
|
||||
{
|
||||
return [[fDisplayedTorrents objectAtIndex: row] infoForCurrentView];
|
||||
|
@ -1712,7 +1727,11 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
|
||||
- (void) tableViewSelectionDidChange: (NSNotification *) notification
|
||||
{
|
||||
[fInfoController updateInfoForTorrents: [self torrentsAtIndexes: [fTableView selectedRowIndexes]]];
|
||||
NSArray * torrents = [self torrentsAtIndexes: [fTableView selectedRowIndexes]];
|
||||
[fInfoController updateInfoForTorrents: torrents];
|
||||
|
||||
Torrent * torrent = [torrents count] == 1 ? [torrents objectAtIndex: 0] : nil;
|
||||
[fPiecesWindowController setTorrent: torrent];
|
||||
}
|
||||
|
||||
- (void) toggleSmallView: (id) sender
|
||||
|
@ -1981,7 +2000,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
while ((torrent = [enumerator nextObject]))
|
||||
if ([torrent isActive])
|
||||
if ([torrent isActive] || [torrent waitingToStart])
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
@ -1992,7 +2011,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
while ((torrent = [enumerator nextObject]))
|
||||
if ([torrent isPaused])
|
||||
if ([torrent isPaused] && ![torrent waitingToStart])
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
@ -2005,20 +2024,27 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
unsigned int i;
|
||||
|
||||
for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
|
||||
if ([[fDisplayedTorrents objectAtIndex: i] isActive])
|
||||
{
|
||||
torrent = [fDisplayedTorrents objectAtIndex: i];
|
||||
if ([torrent isActive] || [torrent waitingToStart])
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
//enable resume item
|
||||
if ([ident isEqualToString: TOOLBAR_RESUME_SELECTED])
|
||||
{
|
||||
Torrent * torrent;
|
||||
NSIndexSet * indexSet = [fTableView selectedRowIndexes];
|
||||
unsigned int i;
|
||||
|
||||
for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
|
||||
if ([[fDisplayedTorrents objectAtIndex: i] isPaused])
|
||||
{
|
||||
torrent = [fDisplayedTorrents objectAtIndex: i];
|
||||
if ([torrent isPaused] && ![torrent waitingToStart])
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
@ -2050,6 +2076,16 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
return YES;
|
||||
}
|
||||
|
||||
//enable show pieces window
|
||||
if (action == @selector(showPiecesView:))
|
||||
{
|
||||
NSString * title = [[fPiecesWindowController window] isVisible] ? @"Hide Pieces Viewer" : @"Show Pieces Viewer";
|
||||
if (![[menuItem title] isEqualToString: title])
|
||||
[menuItem setTitle: title];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//enable prev/next inspector tab
|
||||
if (action == @selector(setInfoTab:))
|
||||
return [[fInfoController window] isVisible];
|
||||
|
@ -2132,7 +2168,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
while ((torrent = [enumerator nextObject]))
|
||||
if ([torrent isActive])
|
||||
if ([torrent isActive] || [torrent waitingToStart])
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
@ -2143,15 +2179,15 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
while ((torrent = [enumerator nextObject]))
|
||||
if ([torrent isPaused])
|
||||
if ([torrent isPaused] && ![torrent waitingToStart])
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
//enable resume waiting item
|
||||
//enable resume all waiting item
|
||||
if (action == @selector(resumeWaitingTorrents:))
|
||||
{
|
||||
if (![[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Wait"])
|
||||
if (![fDefaults boolForKey: @"Queue"])
|
||||
return NO;
|
||||
|
||||
Torrent * torrent;
|
||||
|
@ -2161,6 +2197,25 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
//enable resume selected waiting item
|
||||
if (action == @selector(resumeSelectedTorrentsNoWait:))
|
||||
{
|
||||
if (![fDefaults boolForKey: @"Queue"])
|
||||
return NO;
|
||||
|
||||
Torrent * torrent;
|
||||
NSIndexSet * indexSet = [fTableView selectedRowIndexes];
|
||||
unsigned int i;
|
||||
|
||||
for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
|
||||
{
|
||||
torrent = [fDisplayedTorrents objectAtIndex: i];
|
||||
if ([torrent isPaused])
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
//enable pause item
|
||||
if (action == @selector(stopSelectedTorrents:))
|
||||
|
@ -2175,7 +2230,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
|
||||
{
|
||||
torrent = [fDisplayedTorrents objectAtIndex: i];
|
||||
if ([torrent isActive])
|
||||
if ([torrent isActive] || [torrent waitingToStart])
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
|
@ -2194,7 +2249,7 @@ static void sleepCallBack(void * controller, io_service_t y, natural_t messageTy
|
|||
for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
|
||||
{
|
||||
torrent = [fDisplayedTorrents objectAtIndex: i];
|
||||
if ([torrent isPaused])
|
||||
if ([torrent isPaused] && ![torrent waitingToStart])
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
|
|
|
@ -52,10 +52,18 @@
|
|||
<string>Constant</string>
|
||||
<key>MoveFolder</key>
|
||||
<string>~/Desktop</string>
|
||||
<key>NatTraversal</key>
|
||||
<true/>
|
||||
<key>PiecesViewerVisible</key>
|
||||
<false/>
|
||||
<key>PlayDownloadSound</key>
|
||||
<true/>
|
||||
<key>PlaySeedingSound</key>
|
||||
<false/>
|
||||
<key>Queue</key>
|
||||
<false/>
|
||||
<key>QueueDownloadNumber</key>
|
||||
<integer>3</integer>
|
||||
<key>RatioCheck</key>
|
||||
<false/>
|
||||
<key>RatioLimit</key>
|
||||
|
@ -86,8 +94,8 @@
|
|||
<integer>10</integer>
|
||||
<key>SpeedLimitUploadLimit</key>
|
||||
<integer>10</integer>
|
||||
<key>StartSetting</key>
|
||||
<string>Start</string>
|
||||
<key>StartAtOpen</key>
|
||||
<true/>
|
||||
<key>StatusBar</key>
|
||||
<true/>
|
||||
<key>UpdateCheck</key>
|
||||
|
@ -96,7 +104,5 @@
|
|||
<integer>20</integer>
|
||||
<key>UseAdvancedBar</key>
|
||||
<false/>
|
||||
<key>WaitToStartNumber</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -8,12 +8,7 @@
|
|||
},
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
|
||||
{
|
||||
ACTIONS = {
|
||||
revealFile = id;
|
||||
setRatioCheck = id;
|
||||
setRatioLimit = id;
|
||||
setWaitToStart = id;
|
||||
};
|
||||
ACTIONS = {revealFile = id; setRatioCheck = id; setRatioLimit = id; };
|
||||
CLASS = InfoWindowController;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {
|
||||
|
@ -46,7 +41,6 @@
|
|||
fTrackerField = NSTextField;
|
||||
fUploadedTotalField = NSTextField;
|
||||
fUploadingToField = NSTextField;
|
||||
fWaitToStartButton = NSButton;
|
||||
};
|
||||
SUPERCLASS = NSWindowController;
|
||||
},
|
||||
|
|
Binary file not shown.
|
@ -15,6 +15,7 @@
|
|||
removeNoDelete = id;
|
||||
resumeAllTorrents = id;
|
||||
resumeSelectedTorrents = id;
|
||||
resumeSelectedTorrentsNoWait = id;
|
||||
resumeWaitingTorrents = id;
|
||||
revealFile = id;
|
||||
setFilter = id;
|
||||
|
@ -27,6 +28,7 @@
|
|||
showInfo = id;
|
||||
showMainWindow = id;
|
||||
showMessageWindow = id;
|
||||
showPiecesView = id;
|
||||
showPreferenceWindow = id;
|
||||
stopAllTorrents = id;
|
||||
stopSelectedTorrents = id;
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>1041</key>
|
||||
<string>439 418 208 130 0 0 1152 842 </string>
|
||||
<string>379 362 208 130 0 0 1024 746 </string>
|
||||
<key>1480</key>
|
||||
<string>366 546 420 63 0 0 1152 842 </string>
|
||||
<key>1603</key>
|
||||
<string>337 545 477 67 0 0 1152 842 </string>
|
||||
<key>29</key>
|
||||
<string>104 684 451 44 0 0 1152 842 </string>
|
||||
<string>280 699 451 44 0 0 1152 842 </string>
|
||||
<key>456</key>
|
||||
<string>396 374 216 206 0 0 1152 842 </string>
|
||||
<string>340 316 240 225 0 0 1024 746 </string>
|
||||
<key>581</key>
|
||||
<string>571 464 115 99 0 0 1152 842 </string>
|
||||
<key>589</key>
|
||||
|
@ -31,8 +31,8 @@
|
|||
<integer>3</integer>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>21</integer>
|
||||
<integer>29</integer>
|
||||
<integer>21</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8J135</string>
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
IBClasses = (
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
|
||||
{
|
||||
CLASS = PiecesWindowController;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {fImageView = NSImageView; };
|
||||
SUPERCLASS = NSWindowController;
|
||||
}
|
||||
);
|
||||
IBVersion = 1;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?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>82 71 356 240 0 0 1152 842 </string>
|
||||
<key>IBFramework Version</key>
|
||||
<string>446.1</string>
|
||||
<key>IBLockedObjects</key>
|
||||
<array/>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>5</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8J135</string>
|
||||
</dict>
|
||||
</plist>
|
Binary file not shown.
|
@ -17,16 +17,18 @@
|
|||
setLimit = id;
|
||||
setLimitCheck = id;
|
||||
setMoveTorrent = id;
|
||||
setNat = id;
|
||||
setPlaySound = id;
|
||||
setPort = id;
|
||||
setQueueNumber = id;
|
||||
setRatio = id;
|
||||
setRatioCheck = id;
|
||||
setShowMessage = id;
|
||||
setSound = id;
|
||||
setSpeedLimit = id;
|
||||
setStartNumber = id;
|
||||
setStartSetting = id;
|
||||
setStartAtOpen = id;
|
||||
setUpdate = id;
|
||||
setUseQueue = id;
|
||||
};
|
||||
CLASS = PrefsController;
|
||||
LANGUAGE = ObjC;
|
||||
|
@ -44,10 +46,15 @@
|
|||
fFolderPopUp = NSPopUpButton;
|
||||
fGeneralView = NSView;
|
||||
fImportFolderPopUp = NSPopUpButton;
|
||||
fNatCheck = NSButton;
|
||||
fNatStatusField = NSTextField;
|
||||
fNatStatusImage = NSImageView;
|
||||
fNetworkView = NSView;
|
||||
fPlayDownloadSoundCheck = NSButton;
|
||||
fPlaySeedingSoundCheck = NSButton;
|
||||
fPortField = NSTextField;
|
||||
fQueueCheck = NSButton;
|
||||
fQueueNumberField = NSTextField;
|
||||
fQuitCheck = NSButton;
|
||||
fQuitDownloadingCheck = NSButton;
|
||||
fRatioCheck = NSButton;
|
||||
|
@ -60,8 +67,7 @@
|
|||
fSpeedLimitAutoOnField = NSTextField;
|
||||
fSpeedLimitDownloadField = NSTextField;
|
||||
fSpeedLimitUploadField = NSTextField;
|
||||
fStartMatrix = NSMatrix;
|
||||
fStartNumberField = NSTextField;
|
||||
fStartAtOpenCheck = NSButton;
|
||||
fTransfersView = NSView;
|
||||
fUpdatePopUp = NSPopUpButton;
|
||||
fUpdater = SUUpdater;
|
||||
|
|
|
@ -3,17 +3,17 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>49 68 356 240 0 0 1152 842 </string>
|
||||
<string>15 73 356 240 0 0 1024 746 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>153</key>
|
||||
<string>285 423 582 311 0 0 1152 842 </string>
|
||||
<string>30 315 577 267 0 0 1024 746 </string>
|
||||
<key>28</key>
|
||||
<string>58 372 582 290 0 0 1152 842 </string>
|
||||
<string>22 331 577 290 0 0 1024 746 </string>
|
||||
<key>41</key>
|
||||
<string>285 427 582 304 0 0 1152 842 </string>
|
||||
<string>230 355 563 317 0 0 1024 746 </string>
|
||||
<key>66</key>
|
||||
<string>164 527 582 104 0 0 1152 842 </string>
|
||||
<string>29 426 563 159 0 0 1024 746 </string>
|
||||
</dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>446.1</string>
|
||||
|
@ -23,7 +23,7 @@
|
|||
</array>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>153</integer>
|
||||
<integer>41</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8J135</string>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 448 B |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -52,12 +52,11 @@
|
|||
|
||||
IBOutlet NSMatrix * fRatioMatrix;
|
||||
IBOutlet NSTextField * fRatioLimitField;
|
||||
IBOutlet NSButton * fWaitToStartButton;
|
||||
}
|
||||
|
||||
- (void) updateInfoForTorrents: (NSArray *) torrents;
|
||||
- (void) updateInfoStats;
|
||||
- (void) updateInfoStatsAndSettings;
|
||||
- (void) updateInfoSettings;
|
||||
|
||||
- (void) setNextTab;
|
||||
- (void) setPreviousTab;
|
||||
|
@ -66,6 +65,4 @@
|
|||
- (void) setRatioCheck: (id) sender;
|
||||
- (void) setRatioLimit: (id) sender;
|
||||
|
||||
- (void) setWaitToStart: (id) sender;
|
||||
|
||||
@end
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#define TAB_ACTIVITY_HEIGHT 230.0
|
||||
#define TAB_PEERS_HEIGHT 255.0
|
||||
#define TAB_FILES_HEIGHT 255.0
|
||||
#define TAB_OPTIONS_HEIGHT 116.0
|
||||
#define TAB_OPTIONS_HEIGHT 83.0
|
||||
|
||||
@interface InfoWindowController (Private)
|
||||
|
||||
|
@ -209,7 +209,8 @@
|
|||
}
|
||||
|
||||
//update stats and settings
|
||||
[self updateInfoStatsAndSettings];
|
||||
[self updateInfoStats];
|
||||
[self updateInfoSettings];
|
||||
|
||||
//set file table
|
||||
[fFiles removeAllObjects];
|
||||
|
@ -282,36 +283,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void) updateInfoStatsAndSettings
|
||||
- (void) updateInfoSettings
|
||||
{
|
||||
int numberSelected = [fTorrents count];
|
||||
|
||||
//set wait to start
|
||||
BOOL waiting = NO, notWaiting = NO, canEnableWaiting = numberSelected > 0,
|
||||
waitingSettingOn = [[[NSUserDefaults standardUserDefaults] stringForKey: @"StartSetting"]
|
||||
isEqualToString: @"Wait"];
|
||||
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
Torrent * torrent;
|
||||
while ((torrent = [enumerator nextObject]))
|
||||
{
|
||||
if ([torrent waitingToStart])
|
||||
waiting = YES;
|
||||
else
|
||||
notWaiting = YES;
|
||||
|
||||
if (canEnableWaiting && !(![torrent isActive] && [torrent progress] < 1.0 && waitingSettingOn))
|
||||
canEnableWaiting = NO;
|
||||
}
|
||||
|
||||
[fWaitToStartButton setState: waiting && notWaiting ? NSMixedState : (waiting ? NSOnState : NSOffState)];
|
||||
[fWaitToStartButton setEnabled: canEnableWaiting];
|
||||
|
||||
//set ratio settings
|
||||
if (numberSelected > 0)
|
||||
{
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
torrent = [enumerator nextObject]; //first torrent
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
Torrent * torrent = [enumerator nextObject]; //first torrent
|
||||
const int INVALID = -99;
|
||||
int ratioSetting = [torrent stopRatioSetting];
|
||||
float ratioLimit = [torrent ratioLimit];
|
||||
|
@ -592,16 +572,4 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void) setWaitToStart: (id) sender
|
||||
{
|
||||
int state = [sender state];
|
||||
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
Torrent * torrent;
|
||||
while ((torrent = [enumerator nextObject]))
|
||||
[torrent setWaitToStart: state];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStartSettingChange" object: fTorrents];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#define LEVEL_DEBUG 2
|
||||
|
||||
#define UPDATE_SECONDS 0.6
|
||||
#define MAX_LINES 1000
|
||||
#define MAX_LINES 2500
|
||||
|
||||
@interface MessageWindowController (Private)
|
||||
|
||||
|
|
|
@ -50,13 +50,17 @@
|
|||
IBOutlet NSButton * fUploadCheck, * fDownloadCheck,
|
||||
* fSpeedLimitAutoCheck;
|
||||
|
||||
IBOutlet NSTextField * fPortField;
|
||||
IBOutlet NSTextField * fPortField, * fNatStatusField;
|
||||
IBOutlet NSButton * fNatCheck;
|
||||
IBOutlet NSImageView * fNatStatusImage;
|
||||
NSTimer * fNatStatusTimer;
|
||||
int fNatStatus;
|
||||
|
||||
IBOutlet NSButton * fRatioCheck;
|
||||
IBOutlet NSTextField * fRatioField;
|
||||
|
||||
IBOutlet NSMatrix * fStartMatrix;
|
||||
IBOutlet NSTextField * fStartNumberField;
|
||||
IBOutlet NSButton * fQueueCheck, * fStartAtOpenCheck;
|
||||
IBOutlet NSTextField * fQueueNumberField;
|
||||
|
||||
IBOutlet SUUpdater * fUpdater;
|
||||
|
||||
|
@ -73,14 +77,19 @@
|
|||
- (void) setUpdate: (id) sender;
|
||||
- (void) checkUpdate;
|
||||
|
||||
- (void) setStartSetting: (id) sender;
|
||||
- (void) setStartNumber: (id) sender;
|
||||
- (void) setStartAtOpen: (id) sender;
|
||||
|
||||
- (void) setUseQueue: (id) sender;
|
||||
- (void) setQueueNumber: (id) sender;
|
||||
|
||||
- (void) setMoveTorrent: (id) sender;
|
||||
- (void) setDownloadLocation: (id) sender;
|
||||
- (void) folderSheetShow: (id) sender;
|
||||
|
||||
- (void) setPort: (id) sender;
|
||||
- (void) setPort: (id) sender;
|
||||
- (void) setNat: (id) sender;
|
||||
- (void) updateNatStatus;
|
||||
|
||||
- (void) setSpeedLimit: (id) sender;
|
||||
|
||||
- (void) setAutoSpeedLimitCheck: (id) sender;
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
#define DOWNLOAD_TORRENT 2
|
||||
#define DOWNLOAD_ASK 3
|
||||
|
||||
#define START_YES_CHECK_TAG 0
|
||||
#define START_WAIT_CHECK_TAG 1
|
||||
#define START_NO_CHECK_TAG 2
|
||||
|
||||
#define UPDATE_DAILY 0
|
||||
#define UPDATE_WEEKLY 1
|
||||
#define UPDATE_NEVER 2
|
||||
|
@ -79,6 +75,8 @@
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
[fNatStatusTimer invalidate];
|
||||
|
||||
[fDownloadFolder release];
|
||||
[fImportFolder release];
|
||||
[super dealloc];
|
||||
|
@ -126,6 +124,17 @@
|
|||
[fPortField setIntValue: bindPort];
|
||||
tr_setBindPort(fHandle, bindPort);
|
||||
|
||||
//set NAT
|
||||
BOOL natShouldEnable = [fDefaults boolForKey: @"NatTraversal"];
|
||||
if (natShouldEnable)
|
||||
tr_natTraversalEnable(fHandle);
|
||||
[fNatCheck setState: natShouldEnable];
|
||||
|
||||
fNatStatus = -1;
|
||||
[self updateNatStatus];
|
||||
fNatStatusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: self
|
||||
selector: @selector(updateNatStatus) userInfo: nil repeats: YES];
|
||||
|
||||
//checks for old version upload speed of -1
|
||||
if ([fDefaults integerForKey: @"UploadLimit"] < 0)
|
||||
{
|
||||
|
@ -249,19 +258,13 @@
|
|||
else
|
||||
[fDefaults setObject: [fSeedingSoundPopUp titleOfSelectedItem] forKey: @"SeedingSound"];
|
||||
|
||||
//set start setting
|
||||
NSString * startSetting = [fDefaults stringForKey: @"StartSetting"];
|
||||
int tag;
|
||||
if ([startSetting isEqualToString: @"Start"])
|
||||
tag = START_YES_CHECK_TAG;
|
||||
else if ([startSetting isEqualToString: @"Wait"])
|
||||
tag = START_WAIT_CHECK_TAG;
|
||||
else
|
||||
tag = START_NO_CHECK_TAG;
|
||||
//set start settings
|
||||
BOOL useQueue = [fDefaults boolForKey: @"Queue"];
|
||||
[fQueueCheck setState: useQueue];
|
||||
[fQueueNumberField setEnabled: useQueue];
|
||||
[fQueueNumberField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
|
||||
|
||||
[fStartMatrix selectCellWithTag: tag];
|
||||
[fStartNumberField setEnabled: tag == START_WAIT_CHECK_TAG];
|
||||
[fStartNumberField setIntValue: [fDefaults integerForKey: @"WaitToStartNumber"]];
|
||||
[fStartAtOpenCheck setState: [fDefaults boolForKey: @"StartAtOpen"]];
|
||||
|
||||
//set private torrents
|
||||
BOOL copyTorrents = [fDefaults boolForKey: @"SavePrivateTorrent"];
|
||||
|
@ -354,6 +357,41 @@
|
|||
{
|
||||
tr_setBindPort(fHandle, bindPort);
|
||||
[fDefaults setInteger: bindPort forKey: @"BindPort"];
|
||||
|
||||
[self updateNatStatus];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setNat: (id) sender
|
||||
{
|
||||
BOOL enable = [sender state] == NSOnState;
|
||||
enable ? tr_natTraversalEnable(fHandle) : tr_natTraversalDisable(fHandle);
|
||||
[fDefaults setBool: enable forKey: @"NatTraversal"];
|
||||
|
||||
[self updateNatStatus];
|
||||
}
|
||||
|
||||
- (void) updateNatStatus
|
||||
{
|
||||
int status = tr_natTraversalStatus(fHandle);
|
||||
if (fNatStatus == status)
|
||||
return;
|
||||
fNatStatus = status;
|
||||
|
||||
if (status == 2)
|
||||
{
|
||||
[fNatStatusField setStringValue: @"Ports successfully mapped"];
|
||||
[fNatStatusImage setImage: [NSImage imageNamed: @"Check.png"]];
|
||||
}
|
||||
else if (status == 3 || status == 4)
|
||||
{
|
||||
[fNatStatusField setStringValue: @"Error mapping ports"];
|
||||
[fNatStatusImage setImage: [NSImage imageNamed: @"Error.tiff"]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fNatStatusField setStringValue: @""];
|
||||
[fNatStatusImage setImage: nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,35 +699,31 @@
|
|||
[fUpdater scheduleCheckWithInterval: seconds];
|
||||
}
|
||||
|
||||
- (void) setStartSetting: (id) sender
|
||||
- (void) setStartAtOpen: (id) sender
|
||||
{
|
||||
NSString * startSetting;
|
||||
|
||||
int tag = [[fStartMatrix selectedCell] tag];
|
||||
if (tag == START_YES_CHECK_TAG)
|
||||
startSetting = @"Start";
|
||||
else if (tag == START_WAIT_CHECK_TAG)
|
||||
startSetting = @"Wait";
|
||||
else
|
||||
startSetting = @"Manual";
|
||||
|
||||
[fDefaults setObject: startSetting forKey: @"StartSetting"];
|
||||
|
||||
[self setStartNumber: fStartNumberField];
|
||||
[fStartNumberField setEnabled: tag == START_WAIT_CHECK_TAG];
|
||||
[fDefaults setBool: [sender state] == NSOnState forKey: @"StartAtOpen"];
|
||||
}
|
||||
|
||||
- (void) setStartNumber: (id) sender
|
||||
- (void) setUseQueue: (id) sender
|
||||
{
|
||||
int waitNumber = [sender intValue];
|
||||
if (![[sender stringValue] isEqualToString: [NSString stringWithInt: waitNumber]] || waitNumber < 1)
|
||||
BOOL useQueue = [sender state] == NSOnState;
|
||||
|
||||
[fDefaults setBool: useQueue forKey: @"Queue"];
|
||||
[self setQueueNumber: fQueueNumberField];
|
||||
[fQueueNumberField setEnabled: useQueue];
|
||||
}
|
||||
|
||||
- (void) setQueueNumber: (id) sender
|
||||
{
|
||||
int queueNumber = [sender intValue];
|
||||
if (![[sender stringValue] isEqualToString: [NSString stringWithInt: queueNumber]] || queueNumber < 1)
|
||||
{
|
||||
NSBeep();
|
||||
waitNumber = [fDefaults floatForKey: @"WaitToStartNumber"];
|
||||
[sender setIntValue: waitNumber];
|
||||
queueNumber = [fDefaults integerForKey: @"QueueDownloadNumber"];
|
||||
[sender setIntValue: queueNumber];
|
||||
}
|
||||
else
|
||||
[fDefaults setInteger: waitNumber forKey: @"WaitToStartNumber"];
|
||||
[fDefaults setInteger: queueNumber forKey: @"QueueDownloadNumber"];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName: @"GlobalStartSettingChange" object: self];
|
||||
}
|
||||
|
|
162
macosx/Torrent.m
162
macosx/Torrent.m
|
@ -51,7 +51,7 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
|
|||
kBlue2 = BE(0x78BEFFFF), //120, 190, 255
|
||||
kBlue3 = BE(0x50A0FFFF), //80, 160, 255
|
||||
kBlue4 = BE(0x1E46B4FF), //30, 70, 180
|
||||
kGray = BE(0x828282FF), //130, 130, 130
|
||||
kGray = BE(0x969696FF), //150, 150, 150
|
||||
kGreen = BE(0x00FF00FF), //0, 255, 0
|
||||
kWhite = BE(0xFFFFFFFF); //255, 255, 255
|
||||
|
||||
|
@ -192,7 +192,7 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
|
|||
case TR_STATUS_PAUSE:
|
||||
if (fFinishedSeeding)
|
||||
tempString = @"Seeding complete";
|
||||
else if (fWaitToStart && [[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Wait"])
|
||||
else if (fWaitToStart)
|
||||
tempString = [@"Waiting to start" stringByAppendingEllipsis];
|
||||
else
|
||||
tempString = @"Paused";
|
||||
|
@ -315,83 +315,6 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
|
|||
return info;
|
||||
}
|
||||
|
||||
- (NSImage *) advancedBar
|
||||
{
|
||||
#warning figure out length
|
||||
int width = 250; //integers for bars
|
||||
|
||||
NSBitmapImageRep * bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
|
||||
pixelsWide: width pixelsHigh: BAR_HEIGHT bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
|
||||
isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
|
||||
|
||||
int h, w;
|
||||
uint32_t * p;
|
||||
uint8_t * bitmapData = [bitmap bitmapData];
|
||||
int bytesPerRow = [bitmap bytesPerRow];
|
||||
|
||||
int8_t * pieces = malloc(width);
|
||||
[self getAvailability: pieces size: width];
|
||||
int avail = 0;
|
||||
for (w = 0; w < width; w++)
|
||||
if (pieces[w] != 0)
|
||||
avail++;
|
||||
|
||||
//first two lines: dark blue to show progression, green to show available
|
||||
int end = lrintf(floor([self progress] * width));
|
||||
p = (uint32_t *) bitmapData;
|
||||
|
||||
for (w = 0; w < end; w++)
|
||||
{
|
||||
p[w] = kBlue4;
|
||||
p[w + bytesPerRow / 4] = kBlue4;
|
||||
}
|
||||
for (; w < avail; w++)
|
||||
{
|
||||
p[w] = kGreen;
|
||||
p[w + bytesPerRow / 4] = kGreen;
|
||||
}
|
||||
for (; w < width - 2; w++)
|
||||
{
|
||||
p[w] = kWhite;
|
||||
p[w + bytesPerRow / 4] = kWhite;
|
||||
}
|
||||
|
||||
//lines 2 to 14: blue or grey depending on whether we have the piece or not
|
||||
uint32_t color;
|
||||
for( w = 0; w < width; w++ )
|
||||
{
|
||||
if (pieces[w] < 0)
|
||||
color = kGray;
|
||||
else if (pieces[w] == 0)
|
||||
color = kRed;
|
||||
else if (pieces[w] == 1)
|
||||
color = kBlue1;
|
||||
else if (pieces[w] == 2)
|
||||
color = kBlue2;
|
||||
else
|
||||
color = kBlue3;
|
||||
|
||||
//point to pixel (w, 2) and draw "vertically"
|
||||
p = (uint32_t *) ( bitmapData + 2 * bytesPerRow ) + w;
|
||||
for( h = 2; h < BAR_HEIGHT; h++ )
|
||||
{
|
||||
p[0] = color;
|
||||
p = (uint32_t *) ( (uint8_t *) p + bytesPerRow );
|
||||
}
|
||||
}
|
||||
|
||||
free(pieces);
|
||||
|
||||
//actually draw image
|
||||
NSImage * bar = [[NSImage alloc] initWithSize: [bitmap size]];
|
||||
[bar addRepresentation: bitmap];
|
||||
[bitmap release];
|
||||
|
||||
[bar setScalesWhenResized: YES];
|
||||
|
||||
return [bar autorelease];
|
||||
}
|
||||
|
||||
- (void) startTransfer
|
||||
{
|
||||
if (![self isActive])
|
||||
|
@ -400,8 +323,6 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
|
|||
|
||||
fFinishedSeeding = NO;
|
||||
fWaitToStart = NO;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentSettingChange" object: self];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -835,8 +756,7 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
|
|||
fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
|
||||
fFinishedSeeding = NO;
|
||||
|
||||
fWaitToStart = waitToStart ? [waitToStart boolValue]
|
||||
: ![[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Manual"];
|
||||
fWaitToStart = waitToStart ? [waitToStart boolValue] : [fDefaults boolForKey: @"StartAtOpen"];
|
||||
fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
|
||||
|
||||
NSString * fileType = fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
|
||||
|
@ -859,6 +779,82 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
|
|||
return self;
|
||||
}
|
||||
|
||||
- (NSImage *) advancedBar
|
||||
{
|
||||
int width = 250; //integers for bars
|
||||
|
||||
NSBitmapImageRep * bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
|
||||
pixelsWide: width pixelsHigh: BAR_HEIGHT bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
|
||||
isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
|
||||
|
||||
int h, w;
|
||||
uint32_t * p;
|
||||
uint8_t * bitmapData = [bitmap bitmapData];
|
||||
int bytesPerRow = [bitmap bytesPerRow];
|
||||
|
||||
int8_t * pieces = malloc(width);
|
||||
[self getAvailability: pieces size: width];
|
||||
int avail = 0;
|
||||
for (w = 0; w < width; w++)
|
||||
if (pieces[w] != 0)
|
||||
avail++;
|
||||
|
||||
//first two lines: dark blue to show progression, green to show available
|
||||
int end = lrintf(floor([self progress] * width));
|
||||
p = (uint32_t *) bitmapData;
|
||||
|
||||
for (w = 0; w < end; w++)
|
||||
{
|
||||
p[w] = kBlue4;
|
||||
p[w + bytesPerRow / 4] = kBlue4;
|
||||
}
|
||||
for (; w < avail; w++)
|
||||
{
|
||||
p[w] = kGreen;
|
||||
p[w + bytesPerRow / 4] = kGreen;
|
||||
}
|
||||
for (; w < width; w++)
|
||||
{
|
||||
p[w] = kWhite;
|
||||
p[w + bytesPerRow / 4] = kWhite;
|
||||
}
|
||||
|
||||
//lines 2 to 14: blue or grey depending on whether we have the piece or not
|
||||
uint32_t color;
|
||||
for( w = 0; w < width; w++ )
|
||||
{
|
||||
if (pieces[w] < 0)
|
||||
color = kGreen;
|
||||
else if (pieces[w] == 0)
|
||||
color = kGray;
|
||||
else if (pieces[w] == 1)
|
||||
color = kBlue1;
|
||||
else if (pieces[w] == 2)
|
||||
color = kBlue2;
|
||||
else
|
||||
color = kBlue3;
|
||||
|
||||
//point to pixel (w, 2) and draw "vertically"
|
||||
p = (uint32_t *) ( bitmapData + 2 * bytesPerRow ) + w;
|
||||
for( h = 2; h < BAR_HEIGHT; h++ )
|
||||
{
|
||||
p[0] = color;
|
||||
p = (uint32_t *) ( (uint8_t *) p + bytesPerRow );
|
||||
}
|
||||
}
|
||||
|
||||
free(pieces);
|
||||
|
||||
//actually draw image
|
||||
NSImage * bar = [[NSImage alloc] initWithSize: [bitmap size]];
|
||||
[bar addRepresentation: bitmap];
|
||||
[bitmap release];
|
||||
|
||||
[bar setScalesWhenResized: YES];
|
||||
|
||||
return [bar autorelease];
|
||||
}
|
||||
|
||||
- (void) trashFile: (NSString *) path
|
||||
{
|
||||
//attempt to move to trash
|
||||
|
|
|
@ -151,13 +151,6 @@
|
|||
{
|
||||
NSDictionary * info = [self objectValue];
|
||||
|
||||
//if seeding, there's no need for the advanced bar
|
||||
if ([[info objectForKey: @"Seeding"] boolValue])
|
||||
{
|
||||
[self buildSimpleBar: widthFloat point: point];
|
||||
return;
|
||||
}
|
||||
|
||||
//draw overlay over advanced bar
|
||||
[fProgressEndAdvanced compositeToPoint: point operation: NSCompositeSourceOver];
|
||||
|
||||
|
|
|
@ -39,7 +39,9 @@
|
|||
NSUserDefaults * fDefaults;
|
||||
|
||||
IBOutlet NSMenu * fContextRow, * fContextNoRow;
|
||||
NSImage * fResumeOnIcon, * fResumeOffIcon, * fPauseOnIcon, * fPauseOffIcon,
|
||||
NSImage * fResumeOnIcon, * fResumeOffIcon,
|
||||
* fPauseOnIcon, * fPauseOffIcon,
|
||||
* fResumeNoWaitOnIcon, * fResumeNoWaitOffIcon,
|
||||
* fRevealOnIcon, * fRevealOffIcon;
|
||||
|
||||
NSMutableArray * fKeyStrokes;
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
fResumeOnIcon = [NSImage imageNamed: @"ResumeOn.png"];
|
||||
fResumeOffIcon = [NSImage imageNamed: @"ResumeOff.png"];
|
||||
fPauseOnIcon = [NSImage imageNamed: @"PauseOn.png"];
|
||||
fResumeNoWaitOnIcon = [NSImage imageNamed: @"ResumeNoWaitOn.png"];
|
||||
fResumeNoWaitOffIcon = [NSImage imageNamed: @"ResumeNoWaitOff.png"];
|
||||
fPauseOffIcon = [NSImage imageNamed: @"PauseOff.png"];
|
||||
fRevealOnIcon = [NSImage imageNamed: @"RevealOn.png"];
|
||||
fRevealOffIcon = [NSImage imageNamed: @"RevealOff.png"];
|
||||
|
@ -94,17 +96,20 @@
|
|||
{
|
||||
fClickPoint = [self convertPoint: [event locationInWindow] fromView: nil];
|
||||
|
||||
if ([event modifierFlags] & NSAlternateKeyMask)
|
||||
if (![self pointInPauseRect: fClickPoint] && ![self pointInRevealRect: fClickPoint])
|
||||
{
|
||||
[fController toggleAdvancedBar: self];
|
||||
fClickPoint = NSZeroPoint;
|
||||
}
|
||||
else if (![self pointInPauseRect: fClickPoint] && ![self pointInRevealRect: fClickPoint])
|
||||
{
|
||||
if ([self pointInMinimalStatusRect: fClickPoint])
|
||||
[(TorrentCell *)[[self tableColumnWithIdentifier: @"Torrent"] dataCell] toggleMinimalStatus];
|
||||
if ([event modifierFlags] & NSAlternateKeyMask)
|
||||
{
|
||||
[fController toggleAdvancedBar: self];
|
||||
fClickPoint = NSZeroPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([self pointInMinimalStatusRect: fClickPoint])
|
||||
[(TorrentCell *)[[self tableColumnWithIdentifier: @"Torrent"] dataCell] toggleMinimalStatus];
|
||||
|
||||
[super mouseDown: event];
|
||||
[super mouseDown: event];
|
||||
}
|
||||
}
|
||||
else;
|
||||
|
||||
|
@ -121,15 +126,22 @@
|
|||
{
|
||||
Torrent * torrent = [fTorrents objectAtIndex: row];
|
||||
|
||||
if ([torrent isPaused])
|
||||
[fController resumeTorrents: [NSArray arrayWithObject: torrent]];
|
||||
else if ([torrent isActive])
|
||||
if ([torrent isActive])
|
||||
[fController stopTorrents: [NSArray arrayWithObject: torrent]];
|
||||
else;
|
||||
else if ([torrent isPaused])
|
||||
{
|
||||
if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
|
||||
[fController resumeTorrentsNoWait: [NSArray arrayWithObject: torrent]];
|
||||
else if ([torrent waitingToStart])
|
||||
[fController stopTorrents: [NSArray arrayWithObject: torrent]];
|
||||
else
|
||||
[fController resumeTorrents: [NSArray arrayWithObject: torrent]];
|
||||
}
|
||||
else;
|
||||
}
|
||||
else if (sameRow && [self pointInRevealRect: point] && [self pointInRevealRect: fClickPoint])
|
||||
[[fTorrents objectAtIndex: row] revealData];
|
||||
else if ([event clickCount] == 2)
|
||||
else if ([event clickCount] == 2)
|
||||
{
|
||||
if ([self pointInIconRect: point])
|
||||
[[fTorrents objectAtIndex: row] revealData];
|
||||
|
@ -138,7 +150,7 @@
|
|||
}
|
||||
else;
|
||||
|
||||
[super mouseUp: event];
|
||||
[super mouseUp: event];
|
||||
|
||||
fClickPoint = NSZeroPoint;
|
||||
[self display];
|
||||
|
@ -184,6 +196,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void) flagsChanged: (NSEvent *) event
|
||||
{
|
||||
[self display];
|
||||
[super flagsChanged: event];
|
||||
}
|
||||
|
||||
- (void) insertText: (NSString *) text
|
||||
{
|
||||
//sort torrents by name before finding closest match
|
||||
|
@ -223,10 +241,17 @@
|
|||
torrent = [fTorrents objectAtIndex: i];
|
||||
rect = [self pauseRectForRow: i];
|
||||
|
||||
if ([torrent isPaused])
|
||||
image = NSPointInRect(fClickPoint, rect) ? fResumeOnIcon : fResumeOffIcon;
|
||||
else if ([torrent isActive])
|
||||
if ([torrent isActive])
|
||||
image = NSPointInRect(fClickPoint, rect) ? fPauseOnIcon : fPauseOffIcon;
|
||||
else if ([torrent isPaused])
|
||||
{
|
||||
if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask && [fDefaults boolForKey: @"Queue"])
|
||||
image = NSPointInRect(fClickPoint, rect) ? fResumeNoWaitOnIcon : fResumeNoWaitOffIcon;
|
||||
else if ([torrent waitingToStart])
|
||||
image = NSPointInRect(fClickPoint, rect) ? fPauseOnIcon : fPauseOffIcon;
|
||||
else
|
||||
image = NSPointInRect(fClickPoint, rect) ? fResumeOnIcon : fResumeOffIcon;
|
||||
}
|
||||
else
|
||||
image = nil;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# $Id$
|
||||
|
||||
TMPCFLAGS = -g -Wall -W -O3 -funroll-loops -D_FILE_OFFSET_BITS=64 \
|
||||
TMPCFLAGS = -g -Wall -W -D_FILE_OFFSET_BITS=64 \
|
||||
-D_LARGEFILE_SOURCE -D_GNU_SOURCE \
|
||||
-DSYS_$(shell echo $(SYSTEM) | tr a-z A-Z)
|
||||
TMPCXXFLAGS = $(TMPCFLAGS)
|
||||
|
|
|
@ -5,7 +5,7 @@ include ../mk/common.mk
|
|||
|
||||
SRCS = transmission.c bencode.c net.c tracker.c peer.c inout.c \
|
||||
metainfo.c sha1.c utils.c fdlimit.c clients.c completion.c \
|
||||
platform.c ratecontrol.c choking.c
|
||||
platform.c ratecontrol.c choking.c natpmp.c upnp.c http.c xml.c
|
||||
OBJS = $(SRCS:%.c=%.o)
|
||||
|
||||
CFLAGS += -D__TRANSMISSION__
|
||||
|
|
Loading…
Reference in New Issue