1/*
2 * Copyright (C) 2008, 2009 Julien Chaffraix <julien.chaffraix@gmail.com>
3 * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "ParsedCookie.h"
29
30#include "CookieManager.h"
31#include "KURL.h"
32#include "Logging.h"
33#include <curl/curl.h>
34#include <wtf/CurrentTime.h>
35#include <wtf/text/CString.h>
36#include <wtf/text/StringBuilder.h>
37
38namespace WebCore {
39
40ParsedCookie::ParsedCookie(double currentTime)
41    : m_expiry(-1)
42    , m_creationTime(currentTime)
43    , m_lastAccessed(currentTime)
44    , m_isSecure(false)
45    , m_isHttpOnly(false)
46    , m_isSession(true)
47    , m_isForceExpired(false)
48    , m_domainIsIPAddress(false)
49{
50}
51
52ParsedCookie::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)
53    : m_name(name)
54    , m_value(value)
55    , m_domain(domain)
56    , m_protocol(protocol)
57    , m_path(path)
58    , m_expiry(expiry)
59    , m_creationTime(creationTime)
60    , m_lastAccessed(lastAccessed)
61    , m_isSecure(isSecure)
62    , m_isHttpOnly(isHttpOnly)
63    , m_isSession(false)
64    , m_isForceExpired(false)
65    , m_domainIsIPAddress(false)
66{
67}
68
69void ParsedCookie::setExpiry(const String& expiry)
70{
71    // If a cookie has both the Max-Age and the Expires attribute,
72    // the Max-Age attribute has precedence and controls the expiration date of the cookie.
73    if (m_expiry != -1 || expiry.isEmpty())
74        return;
75
76    m_isSession = false;
77
78    m_expiry = static_cast<double>(curl_getdate(expiry.utf8().data(), 0));
79
80    if (m_expiry == -1) {
81        LOG_ERROR("Could not parse date");
82        // In this case, consider that the cookie is session only
83        m_isSession = true;
84    }
85}
86
87void ParsedCookie::setMaxAge(const String& maxAge)
88{
89    // According to the HTTP Cookie specification (RFC6265, http://tools.ietf.org/html/rfc6265),
90    // the first character can be a DIGIT or a "-", and the reminder
91    // of the value can only contain DIGIT characters.
92    if (maxAge.isEmpty() || (maxAge[0] != '-' && !isASCIIDigit(maxAge[0]))) {
93        LOG_ERROR("Could not parse Max-Age : %s, first character can only be '-' or ascii digit.", maxAge.ascii().data());
94        return;
95    }
96
97    bool ok;
98    int value = maxAge.toIntStrict(&ok);
99
100    if (!ok) {
101        LOG_ERROR("Could not parse Max-Age : %s", maxAge.ascii().data());
102        return;
103    }
104    m_expiry = value;
105    m_isSession = false;
106
107    // If maxAge value is not positive, let expiry-time be the earliest representable time.
108    if (m_expiry > 0)
109        m_expiry += currentTime();
110    else
111        m_expiry = 0;
112}
113
114bool ParsedCookie::hasExpired() const
115{
116    // Session cookies do not expire, they will just not be saved to the backing store.
117    return !m_isSession && (m_isForceExpired || m_expiry < currentTime());
118}
119
120bool ParsedCookie::isUnderSizeLimit() const
121{
122    return m_value.length() <= CookieManager::maxCookieLength() && m_name.length() <= CookieManager::maxCookieLength();
123}
124
125String ParsedCookie::toString() const
126{
127    StringBuilder builder;
128    builder.append(name());
129    builder.append(" = ");
130    builder.append(value());
131    builder.append("; Domain = ");
132    builder.append(domain());
133    builder.append("; Path = ");
134    builder.append(path());
135    builder.append("; Protocol = ");
136    builder.append(protocol());
137    return builder.toString();
138}
139
140String ParsedCookie::toNameValuePair() const
141{
142    static const String equal("=");
143
144    size_t cookieLength = m_name.length() + m_value.length() + 2;
145    Vector<UChar> result;
146    result.reserveInitialCapacity(cookieLength);
147    append(result, m_name);
148    append(result, equal);
149    append(result, m_value);
150
151    return String::adopt(result);
152}
153
154void ParsedCookie::appendWebCoreCookie(Vector<Cookie>& cookieVector) const
155{
156    cookieVector.append(Cookie(String(m_name), String(m_value), String(m_domain),
157        // We multiply m_expiry by 1000 to convert from seconds to milliseconds.
158        // This value is passed to Web Inspector and used in the JavaScript Date constructor.
159        String(m_path), (m_expiry * 1000), m_isHttpOnly, m_isSecure, m_isSession));
160}
161} // namespace WebCore
162