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");
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());
102 sizeArray =
new int[0];
105 reader =
new BinaryCookieReader(cookieFile, sizeArray);
117 public Iterator<Cookie> iterator() {
118 return new CookiePageIterator();
128 Iterator<Cookie> currentIterator = null;
129 DataInputStream dataStream = null;
135 if (pageSizeArray == null || pageSizeArray.length == 0) {
140 dataStream =
new DataInputStream(
new FileInputStream(cookieFile));
142 dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES));
143 }
catch (IOException ex) {
145 String errorMessage = String.format(
"An error occurred creating an input stream for %s", cookieFile.getName());
146 LOG.log(Level.WARNING, errorMessage, ex);
159 if (dataStream == null) {
163 if (currentIterator == null || !currentIterator.hasNext()) {
166 if (pageIndex < pageSizeArray.length) {
167 byte[] nextPage =
new byte[pageSizeArray[pageIndex]];
168 dataStream.read(nextPage);
171 currentIterator = currentPage.
iterator();
178 }
catch (IOException ex) {
180 String errorMessage = String.format(
"A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex);
181 LOG.log(Level.WARNING, errorMessage, ex);
186 return currentIterator.hasNext();
200 throw new NoSuchElementException();
202 return currentIterator.next();
209 if (dataStream != null) {
213 }
catch (IOException ex) {
214 String errorMessage = String.format(
"An error occurred trying to close stream for file %s", cookieFile.getName());
215 LOG.log(Level.WARNING, errorMessage, ex);
227 ByteBuffer pageBuffer;
238 if (page == null || page.length == 0) {
239 throw new IllegalArgumentException(
"Invalid value for page passed to CookiePage constructor");
242 pageBuffer = ByteBuffer.wrap(page);
244 if (pageBuffer.getInt() != PAGE_HEADER_VALUE) {
246 throw new IOException(
"Invalid file format, bad page head value found");
249 pageBuffer.order(ByteOrder.LITTLE_ENDIAN);
250 int count = pageBuffer.getInt();
251 cookieOffSets =
new int[count];
253 for (
int cnt = 0; cnt < count; cnt++) {
254 cookieOffSets[cnt] = pageBuffer.getInt();
284 if (pageBuffer == null) {
288 return index < cookieOffSets.length;
299 throw new NoSuchElementException();
302 int offset = cookieOffSets[index];
303 int size = pageBuffer.getInt(offset);
304 byte[] cookieBytes =
new byte[size];
305 pageBuffer.get(cookieBytes, 0, size);
308 return new Cookie(cookieBytes);
334 if (cookieBytes == null || cookieBytes.length == 0) {
335 throw new IllegalArgumentException(
"Invalid value for cookieBytes passed to Cookie constructor");
338 ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes);
339 byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
344 int urlOffset = byteBuffer.getInt();
345 int nameOffset = byteBuffer.getInt();
346 int pathOffset = byteBuffer.getInt();
347 int valueOffset = byteBuffer.getInt();
348 byteBuffer.getLong();
350 expirationDate = byteBuffer.getDouble();
351 creationDate = byteBuffer.getDouble();
366 return ((
long) expirationDate) + MAC_EPOC_FIX;
376 return ((
long) creationDate) + MAC_EPOC_FIX;
425 byte[] stringBytes =
new byte[byteArray.length - offset];
426 for (
int index = 0; index < stringBytes.length; index++) {
427 byte nibble = byteArray[offset + index];
428 if (nibble !=
'\0') {
429 stringBytes[index] = nibble;
435 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