1 package android.webkit; 2 3 import java.io.UnsupportedEncodingException; 4 import java.net.URI; 5 import java.net.URISyntaxException; 6 import java.net.URLDecoder; 7 import java.text.DateFormat; 8 import java.text.ParseException; 9 import java.text.SimpleDateFormat; 10 import java.util.ArrayList; 11 import java.util.Date; 12 import java.util.List; 13 14 /** 15 * Robolectric implementation of {@link android.webkit.CookieManager}. 16 * 17 * Basic implementation which does not fully implement RFC2109. 18 */ 19 public class RoboCookieManager extends CookieManager { 20 private static final String HTTP = "http://"; 21 private static final String HTTPS = "https://"; 22 private static final String EXPIRATION_FIELD_NAME = "Expires"; 23 private static final String SECURE_ATTR_NAME = "SECURE"; 24 private final List<Cookie> store = new ArrayList<>(); 25 private boolean accept; 26 setCookie(String url, String value)27 @Override public void setCookie(String url, String value) { 28 List<Cookie> cookies = parseCookies(url, value); 29 for (Cookie cookie : cookies) { 30 store.add(cookie); 31 } 32 } 33 34 @Override setCookie(String s, String s1, ValueCallback<Boolean> valueCallback)35 public void setCookie(String s, String s1, ValueCallback<Boolean> valueCallback) { 36 37 } 38 39 @Override setAcceptThirdPartyCookies(WebView webView, boolean b)40 public void setAcceptThirdPartyCookies(WebView webView, boolean b) { 41 42 } 43 44 @Override acceptThirdPartyCookies(WebView webView)45 public boolean acceptThirdPartyCookies(WebView webView) { 46 return false; 47 } 48 49 @Override removeAllCookies(ValueCallback<Boolean> valueCallback)50 public void removeAllCookies(ValueCallback<Boolean> valueCallback) { 51 store.clear(); 52 } 53 54 @Override flush()55 public void flush() { 56 57 } 58 59 @Override removeSessionCookies(ValueCallback<Boolean> valueCallback)60 public void removeSessionCookies(ValueCallback<Boolean> valueCallback) { 61 62 } 63 getCookie(String url)64 @Override public String getCookie(String url) { 65 // Return null value for empty url 66 if (url == null || url.equals("")) { 67 return null; 68 } 69 70 try { 71 url = URLDecoder.decode(url, "UTF-8"); 72 } catch (UnsupportedEncodingException e) { 73 throw new RuntimeException(e); 74 } 75 76 final List<Cookie> matchedCookies; 77 if (url.startsWith(".")) { 78 matchedCookies = filter(url.substring(1)); 79 } else if (url.contains("//.")) { 80 matchedCookies = filter(url.substring(url.indexOf("//.") + 3)); 81 } else { 82 matchedCookies = filter(getCookieHost(url), url.startsWith(HTTPS)); 83 } 84 if (matchedCookies.isEmpty()) { 85 return null; 86 } 87 88 StringBuilder cookieHeaderValue = new StringBuilder(); 89 for (int i = 0, n = matchedCookies.size(); i < n; i++) { 90 Cookie cookie = matchedCookies.get(i); 91 92 if (i > 0) { 93 cookieHeaderValue.append("; "); 94 } 95 cookieHeaderValue.append(cookie.getName()); 96 String value = cookie.getValue(); 97 if (value != null) { 98 cookieHeaderValue.append("="); 99 cookieHeaderValue.append(value); 100 } 101 } 102 103 return cookieHeaderValue.toString(); 104 } 105 106 @Override getCookie(String s, boolean b)107 public String getCookie(String s, boolean b) { 108 return null; 109 } 110 filter(String domain)111 private List<Cookie> filter(String domain) { 112 return filter(domain, false); 113 } 114 filter(String domain, boolean isSecure)115 private List<Cookie> filter(String domain, boolean isSecure) { 116 List<Cookie> matchedCookies = new ArrayList<>(); 117 Date now = new Date(); 118 for (Cookie cookie : store) { 119 if (cookie.isSameHost(domain) 120 && (isSecure == cookie.isSecure() || isSecure)) { 121 matchedCookies.add(cookie); 122 } 123 } 124 return matchedCookies; 125 } 126 setAcceptCookie(boolean accept)127 @Override public void setAcceptCookie(boolean accept) { 128 this.accept = accept; 129 } 130 acceptCookie()131 @Override public boolean acceptCookie() { 132 return this.accept; 133 } 134 removeAllCookie()135 public void removeAllCookie() { 136 store.clear(); 137 } 138 removeExpiredCookie()139 public void removeExpiredCookie() { 140 List<Cookie> expired = new ArrayList<>(); 141 Date now = new Date(); 142 143 for (Cookie cookie : store) { 144 if (cookie.isExpiredAt(now)) { 145 expired.add(cookie); 146 } 147 } 148 149 store.removeAll(expired); 150 } 151 hasCookies()152 @Override public boolean hasCookies() { 153 return !store.isEmpty(); 154 } 155 156 @Override hasCookies(boolean b)157 public boolean hasCookies(boolean b) { 158 return false; 159 } 160 removeSessionCookie()161 public void removeSessionCookie() { 162 synchronized (store) { 163 clearAndAddPersistentCookies(); 164 } 165 } 166 167 @Override allowFileSchemeCookiesImpl()168 protected boolean allowFileSchemeCookiesImpl() { 169 return false; 170 } 171 172 @Override setAcceptFileSchemeCookiesImpl(boolean b)173 protected void setAcceptFileSchemeCookiesImpl(boolean b) { 174 175 } 176 clearAndAddPersistentCookies()177 private void clearAndAddPersistentCookies() { 178 List<Cookie> existing = new ArrayList<>(store); 179 store.clear(); 180 for (Cookie cookie : existing) { 181 if (cookie.isPersistent()) { 182 store.add(cookie); 183 } 184 } 185 } 186 parseCookies(String url, String cookieHeader)187 private List<Cookie> parseCookies(String url, String cookieHeader) { 188 String[] fields = cookieHeader.split(";", 0); 189 190 List<String> parsedFields = new ArrayList<>(); 191 Date expiration = null; 192 boolean isSecure = false; 193 194 for (String field : fields) { 195 field = field.trim(); 196 if (field.startsWith(EXPIRATION_FIELD_NAME)) { 197 expiration = getExpiration(field); 198 } else if (field.toUpperCase().equals(SECURE_ATTR_NAME)) { 199 isSecure = true; 200 } else { 201 parsedFields.add(field); 202 } 203 } 204 205 String hostname = getCookieHost(url); 206 List<Cookie> cookies = new ArrayList<>(); 207 208 for (String cookie : parsedFields) { 209 if (expiration == null || expiration.compareTo(new Date()) >= 0) { 210 cookies.add(new Cookie(hostname, isSecure, cookie, expiration)); 211 } 212 } 213 214 return cookies; 215 } 216 getCookieHost(String url)217 private String getCookieHost(String url) { 218 if (!(url.startsWith(HTTP) || url.startsWith(HTTPS))) { 219 url = HTTP + url; 220 } 221 222 try { 223 return new URI(url).getHost(); 224 } catch (URISyntaxException e) { 225 throw new IllegalArgumentException("wrong URL : " + url, e); 226 } 227 } 228 getExpiration(String field)229 private Date getExpiration(String field) { 230 int equalsIndex = field.indexOf("="); 231 232 if (equalsIndex < 0) { 233 return null; 234 } 235 236 String date = field.substring(equalsIndex + 1); 237 238 try { 239 DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); 240 return dateFormat.parse(date); 241 } catch (ParseException e) { 242 // No-op. Try to inferFromValue additional date formats. 243 } 244 245 try { 246 DateFormat dateFormat = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss zzz"); 247 return dateFormat.parse(date); 248 } catch (ParseException e) { 249 return null; // Was not parsed by any date formatter. 250 } 251 } 252 253 private static class Cookie { 254 private final String mName; 255 private final String mValue; 256 private final Date mExpiration; 257 private final String mHostname; 258 private final boolean mIsSecure; 259 Cookie(String hostname, boolean isSecure, String cookie, Date expiration)260 public Cookie(String hostname, boolean isSecure, String cookie, Date expiration) { 261 mHostname = hostname; 262 mIsSecure = isSecure; 263 mExpiration = expiration; 264 265 int equalsIndex = cookie.indexOf("="); 266 if (equalsIndex >= 0) { 267 mName = cookie.substring(0, equalsIndex); 268 mValue = cookie.substring(equalsIndex + 1); 269 } else { 270 mName = cookie; 271 mValue = null; 272 } 273 } 274 getName()275 public String getName() { 276 return mName; 277 } 278 getValue()279 public String getValue() { 280 return mValue; 281 } 282 isExpiredAt(Date date)283 public boolean isExpiredAt(Date date) { 284 return mExpiration != null && mExpiration.compareTo(date) < 0; 285 } 286 isPersistent()287 public boolean isPersistent() { 288 return mExpiration != null; 289 } 290 isSameHost(String host)291 public boolean isSameHost(String host) { 292 return mHostname.endsWith(host); 293 } 294 isSecure()295 public boolean isSecure() { 296 return mIsSecure; 297 } 298 } 299 } 300