blob: b9626e8820b7a2841bef977cbf59ccf0b2a375d9 [file] [log] [blame]
package android.webkit;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* Robolectric implementation of {@link android.webkit.CookieManager}.
*
* <p>Basic implementation which does not fully implement RFC2109.
*/
public class RoboCookieManager extends CookieManager {
private static final String HTTP = "http://";
private static final String HTTPS = "https://";
private static final String EXPIRATION_FIELD_NAME = "Expires";
private static final String SECURE_ATTR_NAME = "SECURE";
private final List<Cookie> store = new ArrayList<>();
private boolean accept;
@Override
public void setCookie(String url, String value) {
Cookie cookie = parseCookie(url, value);
if (cookie != null) {
Cookie existingCookie = null;
for (Cookie c : store) {
if (c == null) {
continue;
}
if (Objects.equals(c.getName(), cookie.getName())
&& Objects.equals(c.mHostname, cookie.mHostname)) {
existingCookie = c;
break;
}
}
if (existingCookie != null) {
store.remove(existingCookie);
}
store.add(cookie);
}
}
@Override
public void setCookie(String url, String value, @Nullable ValueCallback<Boolean> valueCallback) {
setCookie(url, value);
if (valueCallback != null) {
valueCallback.onReceiveValue(true);
}
}
@Override
public void setAcceptThirdPartyCookies(WebView webView, boolean b) {}
@Override
public boolean acceptThirdPartyCookies(WebView webView) {
return false;
}
@Override
public void removeAllCookies(@Nullable ValueCallback<Boolean> valueCallback) {
store.clear();
if (valueCallback != null) {
valueCallback.onReceiveValue(Boolean.TRUE);
}
}
@Override
public void flush() {}
@Override
public void removeSessionCookies(@Nullable ValueCallback<Boolean> valueCallback) {
boolean value;
synchronized (store) {
value = clearAndAddPersistentCookies();
}
if (valueCallback != null) {
valueCallback.onReceiveValue(value);
}
}
@Override
public String getCookie(String url) {
// Return null value for empty url
if (url == null || url.equals("")) {
return null;
}
try {
url = URLDecoder.decode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
final List<Cookie> matchedCookies;
if (url.startsWith(".")) {
matchedCookies = filter(url.substring(1));
} else if (url.contains("//.")) {
matchedCookies = filter(url.substring(url.indexOf("//.") + 3));
} else {
matchedCookies = filter(getCookieHost(url), url.startsWith(HTTPS));
}
if (matchedCookies.isEmpty()) {
return null;
}
StringBuilder cookieHeaderValue = new StringBuilder();
for (int i = 0, n = matchedCookies.size(); i < n; i++) {
Cookie cookie = matchedCookies.get(i);
if (i > 0) {
cookieHeaderValue.append("; ");
}
cookieHeaderValue.append(cookie.getName());
String value = cookie.getValue();
if (value != null) {
cookieHeaderValue.append("=");
cookieHeaderValue.append(value);
}
}
return cookieHeaderValue.toString();
}
@Override
public String getCookie(String s, boolean b) {
return null;
}
private List<Cookie> filter(String domain) {
return filter(domain, false);
}
private List<Cookie> filter(String domain, boolean isSecure) {
List<Cookie> matchedCookies = new ArrayList<>();
for (Cookie cookie : store) {
if (cookie.isSameHost(domain) && (isSecure == cookie.isSecure() || isSecure)) {
matchedCookies.add(cookie);
}
}
return matchedCookies;
}
@Override
public void setAcceptCookie(boolean accept) {
this.accept = accept;
}
@Override
public boolean acceptCookie() {
return this.accept;
}
public void removeAllCookie() {
store.clear();
}
public void removeExpiredCookie() {
List<Cookie> expired = new ArrayList<>();
Date now = new Date();
for (Cookie cookie : store) {
if (cookie.isExpiredAt(now)) {
expired.add(cookie);
}
}
store.removeAll(expired);
}
@Override
public boolean hasCookies() {
return !store.isEmpty();
}
@Override
public boolean hasCookies(boolean b) {
return false;
}
public void removeSessionCookie() {
synchronized (store) {
clearAndAddPersistentCookies();
}
}
@Override
protected boolean allowFileSchemeCookiesImpl() {
return false;
}
@Override
protected void setAcceptFileSchemeCookiesImpl(boolean b) {}
private boolean clearAndAddPersistentCookies() {
List<Cookie> existing = new ArrayList<>(store);
int length = store.size();
store.clear();
for (Cookie cookie : existing) {
if (cookie.isPersistent()) {
store.add(cookie);
}
}
return store.size() < length;
}
@Nullable
private static Cookie parseCookie(String url, String cookieHeader) {
Date expiration = null;
boolean isSecure = false;
String[] fields = cookieHeader.split(";", 0);
String cookieValue = fields[0].trim();
for (int i = 1; i < fields.length; i++) {
String field = fields[i].trim();
if (field.startsWith(EXPIRATION_FIELD_NAME)) {
expiration = getExpiration(field);
} else if (field.equalsIgnoreCase(SECURE_ATTR_NAME)) {
isSecure = true;
}
}
String hostname = getCookieHost(url);
if (expiration == null || expiration.compareTo(new Date()) >= 0) {
return new Cookie(hostname, isSecure, cookieValue, expiration);
}
return null;
}
private static String getCookieHost(String url) {
if (!(url.startsWith(HTTP) || url.startsWith(HTTPS))) {
url = HTTP + url;
}
try {
return new URI(url).getHost();
} catch (URISyntaxException e) {
throw new IllegalArgumentException("wrong URL : " + url, e);
}
}
private static Date getExpiration(String field) {
int equalsIndex = field.indexOf("=");
if (equalsIndex < 0) {
return null;
}
String date = field.substring(equalsIndex + 1);
try {
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
return dateFormat.parse(date);
} catch (ParseException e) {
// No-op. Try to inferFromValue additional date formats.
}
try {
DateFormat dateFormat = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss zzz");
return dateFormat.parse(date);
} catch (ParseException e) {
return null; // Was not parsed by any date formatter.
}
}
private static class Cookie {
private final String mName;
private final String mValue;
private final Date mExpiration;
private final String mHostname;
private final boolean mIsSecure;
public Cookie(String hostname, boolean isSecure, String cookie, Date expiration) {
mHostname = hostname;
mIsSecure = isSecure;
mExpiration = expiration;
int equalsIndex = cookie.indexOf("=");
if (equalsIndex >= 0) {
mName = cookie.substring(0, equalsIndex);
mValue = cookie.substring(equalsIndex + 1);
} else {
mName = cookie;
mValue = null;
}
}
public String getName() {
return mName;
}
public String getValue() {
return mValue;
}
public boolean isExpiredAt(Date date) {
return mExpiration != null && mExpiration.compareTo(date) < 0;
}
public boolean isPersistent() {
return mExpiration != null;
}
public boolean isSameHost(String host) {
return mHostname.endsWith(host);
}
public boolean isSecure() {
return mIsSecure;
}
}
}