blob: 7cb1b7884e9a11e41fe8eb1948ba4292d488d3c0 [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/webdriver/protocol/cookie.h"
#include "base/string_split.h"
#include "base/stringprintf.h"
namespace cobalt {
namespace webdriver {
namespace protocol {
namespace {
// The key for the JSON Cookie object in the parameters.
const char kCookieKey[] = "cookie";
// JSON Cookie object keys.
const char kNameKey[] = "name";
const char kValueKey[] = "value";
const char kDomainKey[] = "domain";
const char kPathKey[] = "path";
const char kSecureKey[] = "secure";
const char kHttpOnlyKey[] = "httpOnly";
const char kExpiryKey[] = "expiry";
} // namespace
scoped_ptr<base::Value> Cookie::ToValue(const Cookie& cookie) {
scoped_ptr<base::DictionaryValue> cookie_value(new base::DictionaryValue());
cookie_value->SetString(kNameKey, cookie.name_);
cookie_value->SetString(kValueKey, cookie.value_);
return cookie_value.PassAs<base::Value>();
}
base::optional<Cookie> Cookie::FromValue(const base::Value* value) {
// TODO: Malformed data should return an "unable to set cookie"
// error, but the current implementation will return "invalid parameter".
const base::DictionaryValue* dictionary_value;
if (!value->GetAsDictionary(&dictionary_value)) {
DLOG(INFO) << "Parameter is not a dictionary.";
return base::nullopt;
}
const base::DictionaryValue* cookie_dictionary_value;
if (!dictionary_value->GetDictionary(kCookieKey, &cookie_dictionary_value)) {
DLOG(INFO) << StringPrintf("Value of key [%s] is not a JSON object.",
kCookieKey);
return base::nullopt;
}
std::string cookie_name;
std::string cookie_value;
// Name and value are required.
if (!cookie_dictionary_value->GetString(kNameKey, &cookie_name) ||
!cookie_dictionary_value->GetString(kValueKey, &cookie_value)) {
DLOG(INFO) << StringPrintf(
"cookie.%s or cookie.%s either does not exist or is not a string",
kNameKey, kValueKey);
return base::nullopt;
}
Cookie new_cookie(cookie_name, cookie_value);
std::string string_value;
if (cookie_dictionary_value->HasKey(kDomainKey)) {
if (cookie_dictionary_value->GetString(kDomainKey, &string_value)) {
new_cookie.domain_ = string_value;
} else {
DLOG(INFO) << StringPrintf("cookie.%s is not a string", kDomainKey);
return base::nullopt;
}
}
if (cookie_dictionary_value->HasKey(kPathKey)) {
if (cookie_dictionary_value->GetString(kPathKey, &string_value)) {
new_cookie.path_ = string_value;
} else {
DLOG(INFO) << StringPrintf("cookie.%s is not a string", kPathKey);
return base::nullopt;
}
}
bool bool_value = false;
if (cookie_dictionary_value->HasKey(kSecureKey)) {
if (cookie_dictionary_value->GetBoolean(kSecureKey, &bool_value)) {
new_cookie.secure_ = bool_value;
} else {
DLOG(INFO) << StringPrintf("cookie.%s is not a boolean", kSecureKey);
return base::nullopt;
}
}
if (cookie_dictionary_value->HasKey(kHttpOnlyKey)) {
if (cookie_dictionary_value->GetBoolean(kHttpOnlyKey, &bool_value)) {
new_cookie.http_only_ = bool_value;
} else {
DLOG(INFO) << StringPrintf("cookie.%s is not a boolean", kHttpOnlyKey);
return base::nullopt;
}
}
int timestamp_value = 0;
if (cookie_dictionary_value->HasKey(kExpiryKey)) {
if (cookie_dictionary_value->GetInteger(kExpiryKey, &timestamp_value)) {
base::TimeDelta seconds_since_epoch =
base::TimeDelta::FromSeconds(timestamp_value);
new_cookie.expiry_time_ = base::Time::UnixEpoch() + seconds_since_epoch;
} else {
DLOG(INFO) << StringPrintf("cookie.%s is not an integer", kExpiryKey);
return base::nullopt;
}
}
return new_cookie;
}
void Cookie::ToCookieVector(const std::string& cookies_string,
std::vector<Cookie>* cookies) {
std::vector<std::string> cookie_strings;
base::SplitString(cookies_string, ';', &cookie_strings);
for (size_t i = 0; i < cookie_strings.size(); ++i) {
base::optional<Cookie> cookie = FromString(cookie_strings[i]);
if (cookie) {
cookies->push_back(*cookie);
}
}
}
base::optional<Cookie> Cookie::FromString(const std::string& cookie_as_string) {
size_t pos = cookie_as_string.find('=');
if (pos == std::string::npos) {
return base::nullopt;
}
DCHECK(pos < cookie_as_string.size());
return Cookie(cookie_as_string.substr(0, pos),
cookie_as_string.substr(pos + 1));
}
std::string Cookie::ToCookieString(const std::string& current_domain) const {
base::Time expiry_time;
if (expiry_time_) {
expiry_time = expiry_time_.value();
} else {
// If expiry was not set by the client, then the default is 20 years from
// now.
base::Time::Exploded exploded_now;
base::Time::Now().UTCExplode(&exploded_now);
exploded_now.year += 20;
expiry_time = base::Time::FromUTCExploded(exploded_now);
}
// WebDriver protocol defines expiry as seconds since the Unix Epoch, but
// the Max-Age attribute defines it as seconds from right now.
base::TimeDelta max_age = expiry_time - base::Time::Now();
std::string cookie_string =
StringPrintf("%s=%s; Path=%s; Domain=%s; Max-Age=%d%s%s", name_.c_str(),
value_.c_str(), path_.value_or("/").c_str(),
domain_.value_or(current_domain).c_str(),
static_cast<int>(max_age.InSeconds()),
secure_.value_or(false) ? " ;Secure" : "",
http_only_.value_or(false) ? " ;HttpOnly" : "");
return cookie_string;
}
} // namespace protocol
} // namespace webdriver
} // namespace cobalt