1/******************************************************************************
2 * $Id: PortChecker.m 13492 2012-09-10 02:37:29Z livings124 $
3 *
4 * Copyright (c) 2006-2012 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "PortChecker.h"
26
27#define CHECKER_URL(port) [NSString stringWithFormat: @"http://portcheck.transmissionbt.com/%ld", port]
28#define CHECK_FIRE 3.0
29
30@interface PortChecker (Private)
31
32- (void) startProbe: (NSTimer *) timer;
33
34- (void) callBackWithStatus: (port_status_t) status;
35
36@end
37
38@implementation PortChecker
39
40- (id) initForPort: (NSInteger) portNumber delay: (BOOL) delay withDelegate: (id) delegate
41{
42    if ((self = [super init]))
43    {
44        fDelegate = delegate;
45        
46        fStatus = PORT_STATUS_CHECKING;
47        
48        fTimer = [[NSTimer scheduledTimerWithTimeInterval: CHECK_FIRE target: self selector: @selector(startProbe:) userInfo: [NSNumber numberWithInteger: portNumber] repeats: NO] retain];
49        if (!delay)
50            [fTimer fire];
51    }
52    
53    return self;
54}
55
56- (void) dealloc
57{
58    [fTimer invalidate];
59    [fTimer release];
60    
61    [fConnection release];
62    [fPortProbeData release];
63    [super dealloc];
64}
65
66- (port_status_t) status
67{
68    return fStatus;
69}
70
71- (void) cancelProbe
72{
73    [fTimer invalidate];
74    [fTimer release];
75    fTimer = nil;
76    
77    [fConnection cancel];
78}
79
80- (void) connection: (NSURLConnection *) connection didReceiveResponse: (NSURLResponse *) response
81{
82    [fPortProbeData setLength: 0];
83}
84
85- (void) connection: (NSURLConnection *) connection didReceiveData: (NSData *) data
86{
87    [fPortProbeData appendData: data];
88}
89
90- (void) connection: (NSURLConnection *) connection didFailWithError: (NSError *) error
91{
92    NSLog(@"Unable to get port status: connection failed (%@)", [error localizedDescription]);
93    [self callBackWithStatus: PORT_STATUS_ERROR];
94}
95
96- (void) connectionDidFinishLoading: (NSURLConnection *) connection
97{
98    NSString * probeString = [[NSString alloc] initWithData: fPortProbeData encoding: NSUTF8StringEncoding];
99    [fPortProbeData release];
100    fPortProbeData = nil;
101    
102    if (probeString)
103    {
104        if ([probeString isEqualToString: @"1"])
105            [self callBackWithStatus: PORT_STATUS_OPEN];
106        else if ([probeString isEqualToString: @"0"])
107            [self callBackWithStatus: PORT_STATUS_CLOSED];
108        else
109        {
110            NSLog(@"Unable to get port status: invalid response (%@)", probeString);
111            [self callBackWithStatus: PORT_STATUS_ERROR];
112        }
113        [probeString release];
114    }
115    else
116    {
117        NSLog(@"Unable to get port status: invalid data received");
118        [self callBackWithStatus: PORT_STATUS_ERROR];
119    }
120}
121
122@end
123
124@implementation PortChecker (Private)
125
126- (void) startProbe: (NSTimer *) timer
127{
128    [fTimer release];
129    fTimer = nil;
130    
131    NSURLRequest * portProbeRequest = [NSURLRequest requestWithURL: [NSURL URLWithString: CHECKER_URL([[timer userInfo] integerValue])]
132                                        cachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval: 15.0];
133    
134    if ((fConnection = [[NSURLConnection alloc] initWithRequest: portProbeRequest delegate: self]))
135        fPortProbeData = [[NSMutableData alloc] init];
136    else
137    {
138        NSLog(@"Unable to get port status: failed to initiate connection");
139        [self callBackWithStatus: PORT_STATUS_ERROR];
140    }
141}
142
143- (void) callBackWithStatus: (port_status_t) status
144{
145    fStatus = status;
146    
147    if (fDelegate && [fDelegate respondsToSelector: @selector(portCheckerDidFinishProbing:)])
148        [fDelegate performSelectorOnMainThread: @selector(portCheckerDidFinishProbing:) withObject: self waitUntilDone: NO];
149}
150
151@end
152
153