summaryrefslogtreecommitdiffabout
path: root/pumpkin/XFer.m
Unidiff
Diffstat (limited to 'pumpkin/XFer.m') (more/less context) (ignore whitespace changes)
-rw-r--r--pumpkin/XFer.m205
1 files changed, 205 insertions, 0 deletions
diff --git a/pumpkin/XFer.m b/pumpkin/XFer.m
new file mode 100644
index 0000000..6803ade
--- a/dev/null
+++ b/pumpkin/XFer.m
@@ -0,0 +1,205 @@
1
2#import "XFer.h"
3#import "TFTPPacket.h"
4#import "StringsAttached.h"
5
6static void cbXfer(CFSocketRef sockie,CFSocketCallBackType cbt,CFDataRef cba,
7 const void *cbd,void *i) {
8 [(XFer*)i callbackWithType:cbt addr:cba data:cbd];
9}
10
11@implementation XFer
12@synthesize initialPacket;
13@synthesize xferFilename;
14@synthesize localFile;
15@synthesize xferPrefix;
16
17- (id) init {
18 if(!(self = [super init])) return self;
19 blockSize = 512;
20 sockie = NULL;
21 theFile = nil;
22 acked = 0;
23 xferSize = 0; xferBlocks = 0;
24 xferType = nil; xferFilename = nil;
25 state = xferStateNone;
26 pumpkin = NSApplication.sharedApplication.delegate;
27 queue = [[NSMutableArray alloc]initWithCapacity:4];
28 localFile = nil;
29 retryTimeout = 3;
30 giveupTimeout = [[[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey:@"giveUpTimeout"] intValue];
31 lastPacket = nil; retryTimer = nil;
32 giveupTimer = nil;
33 initialPacket = nil;
34 return self;
35
36}
37
38- (id) initWithPeer:(struct sockaddr_in *)sin andPacket:(TFTPPacket*)p {
39 if(!(self=[self init])) return self;
40 memmove(&peer,sin,sizeof(peer));
41 initialPacket = [p retain];
42 return self;
43}
44
45- (struct sockaddr_in*)peer { return &peer; }
46
47- (BOOL) makeLocalFileName:(NSString *)xf {
48 NSString *fn = [xf stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
49 if([fn hasPrefix:@"../"] || [fn hasSuffix:@"/.."] || [fn rangeOfString:@"/../"].location!=NSNotFound) {
50 [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrAccessViolation andMessage:@"bad path"]];
51 return NO;
52 }
53 localFile = [[[pumpkin.theDefaults.values valueForKey:@"tftpRoot"] stringByAppendingPathComponent:fn] retain];
54 return YES;
55}
56
57- (void) retryTimeout {
58 [self queuePacket:lastPacket]; [lastPacket release]; lastPacket = nil;
59}
60- (void) giveUp {
61 [pumpkin log:@"Connection timeout for '%@'",xferFilename];
62 [self abort];
63}
64- (void) renewHope {
65 if(giveupTimer) {
66 [giveupTimer invalidate]; [giveupTimer release];
67 }
68 giveupTimer = [[NSTimer scheduledTimerWithTimeInterval:giveupTimeout target:self selector:@selector(giveUp) userInfo:nil repeats:NO] retain];
69}
70
71- (void) callbackWithType:(CFSocketCallBackType)t addr:(CFDataRef)a data:(const void *)d {
72 if(!giveupTimer) [self renewHope];
73 if(retryTimer) {
74 [retryTimer release]; [retryTimer invalidate]; retryTimer = nil;
75 }
76 switch (t) {
77 case kCFSocketWriteCallBack:
78 if(queue.count) {
79 TFTPPacket *p = queue[0];
80 CFSocketError r = CFSocketSendData(sockie, (CFDataRef)[NSData dataWithBytesNoCopy:&peer length:sizeof(peer) freeWhenDone:NO], (CFDataRef)[NSData dataWithData:p.data], 0);
81 if(r!=kCFSocketSuccess)
82 [pumpkin log:@"Failed to send data, error %d",errno];
83 if(!(p.op==tftpOpDATA || p.op==tftpOpERROR)) {
84 if(lastPacket) [lastPacket release];
85 lastPacket = [p retain];
86 if(retryTimer) {
87 [retryTimer invalidate]; [retryTimer release];
88 }
89 retryTimer = [[NSTimer scheduledTimerWithTimeInterval:retryTimeout target:self selector:@selector(retryTimeout) userInfo:nil repeats:NO] retain];
90 }else{
91 [lastPacket release]; lastPacket = nil;
92 }
93 [queue removeObjectAtIndex:0];
94 if([queue count] || state==xferStateShutdown)
95 CFSocketEnableCallBacks(sockie, kCFSocketWriteCallBack);
96 }else if(state==xferStateShutdown) {
97 [pumpkin log:@"%@ Transfer of '%@' finished.",xferPrefix,xferFilename];
98 [self disappear];
99 }
100 break;
101 case kCFSocketDataCallBack:
102 [self renewHope];
103 [self eatTFTPPacket:[TFTPPacket packetWithData:(NSData*)d] from:(struct sockaddr_in*)CFDataGetBytePtr(a)];
104 break;
105 default:
106 NSLog(@"unhandled %lu callback",t);
107 break;
108 }
109}
110
111- (BOOL) createSocket {
112 CFSocketContext ctx;
113 ctx.version=0; ctx.info=self; ctx.retain=0; ctx.release=0; ctx.copyDescription=0;
114 sockie = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM, IPPROTO_UDP,
115 kCFSocketReadCallBack|kCFSocketWriteCallBack|kCFSocketDataCallBack,
116 cbXfer, &ctx);
117 if(!sockie) return NO;
118 struct sockaddr_in a; memset(&a, 0, sizeof(a));
119 a.sin_family = AF_INET;
120 if(CFSocketSetAddress(sockie, (CFDataRef)[NSData dataWithBytesNoCopy:&a length:sizeof(a) freeWhenDone:NO])
121 !=kCFSocketSuccess) {
122 [pumpkin log:@"failed to set socket address"];
123 return NO;
124 }
125 runloopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sockie, 0);
126 CFRunLoopAddSource(CFRunLoopGetCurrent(), runloopSource, kCFRunLoopDefaultMode);
127 return YES;
128}
129
130- (void) queuePacket:(TFTPPacket*)p {
131 [queue addObject:p];
132 CFSocketEnableCallBacks(sockie, kCFSocketWriteCallBack|kCFSocketReadCallBack);
133 if(p.op==tftpOpERROR) state = xferStateShutdown;
134}
135
136- (void) goOnWithVerdict:(int)verdict {
137 NSAssert(false,@"unimplemented goOnWithVerdict");
138}
139
140- (void) eatTFTPPacket:(TFTPPacket*)p from:(struct sockaddr_in*)sin {
141 NSAssert(false,@"unimplemented eatTFTPPacket");
142}
143-(void) abort {
144 [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrUndefined andMessage:@"transfer cancelled"]];
145}
146
147- (id) cellValueForColumn:(NSString*)ci {
148 if([ci isEqualToString:@"fileName"]) {
149 return [NSString stringWithFormat:@"%@ %@",xferPrefix,xferFilename];
150 }else if([ci isEqualToString:@"xferType"]) {
151 return xferType;
152 }else if([ci isEqualToString:@"peerAddress"]) {
153 switch (state) {
154 case xferStateConnecting: return [NSString stringWithHostAddress:&peer];
155 default: return [NSString stringWithSocketAddress:&peer];
156 }
157 }else if([ci isEqualToString:@"ackBytes"]) {
158 return [NSString stringWithFormat:@"%u",acked*blockSize];
159 }else if([ci isEqualToString:@"xferSize"]) {
160 return xferSize?[NSString stringWithFormat:@"%llu",xferSize]:nil;
161 }
162 return nil;
163}
164
165- (void) updateView {
166 [pumpkin updateXfers];
167}
168- (void) appear {
169 [pumpkin registerXfer:self];
170}
171- (void) disappear {
172 if(retryTimer) {
173 [retryTimer invalidate]; [retryTimer release]; retryTimer = nil;
174 }
175 if(giveupTimer) {
176 [giveupTimer invalidate]; [giveupTimer release]; retryTimer = nil;
177 }
178 [pumpkin unregisterXfer:self];
179}
180
181- (BOOL) isPeer:(struct sockaddr_in*)sin {
182 return sin->sin_len==peer.sin_len && !memcmp(sin,&peer,sin->sin_len);
183}
184
185-(void)dealloc {
186 if(runloopSource) {
187 CFRunLoopSourceInvalidate(runloopSource);
188 CFRelease(runloopSource);
189 }
190 if(sockie) {
191 CFSocketInvalidate(sockie);
192 CFRelease(sockie);
193 }
194 [queue release];
195 if(theFile) [theFile release];
196 if(xferFilename) [xferFilename release];
197 if(xferType) [xferType release];
198 if(lastPacket) [lastPacket release];
199 if(initialPacket) [initialPacket release];
200 if(localFile) [localFile release];
201 [super dealloc];
202}
203
204
205@end