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 "CString.h" 35 #include "FileSystem.h" 36 #include "FormData.h" 37 #include <CFNetwork/CFURLRequestPriv.h> 38 #include <CoreFoundation/CFStreamAbstract.h> 39 #include <WebKitSystemInterface/WebKitSystemInterface.h> 40 #include <sys/types.h> 41 #include <wtf/Assertions.h> 42 #include <wtf/HashMap.h> 43 #include <wtf/RetainPtr.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 } 385