OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.cookies; | 5 package org.chromium.chrome.browser.cookies; |
6 | 6 |
7 import android.content.Context; | 7 import android.content.Context; |
8 import android.os.AsyncTask; | 8 import android.os.AsyncTask; |
9 | 9 |
10 import org.chromium.base.ImportantFileWriterAndroid; | 10 import org.chromium.base.ImportantFileWriterAndroid; |
11 import org.chromium.base.Log; | 11 import org.chromium.base.Log; |
12 import org.chromium.base.ThreadUtils; | 12 import org.chromium.base.ThreadUtils; |
13 import org.chromium.base.annotations.CalledByNative; | 13 import org.chromium.base.annotations.CalledByNative; |
14 import org.chromium.chrome.browser.profiles.Profile; | 14 import org.chromium.chrome.browser.profiles.Profile; |
15 import org.chromium.content.browser.crypto.CipherFactory; | 15 import org.chromium.content.browser.crypto.CipherFactory; |
16 import org.chromium.content.common.CleanupReference; | 16 import org.chromium.content.common.CleanupReference; |
17 | 17 |
18 import java.io.ByteArrayOutputStream; | |
19 import java.io.DataInputStream; | |
20 import java.io.DataOutputStream; | |
21 import java.io.EOFException; | |
22 import java.io.File; | 18 import java.io.File; |
23 import java.io.FileInputStream; | |
24 import java.io.IOException; | 19 import java.io.IOException; |
25 import java.util.ArrayList; | 20 import java.util.ArrayList; |
26 import java.util.List; | 21 import java.util.List; |
27 | 22 |
28 import javax.crypto.Cipher; | 23 import javax.crypto.Cipher; |
29 import javax.crypto.CipherInputStream; | |
30 import javax.crypto.CipherOutputStream; | |
31 | 24 |
32 /** | 25 /** |
33 * Responsible for fetching, (de)serializing, and restoring cookies between the
CookieJar and an | 26 * Responsible for fetching, (de)serializing, and restoring cookies between the
CookieJar and an |
34 * encrypted file storage. | 27 * encrypted file storage. |
35 */ | 28 */ |
36 public class CookiesFetcher { | 29 public class CookiesFetcher { |
37 /** The default file name for the encrypted cookies storage. */ | 30 /** The default file name for the encrypted cookies storage. */ |
38 private static final String DEFAULT_COOKIE_FILE_NAME = "COOKIES.DAT"; | 31 private static final String DEFAULT_COOKIE_FILE_NAME = "COOKIES.DAT"; |
39 | 32 |
40 /** Used for logging. */ | 33 /** Used for logging. */ |
41 private static final String TAG = "CookiesFetcher"; | 34 private static final String TAG = "CookiesFetcher"; |
42 | 35 |
43 /** | |
44 * Used to confirm that the current cipher key matches the previously used c
ipher key when | |
45 * restoring data. If this value cannot be read from the file, the file is
likely garbage. | |
46 * TODO(acleung): May be use real cryptographic integrity checks on the whol
e file, instead. | |
47 */ | |
48 private static final String MAGIC_STRING = "c0Ok135"; | |
49 | |
50 /** Native-side pointer. */ | 36 /** Native-side pointer. */ |
51 private final long mNativeCookiesFetcher; | 37 private final long mNativeCookiesFetcher; |
52 | 38 |
53 private final CleanupReference mCleanupReference; | 39 private final CleanupReference mCleanupReference; |
54 | 40 |
55 private final Context mContext; | 41 private final Context mContext; |
56 | 42 |
57 /** | 43 /** |
58 * Creates a new fetcher that can use to fetch cookies from cookie jar | 44 * Creates a new fetcher that can use to fetch cookies from cookie jar |
59 * or from a file. | 45 * or from a file. |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
112 } catch (RuntimeException e) { | 98 } catch (RuntimeException e) { |
113 e.printStackTrace(); | 99 e.printStackTrace(); |
114 } | 100 } |
115 } | 101 } |
116 | 102 |
117 private static void restoreCookiesInternal(final Context context) { | 103 private static void restoreCookiesInternal(final Context context) { |
118 new AsyncTask<Void, Void, List<CanonicalCookie>>() { | 104 new AsyncTask<Void, Void, List<CanonicalCookie>>() { |
119 @Override | 105 @Override |
120 protected List<CanonicalCookie> doInBackground(Void... voids) { | 106 protected List<CanonicalCookie> doInBackground(Void... voids) { |
121 // Read cookies from disk on a background thread to avoid strict
mode violations. | 107 // Read cookies from disk on a background thread to avoid strict
mode violations. |
122 ArrayList<CanonicalCookie> cookies = new ArrayList<CanonicalCook
ie>(); | 108 List<CanonicalCookie> cookies = new ArrayList<CanonicalCookie>()
; |
123 DataInputStream in = null; | |
124 try { | 109 try { |
125 Cipher cipher = CipherFactory.getInstance().getCipher(Cipher
.DECRYPT_MODE); | 110 Cipher cipher = CipherFactory.getInstance().getCipher(Cipher
.DECRYPT_MODE); |
126 if (cipher == null) { | 111 if (cipher == null) { |
127 // Something is wrong. Can't encrypt, don't restore cook
ies. | 112 // Something is wrong. Can't encrypt, don't restore cook
ies. |
128 return cookies; | 113 return cookies; |
129 } | 114 } |
| 115 |
130 File fileIn = new File(fetchFileName(context)); | 116 File fileIn = new File(fetchFileName(context)); |
131 if (!fileIn.exists()) return cookies; // Nothing to read | 117 if (!fileIn.exists()) return cookies; // Nothing to read |
132 | 118 cookies = CanonicalCookie.readFromFileUnknownFormat(fileIn,
cipher); |
133 FileInputStream streamIn = new FileInputStream(fileIn); | |
134 in = new DataInputStream(new CipherInputStream(streamIn, cip
her)); | |
135 String check = in.readUTF(); | |
136 if (!MAGIC_STRING.equals(check)) { | |
137 // Stale cookie file. Chrome might have crashed before i
t | |
138 // can delete the old file. | |
139 return cookies; | |
140 } | |
141 try { | |
142 while (true) { | |
143 CanonicalCookie cookie = CanonicalCookie.createFromS
tream(in); | |
144 cookies.add(cookie); | |
145 } | |
146 } catch (EOFException ignored) { | |
147 // We are done. | |
148 } | |
149 | 119 |
150 // The Cookie File should not be restored again. It'll be ov
erwritten | 120 // The Cookie File should not be restored again. It'll be ov
erwritten |
151 // on the next onPause. | 121 // on the next onPause. |
152 scheduleDeleteCookiesFile(context); | 122 scheduleDeleteCookiesFile(context); |
153 | 123 |
154 } catch (IOException e) { | 124 } catch (IOException e) { |
155 Log.w(TAG, "IOException during Cookie Restore"); | 125 Log.w(TAG, "IOException during Cookie Restore", e); |
156 } catch (Throwable t) { | 126 } catch (Throwable t) { |
157 Log.w(TAG, "Error restoring cookies.", t); | 127 Log.w(TAG, "Error restoring cookies.", t); |
158 } finally { | |
159 try { | |
160 if (in != null) in.close(); | |
161 } catch (IOException e) { | |
162 Log.w(TAG, "IOException during Cooke Restore"); | |
163 } catch (Throwable t) { | |
164 Log.w(TAG, "Error restoring cookies.", t); | |
165 } | |
166 } | 128 } |
167 return cookies; | 129 return cookies; |
168 } | 130 } |
169 | 131 |
170 @Override | 132 @Override |
171 protected void onPostExecute(List<CanonicalCookie> cookies) { | 133 protected void onPostExecute(List<CanonicalCookie> cookies) { |
172 // We can only access cookies and profiles on the UI thread. | 134 // We can only access cookies and profiles on the UI thread. |
173 for (CanonicalCookie cookie : cookies) { | 135 for (CanonicalCookie cookie : cookies) { |
174 nativeRestoreCookies(cookie.getUrl(), cookie.getName(), cook
ie.getValue(), | 136 nativeRestoreCookies(cookie.getUrl(), cookie.getName(), cook
ie.getValue(), |
175 cookie.getDomain(), cookie.getPath(), cookie.getCrea
tionDate(), | 137 cookie.getDomain(), cookie.getPath(), cookie.getCrea
tionDate(), |
176 cookie.getExpirationDate(), cookie.getLastAccessDate
(), | 138 cookie.getExpirationDate(), cookie.getLastAccessDate
(), |
177 cookie.isSecure(), cookie.isHttpOnly(), cookie.isSam
eSite(), | 139 cookie.isSecure(), cookie.isHttpOnly(), cookie.getSa
meSite(), |
178 cookie.getPriority()); | 140 cookie.getPriority()); |
179 } | 141 } |
180 } | 142 } |
181 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); | 143 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); |
182 } | 144 } |
183 | 145 |
184 /** | 146 /** |
185 * Ensure the incognito cookies are deleted when the incognito profile is go
ne. | 147 * Ensure the incognito cookies are deleted when the incognito profile is go
ne. |
186 * | 148 * |
187 * @param context Context for accessing the file system. | 149 * @param context Context for accessing the file system. |
(...skipping 24 matching lines...) Expand all Loading... |
212 } | 174 } |
213 } | 175 } |
214 return null; | 176 return null; |
215 } | 177 } |
216 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); | 178 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); |
217 } | 179 } |
218 | 180 |
219 @CalledByNative | 181 @CalledByNative |
220 private CanonicalCookie createCookie(String url, String name, String value,
String domain, | 182 private CanonicalCookie createCookie(String url, String name, String value,
String domain, |
221 String path, long creation, long expiration, long lastAccess, boolea
n secure, | 183 String path, long creation, long expiration, long lastAccess, boolea
n secure, |
222 boolean httpOnly, boolean sameSite, int priority) { | 184 boolean httpOnly, int sameSite, int priority) { |
223 return new CanonicalCookie(url, name, value, domain, path, creation, exp
iration, lastAccess, | 185 return new CanonicalCookie(url, name, value, domain, path, creation, exp
iration, lastAccess, |
224 secure, httpOnly, sameSite, priority); | 186 secure, httpOnly, sameSite, priority); |
225 } | 187 } |
226 | 188 |
227 @CalledByNative | 189 @CalledByNative |
228 private void onCookieFetchFinished(final CanonicalCookie[] cookies) { | 190 private void onCookieFetchFinished(final CanonicalCookie[] cookies) { |
229 // Cookies fetching requires operations with the profile and must be | 191 // Cookies fetching requires operations with the profile and must be |
230 // done in the main thread. Once that is done, do the save to disk | 192 // done in the main thread. Once that is done, do the save to disk |
231 // part in {@link AsyncTask} to avoid strict mode violations. | 193 // part in {@link AsyncTask} to avoid strict mode violations. |
232 new AsyncTask<Void, Void, Void>() { | 194 new AsyncTask<Void, Void, Void>() { |
233 @Override | 195 @Override |
234 protected Void doInBackground(Void... voids) { | 196 protected Void doInBackground(Void... voids) { |
235 saveFetchedCookiesToDisk(cookies); | 197 saveFetchedCookiesToDisk(cookies); |
236 return null; | 198 return null; |
237 } | 199 } |
238 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); | 200 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); |
239 } | 201 } |
240 | 202 |
241 private void saveFetchedCookiesToDisk(CanonicalCookie[] cookies) { | 203 private void saveFetchedCookiesToDisk(CanonicalCookie[] cookies) { |
242 DataOutputStream out = null; | |
243 try { | 204 try { |
244 Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.ENCRYPT
_MODE); | 205 Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.ENCRYPT
_MODE); |
245 if (cipher == null) { | 206 if (cipher == null) { |
246 // Something is wrong. Can't encrypt, don't save cookies. | 207 // Something is wrong. Can't encrypt, don't save cookies. |
247 return; | 208 return; |
248 } | 209 } |
249 | 210 |
250 ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); | |
251 CipherOutputStream cipherOut = | |
252 new CipherOutputStream(byteOut, cipher); | |
253 out = new DataOutputStream(cipherOut); | |
254 | |
255 out.writeUTF(MAGIC_STRING); | |
256 for (CanonicalCookie cookie : cookies) { | |
257 cookie.saveToStream(out); | |
258 } | |
259 out.close(); | |
260 ImportantFileWriterAndroid.writeFileAtomically( | 211 ImportantFileWriterAndroid.writeFileAtomically( |
261 fetchFileName(mContext), byteOut.toByteArray()); | 212 fetchFileName(mContext), CanonicalCookie.writeToByteArray(ci
pher, cookies)); |
262 out = null; | |
263 } catch (IOException e) { | 213 } catch (IOException e) { |
264 Log.w(TAG, "IOException during Cookie Fetch"); | 214 Log.w(TAG, "IOException during Cookie Fetch"); |
265 } catch (Throwable t) { | 215 } catch (Throwable t) { |
266 Log.w(TAG, "Error storing cookies.", t); | 216 Log.w(TAG, "Error storing cookies.", t); |
267 } finally { | |
268 try { | |
269 if (out != null) out.close(); | |
270 } catch (IOException e) { | |
271 Log.w(TAG, "IOException during Cookie Fetch"); | |
272 } | |
273 } | 217 } |
274 } | 218 } |
275 | 219 |
276 private static final class DestroyRunnable implements Runnable { | 220 private static final class DestroyRunnable implements Runnable { |
277 private final long mNativeCookiesFetcher; | 221 private final long mNativeCookiesFetcher; |
278 | 222 |
279 private DestroyRunnable(long nativeCookiesFetcher) { | 223 private DestroyRunnable(long nativeCookiesFetcher) { |
280 mNativeCookiesFetcher = nativeCookiesFetcher; | 224 mNativeCookiesFetcher = nativeCookiesFetcher; |
281 } | 225 } |
282 | 226 |
283 @Override | 227 @Override |
284 public void run() { | 228 public void run() { |
285 if (mNativeCookiesFetcher != 0) nativeDestroy(mNativeCookiesFetcher)
; | 229 if (mNativeCookiesFetcher != 0) nativeDestroy(mNativeCookiesFetcher)
; |
286 } | 230 } |
287 } | 231 } |
288 | 232 |
289 @CalledByNative | 233 @CalledByNative |
290 private CanonicalCookie[] createCookiesArray(int size) { | 234 private CanonicalCookie[] createCookiesArray(int size) { |
291 return new CanonicalCookie[size]; | 235 return new CanonicalCookie[size]; |
292 } | 236 } |
293 | 237 |
294 private native long nativeInit(); | 238 private native long nativeInit(); |
295 private static native void nativeDestroy(long nativeCookiesFetcher); | 239 private static native void nativeDestroy(long nativeCookiesFetcher); |
296 private native void nativePersistCookies(long nativeCookiesFetcher); | 240 private native void nativePersistCookies(long nativeCookiesFetcher); |
297 private static native void nativeRestoreCookies(String url, String name, Str
ing value, | 241 private static native void nativeRestoreCookies(String url, String name, Str
ing value, |
298 String domain, String path, long creation, long expiration, long las
tAccess, | 242 String domain, String path, long creation, long expiration, long las
tAccess, |
299 boolean secure, boolean httpOnly, boolean sameSite, int priority); | 243 boolean secure, boolean httpOnly, int sameSite, int priority); |
300 } | 244 } |
OLD | NEW |