Esempio n. 1
0
String XSSAuditor::decodeURL(const String& string, const TextEncoding& encoding, bool decodeEntities, bool decodeURLEscapeSequencesTwice)
{
    String result;
    String url = string;

    url.replace('+', ' ');
    result = decodeURLEscapeSequences(url);
    CString utf8Url = result.utf8();
    String decodedResult = encoding.decode(utf8Url.data(), utf8Url.length());
    if (!decodedResult.isEmpty())
        result = decodedResult;
    if (decodeURLEscapeSequencesTwice) {
        result = decodeURLEscapeSequences(result);
        utf8Url = result.utf8();
        decodedResult = encoding.decode(utf8Url.data(), utf8Url.length());
        if (!decodedResult.isEmpty())
            result = decodedResult;
    }
    if (decodeEntities)
        result = decodeHTMLEntities(result);
    return result;
}
bool ScriptController::executeIfJavaScriptURL(const KURL& url, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL)
{
    if (!protocolIsJavaScript(url))
        return false;

    if (!m_frame->page()
        || !m_frame->page()->javaScriptURLsAreAllowed()
        || !m_frame->document()->contentSecurityPolicy()->allowJavaScriptURLs()
        || m_frame->inViewSourceMode())
        return true;

    // We need to hold onto the Frame here because executing script can
    // destroy the frame.
    RefPtr<Frame> protector(m_frame);
    RefPtr<Document> ownerDocument(m_frame->document());

    const int javascriptSchemeLength = sizeof("javascript:") - 1;

    String decodedURL = decodeURLEscapeSequences(url.string());
    ScriptValue result = executeScript(decodedURL.substring(javascriptSchemeLength));

    // If executing script caused this frame to be removed from the page, we
    // don't want to try to replace its document!
    if (!m_frame->page())
        return true;

    String scriptResult;
#if USE(JSC)
    JSDOMWindowShell* shell = windowShell(mainThreadNormalWorld());
    JSC::ExecState* exec = shell->window()->globalExec();
    if (!result.getString(exec, scriptResult))
        return true;
#else
    if (!result.getString(scriptResult))
        return true;
#endif

    // FIXME: We should always replace the document, but doing so
    //        synchronously can cause crashes:
    //        http://bugs.webkit.org/show_bug.cgi?id=16782
    if (shouldReplaceDocumentIfJavaScriptURL == ReplaceDocumentIfJavaScriptURL) {
        // We're still in a frame, so there should be a DocumentLoader.
        ASSERT(m_frame->document()->loader());
        
        // DocumentWriter::replaceDocument can cause the DocumentLoader to get deref'ed and possible destroyed,
        // so protect it with a RefPtr.
        if (RefPtr<DocumentLoader> loader = m_frame->document()->loader())
            loader->writer()->replaceDocument(scriptResult, ownerDocument.get());
    }
    return true;
}
Esempio n. 3
0
String KURL::fileSystemPath() const
{
    if (!isValid())
        return String();

    if (isLocalFile())
        return static_cast<QUrl>(*this).toLocalFile();

    // A valid qrc resource path begins with a colon.
    if (protocolIs("qrc"))
        return ":" + decodeURLEscapeSequences(path());

    return String();
}
Esempio n. 4
0
// Tests that KURL::decodeURLEscapeSequences works as expected
TEST(KURLTest, Decode)
{
    struct DecodeCase {
        const char* input;
        const char* output;
    } decodeCases[] = {
        {"hello, world", "hello, world"},
        {"%01%02%03%04%05%06%07%08%09%0a%0B%0C%0D%0e%0f/", "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0B\x0C\x0D\x0e\x0f/"},
        {"%10%11%12%13%14%15%16%17%18%19%1a%1B%1C%1D%1e%1f/", "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1B\x1C\x1D\x1e\x1f/"},
        {"%20%21%22%23%24%25%26%27%28%29%2a%2B%2C%2D%2e%2f/", " !\"#$%&'()*+,-.//"},
        {"%30%31%32%33%34%35%36%37%38%39%3a%3B%3C%3D%3e%3f/", "0123456789:;<=>?/"},
        {"%40%41%42%43%44%45%46%47%48%49%4a%4B%4C%4D%4e%4f/", "@ABCDEFGHIJKLMNO/"},
        {"%50%51%52%53%54%55%56%57%58%59%5a%5B%5C%5D%5e%5f/", "PQRSTUVWXYZ[\\]^_/"},
        {"%60%61%62%63%64%65%66%67%68%69%6a%6B%6C%6D%6e%6f/", "`abcdefghijklmno/"},
        {"%70%71%72%73%74%75%76%77%78%79%7a%7B%7C%7D%7e%7f/", "pqrstuvwxyz{|}~\x7f/"},
          // Test un-UTF-8-ization.
        {"%e4%bd%a0%e5%a5%bd", "\xe4\xbd\xa0\xe5\xa5\xbd"},
    };

    for (size_t i = 0; i < arraysize(decodeCases); i++) {
        String input(decodeCases[i].input);
        String str = decodeURLEscapeSequences(input);
        EXPECT_STREQ(decodeCases[i].output, str.utf8().data());
    }

    // Our decode should decode %00
    String zero = decodeURLEscapeSequences("%00");
    EXPECT_STRNE("%00", zero.utf8().data());

    // Test the error behavior for invalid UTF-8 (we differ from WebKit here).
    String invalid = decodeURLEscapeSequences("%e4%a0%e5%a5%bd");
    UChar invalidExpectedHelper[4] = { 0x00e4, 0x00a0, 0x597d, 0 };
    String invalidExpected(
        reinterpret_cast<const ::UChar*>(invalidExpectedHelper),
        3);
    EXPECT_EQ(invalidExpected, invalid);
}
bool DOMFileSystemBase::crackFileSystemURL(const KURL& url, FileSystemType& type, String& filePath)
{
    if (!url.protocolIs("filesystem"))
        return false;

    if (!url.innerURL())
        return false;

    String typeString = url.innerURL()->path().substring(1);
    if (!pathPrefixToFileSystemType(typeString, type))
        return false;

    filePath = decodeURLEscapeSequences(url.path());
    return true;
}
Esempio n. 6
0
bool ScriptController::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument)
{
    if (!protocolIsJavaScript(url))
        return false;

    if (m_frame->page() && !m_frame->page()->javaScriptURLsAreAllowed())
        return true;

    if (m_frame->inViewSourceMode())
        return true;

#if PLATFORM(APOLLO)
    // We should return true even though the script is not going to be executed.
    // Otherwise the frame will actually try to navigate to "javascript:"
    // which will eventually fail, but will also stop any other in progress requests in this page
    // like CSS files, images or JS files
    if (!m_frame->loader()->client()->canExecuteScriptURL())
        return true;
#endif

    const int javascriptSchemeLength = sizeof("javascript:") - 1;

    String decodedURL = decodeURLEscapeSequences(url.string());
    ScriptValue result;
    if (xssAuditor()->canEvaluateJavaScriptURL(decodedURL))
        result = executeScript(decodedURL.substring(javascriptSchemeLength), userGesture, AllowXSS);

    String scriptResult;
#if USE(JSC)
    JSDOMWindowShell* shell = windowShell(mainThreadNormalWorld());
    JSC::ExecState* exec = shell->window()->globalExec();
    if (!result.getString(exec, scriptResult))
        return true;
#else
    if (!result.getString(scriptResult))
        return true;
#endif

    // FIXME: We should always replace the document, but doing so
    //        synchronously can cause crashes:
    //        http://bugs.webkit.org/show_bug.cgi?id=16782
    if (replaceDocument) 
        m_frame->loader()->writer()->replaceDocument(scriptResult);

    return true;
}
Esempio n. 7
0
    void didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
    {
        m_response = adoptGRef(response.toSoupMessage());
        m_download->didReceiveResponse(response);

        if (response.httpStatusCode() >= 400) {
            downloadFailed(platformDownloadNetworkError(response.httpStatusCode(), response.url().string(), response.httpStatusText()));
            return;
        }

        String suggestedFilename = response.suggestedFilename();
        if (suggestedFilename.isEmpty()) {
            URL url = response.url();
            url.setQuery(String());
            url.removeFragmentIdentifier();
            suggestedFilename = decodeURLEscapeSequences(url.lastPathComponent());
        }

        bool overwrite;
        String destinationURI = m_download->decideDestinationWithSuggestedFilename(suggestedFilename, overwrite);
        if (destinationURI.isEmpty()) {
#if PLATFORM(GTK)
            GOwnPtr<char> buffer(g_strdup_printf(_("Cannot determine destination URI for download with suggested filename %s"), suggestedFilename.utf8().data()));
            String errorMessage = String::fromUTF8(buffer.get());
#else
            String errorMessage = makeString("Cannot determine destination URI for download with suggested filename ", suggestedFilename);
#endif
            downloadFailed(platformDownloadDestinationError(response, errorMessage));
            return;
        }

        GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destinationURI.utf8().data()));
        GOwnPtr<GError> error;
        m_outputStream = adoptGRef(g_file_replace(file.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &error.outPtr()));
        if (!m_outputStream) {
            downloadFailed(platformDownloadDestinationError(response, error->message));
            return;
        }

        GRefPtr<GFileInfo> info = adoptGRef(g_file_info_new());
        g_file_info_set_attribute_string(info.get(), "metadata::download-uri", response.url().string().utf8().data());
        g_file_set_attributes_async(file.get(), info.get(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, 0, 0, 0);

        m_download->didCreateDestination(destinationURI);
    }
Esempio n. 8
0
void MediaPlayerPrivateEA::load(const String& url)
{
    if (!mpEAWebKitView)
        mpEAWebKitView = GetEAWebKitMediaView(mpWebCorePlayer);

    // Note: We get the autoplay and loop flags from the element directly.
    HTMLMediaElement* element = static_cast<HTMLMediaElement*>(mpWebCorePlayer->mediaPlayerClient());
    mIsLooping = element->loop();
   
    MediaUpdateInfo& info = GetMediaUpdateInfo();  
    info.mIsLooping = mIsLooping; 
	WebCore::KURL kurl(WebCore::KURL(), url);
	String escapedUrl = kurl.protocolIsInHTTPFamily() ? url : decodeURLEscapeSequences(url);

    GetFixedString(info.mURI)->assign(escapedUrl.characters(), escapedUrl.length());
    ClientUpdate(MediaUpdateInfo::kLoad);
    
    // Set info up to play
    setMuted(mpWebCorePlayer->muted());
    setVolume(mpWebCorePlayer->volume());
    
    if(mNetworkState != MediaPlayer::Loading)
    {
        mNetworkState = MediaPlayer::Loading;
        mpWebCorePlayer->networkStateChanged();
    }
    
    if(mReadyState != MediaPlayer::HaveNothing)
    {
       mReadyState = MediaPlayer::HaveNothing; 
       mpWebCorePlayer->readyStateChanged(); 
    }
    
    bool autoPlay = element->autoplay();
    if(autoPlay)
        mpWebCorePlayer->play();            // This just calls our play.

    ClientUpdateStates();                   // This will update the pause/play state right away.

    // Set up an update timer to control the repaint and playback.
    if (mUpdateTimer.isActive())
        mUpdateTimer.stop();
    const double kUpdateTime = 1.0/60.0;      // We want to update at 60hz so we keep up with the render calls. 
    mUpdateTimer.startRepeating(kUpdateTime);
}
Esempio n. 9
0
bool CSPSourceList::parsePath(const UChar* begin, const UChar* end, String& path)
{
    ASSERT(begin <= end);
    ASSERT(path.isEmpty());

    const UChar* position = begin;
    skipWhile<UChar, isPathComponentCharacter>(position, end);
    // path/to/file.js?query=string || path/to/file.js#anchor
    //                ^                               ^
    if (position < end)
        m_policy->reportInvalidPathCharacter(m_directiveName, String(begin, end - begin), *position);

    path = decodeURLEscapeSequences(String(begin, position - begin));

    ASSERT(position <= end);
    ASSERT(position == end || (*position == '#' || *position == '?'));
    return true;
}
Esempio n. 10
0
void PluginView::performJavaScriptURLRequest(URLRequest* request)
{
    ASSERT(protocolIsJavaScript(request->request().url()));

    RefPtr<Frame> frame = m_pluginElement->document()->frame();
    if (!frame)
        return;
    
    String jsString = decodeURLEscapeSequences(request->request().url().string().substring(sizeof("javascript:") - 1));

    if (!request->target().isNull()) {
        // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
        if (frame->tree()->find(request->target()) != frame) {
            // Let the plug-in know that its frame load failed.
            m_plugin->frameDidFail(request->requestID(), false);
            return;
        }
    }

    // Evaluate the JavaScript code. Note that running JavaScript here could cause the plug-in to be destroyed, so we
    // grab references to the plug-in here.
    RefPtr<Plugin> plugin = m_plugin;
    ScriptValue result = frame->script()->executeScript(jsString, request->allowPopups());

    // Check if evaluating the JavaScript destroyed the plug-in.
    if (!plugin->controller())
        return;

    // Don't notify the plug-in at all about targeted javascript: requests. This matches Mozilla and WebKit1.
    if (!request->target().isNull())
        return;

#if USE(JSC)
    ScriptState* scriptState = frame->script()->globalObject(pluginWorld())->globalExec();
#elif USE(V8)
    ScriptState* scriptState = ScriptState::forContext(frame->script()->proxy()->context(frame.get()));
#endif

    String resultString;
    result.getString(scriptState, resultString);
  
    // Send the result back to the plug-in.
    plugin->didEvaluateJavaScript(request->requestID(), resultString);
}
WebString WebPluginContainerImpl::executeScriptURL(const WebURL& url, bool popupsAllowed)
{
    Frame* frame = m_element->document()->frame();
    if (!frame)
        return WebString();

    const KURL& kurl = url;
    ASSERT(kurl.protocolIs("javascript"));

    String script = decodeURLEscapeSequences(
                        kurl.string().substring(strlen("javascript:")));

    ScriptValue result = frame->script()->executeScript(script, popupsAllowed);

    // Failure is reported as a null string.
    String resultStr;
    result.getString(resultStr);
    return resultStr;
}
Esempio n. 12
0
    void didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
    {
        m_response = response;
        m_download->didReceiveResponse(response);

        if (response.httpStatusCode() >= 400) {
            downloadFailed(platformDownloadNetworkError(response.httpStatusCode(), response.url().string(), response.httpStatusText()));
            return;
        }

        String suggestedFilename = response.suggestedFilename();
        if (suggestedFilename.isEmpty()) {
            URL url = response.url();
            url.setQuery(String());
            url.removeFragmentIdentifier();
            suggestedFilename = decodeURLEscapeSequences(url.lastPathComponent());
        }

        bool overwrite;
        m_destinationURI = m_download->decideDestinationWithSuggestedFilename(suggestedFilename, overwrite);
        if (m_destinationURI.isEmpty()) {
#if PLATFORM(GTK)
            GUniquePtr<char> buffer(g_strdup_printf(_("Cannot determine destination URI for download with suggested filename %s"), suggestedFilename.utf8().data()));
            String errorMessage = String::fromUTF8(buffer.get());
#else
            String errorMessage = makeString("Cannot determine destination URI for download with suggested filename ", suggestedFilename);
#endif
            downloadFailed(platformDownloadDestinationError(response, errorMessage));
            return;
        }

        String intermediateURI = m_destinationURI + ".wkdownload";
        m_intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.utf8().data()));
        GOwnPtr<GError> error;
        m_outputStream = adoptGRef(g_file_replace(m_intermediateFile.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &error.outPtr()));
        if (!m_outputStream) {
            downloadFailed(platformDownloadDestinationError(response, error->message));
            return;
        }

        m_download->didCreateDestination(m_destinationURI);
    }
bool DOMFileSystemBase::crackFileSystemURL(const KURL& url, FileSystemType& type, String& filePath)
{
    if (!url.protocolIs("filesystem"))
        return false;

    if (!url.innerURL())
        return false;

    String typeString = url.innerURL()->path().substring(1);
    if (typeString == temporaryPathPrefix)
        type = FileSystemTypeTemporary;
    else if (typeString == persistentPathPrefix)
        type = FileSystemTypePersistent;
    else if (typeString == externalPathPrefix)
        type = FileSystemTypeExternal;
    else
        return false;

    filePath = decodeURLEscapeSequences(url.path());
    return true;
}
Esempio n. 14
0
    void didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
    {
        m_response = adoptGRef(response.toSoupMessage());
        m_download->didReceiveResponse(response);

        if (response.httpStatusCode() >= 400) {
            downloadFailed(downloadNetworkError(ResourceError(errorDomainDownload, response.httpStatusCode(),
                                                              response.url().string(), response.httpStatusText())));
            return;
        }

        String suggestedFilename = response.suggestedFilename();
        if (suggestedFilename.isEmpty()) {
            KURL url = response.url();
            url.setQuery(String());
            url.removeFragmentIdentifier();
            suggestedFilename = decodeURLEscapeSequences(url.lastPathComponent());
        }

        bool overwrite;
        String destinationURI = m_download->decideDestinationWithSuggestedFilename(suggestedFilename.utf8().data(), overwrite);
        if (destinationURI.isEmpty()) {
            GOwnPtr<char> errorMessage(g_strdup_printf(_("Cannot determine destination URI for download with suggested filename %s"),
                                                       suggestedFilename.utf8().data()));
            downloadFailed(downloadDestinationError(response, errorMessage.get()));
            return;
        }

        GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destinationURI.utf8().data()));
        GOwnPtr<GError> error;
        m_outputStream = adoptGRef(g_file_replace(file.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &error.outPtr()));
        if (!m_outputStream) {
            downloadFailed(downloadDestinationError(response, error->message));
            return;
        }

        m_download->didCreateDestination(destinationURI);
    }
bool ScriptController::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument)
{
    if (!protocolIsJavaScript(url))
        return false;

    if (m_frame->page() && !m_frame->page()->javaScriptURLsAreAllowed())
        return true;

    if (m_frame->inViewSourceMode())
        return true;

    const int javascriptSchemeLength = sizeof("javascript:") - 1;

    String script = decodeURLEscapeSequences(url.string().substring(javascriptSchemeLength));
    ScriptValue result;
    if (xssAuditor()->canEvaluateJavaScriptURL(script))
        result = executeScript(script, userGesture);

    String scriptResult;
#if USE(JSC)
    JSDOMWindowShell* shell = windowShell(mainThreadNormalWorld());
    JSC::ExecState* exec = shell->window()->globalExec();
    if (!result.getString(exec, scriptResult))
        return true;
#else
    if (!result.getString(scriptResult))
        return true;
#endif

    // FIXME: We should always replace the document, but doing so
    //        synchronously can cause crashes:
    //        http://bugs.webkit.org/show_bug.cgi?id=16782
    if (replaceDocument) 
        m_frame->loader()->replaceDocument(scriptResult);

    return true;
}
Esempio n. 16
0
RefPtr<SecurityOrigin> SecurityOrigin::maybeCreateFromDatabaseIdentifier(const String& databaseIdentifier)
{
    // Make sure there's a first separator
    size_t separator1 = databaseIdentifier.find(separatorCharacter);
    if (separator1 == notFound)
        return nullptr;

    // Make sure there's a second separator
    size_t separator2 = databaseIdentifier.reverseFind(separatorCharacter);
    if (separator2 == notFound)
        return nullptr;

    // Ensure there were at least 2 separator characters. Some hostnames on intranets have
    // underscores in them, so we'll assume that any additional underscores are part of the host.
    if (separator1 == separator2)
        return nullptr;

    // Make sure the port section is a valid port number or doesn't exist
    bool portOkay;
    int port = databaseIdentifier.right(databaseIdentifier.length() - separator2 - 1).toInt(&portOkay);
    bool portAbsent = (separator2 == databaseIdentifier.length() - 1);
    if (!(portOkay || portAbsent))
        return nullptr;

    if (port < 0 || port > MaxAllowedPort)
        return nullptr;

    // Split out the 3 sections of data
    String protocol = databaseIdentifier.substring(0, separator1);
    String host = databaseIdentifier.substring(separator1 + 1, separator2 - separator1 - 1);
    
    host = decodeURLEscapeSequences(host);
    auto origin = create(URL(URL(), protocol + "://" + host + "/"));
    origin->m_port = port;
    return WTFMove(origin);
}
Esempio n. 17
0
void Page::userStyleSheetLocationChanged()
{
    // FIXME: Eventually we will move to a model of just being handed the sheet
    // text instead of loading the URL ourselves.
    KURL url = m_settings->userStyleSheetLocation();

    m_didLoadUserStyleSheet = false;
    m_userStyleSheet = String();

    // Data URLs with base64-encoded UTF-8 style sheets are common. We can process them
    // synchronously and avoid using a loader.
    if (url.protocolIsData() && url.string().startsWith("data:text/css;charset=utf-8;base64,")) {
        m_didLoadUserStyleSheet = true;

        Vector<char> styleSheetAsUTF8;
        if (base64Decode(decodeURLEscapeSequences(url.string().substring(35)), styleSheetAsUTF8, Base64IgnoreWhitespace))
            m_userStyleSheet = String::fromUTF8(styleSheetAsUTF8.data(), styleSheetAsUTF8.size());
    }

    for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        if (frame->document())
            frame->document()->styleEngine()->updatePageUserSheet();
    }
}
Esempio n. 18
0
String substituteVariableReferences(const String& reference, Document* document, WMLVariableEscapingMode escapeMode)
{
    ASSERT(document);

    if (reference.isEmpty())
        return reference;

    WMLPageState* pageState = wmlPageStateForDocument(document);
    if (!pageState)
        return reference;

    bool isValid = true; 
    String remainingInput = reference;
    String result;

    while (!remainingInput.isEmpty()) {
        ASSERT(isValid);

        int start = remainingInput.find("$");
        if (start == -1) {
            // Consume all remaining characters, as there's nothing more to substitute
            result += remainingInput;
            break;
        }

        // Consume all characters until the variable reference beginning
        result += remainingInput.left(start);
        remainingInput.remove(0, start);

        // Transform adjacent dollar signs into a single dollar sign as string literal
        if (remainingInput[1] == '$') {
            result += "$";
            remainingInput.remove(0, 2);
            continue;
        }

        String variableName;
        String conversionMode;

        if (remainingInput[1] == '(') {
            int referenceEndPosition = remainingInput.find(")");
            if (referenceEndPosition == -1) {
                isValid = false;
                break;
            }

            variableName = remainingInput.substring(2, referenceEndPosition - 2);
            remainingInput.remove(0, referenceEndPosition + 1);

            // Determine variable conversion mode string
            int pos = variableName.find(':');
            if (pos != -1) {
                conversionMode = variableName.substring(pos + 1, variableName.length() - (pos + 1));
                variableName = variableName.left(pos);
            }
        } else {
            int length = remainingInput.length();
            int referenceEndPosition = 1;

            for (; referenceEndPosition < length; ++referenceEndPosition) {
                if (!isValidVariableNameCharacter(remainingInput[referenceEndPosition]))
                    break;
            }

            variableName = remainingInput.substring(1, referenceEndPosition - 1);
            remainingInput.remove(0, referenceEndPosition);
        }

        isValid = isValidVariableName(variableName);
        if (!isValid)
            break;

        ASSERT(!variableName.isEmpty());

        String variableValue = pageState->getVariable(variableName);
        if (variableValue.isEmpty())
            continue;

        if (containsVariableReference(variableValue, isValid)) {
            if (!isValid)
                break;

            variableValue = substituteVariableReferences(variableValue, document, escapeMode);
            continue;
        }

        if (!conversionMode.isEmpty()) {
            // Override default escape mode, if desired
            WMLVariableEscapingMode specifiedEscapeMode = WMLVariableEscapingNone; 
            if ((isValid = isValidVariableEscapingModeString(conversionMode, specifiedEscapeMode)))
                escapeMode = specifiedEscapeMode;

            if (!isValid)
                break;
        }

        switch (escapeMode) {
        case WMLVariableEscapingNone:
            break;
        case WMLVariableEscapingEscape:
            variableValue = encodeWithURLEscapeSequences(variableValue); 
            break;
        case WMLVariableEscapingUnescape:
            variableValue = decodeURLEscapeSequences(variableValue);
            break;
        }

        result += variableValue;
        ASSERT(isValid);
    }

    if (!isValid) {
        reportWMLError(document, WMLErrorInvalidVariableReference);
        return reference;
    }

    return result;
}
Esempio n. 19
0
// The cookie String passed into this method will only contian the name value pairs as well as other related cookie
// attributes such as max-age and domain. Set-Cookie should never be part of this string.
ParsedCookie* CookieParser::parseOneCookie(const String& cookie, unsigned start, unsigned end, double curTime)
{
    ParsedCookie* res = new ParsedCookie(curTime);

    if (!res)
        LOG_AND_DELETE("Out of memory");

    res->setProtocol(m_defaultCookieURL.protocol());

    // Parse [NAME "="] VALUE
    unsigned tokenEnd = start; // Token end contains the position of the '=' or the end of a token
    unsigned pairEnd = start; // Pair end contains always the position of the ';'

    // find the *first* ';' and the '=' (if they exist)
    bool quoteFound = false;
    bool foundEqual = false;
    while (pairEnd < end && (cookie[pairEnd] != ';' || quoteFound)) {
        if (tokenEnd == start && cookie[pairEnd] == '=') {
            tokenEnd = pairEnd;
            foundEqual = true;
        }
        if (cookie[pairEnd] == '"')
            quoteFound = !quoteFound;
        pairEnd++;
    }

    unsigned tokenStart = start;

    bool hasName = false; // This is a hack to avoid changing too much in this
                          // brutally brittle code.
    if (tokenEnd != start) {
        // There is a '=' so parse the NAME
        unsigned nameEnd = tokenEnd;

        // The tokenEnd is the position of the '=' so the nameEnd is one less
        nameEnd--;

        // Remove lightweight spaces.
        while (nameEnd && isLightweightSpace(cookie[nameEnd]))
            nameEnd--;

        while (tokenStart < nameEnd && isLightweightSpace(cookie[tokenStart]))
            tokenStart++;

        if (nameEnd + 1 <= tokenStart)
            LOG_AND_DELETE("Empty name. Rejecting the cookie");

        String name = cookie.substring(tokenStart, nameEnd + 1 - start);
        res->setName(name);
        hasName = true;
    }

    // Now parse the VALUE
    tokenStart = tokenEnd + 1;
    if (!hasName)
        --tokenStart;

    // Skip lightweight spaces in our token
    while (tokenStart < pairEnd && isLightweightSpace(cookie[tokenStart]))
        tokenStart++;

    tokenEnd = pairEnd;
    while (tokenEnd > tokenStart && isLightweightSpace(cookie[tokenEnd - 1]))
        tokenEnd--;

    String value;
    if (tokenEnd == tokenStart) {
        // Firefox accepts empty value so we will do the same
        value = String();
    } else
        value = cookie.substring(tokenStart, tokenEnd - tokenStart);

    if (hasName)
        res->setValue(value);
    else if (foundEqual) {
        delete res;
        return 0;
    } else
        res->setName(value); // No NAME=VALUE, only NAME

    while (pairEnd < end) {
        // Switch to the next pair as pairEnd is on the ';' and fast-forward any lightweight spaces.
        pairEnd++;
        while (pairEnd < end && isLightweightSpace(cookie[pairEnd]))
            pairEnd++;

        tokenStart = pairEnd;
        tokenEnd = tokenStart; // initialize token end to catch first '='

        while (pairEnd < end && cookie[pairEnd] != ';') {
            if (tokenEnd == tokenStart && cookie[pairEnd] == '=')
                tokenEnd = pairEnd;
            pairEnd++;
        }

        // FIXME : should we skip lightweight spaces here ?

        unsigned length = tokenEnd - tokenStart;
        unsigned tokenStartSvg = tokenStart;

        String parsedValue;
        if (tokenStart != tokenEnd) {
            // There is an equal sign so remove lightweight spaces in VALUE
            tokenStart = tokenEnd + 1;
            while (tokenStart < pairEnd && isLightweightSpace(cookie[tokenStart]))
                tokenStart++;

            tokenEnd = pairEnd;
            while (tokenEnd > tokenStart && isLightweightSpace(cookie[tokenEnd - 1]))
                tokenEnd--;

            parsedValue = cookie.substring(tokenStart, tokenEnd - tokenStart);
        } else {
            // If the parsedValue is empty, initialise it in case we need it
            parsedValue = String();
            // Handle a token without value.
            length = pairEnd - tokenStart;
        }

       // Detect which "cookie-av" is parsed
       // Look at the first char then parse the whole for performance issue
        switch (cookie[tokenStartSvg]) {
        case 'P':
        case 'p' : {
            if (length >= 4 && cookie.find("ath", tokenStartSvg + 1, false)) {
                // We need the path to be decoded to match those returned from KURL::path().
                // The path attribute may or may not include percent-encoded characters. Fortunately
                // if there are no percent-encoded characters, decoding the url is a no-op.
                res->setPath(decodeURLEscapeSequences(parsedValue));
            } else
                LOG_AND_DELETE("Invalid cookie %s (path)", cookie.ascii().data());
            break;
        }

        case 'D':
        case 'd' : {
            if (length >= 6 && cookie.find("omain", tokenStartSvg + 1, false)) {
                if (parsedValue.length() > 1 && parsedValue[0] == '"' && parsedValue[parsedValue.length() - 1] == '"')
                    parsedValue = parsedValue.substring(1, parsedValue.length() - 2);
                // If the domain does not start with a dot, add one for security checks,
                // For example: ab.c.com dose not domain match b.c.com;
                String realDomain = parsedValue[0] == '.' ? parsedValue : "." + parsedValue;
                res->setDomain(realDomain);
            } else
                LOG_AND_DELETE("Invalid cookie %s (domain)", cookie.ascii().data());
            break;
        }

        case 'E' :
        case 'e' : {
            if (length >= 7 && cookie.find("xpires", tokenStartSvg + 1, false))
                res->setExpiry(parsedValue);
            else
                LOG_AND_DELETE("Invalid cookie %s (expires)", cookie.ascii().data());
            break;
        }

        case 'M' :
        case 'm' : {
            if (length >= 7 && cookie.find("ax-age", tokenStartSvg + 1, false))
                res->setMaxAge(parsedValue);
            else
                LOG_AND_DELETE("Invalid cookie %s (max-age)", cookie.ascii().data());
            break;
        }

        case 'C' :
        case 'c' : {
            if (length >= 7 && cookie.find("omment", tokenStartSvg + 1, false))
                // We do not have room for the comment part (and so do Mozilla) so just log the comment.
                LOG(Network, "Comment %s for ParsedCookie : %s\n", parsedValue.ascii().data(), cookie.ascii().data());
            else
                LOG_AND_DELETE("Invalid cookie %s (comment)", cookie.ascii().data());
            break;
        }

        case 'V' :
        case 'v' : {
            if (length >= 7 && cookie.find("ersion", tokenStartSvg + 1, false)) {
                // Although the out-of-dated Cookie Spec(RFC2965, http://tools.ietf.org/html/rfc2965) defined
                // the value of version can only contain DIGIT, some random sites, e.g. https://devforums.apple.com
                // would use double quotation marks to quote the digit. So we need to get rid of them for compliance.
                if (parsedValue.length() > 1 && parsedValue[0] == '"' && parsedValue[parsedValue.length() - 1] == '"')
                    parsedValue = parsedValue.substring(1, parsedValue.length() - 2);

                if (parsedValue.toInt() != 1)
                    LOG_AND_DELETE("ParsedCookie version %d not supported (only support version=1)", parsedValue.toInt());
            } else
                LOG_AND_DELETE("Invalid cookie %s (version)", cookie.ascii().data());
            break;
        }

        case 'S' :
        case 's' : {
            // Secure is a standalone token ("Secure;")
            if (length >= 6 && cookie.find("ecure", tokenStartSvg + 1, false))
                res->setSecureFlag(true);
            else
                LOG_AND_DELETE("Invalid cookie %s (secure)", cookie.ascii().data());
            break;
        }
        case 'H':
        case 'h': {
            // HttpOnly is a standalone token ("HttpOnly;")
            if (length >= 8 && cookie.find("ttpOnly", tokenStartSvg + 1, false))
                res->setIsHttpOnly(true);
            else
                LOG_AND_DELETE("Invalid cookie %s (HttpOnly)", cookie.ascii().data());
            break;
        }

        default : {
            // If length == 0, we should be at the end of the cookie (case : ";\r") so ignore it
            if (length)
                LOG_ERROR("Invalid token for cookie %s", cookie.ascii().data());
        }
        }
    }

    // Check if the cookie is valid with respect to the size limit.
    if (!res->isUnderSizeLimit())
        LOG_AND_DELETE("ParsedCookie %s is above the 4kb in length : REJECTED", cookie.ascii().data());

    // If some pair was not provided, during parsing then apply some default value
    // the rest has been done in the constructor.

    // If no domain was provided, set it to the host
    if (!res->domain())
        res->setDefaultDomain(m_defaultCookieURL);

    // According to the Cookie Specificaiton (RFC6265, section 4.1.2.4 and 5.2.4, http://tools.ietf.org/html/rfc6265),
    // If no path was provided or the first character of the path value is not '/', set it to the host's path
    //
    // REFERENCE
    // 4.1.2.4. The Path Attribute
    //
    // The scope of each cookie is limited to a set of paths, controlled by
    // the Path attribute. If the server omits the Path attribute, the user
    // agent will use the "directory" of the request-uri's path component as
    // the default value. (See Section 5.1.4 for more details.)
    // ...........
    // 5.2.4. The Path Attribute
    //
    // If the attribute-name case-insensitively matches the string "Path",
    // the user agent MUST process the cookie-av as follows.
    //
    // If the attribute-value is empty or if the first character of the
    // attribute-value is not %x2F ("/"):
    //
    // Let cookie-path be the default-path.
    //
    // Otherwise:
    //
    // Let cookie-path be the attribute-value.
    //
    // Append an attribute to the cookie-attribute-list with an attribute-
    // name of Path and an attribute-value of cookie-path.
    if (!res->path() || !res->path().length() || !res->path().startsWith("/", false)) {
        String path = m_defaultCookieURL.string().substring(m_defaultCookieURL.pathStart(), m_defaultCookieURL.pathAfterLastSlash() - m_defaultCookieURL.pathStart() - 1);
        if (path.isEmpty())
            path = "/";
        // Since this is reading the raw url string, it could contain percent-encoded sequences. We
        // want it to be comparable to the return value of url.path(), which is not percent-encoded,
        // so we must remove the escape sequences.
        res->setPath(decodeURLEscapeSequences(path));
    }
 
    return res;
}
void PluginStream::startStream()
{
    ASSERT(m_streamState == StreamBeforeStarted);

    const KURL& responseURL = m_resourceResponse.url();

    // Some plugins (Flash) expect that javascript URLs are passed back decoded as this is the
    // format used when requesting the URL.
    if (protocolIsJavaScript(responseURL))
        m_stream.url = fastStrDup(decodeURLEscapeSequences(responseURL.string()).utf8().data());
    else
        m_stream.url = fastStrDup(responseURL.string().utf8().data());

    CString mimeTypeStr = m_resourceResponse.mimeType().utf8();

    long long expectedContentLength = m_resourceResponse.expectedContentLength();

    if (m_resourceResponse.isHTTP()) {
        Vector<UChar> stringBuilder;
        String separator(": ");

        String statusLine = makeString("HTTP ", String::number(m_resourceResponse.httpStatusCode()), " OK\n");
        stringBuilder.append(statusLine.characters(), statusLine.length());

        HTTPHeaderMap::const_iterator end = m_resourceResponse.httpHeaderFields().end();
        for (HTTPHeaderMap::const_iterator it = m_resourceResponse.httpHeaderFields().begin(); it != end; ++it) {
            stringBuilder.append(it->first.characters(), it->first.length());
            stringBuilder.append(separator.characters(), separator.length());
            stringBuilder.append(it->second.characters(), it->second.length());
            stringBuilder.append('\n');
        }

        m_headers = String::adopt(stringBuilder).utf8();

        // If the content is encoded (most likely compressed), then don't send its length to the plugin,
        // which is only interested in the decoded length, not yet known at the moment.
        // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic.
        String contentEncoding = m_resourceResponse.httpHeaderField("Content-Encoding");
        if (!contentEncoding.isNull() && contentEncoding != "identity")
            expectedContentLength = -1;
    }

    m_stream.headers = m_headers.data();
    m_stream.pdata = 0;
    m_stream.ndata = this;
    m_stream.end = max(expectedContentLength, 0LL);
    m_stream.lastmodified = m_resourceResponse.lastModifiedDate();
    m_stream.notifyData = m_notifyData;

    m_transferMode = NP_NORMAL;
    m_offset = 0;
    m_reason = WebReasonNone;

    // Protect the stream if destroystream is called from within the newstream handler
    RefPtr<PluginStream> protect(this);

    // calling into a plug-in could result in re-entrance if the plug-in yields
    // control to the system (rdar://5744899). prevent this by deferring further
    // loading while calling into the plug-in.
    if (m_loader)
        m_loader->setDefersLoading(true);
    NPError npErr = m_pluginFuncs->newstream(m_instance, (NPMIMEType)mimeTypeStr.data(), &m_stream, false, &m_transferMode);
    if (m_loader)
        m_loader->setDefersLoading(false);
    
    // If the stream was destroyed in the call to newstream we return
    if (m_reason != WebReasonNone)
        return;
        
    if (npErr != NPERR_NO_ERROR) {
        cancelAndDestroyStream(npErr);
        return;
    }

    m_streamState = StreamStarted;

    if (m_transferMode == NP_NORMAL)
        return;

    m_path = openTemporaryFile("WKP", m_tempFileHandle);

    // Something went wrong, cancel loading the stream
    if (!isHandleValid(m_tempFileHandle))
        cancelAndDestroyStream(NPRES_NETWORK_ERR);
}
Esempio n. 21
0
// The cookie String passed into this method will only contian the name value pairs as well as other related cookie
// attributes such as max-age and domain. Set-Cookie should never be part of this string.
PassRefPtr<ParsedCookie> CookieParser::parseOneCookie(const String& cookie, unsigned start, unsigned end, double curTime)
{
    RefPtr<ParsedCookie> res = ParsedCookie::create(curTime);

    if (!res)
        LOG_ERROR_AND_RETURN("Out of memory");

    res->setProtocol(m_defaultCookieURL.protocol());

    // Parse [NAME "="] VALUE
    unsigned tokenEnd = start; // Token end contains the position of the '=' or the end of a token
    unsigned pairEnd = start; // Pair end contains always the position of the ';'

    // Find the first ';' which is not double-quoted and the '=' (if they exist).
    bool foundEqual = false;
    while (pairEnd < end && cookie[pairEnd] != ';') {
        if (cookie[pairEnd] == '=') {
            if (tokenEnd == start) {
                tokenEnd = pairEnd;
                foundEqual = true;
            }
        } else if (cookie[pairEnd] == '"') {
            size_t secondQuotePosition = cookie.find('"', pairEnd + 1);
            if (secondQuotePosition != notFound && secondQuotePosition <= end) {
                pairEnd = secondQuotePosition + 1;
                continue;
            }
        }
        pairEnd++;
    }

    unsigned tokenStart = start;

    bool hasName = false; // This is a hack to avoid changing too much in this brutally brittle code.
    if (tokenEnd != start) {
        // There is a '=' so parse the NAME
        unsigned nameEnd = tokenEnd;

        // The tokenEnd is the position of the '=' so the nameEnd is one less
        nameEnd--;

        // Remove lightweight spaces.
        while (nameEnd && isLightweightSpace(cookie[nameEnd]))
            nameEnd--;

        while (tokenStart < nameEnd && isLightweightSpace(cookie[tokenStart]))
            tokenStart++;

        if (nameEnd + 1 <= tokenStart)
            LOG_ERROR_AND_RETURN("Empty name. Rejecting the cookie");

        String name = cookie.substring(tokenStart, nameEnd + 1 - start);
        res->setName(name);
        hasName = true;
    }

    // Now parse the VALUE
    tokenStart = tokenEnd + 1;
    if (!hasName)
        --tokenStart;

    // Skip lightweight spaces in our token
    while (tokenStart < pairEnd && isLightweightSpace(cookie[tokenStart]))
        tokenStart++;

    tokenEnd = pairEnd;
    while (tokenEnd > tokenStart && isLightweightSpace(cookie[tokenEnd - 1]))
        tokenEnd--;

    String value;
    if (tokenEnd == tokenStart) {
        // Firefox accepts empty value so we will do the same
        value = String();
    } else
        value = cookie.substring(tokenStart, tokenEnd - tokenStart);

    if (hasName)
        res->setValue(value);
    else if (foundEqual)
        return 0;
    else
        res->setName(value); // No NAME=VALUE, only NAME

    while (pairEnd < end) {
        // Switch to the next pair as pairEnd is on the ';' and fast-forward any lightweight spaces.
        pairEnd++;
        while (pairEnd < end && isLightweightSpace(cookie[pairEnd]))
            pairEnd++;

        tokenStart = pairEnd;
        tokenEnd = tokenStart; // initialize token end to catch first '='

        while (pairEnd < end && cookie[pairEnd] != ';') {
            if (tokenEnd == tokenStart && cookie[pairEnd] == '=')
                tokenEnd = pairEnd;
            pairEnd++;
        }

        // FIXME : should we skip lightweight spaces here ?

        unsigned length = tokenEnd - tokenStart;
        unsigned tokenStartSvg = tokenStart;

        String parsedValue;
        if (tokenStart != tokenEnd) {
            // There is an equal sign so remove lightweight spaces in VALUE
            tokenStart = tokenEnd + 1;
            while (tokenStart < pairEnd && isLightweightSpace(cookie[tokenStart]))
                tokenStart++;

            tokenEnd = pairEnd;
            while (tokenEnd > tokenStart && isLightweightSpace(cookie[tokenEnd - 1]))
                tokenEnd--;

            parsedValue = cookie.substring(tokenStart, tokenEnd - tokenStart);
        } else {
            // If the parsedValue is empty, initialise it in case we need it
            parsedValue = String();
            // Handle a token without value.
            length = pairEnd - tokenStart;
        }

        // Detect which "cookie-av" is parsed
        // Look at the first char then parse the whole for performance issue
        switch (cookie[tokenStartSvg]) {
        case 'P':
        case 'p' : {
            if (length >= 4 && ((cookie.find("ath", tokenStartSvg + 1, false) - tokenStartSvg) == 1)) {
                // We need the path to be decoded to match those returned from URL::path().
                // The path attribute may or may not include percent-encoded characters. Fortunately
                // if there are no percent-encoded characters, decoding the url is a no-op.
                res->setPath(decodeURLEscapeSequences(parsedValue));

                // We have to disable the following check because sites like Facebook and
                // Gmail currently do not follow the spec.
#if 0
                // Check if path attribute is a prefix of the request URI.
                if (!m_defaultCookieURL.path().startsWith(res->path()))
                    LOG_ERROR_AND_RETURN("Invalid cookie attribute %s (path): it does not math the URL", cookie.ascii().data());
#endif
            } else
                LOG_ERROR("Invalid cookie attribute %s (path)", cookie.ascii().data());
            break;
        }

        case 'D':
        case 'd' : {
            if (length >= 6 && ((cookie.find("omain", tokenStartSvg + 1, false) - tokenStartSvg) == 1)) {
                if (parsedValue.length() > 1 && parsedValue[0] == '"' && parsedValue[parsedValue.length() - 1] == '"')
                    parsedValue = parsedValue.substring(1, parsedValue.length() - 2);

                // Check if the domain contains an embedded dot.
                size_t dotPosition = parsedValue.find(".", 1);
                if (dotPosition == notFound || dotPosition == parsedValue.length())
                    LOG_ERROR_AND_RETURN("Invalid cookie attribute %s (domain): it does not contain an embedded dot", cookie.ascii().data());

                // If the domain does not start with a dot, add one for security checks and to distinguish it from host-only domains
                // For example: ab.c.com dose not domain match b.c.com;
                StringBuilder parsedValueBuilder;
                if (parsedValue[0] != '.')
                    parsedValueBuilder.appendLiteral(".");
                parsedValueBuilder.append(parsedValue);
                String realDomain = parsedValueBuilder.toString();

                StringBuilder defaultHostBuilder;
                defaultHostBuilder.appendLiteral(".");
                defaultHostBuilder.append(m_defaultCookieHost);
                String defaultHost = defaultHostBuilder.toString();

                // Try to return an canonical ip address if the domain is an ip

                bool isIPAddress = false;
                // We only check if the current domain is an IP address when the default domain is an IP address
                // We know if the default domain is not an IP address and the current domain is, it won't suffix match
                // If it is an IP Address, we should treat it only if it matches the host exactly
                // We determine the canonical IP format before comparing because IPv6 could be represented in multiple formats
                if (m_defaultDomainIsIPAddress) {
                    String realDomainCanonical = BlackBerry::Platform::getCanonicalIPFormat(realDomain);
                    if (realDomainCanonical.isEmpty() || realDomainCanonical != defaultHost)
                        LOG_ERROR_AND_RETURN("Invalid cookie attribute %s (domain): domain is IP but does not match host's IP", cookie.ascii().data());
                    realDomain = realDomainCanonical;
                    isIPAddress = true;
                } else {
                    // The request host should domain match the Domain attribute.
                    // Domain string starts with a dot, so a.b.com should domain match .a.b.com.
                    // add a "." at beginning of host name, because it can handle many cases such as
                    // a.b.com matches b.com, a.b.com matches .B.com and a.b.com matches .A.b.Com
                    // and so on.
                    if (!defaultHost.endsWith(realDomain, false))
                        LOG_ERROR_AND_RETURN("Invalid cookie attribute %s (domain): it does not domain match the host", cookie.ascii().data());

                    // We should check for an embedded dot in the portion of string in the host not in the domain
                    // but to match firefox behaviour we do not.

                    // Check whether the domain is a top level domain, if it is throw it out
                    // http://publicsuffix.org/list/
                    if (BlackBerry::Platform::isTopLevelDomain(realDomain))
                        LOG_ERROR_AND_RETURN("Invalid cookie attribute %s (domain): it did not pass the top level domain check", cookie.ascii().data());
                }
                res->setDomain(realDomain, isIPAddress);
            } else
                LOG_ERROR("Invalid cookie attribute %s (domain)", cookie.ascii().data());
            break;
        }

        case 'E' :
        case 'e' : {
            if (length >= 7 && ((cookie.find("xpires", tokenStartSvg + 1, false) - tokenStartSvg) == 1))
                res->setExpiry(parsedValue);
            else
                LOG_ERROR("Invalid cookie attribute %s (expires)", cookie.ascii().data());
            break;
        }

        case 'M' :
        case 'm' : {
            if (length >= 7 && ((cookie.find("ax-age", tokenStartSvg + 1, false) - tokenStartSvg) == 1))
                res->setMaxAge(parsedValue);
            else
                LOG_ERROR("Invalid cookie attribute %s (max-age)", cookie.ascii().data());
            break;
        }

        case 'C' :
        case 'c' : {
            if (length >= 7 && ((cookie.find("omment", tokenStartSvg + 1, false) - tokenStartSvg) == 1))
                // We do not have room for the comment part (and so do Mozilla) so just log the comment.
                LOG(Network, "Comment %s for ParsedCookie : %s\n", parsedValue.ascii().data(), cookie.ascii().data());
            else
                LOG_ERROR("Invalid cookie attribute %s (comment)", cookie.ascii().data());
            break;
        }

        case 'V' :
        case 'v' : {
            if (length >= 7 && ((cookie.find("ersion", tokenStartSvg + 1, false) - tokenStartSvg) == 1)) {
                // Although the out-of-dated Cookie Spec(RFC2965, http://tools.ietf.org/html/rfc2965) defined
                // the value of version can only contain DIGIT, some random sites, e.g. https://devforums.apple.com
                // would use double quotation marks to quote the digit. So we need to get rid of them for compliance.
                if (parsedValue.length() > 1 && parsedValue[0] == '"' && parsedValue[parsedValue.length() - 1] == '"')
                    parsedValue = parsedValue.substring(1, parsedValue.length() - 2);

                if (parsedValue.toInt() != 1)
                    LOG_ERROR_AND_RETURN("ParsedCookie version %d not supported (only support version=1)", parsedValue.toInt());
            } else
                LOG_ERROR("Invalid cookie attribute %s (version)", cookie.ascii().data());
            break;
        }

        case 'S' :
        case 's' : {
            // Secure is a standalone token ("Secure;")
            if (length >= 6 && ((cookie.find("ecure", tokenStartSvg + 1, false) - tokenStartSvg) == 1))
                res->setSecureFlag(true);
            else
                LOG_ERROR("Invalid cookie attribute %s (secure)", cookie.ascii().data());
            break;
        }
        case 'H':
        case 'h': {
            // HttpOnly is a standalone token ("HttpOnly;")
            if (length >= 8 && ((cookie.find("ttpOnly", tokenStartSvg + 1, false) - tokenStartSvg) == 1))
                res->setIsHttpOnly(true);
            else
                LOG_ERROR("Invalid cookie attribute %s (HttpOnly)", cookie.ascii().data());
            break;
        }

        default : {
            // If length == 0, we should be at the end of the cookie (case : ";\r") so ignore it
            if (length)
                LOG_ERROR("Invalid token for cookie %s", cookie.ascii().data());
        }
        }
    }

    // Check if the cookie is valid with respect to the size limit.
    if (!res->isUnderSizeLimit())
        LOG_ERROR_AND_RETURN("ParsedCookie %s is above the 4kb in length : REJECTED", cookie.ascii().data());

    // If some pair was not provided, during parsing then apply some default value
    // the rest has been done in the constructor.

    // If no domain was provided, set it to the host
    if (!res->domain())
        res->setDomain(m_defaultCookieHost, m_defaultDomainIsIPAddress);

    // According to the Cookie Specificaiton (RFC6265, section 4.1.2.4 and 5.2.4, http://tools.ietf.org/html/rfc6265),
    // If no path was provided or the first character of the path value is not '/', set it to the host's path
    //
    // REFERENCE
    // 4.1.2.4. The Path Attribute
    //
    // The scope of each cookie is limited to a set of paths, controlled by
    // the Path attribute. If the server omits the Path attribute, the user
    // agent will use the "directory" of the request-uri's path component as
    // the default value. (See Section 5.1.4 for more details.)
    // ...........
    // 5.2.4. The Path Attribute
    //
    // If the attribute-name case-insensitively matches the string "Path",
    // the user agent MUST process the cookie-av as follows.
    //
    // If the attribute-value is empty or if the first character of the
    // attribute-value is not %x2F ("/"):
    //
    // Let cookie-path be the default-path.
    //
    // Otherwise:
    //
    // Let cookie-path be the attribute-value.
    //
    // Append an attribute to the cookie-attribute-list with an attribute-
    // name of Path and an attribute-value of cookie-path.
    if (!res->path() || !res->path().length() || !res->path().startsWith("/", false)) {
        String path = m_defaultCookieURL.string().substring(m_defaultCookieURL.pathStart(), m_defaultCookieURL.pathAfterLastSlash() - m_defaultCookieURL.pathStart() - 1);
        if (path.isEmpty())
            path = "/";
        // Since this is reading the raw url string, it could contain percent-encoded sequences. We
        // want it to be comparable to the return value of url.path(), which is not percent-encoded,
        // so we must remove the escape sequences.
        res->setPath(decodeURLEscapeSequences(path));
    }

    return res;
}
Esempio n. 22
0
// In general, extracting the inner URL varies by scheme. It just so happens
// that all the URL schemes we currently support that use inner URLs for their
// security origin can be parsed using this algorithm.
static KURL extractInnerURL(const KURL& url)
{
    // FIXME: Update this callsite to use the innerURL member function when
    // we finish implementing it.
    return KURL(ParsedURLString, decodeURLEscapeSequences(url.path()));
}
Esempio n. 23
0
// parseDataUrl() is taken from the CURL http backend.
static gboolean parseDataUrl(gpointer callback_data)
{
    ResourceHandle* handle = static_cast<ResourceHandle*>(callback_data);
    ResourceHandleClient* client = handle->client();
    ResourceHandleInternal* d = handle->getInternal();
    if (d->m_cancelled)
        return false;

    d->m_idleHandler = 0;

    ASSERT(client);
    if (!client)
        return false;

    String url = handle->request().url().string();
    ASSERT(url.startsWith("data:", false));

    int index = url.find(',');
    if (index == -1) {
        client->cannotShowURL(handle);
        return false;
    }

    String mediaType = url.substring(5, index - 5);
    String data = url.substring(index + 1);

    bool isBase64 = mediaType.endsWith(";base64", false);
    if (isBase64)
        mediaType = mediaType.left(mediaType.length() - 7);

    if (mediaType.isEmpty())
        mediaType = "text/plain;charset=US-ASCII";

    String mimeType = extractMIMETypeFromMediaType(mediaType);
    String charset = extractCharsetFromMediaType(mediaType);

    ResourceResponse response;
    response.setMimeType(mimeType);

    if (isBase64) {
        data = decodeURLEscapeSequences(data);
        response.setTextEncodingName(charset);
        client->didReceiveResponse(handle, response);

        if (d->m_cancelled)
            return false;

        // Use the GLib Base64, since WebCore's decoder isn't
        // general-purpose and fails on Acid3 test 97 (whitespace).
        size_t outLength = 0;
        char* outData = 0;
        outData = reinterpret_cast<char*>(g_base64_decode(data.utf8().data(), &outLength));
        if (outData && outLength > 0)
            client->didReceiveData(handle, outData, outLength, 0);
        g_free(outData);
    } else {
        // We have to convert to UTF-16 early due to limitations in KURL
        data = decodeURLEscapeSequences(data, TextEncoding(charset));
        response.setTextEncodingName("UTF-16");
        client->didReceiveResponse(handle, response);

        if (d->m_cancelled)
            return false;

        if (data.length() > 0)
            client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0);

        if (d->m_cancelled)
            return false;
    }

    client->didFinishLoading(handle);

    return false;
}
Esempio n. 24
0
// FIXME: This function does not deal properly with text encodings.
static void parseDataUrl(ResourceHandle* handle)
{
    String data = handle->request().url().string();

    ASSERT(data.startsWith("data:", false));

    String header;
    bool base64 = false;

    int index = data.find(',');
    if (index != -1) {
        header = data.substring(5, index - 5).lower();
        data = data.substring(index + 1);

        if (header.endsWith(";base64")) {
            base64 = true;
            header = header.left(header.length() - 7);
        }
    } else
        data = String();

    data = decodeURLEscapeSequences(data);

    size_t outLength = 0;
    char* outData = 0;
    if (base64 && !data.isEmpty()) {
        // Use the GLib Base64 if available, since WebCore's decoder isn't
        // general-purpose and fails on Acid3 test 97 (whitespace).
#ifdef USE_GLIB_BASE64
        outData = reinterpret_cast<char*>(g_base64_decode(data.utf8().data(), &outLength));
#else
        Vector<char> out;
        if (base64Decode(data.latin1().data(), data.length(), out))
            data = String(out.data(), out.size());
        else
            data = String();
#endif
    }

    if (header.isEmpty())
        header = "text/plain;charset=US-ASCII";

    ResourceHandleClient* client = handle->getInternal()->client();

    ResourceResponse response;

    response.setMimeType(extractMIMETypeFromMediaType(header));
    response.setTextEncodingName(extractCharsetFromMediaType(header));
    if (outData)
        response.setExpectedContentLength(outLength);
    else
        response.setExpectedContentLength(data.length());
    response.setHTTPStatusCode(200);

    client->didReceiveResponse(handle, response);

    if (outData)
        client->didReceiveData(handle, outData, outLength, 0);
    else
        client->didReceiveData(handle, data.latin1().data(), data.length(), 0);

#ifdef USE_GLIB_BASE64
    g_free(outData);
#endif

    client->didFinishLoading(handle);
}
Esempio n. 25
0
static bool isMainInspectorPage(const WebInspectorProxy* webInspectorProxy, WKURLRequestRef requestRef)
{
    // Use URL so we can compare just the paths.
    URL inspectorURL(URL(), webInspectorProxy->inspectorPageURL());
    URL requestURL(URL(), toImpl(requestRef)->resourceRequest().url());

    ASSERT(WebCore::SchemeRegistry::shouldTreatURLSchemeAsLocal(inspectorURL.protocol()));

    return WebCore::SchemeRegistry::shouldTreatURLSchemeAsLocal(requestURL.protocol()) && decodeURLEscapeSequences(requestURL.path()) == decodeURLEscapeSequences(inspectorURL.path());
}
Esempio n. 26
0
String decodeURLEscapeSequences(const String& str)
{
    return decodeURLEscapeSequences(str, UTF8Encoding());
}