-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathMyHTTPConnection.m
More file actions
249 lines (206 loc) · 9.26 KB
/
MyHTTPConnection.m
File metadata and controls
249 lines (206 loc) · 9.26 KB
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
//
// This class was created by Nonnus,
// who graciously decided to share it with the CocoaHTTPServer community.
//
#import "MyHTTPConnection.h"
#import "HTTPServer.h"
#import "HTTPResponse.h"
#import "AsyncSocket.h"
@implementation MyHTTPConnection
/**
* Returns whether or not the requested resource is browseable.
**/
- (BOOL)isBrowseable:(NSString *)path
{
// Override me to provide custom configuration...
// You can configure it for the entire server, or based on the current request
return YES;
}
/**
* This method creates a html browseable page.
* Customize to fit your needs
**/
- (NSString *)createBrowseableIndex:(NSString *)path
{
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
NSMutableString *outdata = [NSMutableString new];
[outdata appendString:@"<html><head>"];
[outdata appendString:@"<title>KeePass Files</title>"];
[outdata appendString:@"<style>#keepass {font-family: \"Lucida Sans Unicode\", \"Lucida Grande\", Sans-Serif;font-size: 12px;margin: 45px;width: 480px;text-align: left;border-collapse: collapse;border: 1px solid #69c;} #keepass th{padding: 12px 17px 12px 17px;font-weight: normal;font-size: 14px;color: #039;border-bottom: 1px dashed #69c;} #keepass td{padding: 7px 17px 7px 17px;color: #669;}#keepass tbody tr:hover td{color: #339;background: #d0dafd;}</style>"];
[outdata appendString:@"</head><body>"];
[outdata appendString:@"<table id=\"keepass\"><thead><tr><th colspan=2>KeePass Files</th></tr></thead><tbody>"];
for (NSString *fname in array)
{
NSDictionary *fileDict = [[NSFileManager defaultManager] fileAttributesAtPath:[path stringByAppendingPathComponent:fname] traverseLink:NO];
if ([[fileDict objectForKey:NSFileType] isEqualToString: @"NSFileTypeRegular"]){
[outdata appendFormat:@"<tr><td><a href=\"%@\">%@</a></td><td>(%8.1f Kb)</td></tr>", fname, fname, [[fileDict objectForKey:NSFileSize] floatValue] / 1024];
}
}
[outdata appendString:@"</tbody>"];
if ([self supportsMethod:@"POST" atPath:path])
{
[outdata appendString:@"<form action=\"\" method=\"post\" enctype=\"multipart/form-data\" name=\"form1\" id=\"form1\">"];
[outdata appendString:@"<tfoot><tr><td>"];
[outdata appendString:@"upload file"];
[outdata appendString:@"<input type=\"file\" name=\"file\" id=\"file\" />"];
[outdata appendString:@"</td><td>"];
[outdata appendString:@"<input type=\"submit\" name=\"button\" id=\"button\" value=\"Submit\" />"];
[outdata appendString:@"</td></tr></tfoot>"];
[outdata appendString:@"</form>"];
}
[outdata appendString:@"</table></body></html>"];
return [outdata autorelease];
}
/**
* Returns whether or not the server will accept POSTs.
* That is, whether the server will accept uploaded data for the given URI.
**/
- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)relativePath
{
if ([@"POST" isEqualToString:method])
{
return YES;
}
return [super supportsMethod:method atPath:relativePath];
}
/**
* Called from the superclass after receiving all HTTP headers, but before reading any of the request body
* so we use it to initialize our stuff
**/
- (void)prepareForBodyWithSize:(UInt64)contentLength
{
dataStartIndex = 0;
if (multipartData == nil ) multipartData = [[NSMutableArray alloc] init]; //jlz
postHeaderOK = FALSE ;
}
/**
* This method is called to get a response for a request.
* You may return any object that adopts the HTTPResponse protocol.
* The HTTPServer comes with two such classes: HTTPFileResponse and HTTPDataResponse.
* HTTPFileResponse is a wrapper for an NSFileHandle object, and is the preferred way to send a file response.
* HTTPDataResopnse is a wrapper for an NSData object, and may be used to send a custom response.
**/
- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
{
//NSLog(@"httpResponseForURI: method:%@ path:%@", method, path);
NSData *requestData = [(NSData *)CFHTTPMessageCopySerializedMessage(request) autorelease];
/*NSString *requestStr = */[[[NSString alloc] initWithData:requestData encoding:NSASCIIStringEncoding] autorelease];
//NSLog(@"\n=== Request ====================\n%@\n================================", requestStr);
if (requestContentLength > 0) // Process POST data
{
//NSLog(@"processing post data: %i", requestContentLength);
if ([multipartData count] < 2) return nil;
NSString* postInfo = [[NSString alloc] initWithBytes:[[multipartData objectAtIndex:1] bytes]
length:[[multipartData objectAtIndex:1] length]
encoding:NSUTF8StringEncoding];
NSArray* postInfoComponents = [postInfo componentsSeparatedByString:@"; filename="];
postInfoComponents = [[postInfoComponents lastObject] componentsSeparatedByString:@"\""];
postInfoComponents = [[postInfoComponents objectAtIndex:1] componentsSeparatedByString:@"\\"];
NSString* filename = [postInfoComponents lastObject];
if (![filename isEqualToString:@""]) //this makes sure we did not submitted upload form without selecting file
{
UInt16 separatorBytes = 0x0A0D;
NSMutableData* separatorData = [NSMutableData dataWithBytes:&separatorBytes length:2];
[separatorData appendData:[multipartData objectAtIndex:0]];
int l = [separatorData length];
int count = 2; //number of times the separator shows up at the end of file data
NSFileHandle* dataToTrim = [multipartData lastObject];
//NSLog(@"data: %@", dataToTrim);
for (unsigned long long i = [dataToTrim offsetInFile] - l; i > 0; i--)
{
[dataToTrim seekToFileOffset:i];
if ([[dataToTrim readDataOfLength:l] isEqualToData:separatorData])
{
[dataToTrim truncateFileAtOffset:i];
i -= l;
if (--count == 0) break;
}
}
//NSLog(@"NewFileUploaded");
[[NSNotificationCenter defaultCenter] postNotificationName:@"NewFileUploaded" object:nil];
}
//for (int n = 1; n < [multipartData count] - 1; n++)
// NSLog(@"%@", [[[NSString alloc] initWithBytes:[[multipartData objectAtIndex:n] bytes] length:[[multipartData objectAtIndex:n] length] encoding:NSUTF8StringEncoding] autorelease]);
[postInfo release];
[multipartData release];
multipartData = nil ;
requestContentLength = 0;
}
NSString *filePath = [self filePathForURI:path];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
return [[[HTTPFileResponse alloc] initWithFilePath:filePath] autorelease];
}
else
{
NSString *folder = [path isEqualToString:@"/"] ? [[server documentRoot] path] : [NSString stringWithFormat: @"%@%@", [[server documentRoot] path], path];
if ([self isBrowseable:folder])
{
//NSLog(@"folder: %@", folder);
NSData *browseData = [[self createBrowseableIndex:folder] dataUsingEncoding:NSUTF8StringEncoding];
return [[[HTTPDataResponse alloc] initWithData:browseData] autorelease];
}
}
return nil;
}
/**
* This method is called to handle data read from a POST.
* The given data is part of the POST body.
**/
- (void)processDataChunk:(NSData *)postDataChunk
{
// Override me to do something useful with a POST.
// If the post is small, such as a simple form, you may want to simply append the data to the request.
// If the post is big, such as a file upload, you may want to store the file to disk.
//
// Remember: In order to support LARGE POST uploads, the data is read in chunks.
// This prevents a 50 MB upload from being stored in RAM.
// The size of the chunks are limited by the POST_CHUNKSIZE definition.
// Therefore, this method may be called multiple times for the same POST request.
//NSLog(@"processPostDataChunk");
if (!postHeaderOK)
{
UInt16 separatorBytes = 0x0A0D;
NSData* separatorData = [NSData dataWithBytes:&separatorBytes length:2];
int l = [separatorData length];
for (int i = 0; i < [postDataChunk length] - l; i++)
{
NSRange searchRange = {i, l};
if ([[postDataChunk subdataWithRange:searchRange] isEqualToData:separatorData])
{
NSRange newDataRange = {dataStartIndex, i - dataStartIndex};
dataStartIndex = i + l;
i += l - 1;
NSData *newData = [postDataChunk subdataWithRange:newDataRange];
if ([newData length])
{
[multipartData addObject:newData];
}
else
{
postHeaderOK = TRUE;
NSString* postInfo = [[NSString alloc] initWithBytes:[[multipartData objectAtIndex:1] bytes] length:[[multipartData objectAtIndex:1] length] encoding:NSUTF8StringEncoding];
NSArray* postInfoComponents = [postInfo componentsSeparatedByString:@"; filename="];
postInfoComponents = [[postInfoComponents lastObject] componentsSeparatedByString:@"\""];
postInfoComponents = [[postInfoComponents objectAtIndex:1] componentsSeparatedByString:@"\\"];
NSString* filename = [[[server documentRoot] path] stringByAppendingPathComponent:[postInfoComponents lastObject]];
NSRange fileDataRange = {dataStartIndex, [postDataChunk length] - dataStartIndex};
[[NSFileManager defaultManager] createFileAtPath:filename contents:[postDataChunk subdataWithRange:fileDataRange] attributes:nil];
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:filename];
if (file)
{
[file seekToEndOfFile];
[multipartData addObject:file];
}
[postInfo release];
break;
}
}
}
}
else
{
[(NSFileHandle*)[multipartData lastObject] writeData:postDataChunk];
}
}
@end