Not reviewed, fix for accidental commit.
[WebKit-https.git] / WebCore / loader / mac / FormDataStream.m
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  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
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /* originally written by Becky Willrich, additional code by Darin Adler */
30
31 #import "config.h"
32 #import "FormDataStream.h"
33
34 #import "CString.h"
35 #import "FormData.h"
36 #import "WebCoreSystemInterface.h"
37 #import <sys/stat.h>
38 #import <sys/types.h>
39 #import <wtf/Assertions.h>
40
41 namespace WebCore {
42
43 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
44
45 struct FormStreamFields {
46     CFMutableSetRef scheduledRunLoopPairs;
47     Vector<FormDataElement> remainingElements; // in reverse order
48     CFReadStreamRef currentStream;
49     char* currentData;
50     CFReadStreamRef formStream;
51 };
52
53 struct SchedulePair {
54     CFRunLoopRef runLoop;
55     CFStringRef mode;
56 };
57
58 static const void* pairRetain(CFAllocatorRef alloc, const void* value)
59 {
60     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
61
62     SchedulePair* result = new SchedulePair;
63     CFRetain(pair->runLoop);
64     result->runLoop = pair->runLoop;
65     result->mode = CFStringCreateCopy(alloc, pair->mode);
66     return result;
67 }
68
69 static void pairRelease(CFAllocatorRef alloc, const void* value)
70 {
71     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
72
73     CFRelease(pair->runLoop);
74     CFRelease(pair->mode);
75     delete pair;
76 }
77
78 static Boolean pairEqual(const void* a, const void* b)
79 {
80     const SchedulePair* pairA = static_cast<const SchedulePair*>(a);
81     const SchedulePair* pairB = static_cast<const SchedulePair*>(b);
82
83     return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode);
84 }
85
86 static CFHashCode pairHash(const void* value)
87 {
88     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
89
90     return (CFHashCode)pair->runLoop ^ CFHash(pair->mode);
91 }
92
93 static void closeCurrentStream(FormStreamFields *form)
94 {
95     if (form->currentStream) {
96         CFReadStreamClose(form->currentStream);
97         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
98         CFRelease(form->currentStream);
99         form->currentStream = NULL;
100     }
101     if (form->currentData) {
102         fastFree(form->currentData);
103         form->currentData = 0;
104     }
105 }
106
107 static void scheduleWithPair(const void* value, void* context)
108 {
109     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
110     CFReadStreamRef stream = (CFReadStreamRef)context;
111
112     CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode);
113 }
114
115 static void advanceCurrentStream(FormStreamFields *form)
116 {
117     closeCurrentStream(form);
118
119     if (form->remainingElements.isEmpty())
120         return;
121
122     // Create the new stream.
123     FormDataElement& nextInput = form->remainingElements.last();
124     if (nextInput.m_type == FormDataElement::data) {
125         size_t size = nextInput.m_data.size();
126         char* data = nextInput.m_data.releaseBuffer();
127         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
128         form->currentData = data;
129     } else {
130         CFStringRef filename = nextInput.m_filename.createCFString();
131         CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE);
132         CFRelease(filename);
133         form->currentStream = CFReadStreamCreateWithFile(0, fileURL);
134         CFRelease(fileURL);
135     }
136     form->remainingElements.removeLast();
137
138     // Set up the callback.
139     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
140     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
141         formEventCallback, &context);
142
143     // Schedule with the current set of run loops.
144     CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream);
145 }
146
147 static void openNextStream(FormStreamFields* form)
148 {
149     // Skip over any streams we can't open.
150     // For some purposes we might want to return an error, but the current NSURLConnection
151     // can't really do anything useful with an error at this point, so this is better.
152     advanceCurrentStream(form);
153     while (form->currentStream && !CFReadStreamOpen(form->currentStream))
154         advanceCurrentStream(form);
155 }
156
157 static void* formCreate(CFReadStreamRef stream, void* context)
158 {
159     const FormData* formData = static_cast<const FormData*>(context);
160
161     CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash };
162
163     FormStreamFields* newInfo = new FormStreamFields;
164     newInfo->scheduledRunLoopPairs = CFSetCreateMutable(0, 0, &runLoopAndModeCallBacks);
165     newInfo->currentStream = NULL;
166     newInfo->currentData = 0;
167     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
168
169     // Append in reverse order since we remove elements from the end.
170     size_t size = formData->elements().size();
171     newInfo->remainingElements.reserveCapacity(size);
172     for (size_t i = 0; i < size; ++i)
173         newInfo->remainingElements.append(formData->elements()[size - i - 1]);
174
175     return newInfo;
176 }
177
178 static void formFinalize(CFReadStreamRef stream, void* context)
179 {
180     FormStreamFields* form = static_cast<FormStreamFields*>(context);
181
182     closeCurrentStream(form);
183     CFRelease(form->scheduledRunLoopPairs);
184     delete form;
185 }
186
187 static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context)
188 {
189     FormStreamFields* form = static_cast<FormStreamFields*>(context);
190
191     openNextStream(form);
192
193     *openComplete = TRUE;
194     error->error = 0;
195     return TRUE;
196 }
197
198 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
199 {
200     FormStreamFields* form = static_cast<FormStreamFields*>(context);
201
202     while (form->currentStream) {
203         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
204         if (bytesRead < 0) {
205             *error = CFReadStreamGetError(form->currentStream);
206             return -1;
207         }
208         if (bytesRead > 0) {
209             error->error = 0;
210             *atEOF = FALSE;
211             return bytesRead;
212         }
213         openNextStream(form);
214     }
215
216     error->error = 0;
217     *atEOF = TRUE;
218     return 0;
219 }
220
221 static Boolean formCanRead(CFReadStreamRef stream, void* context)
222 {
223     FormStreamFields* form = static_cast<FormStreamFields*>(context);
224
225     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
226         openNextStream(form);
227     }
228     if (!form->currentStream) {
229         wkSignalCFReadStreamEnd(stream);
230         return FALSE;
231     }
232     return CFReadStreamHasBytesAvailable(form->currentStream);
233 }
234
235 static void formClose(CFReadStreamRef stream, void* context)
236 {
237     FormStreamFields* form = static_cast<FormStreamFields*>(context);
238
239     closeCurrentStream(form);
240 }
241
242 static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
243 {
244     FormStreamFields* form = static_cast<FormStreamFields*>(context);
245
246     if (form->currentStream)
247         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
248     SchedulePair pair = { runLoop, runLoopMode };
249     CFSetAddValue(form->scheduledRunLoopPairs, &pair);
250 }
251
252 static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
253 {
254     FormStreamFields* form = static_cast<FormStreamFields*>(context);
255
256     if (form->currentStream)
257         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
258     SchedulePair pair = { runLoop, runLoopMode };
259     CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
260 }
261
262 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
263 {
264     FormStreamFields* form = static_cast<FormStreamFields*>(context);
265
266     switch (type) {
267     case kCFStreamEventHasBytesAvailable:
268         wkSignalCFReadStreamHasBytes(form->formStream);
269         break;
270     case kCFStreamEventErrorOccurred: {
271         CFStreamError readStreamError = CFReadStreamGetError(stream);
272         wkSignalCFReadStreamError(form->formStream, &readStreamError);
273         break;
274     }
275     case kCFStreamEventEndEncountered:
276         openNextStream(form);
277         if (!form->currentStream) {
278             wkSignalCFReadStreamEnd(form->formStream);
279         }
280         break;
281     case kCFStreamEventNone:
282         LOG_ERROR("unexpected kCFStreamEventNone");
283         break;
284     case kCFStreamEventOpenCompleted:
285         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
286         break;
287     case kCFStreamEventCanAcceptBytes:
288         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
289         break;
290     }
291 }
292
293 void setHTTPBody(NSMutableURLRequest *request, const FormData& formData)
294 {
295     size_t count = formData.elements().size();
296
297     // Handle the common special case of one piece of form data, with no files.
298     if (count == 1) {
299         const FormDataElement& element = formData.elements()[0];
300         if (element.m_type == FormDataElement::data) {
301             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
302             [request setHTTPBody:data];
303             [data release];
304             return;
305         }
306     }
307
308     // Precompute the content length so NSURLConnection doesn't use chunked mode.
309     long long length = 0;
310     for (size_t i = 0; i < count; ++i) {
311         const FormDataElement& element = formData.elements()[i];
312         if (element.m_type == FormDataElement::data)
313             length += element.m_data.size();
314         else {
315             struct stat sb;
316             int statResult = stat(element.m_filename.utf8(), &sb);
317             if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
318                 length += sb.st_size;
319         }
320     }
321
322     // Set the length.
323     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
324
325     // Create and set the stream.
326     CFReadStreamRef stream = wkCreateCustomCFReadStream(formCreate, formFinalize,
327         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
328         const_cast<FormData*>(&formData));
329     [request setHTTPBodyStream:(NSInputStream *)stream];
330     CFRelease(stream);
331 }
332
333 }