summaryrefslogtreecommitdiffabout
path: root/pumpkin/ReceiveXFer.m
Unidiff
Diffstat (limited to 'pumpkin/ReceiveXFer.m') (more/less context) (ignore whitespace changes)
-rw-r--r--pumpkin/ReceiveXFer.m168
1 files changed, 168 insertions, 0 deletions
diff --git a/pumpkin/ReceiveXFer.m b/pumpkin/ReceiveXFer.m
new file mode 100644
index 0000000..33e9604
--- a/dev/null
+++ b/pumpkin/ReceiveXFer.m
@@ -0,0 +1,168 @@
1#import "ReceiveXFer.h"
2#import "StringsAttached.h"
3#import "ConfirmRequest.h"
4
5@implementation ReceiveXFer
6
7- (ReceiveXFer*)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 [[NSFileManager defaultManager] createFileAtPath:localFile contents:nil attributes:nil];
14 if(!(theFile = [[NSFileHandle fileHandleForWritingAtPath:localFile] retain])) {
15 [pumpkin log:@"Failed to create '%@', transfer aborted.", localFile];
16 return self;
17 }
18 [self createSocket];
19 NSMutableDictionary *o = [NSMutableDictionary dictionaryWithCapacity:4];
20 [o setValue:[NSString stringWithFormat:@"%u",bs] forKey:@"blksize"];
21 [o setValue:@"" forKey:@"tsize"];
22 [o setValue:[NSString stringWithFormat:@"%d",(int)retryTimeout] forKey:@"timeout"];
23 state = xferStateConnecting;
24 [self queuePacket:[TFTPPacket packetRRQWithFile:xferFilename=rf xferType:xferType=xt andOptions:o]];
25 [self appear];
26 return self;
27}
28
29- (ReceiveXFer*)initWithPeer:(struct sockaddr_in *)sin andPacket:(TFTPPacket *)p {
30 if(!(self = [super initWithPeer:sin andPacket:p])) return self;
31 xferPrefix = @"⬇";
32 xferFilename=[p.rqFilename retain]; xferType=[p.rqType retain];
33 [pumpkin log:@"'%@' of type '%@' is coming from %@", xferFilename, xferType, [NSString stringWithSocketAddress:&peer]];
34
35 [self createSocket];
36 [self appear];
37
38 if(![self makeLocalFileName:xferFilename])
39 return self;
40
41 switch([[pumpkin.theDefaults.values valueForKey:@"wrqBehavior"] intValue]) {
42 case onWRQDeny: [self goOnWithVerdict:verdictDeny]; break;
43 case onWRQTake: [self goOnWithVerdict:verdictAllow]; break;
44 case onWRQPromptIfExists:
45 if(![[NSFileManager defaultManager] fileExistsAtPath:localFile]) {
46 [self goOnWithVerdict:verdictAllow];
47 break;
48 }
49 case onWRQPrompt:
50 [ConfirmRequest confirmationWithXfer:self];
51 break;
52 }
53 return self;
54}
55-(void)goOnWithVerdict:(int)verdict {
56 if(!(verdict==verdictAllow || verdict==verdictRename)) {
57 [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrAccessViolation andMessage:@"Access denied"]];
58 return;
59 }
60 NSFileManager *fm = [NSFileManager defaultManager];
61 if(verdict==verdictRename) {
62 int i;
63 for(i=1;i>0;++i) {
64 if(![self makeLocalFileName:[NSString stringWithFormat:@"%@ (%d)",xferFilename,i]])
65 return;
66 if(![fm fileExistsAtPath:localFile]) break;
67 [localFile release],localFile=nil;
68 }
69 if(!localFile) {
70 [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrFileExists andMessage:@"Couldn't find a name for a file"]];
71 return;
72 }
73 }
74 [pumpkin log:@"Receiving '%@'",localFile];
75 [fm createFileAtPath:localFile contents:nil attributes:nil];
76 if(!(theFile = [[NSFileHandle fileHandleForWritingAtPath:localFile] retain])) {
77 [self queuePacket:[TFTPPacket packetErrorWithErrno:errno andFallback:@"couldn't write to file"]];
78 return;
79 }
80 xferSize=0;
81 NSMutableDictionary *o = [NSMutableDictionary dictionaryWithCapacity:4];
82 [initialPacket.rqOptions enumerateKeysAndObjectsUsingBlock:^(NSString* k,NSString *v,BOOL *s) {
83 if([k isEqualToString:@"blksize"]) {
84 [o setValue:[NSString stringWithFormat:@"%u",blockSize=v.intValue] forKey:@"blksize"];
85 }else if([k isEqualToString:@"tsize"]) {
86 [o setValue:[NSString stringWithFormat:@"%lld",xferSize=v.longLongValue] forKey:@"tsize"];
87 }else if([k isEqualToString:@"timeout"]) {
88 [o setValue:[NSString stringWithFormat:@"%d",v.intValue] forKey:@"timeout"];
89 retryTimeout = v.intValue;
90 }else
91 [pumpkin log:@"Unknown option '%@' with value '%@'. Ignoring.",k,v];
92 }];
93 if(xferSize) {
94 long xb = (xferSize/blockSize)+1;
95 if(xb>UINT16_MAX) {
96 [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrUndefined andMessage:[NSString stringWithFormat:@"file seems to be too big (%lld bytes) and would take %ld blocks to be transferred with the block size of %d bytes", xferSize, xb,blockSize]] ];
97 return;
98 }
99 xferBlocks = xb;
100 }
101 state = xferStateXfer;
102 if([o count]) {
103 [self queuePacket:[TFTPPacket packetOACKWithOptions:o]];
104 }else{
105 [self queuePacket:[TFTPPacket packetACKWithBlock:acked=0]];
106 }
107 [self updateView];
108}
109
110-(void)eatTFTPPacket:(TFTPPacket *)p from:(struct sockaddr_in *)sin {
111 if(state==xferStateConnecting) {
112 peer.sin_port = sin->sin_port;
113 [self updateView];
114 }else if(![self isPeer:sin]) {
115 [pumpkin log:@"Packet from unexpected source (%@) received",[NSString stringWithSocketAddress:sin]];
116 return;
117 }
118 switch(p.op) {
119 case tftpOpDATA:
120 {
121 NSData *d=p.rqData;;
122 @try {
123 if(p.block > (acked+1))
124 [pumpkin log:@"While transferring %@ block %d seems to immediately follow block %d",xferFilename,p.block,acked];
125 [theFile seekToFileOffset:(p.block-1)*blockSize];
126 [theFile writeData:d];
127 [theFile truncateFileAtOffset:(p.block-1)*blockSize+d.length];
128 }@catch (NSException *e) {
129 [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrUndefined andMessage:e.reason]];
130 break;
131 }
132 [self queuePacket:[TFTPPacket packetACKWithBlock: acked=p.block]];
133 [self updateView];
134 if(d.length<blockSize)
135 state = xferStateShutdown;
136 }
137 break;
138 case tftpOpOACK:
139 {
140 __block BOOL a=NO;
141 [p.rqOptions enumerateKeysAndObjectsUsingBlock:^(NSString *k,NSString *v,BOOL *s) {
142 if([k isEqualToString:@"blksize"])
143 blockSize = v.intValue;
144 else if([k isEqualToString:@"tsize"])
145 xferSize = v.longLongValue;
146 else if([k isEqualToString:@"timeout"])
147 retryTimeout = v.intValue;
148 else{
149 [pumpkin log:@"Totally unknown option %@ acknowledged by remote.",k];
150 a=YES;
151 }
152 }];
153 if(a) {
154 [self abort];
155 break;
156 }
157 [self queuePacket:[TFTPPacket packetACKWithBlock:0]];
158 state = xferStateXfer;
159 [self updateView];
160 }
161 break;
162 default:
163 [pumpkin log:@"Totaly unexpected opcode %d received",p.op];
164 break;
165 }
166}
167
168@end