-rw-r--r-- | pumpkin/SendXFer.m | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/pumpkin/SendXFer.m b/pumpkin/SendXFer.m new file mode 100644 index 0000000..9a1d85b --- a/dev/null +++ b/pumpkin/SendXFer.m | |||
@@ -0,0 +1,156 @@ | |||
1 | #import "SendXFer.h" | ||
2 | #import "StringsAttached.h" | ||
3 | #import "ConfirmRequest.h" | ||
4 | |||
5 | @implementation SendXFer | ||
6 | |||
7 | -(SendXFer*)initWithLocalFile:(NSString *)lf peerAddress:(const struct sockaddr_in *)pa remoteFile:(NSString *)rf xferType:(NSString *)xt blockSize:(uint16_t)bs andTimeout:(int)to { | ||
8 | if(!(self = [super init])) return self; | ||
9 | xferPrefix = @"⬆"; | ||
10 | retryTimeout = to; | ||
11 | localFile = lf; | ||
12 | memmove(&peer,pa,sizeof(peer)); | ||
13 | if(!(theFile = [[NSFileHandle fileHandleForReadingAtPath:localFile] retain])) { | ||
14 | [pumpkin log:@"Failed to open '%@', transfer aborted.",localFile]; | ||
15 | return self; | ||
16 | } | ||
17 | |||
18 | long xb = ((xferSize=[theFile seekToEndOfFile])/blockSize)+1; | ||
19 | if(xb > UINT16_MAX) { | ||
20 | [pumpkin log:@"file is too big (%lld bytes) and will take %ld blocks to be sent with block size of %d bytes",xferSize,xb,blockSize]; | ||
21 | return self; | ||
22 | } | ||
23 | xferBlocks = xb; | ||
24 | |||
25 | [self createSocket]; | ||
26 | NSMutableDictionary *o = [NSMutableDictionary dictionaryWithCapacity:4]; | ||
27 | [o setValue:[NSString stringWithFormat:@"%u",bs] forKey:@"blksize"]; | ||
28 | [o setValue:[NSString stringWithFormat:@"%llu",xferSize] forKey:@"tsize"]; | ||
29 | [o setValue:[NSString stringWithFormat:@"%d",(int)retryTimeout] forKey:@"timeout"]; | ||
30 | state = xferStateConnecting; | ||
31 | [self queuePacket:[TFTPPacket packetWRQWithFile:xferFilename=rf xferType:xferType=xt andOptions:o]]; | ||
32 | [self appear]; | ||
33 | return self; | ||
34 | } | ||
35 | |||
36 | -(SendXFer*)initWithPeer:(struct sockaddr_in *)sin andPacket:(TFTPPacket*)p { | ||
37 | if(!(self = [super initWithPeer:sin andPacket:p])) return self; | ||
38 | xferPrefix = @"⬆"; | ||
39 | xferFilename = [p.rqFilename retain]; xferType = [p.rqType retain]; | ||
40 | [pumpkin log:@"'%@' of type '%@' is requested from %@", | ||
41 | xferFilename, xferType, [NSString stringWithSocketAddress:&peer] ]; | ||
42 | |||
43 | [self createSocket]; | ||
44 | [self appear]; | ||
45 | |||
46 | if(![self makeLocalFileName:xferFilename]) | ||
47 | return self; | ||
48 | |||
49 | switch([[pumpkin.theDefaults.values valueForKey:@"rrqBehavior"] intValue]) { | ||
50 | case onRRQDeny: [self goOnWithVerdict:verdictDeny]; break; | ||
51 | case onRRQGive: [self goOnWithVerdict:verdictAllow]; break; | ||
52 | default: | ||
53 | [ConfirmRequest confirmationWithXfer:self]; | ||
54 | break; | ||
55 | } | ||
56 | return self; | ||
57 | } | ||
58 | -(void)goOnWithVerdict:(int)verdict { | ||
59 | if(verdict!=verdictAllow) { | ||
60 | [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrAccessViolation andMessage:@"Access denied"]]; | ||
61 | return; | ||
62 | } | ||
63 | if(!(theFile = [[NSFileHandle fileHandleForReadingAtPath:localFile] retain])) { | ||
64 | [self queuePacket:[TFTPPacket packetErrorWithErrno:errno andFallback:@"couldn't open file"]]; | ||
65 | return; | ||
66 | } | ||
67 | xferSize = [theFile seekToEndOfFile]; | ||
68 | NSMutableDictionary *o = [NSMutableDictionary dictionaryWithCapacity:4]; | ||
69 | [[initialPacket rqOptions] enumerateKeysAndObjectsUsingBlock:^(NSString* k, NSString* v, BOOL *stop) { | ||
70 | if([k isEqualToString:@"blksize"]) { | ||
71 | [o setValue:[NSString stringWithFormat:@"%u",blockSize=v.intValue] forKey:@"blksize"]; | ||
72 | }else if([k isEqualToString:@"tsize"]) { | ||
73 | [o setValue:[NSString stringWithFormat:@"%lld",xferSize] forKey:@"tsize"]; | ||
74 | }else if([k isEqualToString:@"timeout"]) { | ||
75 | [o setValue:[NSString stringWithFormat:@"%d",v.intValue] forKey:@"timeout"]; | ||
76 | retryTimeout = v.intValue; | ||
77 | }else | ||
78 | [pumpkin log:@"Unknown option '%@' with value '%@'. Ignoring.",k,v]; | ||
79 | }]; | ||
80 | long xb = (xferSize/blockSize)+1; | ||
81 | if(xb > UINT16_MAX) { | ||
82 | [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrUndefined andMessage:[NSString stringWithFormat:@"file is too big (%lld bytes) and will take %ld blocks to be sent with block size of %d bytes",xferSize,xb,blockSize]]]; | ||
83 | return; | ||
84 | } | ||
85 | xferBlocks = xb; | ||
86 | state = xferStateXfer; | ||
87 | if(o.count) { | ||
88 | [self queuePacket:[TFTPPacket packetOACKWithOptions:o]]; | ||
89 | }else{ | ||
90 | [self xfer]; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | - (void) xfer { | ||
95 | NSAssert(theFile,@"no file!"); | ||
96 | [theFile seekToFileOffset:acked*blockSize]; | ||
97 | [self queuePacket:[TFTPPacket packetDataWithBlock:acked+1 andData:[theFile readDataOfLength:blockSize]]]; | ||
98 | } | ||
99 | |||
100 | - (void) eatTFTPPacket:(TFTPPacket*)p from:(struct sockaddr_in*)sin{ | ||
101 | if(state==xferStateConnecting) { | ||
102 | peer.sin_port = sin->sin_port; | ||
103 | [self updateView]; | ||
104 | }else if(![self isPeer:sin]) { | ||
105 | [pumpkin log:@"Packet from unexpected source (%@) recevied",[NSString stringWithSocketAddress:sin]]; | ||
106 | return; | ||
107 | } | ||
108 | switch(p.op) { | ||
109 | case tftpOpACK: | ||
110 | if(state==xferStateShutdown || ( (acked=p.block)==xferBlocks && (state=xferStateShutdown) ) ) { | ||
111 | CFSocketEnableCallBacks(sockie, kCFSocketWriteCallBack); | ||
112 | return; | ||
113 | } | ||
114 | [self updateView]; | ||
115 | [self xfer]; | ||
116 | break; | ||
117 | case tftpOpERROR: | ||
118 | [pumpkin log:@"Error %u:%@",p.rqCode, p.rqMessage]; | ||
119 | [self updateView]; | ||
120 | [self disappear]; | ||
121 | return; | ||
122 | case tftpOpOACK: | ||
123 | if(acked) { | ||
124 | [pumpkin log:@"It's a bit too late to acknowledge options, ignoring OACK packet"]; | ||
125 | break; | ||
126 | } | ||
127 | { | ||
128 | __block BOOL a=NO; | ||
129 | [p.rqOptions enumerateKeysAndObjectsUsingBlock:^(NSString *k,NSString *v,BOOL *s) { | ||
130 | if([k isEqualToString:@"blksize"]) | ||
131 | blockSize = v.intValue; | ||
132 | else if([k isEqualToString:@"tsize"]) { | ||
133 | }else if([k isEqualToString:@"timeout"]) | ||
134 | retryTimeout = v.intValue; | ||
135 | else{ | ||
136 | [pumpkin log:@"Totally unknown option '%@' with value '%@' acknowledged by peer",k,v]; | ||
137 | a=YES; | ||
138 | } | ||
139 | }]; | ||
140 | if(a) { | ||
141 | [self abort]; | ||
142 | break; | ||
143 | } | ||
144 | state = xferStateXfer; | ||
145 | [self updateView]; | ||
146 | [self xfer]; | ||
147 | } | ||
148 | break; | ||
149 | default: | ||
150 | [pumpkin log:@"Totaly unexpected opcode %d received",p.op]; | ||
151 | break; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | |||
156 | @end | ||