| 1 | /* |
|---|
| 2 | * Copyright (C) 2005, 2006, 2007 Apple 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 | #include "config.h" |
|---|
| 32 | #include "FormDataStreamCFNet.h" |
|---|
| 33 | |
|---|
| 34 | #include "FileSystem.h" |
|---|
| 35 | #include "FormData.h" |
|---|
| 36 | #include <CFNetwork/CFURLRequestPriv.h> |
|---|
| 37 | #include <CoreFoundation/CFStreamAbstract.h> |
|---|
| 38 | #include <WebKitSystemInterface/WebKitSystemInterface.h> |
|---|
| 39 | #include <sys/types.h> |
|---|
| 40 | #include <wtf/Assertions.h> |
|---|
| 41 | #include <wtf/HashMap.h> |
|---|
| 42 | #include <wtf/RetainPtr.h> |
|---|
| 43 | #include <wtf/text/CString.h> |
|---|
| 44 | |
|---|
| 45 | #define USE_V1_CFSTREAM_CALLBACKS |
|---|
| 46 | #ifdef USE_V1_CFSTREAM_CALLBACKS |
|---|
| 47 | typedef CFReadStreamCallBacksV1 WCReadStreamCallBacks; |
|---|
| 48 | #else |
|---|
| 49 | typedef CFReadStreamCallBacks WCReadStreamCallBacks; |
|---|
| 50 | #endif |
|---|
| 51 | |
|---|
| 52 | namespace WebCore { |
|---|
| 53 | |
|---|
| 54 | static HashMap<CFReadStreamRef, RefPtr<FormData> >& getStreamFormDatas() |
|---|
| 55 | { |
|---|
| 56 | static HashMap<CFReadStreamRef, RefPtr<FormData> > streamFormDatas; |
|---|
| 57 | return streamFormDatas; |
|---|
| 58 | } |
|---|
| 59 | |
|---|
| 60 | static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context); |
|---|
| 61 | |
|---|
| 62 | struct FormStreamFields { |
|---|
| 63 | CFMutableSetRef scheduledRunLoopPairs; |
|---|
| 64 | Vector<FormDataElement> remainingElements; // in reverse order |
|---|
| 65 | CFReadStreamRef currentStream; |
|---|
| 66 | char* currentData; |
|---|
| 67 | CFReadStreamRef formStream; |
|---|
| 68 | }; |
|---|
| 69 | |
|---|
| 70 | struct SchedulePair { |
|---|
| 71 | CFRunLoopRef runLoop; |
|---|
| 72 | CFStringRef mode; |
|---|
| 73 | }; |
|---|
| 74 | |
|---|
| 75 | static const void* pairRetain(CFAllocatorRef alloc, const void* value) |
|---|
| 76 | { |
|---|
| 77 | const SchedulePair* pair = static_cast<const SchedulePair*>(value); |
|---|
| 78 | |
|---|
| 79 | SchedulePair* result = new SchedulePair; |
|---|
| 80 | CFRetain(pair->runLoop); |
|---|
| 81 | result->runLoop = pair->runLoop; |
|---|
| 82 | result->mode = CFStringCreateCopy(alloc, pair->mode); |
|---|
| 83 | return result; |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | static void pairRelease(CFAllocatorRef alloc, const void* value) |
|---|
| 87 | { |
|---|
| 88 | const SchedulePair* pair = static_cast<const SchedulePair*>(value); |
|---|
| 89 | |
|---|
| 90 | CFRelease(pair->runLoop); |
|---|
| 91 | CFRelease(pair->mode); |
|---|
| 92 | delete pair; |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | static Boolean pairEqual(const void* a, const void* b) |
|---|
| 96 | { |
|---|
| 97 | const SchedulePair* pairA = static_cast<const SchedulePair*>(a); |
|---|
| 98 | const SchedulePair* pairB = static_cast<const SchedulePair*>(b); |
|---|
| 99 | |
|---|
| 100 | return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode); |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | static CFHashCode pairHash(const void* value) |
|---|
| 104 | { |
|---|
| 105 | const SchedulePair* pair = static_cast<const SchedulePair*>(value); |
|---|
| 106 | |
|---|
| 107 | return (CFHashCode)pair->runLoop ^ CFHash(pair->mode); |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | static void closeCurrentStream(FormStreamFields *form) |
|---|
| 111 | { |
|---|
| 112 | if (form->currentStream) { |
|---|
| 113 | CFReadStreamClose(form->currentStream); |
|---|
| 114 | CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL); |
|---|
| 115 | CFRelease(form->currentStream); |
|---|
| 116 | form->currentStream = NULL; |
|---|
| 117 | } |
|---|
| 118 | if (form->currentData) { |
|---|
| 119 | fastFree(form->currentData); |
|---|
| 120 | form->currentData = 0; |
|---|
| 121 | } |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | static void scheduleWithPair(const void* value, void* context) |
|---|
| 125 | { |
|---|
| 126 | const SchedulePair* pair = static_cast<const SchedulePair*>(value); |
|---|
| 127 | CFReadStreamRef stream = (CFReadStreamRef)context; |
|---|
| 128 | |
|---|
| 129 | CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode); |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | static void advanceCurrentStream(FormStreamFields *form) |
|---|
| 133 | { |
|---|
| 134 | closeCurrentStream(form); |
|---|
| 135 | |
|---|
| 136 | if (form->remainingElements.isEmpty()) |
|---|
| 137 | return; |
|---|
| 138 | |
|---|
| 139 | // Create the new stream. |
|---|
| 140 | FormDataElement& nextInput = form->remainingElements.last(); |
|---|
| 141 | if (nextInput.m_type == FormDataElement::data) { |
|---|
| 142 | size_t size = nextInput.m_data.size(); |
|---|
| 143 | char* data = nextInput.m_data.releaseBuffer(); |
|---|
| 144 | form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull); |
|---|
| 145 | form->currentData = data; |
|---|
| 146 | } else { |
|---|
| 147 | CFStringRef filename = nextInput.m_filename.createCFString(); |
|---|
| 148 | #if PLATFORM(WIN) |
|---|
| 149 | CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLWindowsPathStyle, FALSE); |
|---|
| 150 | #else |
|---|
| 151 | CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE); |
|---|
| 152 | #endif |
|---|
| 153 | CFRelease(filename); |
|---|
| 154 | form->currentStream = CFReadStreamCreateWithFile(0, fileURL); |
|---|
| 155 | CFRelease(fileURL); |
|---|
| 156 | } |
|---|
| 157 | form->remainingElements.removeLast(); |
|---|
| 158 | |
|---|
| 159 | // Set up the callback. |
|---|
| 160 | CFStreamClientContext context = { 0, form, NULL, NULL, NULL }; |
|---|
| 161 | CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, |
|---|
| 162 | formEventCallback, &context); |
|---|
| 163 | |
|---|
| 164 | // Schedule with the current set of run loops. |
|---|
| 165 | CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream); |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | static void openNextStream(FormStreamFields* form) |
|---|
| 169 | { |
|---|
| 170 | // Skip over any streams we can't open. |
|---|
| 171 | // For some purposes we might want to return an error, but the current CFURLConnection |
|---|
| 172 | // can't really do anything useful with an error at this point, so this is better. |
|---|
| 173 | advanceCurrentStream(form); |
|---|
| 174 | while (form->currentStream && !CFReadStreamOpen(form->currentStream)) |
|---|
| 175 | advanceCurrentStream(form); |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | static void* formCreate(CFReadStreamRef stream, void* context) |
|---|
| 179 | { |
|---|
| 180 | FormData* formData = static_cast<FormData*>(context); |
|---|
| 181 | |
|---|
| 182 | CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash }; |
|---|
| 183 | |
|---|
| 184 | FormStreamFields* newInfo = new FormStreamFields; |
|---|
| 185 | newInfo->scheduledRunLoopPairs = CFSetCreateMutable(0, 0, &runLoopAndModeCallBacks); |
|---|
| 186 | newInfo->currentStream = NULL; |
|---|
| 187 | newInfo->currentData = 0; |
|---|
| 188 | newInfo->formStream = stream; // Don't retain. That would create a reference cycle. |
|---|
| 189 | |
|---|
| 190 | // Append in reverse order since we remove elements from the end. |
|---|
| 191 | size_t size = formData->elements().size(); |
|---|
| 192 | newInfo->remainingElements.reserveCapacity(size); |
|---|
| 193 | for (size_t i = 0; i < size; ++i) |
|---|
| 194 | newInfo->remainingElements.append(formData->elements()[size - i - 1]); |
|---|
| 195 | |
|---|
| 196 | getStreamFormDatas().set(stream, adoptRef(formData)); |
|---|
| 197 | |
|---|
| 198 | return newInfo; |
|---|
| 199 | } |
|---|
| 200 | |
|---|
| 201 | static void formFinalize(CFReadStreamRef stream, void* context) |
|---|
| 202 | { |
|---|
| 203 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 204 | |
|---|
| 205 | getStreamFormDatas().remove(stream); |
|---|
| 206 | |
|---|
| 207 | closeCurrentStream(form); |
|---|
| 208 | CFRelease(form->scheduledRunLoopPairs); |
|---|
| 209 | delete form; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context) |
|---|
| 213 | { |
|---|
| 214 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 215 | |
|---|
| 216 | openNextStream(form); |
|---|
| 217 | |
|---|
| 218 | *openComplete = TRUE; |
|---|
| 219 | error->error = 0; |
|---|
| 220 | return TRUE; |
|---|
| 221 | } |
|---|
| 222 | |
|---|
| 223 | static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context) |
|---|
| 224 | { |
|---|
| 225 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 226 | |
|---|
| 227 | while (form->currentStream) { |
|---|
| 228 | CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength); |
|---|
| 229 | if (bytesRead < 0) { |
|---|
| 230 | *error = CFReadStreamGetError(form->currentStream); |
|---|
| 231 | return -1; |
|---|
| 232 | } |
|---|
| 233 | if (bytesRead > 0) { |
|---|
| 234 | error->error = 0; |
|---|
| 235 | *atEOF = FALSE; |
|---|
| 236 | return bytesRead; |
|---|
| 237 | } |
|---|
| 238 | openNextStream(form); |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | error->error = 0; |
|---|
| 242 | *atEOF = TRUE; |
|---|
| 243 | return 0; |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | static Boolean formCanRead(CFReadStreamRef stream, void* context) |
|---|
| 247 | { |
|---|
| 248 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 249 | |
|---|
| 250 | while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) { |
|---|
| 251 | openNextStream(form); |
|---|
| 252 | } |
|---|
| 253 | if (!form->currentStream) { |
|---|
| 254 | CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0); |
|---|
| 255 | return FALSE; |
|---|
| 256 | } |
|---|
| 257 | return CFReadStreamHasBytesAvailable(form->currentStream); |
|---|
| 258 | } |
|---|
| 259 | |
|---|
| 260 | static void formClose(CFReadStreamRef stream, void* context) |
|---|
| 261 | { |
|---|
| 262 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 263 | |
|---|
| 264 | closeCurrentStream(form); |
|---|
| 265 | } |
|---|
| 266 | |
|---|
| 267 | static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context) |
|---|
| 268 | { |
|---|
| 269 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 270 | |
|---|
| 271 | if (form->currentStream) |
|---|
| 272 | CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode); |
|---|
| 273 | SchedulePair pair = { runLoop, runLoopMode }; |
|---|
| 274 | CFSetAddValue(form->scheduledRunLoopPairs, &pair); |
|---|
| 275 | } |
|---|
| 276 | |
|---|
| 277 | static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context) |
|---|
| 278 | { |
|---|
| 279 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 280 | |
|---|
| 281 | if (form->currentStream) |
|---|
| 282 | CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode); |
|---|
| 283 | SchedulePair pair = { runLoop, runLoopMode }; |
|---|
| 284 | CFSetRemoveValue(form->scheduledRunLoopPairs, &pair); |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context) |
|---|
| 288 | { |
|---|
| 289 | FormStreamFields* form = static_cast<FormStreamFields*>(context); |
|---|
| 290 | |
|---|
| 291 | switch (type) { |
|---|
| 292 | case kCFStreamEventHasBytesAvailable: |
|---|
| 293 | CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0); |
|---|
| 294 | break; |
|---|
| 295 | case kCFStreamEventErrorOccurred: { |
|---|
| 296 | CFStreamError readStreamError = CFReadStreamGetError(stream); |
|---|
| 297 | CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError); |
|---|
| 298 | break; |
|---|
| 299 | } |
|---|
| 300 | case kCFStreamEventEndEncountered: |
|---|
| 301 | openNextStream(form); |
|---|
| 302 | if (!form->currentStream) |
|---|
| 303 | CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0); |
|---|
| 304 | break; |
|---|
| 305 | case kCFStreamEventNone: |
|---|
| 306 | LOG_ERROR("unexpected kCFStreamEventNone"); |
|---|
| 307 | break; |
|---|
| 308 | case kCFStreamEventOpenCompleted: |
|---|
| 309 | LOG_ERROR("unexpected kCFStreamEventOpenCompleted"); |
|---|
| 310 | break; |
|---|
| 311 | case kCFStreamEventCanAcceptBytes: |
|---|
| 312 | LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes"); |
|---|
| 313 | break; |
|---|
| 314 | } |
|---|
| 315 | } |
|---|
| 316 | |
|---|
| 317 | void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData) |
|---|
| 318 | { |
|---|
| 319 | if (!formData) { |
|---|
| 320 | wkCFURLRequestSetHTTPRequestBodyParts(request, 0); |
|---|
| 321 | return; |
|---|
| 322 | } |
|---|
| 323 | |
|---|
| 324 | size_t count = formData->elements().size(); |
|---|
| 325 | |
|---|
| 326 | if (count == 0) |
|---|
| 327 | return; |
|---|
| 328 | |
|---|
| 329 | // Handle the common special case of one piece of form data, with no files. |
|---|
| 330 | if (count == 1) { |
|---|
| 331 | const FormDataElement& element = formData->elements()[0]; |
|---|
| 332 | if (element.m_type == FormDataElement::data) { |
|---|
| 333 | CFDataRef data = CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size()); |
|---|
| 334 | CFURLRequestSetHTTPRequestBody(request, data); |
|---|
| 335 | CFRelease(data); |
|---|
| 336 | return; |
|---|
| 337 | } |
|---|
| 338 | } |
|---|
| 339 | |
|---|
| 340 | RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); |
|---|
| 341 | |
|---|
| 342 | for (size_t i = 0; i < count; ++i) { |
|---|
| 343 | const FormDataElement& element = formData->elements()[i]; |
|---|
| 344 | if (element.m_type == FormDataElement::data) { |
|---|
| 345 | RetainPtr<CFDataRef> data(AdoptCF, CFDataCreate(0, reinterpret_cast<const UInt8*>(element.m_data.data()), element.m_data.size())); |
|---|
| 346 | CFArrayAppendValue(array.get(), data.get()); |
|---|
| 347 | } else { |
|---|
| 348 | RetainPtr<CFStringRef> filename(AdoptCF, element.m_filename.createCFString()); |
|---|
| 349 | CFArrayAppendValue(array.get(), filename.get()); |
|---|
| 350 | } |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | wkCFURLRequestSetHTTPRequestBodyParts(request, array.get()); |
|---|
| 354 | } |
|---|
| 355 | |
|---|
| 356 | PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef request) |
|---|
| 357 | { |
|---|
| 358 | if (RetainPtr<CFDataRef> bodyData = CFURLRequestCopyHTTPRequestBody(request)) |
|---|
| 359 | return FormData::create(CFDataGetBytePtr(bodyData.get()), CFDataGetLength(bodyData.get())); |
|---|
| 360 | |
|---|
| 361 | if (RetainPtr<CFArrayRef> bodyParts = wkCFURLRequestCopyHTTPRequestBodyParts(request)) { |
|---|
| 362 | RefPtr<FormData> formData = FormData::create(); |
|---|
| 363 | |
|---|
| 364 | CFIndex count = CFArrayGetCount(bodyParts.get()); |
|---|
| 365 | for (CFIndex i = 0; i < count; i++) { |
|---|
| 366 | CFTypeRef bodyPart = CFArrayGetValueAtIndex(bodyParts.get(), i); |
|---|
| 367 | CFTypeID typeID = CFGetTypeID(bodyPart); |
|---|
| 368 | if (typeID == CFStringGetTypeID()) { |
|---|
| 369 | String filename = (CFStringRef)bodyPart; |
|---|
| 370 | formData->appendFile(filename); |
|---|
| 371 | } else if (typeID == CFDataGetTypeID()) { |
|---|
| 372 | CFDataRef data = (CFDataRef)bodyPart; |
|---|
| 373 | formData->appendData(CFDataGetBytePtr(data), CFDataGetLength(data)); |
|---|
| 374 | } else |
|---|
| 375 | ASSERT_NOT_REACHED(); |
|---|
| 376 | } |
|---|
| 377 | return formData.release(); |
|---|
| 378 | } |
|---|
| 379 | |
|---|
| 380 | // FIXME: what to do about arbitrary body streams? |
|---|
| 381 | return 0; |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | } |
|---|