-rw-r--r-- | pumpkin/ARequest.m | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/pumpkin/ARequest.m b/pumpkin/ARequest.m new file mode 100644 index 0000000..439366e --- a/dev/null +++ b/pumpkin/ARequest.m | |||
@@ -0,0 +1,184 @@ | |||
1 | |||
2 | #import "pumpkin.h" | ||
3 | #import "ARequest.h" | ||
4 | #import "ReceiveXFer.h" | ||
5 | #import "SendXFer.h" | ||
6 | |||
7 | static void cbHost(CFHostRef h,CFHostInfoType hi,const CFStreamError *e,void *i) { | ||
8 | [(ARequest*)i hostCallbackWithHost:h info:hi andError:e]; | ||
9 | } | ||
10 | |||
11 | |||
12 | @implementation ARequest | ||
13 | @synthesize requestIsGet; | ||
14 | @synthesize doTouchMe; | ||
15 | @synthesize statusLabel; | ||
16 | @synthesize errorLabel; | ||
17 | |||
18 | @synthesize localFile; | ||
19 | @synthesize remoteHost; | ||
20 | @synthesize remotePort; | ||
21 | @synthesize remoteFile; | ||
22 | @synthesize xferType; | ||
23 | @synthesize blockSize; | ||
24 | @synthesize timeout; | ||
25 | |||
26 | @synthesize remoteHostBox; | ||
27 | |||
28 | -(void)unhost { | ||
29 | if(!cfhost) return; | ||
30 | CFHostCancelInfoResolution(cfhost, kCFHostAddresses); | ||
31 | CFHostUnscheduleFromRunLoop(cfhost, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); | ||
32 | CFRelease(cfhost); | ||
33 | cfhost = nil; | ||
34 | } | ||
35 | -(void)loadDefaults { | ||
36 | id d = [NSUserDefaultsController.sharedUserDefaultsController values]; | ||
37 | self.remotePort = [d valueForKey:@"remotePort"]; | ||
38 | self.blockSize = [d valueForKey:@"blockSize"]; | ||
39 | self.xferType = [d valueForKey:@"xferType"]; | ||
40 | self.remoteHost = [d valueForKey:@"remoteHost"]; | ||
41 | self.timeout = [d valueForKey:@"timeout"]; | ||
42 | |||
43 | self.localFile = [[d valueForKey:@"tftpRoot"] stringByAppendingString:@"/"]; | ||
44 | } | ||
45 | -(void)saveDefaults { | ||
46 | NSUserDefaultsController *dc = [NSUserDefaultsController sharedUserDefaultsController]; | ||
47 | id d = dc.values; | ||
48 | [d setValue:self.remotePort forKey:@"remotePort"]; | ||
49 | [d setValue:self.remoteHost forKey:@"remoteHost"]; | ||
50 | [d setValue:self.blockSize forKey:@"blockSize"]; | ||
51 | [d setValue:self.xferType forKey:@"xferType"]; | ||
52 | [d setValue:self.timeout forKey:@"timeout"]; | ||
53 | [dc save:self]; | ||
54 | } | ||
55 | |||
56 | |||
57 | - (IBAction)startXfer:(id)sender { | ||
58 | if(!(cfhost = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)remoteHost))) { | ||
59 | self.errorLabel = @"failed to even try to resolve."; | ||
60 | return; | ||
61 | } | ||
62 | struct CFHostClientContext hc; | ||
63 | hc.version=0; hc.info=self; hc.retain=0;hc.release=0; | ||
64 | hc.copyDescription=0; | ||
65 | CFHostSetClient(cfhost, cbHost, &hc); | ||
66 | CFHostScheduleWithRunLoop(cfhost, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); | ||
67 | CFStreamError e; | ||
68 | if(!CFHostStartInfoResolution(cfhost, kCFHostAddresses, &e)) { | ||
69 | self.errorLabel = @"failed to start host resolution."; | ||
70 | [self unhost]; | ||
71 | return; | ||
72 | } | ||
73 | self.statusLabel = @"resolving remote host…"; | ||
74 | self.doTouchMe = NO; | ||
75 | } | ||
76 | |||
77 | -(void)hostCallbackWithHost:(CFHostRef)h info:(CFHostInfoType)hi andError:(const CFStreamError *)e { | ||
78 | NSString *el = nil; | ||
79 | CFArrayRef aa = nil; | ||
80 | __block struct sockaddr_in peer; | ||
81 | do { | ||
82 | if(e && (e->domain || e->error)) { | ||
83 | el=@"failed to resolve remote address"; break; | ||
84 | } | ||
85 | Boolean hbr; | ||
86 | aa = CFHostGetAddressing(cfhost, &hbr); | ||
87 | if(!(hbr && aa && CFArrayGetCount(aa))) { | ||
88 | el=@"failed to find remote address"; break; | ||
89 | } | ||
90 | peer.sin_addr.s_addr=INADDR_NONE; [(NSArray*)aa enumerateObjectsUsingBlock:^(NSData *o,NSUInteger i,BOOL *s) { | ||
91 | const struct sockaddr_in *sin = o.bytes; | ||
92 | if(sin->sin_family!=AF_INET) return; | ||
93 | memmove(&peer,sin,sizeof(peer)); | ||
94 | *s = YES; | ||
95 | }]; | ||
96 | if(peer.sin_addr.s_addr==INADDR_NONE) { | ||
97 | el=@"found no ipv4 address"; break; | ||
98 | } | ||
99 | peer.sin_port = htons([remotePort unsignedIntValue]); | ||
100 | }while(false); | ||
101 | [self unhost]; | ||
102 | if(el) { | ||
103 | self.errorLabel = el; self.doTouchMe = YES; return; | ||
104 | } | ||
105 | [self saveDefaults]; | ||
106 | [[[requestIsGet?ReceiveXFer.class:SendXFer.class alloc] | ||
107 | initWithLocalFile:localFile peerAddress:&peer remoteFile:remoteFile xferType:xferType blockSize:blockSize.unsignedIntValue andTimeout:timeout.intValue] | ||
108 | autorelease]; | ||
109 | [self.window performClose:nil]; | ||
110 | } | ||
111 | |||
112 | - (IBAction)pickFile:(id)sender { | ||
113 | NSSavePanel *p = nil; | ||
114 | if(requestIsGet) { | ||
115 | p = [NSSavePanel savePanel]; | ||
116 | p.canCreateDirectories = YES; | ||
117 | }else{ | ||
118 | NSOpenPanel *pp = [NSOpenPanel openPanel]; | ||
119 | pp.canChooseDirectories = NO; | ||
120 | pp.canChooseFiles = YES; | ||
121 | pp.allowsMultipleSelection = NO; | ||
122 | p = pp; | ||
123 | } | ||
124 | p.prompt = @"Pick the local file"; | ||
125 | if([p runModal]!=NSFileHandlingPanelOKButton) return; | ||
126 | self.localFile = p.URL.path; | ||
127 | } | ||
128 | |||
129 | - (ARequest*) initWithGet:(BOOL)gr { | ||
130 | if(!(self = [super initWithWindowNibName:@"ARequest"])) return self; | ||
131 | self.doTouchMe = YES; | ||
132 | cfhost = nil; | ||
133 | requestIsGet = gr; | ||
134 | if(requestIsGet) { | ||
135 | self.window.title = @"Get file from remote TFTP server"; | ||
136 | self.window.initialFirstResponder = remoteHostBox; | ||
137 | }else{ | ||
138 | self.window.title = @"Put file to remote TFTP server"; | ||
139 | } | ||
140 | [self loadDefaults]; | ||
141 | [self addObserver:self forKeyPath:@"localFile" options:0 context:0]; | ||
142 | [self addObserver:self forKeyPath:@"remoteFile" options:0 context:0]; | ||
143 | return [self retain]; | ||
144 | } | ||
145 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { | ||
146 | if(requestIsGet) { | ||
147 | if([keyPath isEqualToString:@"remoteFile"]) { | ||
148 | if(self.remoteFile.length) { | ||
149 | self.localFile= [([self.localFile hasSuffix:@"/"] | ||
150 | ?self.localFile | ||
151 | :[self.localFile stringByDeletingLastPathComponent]) | ||
152 | stringByAppendingPathComponent:self.remoteFile.lastPathComponent]; | ||
153 | }else | ||
154 | self.localFile=[[self.localFile stringByDeletingLastPathComponent] stringByAppendingString:@"/"]; | ||
155 | } | ||
156 | }else{ | ||
157 | if([keyPath isEqualToString:@"localFile"]) { | ||
158 | self.remoteFile=[self.localFile hasSuffix:@"/"] | ||
159 | ?@"":self.localFile.lastPathComponent; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | + (ARequest*) aRequestWithGet:(BOOL)gr { | ||
165 | return [[[ARequest alloc] initWithGet:gr] autorelease]; | ||
166 | } | ||
167 | |||
168 | static void popMeUp(BOOL g) { | ||
169 | [[ARequest aRequestWithGet:g].window makeKeyAndOrderFront:nil]; | ||
170 | } | ||
171 | + (void)getFile { popMeUp(YES); } | ||
172 | + (void)putFile { popMeUp(NO); } | ||
173 | |||
174 | - (void)windowDidLoad { | ||
175 | } | ||
176 | |||
177 | - (void)windowWillClose:(NSNotification*)n { | ||
178 | [self unhost]; | ||
179 | [self removeObserver:self forKeyPath:@"localFile" context:0]; | ||
180 | [self removeObserver:self forKeyPath:@"remoteFile" context:0]; | ||
181 | [self release]; | ||
182 | } | ||
183 | |||
184 | @end | ||