Files
i2p.plugins.firefox/src/java/net/i2p/i2pfirefox/I2PCommonBrowser.java

604 lines
19 KiB
Java

package net.i2p.i2pfirefox;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Properties;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* I2PCommonBrowser.java
* Copyright (C) 2022 idk <hankhill19580@gmail.com>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the MIT License. See LICENSE.md for details.
*
* 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.
*
*
* @author idk
* @since 0.0.19
*/
public class I2PCommonBrowser {
private Properties prop = new Properties();
public Logger logger = Logger.getLogger("browserlauncher");
private FileHandler fh;
int CONFIGURED_TIMEOUT = 200;
public I2PCommonBrowser() {
try {
logger.info("Browser log");
} catch (SecurityException e) {
e.printStackTrace();
}
loadPropertiesFile(new File(runtimeDirectory(""), "browser.config"));
}
/**
* Loads a properties file.
*
* @param propertiesFile the file to load
*/
public void loadPropertiesFile(File propertiesFile) {
try (InputStream inputStream = new FileInputStream(propertiesFile)) {
prop = new Properties();
prop.load(inputStream);
} catch (IOException exception) {
logger.warning(exception.toString());
}
}
/**
* Retrieves the properties of the object.
*
* @return the properties of the object
*/
public Properties getProperties() { return prop; }
/**
* Validates the user directory.
*
* @param None No parameters.
* @return None No return value.
*/
public void validateUserDirectory() {
logger.info("Validating user directory");
String userDir = System.getProperty("user.dir");
String userHome = System.getProperty("user.home");
File userDirFile = new File(userDir);
File userHomeFile = new File(userHome);
logger.info("user.dir: " + userDirFile.getAbsolutePath());
logger.info("user.home: " + userHomeFile.getAbsolutePath());
if (!userDirFile.getAbsolutePath().contains("Program Files")) {
if (!userDirFile.getAbsolutePath().equals(
userHomeFile.getAbsolutePath())) {
logger.info("user.dir is not inconvenient");
if (userDirFile.exists()) {
logger.info("user.dir exists");
if (userDirFile.isDirectory()) {
logger.info("user.dir is a directory");
if (userDirFile.canWrite()) {
logger.info("user.dir is writable");
return;
} else {
logger.info("user.dir is not writable");
}
} else {
logger.info("user.dir is not actually a directory");
}
} else {
logger.info("user.dir does not exist");
}
} else {
logger.info("user.dir should not be the same as user.home");
}
} else {
logger.info("user.dir cannot run from inside Program Files");
}
if (isWindows()) {
userHome = new File(userHome, "AppData/Local/I2P").getAbsolutePath();
}
File defaultPathFile = new File(userHome, "i2p/i2pbrowser");
if (!defaultPathFile.exists()) {
defaultPathFile.mkdirs();
}
if (!defaultPathFile.isDirectory()) {
logger.info(
"default path exists and is not a directory, get it out of the way");
logger.info(defaultPathFile.getAbsolutePath());
}
System.setProperty("user.dir", defaultPathFile.getAbsolutePath());
}
/**
* Get the operating system.
*
* @return the name of the operating system (Windows, Linux, BSD, Mac, or
* Unknown)
*/
public String getOperatingSystem() {
if (isWindows()) {
return "Windows";
} else if (isLinux()) {
return "Linux";
} else if (isBSD()) {
return "BSD";
} else if (isOSX()) {
return "Mac";
}
return "Unknown";
}
/**
* Determines if the current operating system is Windows.
*
* @return true if the current operating system is Windows, false otherwise
*/
protected boolean isWindows() {
String osName = System.getProperty("os.name").toLowerCase();
return osName.contains("windows");
}
/**
* Determines if the current operating system is macOS.
*
* @return true if the operating system is macOS, false otherwise
*/
protected boolean isOSX() {
String osName = System.getProperty("os.name").toLowerCase();
return osName.contains("osx") || osName.contains("mac") ||
osName.contains("apple") || osName.contains("darwin");
}
/**
* Determines whether the current operating system is Linux.
*
* @return true if the operating system is Linux, false otherwise
*/
protected boolean isLinux() {
String osName = System.getProperty("os.name").toLowerCase();
return osName.contains("linux");
}
/**
* Checks if the current operating system is a BSD variant.
*
* @return true if the operating system is a BSD variant, false otherwise
*/
protected boolean isBSD() {
String osName = System.getProperty("os.name").toLowerCase();
return osName.contains("bsd");
}
// public void logger.info(String line) { logger.info(line); }
/**
* Returns the log file for the browser launcher.
*
* @return the log file for the browser launcher
*/
/*private File logFile() {
// validateUserDirectory();
String userDirectory = System.getProperty("user.dir");
File logDirectory = new File(userDirectory, "logs");
if (!logDirectory.exists()) {
logDirectory.mkdirs();
}
return new File(logDirectory, "browserlauncher.log");
}*/
/**
* Get the runtime directory, creating it if create=true.
*
* @param create If true, create the runtime directory if it does not exist.
* @param override The runtime directory override.
* @return The runtime directory, or null if it could not be created.
* @since 0.0.19
*/
protected File runtimeDirectory(boolean create, String override) {
String runtimeDir = runtimeDirectory(override);
File runtimeDirFile = new File(runtimeDir);
if (create && !runtimeDirFile.exists()) {
runtimeDirFile.mkdir();
}
return runtimeDirFile;
}
/**
* Returns the runtime directory path based on the given override parameter.
*
* @param override the name of the environment variable to override the
* runtime
* directory
* @return the runtime directory path as a string
*/
protected String runtimeDirectory(String override) {
String runtimeDir = System.getenv(override);
if (isDirectoryValid(runtimeDir)) {
return runtimeDir;
}
String pluginDir = System.getenv("PLUGIN");
if (isDirectoryValid(pluginDir)) {
return pluginDir;
}
String userDir = System.getProperty("user.dir");
if (isDirectoryValid(userDir)) {
return userDir;
}
String homeDir = System.getProperty("user.home");
if (isDirectoryValid(homeDir)) {
String i2pDir = homeDir + "/.i2p";
if (isDirectoryValid(i2pDir)) {
return homeDir;
}
String altI2pDir = homeDir + "/i2p";
if (isDirectoryValid(altI2pDir)) {
return altI2pDir;
}
}
return "";
}
/**
* Checks if the given directory is valid.
*
* @param directory the directory to check
* @return true if the directory is valid, false otherwise
*/
private boolean isDirectoryValid(String directory) {
return directory != null && !directory.isEmpty() &&
new File(directory).exists();
}
/**
* Retrieves the profile directory, creating it if necessary.
*
* @param envVar the environment variable name
* @param browser the browser name
* @param base the base directory
* @param app indicates if it is an app directory
* @return the profile directory, or null if it could not be created
* @since 0.0.19
*/
protected String profileDirectory(String envVar, String browser, String base,
boolean app) {
String profileDir = System.getenv(envVar);
if (profileDir != null && !profileDir.isEmpty()) {
File profileDirFile = new File(profileDir);
if (profileDirFile.exists() && profileDirFile.isDirectory()) {
return profileDir;
}
}
String runtimeDir = runtimeDirectory("");
return profileDir(runtimeDir, browser, base, app);
}
/**
* A description of the entire Java function.
*
* @param file description of parameter
* @param browser description of parameter
* @param base description of parameter
* @param app description of parameter
* @return description of return value
*/
protected String profileDir(String file, String browser, String base,
boolean app) {
String appString = app ? ".app" : "";
String profileDirName =
String.format("i2p.%s.profile.%s%s", browser, base, appString);
File profileDir = new File(file, profileDirName);
return profileDir.getAbsolutePath();
}
/**
* Unpacks the base profile to a specified directory.
*
* @param profileDirectory the directory where the profile will be unpacked
* @param browser the browser type
* @param base the base profile
* @return true if the profile was successfully unpacked, false otherwise
*/
protected boolean unpackProfile(String profileDirectory, String browser,
String base) {
logger.info("Unpacking base profile to " + profileDirectory);
try {
final InputStream resources =
this.getClass().getClassLoader().getResourceAsStream(
"i2p." + browser + "." + base + ".profile.zip");
if (resources == null) {
logger.info("Could not find resources");
return false;
}
logger.info(resources.toString());
// InputStream corresponds to a zip file. Unzip it.
// Files.copy(r, new File(profileDirectory).toPath(),
// StandardCopyOption.REPLACE_EXISTING);
ZipInputStream zis = new ZipInputStream(resources);
ZipEntry entry;
// while there are entries I process them
while ((entry = zis.getNextEntry()) != null) {
logger.info("entry: " + entry.getName() + ", " + entry.getSize());
// consume all the data from this entry
if (entry.isDirectory()) {
logger.info("Creating directory: " + entry.getName());
File dir = new File(profileDirectory, entry.getName());
dir.mkdirs();
} else {
logger.info("Creating file: " + entry.getName());
File file = new File(profileDirectory, entry.getName());
file.createNewFile();
Files.copy(zis, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
while (zis.available() > 0)
zis.read();
// I could close the entry, but getNextEntry does it automatically
// zis.closeEntry()
}
// loop through the Enumeration
} catch (Exception e) {
logger.info("Error copying profile files: " + e.getMessage());
return false;
}
return true;
}
/**
* Copy a directory from sourceDir to destDir, excluding certain files based
* on browser and base.
*
* @param sourceDir directory to be copied from
* @param destDir directory to be copied to
* @param browser the browser name
* @param base the base name
* @throws IOException if an I/O error occurs during copying
*/
protected void copyDirectory(File sourceDir, File destDir, String browser,
String base) throws IOException {
destDir = new File(destDir.toString().replace(
"i2p." + browser + "." + base + ".profile", ""));
if (!destDir.exists()) {
destDir.mkdir();
}
for (String file : sourceDir.list()) {
copyDirectoryCompatibilityMode(new File(sourceDir, file),
new File(destDir, file), browser, base);
}
}
/**
* Copy a directory in compatibility mode.
*
* @param sourceDirectory the source directory to copy
* @param destinationDirectory the destination directory to copy to
* @param browser the browser
* @param base the base
* @throws IOException if an I/O error occurs
*/
private void copyDirectoryCompatibilityMode(File sourceDirectory,
File destinationDirectory,
String browser, String base)
throws IOException {
if (sourceDirectory.isDirectory()) {
copyDirectory(sourceDirectory, destinationDirectory, browser, base);
} else {
copyFile(sourceDirectory, destinationDirectory);
}
}
/**
* Copies the content from the source InputStream to the target OutputStream.
*
* @param source the InputStream to copy from
* @param target the OutputStream to copy to
* @throws IOException if an I/O error occurs during the copying process
*/
public void copy(InputStream source, OutputStream target) throws IOException {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = source.read(buffer)) != -1) {
target.write(buffer, 0, bytesRead);
}
}
/**
* Copies a file from the source file to the destination file.
*
* @param sourceFile the source file to be copied
* @param destinationFile the destination file where the source file will be
* copied to
* @throws IOException if an I/O error occurs during the file copy process
*/
private void copyFile(File sourceFile, File destinationFile)
throws IOException {
try (InputStream in = new FileInputStream(sourceFile);
OutputStream out = new FileOutputStream(destinationFile)) {
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
}
/**
* Validates the profile for the first run.
*
* @param profileDirectory the directory of the profile
* @return true if the profile is valid for the first run, false otherwise
*/
public boolean validateProfileFirstRun(String profileDirectory) {
File profileDir = new File(profileDirectory);
if (!profileDir.exists()) {
return false;
}
if (!profileDir.isDirectory()) {
return false;
}
File firstRunFile = new File(profileDir, "first-run");
if (firstRunFile.exists()) {
firstRunFile.delete();
return true;
}
return false;
}
/**
* Waits for an HTTP proxy on port 4444 to be ready.
* Returns false on timeout of 200 seconds.
*
* @return true if the proxy is ready, false if it is not.
* @since 0.0.1
*/
public boolean waitForProxy() { return waitForProxy(CONFIGURED_TIMEOUT); }
/**
* Waits for an HTTP proxy on port 4444 to be ready.
* Returns false on timeout of the specified number of seconds.
*
* @param timeout the number of seconds to wait for the proxy to be ready.
* @return true if the proxy is ready, false if it is not.
* @since 0.0.1
*/
public boolean waitForProxy(int timeout) {
return waitForProxy(timeout, 4444);
}
/**
* Waits for an HTTP proxy on the specified port to be ready.
* Returns false on timeout of the specified number of seconds.
*
* @param timeout the number of seconds to wait for the proxy to be ready.
* @param port the port to wait for the proxy to be ready on.
* @return true if the proxy is ready, false if it is not.
* @since 0.0.1
*/
public boolean waitForProxy(int timeout, int port) {
return waitForProxy(timeout, port, "localhost");
}
/**
* Waits for an HTTP proxy on the specified port to be ready.
* Returns false on timeout of the specified number of seconds.
* If the timeout is zero or less, the check is disabled and always
* returns true.
*
* @param timeout the number of seconds to wait for the proxy to be ready.
* @param port the port to wait for the proxy to be ready on.
* @param host the host to wait for the proxy to be ready on.
* @return true if the proxy is ready, false if it is not.
* @since 0.0.1
*/
public boolean waitForProxy(int timeout, int port, String host) {
if (timeout <= 0) {
return true;
}
for (int i = 0; i < timeout; i++) {
if (isPortOccupied(port, host)) {
return true;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return false;
}
/**
* Determines if a given port on a specified host is occupied.
*
* @param port the port number to check
* @param host the host address to check
* @return true if the port is occupied, false otherwise
*/
public boolean isPortOccupied(int port, String host) {
try {
new Socket(host, port).close();
return true;
} catch (IOException e) {
return false;
}
}
/**
* Alters the proxy timeout to customized value time, in seconds.
* May be zero.
*
* @param time
*/
public void setProxyTimeoutTime(int time) { CONFIGURED_TIMEOUT = time; }
/**
* Joins the elements of the given string array into a single string.
*
* @param arr the string array to be joined
* @return the joined string
*/
protected String join(String[] arr) {
StringBuilder result = new StringBuilder();
for (String item : arr) {
result.append(" \"").append(item).append("\"");
}
return result.toString();
}
/**
* Sleeps for a specified number of milliseconds.
*
* @param millis the number of milliseconds to sleep
*/
public void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* Searches for a file in a given directory and its subdirectories.
*
* @param directory the directory to search in
* @param search the name of the file to search for
* @return the found file or null if not found
*/
public File searchFile(File directory, String search) {
if (directory.isDirectory()) {
File[] files = directory.listFiles();
for (File file : files) {
File foundFile = searchFile(file, search);
if (foundFile != null)
return foundFile;
}
} else {
if (directory.getName().equals(search)) {
return directory;
}
}
return null;
}
}