-rw-r--r-- | pumpkin/XFer.m | 205 |
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 | |||
6 | static 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 | ||