summaryrefslogtreecommitdiffabout
path: root/pumpkin/SendXFer.m
Unidiff
Diffstat (limited to 'pumpkin/SendXFer.m') (more/less context) (ignore whitespace changes)
-rw-r--r--pumpkin/SendXFer.m156
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