forked from mus1cjunk1e/NativaPlus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRTSCGIOperation.m
333 lines (280 loc) · 7.97 KB
/
RTSCGIOperation.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/******************************************************************************
* Nativa - MacOS X UI for rtorrent
* http://www.aramzamzam.net
*
* Copyright Solomenchuk V. 2010.
* Solomenchuk Vladimir <[email protected]>
*
* Licensed under the GPL, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/gpl-3.0.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/
#import "RTSCGIOperation.h"
#import "RTorrentController.h"
#import "RTConnection.h"
#import "XMLRPCEncoder.h"
#import "XMLRPCTreeBasedParser.h"
#import "NSStringSCGIAdditions.h"
//#define MAX_RESPONSE_SIZE 524288
#define MAX_RESPONSE_SIZE 10485760
@interface RTSCGIOperation ()
- (void) requestDidSent;
- (void) responseDidReceived;
- (void) setError:(NSString*) error;
- (void) finish;
- (void) runResponse:(NSData*) data error:(NSString*) error;
- (NSArray*) arguments;
- (void) setArguments:(NSArray *) value;
- (NSString*) command;
- (void) setCommand:(NSString *) value;
@property (nonatomic, assign) NSAutoreleasePool *pool;
@property (retain) RTorrentController *controller;
@property (retain) id<RTorrentCommand> operation;
@end
@implementation RTSCGIOperation
@synthesize pool, handler, controller, operation;
- (id)initWithCommand:(RTorrentController *)control command:(NSString*)cmd arguments:(NSArray*)args handler:(void(^)(id data, NSString* error)) h;
{
if (self = [super init])
{
[self setController: control];
[self setOperation: nil];
[self setArguments: args];
[self setCommand: cmd];
[self setHandler: h];
}
return self;
}
- (id)initWithOperation:(RTorrentController *)control operation:(id<RTorrentCommand>) oper;
{
if (self = [super init])
{
[self setController: control];
[self setOperation: oper];
[self setCommand: nil];
[self setArguments: nil];
}
return self;
}
- (void)main;
{
self.pool = [[NSAutoreleasePool alloc] init];
_isExecuting = YES;
oStream = nil;
iStream = nil;
XMLRPCEncoder* xmlrpc_request = [[XMLRPCEncoder alloc] init];
[xmlrpc_request setMethod:[self command] withParameters:[self arguments]];
NSString* scgi_req = [xmlrpc_request encode];
[xmlrpc_request release];
_writtenBytesCounter = 0;
// NSLog(@"request: %@", scgi_req);
_requestData = [scgi_req encodeSCGI];
[_requestData retain];
NSString *error;
if ([controller.connection openStreams:&iStream oStream:&oStream delegate:self error:&error])
{
[iStream retain];
[oStream retain];
time_t startTime = time(NULL) * 1000;
time_t timeout = 1000;
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
if ((time(NULL) * 1000 - startTime)>timeout)
{
if (_isExecuting)
[self setError:NSLocalizedString(@"Network timeout", "Network -> error")];
break;
}
} while (_isExecuting);
}
else
[self setError:error];
[pool release];
self.pool = nil;
}
- (void)dealloc
{
[_responseData release];
[self setCommand: nil];
[self setArguments: nil];
[self setHandler: nil];
[self setOperation:nil];
[self setController:nil];
[_requestData release];
[super dealloc];
}
- (void) setArguments:(NSArray *) value
{
if (arguments == value)
return;
[arguments release];
arguments = [value retain];
}
- (NSArray*) arguments
{
return operation==nil?arguments:[operation arguments];
}
-(void) setCommand:(NSString *) value
{
if (command == value)
return;
[command release];
command = [value retain];
}
- (NSString*) command
{
return operation==nil?command:[operation command];
}
- (void) requestDidSent;
{
if (oStream != nil)
{
oStream.delegate = nil;
[oStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[oStream close];
[oStream release];
oStream = nil;
}
}
- (void) responseDidReceived;
{
if (iStream != nil)
{
iStream.delegate = nil;
[iStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[iStream close];
[iStream release];
iStream = nil;
}
}
- (void)finish;
{
_isExecuting = NO;
}
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
{
switch(eventCode) {
case NSStreamEventHasSpaceAvailable:
{
if (stream == oStream)
{
uint8_t *readBytes = (uint8_t *)[_requestData bytes];
readBytes += _writtenBytesCounter; // instance variable to move pointer
int data_len = [_requestData length];
unsigned int len = ((data_len - _writtenBytesCounter >= 1024) ?
1024 : (data_len-_writtenBytesCounter));
uint8_t buf[len];
(void)memcpy(buf, readBytes, len);
len = [oStream write:(const uint8_t *)buf maxLength:len];
_writtenBytesCounter += len;
if (_writtenBytesCounter == data_len)
[self requestDidSent];
}
break;
}
case NSStreamEventHasBytesAvailable:
{
if ([_responseData length]>MAX_RESPONSE_SIZE)
{
[self setError:@"Response too large to fit into memory"];
return;
}
if(!_responseData)
_responseData = [[NSMutableData data] retain];
uint8_t buf[1024];
NSInteger len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if (len>0)
[_responseData appendBytes:buf length:len];
break;
}
case NSStreamEventEndEncountered:
{
[self responseDidReceived];
NSInteger len = [_responseData length];
NSInteger start = 0;
uint8_t *buf = (uint8_t *)[_responseData bytes];
BOOL headerDividerFound = NO;
//look for \n\n or \r\n\r\n
if (len)
{
BOOL carriageReturnFound = NO;
for (int i=0;i<len;i++)
{
if (buf[i]=='\r') //skip single \r's
continue;
if (buf[i]=='\n')
{
if (carriageReturnFound)
{
headerDividerFound = YES;
start = i+1;
break;
}
else
carriageReturnFound = YES;
}
else
carriageReturnFound = NO;
}
if (!headerDividerFound || len <= start)
{
[self setError:NSLocalizedString(@"Invalid rtorrent response (is rtorrent running?)", "Network -> error")];
break;
}
}
else
{
[self setError:NSLocalizedString(@"Invalid rtorrent response (is rtorrent running?)", "Network -> error")];
break;
}
NSData *body = [NSData dataWithBytes:(buf+start) length:(len-start)];
XMLRPCTreeBasedParser* xmlrpcResponse = [[XMLRPCTreeBasedParser alloc] initWithData: body];
// NSLog(@"%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
id result = [xmlrpcResponse parse];
BOOL fault = [xmlrpcResponse isFault];
[xmlrpcResponse release];
if (result == nil)//empty response, occured with bad xml. network error?
{
[self setError:NSLocalizedString(@"Invalid rtorrent response (is rtorrent running?)", "Network -> error")];
return;
}
if (fault)
[self setError:result];
else
{
[self runResponse:result error:nil];
}
break;
}
case NSStreamEventErrorOccurred:
{
[self setError:[[stream streamError] localizedDescription]];
break;
}
}
}
- (void) setError:(NSString*) error;
{
[self finish];
[self requestDidSent];
[self responseDidReceived];
[self runResponse:nil error:error];
}
- (void) runResponse:(NSData*) data error:(NSString*) error
{
[self finish];
if (operation == nil && handler)
handler(data, error);
else
[operation processResponse:data error:error];
}
@end