Removed not used open from browser cache feature.

This commit is contained in:
Jindra Petřík
2023-09-28 19:15:25 +02:00
parent 7e86723b2e
commit 5a2da3a327
29 changed files with 1 additions and 2484 deletions

View File

@@ -1,112 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache;
import com.jpexs.helpers.LimitedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
/**
*
* @author JPEXS
*/
public abstract class CacheEntry {
public abstract String getRequestURL();
public abstract Map<String, String> getResponseHeaders();
public abstract String getStatusLine();
public abstract String getRequestMethod();
public abstract InputStream getResponseRawDataStream();
public InputStream getResponseDataStream() {
String contentLengthStr = getHeader("Content-Length");
int contentLength = -1;
if (contentLengthStr != null) {
try {
contentLength = Integer.parseInt(contentLengthStr);
} catch (NumberFormatException nex) {
}
}
final InputStream rawIs = getResponseRawDataStream();
InputStream is = rawIs;
if (contentLength > -1) {
is = new LimitedInputStream(is, contentLength);
}
String encoding = getHeader("Content-Encoding");
if (encoding != null) {
switch (encoding) {
case "gzip":
try {
is = new GZIPInputStream(is);
} catch (IOException ex) {
is = null;
//ignore
}
break;
case "deflate":
is = new InflaterInputStream(is);
break;
default: //unknown
return null;
}
}
if ("chunked".equals(getHeader("Transfer-Encoding"))) {
is = new ChunkedInputStream(is);
}
return is;
}
@Override
public String toString() {
return getRequestURL();
}
public int getStatusCode() {
String st = getStatusLine();
if (st == null) {
return 0;
}
String[] parts = st.split(" ");
try {
return Integer.parseInt(parts[1]);
} catch (NumberFormatException nfe) {
return 0;
}
}
public String getHeader(String header) {
Map<String, String> m = getResponseHeaders();
if (m == null) {
return null;
}
for (String k : m.keySet()) {
if (k.toLowerCase(Locale.ENGLISH).equals(header.toLowerCase(Locale.ENGLISH))) {
return m.get(k);
}
}
return null;
}
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache;
import java.util.List;
/**
*
* @author JPEXS
*/
public interface CacheImplementation {
public List<CacheEntry> getEntries();
public void refresh();
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache;
import com.jpexs.browsers.cache.chrome.ChromeCache;
import com.jpexs.browsers.cache.firefox.FirefoxCache;
/**
*
* @author JPEXS
*/
public class CacheReader {
public static final String BROWSER_FIREFOX = "firefox";
public static final String BROWSER_CHROME = "chrome";
public static String[] availableBrowsers() {
return new String[]{BROWSER_FIREFOX, BROWSER_CHROME};
}
public static CacheImplementation getBrowserCache(String browser) {
switch (browser) {
case BROWSER_CHROME:
return ChromeCache.getInstance();
case BROWSER_FIREFOX:
return FirefoxCache.getInstance();
default:
throw new IllegalArgumentException("Invalid browser:" + browser);
}
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache;
import java.io.IOException;
import java.io.InputStream;
/**
*
* @author JPEXS
*/
public class ChunkedInputStream extends InputStream {
private final InputStream is;
private int chunkPos = 0;
private int chunkLen = 0;
private boolean end = false;
private boolean first = true;
public ChunkedInputStream(InputStream is) {
this.is = is;
}
@Override
public int read() throws IOException {
if (end) {
return -1;
}
if (chunkPos >= chunkLen) {
if (!first) {
if (is.read() != '\r') {
throw new IOException("Invalid chunk");
}
if (is.read() != '\n') {
throw new IOException("Invalid chunk");
}
}
String lenStr = readLine();
try {
chunkLen = Integer.parseInt(lenStr);
} catch (NumberFormatException nfe) {
throw new IOException("Invalid chunk");
}
if (chunkLen == 0) {
is.read(); // \r
is.read(); // \n
end = true;
return -1;
}
chunkPos = 0;
first = false;
}
chunkPos++;
return is.read();
}
private String readLine() throws IOException {
int i;
boolean inr = false;
StringBuilder ret = new StringBuilder();
while ((i = is.read()) > -1) {
if (inr) {
inr = false;
if (i == '\n') {
break;
} else {
ret.append("\r");
}
}
if (i == '\r') {
inr = true;
continue;
}
ret.append((char) i);
}
if (inr) {
ret.append("\r");
}
return ret.toString();
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class RafInputStream extends InputStream {
private final RandomAccessFile raf;
private long pos = 0;
public RafInputStream(RandomAccessFile raf) {
this.raf = raf;
try {
pos = raf.getFilePointer();
} catch (IOException ex) {
Logger.getLogger(RafInputStream.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public int read() throws IOException {
raf.seek(pos++);
return raf.read();
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import java.io.IOException;
import java.io.InputStream;
/**
*
* @author JPEXS
*/
public class BlockFileHeader {
protected static final int kBlockHeaderSize = 8192; // Two pages: almost 64k entries
private static final int kMaxBlocks = (kBlockHeaderSize - 80) * 8;
private final long magic; // c3 ca 04 c1
private final long version; // 00 00 02 00
private final int this_file;
private final int next_file;
private final int entry_size;
private final int num_entries;
private final int max_entries;
private int[] empty = new int[4];
private int[] hints = new int[4];
private final int updating;
private int[] user = new int[5];
private final long allocation_map[];
public BlockFileHeader(InputStream is) throws IOException {
this.allocation_map = new long[kMaxBlocks / 32];
IndexInputStream iis = new IndexInputStream(is);
magic = iis.readUInt32();
version = iis.readUInt32();
this_file = iis.readInt16();
next_file = iis.readInt16();
entry_size = iis.readInt32();
num_entries = iis.readInt32();
max_entries = iis.readInt32();
empty = new int[4];
for (int i = 0; i < 4; i++) {
empty[i] = iis.readInt32();
}
hints = new int[4];
for (int i = 0; i < 4; i++) {
hints[i] = iis.readInt32();
}
updating = iis.readInt32();
user = new int[5];
for (int i = 0; i < 5; i++) {
user[i] = iis.readInt32();
}
for (int i = 0; i < allocation_map.length; i++) {
allocation_map[i] = iis.readUInt32();
}
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import com.jpexs.browsers.cache.RafInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class CacheAddr {
private static final int EXTERNAL = 0;
private static final int RANKINGS = 1;
private static final int BLOCK_256 = 2;
private static final int BLOCK_1K = 3;
private static final int BLOCK_4K = 4;
private static final int BLOCK_FILES = 5;
private static final int BLOCK_ENTRIES = 6;
private static final int BLOCK_EVICTED = 7;
private static final String[] blockNames = new String[]{"EXTERNAL", "RANKINGS", "BLOCK_256", "BLOCK_1K", "BLOCK_4K", "BLOCK_FILES", "BLOCK_ENTRIES", "BLOCK_EVICTED"};
private static final int[] blockSizes = new int[]{0, 36, 256, 1024, 4096, 8, 104, 48};
private static final long kInitializedMask = 0x80000000L;
private static final long kFileTypeMask = 0x70000000L;
private static final int kFileTypeOffset = 28;
private static final long kReservedBitsMask = 0x0c000000L;
private static final long kNumBlocksMask = 0x03000000L;
private static final int kNumBlocksOffset = 24;
private static final long kFileSelectorMask = 0x00ff0000L;
private static final int FileSelectorOffset = 16;
private static final long kStartBlockMask = 0x0000FFFFL;
private static final long kFileNameMask = 0x0FFFFFFFL;
private final boolean initialized;
private final int fileType;
private int numBlocks;
private int fileSelector;
private int startBlock;
private int fileName;
private final long val;
private final File rootPath;
private final Map<Integer, RandomAccessFile> dataFiles;
private final File externalFilesDir;
public CacheAddr(InputStream is, File rootPath, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
this.dataFiles = dataFiles;
this.rootPath = rootPath;
this.externalFilesDir = externalFilesDir;
IndexInputStream iis = new IndexInputStream(is);
val = iis.readUInt32();
initialized = (val & kInitializedMask) == kInitializedMask;
fileType = (int) ((val & kFileTypeMask) >> kFileTypeOffset);
if (fileType == EXTERNAL) {
fileName = (int) (val & kFileNameMask);
} else {
numBlocks = (int) ((val & kNumBlocksMask) >> kNumBlocksOffset);
fileSelector = (int) ((val & kFileSelectorMask) >> FileSelectorOffset);
startBlock = (int) (val & kStartBlockMask);
}
}
@Override
public String toString() {
String ft = blockNames[fileType];
if (fileType == EXTERNAL) {
return ft + ":" + fileName;
}
if (!initialized) {
return "uninitialized";
}
return ft + ": numBlocks " + numBlocks + " fileSelector " + fileSelector + " startBlock " + startBlock;
}
public InputStream getInputStream() throws IOException {
if (!initialized) {
return null;
}
switch (fileType) {
case EXTERNAL:
String fileNameStr = Long.toHexString(fileName);
while (fileNameStr.length() < 6) {
fileNameStr = "0" + fileNameStr;
}
fileNameStr = "f_" + fileNameStr;
return new RafInputStream(new RandomAccessFile(new File(externalFilesDir, fileNameStr), "r"));
case BLOCK_1K:
case BLOCK_256:
case BLOCK_4K:
RandomAccessFile raf;
if (dataFiles.containsKey(fileSelector)) {
raf = dataFiles.get(fileSelector);
} else {
raf = new RandomAccessFile(rootPath + "\\data_" + fileSelector, "r");
dataFiles.put(fileSelector, raf);
}
raf.seek(BlockFileHeader.kBlockHeaderSize + startBlock * blockSizes[fileType]);
return new RafInputStream(raf);
}
return null;
}
}

View File

@@ -1,207 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import com.jpexs.browsers.cache.CacheEntry;
import com.jpexs.browsers.cache.CacheImplementation;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class ChromeCache implements CacheImplementation {
private static volatile ChromeCache instance;
private File tempDir;
private List<File> dataFiles;
private File indexFile;
private ChromeCache() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
free();
}
});
}
public static ChromeCache getInstance() {
if (instance == null) {
synchronized (ChromeCache.class) {
if (instance == null) {
instance = new ChromeCache();
}
}
}
return instance;
}
private boolean loaded = false;
private Index index;
@Override
public List<CacheEntry> getEntries() {
if (!loaded) {
refresh();
}
if (!loaded) {
return null;
}
List<CacheEntry> ret = new ArrayList<>();
try {
List<EntryStore> entries = index.getEntries();
for (EntryStore en : entries) {
if (en.state == EntryStore.ENTRY_NORMAL) {
String key = en.getKey();
if (key != null && !key.trim().isEmpty()) {
ret.add(en);
}
}
}
} catch (IOException ex) {
Logger.getLogger(ChromeCache.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
return ret;
}
@Override
public void refresh() {
free();
File cacheDir = null;
try {
cacheDir = getCacheDirectory();
} catch (IOException ex) {
Logger.getLogger(ChromeCache.class.getName()).log(Level.SEVERE, null, ex);
}
if (cacheDir == null) {
return;
}
File systemTempDir = new File(System.getProperty("java.io.tmpdir"));
File originalIndexFile = new File(cacheDir + File.separator + "index");
tempDir = new File(systemTempDir, "cacheView" + System.identityHashCode(this));
tempDir.deleteOnExit();
tempDir.mkdir();
indexFile = new File(tempDir, "index");
indexFile.deleteOnExit();
try {
Files.copy(originalIndexFile.toPath(), indexFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Logger.getLogger(ChromeCache.class.getName()).log(Level.SEVERE, null, ex);
return;
}
File originalDataFile;
dataFiles = new ArrayList<>();
for (int i = 0; (originalDataFile = new File(cacheDir, "data_" + i)).exists(); i++) {
File dataFile = new File(tempDir, "data_" + i);
dataFile.deleteOnExit();
dataFiles.add(dataFile);
try {
Files.copy(originalDataFile.toPath(), dataFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Logger.getLogger(ChromeCache.class.getName()).log(Level.SEVERE, null, ex);
return;
}
}
try {
index = new Index(indexFile, cacheDir);
} catch (IOException ex) {
Logger.getLogger(ChromeCache.class.getName()).log(Level.SEVERE, null, ex);
}
loaded = true;
}
private enum OSId {
WINDOWS, OSX, UNIX
}
private static OSId getOSId() {
PrivilegedAction<String> doGetOSName = new PrivilegedAction<String>() {
@Override
public String run() {
return System.getProperty("os.name");
}
};
OSId id = OSId.UNIX;
String osName = AccessController.doPrivileged(doGetOSName);
if (osName != null) {
if (osName.toLowerCase(Locale.ENGLISH).startsWith("mac os x")) {
id = OSId.OSX;
} else if (osName.contains("Windows")) {
id = OSId.WINDOWS;
}
}
return id;
}
public static File getCacheDirectory() throws IOException {
String userHome = null;
File ret = null;
try {
userHome = System.getProperty("user.home");
} catch (SecurityException ignore) {
}
if (userHome != null) {
OSId osId = getOSId();
if (osId == OSId.WINDOWS) {
ret = new File(userHome + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cache\\");
if (!ret.exists()) {
ret = new File(userHome + "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default\\Cache");
}
} else if (osId == OSId.OSX) {
ret = new File(userHome + "/Library/Caches/Google/Chrome/Default/Cache");
} else {
ret = new File(userHome + "/.config/google-chrome/Default/Application Cache/Cache/");
if (!ret.exists()) {
ret = new File(userHome + "/.cache/chromium/Default/Cache");
}
}
}
if ((ret != null) && !ret.exists()) {
return null;
}
return ret;
}
private void free() {
if (loaded) {
index.free();
indexFile.delete();
for (File d : dataFiles) {
d.delete();
}
tempDir.delete();
}
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
/**
*
* @author JPEXS
*/
public class EntryFlags {
public static final int PARENT_ENTRY = 1;
public static final int CHILD_ENTRY = 1 << 1;
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
/**
*
* @author JPEXS
*/
public class EntryState {
public static final int ENTRY_NORMAL = 0;
public static final int ENTRY_EVICTED = 1;
public static final int ENTRY_DOOMED = 2;
}

View File

@@ -1,174 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import com.jpexs.browsers.cache.CacheEntry;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class EntryStore extends CacheEntry {
public static final int ENTRY_NORMAL = 0;
public static final int ENTRY_EVICTED = 1; // The entry was recently evicted from the cache.
public static final int ENTRY_DOOMED = 2; // The entry was doomed
public static final int PARENT_ENTRY = 1; // This entry has children (sparse) entries.
public static final int CHILD_ENTRY = 1 << 1;
public long hash; // Full hash of the key.
public CacheAddr next; // Next entry with the same hash or bucket.
public CacheAddr rankings_node; // Rankings node for this entry.
public int reuse_count; // How often is this entry used.
public int refetch_count; // How often is this fetched from the net.
public int state; // Current state.
public long creation_time;
public int key_len;
public CacheAddr long_key; // Optional address of a long key.
public int[] data_size = new int[4]; // We can store up to 4 data streams for each
public CacheAddr[] data_addr = new CacheAddr[4]; // entry.
public long flags; // Any combination of EntryFlags.
public int[] pad = new int[4];
public long self_hash; // The hash of EntryStore up to this point.
public byte[] key = new byte[256 - 24 * 4]; // null terminated
public EntryStore(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
IndexInputStream iis = new IndexInputStream(is);
hash = iis.readUInt32();
next = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
rankings_node = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
reuse_count = iis.readInt32();
refetch_count = iis.readInt32();
state = iis.readInt32();
creation_time = iis.readUInt64();
key_len = iis.readInt32();
long_key = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
data_size = new int[4];
for (int i = 0; i < 4; i++) {
data_size[i] = iis.readInt32();
}
data_addr = new CacheAddr[4];
for (int i = 0; i < 4; i++) {
data_addr[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
}
flags = iis.readUInt32();
pad = new int[4];
for (int i = 0; i < 4; i++) {
pad[i] = iis.readInt32();
}
self_hash = iis.readUInt32();
key = new byte[256 - 24 * 4];
if (iis.read(key) != key.length) {
throw new IOException();
}
}
public HttpResponseInfo getResponseInfo() {
try {
InputStream is = data_addr[0].getInputStream();
if (is == null) {
return null;
}
return new HttpResponseInfo(is);
} catch (IOException ex) {
Logger.getLogger(EntryStore.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
public int getResponseDataSize() {
return data_size[1];
}
@Override
public InputStream getResponseRawDataStream() {
try {
return data_addr[1].getInputStream();
} catch (IOException ex) {
Logger.getLogger(EntryStore.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public String getKey() {
if (key_len < 0) {
return null;
}
return new String(key, 0, key_len > key.length ? key.length : key_len);
}
@Override
public String getRequestURL() {
return getKey();
}
@Override
public Map<String, String> getResponseHeaders() {
HttpResponseInfo ri = getResponseInfo();
if (ri == null) {
return new HashMap<>();
}
List<String> headers = ri.headers;
Map<String, String> ret = new HashMap<>();
for (int h = 1; h < headers.size(); h++) {
String hs = headers.get(h);
if (hs.contains(":")) {
String[] hp = hs.split(":");
ret.put(hp[0].trim(), hp[1].trim());
}
}
return ret;
}
@Override
public String getStatusLine() {
HttpResponseInfo ri = getResponseInfo();
return ri.headers.get(0);
}
@Override
public String getRequestMethod() {
return "GET";
}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
*
* @author JPEXS
*/
public class HttpResponseInfo {
public long flags;
public int version;
public long request_time;
public long response_time;
public long payload_size;
public List<String> headers;
// The version of the response info used when persisting response info.
public static final int RESPONSE_INFO_VERSION = 3;
// The minimum version supported for deserializing response info.
public static final int RESPONSE_INFO_MINIMUM_VERSION = 1;
// We reserve up to 8 bits for the version number.
public static final int RESPONSE_INFO_VERSION_MASK = 0xFF;
// This bit is set if the response info has a cert at the end.
// Version 1 serialized only the end-entity certificate, while subsequent
// versions include the available certificate chain.
public static final int RESPONSE_INFO_HAS_CERT = 1 << 8;
// This bit is set if the response info has a security-bits field (security
// strength, in bits, of the SSL connection) at the end.
public static final int RESPONSE_INFO_HAS_SECURITY_BITS = 1 << 9;
// This bit is set if the response info has a cert status at the end.
public static final int RESPONSE_INFO_HAS_CERT_STATUS = 1 << 10;
// This bit is set if the response info has vary header data.
public static final int RESPONSE_INFO_HAS_VARY_DATA = 1 << 11;
// This bit is set if the request was cancelled before completion.
public static final int RESPONSE_INFO_TRUNCATED = 1 << 12;
// This bit is set if the response was received via SPDY.
public static final int RESPONSE_INFO_WAS_SPDY = 1 << 13;
// This bit is set if the request has NPN negotiated.
public static final int RESPONSE_INFO_WAS_NPN = 1 << 14;
// This bit is set if the request was fetched via an explicit proxy.
public static final int RESPONSE_INFO_WAS_PROXY = 1 << 15;
// This bit is set if the response info has an SSL connection status field.
// This contains the ciphersuite used to fetch the resource as well as the
// protocol version, compression method and whether SSLv3 fallback was used.
public static final int RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS = 1 << 16;
// This bit is set if the response info has protocol version.
public static final int RESPONSE_INFO_HAS_NPN_NEGOTIATED_PROTOCOL = 1 << 17;
// This bit is set if the response info has connection info.
public static final int RESPONSE_INFO_HAS_CONNECTION_INFO = 1 << 18;
// This bit is set if the request has http authentication.
public static final int RESPONSE_INFO_USE_HTTP_AUTHENTICATION = 1 << 19;
public String getHeaderValue(String header) {
for (String h : headers) {
if (h.contains(":")) {
String[] keyval = h.split(":");
String key = keyval[0].trim().toLowerCase(Locale.ENGLISH);
String val = keyval[1].trim();
if (header.toLowerCase(Locale.ENGLISH).equals(key)) {
return val;
}
}
}
return null;
}
public HttpResponseInfo(InputStream is) throws IOException {
IndexInputStream iis = new IndexInputStream(is);
payload_size = iis.readUInt32();
flags = iis.readInt();
version = (int) (flags & RESPONSE_INFO_VERSION_MASK);
if (version < RESPONSE_INFO_MINIMUM_VERSION || version > RESPONSE_INFO_VERSION) {
throw new RuntimeException("unexpected response info version: " + version);
}
request_time = iis.readInt64();
response_time = iis.readInt64();
String headersStr = iis.readString();
headers = new ArrayList<>();
int nulpos;
while ((nulpos = headersStr.indexOf(0)) > 0) {
String h = headersStr.substring(0, nulpos);
headersStr = headersStr.substring(nulpos + 1);
headers.add(h);
}
//TODO: Read SSL info
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class Index {
IndexHeader header;
CacheAddr table[];
public static final int kIndexTablesize = 0x10000;
public File rootDir;
private final Map<Integer, RandomAccessFile> dataFiles;
private final File externalFilesDir;
public void free() {
for (RandomAccessFile r : dataFiles.values()) {
try {
r.close();
} catch (IOException ex) {
}
}
}
public List<EntryStore> getEntries() throws IOException {
List<EntryStore> ret = new ArrayList<>();
for (CacheAddr ca : table) {
InputStream is = ca.getInputStream();
if (is != null) {
EntryStore es = new EntryStore(is, rootDir, dataFiles, externalFilesDir);
ret.add(es);
}
}
return ret;
}
public Index(File file, File externalFilesDir) throws IOException {
dataFiles = new HashMap<>();
this.externalFilesDir = externalFilesDir;
try (FileInputStream is = new FileInputStream(file)) {
rootDir = file.getParentFile();
header = new IndexHeader(is, rootDir, dataFiles, externalFilesDir);
int tsize = kIndexTablesize;
if (header.table_len > 0) {
tsize = header.table_len;
}
table = new CacheAddr[tsize];
for (int i = 0; i < tsize; i++) {
table[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
}
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class IndexHeader {
long magic; //c3 ca 03 c1
long version; //01 00 02 00
int num_entries;
int num_bytes;
int last_file;
int this_id;
CacheAddr stats;
int table_len;
int crash;
int experiment;
long create_time;
int[] pad = new int[52];
LruData lru;
public IndexHeader(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
IndexInputStream iis = new IndexInputStream(is);
magic = iis.readUInt32();
version = iis.readUInt32();
num_entries = iis.readInt32();
num_bytes = iis.readInt32();
last_file = iis.readInt32();
this_id = iis.readInt32();
stats = new CacheAddr(iis, rootDir, dataFiles, externalFilesDir);
table_len = iis.readInt32();
crash = iis.readInt32();
experiment = iis.readInt32();
create_time = iis.readUInt64();
pad = new int[52];
for (int i = 0; i < 52; i++) {
pad[i] = iis.readInt32();
}
lru = new LruData(is, rootDir, dataFiles, externalFilesDir);
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import java.io.IOException;
import java.io.InputStream;
/**
*
* @author JPEXS
*/
public class IndexInputStream extends InputStream {
private final InputStream is;
public long pos = 0;
public long getPos() {
return pos;
}
public IndexInputStream(InputStream is) {
this.is = is;
}
@Override
public int read() throws IOException {
int r = is.read();
//System.out.print("$"+Integer.toHexString(r));
pos++;
return r;
}
public long readUInt32() throws IOException {
long r = (((long) read()) + ((long) (read() << 8)) + ((long) (read() << 16)) + ((long) (read() << 24))) & 0xffffffffL;
//System.out.println("");
return r;
}
public long readUInt64() throws IOException {
return readUInt32() + (readUInt32() << 32);
}
public long readInt64() throws IOException {
return readUInt64(); //FIXME
}
public int readInt32() throws IOException {
return (int) readUInt32();
}
public int readInt16() throws IOException {
return (short) ((int) read() + (int) (read() << 8));
}
public long readInt() throws IOException {
return readInt32();
}
public String readString() throws IOException {
int len = (int) readInt();
byte[] data = new byte[len];
if (read(data) != len) {
throw new IOException();
}
return new String(data);
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class LruData {
int[] pad1 = new int[2];
int filled;
int[] sizes = new int[5];
CacheAddr[] heads = new CacheAddr[5];
CacheAddr[] tails = new CacheAddr[5];
CacheAddr transaction;
int operation;
int operation_list;
int[] pad2 = new int[7];
public LruData(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
IndexInputStream iis = new IndexInputStream(is);
pad1 = new int[2];
pad1[0] = iis.readInt32();
pad1[1] = iis.readInt32();
filled = iis.readInt32();
sizes = new int[5];
for (int i = 0; i < 5; i++) {
sizes[i] = iis.readInt32();
}
sizes = new int[5];
for (int i = 0; i < 5; i++) {
sizes[i] = iis.readInt32();
}
heads = new CacheAddr[5];
for (int i = 0; i < 5; i++) {
heads[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
}
tails = new CacheAddr[5];
for (int i = 0; i < 5; i++) {
tails[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
}
transaction = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
operation = iis.readInt32();
operation_list = iis.readInt32();
pad2 = new int[7];
for (int i = 0; i < 7; i++) {
pad2[i] = iis.readInt32();
}
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.chrome;
/**
*
* @author JPEXS
*/
public class RankingsNode {
public long last_used;
public long last_modified;
CacheAddr next;
CacheAddr prev;
CacheAddr contents;
int dirty;
long self_hash;
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.firefox;
import java.io.IOException;
import java.io.InputStream;
/**
*
* @author JPEXS
*/
public class CacheInputStream extends InputStream {
private final InputStream is;
@Override
public int available() throws IOException {
return is.available();
}
public CacheInputStream(InputStream is) {
this.is = is;
}
@Override
public int read() throws IOException {
return is.read();
}
public long readInt32() throws IOException {
return (read() << 24) + (read() << 16) + (read() << 8) + read();
}
public int readInt16() throws IOException {
return (read() << 8) + read();
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.firefox;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class CacheMap {
public long version;
public long datasize;
public long entryCount;
public long dirtyFlag;
public long recordCount;
public long evictionRank[];
public long bucketUsage[];
public List<MapBucket> mapBuckets;
public CacheMap(File file) throws IOException {
File cacheDir = file.getParentFile();
CacheInputStream cis = new CacheInputStream(new FileInputStream(file));
version = cis.readInt32();
datasize = cis.readInt32();
entryCount = cis.readInt32();
dirtyFlag = cis.readInt32();
recordCount = cis.readInt32();
evictionRank = new long[32];
for (int i = 0; i < evictionRank.length; i++) {
evictionRank[i] = cis.readInt32();
}
bucketUsage = new long[32];
for (int i = 0; i < bucketUsage.length; i++) {
bucketUsage[i] = cis.readInt32();
}
mapBuckets = new ArrayList<>();
Map<Integer, RandomAccessFile> cacheFiles = new HashMap<>();
for (int i = 1; i <= 3; i++) {
cacheFiles.put(i, new RandomAccessFile(new File(cacheDir, "_CACHE_00" + i + "_"), "r"));
}
while (cis.available() > 0) {
MapBucket mb = new MapBucket(cis, cacheDir, cacheFiles);
if (mb.hash == 0) {
continue;
}
mapBuckets.add(mb);
}
}
}

View File

@@ -1,168 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.firefox;
import com.jpexs.browsers.cache.CacheEntry;
import com.jpexs.browsers.cache.CacheImplementation;
import java.io.File;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class FirefoxCache implements CacheImplementation {
private static volatile FirefoxCache instance;
private FirefoxCache() {
}
public static FirefoxCache getInstance() {
if (instance == null) {
synchronized (FirefoxCache.class) {
if (instance == null) {
instance = new FirefoxCache();
}
}
}
return instance;
}
private boolean loaded = false;
private CacheMap map;
@Override
public void refresh() {
File dir = getCacheDirectory();
if (dir == null) {
return;
}
File cacheMapFile = new File(dir, "_CACHE_MAP_");
try {
map = new CacheMap(cacheMapFile);
} catch (IOException ex) {
Logger.getLogger(FirefoxCache.class.getName()).log(Level.SEVERE, null, ex);
}
loaded = true;
}
@Override
public List<CacheEntry> getEntries() {
if (!loaded) {
refresh();
}
if (map == null) {
return null;
}
List<CacheEntry> ret = new ArrayList<>();
ret.addAll(map.mapBuckets);
return ret;
}
private enum OSId {
WINDOWS, OSX, UNIX
}
private static OSId getOSId() {
PrivilegedAction<String> doGetOSName = new PrivilegedAction<String>() {
@Override
public String run() {
return System.getProperty("os.name");
}
};
OSId id = OSId.UNIX;
String osName = AccessController.doPrivileged(doGetOSName);
if (osName != null) {
if (osName.toLowerCase(Locale.ENGLISH).startsWith("mac os x")) {
id = OSId.OSX;
} else if (osName.contains("Windows")) {
id = OSId.WINDOWS;
}
}
return id;
}
public static File getProfileDirectory() {
File profilesDir = getProfilesDirectory();
if (profilesDir == null) {
return null;
}
File[] profiles = profilesDir.listFiles();
File profileDir = null;
for (File f : profiles) {
if (f.isDirectory()) {
if (f.getName().matches("[a-z0-9]+\\.default")) {
profileDir = f;
break;
}
}
}
return profileDir;
}
public static File getCacheDirectory() {
File profileDir = getProfileDirectory();
File cacheDir = null;
if (profileDir != null) {
cacheDir = new File(profileDir, "Cache");
}
if (cacheDir == null) {
return null;
}
if (!cacheDir.exists()) {
return null;
}
return cacheDir;
}
public static File getProfilesDirectory() {
String userHome = null;
File profilesDir = null;
try {
userHome = System.getProperty("user.home");
} catch (SecurityException ignore) {
}
if (userHome != null) {
OSId osId = getOSId();
if (osId == OSId.WINDOWS) {
profilesDir = new File(userHome + "\\AppData\\Local\\Mozilla\\Firefox\\Profiles");
if (!profilesDir.exists()) {
profilesDir = new File(userHome + "\\Local Settings\\Application Data\\Mozilla\\Firefox\\Profiles");
}
} else if (osId == OSId.OSX) {
profilesDir = new File(userHome + "/Library/Caches/Firefox/Profiles");
} else {
profilesDir = new File(userHome + "/.mozilla/firefox");
}
}
if ((profilesDir == null) || !profilesDir.exists()) {
return null;
}
return profilesDir;
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.firefox;
/**
*
* @author JPEXS
*/
public class IncompatibleVersionException extends Exception {
public IncompatibleVersionException(int version) {
super("Incompatible version: " + version);
}
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.firefox;
import com.jpexs.browsers.cache.RafInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class Location {
public int locationSelector;
public int extraBlocks;
public long blockNumber;
public int fileGeneration;
public int fileSize;
public boolean isMetadata;
public long hash;
private final File rootDir;
public static final long eReservedMask = 0x4C000000L;
public static final long eLocationSelectorMask = 0x30000000L;
public static final int eLocationSelectorOffset = 28;
public static final long eExtraBlocksMask = 0x03000000L;
public static final int eExtraBlocksOffset = 24;
public static final long eBlockNumberMask = 0x00FFFFFFL;
public static final long eFileGenerationMask = 0x000000FFL;
public static final long eFileSizeMask = 0x00FFFF00L;
public static final int eFileSizeOffset = 8;
public static final long eFileReservedMask = 0x4F000000L;
public static int size_shift(int idx) {
return (2 * ((idx) - 1));
}
public static int bitmapSizeForIndex(int idx) {
return ((idx > 0) ? (131072 >> size_shift(idx)) : 0);
}
public static int blockSizeForIndex(int idx) {
return ((idx > 0) ? (256 << size_shift(idx)) : 0);
}
/*
#define BLOCK_SIZE_FOR_INDEX(idx) ((idx) ? (256 << SIZE_SHIFT(idx)) : 0)
#define BITMAP_SIZE_FOR_INDEX(idx) ((idx) ? (131072 >> SIZE_SHIFT(idx)) : 0)
*/
private final Map<Integer, RandomAccessFile> dataFiles;
public InputStream getInputStream() throws IOException {
String fileName = getFileName();
if (locationSelector > 0) {
RandomAccessFile raf = dataFiles.get(locationSelector);
raf.seek(bitmapSizeForIndex(locationSelector) / 8 + blockSizeForIndex(locationSelector) * blockNumber);
return new RafInputStream(raf);
}
return new FileInputStream(new File(rootDir, fileName));
}
public Location(long val, boolean isMetadata, long hash, File rootDir, Map<Integer, RandomAccessFile> dataFiles) {
this.hash = hash;
this.dataFiles = dataFiles;
this.rootDir = rootDir;
this.isMetadata = isMetadata;
locationSelector = (int) ((val & eLocationSelectorMask) >> eLocationSelectorOffset);
if (locationSelector > 0) {
extraBlocks = (int) ((val & eExtraBlocksMask) >> eExtraBlocksOffset);
blockNumber = (int) (val & eBlockNumberMask);
} else {
fileSize = (int) ((val & eFileSizeMask) >> eFileSizeOffset);
fileGeneration = (int) (val & eFileGenerationMask);
}
}
public String getFileName() {
if (locationSelector > 0) {
return "_CACHE_00" + locationSelector + "_";
}
String hashHex = Long.toHexString(hash & 0xffffffffL).toUpperCase();
while (hashHex.length() < 8) {
hashHex = "0" + hashHex;
}
String genHex = Integer.toHexString(fileGeneration);
while (genHex.length() < 2) {
genHex = "0" + genHex;
}
return hashHex.charAt(0) + File.separator + hashHex.substring(1, 1 + 2) + File.separator + hashHex.substring(3) + (isMetadata ? "m" : "d") + genHex;
}
@Override
public String toString() {
if (locationSelector > 0) {
return getFileName() + " block " + blockNumber + " extraBlocks " + extraBlocks;
}
return getFileName();
}
}

View File

@@ -1,132 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.firefox;
import com.jpexs.browsers.cache.CacheEntry;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class MapBucket extends CacheEntry {
public long hash;
public long enviction;
public Location dataLocation;
public Location metadataLocation;
private MetaData metadata;
public MapBucket(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles) throws IOException {
CacheInputStream cis = new CacheInputStream(is);
hash = cis.readInt32();
enviction = cis.readInt32();
dataLocation = new Location(cis.readInt32(), false, hash, rootDir, dataFiles);
metadataLocation = new Location(cis.readInt32(), true, hash, rootDir, dataFiles);
}
public InputStream getMetaDataStream() throws IOException {
return metadataLocation.getInputStream();
}
public MetaData getMetaData() {
if (metadata == null) {
try {
metadata = new MetaData(getMetaDataStream());
} catch (IncompatibleVersionException ie) {
} catch (IOException ex) {
Logger.getLogger(MapBucket.class.getName()).log(Level.SEVERE, null, ex);
}
}
return metadata;
}
@Override
public String getRequestURL() {
MetaData m = getMetaData();
if (m == null) {
return null;
}
String req = m.request;
if (req == null) {
return null;
}
if (req.startsWith("HTTP:")) {
req = req.substring("HTTP:".length());
}
return req;
}
@Override
public Map<String, String> getResponseHeaders() {
MetaData m = getMetaData();
if (m == null) {
return null;
}
String responseHead = m.response.get("response-head");
if (responseHead == null) {
return null;
}
String[] headers = responseHead.split("\r\n");
Map<String, String> ret = new HashMap<>();
for (int h = 1; h < headers.length; h++) {
String hs = headers[h];
if (hs.contains(":")) {
String[] hp = hs.split(":");
ret.put(hp[0].trim(), hp[1].trim());
}
}
return ret;
}
@Override
public String getStatusLine() {
MetaData m = getMetaData();
if (m == null) {
return null;
}
String responseHead = m.response.get("response-head");
String[] headers = responseHead.split("\r\n");
return headers[0];
}
@Override
public String getRequestMethod() {
return "GET"; //No POST caching in Firefox
}
@Override
public InputStream getResponseRawDataStream() {
try {
return dataLocation.getInputStream();
} catch (IOException ex) {
Logger.getLogger(MapBucket.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.browsers.cache.firefox;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class MetaData {
public int majorVersion;
public int minorVersion;
public long location;
public long fetchCount;
public long firstFetchTime;
public long lastFetchTime;
public long expireTime;
public long dataSize;
public long requestSize;
public long infoSize;
public String request;
public Map<String, String> response;
public MetaData(InputStream is) throws IOException, IncompatibleVersionException {
CacheInputStream cis = new CacheInputStream(is);
majorVersion = cis.readInt16();
if (majorVersion != 1) {
throw new IncompatibleVersionException(majorVersion);
}
minorVersion = cis.readInt16();
location = cis.readInt32();
fetchCount = cis.readInt32();
firstFetchTime = cis.readInt32();
lastFetchTime = cis.readInt32();
expireTime = cis.readInt32();
dataSize = cis.readInt32();
requestSize = cis.readInt32();
infoSize = cis.readInt32();
byte[] req = new byte[(int) requestSize];
if (cis.read(req) != req.length) {
throw new IOException();
}
request = new String(req, 0, (int) requestSize - 1/*Ends with char 0*/);
byte[] res = new byte[(int) infoSize];
cis.read(res);
String responseStr = new String(res);
int nulpos;
boolean inKey = true;
String key = null;
response = new HashMap<>();
while ((nulpos = responseStr.indexOf(0)) > 0) {
String v = responseStr.substring(0, nulpos);
responseStr = responseStr.substring(nulpos + 1);
if (inKey) {
key = v;
} else {
response.put(key, v);
}
inKey = !inKey;
}
}
}

View File

@@ -1,281 +0,0 @@
/*
* Copyright (C) 2010-2023 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui;
import com.jpexs.browsers.cache.CacheEntry;
import com.jpexs.browsers.cache.CacheImplementation;
import com.jpexs.browsers.cache.CacheReader;
import com.jpexs.decompiler.flash.OpenableSourceInfo;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.ReReadableInputStream;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileFilter;
/**
*
* @author JPEXS
*/
public class LoadFromCacheFrame extends AppFrame {
private final JList<CacheEntry> list;
private final JTextField searchField;
private List<CacheImplementation> caches;
private List<CacheEntry> entries;
private final JProgressBar progressBar;
private final JButton saveButton;
private final JButton refreshButton;
private final JButton openButton;
public LoadFromCacheFrame() {
setSize(900, 600);
View.setWindowIcon(this);
View.centerScreen(this);
setTitle(translate("dialog.title"));
setDefaultCloseOperation(HIDE_ON_CLOSE);
Container cnt = getContentPane();
list = new JList<>();
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() > 1) {
openSWF();
}
}
});
searchField = new JTextField();
searchField.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
filter();
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
});
cnt.setLayout(new BorderLayout());
cnt.add(searchField, BorderLayout.NORTH);
cnt.add(new FasterScrollPane(list), BorderLayout.CENTER);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
JPanel buttonsPanel = new JPanel(new FlowLayout());
openButton = new JButton(translate("button.open"));
openButton.addActionListener(this::openButtonActionPerformed);
buttonsPanel.add(openButton);
saveButton = new JButton(translate("button.save"));
saveButton.addActionListener(this::saveButtonActionPerformed);
buttonsPanel.add(saveButton);
refreshButton = new JButton(translate("button.refresh"));
refreshButton.addActionListener(this::refreshButtonActionPerformed);
buttonsPanel.add(refreshButton);
JPanel browsersPanel = new JPanel(new FlowLayout());
browsersPanel.add(new JLabel(translate("supported.browsers")));
JLabel chromeLabel = new JLabel("Google Chrome", View.getIcon("chrome16"), JLabel.CENTER);
JLabel firefoxLabel = new JLabel("Mozilla Firefox*", View.getIcon("firefox16"), JLabel.CENTER);
browsersPanel.add(chromeLabel);
browsersPanel.add(firefoxLabel);
buttonsPanel.setAlignmentX(0.5f);
progressBar = new JProgressBar();
progressBar.setAlignmentX(0.5f);
progressBar.setIndeterminate(true);
bottomPanel.add(progressBar);
bottomPanel.add(buttonsPanel);
browsersPanel.setAlignmentX(0.5f);
bottomPanel.add(browsersPanel);
JLabel infoLabel = new JLabel(translate("info.closed"));
infoLabel.setAlignmentX(0.5f);
bottomPanel.add(infoLabel);
cnt.add(bottomPanel, BorderLayout.SOUTH);
progressBar.setVisible(false);
openButton.setEnabled(false);
saveButton.setEnabled(false);
List<Image> images = new ArrayList<>();
images.add(View.loadImage("loadcache16"));
images.add(View.loadImage("loadcache32"));
setIconImages(images);
refresh();
}
private void refresh() {
progressBar.setVisible(true);
openButton.setEnabled(false);
saveButton.setEnabled(false);
refreshButton.setEnabled(false);
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
if (caches == null) {
caches = new ArrayList<>();
for (String b : CacheReader.availableBrowsers()) {
caches.add(CacheReader.getBrowserCache(b));
}
} else {
for (CacheImplementation c : caches) {
c.refresh();
}
}
entries = new ArrayList<>();
for (CacheImplementation c : caches) {
List<CacheEntry> list = c.getEntries();
if (list != null) {
for (CacheEntry en : c.getEntries()) {
String contentType = en.getHeader("Content-Type");
if ("application/x-shockwave-flash".equals(contentType)) {
entries.add(en);
}
}
}
}
filter();
return null;
}
@Override
protected void done() {
openButton.setEnabled(true);
saveButton.setEnabled(true);
refreshButton.setEnabled(true);
progressBar.setVisible(false);
}
}.execute();
}
private void filter() {
String search = searchField.getText();
List<CacheEntry> filtered = new ArrayList<>();
for (CacheEntry en : entries) {
if (search.isEmpty() || en.getRequestURL().contains(search)) {
filtered.add(en);
}
}
list.setListData(new Vector<>(filtered));
}
private static String entryToFileName(CacheEntry en) {
String ret = en.getRequestURL();
//Strip parameters
if (ret.contains("?")) {
ret = ret.substring(0, ret.indexOf('?'));
}
//Strip path
if (ret.contains("/")) {
ret = ret.substring(ret.lastIndexOf('/') + 1);
}
return ret;
}
private void openSWF() {
CacheEntry en = list.getSelectedValue();
if (en != null) {
ReReadableInputStream str = new ReReadableInputStream(en.getResponseDataStream());
OpenableSourceInfo sourceInfo = new OpenableSourceInfo(str, null, entryToFileName(en));
Main.openFile(sourceInfo);
}
}
private void refreshButtonActionPerformed(ActionEvent evt) {
refresh();
}
private void openButtonActionPerformed(ActionEvent evt) {
openSWF();
}
private void saveButtonActionPerformed(ActionEvent evt) {
List<CacheEntry> selected = list.getSelectedValuesList();
if (!selected.isEmpty()) {
JFileChooser fc = new JFileChooser();
fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get()));
if (selected.size() > 1) {
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
} else {
fc.setSelectedFile(new File(Configuration.lastSaveDir.get(), entryToFileName(selected.get(0))));
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return (f.getName().endsWith(".swf")) || (f.isDirectory());
}
@Override
public String getDescription() {
return AppStrings.translate("filter.swf");
}
});
}
fc.setAcceptAllFileFilterUsed(false);
if (fc.showSaveDialog(LoadFromCacheFrame.this) == JFileChooser.APPROVE_OPTION) {
File file = Helper.fixDialogFile(fc.getSelectedFile());
try {
if (selected.size() == 1) {
Helper.saveStream(selected.get(0).getResponseDataStream(), file);
} else {
for (CacheEntry sel : selected) {
Helper.saveStream(sel.getResponseDataStream(), new File(file, entryToFileName(sel)));
}
}
Configuration.lastSaveDir.set(file.getParentFile().getAbsolutePath());
} catch (IOException ex) {
ViewMessages.showMessageDialog(this, translate("error.file.write"));
}
}
}
}
}

View File

@@ -179,8 +179,6 @@ public class Main {
private static LoadFromMemoryFrame loadFromMemoryFrame;
private static LoadFromCacheFrame loadFromCacheFrame;
private static final Logger logger = Logger.getLogger(Main.class.getName());
public static DebugLogDialog debugDialog;
@@ -754,13 +752,6 @@ public class Main {
return null;
}
public static void loadFromCache() {
if (loadFromCacheFrame == null) {
loadFromCacheFrame = new LoadFromCacheFrame();
}
loadFromCacheFrame.setVisible(true);
}
public static void loadFromMemory() {
if (loadFromMemoryFrame == null) {
loadFromMemoryFrame = new LoadFromMemoryFrame(mainFrame);
@@ -1710,11 +1701,6 @@ public class Main {
loadFromMemoryFrame.dispose();
loadFromMemoryFrame = null;
}
if (loadFromCacheFrame != null) {
loadFromCacheFrame.setVisible(false);
loadFromCacheFrame.dispose();
loadFromCacheFrame = null;
}
if (mainFrame != null) {
mainFrame.setVisible(false);
mainFrame.getPanel().closeAll(false, false);

View File

@@ -792,10 +792,6 @@ public abstract class MainFrameMenu implements MenuBuilder {
Main.loadFromMemory();
}
protected void searchCacheActionPerformed(ActionEvent evt) {
Main.loadFromCache();
}
protected void gotoDucumentClassOnStartupActionPerformed(ActionEvent evt) {
AbstractButton button = (AbstractButton) evt.getSource();
boolean selected = button.isSelected();

View File

@@ -25,7 +25,6 @@ import com.jpexs.decompiler.flash.gui.ExportDialog;
import com.jpexs.decompiler.flash.gui.FontEmbedDialog;
import com.jpexs.decompiler.flash.gui.FontPreviewDialog;
import com.jpexs.decompiler.flash.gui.GraphDialog;
import com.jpexs.decompiler.flash.gui.LoadFromCacheFrame;
import com.jpexs.decompiler.flash.gui.LoadFromMemoryFrame;
import com.jpexs.decompiler.flash.gui.LoadingDialog;
import com.jpexs.decompiler.flash.gui.MainFrame;
@@ -249,7 +248,6 @@ public class CheckResources {
FontPreviewDialog.class,
GraphDialog.class,
// GraphTreeFrame.class, // empty
LoadFromCacheFrame.class,
LoadFromMemoryFrame.class,
LoadingDialog.class,
MainFrame.class,

View File

@@ -13,6 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#Note: The feature of opening file from browser cache was removed. There is no need to translate this.
button.open = Open
button.save = Save
button.refresh = Refresh list