19 package org.sleuthkit.autopsy.recentactivity;
21 import java.io.DataInputStream;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.nio.ByteBuffer;
27 import java.nio.ByteOrder;
28 import java.util.Iterator;
29 import java.util.NoSuchElementException;
30 import java.util.logging.Level;
43 final class BinaryCookieReader
implements Iterable<Cookie> {
45 private static final int MAGIC_SIZE = 4;
46 private static final int SIZEOF_INT_BYTES = 4;
47 private static final int PAGE_HEADER_VALUE = 256;
49 private static final String COOKIE_MAGIC =
"cook";
51 private static final int MAC_EPOC_FIX = 978307200;
53 private final int[] pageSizeArray;
54 private final File cookieFile;
63 private BinaryCookieReader(File cookieFile,
int[] pageSizeArray) {
64 this.cookieFile = cookieFile;
65 this.pageSizeArray = pageSizeArray;
78 public static BinaryCookieReader initalizeReader(File cookieFile)
throws FileNotFoundException, IOException {
79 BinaryCookieReader reader = null;
80 try (DataInputStream dataStream =
new DataInputStream(
new FileInputStream(cookieFile))) {
82 byte[] magic =
new byte[MAGIC_SIZE];
83 if (dataStream.read(magic) != MAGIC_SIZE) {
84 throw new IOException(
"Failed to read header, invalid file size (" + cookieFile.getName() +
")");
87 if (!(
new String(magic)).equals(COOKIE_MAGIC)) {
88 throw new IOException(cookieFile.getName() +
" is not a cookie file");
91 int[] sizeArray = null;
92 int pageCount = dataStream.readInt();
94 sizeArray =
new int[pageCount];
96 for (
int cnt = 0; cnt < pageCount; cnt++) {
97 sizeArray[cnt] = dataStream.readInt();
100 LOG.log(Level.INFO,
"No cookies found in {0}", cookieFile.getName());
103 reader =
new BinaryCookieReader(cookieFile, sizeArray);
115 public Iterator<Cookie> iterator() {
116 return new CookiePageIterator();
126 Iterator<Cookie> currentIterator = null;
127 DataInputStream dataStream = null;
133 if (pageSizeArray == null || pageSizeArray.length == 0) {
138 dataStream =
new DataInputStream(
new FileInputStream(cookieFile));
140 dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES));
141 }
catch (IOException ex) {
143 String errorMessage = String.format(
"An error occurred creating an input stream for %s", cookieFile.getName());
144 LOG.log(Level.WARNING, errorMessage, ex);
157 if (dataStream == null) {
161 if (currentIterator == null || !currentIterator.hasNext()) {
164 if (pageIndex < pageSizeArray.length) {
165 byte[] nextPage =
new byte[pageSizeArray[pageIndex]];
166 dataStream.read(nextPage);
169 currentIterator = currentPage.
iterator();
176 }
catch (IOException ex) {
178 String errorMessage = String.format(
"A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex);
179 LOG.log(Level.WARNING, errorMessage, ex);
184 return currentIterator.hasNext();
198 throw new NoSuchElementException();
200 return currentIterator.next();
207 if (dataStream != null) {
211 }
catch (IOException ex) {
212 String errorMessage = String.format(
"An error occurred trying to close stream for file %s", cookieFile.getName());
213 LOG.log(Level.WARNING, errorMessage, ex);
225 ByteBuffer pageBuffer;
235 if (page == null || page.length == 0) {
236 throw new IllegalArgumentException(
"Invalid value for page passed to CookiePage constructor");
239 pageBuffer = ByteBuffer.wrap(page);
241 if (pageBuffer.getInt() != PAGE_HEADER_VALUE) {
243 throw new IOException(
"Invalid file format, bad page head value found");
246 pageBuffer.order(ByteOrder.LITTLE_ENDIAN);
247 int count = pageBuffer.getInt();
248 cookieOffSets =
new int[count];
250 for (
int cnt = 0; cnt < count; cnt++) {
251 cookieOffSets[cnt] = pageBuffer.getInt();
281 if (pageBuffer == null) {
285 return index < cookieOffSets.length;
296 throw new NoSuchElementException();
299 int offset = cookieOffSets[index];
300 int size = pageBuffer.getInt(offset);
301 byte[] cookieBytes =
new byte[size];
302 pageBuffer.get(cookieBytes, 0, size);
305 return new Cookie(cookieBytes);
331 if (cookieBytes == null || cookieBytes.length == 0) {
332 throw new IllegalArgumentException(
"Invalid value for cookieBytes passed to Cookie constructor");
335 ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes);
336 byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
341 int urlOffset = byteBuffer.getInt();
342 int nameOffset = byteBuffer.getInt();
343 int pathOffset = byteBuffer.getInt();
344 int valueOffset = byteBuffer.getInt();
345 byteBuffer.getLong();
347 expirationDate = byteBuffer.getDouble();
348 creationDate = byteBuffer.getDouble();
363 return ((
long) expirationDate) + MAC_EPOC_FIX;
373 return ((
long) creationDate) + MAC_EPOC_FIX;
421 byte[] stringBytes =
new byte[byteArray.length - offset];
422 for (
int index = 0; index < stringBytes.length; index++) {
423 byte nibble = byteArray[offset + index];
424 if (nibble !=
'\0') {
425 stringBytes[index] = nibble;
431 return new String(stringBytes);
Iterator< Cookie > iterator()
final Long getExpirationDate()
String decodeString(byte[] byteArray, int offset)
final Long getCreationDate()
Cookie(byte[] cookieBytes)
final double expirationDate
synchronized static Logger getLogger(String name)
static final int COOKIE_HEAD_SKIP
final double creationDate