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> {
46 private static final int MAGIC_SIZE = 4;
47 private static final int SIZEOF_INT_BYTES = 4;
48 private static final int PAGE_HEADER_VALUE = 256;
50 private static final String COOKIE_MAGIC =
"cook";
52 private static final int MAC_EPOC_FIX = 978307200;
54 private final int[] pageSizeArray;
55 private final File cookieFile;
62 private BinaryCookieReader(File cookieFile,
int[] pageSizeArray) {
63 this.cookieFile = cookieFile;
64 this.pageSizeArray = pageSizeArray.clone();
79 public static BinaryCookieReader initalizeReader(File cookieFile)
throws FileNotFoundException, IOException {
80 BinaryCookieReader reader = null;
81 try (DataInputStream dataStream =
new DataInputStream(
new FileInputStream(cookieFile))) {
83 byte[] magic =
new byte[MAGIC_SIZE];
84 if (dataStream.read(magic) != MAGIC_SIZE) {
85 throw new IOException(
"Failed to read header, invalid file size (" + cookieFile.getName() +
")");
88 if (!(
new String(magic)).equals(COOKIE_MAGIC)) {
89 throw new IOException(cookieFile.getName() +
" is not a cookie file");
92 int[] sizeArray = null;
93 int pageCount = dataStream.readInt();
95 sizeArray =
new int[pageCount];
97 for (
int cnt = 0; cnt < pageCount; cnt++) {
98 sizeArray[cnt] = dataStream.readInt();
101 LOG.log(Level.INFO,
"No cookies found in {0}", cookieFile.getName());
104 reader =
new BinaryCookieReader(cookieFile, sizeArray);
116 public Iterator<Cookie> iterator() {
117 return new CookiePageIterator();
127 Iterator<Cookie> currentIterator = null;
128 DataInputStream dataStream = null;
134 if (pageSizeArray == null || pageSizeArray.length == 0) {
139 dataStream =
new DataInputStream(
new FileInputStream(cookieFile));
141 dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES));
142 }
catch (IOException ex) {
144 String errorMessage = String.format(
"An error occurred creating an input stream for %s", cookieFile.getName());
145 LOG.log(Level.WARNING, errorMessage, ex);
158 if (dataStream == null) {
162 if (currentIterator == null || !currentIterator.hasNext()) {
165 if (pageIndex < pageSizeArray.length) {
166 byte[] nextPage =
new byte[pageSizeArray[pageIndex]];
167 dataStream.read(nextPage);
170 currentIterator = currentPage.
iterator();
177 }
catch (IOException ex) {
179 String errorMessage = String.format(
"A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex);
180 LOG.log(Level.WARNING, errorMessage, ex);
185 return currentIterator.hasNext();
199 throw new NoSuchElementException();
201 return currentIterator.next();
208 if (dataStream != null) {
212 }
catch (IOException ex) {
213 String errorMessage = String.format(
"An error occurred trying to close stream for file %s", cookieFile.getName());
214 LOG.log(Level.WARNING, errorMessage, ex);
226 ByteBuffer pageBuffer;
237 if (page == null || page.length == 0) {
238 throw new IllegalArgumentException(
"Invalid value for page passed to CookiePage constructor");
241 pageBuffer = ByteBuffer.wrap(page);
243 if (pageBuffer.getInt() != PAGE_HEADER_VALUE) {
245 throw new IOException(
"Invalid file format, bad page head value found");
248 pageBuffer.order(ByteOrder.LITTLE_ENDIAN);
249 int count = pageBuffer.getInt();
250 cookieOffSets =
new int[count];
252 for (
int cnt = 0; cnt < count; cnt++) {
253 cookieOffSets[cnt] = pageBuffer.getInt();
283 if (pageBuffer == null) {
287 return index < cookieOffSets.length;
298 throw new NoSuchElementException();
301 int offset = cookieOffSets[index];
302 int size = pageBuffer.getInt(offset);
303 byte[] cookieBytes =
new byte[size];
304 pageBuffer.get(cookieBytes, 0, size);
307 return new Cookie(cookieBytes);
333 if (cookieBytes == null || cookieBytes.length == 0) {
334 throw new IllegalArgumentException(
"Invalid value for cookieBytes passed to Cookie constructor");
337 ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes);
338 byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
343 int urlOffset = byteBuffer.getInt();
344 int nameOffset = byteBuffer.getInt();
345 int pathOffset = byteBuffer.getInt();
346 int valueOffset = byteBuffer.getInt();
347 byteBuffer.getLong();
349 expirationDate = byteBuffer.getDouble();
350 creationDate = byteBuffer.getDouble();
365 return ((
long) expirationDate) + MAC_EPOC_FIX;
375 return ((
long) creationDate) + MAC_EPOC_FIX;
424 byte[] stringBytes =
new byte[byteArray.length - offset];
425 for (
int index = 0; index < stringBytes.length; index++) {
426 byte nibble = byteArray[offset + index];
427 if (nibble !=
'\0') {
428 stringBytes[index] = nibble;
434 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