package com.subgraph.orchid.config; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import com.subgraph.orchid.TorConfig; import com.subgraph.orchid.TorConfig.ConfigVar; import com.subgraph.orchid.TorConfig.ConfigVarType; import com.subgraph.orchid.data.HexDigest; import com.subgraph.orchid.data.IPv4Address; public class TorConfigProxy implements InvocationHandler { private final Map<String, Object> configValues; private final List<TorConfigBridgeLine> bridges; private final TorConfigParser parser; public TorConfigProxy() { this.configValues = new HashMap<String, Object>(); this.bridges = new ArrayList<TorConfigBridgeLine>(); this.configValues.put("Bridges", bridges); this.parser = new TorConfigParser(); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().startsWith("set")) { invokeSetMethod(method, args); return null; } else if(method.getName().startsWith("get")) { if(args == null) { return invokeGetMethod(method); } else { return invokeGetMethodWithArgs(method, args); } } else if(method.getName().startsWith("add")) { invokeAddMethod(method, args); return null; } else { throw new IllegalArgumentException(); } } void invokeSetMethod(Method method, Object[] args) { final String name = getVariableNameForMethod(method); final ConfigVar annotation = getAnnotationForVariable(name); if(annotation != null && annotation.type() == ConfigVarType.INTERVAL) { setIntervalValue(name, args); } else { configValues.put(name, args[0]); } } private void setIntervalValue(String varName, Object[] args) { if(!(args[0] instanceof Long && args[1] instanceof TimeUnit)) { throw new IllegalArgumentException(); } final long time = (Long) args[0]; final TimeUnit unit = (TimeUnit) args[1]; final TorConfigInterval interval = new TorConfigInterval(time, unit); configValues.put(varName, interval); } private Object invokeGetMethodWithArgs(Method method, Object[] args) { final String varName = getVariableNameForMethod(method); if(getVariableType(varName) == ConfigVarType.HS_AUTH) { return invokeHSAuthGet(varName, args); } else { throw new IllegalArgumentException(); } } private Object invokeGetMethod(Method method) { final String varName = getVariableNameForMethod(method); final Object value = getVariableValue(varName); if(value instanceof TorConfigInterval) { final TorConfigInterval interval = (TorConfigInterval) value; return interval.getMilliseconds(); } else { return value; } } private Object invokeHSAuthGet(String varName, Object[] args) { if(!(args[0] instanceof String)) { throw new IllegalArgumentException(); } final TorConfigHSAuth hsAuth = getHSAuth(varName); return hsAuth.get((String) args[0]); } private void invokeAddMethod(Method method, Object[] args) { final String name = getVariableNameForMethod(method); final ConfigVarType type = getVariableType(name); switch(type) { case HS_AUTH: invokeHSAuthAdd(name, args); break; case BRIDGE_LINE: invokeBridgeAdd(args); break; default: throw new UnsupportedOperationException("addX configuration methods only supported for HS_AUTH or BRIDGE_LINE type"); } } private void invokeBridgeAdd(Object[] args) { if(args.length >= 2 && (args[0] instanceof IPv4Address) && (args[1] instanceof Integer)) { if(args.length == 2) { bridges.add(new TorConfigBridgeLine((IPv4Address)args[0], (Integer)args[1], null)); return; } else if(args.length == 3 && (args[2] instanceof HexDigest)) { bridges.add(new TorConfigBridgeLine((IPv4Address) args[0], (Integer) args[1], (HexDigest) args[2])); return; } } throw new IllegalArgumentException(); } private void invokeHSAuthAdd(String name, Object[] args) { if(!(args.length == 2 && (args[0] instanceof String) && (args[1] instanceof String))) { throw new IllegalArgumentException(); } final TorConfigHSAuth hsAuth = getHSAuth(name); hsAuth.add((String)args[0], (String)args[1]); } private TorConfigHSAuth getHSAuth(String keyName) { if(!configValues.containsKey(keyName)) { configValues.put(keyName, new TorConfigHSAuth()); } return (TorConfigHSAuth) configValues.get(keyName); } private Object getVariableValue(String varName) { if(configValues.containsKey(varName)) { return configValues.get(varName); } else { return getDefaultVariableValue(varName); } } private Object getDefaultVariableValue(String varName) { final String defaultValue = getDefaultValueString(varName); final ConfigVarType type = getVariableType(varName); if(defaultValue == null || type == null) { return null; } return parser.parseValue(defaultValue, type); } private String getDefaultValueString(String varName) { final ConfigVar var = getAnnotationForVariable(varName); if(var == null) { return null; } else { return var.defaultValue(); } } private ConfigVarType getVariableType(String varName) { if("Bridge".equals(varName)) { return ConfigVarType.BRIDGE_LINE; } final ConfigVar var = getAnnotationForVariable(varName); if(var == null) { return null; } else { return var.type(); } } private String getVariableNameForMethod(Method method) { final String methodName = method.getName(); if(methodName.startsWith("get") || methodName.startsWith("set") || methodName.startsWith("add")) { return methodName.substring(3); } throw new IllegalArgumentException(); } private ConfigVar getAnnotationForVariable(String varName) { final String getName = "get"+ varName; for(Method m: TorConfig.class.getDeclaredMethods()) { if(getName.equals(m.getName())) { return m.getAnnotation(TorConfig.ConfigVar.class); } } return null; } }