propagate from branch 'i2p.i2p.zzz.dir' (head a871493662f67163f823576ba26e98322d3f896f)
to branch 'i2p.i2p.zzz.test' (head 1168ac4132d737382bf24ba8458a53a9db002ffa)
This commit is contained in:
@ -9,6 +9,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* @author Iakin
|
||||
* A class for retrieveing details about the CPU using the CPUID assembly instruction.
|
||||
@ -503,9 +505,10 @@ public class CPUID {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
InputStream libStream = resource.openStream();
|
||||
outFile = new File(libPrefix + "jcpuid" + libSuffix);
|
||||
outFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), libPrefix + "jcpuid" + libSuffix);
|
||||
fos = new FileOutputStream(outFile);
|
||||
byte buf[] = new byte[4096*1024];
|
||||
// wtf this was 4096*1024 which is really excessive for a roughly 4KB file
|
||||
byte buf[] = new byte[4096];
|
||||
while (true) {
|
||||
int read = libStream.read(buf);
|
||||
if (read < 0) break;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.i2p;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
@ -20,10 +21,12 @@ import net.i2p.crypto.KeyGenerator;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.crypto.TransientSessionKeyManager;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.RoutingKeyGenerator;
|
||||
import net.i2p.stat.StatManager;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.FortunaRandomSource;
|
||||
import net.i2p.util.KeyRing;
|
||||
import net.i2p.util.LogManager;
|
||||
@ -96,6 +99,13 @@ public class I2PAppContext {
|
||||
private volatile boolean _keyGeneratorInitialized;
|
||||
protected volatile boolean _keyRingInitialized; // used in RouterContext
|
||||
private Set<Runnable> _shutdownTasks;
|
||||
private File _baseDir;
|
||||
private File _configDir;
|
||||
private File _routerDir;
|
||||
private File _pidDir;
|
||||
private File _logDir;
|
||||
private File _appDir;
|
||||
private File _tmpDir;
|
||||
|
||||
|
||||
/**
|
||||
@ -155,8 +165,145 @@ public class I2PAppContext {
|
||||
_logManagerInitialized = false;
|
||||
_keyRingInitialized = false;
|
||||
_shutdownTasks = new ConcurrentHashSet(0);
|
||||
initializeDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Directories. These are all set at instantiation and will not be changed by
|
||||
* subsequent property changes.
|
||||
* All properties, if set, should be absolute paths.
|
||||
*
|
||||
* Name Property Method Files
|
||||
* ----- -------- ----- -----
|
||||
* Base i2p.dir.base getBaseDir() lib/, webapps/, docs/, geoip/, licenses/, ...
|
||||
* Temp i2p.dir.temp getTempDir() Temporary files
|
||||
* Config i2p.dir.config getConfigDir() *.config, hosts.txt, addressbook/, ...
|
||||
* PID i2p.dir.pid getPIDDir() wrapper *.pid files, router.ping
|
||||
*
|
||||
* (the following all default to the same as Config)
|
||||
*
|
||||
* Router i2p.dir.router getRouterDir() netDb/, peerProfiles/, router.*, keyBackup/, ...
|
||||
* Log i2p.dir.log getLogDir() wrapper.log*, logs/
|
||||
* App i2p.dir.app getAppDir() eepsite/, ...
|
||||
*
|
||||
* Note that we can't control where the wrapper puts its files.
|
||||
*
|
||||
* The app dir is where all data files should be. Apps should always read and write files here,
|
||||
* using a constructor such as:
|
||||
*
|
||||
* String path = mypath;
|
||||
* File f = new File(path);
|
||||
* if (!f.isAbsolute())
|
||||
* f = new File(_context.geAppDir(), path);
|
||||
*
|
||||
* and never attempt to access files in the CWD using
|
||||
*
|
||||
* File f = new File("foo");
|
||||
*
|
||||
* An app should assume the CWD is not writable.
|
||||
*
|
||||
* Here in I2PAppContext, all the dirs default to CWD.
|
||||
* However these will be different in RouterContext, as Router.java will set
|
||||
* the properties in the RouterContext constructor.
|
||||
*
|
||||
* Apps should never need to access the base dir, which is the location of the base I2P install.
|
||||
* However this is provided for the router's use, and for backward compatibility should an app
|
||||
* need to look there as well.
|
||||
*
|
||||
* All dirs except the base are created if they don't exist, but the creation will fail silently.
|
||||
*/
|
||||
private void initializeDirs() {
|
||||
String s = getProperty("i2p.dir.base", System.getProperty("user.dir"));
|
||||
_baseDir = new File(s);
|
||||
// config defaults to base
|
||||
s = getProperty("i2p.dir.config");
|
||||
if (s != null) {
|
||||
_configDir = new File(s);
|
||||
if (!_configDir.exists())
|
||||
_configDir.mkdir();
|
||||
} else {
|
||||
_configDir = _baseDir;
|
||||
}
|
||||
_configDir = new File(s);
|
||||
// router defaults to config
|
||||
s = getProperty("i2p.dir.router");
|
||||
if (s != null) {
|
||||
_routerDir = new File(s);
|
||||
if (!_routerDir.exists())
|
||||
_routerDir.mkdir();
|
||||
} else {
|
||||
_routerDir = _configDir;
|
||||
}
|
||||
// pid defaults to system temp directory
|
||||
s = getProperty("i2p.dir.pid", System.getProperty("java.io.tmpdir"));
|
||||
_pidDir = new File(s);
|
||||
if (!_pidDir.exists())
|
||||
_pidDir.mkdir();
|
||||
// these all default to router
|
||||
s = getProperty("i2p.dir.log");
|
||||
if (s != null) {
|
||||
_logDir = new File(s);
|
||||
if (!_logDir.exists())
|
||||
_logDir.mkdir();
|
||||
} else {
|
||||
_logDir = _routerDir;
|
||||
}
|
||||
s = getProperty("i2p.dir.app");
|
||||
if (s != null) {
|
||||
_appDir = new File(s);
|
||||
if (!_appDir.exists())
|
||||
_appDir.mkdir();
|
||||
} else {
|
||||
_appDir = _routerDir;
|
||||
}
|
||||
// comment these out later, don't want user names in the wrapper logs
|
||||
System.err.println("Base directory: " + _baseDir.getAbsolutePath());
|
||||
System.err.println("Config directory: " + _configDir.getAbsolutePath());
|
||||
System.err.println("Router directory: " + _routerDir.getAbsolutePath());
|
||||
System.err.println("App directory: " + _appDir.getAbsolutePath());
|
||||
System.err.println("Log directory: " + _logDir.getAbsolutePath());
|
||||
System.err.println("PID directory: " + _pidDir.getAbsolutePath());
|
||||
System.err.println("Temp directory: " + getTempDir().getAbsolutePath());
|
||||
}
|
||||
|
||||
public File getBaseDir() { return _baseDir; }
|
||||
public File getConfigDir() { return _configDir; }
|
||||
public File getRouterDir() { return _routerDir; }
|
||||
public File getPIDDir() { return _pidDir; }
|
||||
public File getLogDir() { return _logDir; }
|
||||
public File getAppDir() { return _appDir; }
|
||||
public File getTempDir() {
|
||||
// fixme don't synchronize every time
|
||||
synchronized (this) {
|
||||
if (_tmpDir == null) {
|
||||
String d = getProperty("i2p.dir.temp", System.getProperty("java.io.tmpdir"));
|
||||
// our random() probably isn't warmed up yet
|
||||
String f = "i2p-" + (new java.util.Random()).nextInt() + ".tmp";
|
||||
_tmpDir = new File(d, f);
|
||||
if (_tmpDir.exists()) {
|
||||
// good or bad ?
|
||||
} else if (_tmpDir.mkdir()) {
|
||||
_tmpDir.deleteOnExit();
|
||||
} else {
|
||||
System.err.println("Could not create temp dir " + _tmpDir.getAbsolutePath());
|
||||
_tmpDir = new File(_routerDir, "tmp");
|
||||
_tmpDir.mkdir();
|
||||
}
|
||||
}
|
||||
}
|
||||
return _tmpDir;
|
||||
}
|
||||
|
||||
/** don't rely on deleteOnExit() */
|
||||
public void deleteTempDir() {
|
||||
synchronized (this) {
|
||||
if (_tmpDir != null) {
|
||||
FileUtil.rmdir(_tmpDir, false);
|
||||
_tmpDir = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the configuration attributes of this context, using properties
|
||||
* provided during the context construction, or falling back on
|
||||
|
@ -89,7 +89,7 @@ public class HostsTxtNamingService extends NamingService {
|
||||
String hostsfile = (String)filenames.get(i);
|
||||
Properties hosts = new Properties();
|
||||
try {
|
||||
File f = new File(hostsfile);
|
||||
File f = new File(_context.getRouterDir(), hostsfile);
|
||||
if ( (f.exists()) && (f.canRead()) ) {
|
||||
DataHelper.loadProps(hosts, f, true);
|
||||
|
||||
@ -119,7 +119,7 @@ public class HostsTxtNamingService extends NamingService {
|
||||
String hostsfile = (String)filenames.get(i);
|
||||
Properties hosts = new Properties();
|
||||
try {
|
||||
File f = new File(hostsfile);
|
||||
File f = new File(_context.getRouterDir(), hostsfile);
|
||||
if ( (f.exists()) && (f.canRead()) ) {
|
||||
DataHelper.loadProps(hosts, f, true);
|
||||
Set keyset = hosts.keySet();
|
||||
@ -145,7 +145,7 @@ public class HostsTxtNamingService extends NamingService {
|
||||
String hostsfile = (String)filenames.get(i);
|
||||
Properties hosts = new Properties();
|
||||
try {
|
||||
File f = new File(hostsfile);
|
||||
File f = new File(_context.getRouterDir(), hostsfile);
|
||||
if ( (f.exists()) && (f.canRead()) ) {
|
||||
DataHelper.loadProps(hosts, f, true);
|
||||
Set keyset = hosts.keySet();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -276,7 +277,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
}
|
||||
|
||||
private static final void showVersionCLI(String signedFile) {
|
||||
String versionString = new TrustedUpdate().getVersionString(signedFile);
|
||||
String versionString = new TrustedUpdate().getVersionString(new File(signedFile));
|
||||
|
||||
if (versionString == "")
|
||||
System.out.println("No version string found in file '" + signedFile + "'");
|
||||
@ -294,7 +295,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
}
|
||||
|
||||
private static final void verifySigCLI(String signedFile) {
|
||||
boolean isValidSignature = new TrustedUpdate().verify(signedFile);
|
||||
boolean isValidSignature = new TrustedUpdate().verify(new File(signedFile));
|
||||
|
||||
if (isValidSignature)
|
||||
System.out.println("Signature VALID");
|
||||
@ -303,7 +304,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
}
|
||||
|
||||
private static final void verifyUpdateCLI(String signedFile) {
|
||||
boolean isUpdate = new TrustedUpdate().isUpdatedVersion(CoreVersion.VERSION, signedFile);
|
||||
boolean isUpdate = new TrustedUpdate().isUpdatedVersion(CoreVersion.VERSION, new File(signedFile));
|
||||
|
||||
if (isUpdate)
|
||||
System.out.println("File version is newer than current version.");
|
||||
@ -347,7 +348,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
* @return The version string read, or an empty string if no version string
|
||||
* is present.
|
||||
*/
|
||||
public String getVersionString(String signedFile) {
|
||||
public String getVersionString(File signedFile) {
|
||||
FileInputStream fileInputStream = null;
|
||||
|
||||
try {
|
||||
@ -396,9 +397,9 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
* @return <code>true</code> if the signed update file's version is newer
|
||||
* than the current version, otherwise <code>false</code>.
|
||||
*/
|
||||
public boolean isUpdatedVersion(String currentVersion, String signedFile) {
|
||||
public boolean isUpdatedVersion(String currentVersion, File signedFile) {
|
||||
_newVersion = getVersionString(signedFile);
|
||||
return needsUpdate(currentVersion, getVersionString(signedFile));
|
||||
return needsUpdate(currentVersion, _newVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -413,7 +414,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
* @return <code>null</code> if the signature and version were valid and the
|
||||
* data was moved, and an error <code>String</code> otherwise.
|
||||
*/
|
||||
public String migrateVerified(String currentVersion, String signedFile, String outputFile) {
|
||||
public String migrateVerified(String currentVersion, File signedFile, File outputFile) {
|
||||
if (!isUpdatedVersion(currentVersion, signedFile))
|
||||
return "Downloaded version is not greater than current version";
|
||||
|
||||
@ -606,7 +607,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
* @return <code>true</code> if the file has a valid signature, otherwise
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public boolean verify(String signedFile) {
|
||||
public boolean verify(File signedFile) {
|
||||
for (int i = 0; i < _trustedKeys.size(); i++) {
|
||||
SigningPublicKey signingPublicKey = new SigningPublicKey();
|
||||
|
||||
@ -662,7 +663,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
}
|
||||
}
|
||||
|
||||
return verify(signedFile, signingPublicKey);
|
||||
return verify(new File(signedFile), signingPublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -676,7 +677,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
|
||||
* @return <code>true</code> if the file has a valid signature, otherwise
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public boolean verify(String signedFile, SigningPublicKey signingPublicKey) {
|
||||
public boolean verify(File signedFile, SigningPublicKey signingPublicKey) {
|
||||
FileInputStream fileInputStream = null;
|
||||
|
||||
try {
|
||||
|
@ -109,6 +109,10 @@ public class PrivateKeyFile {
|
||||
this(new File(file), I2PClientFactory.createClient());
|
||||
}
|
||||
|
||||
public PrivateKeyFile(File file) {
|
||||
this(file, I2PClientFactory.createClient());
|
||||
}
|
||||
|
||||
public PrivateKeyFile(File file, I2PClient client) {
|
||||
this.file = file;
|
||||
this.client = client;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.i2p.stat;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -109,6 +110,9 @@ public class BufferedStatLog implements StatLog {
|
||||
String filename = _context.getProperty(StatManager.PROP_STAT_FILE);
|
||||
if (filename == null)
|
||||
filename = StatManager.DEFAULT_STAT_FILE;
|
||||
File foo = new File(filename);
|
||||
if (!foo.isAbsolute())
|
||||
filename = (new File(_context.getRouterDir(), filename)).getAbsolutePath();
|
||||
if ( (_outFile != null) && (_outFile.equals(filename)) ) {
|
||||
// noop
|
||||
} else {
|
||||
|
@ -18,6 +18,12 @@ import java.util.zip.ZipFile;
|
||||
/**
|
||||
* General helper methods for messing with files
|
||||
*
|
||||
* These are static methods that do NOT convert arguments
|
||||
* to absolute paths for a particular context and directtory.
|
||||
*
|
||||
* Callers should ALWAYS provide absolute paths as arguments,
|
||||
* and should NEVER assume files are in the current working directory.
|
||||
*
|
||||
*/
|
||||
public class FileUtil {
|
||||
/**
|
||||
|
@ -67,8 +67,8 @@ public class LogManager {
|
||||
/** when was the config file last read (or -1 if never) */
|
||||
private long _configLastRead;
|
||||
|
||||
/** filename of the config file */
|
||||
private String _location;
|
||||
/** the config file */
|
||||
private File _locationFile;
|
||||
/** Ordered list of LogRecord elements that have not been written out yet */
|
||||
private List _records;
|
||||
/** List of explicit overrides of log levels (LogLimit objects) */
|
||||
@ -115,11 +115,11 @@ public class LogManager {
|
||||
_logs = new HashMap(128);
|
||||
_defaultLimit = Log.ERROR;
|
||||
_configLastRead = 0;
|
||||
_location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT);
|
||||
_context = context;
|
||||
_log = getLog(LogManager.class);
|
||||
String location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT);
|
||||
setConfig(location);
|
||||
_consoleBuffer = new LogConsoleBuffer(context);
|
||||
loadConfig();
|
||||
_writer = new LogWriter(this);
|
||||
Thread t = new I2PThread(_writer);
|
||||
t.setName("LogWriter");
|
||||
@ -196,8 +196,9 @@ public class LogManager {
|
||||
}
|
||||
|
||||
public void setConfig(String filename) {
|
||||
_log.debug("Config filename set to " + filename);
|
||||
_location = filename;
|
||||
_locationFile = new File(filename);
|
||||
if (!_locationFile.isAbsolute())
|
||||
_locationFile = new File(_context.getConfigDir(), filename);
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
@ -232,20 +233,12 @@ public class LogManager {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
private void loadConfig() {
|
||||
File cfgFile = new File(_location);
|
||||
File cfgFile = _locationFile;
|
||||
if (!cfgFile.exists()) {
|
||||
if (!_alreadyNoticedMissingConfig) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Log file " + _location + " does not exist");
|
||||
//System.err.println("Log file " + _location + " does not exist");
|
||||
_log.warn("Log file " + _locationFile.getAbsolutePath() + " does not exist");
|
||||
_alreadyNoticedMissingConfig = true;
|
||||
}
|
||||
parseConfig(new Properties());
|
||||
@ -262,9 +255,6 @@ public class LogManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Loading config from " + _location);
|
||||
|
||||
Properties p = new Properties();
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
@ -272,7 +262,7 @@ public class LogManager {
|
||||
p.load(fis);
|
||||
_configLastRead = _context.clock().now();
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error loading logger config from " + new File(_location).getAbsolutePath());
|
||||
System.err.println("Error loading logger config from " + cfgFile.getAbsolutePath());
|
||||
} finally {
|
||||
if (fis != null) try {
|
||||
fis.close();
|
||||
@ -540,7 +530,7 @@ public class LogManager {
|
||||
String config = createConfig();
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(_location);
|
||||
fos = new FileOutputStream(_locationFile);
|
||||
fos.write(config.getBytes());
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
|
@ -15,6 +15,8 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Log writer thread that pulls log records from the LogManager, writes them to
|
||||
* the current logfile, and rotates the logs as necessary. This also periodically
|
||||
@ -22,6 +24,7 @@ import java.util.List;
|
||||
*
|
||||
*/
|
||||
class LogWriter implements Runnable {
|
||||
/** every 10 seconds? why? Just have the gui force a reread after a change?? */
|
||||
private final static long CONFIG_READ_ITERVAL = 10 * 1000;
|
||||
private long _lastReadConfig = 0;
|
||||
private long _numBytesInCurrentFile = 0;
|
||||
@ -38,6 +41,7 @@ class LogWriter implements Runnable {
|
||||
|
||||
public LogWriter(LogManager manager) {
|
||||
_manager = manager;
|
||||
_lastReadConfig = Clock.getInstance().now();
|
||||
}
|
||||
|
||||
public void stopWriting() {
|
||||
@ -168,15 +172,21 @@ class LogWriter implements Runnable {
|
||||
*
|
||||
*/
|
||||
private File getNextFile(String pattern) {
|
||||
File f = null;
|
||||
File f = new File(pattern);
|
||||
File base = null;
|
||||
if (!f.isAbsolute())
|
||||
base = I2PAppContext.getGlobalContext().getLogDir();
|
||||
|
||||
if ( (pattern.indexOf('#') < 0) && (pattern.indexOf('@') <= 0) ) {
|
||||
return new File(pattern);
|
||||
if (base != null)
|
||||
return new File(base, pattern);
|
||||
else
|
||||
return f;
|
||||
}
|
||||
|
||||
int max = _manager.getRotationLimit();
|
||||
if (_rotationNum == -1) {
|
||||
return getFirstFile(pattern, max);
|
||||
return getFirstFile(base, pattern, max);
|
||||
}
|
||||
|
||||
// we're in rotation, just go to the next
|
||||
@ -190,9 +200,13 @@ class LogWriter implements Runnable {
|
||||
* Retrieve the first file, updating the rotation number accordingly
|
||||
*
|
||||
*/
|
||||
private File getFirstFile(String pattern, int max) {
|
||||
private File getFirstFile(File base, String pattern, int max) {
|
||||
for (int i = 0; i < max; i++) {
|
||||
File f = new File(replace(pattern, i));
|
||||
File f;
|
||||
if (base != null)
|
||||
f = new File(base, replace(pattern, i));
|
||||
else
|
||||
f = new File(replace(pattern, i));
|
||||
if (!f.exists()) {
|
||||
_rotationNum = i;
|
||||
return f;
|
||||
@ -202,7 +216,11 @@ class LogWriter implements Runnable {
|
||||
// all exist, pick the oldest to replace
|
||||
File oldest = null;
|
||||
for (int i = 0; i < max; i++) {
|
||||
File f = new File(replace(pattern, i));
|
||||
File f;
|
||||
if (base != null)
|
||||
f = new File(base, replace(pattern, i));
|
||||
else
|
||||
f = new File(replace(pattern, i));
|
||||
if (oldest == null) {
|
||||
oldest = f;
|
||||
} else {
|
||||
|
@ -540,9 +540,10 @@ public class NativeBigInteger extends BigInteger {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
InputStream libStream = resource.openStream();
|
||||
outFile = new File(_libPrefix + "jbigi" + _libSuffix);
|
||||
outFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), _libPrefix + "jbigi" + _libSuffix);
|
||||
fos = new FileOutputStream(outFile);
|
||||
byte buf[] = new byte[4096*1024];
|
||||
// wtf this was 4096*1024 which is really excessive for a roughly 50KB file
|
||||
byte buf[] = new byte[4096];
|
||||
while (true) {
|
||||
int read = libStream.read(buf);
|
||||
if (read < 0) break;
|
||||
|
@ -142,7 +142,7 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
|
||||
private static final String SEEDFILE = "prngseed.rnd";
|
||||
|
||||
public static final void writeSeed(byte buf[]) {
|
||||
File f = new File(SEEDFILE);
|
||||
File f = new File(I2PAppContext.getGlobalContext().getConfigDir(), SEEDFILE);
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(f);
|
||||
@ -164,7 +164,7 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
|
||||
}
|
||||
|
||||
private static final boolean seedFromFile(String filename, byte buf[]) {
|
||||
File f = new File(filename);
|
||||
File f = new File(I2PAppContext.getGlobalContext().getConfigDir(), filename);
|
||||
if (f.exists()) {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
|
481
core/java/src/net/i2p/util/WorkingDir.java
Normal file
481
core/java/src/net/i2p/util/WorkingDir.java
Normal file
@ -0,0 +1,481 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Get a working directory for i2p.
|
||||
*
|
||||
* For the location, first try the system property i2p.dir.config
|
||||
* Next try $HOME/.i2p on linux or %APPDATA%\I2P on Windows.
|
||||
*
|
||||
* If the dir exists, return it.
|
||||
* Otherwise, attempt to create it, and copy files from the base directory.
|
||||
* To successfully copy, the base install dir must be the system property i2p.dir.base
|
||||
* or else must be in $CWD.
|
||||
*
|
||||
* If I2P was run from the install directory in the past,
|
||||
* and migrateOldData = true, copy the
|
||||
* necessary data files (except i2psnark/) over to the new working directory.
|
||||
*
|
||||
* Otherwise, just copy over a limited number of files over.
|
||||
*
|
||||
* Do not ever copy or move the old i2psnark/ directory, as if the
|
||||
* old and new locations are on different file systems, this could
|
||||
* be quite slow.
|
||||
*
|
||||
* Modify some files while copying, see methods below.
|
||||
*
|
||||
* After migration, the router will run using the new directory.
|
||||
* The wrapper, however, must be stopped and restarted from the new script - until then,
|
||||
* it will continue to write to wrapper.log* in the old directory.
|
||||
*
|
||||
* @param whether to copy all data over from an existing install
|
||||
*/
|
||||
public class WorkingDir {
|
||||
|
||||
private final static String PROP_BASE_DIR = "i2p.dir.base";
|
||||
private final static String PROP_WORKING_DIR = "i2p.dir.config";
|
||||
private final static String WORKING_DIR_DEFAULT_WINDOWS = "I2P";
|
||||
private final static String WORKING_DIR_DEFAULT = ".i2p";
|
||||
|
||||
/**
|
||||
* Only call this once on router invocation.
|
||||
* Caller should store the return value for future reference.
|
||||
*/
|
||||
public static String getWorkingDir(boolean migrateOldData) {
|
||||
String dir = System.getProperty(PROP_WORKING_DIR);
|
||||
boolean isWindows = System.getProperty("os.name").startsWith("Win");
|
||||
File dirf = null;
|
||||
if (dir != null) {
|
||||
dirf = new File(dir);
|
||||
} else {
|
||||
String home = System.getProperty("user.home");
|
||||
if (isWindows) {
|
||||
String appdata = System.getenv("APPDATA");
|
||||
if (appdata != null)
|
||||
home = appdata;
|
||||
dirf = new File(home, WORKING_DIR_DEFAULT_WINDOWS);
|
||||
} else {
|
||||
dirf = new File(home, WORKING_DIR_DEFAULT);
|
||||
}
|
||||
}
|
||||
// where we are now
|
||||
String cwd = System.getProperty(PROP_BASE_DIR);
|
||||
if (cwd == null)
|
||||
cwd = System.getProperty("user.dir");
|
||||
// where we want to go
|
||||
String rv = dirf.getAbsolutePath();
|
||||
if (dirf.exists()) {
|
||||
if (dirf.isDirectory())
|
||||
return rv; // all is good, we found the user directory
|
||||
System.err.println("Wanted to use " + rv + " for a working directory but it is not a directory");
|
||||
return cwd;
|
||||
}
|
||||
if (!dirf.mkdir()) {
|
||||
System.err.println("Wanted to use " + rv + " for a working directory but could not create it");
|
||||
return cwd;
|
||||
}
|
||||
|
||||
// Check for a hosts.txt file, if it exists then I2P is there
|
||||
File oldDirf = new File(cwd);
|
||||
File test = new File(oldDirf, "hosts.txt");
|
||||
if (!test.exists()) {
|
||||
System.err.println("ERROR - Cannot find I2P installation in " + cwd);
|
||||
return cwd;
|
||||
}
|
||||
|
||||
// Check for a router.keys file, if it exists it's an old install,
|
||||
// and only migrate the data files if told to do so
|
||||
test = new File(oldDirf, "router.keys");
|
||||
boolean oldInstall = test.exists();
|
||||
migrateOldData &= oldInstall;
|
||||
|
||||
// Do the copying
|
||||
if (migrateOldData)
|
||||
System.err.println("Migrating data files to new user directory " + rv);
|
||||
else
|
||||
System.err.println("Setting up new user directory " + rv);
|
||||
boolean success = migrate(MIGRATE_BASE, oldDirf, dirf);
|
||||
// this one must be after MIGRATE_BASE
|
||||
success &= migrateJettyXml(oldDirf, dirf);
|
||||
success &= migrateWrapperConfig(oldDirf, dirf);
|
||||
if (migrateOldData) {
|
||||
success &= migrate(MIGRATE_DATA, oldDirf, dirf);
|
||||
success &= migrateI2PTunnelKeys(oldDirf, dirf);
|
||||
success &= migrateSnark(oldDirf, dirf);
|
||||
// new installs will have updated scripts left in the install dir
|
||||
// don't bother migrating runplain.sh or i2prouter.bat
|
||||
if (!isWindows)
|
||||
success &= migrateI2prouter(oldDirf, dirf);
|
||||
} else if (!oldInstall) {
|
||||
// copy the default i2psnark.config over
|
||||
success &= migrate("i2psnark.config", oldDirf, dirf);
|
||||
}
|
||||
|
||||
// Report success or failure
|
||||
if (success) {
|
||||
System.err.println("Successfully copied data files to new user directory " + rv);
|
||||
if (migrateOldData) {
|
||||
System.err.println("Libraries and other files remain in the old directory " + cwd + ", do not remove them.");
|
||||
System.err.println("You should manually move any non-standard files, such as additional eepsite directories and key files");
|
||||
System.err.println("After verifying that all is working, you may delete the following data files and directories in " +
|
||||
cwd + ": " + MIGRATE_DATA.replace(',', ' ') + " i2psnark.config tmp work");
|
||||
if (System.getProperty("wrapper.version") != null)
|
||||
System.err.println("Note that until you shutdown your router completely and restart, the wrapper will continue" +
|
||||
" to log to the old wrapper logs in " + cwd);
|
||||
if (!isWindows)
|
||||
System.err.println("From now on, you should now use the i2prouter" +
|
||||
" script in the " + rv + " directory to start i2p." +
|
||||
" You may copy or move this script elsewhere, you need not run it from that directory.");
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
System.err.println("FAILED copy of some or all data files to new directory " + rv);
|
||||
System.err.println("Check logs for details");
|
||||
System.err.println("Continung to use data files in old directory " + cwd);
|
||||
return cwd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* files and directories from the base install to copy over
|
||||
* None of these should be included in i2pupdate.zip
|
||||
*
|
||||
* The user should not delete these in the old location, leave them as templates for new users
|
||||
*/
|
||||
private static final String MIGRATE_BASE =
|
||||
// base install - dirs
|
||||
// We don't currently have a default addressbook/ in the base distribution,
|
||||
// but distros might put one in
|
||||
"addressbook,eepsite," +
|
||||
// base install - files
|
||||
// We don't currently have a default router.config or logger.config in the base distribution,
|
||||
// but distros might put one in
|
||||
"blocklist.txt,clients.config,hosts.txt,i2ptunnel.config,jetty-i2psnark.xml," +
|
||||
"logger.config,router.config,systray.config";
|
||||
|
||||
/**
|
||||
* files and directories from an old single-directory installation to copy over - NOT including snark
|
||||
* None of these should be included in i2pupdate.zip
|
||||
*
|
||||
* The user can be advised to delete these from the old location
|
||||
*/
|
||||
private static final String MIGRATE_DATA =
|
||||
// post install - dirs
|
||||
// not required to copy - tmp/, work/
|
||||
// addressbook included in MIGRATE_BASE above
|
||||
"keyBackup,logs,netDb,peerProfiles," +
|
||||
// post install - files
|
||||
// not required to copy - prngseed.rnd
|
||||
// logger.config and router.config included in MIGRATE_BASE above
|
||||
"bob.config,privatehosts.txt,router.info,router.keys," +
|
||||
"sam.keys,susimail.config,userhosts.txt,webapps.config," +
|
||||
"wrapper.log,wrapper.log.1,wrapper.log.2";
|
||||
|
||||
private static boolean migrate(String list, File olddir, File todir) {
|
||||
boolean rv = true;
|
||||
String files[] = list.split(",");
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File from = new File(olddir, files[i]);
|
||||
if (!copy(from, todir)) {
|
||||
System.err.println("Error copying " + from.getAbsolutePath());
|
||||
rv = false;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy over the i2psnark.config file with modifications
|
||||
*/
|
||||
private static boolean migrateSnark(File olddir, File todir) {
|
||||
boolean rv = true;
|
||||
File oldSnark = new File(olddir, "i2psnark");
|
||||
File newSnark = new File(todir, "i2psnark");
|
||||
File oldSnarkConfig = new File(olddir, "i2psnark.config");
|
||||
File newSnarkConfig = new File(todir, "i2psnark.config");
|
||||
boolean hasData = oldSnark.exists();
|
||||
if (hasData) {
|
||||
File children[] = oldSnark.listFiles();
|
||||
hasData = children != null && children.length > 0;
|
||||
}
|
||||
if (oldSnarkConfig.exists()) {
|
||||
if (hasData) {
|
||||
// edit the snark config file to point to the old location, we aren't moving the data
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
DataHelper.loadProps(props, oldSnarkConfig);
|
||||
String dir = props.getProperty("i2psnark.dir");
|
||||
if (dir == null)
|
||||
dir = "i2psnark";
|
||||
// change relative to absolute path
|
||||
File f = new File(dir);
|
||||
props.setProperty("i2psnark.dir", f.getAbsolutePath());
|
||||
DataHelper.storeProps(props, newSnarkConfig);
|
||||
System.err.println("Copied i2psnark.config with modifications");
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("FAILED copy i2psnark.config");
|
||||
rv = false;
|
||||
}
|
||||
} else {
|
||||
// copy the i2psnark config file over
|
||||
copy(newSnarkConfig, todir);
|
||||
System.err.println("Copied i2psnark.config");
|
||||
}
|
||||
} else {
|
||||
if (hasData) {
|
||||
// data but no previous config file (unlikely) - make new config file
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
File f = new File("i2psnark");
|
||||
props.setProperty("i2psnark.dir", f.getAbsolutePath());
|
||||
DataHelper.storeProps(props, newSnarkConfig);
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
} // else no config and no data
|
||||
}
|
||||
if (hasData) {
|
||||
/*************
|
||||
// crude attempt to detect same filesystem
|
||||
if ((oldSnark.getAbsolutePath().startsWith("/home/") && newSnark.getAbsolutePath().startsWith("/home/")) ||
|
||||
(System.getProperty("os.name").toLowerCase.indexOf("windows") >= 0 &&
|
||||
oldSnark.getAbsolutePath().substring(0,1).equals(newSnark.getAbsolutePath().substring(0,1) &&
|
||||
oldSnark.getAbsolutePath().substring(1,2).equals(":\\") &&
|
||||
newSnark.getAbsolutePath().substring(1,2).equals(":\\"))) {
|
||||
// OK, apparently in same file system
|
||||
// move everything
|
||||
}
|
||||
**************/
|
||||
System.err.println("NOT moving the i2psnark data directory " + oldSnark.getAbsolutePath() +
|
||||
" to the new directory " + newSnark.getAbsolutePath() +
|
||||
". You may move the directory contents manually WHILE I2P IS NOT RUNNING," +
|
||||
" and edit the file " + newSnarkConfig.getAbsolutePath() +
|
||||
" to configure i2psnark to use a different location by editing the i2psnark.dir configuration to be" +
|
||||
" i2psnark.dir=" + oldSnark.getAbsolutePath() +
|
||||
" and restart, or you may leave the i2psnark directory in its old location.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy over the i2prouter file with modifications
|
||||
* The resulting script can be run from any location.
|
||||
*/
|
||||
private static boolean migrateI2prouter(File olddir, File todir) {
|
||||
File oldFile = new File(olddir, "i2prouter");
|
||||
File newFile = new File(todir, "i2prouter");
|
||||
FileInputStream in = null;
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
in = new FileInputStream(oldFile);
|
||||
out = new PrintWriter(new BufferedWriter(new FileWriter(newFile)));
|
||||
boolean firstTime = true;
|
||||
String s = null;
|
||||
while ((s = DataHelper.readLine(in)) != null) {
|
||||
if (s.equals("WRAPPER_CMD=\"./i2psvc\"")) {
|
||||
// i2psvc in the old location
|
||||
File f = new File("i2psvc");
|
||||
s = "WRAPPER_CMD=\"" + f.getAbsolutePath() + "\"";
|
||||
} else if(s.equals("WRAPPER_CONF=\"wrapper.config\"")) {
|
||||
// wrapper.config the new location
|
||||
File f = new File(todir, "wrapper.config");
|
||||
s = "WRAPPER_CONF=\"" + f.getAbsolutePath() + "\"";
|
||||
} else if(s.equals("PIDDIR=\".\"")) {
|
||||
// i2p.pid in the new location
|
||||
s = "PIDDIR=\"" + todir.getAbsolutePath() + "\"";
|
||||
}
|
||||
out.println(s);
|
||||
if (firstTime) {
|
||||
// first line was #!/bin/sh, so had to wait until now
|
||||
out.println("# Modified by I2P User dir migration script");
|
||||
firstTime = false;
|
||||
}
|
||||
}
|
||||
System.err.println("Copied i2prouter with modifications");
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
if (in != null) {
|
||||
System.err.println("FAILED copy i2prouter");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
if (out != null) out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy over the wrapper.config file with modifications
|
||||
*/
|
||||
private static boolean migrateWrapperConfig(File olddir, File todir) {
|
||||
File oldFile = new File(olddir, "wrapper.config");
|
||||
File newFile = new File(todir, "wrapper.config");
|
||||
FileInputStream in = null;
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
in = new FileInputStream(oldFile);
|
||||
out = new PrintWriter(new BufferedWriter(new FileWriter(newFile)));
|
||||
out.println("# Modified by I2P User dir migration script");
|
||||
String s = null;
|
||||
// Don't use replaceFirst because backslashes in the replacement string leads to havoc
|
||||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4689750
|
||||
// "Note that backslashes and dollar signs in the replacement string may cause the results
|
||||
// to be different than if it were being treated as a literal replacement string.
|
||||
// Dollar signs may be treated as references to captured subsequences as described above,
|
||||
// and backslashes are used to escape literal characters in the replacement string."
|
||||
while ((s = DataHelper.readLine(in)) != null) {
|
||||
if (s.startsWith("wrapper.java.classpath.")) {
|
||||
// libraries in the old location
|
||||
s = s.replace("=lib/", '=' + olddir.getAbsolutePath() + File.separatorChar + "lib" + File.separatorChar);
|
||||
} else if (s.startsWith("wrapper.java.library.path.")) {
|
||||
// libraries in the old location
|
||||
if (s.contains("=."))
|
||||
s = s.replace("=.", '=' + olddir.getAbsolutePath());
|
||||
else if (s.contains("=lib"))
|
||||
s = s.replace("=lib", '=' + olddir.getAbsolutePath() + File.separatorChar + "lib");
|
||||
} else if (s.startsWith("wrapper.logfile=wrapper.log")) {
|
||||
// wrapper logs in the new location
|
||||
s = s.replace("=", '=' + todir.getAbsolutePath() + File.separatorChar);
|
||||
} else if (s.startsWith("wrapper.pidfile=i2p.pid")) {
|
||||
// i2p.pid in the new location
|
||||
s = s.replace("=", '=' + todir.getAbsolutePath() + File.separatorChar);
|
||||
}
|
||||
out.println(s);
|
||||
}
|
||||
System.err.println("Copied wrapper.config with modifications");
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
if (in != null) {
|
||||
System.err.println("FAILED copy wrapper.config");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
if (out != null) out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy over the jetty.xml file with modifications
|
||||
* It was already copied over once in migrate(), throw that out and
|
||||
* do it again with modifications.
|
||||
*/
|
||||
private static boolean migrateJettyXml(File olddir, File todir) {
|
||||
File eepsite1 = new File(olddir, "eepsite");
|
||||
File oldFile = new File(eepsite1, "jetty.xml");
|
||||
File eepsite2 = new File(todir, "eepsite");
|
||||
File newFile = new File(eepsite2, "jetty.xml");
|
||||
FileInputStream in = null;
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
in = new FileInputStream(oldFile);
|
||||
out = new PrintWriter(new BufferedWriter(new FileWriter(newFile)));
|
||||
String s = null;
|
||||
while ((s = DataHelper.readLine(in)) != null) {
|
||||
if (s.indexOf("./eepsite/") >= 0) {
|
||||
s = s.replace("./eepsite/", todir.getAbsolutePath() + File.separatorChar + "eepsite" + File.separatorChar);
|
||||
}
|
||||
out.println(s);
|
||||
}
|
||||
out.println("<!-- Modified by I2P User dir migration script -->");
|
||||
System.err.println("Copied jetty.xml with modifications");
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
if (in != null) {
|
||||
System.err.println("FAILED copy jetty.xml");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
if (out != null) out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Relatively recent default i2ptunnel key file name
|
||||
*/
|
||||
private static boolean migrateI2PTunnelKeys(File olddir, File todir) {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
copy(new File(olddir, "i2ptunnel" + i + "-privKeys.dat"), todir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive copy a file or dir to a dir
|
||||
*
|
||||
* @param src file or directory, need not exist
|
||||
* @param target the directory to copy to, will be created if it doesn't exist
|
||||
* @return true for success OR if src does not exist
|
||||
*/
|
||||
public static final boolean copy(File src, File targetDir) {
|
||||
if (!src.exists())
|
||||
return true;
|
||||
if (!targetDir.exists()) {
|
||||
if (!targetDir.mkdir()) {
|
||||
System.err.println("FAILED copy " + src.getPath());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File targetFile = new File(targetDir, src.getName());
|
||||
if (!src.isDirectory())
|
||||
return copyFile(src, targetFile);
|
||||
File children[] = src.listFiles();
|
||||
if (children == null) {
|
||||
System.err.println("FAILED copy " + src.getPath());
|
||||
return false;
|
||||
}
|
||||
boolean rv = true;
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
rv &= copy(children[i], targetFile);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param src not a directory, must exist
|
||||
* @param dest not a directory, will be overwritten if existing
|
||||
* @@reurn true if it was copied successfully
|
||||
*/
|
||||
public static boolean copyFile(File src, File dst) {
|
||||
if (!src.exists()) return false;
|
||||
boolean rv = true;
|
||||
|
||||
byte buf[] = new byte[4096];
|
||||
FileInputStream in = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
in = new FileInputStream(src);
|
||||
out = new FileOutputStream(dst);
|
||||
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1)
|
||||
out.write(buf, 0, read);
|
||||
|
||||
System.err.println("Copied " + src.getPath());
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("FAILED copy " + src.getPath());
|
||||
rv = false;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
if (rv)
|
||||
dst.setLastModified(src.lastModified());
|
||||
return rv;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user