forked from I2P_Developers/i2p.i2p
Compare commits
5 Commits
i2p.i2p.2.
...
bulk-confi
Author | SHA1 | Date | |
---|---|---|---|
56f76d8162 | |||
62c5d1a3e4 | |||
9047ada936 | |||
82c9e027da | |||
7312e9693d |
265
router/java/src/net/i2p/router/ClientConfigManager.java
Normal file
265
router/java/src/net/i2p/router/ClientConfigManager.java
Normal file
@ -0,0 +1,265 @@
|
||||
package net.i2p.router;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Pattern;
|
||||
import net.i2p.router.startup.WorkingDir;
|
||||
import net.i2p.util.FileSuffixFilter;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Execute bulk edits against I2P application config files or config
|
||||
* directories. This is a command-line application only, which is intended
|
||||
* to work agnostic of whether the router is runnning.
|
||||
*
|
||||
* @author idk 2023
|
||||
*/
|
||||
public class ClientConfigManager extends WorkingDir {
|
||||
private String clientName = null;
|
||||
private String workDir = null;
|
||||
/**
|
||||
* editPropertiesFile opens a single properties(.config) file and edits
|
||||
* every entry in it that matches the regular expression `prop` to contain
|
||||
* the value `value`.
|
||||
* @param clientAppConfig
|
||||
* @param prop
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
private boolean editPropertiesFile(File clientAppConfig, String prop,
|
||||
String value)
|
||||
throws FileNotFoundException {
|
||||
System.out.println("editing config file " + clientAppConfig.getName());
|
||||
boolean go = true;
|
||||
Properties clientAppConfigProps = new Properties();
|
||||
File backupConfig = new File(clientAppConfig + ".bak");
|
||||
FileUtil.copy(clientAppConfig, backupConfig, true,
|
||||
false);
|
||||
try {
|
||||
FileInputStream clientAppConfigReader = new FileInputStream(backupConfig);
|
||||
clientAppConfigProps.load(clientAppConfigReader);
|
||||
final Iterator entries = clientAppConfigProps.entrySet().iterator();
|
||||
while (entries.hasNext()) {
|
||||
final Map.Entry entry = (Map.Entry)entries.next();
|
||||
String key = (String)entry.getKey();
|
||||
if (Pattern.matches(prop, key)) {
|
||||
clientAppConfigProps.setProperty(key, value);
|
||||
// System.out.println("set property " + key + "=" + value);
|
||||
}
|
||||
}
|
||||
try {
|
||||
FileWriter clientAppConfigWriter = new FileWriter(clientAppConfig);
|
||||
clientAppConfigProps.store(clientAppConfigWriter,
|
||||
"inserted by ClientConfigManager");
|
||||
} catch (IOException e) {
|
||||
go = false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
go = false;
|
||||
} catch (IllegalStateException e) {
|
||||
go = false;
|
||||
}
|
||||
return go;
|
||||
}
|
||||
/**
|
||||
* editClientsConfig checks clients.config and clients.config.d to enable
|
||||
* bulk configuration of client applications
|
||||
*
|
||||
* @param configDir
|
||||
* @param prop
|
||||
* @param value
|
||||
* @return
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public void editClientsConfig(File configDir, String prop, String value)
|
||||
throws FileNotFoundException {
|
||||
File clientAppConfigDir = new File(configDir, "clients.config.d");
|
||||
if (clientAppConfigDir.exists()) {
|
||||
File[] clientAppConfigFiles =
|
||||
clientAppConfigDir.listFiles(new FileSuffixFilter(".config"));
|
||||
if (clientAppConfigFiles != null) {
|
||||
for (int i = 0; i < clientAppConfigFiles.length; i++) {
|
||||
boolean cont =
|
||||
editPropertiesFile(clientAppConfigFiles[i], prop, value);
|
||||
if (!cont)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
File clientAppConfig = new File(configDir, "clients.config");
|
||||
if (!clientAppConfig.exists())
|
||||
throw new FileNotFoundException();
|
||||
editPropertiesFile(clientAppConfig, prop, value);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* editClientAppConfig edits the configuration files for a client based
|
||||
* on the client's name(the un-translated one). Common values would be
|
||||
* `i2ptunnel` or `i2psnark`. It can handle both monolithic and split-file
|
||||
* confiurations I2P apps.
|
||||
*
|
||||
* @param configDir
|
||||
* @param clientName
|
||||
* @param prop
|
||||
* @param value
|
||||
* @return
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public void editClientAppConfig(File configDir, String clientName,
|
||||
String prop, String value)
|
||||
throws FileNotFoundException {
|
||||
File i2pTunnelConfigDir = new File(configDir, clientName + ".config.d");
|
||||
if (i2pTunnelConfigDir.exists()) {
|
||||
File[] i2pTunnelConfigFiles =
|
||||
i2pTunnelConfigDir.listFiles(new FileSuffixFilter(".config"));
|
||||
if (i2pTunnelConfigFiles != null) {
|
||||
for (int i = 0; i < i2pTunnelConfigFiles.length; i++) {
|
||||
boolean cont =
|
||||
editPropertiesFile(i2pTunnelConfigFiles[i], prop, value);
|
||||
if (!cont)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
File i2pTunnelConfig = new File(configDir, clientName + ".config");
|
||||
if (!i2pTunnelConfig.exists())
|
||||
throw new FileNotFoundException();
|
||||
editPropertiesFile(i2pTunnelConfig, prop, value);
|
||||
}
|
||||
}
|
||||
private class PropSet {
|
||||
String key;
|
||||
String value;
|
||||
PropSet(String inkey, String invalue) {
|
||||
key = inkey;
|
||||
value = invalue;
|
||||
}
|
||||
public String toString() {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
if (value == null) {
|
||||
return key + "=";
|
||||
}
|
||||
return key + "=" + value;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* parses arguments and flags from the command line. Valid flags are:
|
||||
*
|
||||
* -clientapp name
|
||||
* -workdir path
|
||||
*
|
||||
* which must be followed by 2 trailing arguments, the first of which is
|
||||
* a regular expression, the second of which is a value which will be applied
|
||||
* to all keys matching the regular expression.
|
||||
*
|
||||
* @param args
|
||||
* @return PropSet containing the regex to match against the key, and the
|
||||
* value to set
|
||||
* @throws Error
|
||||
*/
|
||||
public PropSet parseArgs(String args[]) throws Error {
|
||||
if (args.length < 2) {
|
||||
throw new Error("insufficient bulk edit arguments");
|
||||
} else if (args.length == 2) {
|
||||
return new PropSet(args[0], args[1]);
|
||||
}
|
||||
String key = null;
|
||||
String value = null;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (args[i].startsWith("-")) {
|
||||
if (args[i].equals("-clientapp")) {
|
||||
if (args.length >= i + 1) {
|
||||
System.out.println("Setting clientName to " + args[i] +
|
||||
args[i + 1]);
|
||||
clientName = args[i + 1];
|
||||
i++;
|
||||
} else
|
||||
throw new Error(
|
||||
"Insufficient arguments, -clientapp requires an an application name");
|
||||
} else if (args[i].equals("-clientapp")) {
|
||||
if (args.length >= i + 1) {
|
||||
System.out.println("Setting workDir to " + args[i] + args[i + 1]);
|
||||
workDir = args[i + 1];
|
||||
i++;
|
||||
} else
|
||||
throw new Error(
|
||||
"Insufficient arguments, -workdir requires an a directory");
|
||||
}
|
||||
} else {
|
||||
if (key == null)
|
||||
key = args[i];
|
||||
else {
|
||||
value = args[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new PropSet(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* editClientApp helps determine if we're editing clients.config or an
|
||||
* application config.
|
||||
*
|
||||
* @return true if editing an app, false if editing clients.config
|
||||
*/
|
||||
public boolean editClientApp() {
|
||||
if (clientName != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* get the "default" config directory for your platform.
|
||||
*
|
||||
* @return the default configuration directory, or workDir if it is set.
|
||||
*/
|
||||
public File getConfigDir() {
|
||||
if (workDir != null) {
|
||||
System.out.println("workdir set to" + workDir);
|
||||
return new File(workDir);
|
||||
}
|
||||
System.out.println("finding default config dir");
|
||||
boolean isWindows = SystemVersion.isWindows();
|
||||
String defaultPath = getDefaultDir(isWindows).getAbsolutePath();
|
||||
System.out.println("using default config dir " + defaultPath);
|
||||
return new File(defaultPath);
|
||||
}
|
||||
public static void main(String args[]) {
|
||||
System.out.println("ClientConfigManager");
|
||||
ClientConfigManager ccm = new ClientConfigManager();
|
||||
try {
|
||||
PropSet propSet = ccm.parseArgs(args);
|
||||
System.out.println("args are " + propSet.toString());
|
||||
File configDir = ccm.getConfigDir();
|
||||
System.out.println("configs are " + configDir.getAbsolutePath());
|
||||
if (ccm.editClientApp()) {
|
||||
try {
|
||||
ccm.editClientAppConfig(configDir, ccm.clientName, propSet.key,
|
||||
propSet.value);
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println(
|
||||
"Client not found, check name and config directory");
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
ccm.editClientsConfig(configDir, propSet.key, propSet.value);
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println(
|
||||
"Client config file not found, check config directory");
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||
public class CommandLine extends net.i2p.util.CommandLine {
|
||||
|
||||
protected static final List<String> RCLASSES = Arrays.asList(new String[] {
|
||||
"net.i2p.router.ClientConfigManager",
|
||||
"com.maxmind.geoip2.DatabaseReader",
|
||||
"net.i2p.data.router.RouterInfo",
|
||||
"net.i2p.data.router.RouterKeyGenerator",
|
||||
|
@ -56,6 +56,103 @@ public class WorkingDir {
|
||||
/** Feb 16 2006 */
|
||||
private static final long EEPSITE_TIMESTAMP = 1140048000000l;
|
||||
|
||||
|
||||
/**
|
||||
* Determines the default working directory for a given platform by examining
|
||||
* the host system, and applies secure configuration to that directory.
|
||||
*
|
||||
* @param isWindows set to true if the host system is Windows, false if it's anything else
|
||||
* @return a File containing the working directory
|
||||
*/
|
||||
public static File getDefaultDir(boolean isWindows){
|
||||
File dirf = null;
|
||||
String gentooWarning = null;
|
||||
String home = System.getProperty("user.home");
|
||||
if (isWindows) {
|
||||
String localappdata = System.getenv("LOCALAPPDATA");
|
||||
if (localappdata != null) {
|
||||
home = localappdata;
|
||||
}
|
||||
// Don't mess with existing Roaming Application Data installs,
|
||||
// in case somebody is using roaming appdata for a reason
|
||||
// already. In new installs, use local appdata by default. -idk
|
||||
String appdata = System.getenv("APPDATA");
|
||||
if (appdata != null) {
|
||||
File checkOld = new File(appdata, WORKING_DIR_DEFAULT_WINDOWS);
|
||||
if (checkOld.exists() && checkOld.isDirectory()){
|
||||
File routerConfig = new File(checkOld.getAbsolutePath(), "router.config");
|
||||
// The Firefox profile installer was mistakenly using the Roaming application data
|
||||
// which is synced between devices on some Windows machines using MS cloud services,
|
||||
// instead of the local application data which is used by default.
|
||||
// It would create the router.config file in an empty directory, which the router would
|
||||
// then attempt to use, resulting in a router with no client applications. Checking
|
||||
// for clients.config.d determines if the directory is "Real" or not.
|
||||
File clientAppsConfig = new File(checkOld.getAbsolutePath(), "clients.config.d");
|
||||
if (routerConfig.exists() && clientAppsConfig.exists() && clientAppsConfig.isDirectory()) {
|
||||
home = appdata;
|
||||
} else {
|
||||
clientAppsConfig = new File(checkOld.getAbsolutePath(), "clients.config");
|
||||
if (routerConfig.exists() && clientAppsConfig.exists())
|
||||
home = appdata;
|
||||
}
|
||||
System.err.println("System is Windows: " + home);
|
||||
}
|
||||
}
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_WINDOWS);
|
||||
} else if (SystemVersion.isMac()) {
|
||||
String appdata = "/Library/Application Support/";
|
||||
File old = new File(home,WORKING_DIR_DEFAULT);
|
||||
if (old.exists() && old.isDirectory())
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT);
|
||||
else {
|
||||
home = home+appdata;
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_MAC);
|
||||
}
|
||||
} else {
|
||||
if (SystemVersion.isLinuxService()) {
|
||||
if (SystemVersion.isGentoo() &&
|
||||
SystemVersion.GENTOO_USER.equals(System.getProperty("user.name"))) {
|
||||
// whoops, we didn't recognize Gentoo as a service until 0.9.29,
|
||||
// so the config dir was /var/lib/i2p/.i2p through 0.9.28
|
||||
// and changed to /var/lib/i2p/i2p-config in 0.9.29.
|
||||
// Look for both to decide which to use.
|
||||
// We prefer .i2p if neither exists.
|
||||
// We prefer the newer if both exist.
|
||||
File d1 = new SecureDirectory(home, WORKING_DIR_DEFAULT);
|
||||
File d2 = new SecureDirectory(home, WORKING_DIR_DEFAULT_DAEMON);
|
||||
boolean e1 = isSetup(d1);
|
||||
boolean e2 = isSetup(d2);
|
||||
if (e1 && e2) {
|
||||
// d1 is probably older. Switch if it isn't.
|
||||
if (d2.lastModified() < d1.lastModified()) {
|
||||
File tmp = d2;
|
||||
d2 = d1;
|
||||
d1 = tmp;
|
||||
// d1 now is the older one
|
||||
}
|
||||
dirf = d2;
|
||||
gentooWarning = "Warning - Found both an old configuration directory " + d1.getAbsolutePath() +
|
||||
" and new configuration directory " + d2.getAbsolutePath() +
|
||||
" created due to a bug in release 0.9.29\n. Using the new configuration" +
|
||||
" directory. To use the old directory instead, stop i2p," +
|
||||
" delete the new directory, and restart.";
|
||||
} else if (e1 && !e2) {
|
||||
dirf = d1;
|
||||
} else if (!e1 && e2) {
|
||||
dirf = d2;
|
||||
} else {
|
||||
dirf = d1;
|
||||
}
|
||||
} else {
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_DAEMON);
|
||||
}
|
||||
} else {
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT);
|
||||
}
|
||||
}
|
||||
return dirf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only call this once on router invocation.
|
||||
* Caller should store the return value for future reference.
|
||||
@ -78,89 +175,7 @@ public class WorkingDir {
|
||||
if (dir != null) {
|
||||
dirf = new SecureDirectory(dir);
|
||||
} else {
|
||||
String home = System.getProperty("user.home");
|
||||
if (isWindows) {
|
||||
String localappdata = System.getenv("LOCALAPPDATA");
|
||||
if (localappdata != null) {
|
||||
home = localappdata;
|
||||
}
|
||||
// Don't mess with existing Roaming Application Data installs,
|
||||
// in case somebody is using roaming appdata for a reason
|
||||
// already. In new installs, use local appdata by default. -idk
|
||||
String appdata = System.getenv("APPDATA");
|
||||
if (appdata != null) {
|
||||
File checkOld = new File(appdata, WORKING_DIR_DEFAULT_WINDOWS);
|
||||
if (checkOld.exists() && checkOld.isDirectory()){
|
||||
File routerConfig = new File(checkOld.getAbsolutePath(), "router.config");
|
||||
// The Firefox profile installer was mistakenly using the Roaming application data
|
||||
// which is synced between devices on some Windows machines using MS cloud services,
|
||||
// instead of the local application data which is used by default.
|
||||
// It would create the router.config file in an empty directory, which the router would
|
||||
// then attempt to use, resulting in a router with no client applications. Checking
|
||||
// for clients.config.d determines if the directory is "Real" or not.
|
||||
File clientAppsConfig = new File(checkOld.getAbsolutePath(), "clients.config.d");
|
||||
if (routerConfig.exists() && clientAppsConfig.exists() && clientAppsConfig.isDirectory()) {
|
||||
home = appdata;
|
||||
} else {
|
||||
clientAppsConfig = new File(checkOld.getAbsolutePath(), "clients.config");
|
||||
if (routerConfig.exists() && clientAppsConfig.exists())
|
||||
home = appdata;
|
||||
}
|
||||
System.err.println("System is Windows: " + home);
|
||||
}
|
||||
}
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_WINDOWS);
|
||||
} else if (SystemVersion.isMac()) {
|
||||
String appdata = "/Library/Application Support/";
|
||||
File old = new File(home,WORKING_DIR_DEFAULT);
|
||||
if (old.exists() && old.isDirectory())
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT);
|
||||
else {
|
||||
home = home+appdata;
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_MAC);
|
||||
}
|
||||
} else {
|
||||
if (SystemVersion.isLinuxService()) {
|
||||
if (SystemVersion.isGentoo() &&
|
||||
SystemVersion.GENTOO_USER.equals(System.getProperty("user.name"))) {
|
||||
// whoops, we didn't recognize Gentoo as a service until 0.9.29,
|
||||
// so the config dir was /var/lib/i2p/.i2p through 0.9.28
|
||||
// and changed to /var/lib/i2p/i2p-config in 0.9.29.
|
||||
// Look for both to decide which to use.
|
||||
// We prefer .i2p if neither exists.
|
||||
// We prefer the newer if both exist.
|
||||
File d1 = new SecureDirectory(home, WORKING_DIR_DEFAULT);
|
||||
File d2 = new SecureDirectory(home, WORKING_DIR_DEFAULT_DAEMON);
|
||||
boolean e1 = isSetup(d1);
|
||||
boolean e2 = isSetup(d2);
|
||||
if (e1 && e2) {
|
||||
// d1 is probably older. Switch if it isn't.
|
||||
if (d2.lastModified() < d1.lastModified()) {
|
||||
File tmp = d2;
|
||||
d2 = d1;
|
||||
d1 = tmp;
|
||||
// d1 now is the older one
|
||||
}
|
||||
dirf = d2;
|
||||
gentooWarning = "Warning - Found both an old configuration directory " + d1.getAbsolutePath() +
|
||||
" and new configuration directory " + d2.getAbsolutePath() +
|
||||
" created due to a bug in release 0.9.29\n. Using the new configuration" +
|
||||
" directory. To use the old directory instead, stop i2p," +
|
||||
" delete the new directory, and restart.";
|
||||
} else if (e1 && !e2) {
|
||||
dirf = d1;
|
||||
} else if (!e1 && e2) {
|
||||
dirf = d2;
|
||||
} else {
|
||||
dirf = d1;
|
||||
}
|
||||
} else {
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_DAEMON);
|
||||
}
|
||||
} else {
|
||||
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT);
|
||||
}
|
||||
}
|
||||
dirf = getDefaultDir(isWindows);
|
||||
}
|
||||
|
||||
// where we are now
|
||||
|
Reference in New Issue
Block a user