summaryrefslogtreecommitdiffabout
path: root/pumpkin/ReceiveXFer.m
Side-by-side diff
Diffstat (limited to 'pumpkin/ReceiveXFer.m') (more/less context) (show 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 @@
+#import "ReceiveXFer.h"
+#import "StringsAttached.h"
+#import "ConfirmRequest.h"
+
+@implementation ReceiveXFer
+
+- (ReceiveXFer*)initWithLocalFile:(NSString *)lf peerAddress:(const struct sockaddr_in *)pa remoteFile:(NSString *)rf xferType :(NSString *)xt blockSize:(uint16_t)bs andTimeout:(int)to{
+ if(!(self = [super init])) return self;
+ xferPrefix = @"⬇";
+ retryTimeout = to;
+ localFile = lf;
+ memmove(&peer,pa,sizeof(peer));
+ [[NSFileManager defaultManager] createFileAtPath:localFile contents:nil attributes:nil];
+ if(!(theFile = [[NSFileHandle fileHandleForWritingAtPath:localFile] retain])) {
+ [pumpkin log:@"Failed to create '%@', transfer aborted.", localFile];
+ return self;
+ }
+ [self createSocket];
+ NSMutableDictionary *o = [NSMutableDictionary dictionaryWithCapacity:4];
+ [o setValue:[NSString stringWithFormat:@"%u",bs] forKey:@"blksize"];
+ [o setValue:@"" forKey:@"tsize"];
+ [o setValue:[NSString stringWithFormat:@"%d",(int)retryTimeout] forKey:@"timeout"];
+ state = xferStateConnecting;
+ [self queuePacket:[TFTPPacket packetRRQWithFile:xferFilename=rf xferType:xferType=xt andOptions:o]];
+ [self appear];
+ return self;
+}
+
+- (ReceiveXFer*)initWithPeer:(struct sockaddr_in *)sin andPacket:(TFTPPacket *)p {
+ if(!(self = [super initWithPeer:sin andPacket:p])) return self;
+ xferPrefix = @"⬇";
+ xferFilename=[p.rqFilename retain]; xferType=[p.rqType retain];
+ [pumpkin log:@"'%@' of type '%@' is coming from %@", xferFilename, xferType, [NSString stringWithSocketAddress:&peer]];
+
+ [self createSocket];
+ [self appear];
+
+ if(![self makeLocalFileName:xferFilename])
+ return self;
+
+ switch([[pumpkin.theDefaults.values valueForKey:@"wrqBehavior"] intValue]) {
+ case onWRQDeny: [self goOnWithVerdict:verdictDeny]; break;
+ case onWRQTake: [self goOnWithVerdict:verdictAllow]; break;
+ case onWRQPromptIfExists:
+ if(![[NSFileManager defaultManager] fileExistsAtPath:localFile]) {
+ [self goOnWithVerdict:verdictAllow];
+ break;
+ }
+ case onWRQPrompt:
+ [ConfirmRequest confirmationWithXfer:self];
+ break;
+ }
+ return self;
+}
+-(void)goOnWithVerdict:(int)verdict {
+ if(!(verdict==verdictAllow || verdict==verdictRename)) {
+ [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrAccessViolation andMessage:@"Access denied"]];
+ return;
+ }
+ NSFileManager *fm = [NSFileManager defaultManager];
+ if(verdict==verdictRename) {
+ int i;
+ for(i=1;i>0;++i) {
+ if(![self makeLocalFileName:[NSString stringWithFormat:@"%@ (%d)",xferFilename,i]])
+ return;
+ if(![fm fileExistsAtPath:localFile]) break;
+ [localFile release],localFile=nil;
+ }
+ if(!localFile) {
+ [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrFileExists andMessage:@"Couldn't find a name for a file"]];
+ return;
+ }
+ }
+ [pumpkin log:@"Receiving '%@'",localFile];
+ [fm createFileAtPath:localFile contents:nil attributes:nil];
+ if(!(theFile = [[NSFileHandle fileHandleForWritingAtPath:localFile] retain])) {
+ [self queuePacket:[TFTPPacket packetErrorWithErrno:errno andFallback:@"couldn't write to file"]];
+ return;
+ }
+ xferSize=0;
+ NSMutableDictionary *o = [NSMutableDictionary dictionaryWithCapacity:4];
+ [initialPacket.rqOptions enumerateKeysAndObjectsUsingBlock:^(NSString* k,NSString *v,BOOL *s) {
+ if([k isEqualToString:@"blksize"]) {
+ [o setValue:[NSString stringWithFormat:@"%u",blockSize=v.intValue] forKey:@"blksize"];
+ }else if([k isEqualToString:@"tsize"]) {
+ [o setValue:[NSString stringWithFormat:@"%lld",xferSize=v.longLongValue] forKey:@"tsize"];
+ }else if([k isEqualToString:@"timeout"]) {
+ [o setValue:[NSString stringWithFormat:@"%d",v.intValue] forKey:@"timeout"];
+ retryTimeout = v.intValue;
+ }else
+ [pumpkin log:@"Unknown option '%@' with value '%@'. Ignoring.",k,v];
+ }];
+ if(xferSize) {
+ long xb = (xferSize/blockSize)+1;
+ if(xb>UINT16_MAX) {
+ [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]] ];
+ return;
+ }
+ xferBlocks = xb;
+ }
+ state = xferStateXfer;
+ if([o count]) {
+ [self queuePacket:[TFTPPacket packetOACKWithOptions:o]];
+ }else{
+ [self queuePacket:[TFTPPacket packetACKWithBlock:acked=0]];
+ }
+ [self updateView];
+}
+
+-(void)eatTFTPPacket:(TFTPPacket *)p from:(struct sockaddr_in *)sin {
+ if(state==xferStateConnecting) {
+ peer.sin_port = sin->sin_port;
+ [self updateView];
+ }else if(![self isPeer:sin]) {
+ [pumpkin log:@"Packet from unexpected source (%@) received",[NSString stringWithSocketAddress:sin]];
+ return;
+ }
+ switch(p.op) {
+ case tftpOpDATA:
+ {
+ NSData *d=p.rqData;;
+ @try {
+ if(p.block > (acked+1))
+ [pumpkin log:@"While transferring %@ block %d seems to immediately follow block %d",xferFilename,p.block,acked];
+ [theFile seekToFileOffset:(p.block-1)*blockSize];
+ [theFile writeData:d];
+ [theFile truncateFileAtOffset:(p.block-1)*blockSize+d.length];
+ }@catch (NSException *e) {
+ [self queuePacket:[TFTPPacket packetErrorWithCode:tftpErrUndefined andMessage:e.reason]];
+ break;
+ }
+ [self queuePacket:[TFTPPacket packetACKWithBlock: acked=p.block]];
+ [self updateView];
+ if(d.length<blockSize)
+ state = xferStateShutdown;
+ }
+ break;
+ case tftpOpOACK:
+ {
+ __block BOOL a=NO;
+ [p.rqOptions enumerateKeysAndObjectsUsingBlock:^(NSString *k,NSString *v,BOOL *s) {
+ if([k isEqualToString:@"blksize"])
+ blockSize = v.intValue;
+ else if([k isEqualToString:@"tsize"])
+ xferSize = v.longLongValue;
+ else if([k isEqualToString:@"timeout"])
+ retryTimeout = v.intValue;
+ else{
+ [pumpkin log:@"Totally unknown option %@ acknowledged by remote.",k];
+ a=YES;
+ }
+ }];
+ if(a) {
+ [self abort];
+ break;
+ }
+ [self queuePacket:[TFTPPacket packetACKWithBlock:0]];
+ state = xferStateXfer;
+ [self updateView];
+ }
+ break;
+ default:
+ [pumpkin log:@"Totaly unexpected opcode %d received",p.op];
+ break;
+ }
+}
+
+@end