ParsedCookie.cpp   [plain text]


/*
 * Copyright (C) 2008, 2009 Julien Chaffraix <julien.chaffraix@gmail.com>
 * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "ParsedCookie.h"

#include "CookieManager.h"
#include <wtf/CurrentTime.h>
#include "KURL.h"
#include "Logging.h"
#include <curl/curl.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>

namespace WebCore {

ParsedCookie::ParsedCookie(double currentTime)
    : m_expiry(-1)
    , m_creationTime(currentTime)
    , m_lastAccessed(currentTime)
    , m_isSecure(false)
    , m_isHttpOnly(false)
    , m_isSession(true)
    , m_isForceExpired(false)
{
}

ParsedCookie::ParsedCookie(const String& name, const String& value, const String& domain, const String& protocol, const String& path, double expiry, double lastAccessed, double creationTime, bool isSecure, bool isHttpOnly)
    : m_name(name)
    , m_value(value)
    , m_domain(domain)
    , m_protocol(protocol)
    , m_path(path)
    , m_expiry(expiry)
    , m_creationTime(creationTime)
    , m_lastAccessed(lastAccessed)
    , m_isSecure(isSecure)
    , m_isHttpOnly(isHttpOnly)
    , m_isSession(false)
    , m_isForceExpired(false)
{
}

ParsedCookie::ParsedCookie(const ParsedCookie* cookie)
    : m_name(String(cookie->m_name))
    , m_value(String(cookie->m_value))
    , m_domain(String(cookie->m_domain))
    , m_protocol(String(cookie->m_protocol))
    , m_path(String(cookie->m_path))
    , m_expiry(cookie->m_expiry)
    , m_creationTime(cookie->m_creationTime)
    , m_lastAccessed(cookie->m_lastAccessed)
    , m_isSecure(cookie->m_isSecure)
    , m_isHttpOnly(cookie->m_isHttpOnly)
    , m_isSession(cookie->m_isSession)
    , m_isForceExpired(cookie->m_isForceExpired)
{
}

ParsedCookie::~ParsedCookie()
{
}

void ParsedCookie::setExpiry(const String& expiry)
{
    // If a cookie has both the Max-Age and the Expires attribute,
    // the Max-Age attribute has precedence and controls the expiration date of the cookie.
    if (m_expiry != -1 || expiry.isEmpty())
        return;

    m_isSession = false;

    m_expiry = static_cast<double>(curl_getdate(expiry.utf8().data(), 0));

    if (m_expiry == -1) {
        LOG_ERROR("Could not parse date");
        // In this case, consider that the cookie is session only
        m_isSession = true;
    }
}

void ParsedCookie::setMaxAge(const String& maxAge)
{
    // According to the HTTP Cookie specification (RFC6265, http://tools.ietf.org/html/rfc6265),
    // the first character can be a DIGIT or a "-", and the reminder
    // of the value can only contain DIGIT characters.
    if (maxAge.isEmpty() || (maxAge[0] != '-' && !isASCIIDigit(maxAge[0]))) {
        LOG_ERROR("Could not parse Max-Age : %s, first character can only be '-' or ascii digit.", maxAge.ascii().data());
        return;
    }

    bool ok;
    int value = maxAge.toIntStrict(&ok);

    if (!ok) {
        LOG_ERROR("Could not parse Max-Age : %s", maxAge.ascii().data());
        return;
    }
    m_expiry = value;
    m_isSession = false;

    // If maxAge value is not positive, let expiry-time be the earliest representable time.
    if (m_expiry > 0)
        m_expiry += currentTime();
    else
        m_expiry = 0;
}

bool ParsedCookie::hasExpired() const
{
    // Session cookies do not expire, they will just not be saved to the backing store.
    return !m_isSession && (m_isForceExpired || m_expiry < currentTime());
}

bool ParsedCookie::isUnderSizeLimit() const
{
    return m_value.length() <= CookieManager::maxCookieLength() && m_name.length() <= CookieManager::maxCookieLength();
}

String ParsedCookie::toString() const
{
    StringBuilder builder;
    builder.append(name());
    builder.append(" = ");
    builder.append(value());
    builder.append("; Domain = ");
    builder.append(domain());
    builder.append("; Path = ");
    builder.append(path());
    builder.append("; Protocol = ");
    builder.append(protocol());
    return builder.toString();
}

String ParsedCookie::toNameValuePair() const
{
    static const String equal("=");

    size_t cookieLength = m_name.length() + m_value.length() + 2;
    Vector<UChar> result;
    result.reserveInitialCapacity(cookieLength);
    append(result, m_name);
    append(result, equal);
    append(result, m_value);

    return String::adopt(result);
}

void ParsedCookie::appendWebCoreCookie(Vector<Cookie>& cookieVector) const
{
    cookieVector.append(Cookie(String(m_name), String(m_value), String(m_domain),
            // We multiply m_expiry by 1000 to convert from seconds to milliseconds.
            // This value is passed to Web Inspector and used in the JavaScript Date constructor.
            String(m_path), (m_expiry * 1000), m_isHttpOnly, m_isSecure, m_isSession));
}
} // namespace WebCore