21 package org.sleuthkit.autopsy.recentactivity;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.RandomAccessFile;
27 import java.nio.ByteBuffer;
28 import java.nio.ByteOrder;
29 import java.nio.channels.FileChannel;
30 import java.nio.charset.Charset;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.List;
38 import java.util.Map.Entry;
39 import java.util.Optional;
40 import java.util.logging.Level;
41 import org.openide.util.NbBundle;
55 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
76 final class ChromeCacheExtractor {
78 private final static String DEFAULT_CACHE_STR =
"default/cache";
79 private final static String BROTLI_MIMETYPE =
"application/x-brotli";
81 private final static long UINT32_MASK = 0xFFFFFFFFl;
83 private final static int INDEXFILE_HDR_SIZE = 92*4;
84 private final static int DATAFILE_HDR_SIZE = 8192;
86 private final static Logger logger = Logger.getLogger(ChromeCacheExtractor.class.getName());
88 private static final String VERSION_NUMBER =
"1.0.0";
89 private final String moduleName;
91 private String absOutputFolderName;
92 private String relOutputFolderName;
94 private final Content dataSource;
95 private final IngestJobContext context;
96 private final DataSourceIngestModuleProgress progressBar;
97 private final IngestServices services = IngestServices.getInstance();
98 private Case currentCase;
99 private FileManager fileManager;
102 private final Map<String, CacheFileCopy> filesTable =
new HashMap<>();
105 private final Map<String, AbstractFile> externalFilesTable =
new HashMap<>();
111 final class CacheFileCopy {
113 private final AbstractFile abstractFile;
114 private final RandomAccessFile fileCopy;
115 private final ByteBuffer byteBuffer;
117 CacheFileCopy (AbstractFile abstractFile, RandomAccessFile fileCopy, ByteBuffer buffer ) {
118 this.abstractFile = abstractFile;
119 this.fileCopy = fileCopy;
120 this.byteBuffer = buffer;
123 public RandomAccessFile getFileCopy() {
126 public ByteBuffer getByteBuffer() {
129 AbstractFile getAbstractFile() {
135 "ChromeCacheExtractor.moduleName=ChromeCacheExtractor",
136 "ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}"
138 ChromeCacheExtractor(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar ) {
139 moduleName = Bundle.ChromeCacheExtractor_moduleName();
140 this.dataSource = dataSource;
141 this.context = context;
142 this.progressBar = progressBar;
151 void moduleInit() throws IngestModuleException {
154 currentCase = Case.getCurrentCaseThrows();
155 fileManager = currentCase.getServices().getFileManager();
158 absOutputFolderName = RAImageIngestModule.getRAOutputPath(currentCase, moduleName);
159 relOutputFolderName = Paths.get( RAImageIngestModule.getRelModuleOutputPath(), moduleName).normalize().toString();
161 File dir =
new File(absOutputFolderName);
162 if (dir.exists() ==
false) {
165 }
catch (NoCurrentCaseException ex) {
166 String msg =
"Failed to get current case.";
167 throw new IngestModuleException(msg, ex);
178 void subInit(String cachePath)
throws IngestModuleException {
181 externalFilesTable.clear();
183 String cacheAbsOutputFolderName = this.getAbsOutputFolderName() + cachePath;
184 File outDir =
new File(cacheAbsOutputFolderName);
185 if (outDir.exists() ==
false) {
189 String cacheTempPath = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath;
190 File tempDir =
new File(cacheTempPath);
191 if (tempDir.exists() ==
false) {
204 for (Entry<String, CacheFileCopy> entry : this.filesTable.entrySet()) {
205 Path tempFilePath = Paths.get(RAImageIngestModule.getRATempPath(currentCase, moduleName), entry.getKey() );
207 entry.getValue().getFileCopy().getChannel().close();
208 entry.getValue().getFileCopy().close();
210 File tmpFile = tempFilePath.toFile();
211 if (!tmpFile.delete()) {
212 tmpFile.deleteOnExit();
214 }
catch (IOException ex) {
215 logger.log(Level.WARNING, String.format(
"Failed to delete cache file copy %s", tempFilePath.toString()), ex);
225 private String getAbsOutputFolderName() {
226 return absOutputFolderName;
234 private String getRelOutputFolderName() {
235 return relOutputFolderName;
248 }
catch (IngestModuleException ex) {
249 String msg =
"Failed to initialize ChromeCacheExtractor.";
250 logger.log(Level.SEVERE, msg, ex);
255 List<AbstractFile> indexFiles;
257 indexFiles = findCacheFiles(
"index");
260 for (AbstractFile indexFile: indexFiles) {
264 }
catch (TskCoreException ex) {
265 String msg =
"Failed to find cache index files";
266 logger.log(Level.SEVERE, msg, ex);
275 void getCache(AbstractFile indexAbstractFile) {
277 String cachePath = indexAbstractFile.getParentPath();
278 Optional<CacheFileCopy> indexFile;
282 indexFile = this.getCacheFileCopy(indexAbstractFile.getName(), cachePath);
283 if (!indexFile.isPresent()) {
284 String msg = String.format(
"Failed to find copy cache index file %s", indexAbstractFile.getUniquePath());
285 logger.log(Level.SEVERE, msg);
289 for (
int i = 0; i < 4; i ++) {
290 Optional<CacheFileCopy> dataFile = findAndCopyCacheFile(String.format(
"data_%1d",i), cachePath );
291 if (!dataFile.isPresent()) {
297 findExternalFiles(cachePath);
299 }
catch (TskCoreException | IngestModuleException ex) {
300 String msg =
"Failed to find cache files in path " + cachePath;
301 logger.log(Level.SEVERE, msg, ex);
305 logger.log(Level.INFO,
"{0}- Now reading Cache index file from path {1}",
new Object[]{moduleName, cachePath });
307 List<AbstractFile> derivedFiles =
new ArrayList<>();
308 Collection<BlackboardArtifact> sourceArtifacts =
new ArrayList<>();
309 Collection<BlackboardArtifact> webCacheArtifacts =
new ArrayList<>();
311 ByteBuffer indexFileROBuffer = indexFile.get().getByteBuffer();
312 IndexFileHeader indexHdr =
new IndexFileHeader(indexFileROBuffer);
315 indexFileROBuffer.position(INDEXFILE_HDR_SIZE);
318 for (
int i = 0; i < indexHdr.getTableLen(); i++) {
319 CacheAddress addr =
new CacheAddress(indexFileROBuffer.getInt() & UINT32_MASK, cachePath);
320 if (addr.isInitialized()) {
321 progressBar.progress( NbBundle.getMessage(
this.getClass(),
322 "ChromeCacheExtractor.progressMsg",
323 moduleName, i, indexHdr.getTableLen(), cachePath) );
325 List<DerivedFile> addedFiles = this.getCacheEntry(addr, sourceArtifacts, webCacheArtifacts);
326 derivedFiles.addAll(addedFiles);
328 catch (TskCoreException | IngestModuleException ex) {
329 logger.log(Level.SEVERE, String.format(
"Failed to get cache entry at address %s", addr), ex);
334 derivedFiles.forEach((derived) -> {
335 services.fireModuleContentEvent(
new ModuleContentEvent(derived));
338 context.addFilesToJob(derivedFiles);
340 services.fireModuleDataEvent(
new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE, !sourceArtifacts.isEmpty() ? sourceArtifacts : null));
341 services.fireModuleDataEvent(
new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE, !webCacheArtifacts.isEmpty() ? webCacheArtifacts : null));
357 List<DerivedFile> getCacheEntry(CacheAddress cacheEntryAddress, Collection<BlackboardArtifact> sourceArtifacts, Collection<BlackboardArtifact> webCacheArtifacts )
throws TskCoreException, IngestModuleException {
359 List<DerivedFile> derivedFiles =
new ArrayList<>();
361 String cacheEntryFileName = cacheEntryAddress.getFilename();
362 String cachePath = cacheEntryAddress.getCachePath();
365 Optional<CacheFileCopy> cacheEntryFile = this.getCacheFileCopy(cacheEntryFileName, cachePath);
366 if (!cacheEntryFile.isPresent()) {
367 String msg = String.format(
"Failed to get cache entry at address %s", cacheEntryAddress);
368 throw new IngestModuleException(msg);
373 CacheEntry cacheEntry =
new CacheEntry(cacheEntryAddress, cacheEntryFile.get() );
374 List<CacheData> dataEntries = cacheEntry.getData();
376 BlackboardAttribute urlAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL,
378 ((cacheEntry.getKey() != null) ? cacheEntry.getKey() :
""));
380 BlackboardAttribute createTimeAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
382 cacheEntry.getCreationTime());
384 BlackboardAttribute hhtpHeaderAttr =
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS,
386 cacheEntry.getHTTPHeaders());
391 for (
int j = 1; j < dataEntries.size() && j < 2; j++) {
392 CacheData data = dataEntries.get(j);
393 String dataFilename = data.getAddress().getFilename();
394 Optional<AbstractFile> dataFile = this.findCacheFile(dataFilename, cachePath);
396 boolean isBrotliCompressed =
false;
397 if (data.getType() != CacheDataTypeEnum.HTTP_HEADER && cacheEntry.isBrotliCompressed() ) {
398 isBrotliCompressed =
true;
401 Collection<BlackboardAttribute> sourceArtifactAttributes =
new ArrayList<>();
402 sourceArtifactAttributes.add(urlAttr);
403 sourceArtifactAttributes.add(createTimeAttr);
405 Collection<BlackboardAttribute> webCacheAttributes =
new ArrayList<>();
406 webCacheAttributes.add(urlAttr);
407 webCacheAttributes.add(createTimeAttr);
408 webCacheAttributes.add(hhtpHeaderAttr);
410 if (dataFile.isPresent()) {
411 if (data.isInExternalFile() ) {
413 BlackboardArtifact sourceArtifact = dataFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
414 if (sourceArtifact != null) {
415 sourceArtifact.addAttributes(sourceArtifactAttributes);
416 sourceArtifacts.add(sourceArtifact);
419 BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
420 if (webCacheArtifact != null) {
421 webCacheArtifact.addAttributes(webCacheAttributes);
424 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
426 dataFile.get().getUniquePath()));
428 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
429 moduleName, dataFile.get().getId()));
431 webCacheArtifacts.add(webCacheArtifact);
434 if (isBrotliCompressed) {
435 dataFile.get().setMIMEType(BROTLI_MIMETYPE);
436 dataFile.get().save();
438 }
catch (TskException ex) {
439 logger.log(Level.SEVERE,
"Error while trying to add an artifact", ex);
444 String filename = data.save();
445 String relPathname = getRelOutputFolderName() + data.getAddress().getCachePath() + filename;
447 DerivedFile derivedFile = fileManager.addDerivedFile(filename, relPathname,
448 data.getDataLength(),
449 cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(),
456 TskData.EncodingType.NONE);
458 BlackboardArtifact sourceArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
459 if (sourceArtifact != null) {
460 sourceArtifact.addAttributes(sourceArtifactAttributes);
461 sourceArtifacts.add(sourceArtifact);
464 BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
465 if (webCacheArtifact != null) {
466 webCacheArtifact.addAttributes(webCacheAttributes);
469 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
471 derivedFile.getUniquePath()));
473 webCacheArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
474 moduleName, derivedFile.getId()));
476 webCacheArtifacts.add(webCacheArtifact);
479 if (isBrotliCompressed) {
480 derivedFile.setMIMEType(BROTLI_MIMETYPE);
484 derivedFiles.add(derivedFile);
485 }
catch (TskException ex) {
486 logger.log(Level.SEVERE,
"Error while trying to add an artifact", ex);
503 private void findExternalFiles(String cachePath)
throws TskCoreException {
505 List<AbstractFile> effFiles = fileManager.findFiles(dataSource,
"f_%", cachePath);
506 for (AbstractFile abstractFile : effFiles ) {
507 this.externalFilesTable.put(cachePath + abstractFile.getName(), abstractFile);
518 Optional<AbstractFile> findCacheFile(String cacheFileName, String cachePath)
throws TskCoreException {
520 String fileTableKey = cachePath + cacheFileName;
521 if (cacheFileName.startsWith(
"f_") && externalFilesTable.containsKey(fileTableKey)) {
522 return Optional.of(externalFilesTable.get(fileTableKey));
524 if (filesTable.containsKey(fileTableKey)) {
525 return Optional.of(filesTable.get(fileTableKey).getAbstractFile());
528 List<AbstractFile> cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath);
529 if (!cacheFiles.isEmpty()) {
530 for (AbstractFile abstractFile: cacheFiles ) {
531 if (abstractFile.getUniquePath().trim().endsWith(DEFAULT_CACHE_STR)) {
532 return Optional.of(abstractFile);
535 return Optional.of(cacheFiles.get(0));
538 return Optional.empty();
548 List<AbstractFile> findCacheFiles(String cacheFileName)
throws TskCoreException {
549 return fileManager.findFiles(dataSource, cacheFileName, DEFAULT_CACHE_STR);
561 Optional<CacheFileCopy> getCacheFileCopy(String cacheFileName, String cachePath)
throws TskCoreException, IngestModuleException {
564 String fileTableKey = cachePath + cacheFileName;
565 if (filesTable.containsKey(fileTableKey)) {
566 return Optional.of(filesTable.get(fileTableKey));
569 return findAndCopyCacheFile(cacheFileName, cachePath);
579 Optional<CacheFileCopy> findAndCopyCacheFile(String cacheFileName, String cachePath)
throws TskCoreException, IngestModuleException {
581 Optional<AbstractFile> cacheFileOptional = findCacheFile(cacheFileName, cachePath);
582 if (!cacheFileOptional.isPresent()) {
583 return Optional.empty();
586 AbstractFile cacheFile = cacheFileOptional.get();
587 RandomAccessFile randomAccessFile = null;
588 String tempFilePathname = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath + cacheFile.getName();
590 File newFile =
new File(tempFilePathname);
591 ContentUtils.writeToFile(cacheFile, newFile, context::dataSourceIngestIsCancelled);
593 randomAccessFile =
new RandomAccessFile(tempFilePathname,
"r");
594 FileChannel roChannel = randomAccessFile.getChannel();
595 ByteBuffer cacheFileROBuf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0,
596 (
int) roChannel.size());
598 cacheFileROBuf.order(ByteOrder.nativeOrder());
599 CacheFileCopy cacheFileCopy =
new CacheFileCopy(cacheFile, randomAccessFile, cacheFileROBuf );
601 if (!cacheFileName.startsWith(
"f_")) {
602 filesTable.put(cachePath + cacheFileName, cacheFileCopy);
605 return Optional.of(cacheFileCopy);
607 catch (IOException ex) {
610 if (randomAccessFile != null) {
611 randomAccessFile.close();
614 catch (IOException ex2) {
615 logger.log(Level.SEVERE,
"Error while trying to close temp file after exception.", ex2);
617 String msg = String.format(
"Error reading/copying Chrome cache file '%s' (id=%d).",
618 cacheFile.getName(), cacheFile.getId());
619 throw new IngestModuleException(msg, ex);
626 final class IndexFileHeader {
628 private final long magic;
629 private final int version;
630 private final int numEntries;
631 private final int numBytes;
632 private final int lastFile;
633 private final int tableLen;
635 IndexFileHeader(ByteBuffer indexFileROBuf) {
637 magic = indexFileROBuf.getInt() & UINT32_MASK;
639 indexFileROBuf.position(indexFileROBuf.position()+2);
641 version = indexFileROBuf.getShort();
642 numEntries = indexFileROBuf.getInt();
643 numBytes = indexFileROBuf.getInt();
644 lastFile = indexFileROBuf.getInt();
646 indexFileROBuf.position(indexFileROBuf.position()+4);
647 indexFileROBuf.position(indexFileROBuf.position()+4);
649 tableLen = indexFileROBuf.getInt();
652 public long getMagic() {
656 public int getVersion() {
660 public int getNumEntries() {
664 public int getNumBytes() {
668 public int getLastFile() {
672 public int getTableLen() {
677 public String toString() {
678 StringBuilder sb =
new StringBuilder();
680 sb.append(String.format(
"Index Header:"))
681 .append(String.format(
"\tMagic = %x" , getMagic()) )
682 .append(String.format(
"\tVersion = %x" , getVersion()) )
683 .append(String.format(
"\tNumEntries = %x" , getNumEntries()) )
684 .append(String.format(
"\tNumBytes = %x" , getNumBytes()) )
685 .append(String.format(
"\tLastFile = %x" , getLastFile()) )
686 .append(String.format(
"\tTableLen = %x" , getTableLen()) );
688 return sb.toString();
695 enum CacheFileTypeEnum {
727 final class CacheAddress {
729 private static final long ADDR_INITIALIZED_MASK = 0x80000000l;
730 private static final long FILE_TYPE_MASK = 0x70000000;
731 private static final long FILE_TYPE_OFFSET = 28;
732 private static final long NUM_BLOCKS_MASK = 0x03000000;
733 private static final long NUM_BLOCKS_OFFSET = 24;
734 private static final long FILE_SELECTOR_MASK = 0x00ff0000;
735 private static final long FILE_SELECTOR_OFFSET = 16;
736 private static final long START_BLOCK_MASK = 0x0000FFFF;
737 private static final long EXTERNAL_FILE_NAME_MASK = 0x0FFFFFFF;
739 private final long uint32CacheAddr;
740 private final CacheFileTypeEnum fileType;
741 private final int numBlocks;
742 private final int startBlock;
743 private final String fileName;
744 private final int fileNumber;
746 private final String cachePath;
749 CacheAddress(
long uint32, String cachePath) {
751 uint32CacheAddr = uint32;
752 this.cachePath = cachePath;
754 int fileTypeEnc = (int)(uint32CacheAddr & FILE_TYPE_MASK) >> FILE_TYPE_OFFSET;
755 fileType = CacheFileTypeEnum.values()[fileTypeEnc];
757 if (isInitialized()) {
758 if (isInExternalFile()) {
759 fileNumber = (int)(uint32CacheAddr & EXTERNAL_FILE_NAME_MASK);
760 fileName = String.format(
"f_%06x", getFileNumber() );
764 fileNumber = (int)((uint32CacheAddr & FILE_SELECTOR_MASK) >> FILE_SELECTOR_OFFSET);
765 fileName = String.format(
"data_%d", getFileNumber() );
766 numBlocks = (int)(uint32CacheAddr & NUM_BLOCKS_MASK >> NUM_BLOCKS_OFFSET);
767 startBlock = (int)(uint32CacheAddr & START_BLOCK_MASK);
778 boolean isInitialized() {
779 return ((uint32CacheAddr & ADDR_INITIALIZED_MASK) != 0);
782 CacheFileTypeEnum getFileType() {
786 String getFilename() {
790 String getCachePath() {
794 boolean isInExternalFile() {
795 return (fileType == CacheFileTypeEnum.EXTERNAL);
798 int getFileNumber() {
802 int getStartBlock() {
831 public long getUint32CacheAddr() {
832 return uint32CacheAddr;
836 public String toString() {
837 StringBuilder sb =
new StringBuilder();
838 sb.append(String.format(
"CacheAddr %08x : %s : filename %s",
840 isInitialized() ?
"Initialized" :
"UnInitialized",
843 if ((fileType == CacheFileTypeEnum.BLOCK_256) ||
844 (fileType == CacheFileTypeEnum.BLOCK_1K) ||
845 (fileType == CacheFileTypeEnum.BLOCK_4K) ) {
846 sb.append(String.format(
" (%d blocks starting at %08X)",
852 return sb.toString();
860 enum CacheDataTypeEnum {
874 final class CacheData {
877 private final CacheAddress address;
878 private CacheDataTypeEnum type;
880 private boolean isHTTPHeaderHint;
882 private CacheFileCopy cacheFileCopy = null;
883 private byte[] data = null;
885 private String httpResponse;
886 private final Map<String, String> httpHeaders =
new HashMap<>();
888 CacheData(CacheAddress cacheAdress,
int len) {
889 this(cacheAdress, len,
false);
892 CacheData(CacheAddress cacheAdress,
int len,
boolean isHTTPHeader ) {
893 this.type = CacheDataTypeEnum.UNKNOWN;
895 this.address = cacheAdress;
896 this.isHTTPHeaderHint = isHTTPHeader;
899 boolean isInExternalFile() {
900 return address.isInExternalFile();
903 boolean hasHTTPHeaders() {
904 return this.type == CacheDataTypeEnum.HTTP_HEADER;
907 String getHTTPHeader(String key) {
908 return this.httpHeaders.get(key);
916 String getHTTPHeaders() {
917 if (!hasHTTPHeaders()) {
921 StringBuilder sb =
new StringBuilder();
922 httpHeaders.entrySet().forEach((entry) -> {
923 if (sb.length() > 0) {
926 sb.append(String.format(
"%s : %s",
927 entry.getKey(), entry.getValue()));
930 return sb.toString();
933 String getHTTPRespone() {
942 void extract() throws TskCoreException, IngestModuleException {
950 if (!address.isInExternalFile() ) {
952 cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).
get();
954 this.data =
new byte [length];
955 ByteBuffer buf = cacheFileCopy.getByteBuffer();
956 int dataOffset = DATAFILE_HDR_SIZE + address.getStartBlock() * address.getBlockSize();
957 buf.position(dataOffset);
958 buf.get(data, 0, length);
961 if ((isHTTPHeaderHint)) {
962 String strData =
new String(data);
963 if (strData.contains(
"HTTP")) {
974 type = CacheDataTypeEnum.HTTP_HEADER;
976 int startOff = strData.indexOf(
"HTTP");
977 Charset charset = Charset.forName(
"UTF-8");
978 boolean done =
false;
985 while (i < data.length && data[i] != 0) {
990 if (i == data.length || data[i+1] == 0) {
994 int len = (i - start);
995 String headerLine =
new String(data, start, len, charset);
999 httpResponse = headerLine;
1001 int nPos = headerLine.indexOf(
':');
1003 String key = headerLine.substring(0, nPos);
1004 String val= headerLine.substring(nPos+1);
1005 httpHeaders.put(key.toLowerCase(), val);
1017 String getDataString() throws TskCoreException, IngestModuleException {
1021 return new String(data);
1024 byte[] getDataBytes() throws TskCoreException, IngestModuleException {
1028 return data.clone();
1031 int getDataLength() {
1035 CacheDataTypeEnum getType() {
1039 CacheAddress getAddress() {
1052 String save() throws TskCoreException, IngestModuleException {
1055 if (address.isInExternalFile()) {
1056 fileName = address.getFilename();
1058 fileName = String.format(
"%s__%08x", address.getFilename(), address.getUint32CacheAddr());
1061 String filePathName = getAbsOutputFolderName() + address.getCachePath() + fileName;
1076 void save(String filePathName)
throws TskCoreException, IngestModuleException {
1084 if (!this.isInExternalFile()) {
1086 try (FileOutputStream stream =
new FileOutputStream(filePathName)) {
1088 }
catch (IOException ex) {
1089 throw new TskCoreException(String.format(
"Failed to write output file %s", filePathName), ex);
1095 public String toString() {
1096 StringBuilder strBuilder =
new StringBuilder();
1097 strBuilder.append(String.format(
"\t\tData type = : %s, Data Len = %d ",
1098 this.type.toString(), this.length ));
1100 if (hasHTTPHeaders()) {
1101 String str = getHTTPHeader(
"content-encoding");
1103 strBuilder.append(String.format(
"\t%s=%s",
"content-encoding", str ));
1107 return strBuilder.toString();
1116 enum EntryStateEnum {
1155 final class CacheEntry {
1158 private static final int MAX_KEY_LEN = 256-24*4;
1160 private final CacheAddress selfAddress;
1161 private final CacheFileCopy cacheFileCopy;
1163 private final long hash;
1164 private final CacheAddress nextAddress;
1165 private final CacheAddress rankingsNodeAddress;
1167 private final int reuseCount;
1168 private final int refetchCount;
1169 private final EntryStateEnum state;
1171 private final long creationTime;
1172 private final int keyLen;
1174 private final CacheAddress longKeyAddresses;
1176 private final int dataSizes[];
1177 private final CacheAddress dataAddresses[];
1178 private List<CacheData> dataList;
1180 private final long flags;
1184 CacheEntry(CacheAddress cacheAdress, CacheFileCopy cacheFileCopy ) {
1185 this.selfAddress = cacheAdress;
1186 this.cacheFileCopy = cacheFileCopy;
1188 ByteBuffer fileROBuf = cacheFileCopy.getByteBuffer();
1190 int entryOffset = DATAFILE_HDR_SIZE + cacheAdress.getStartBlock() * cacheAdress.getBlockSize();
1193 fileROBuf.position(entryOffset);
1195 hash = fileROBuf.getInt() & UINT32_MASK;
1197 long uint32 = fileROBuf.getInt() & UINT32_MASK;
1198 nextAddress = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1200 uint32 = fileROBuf.getInt() & UINT32_MASK;
1201 rankingsNodeAddress = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1203 reuseCount = fileROBuf.getInt();
1204 refetchCount = fileROBuf.getInt();
1206 state = EntryStateEnum.values()[fileROBuf.getInt()];
1207 creationTime = (fileROBuf.getLong() / 1000000) - Long.valueOf(
"11644473600");
1209 keyLen = fileROBuf.getInt();
1211 uint32 = fileROBuf.getInt() & UINT32_MASK;
1212 longKeyAddresses = (uint32 != 0) ?
new CacheAddress(uint32, selfAddress.getCachePath()) : null;
1215 dataSizes=
new int[4];
1216 for (
int i = 0; i < 4; i++) {
1217 dataSizes[i] = fileROBuf.getInt();
1219 dataAddresses =
new CacheAddress[4];
1220 for (
int i = 0; i < 4; i++) {
1221 dataAddresses[i] =
new CacheAddress(fileROBuf.getInt() & UINT32_MASK, selfAddress.getCachePath());
1224 flags = fileROBuf.getInt() & UINT32_MASK;
1226 for (
int i = 0; i < 4; i++) {
1234 if (longKeyAddresses != null) {
1237 CacheData data =
new CacheData(longKeyAddresses, this.keyLen,
true);
1238 key = data.getDataString();
1239 }
catch (TskCoreException | IngestModuleException ex) {
1240 logger.log(Level.SEVERE, String.format(
"Failed to get external key from address %s", longKeyAddresses));
1244 StringBuilder strBuilder =
new StringBuilder(MAX_KEY_LEN);
1246 while (fileROBuf.remaining() > 0 && keyLen < MAX_KEY_LEN) {
1247 char keyChar = (char)fileROBuf.get();
1248 if (keyChar ==
'\0') {
1251 strBuilder.append(keyChar);
1255 key = strBuilder.toString();
1259 public CacheAddress getAddress() {
1263 public long getHash() {
1267 public CacheAddress getNextAddress() {
1271 public int getReuseCount() {
1275 public int getRefetchCount() {
1276 return refetchCount;
1279 public EntryStateEnum getState() {
1283 public long getCreationTime() {
1284 return creationTime;
1287 public long getFlags() {
1291 public String getKey() {
1303 public List<CacheData> getData() throws TskCoreException, IngestModuleException {
1305 if (dataList == null) {
1306 dataList =
new ArrayList<>();
1307 for (
int i = 0; i < 4; i++) {
1308 if (dataSizes[i] > 0) {
1309 CacheData cacheData =
new CacheData(dataAddresses[i], dataSizes[i],
true );
1311 cacheData.extract();
1312 dataList.add(cacheData);
1326 boolean hasHTTPHeaders() {
1327 if ((dataList == null) || dataList.isEmpty()) {
1330 return dataList.get(0).hasHTTPHeaders();
1339 String getHTTPHeader(String key) {
1340 if ((dataList == null) || dataList.isEmpty()) {
1344 return dataList.get(0).getHTTPHeader(key);
1352 String getHTTPHeaders() {
1353 if ((dataList == null) || dataList.isEmpty()) {
1357 return dataList.get(0).getHTTPHeaders();
1368 boolean isBrotliCompressed() {
1370 if (hasHTTPHeaders() ) {
1371 String encodingHeader = getHTTPHeader(
"content-encoding");
1372 if (encodingHeader!= null) {
1373 return encodingHeader.trim().equalsIgnoreCase(
"br");
1381 public String toString() {
1382 StringBuilder sb =
new StringBuilder();
1383 sb.append(String.format(
"Entry = Hash: %08x, State: %s, ReuseCount: %d, RefetchCount: %d",
1384 this.hash,
this.state.toString(), this.reuseCount, this.refetchCount ))
1385 .append(String.format(
"\n\tKey: %s, Keylen: %d",
1386 this.key,
this.keyLen,
this.reuseCount,
this.refetchCount ))
1387 .append(String.format(
"\n\tCreationTime: %s",
1388 TimeUtilities.epochToTime(
this.creationTime) ))
1389 .append(String.format(
"\n\tNext Address: %s",
1390 (nextAddress != null) ? nextAddress.toString() :
"None"));
1392 for (
int i = 0; i < 4; i++) {
1393 if (dataSizes[i] > 0) {
1394 sb.append(String.format(
"\n\tData %d: cache address = %s, Data = %s",
1395 i, dataAddresses[i].toString(),
1397 ? dataList.get(i).toString()
1398 :
"Data not retrived yet."));
1402 return sb.toString();