Home | History | Annotate | Download | only in cf
      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