/* * Copyright 2015 Hippo Seven * * 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. */ package com.hippo.nimingban.network; import android.support.annotation.NonNull; import android.text.TextUtils; import com.hippo.yorozuya.ObjectUtils; import java.net.HttpCookie; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; public class SimpleCookieStore { /** this map may have null keys! */ private final Map<URL, List<HttpCookieWithId>> map; public SimpleCookieStore() { map = HttpCookieDB.getAllCookies(); } /** * Returns a non-null path ending in "/". */ private static String matchablePath(String path) { if (path == null) { return "/"; } else if (path.endsWith("/")) { return path; } else { return path + "/"; } } /** * Returns true if {@code cookie} should be sent to or accepted from {@code uri} with respect * to the cookie's path. Cookies match by directory prefix: URI "/foo" matches cookies "/foo", * "/foo/" and "/foo/bar", but not "/" or "/foobar". */ private static boolean pathMatches(HttpCookie cookie, URL url) { String uriPath = matchablePath(url.getPath()); String cookiePath = matchablePath(cookie.getPath()); return uriPath.startsWith(cookiePath); } /** * Returns the port to use for {@code scheme} connections will use when * {@link URI#getPort} returns {@code specifiedPort}. */ public static int getEffectivePort(URL url) { int specifiedPort = url.getPort(); if (specifiedPort != -1) { return specifiedPort; } String protocol = url.getProtocol(); if ("http".equalsIgnoreCase(protocol)) { return 80; } else if ("https".equalsIgnoreCase(protocol)) { return 443; } else { return -1; } } /** * Returns true if {@code cookie} should be sent to {@code uri} with respect to the cookie's * port list. */ private static boolean portMatches(HttpCookie cookie, URL url) { if (cookie.getPortlist() == null) { return true; } return Arrays.asList(cookie.getPortlist().split(",")) .contains(Integer.toString(getEffectivePort(url))); } private static HttpCookieWithId removeCookie(List<HttpCookieWithId> list, HttpCookie cookie) { for (int i = 0, n = list.size(); i < n; i++) { HttpCookieWithId hcwi = list.get(i); if (hcwi.httpCookie.equals(cookie)) { list.remove(i); return hcwi; } } return null; } public synchronized void add(URL url, HttpCookie cookie) { if (cookie == null) { throw new NullPointerException("cookie == null"); } if (cookie.hasExpired()) { remove(url, cookie); return; } url = cookiesUrl(url); List<HttpCookieWithId> cookies = map.get(url); if (cookies == null) { cookies = new ArrayList<>(); map.put(url, cookies); } else { HttpCookieWithId hcwi = removeCookie(cookies, cookie); if (hcwi != null) { // Remove cookie in DB HttpCookieDB.removeCookie(hcwi.id); } } // Add to DB long id = HttpCookieDB.addCookie(cookie, url); // Add to list cookies.add(new HttpCookieWithId(id, cookie)); } private URL cookiesUrl(URL url) { if (url == null) { return null; } try { return new URL("http", url.getHost(), -1, ""); } catch (MalformedURLException e) { return url; } } public synchronized List<HttpCookie> get(URL url) { if (url == null) { throw new NullPointerException("uri == null"); } List<HttpCookie> result = new ArrayList<>(); // get cookies associated with given URI. If none, returns an empty list List<HttpCookieWithId> cookiesForUri = map.get(cookiesUrl(url)); if (cookiesForUri != null) { for (Iterator<HttpCookieWithId> i = cookiesForUri.iterator(); i.hasNext(); ) { HttpCookieWithId hcwi = i.next(); HttpCookie cookie = hcwi.httpCookie; if (hcwi.hasExpired()) { i.remove(); // remove expired cookies HttpCookieDB.removeCookie(hcwi.id); // remove from DB } else if (pathMatches(cookie, url) && portMatches(cookie, url)) { result.add(cookie); } } } // get all cookies that domain matches the URI for (Map.Entry<URL, List<HttpCookieWithId>> entry : map.entrySet()) { if (url.equals(entry.getKey())) { continue; // skip the given URI; we've already handled it } List<HttpCookieWithId> entryCookies = entry.getValue(); for (Iterator<HttpCookieWithId> i = entryCookies.iterator(); i.hasNext(); ) { HttpCookieWithId hcwi = i.next(); HttpCookie cookie = hcwi.httpCookie; if (!HttpCookie.domainMatches(cookie.getDomain(), url.getHost())) { continue; } if (hcwi.hasExpired()) { i.remove(); // remove expired cookies HttpCookieDB.removeCookie(hcwi.id); // remove from DB } else if (pathMatches(cookie, url) && portMatches(cookie, url) && !result.contains(cookie)) { result.add(cookie); } } } return Collections.unmodifiableList(result); } public synchronized List<HttpCookie> getCookies() { List<HttpCookie> result = new ArrayList<>(); for (List<HttpCookieWithId> list : map.values()) { for (Iterator<HttpCookieWithId> i = list.iterator(); i.hasNext(); ) { HttpCookieWithId hcwi = i.next(); HttpCookie cookie = hcwi.httpCookie; if (hcwi.hasExpired()) { i.remove(); // remove expired cookies HttpCookieDB.removeCookie(hcwi.id); // remove from DB } else if (!result.contains(cookie)) { result.add(cookie); } } } return Collections.unmodifiableList(result); } public synchronized List<URL> getURLs() { List<URL> result = new ArrayList<>(map.keySet()); result.remove(null); // sigh return Collections.unmodifiableList(result); } public synchronized void remove(URL url) { if (url == null) { throw new NullPointerException("cookie == null"); } url = cookiesUrl(url); map.remove(url); HttpCookieDB.removeCookies(url); } public synchronized void remove(URL url, HttpCookie cookie) { if (url == null) { throw new NullPointerException("url == null"); } url = cookiesUrl(url); List<HttpCookieWithId> cookies = map.get(url); if (cookies != null) { HttpCookieWithId hcwi = removeCookie(cookies, cookie); if (hcwi != null) { HttpCookieDB.removeCookie(hcwi.id); } } } public synchronized void remove(URL url, String name) { if (url == null) { throw new NullPointerException("cookie == null"); } url = cookiesUrl(url); List<HttpCookieWithId> cookies = map.get(url); if (cookies != null) { for (Iterator<HttpCookieWithId> i = cookies.iterator(); i.hasNext(); ) { HttpCookieWithId hcwi = i.next(); HttpCookie cookie = hcwi.httpCookie; if (hcwi.hasExpired() || (ObjectUtils.equal(name, hcwi.httpCookie.getName()) && pathMatches(cookie, url) && portMatches(cookie, url))) { i.remove(); // remove expired cookies HttpCookieDB.removeCookie(hcwi.id); // remove from DB } } } } public synchronized boolean removeAll() { boolean result = !map.isEmpty(); map.clear(); HttpCookieDB.removeAllCookies(); return result; } public synchronized HttpCookieWithId getCookie(@NonNull URL url, String name) { List<HttpCookieWithId> cookies = map.get(cookiesUrl(url)); if (cookies != null) { for (Iterator<HttpCookieWithId> i = cookies.iterator(); i.hasNext(); ) { HttpCookieWithId hcwi = i.next(); HttpCookie cookie = hcwi.httpCookie; if (hcwi.hasExpired()) { i.remove(); // remove expired cookies HttpCookieDB.removeCookie(hcwi.id); // remove from DB } else if (ObjectUtils.equal(name, hcwi.httpCookie.getName()) && pathMatches(cookie, url) && portMatches(cookie, url)) { return hcwi; } } } return null; } public synchronized boolean contain(@NonNull URL url, String name) { return getCookie(url, name) != null; } public synchronized List<TransportableHttpCookie> getTransportableCookies() { List<TransportableHttpCookie> result = new ArrayList<>(); for (URL url : map.keySet()) { List<HttpCookieWithId> list = map.get(url); result.addAll(TransportableHttpCookie.from(url, list)); } return result; } public void fixLostCookiePath() { for (URL url : map.keySet()) { List<HttpCookieWithId> list = map.get(url); for (HttpCookieWithId hcwi : list) { HttpCookie cookie = hcwi.httpCookie; if (TextUtils.isEmpty(cookie.getPath())) { cookie.setPath("/"); HttpCookieDB.updateCookie(hcwi, url); } } } } }