|
1 // Copyright (c) 2006, Google Inc. |
|
2 // All rights reserved. |
|
3 // |
|
4 // Redistribution and use in source and binary forms, with or without |
|
5 // modification, are permitted provided that the following conditions are |
|
6 // met: |
|
7 // |
|
8 // * Redistributions of source code must retain the above copyright |
|
9 // notice, this list of conditions and the following disclaimer. |
|
10 // * Redistributions in binary form must reproduce the above |
|
11 // copyright notice, this list of conditions and the following disclaimer |
|
12 // in the documentation and/or other materials provided with the |
|
13 // distribution. |
|
14 // * Neither the name of Google Inc. nor the names of its |
|
15 // contributors may be used to endorse or promote products derived from |
|
16 // this software without specific prior written permission. |
|
17 // |
|
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 |
|
30 #import "HTTPMultipartUpload.h" |
|
31 #import "GTMDefines.h" |
|
32 |
|
33 @interface HTTPMultipartUpload(PrivateMethods) |
|
34 - (NSString *)multipartBoundary; |
|
35 // Each of the following methods will append the starting multipart boundary, |
|
36 // but not the ending one. |
|
37 - (NSData *)formDataForKey:(NSString *)key value:(NSString *)value; |
|
38 - (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name; |
|
39 - (NSData *)formDataForFile:(NSString *)file name:(NSString *)name; |
|
40 @end |
|
41 |
|
42 @implementation HTTPMultipartUpload |
|
43 //============================================================================= |
|
44 #pragma mark - |
|
45 #pragma mark || Private || |
|
46 //============================================================================= |
|
47 - (NSString *)multipartBoundary { |
|
48 // The boundary has 27 '-' characters followed by 16 hex digits |
|
49 return [NSString stringWithFormat:@"---------------------------%08X%08X", |
|
50 rand(), rand()]; |
|
51 } |
|
52 |
|
53 //============================================================================= |
|
54 - (NSData *)formDataForKey:(NSString *)key value:(NSString *)value { |
|
55 NSString *escaped = |
|
56 [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
|
57 NSString *fmt = |
|
58 @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n"; |
|
59 NSString *form = [NSString stringWithFormat:fmt, boundary_, escaped, value]; |
|
60 |
|
61 return [form dataUsingEncoding:NSUTF8StringEncoding]; |
|
62 } |
|
63 |
|
64 //============================================================================= |
|
65 - (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name { |
|
66 NSMutableData *data = [NSMutableData data]; |
|
67 NSString *escaped = |
|
68 [name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
|
69 NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; " |
|
70 "filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n"; |
|
71 NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped]; |
|
72 |
|
73 [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]]; |
|
74 [data appendData:contents]; |
|
75 |
|
76 return data; |
|
77 } |
|
78 |
|
79 //============================================================================= |
|
80 - (NSData *)formDataForFile:(NSString *)file name:(NSString *)name { |
|
81 NSData *contents = [NSData dataWithContentsOfFile:file]; |
|
82 |
|
83 return [self formDataForFileContents:contents name:name]; |
|
84 } |
|
85 |
|
86 //============================================================================= |
|
87 #pragma mark - |
|
88 #pragma mark || Public || |
|
89 //============================================================================= |
|
90 - (id)initWithURL:(NSURL *)url { |
|
91 if ((self = [super init])) { |
|
92 url_ = [url copy]; |
|
93 boundary_ = [[self multipartBoundary] retain]; |
|
94 files_ = [[NSMutableDictionary alloc] init]; |
|
95 } |
|
96 |
|
97 return self; |
|
98 } |
|
99 |
|
100 //============================================================================= |
|
101 - (void)dealloc { |
|
102 [url_ release]; |
|
103 [parameters_ release]; |
|
104 [files_ release]; |
|
105 [boundary_ release]; |
|
106 [response_ release]; |
|
107 |
|
108 [super dealloc]; |
|
109 } |
|
110 |
|
111 //============================================================================= |
|
112 - (NSURL *)URL { |
|
113 return url_; |
|
114 } |
|
115 |
|
116 //============================================================================= |
|
117 - (void)setParameters:(NSDictionary *)parameters { |
|
118 if (parameters != parameters_) { |
|
119 [parameters_ release]; |
|
120 parameters_ = [parameters copy]; |
|
121 } |
|
122 } |
|
123 |
|
124 //============================================================================= |
|
125 - (NSDictionary *)parameters { |
|
126 return parameters_; |
|
127 } |
|
128 |
|
129 //============================================================================= |
|
130 - (void)addFileAtPath:(NSString *)path name:(NSString *)name { |
|
131 [files_ setObject:path forKey:name]; |
|
132 } |
|
133 |
|
134 //============================================================================= |
|
135 - (void)addFileContents:(NSData *)data name:(NSString *)name { |
|
136 [files_ setObject:data forKey:name]; |
|
137 } |
|
138 |
|
139 //============================================================================= |
|
140 - (NSDictionary *)files { |
|
141 return files_; |
|
142 } |
|
143 |
|
144 //============================================================================= |
|
145 - (NSData *)send:(NSError **)error { |
|
146 NSMutableURLRequest *req = |
|
147 [[NSMutableURLRequest alloc] |
|
148 initWithURL:url_ cachePolicy:NSURLRequestUseProtocolCachePolicy |
|
149 timeoutInterval:10.0 ]; |
|
150 |
|
151 NSMutableData *postBody = [NSMutableData data]; |
|
152 |
|
153 [req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", |
|
154 boundary_] forHTTPHeaderField:@"Content-type"]; |
|
155 |
|
156 // Add any parameters to the message |
|
157 NSArray *parameterKeys = [parameters_ allKeys]; |
|
158 NSString *key; |
|
159 |
|
160 NSInteger count = [parameterKeys count]; |
|
161 for (NSInteger i = 0; i < count; ++i) { |
|
162 key = [parameterKeys objectAtIndex:i]; |
|
163 [postBody appendData:[self formDataForKey:key |
|
164 value:[parameters_ objectForKey:key]]]; |
|
165 } |
|
166 |
|
167 // Add any files to the message |
|
168 NSArray *fileNames = [files_ allKeys]; |
|
169 count = [fileNames count]; |
|
170 for (NSInteger i = 0; i < count; ++i) { |
|
171 NSString *name = [fileNames objectAtIndex:i]; |
|
172 id fileOrData = [files_ objectForKey:name]; |
|
173 NSData *fileData; |
|
174 |
|
175 // The object can be either the path to a file (NSString) or the contents |
|
176 // of the file (NSData). |
|
177 if ([fileOrData isKindOfClass:[NSData class]]) |
|
178 fileData = [self formDataForFileContents:fileOrData name:name]; |
|
179 else |
|
180 fileData = [self formDataForFile:fileOrData name:name]; |
|
181 |
|
182 [postBody appendData:fileData]; |
|
183 } |
|
184 |
|
185 NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_]; |
|
186 [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]]; |
|
187 |
|
188 [req setHTTPBody:postBody]; |
|
189 [req setHTTPMethod:@"POST"]; |
|
190 |
|
191 [response_ release]; |
|
192 response_ = nil; |
|
193 |
|
194 NSData *data = [NSURLConnection sendSynchronousRequest:req |
|
195 returningResponse:&response_ |
|
196 error:error]; |
|
197 |
|
198 [response_ retain]; |
|
199 [req release]; |
|
200 |
|
201 return data; |
|
202 } |
|
203 |
|
204 //============================================================================= |
|
205 - (NSHTTPURLResponse *)response { |
|
206 return response_; |
|
207 } |
|
208 |
|
209 @end |