summaryrefslogtreecommitdiffabout
path: root/pumpkin/ReceiveXFer.m
blob: 33e96044ffab1f8e60ea450e1c8b216d77667248 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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