package org.torproject.jtor.control.impl;
import java.util.HashMap;
import java.util.Iterator;
import org.torproject.jtor.control.ControlConnectionHandler;
import org.torproject.jtor.control.FeatureNotSupportedException;
import org.torproject.jtor.control.KeyNotFoundException;
import org.torproject.jtor.control.auth.ControlAuthenticator;
import org.torproject.jtor.control.commands.*;
/**
*
* @author Merlijn Hofstra
*/
public class ControlCommandParser {
private ControlCommandParser() {}
@SuppressWarnings("unchecked")
public static void execute(ControlConnectionHandler cch, String in) {
String command = in.substring(0, in.indexOf(" ")).toLowerCase();
String args = in.substring(in.indexOf(" ", 0)+1);
args = removeQuotes(args);
if (command.startsWith("quit")) {
cch.disconnect();
}
else if (command.startsWith("authenticate")) {
if (ControlAuthenticator.authenticate(cch.getControlServer().getTorConfig(), args)) {
cch.setAuthenticated(true);
cch.write("250 OK");
} else {
cch.write("515 Bad authentication");
cch.disconnect();
}
}
else if (command.startsWith("protocolinfo")) {
if (!cch.isRequestedProtocolinfo() || cch.isAuthenticated()) {
cch.setRequestedProtocolinfo(!cch.isAuthenticated());
ControlCommandProtocolInfo.handleProtocolInfo(cch);
} else {
cch.getControlServer().getLogger().debug("Control command: refused repeated protocolinfo to unauthenticated client");
cch.disconnect();
}
}
else if (!cch.isAuthenticated()) { // user is trying something illegal
cch.disconnect();
}
else if (command.equals("setconf")) {
ControlCommandSetConf.handleSetConf(cch, args);
}
else if (command.equals("resetconf")) {
ControlCommandSetConf.handleSetConf(cch, args);
}
else if (command.equals("getconf")) {
String[] confs = args.split(" ");
HashMap pairs = new HashMap();
for (int i = 0; i < confs.length; i++) {
try {
if (confs[i].toLowerCase().equals("hiddenserviceoptions")) {
pairs.put("HiddenServiceDir", ControlCommandGetConf.handleGetConf(cch, "HiddenServiceDir"));
pairs.put("HiddenServicePort", ControlCommandGetConf.handleGetConf(cch, "HiddenServicePort"));
pairs.put("HiddenServiceNodes", ControlCommandGetConf.handleGetConf(cch, "HiddenServiceNodes"));
pairs.put("HiddenServiceExcludeNodes", ControlCommandGetConf.handleGetConf(cch, "HiddenServiceExcludeNodes"));
} else {
String value = ControlCommandGetConf.handleGetConf(cch, confs[i]);
pairs.put(confs[i], value);
}
} catch (KeyNotFoundException e) {
cch.write("552 unknown configuration keyword");
cch.getControlServer().getLogger().warning("Control command: key not found: " + confs[i]);
return;
}
}
// reply with key=value pairs
Iterator it = pairs.keySet().iterator();
while (it.hasNext()) {
String key = (String)it.next();
String val = ((String)pairs.get(key));
if (val == null || val.equals("")) {
cch.write("250 " + key);
continue;
}
String[] vals = val.split("\n");
for (int i = 0; i < vals.length; i++) {
if (vals[i] == null || vals[i].equals("")) {
// default value
cch.write("250 " + key);
} else {
cch.write("250 " + key + "=" + vals[i]);
}
}
}
}
else if (command.equals("signal")) {
if (ControlCommandSignal.handleSignal(cch, args)) {
cch.write("250 OK");
} else {
cch.write("552 Unrecognized signal");
cch.getControlServer().getLogger().warning("Control command: unrecognized signal: " + args);
}
}
else if (command.equals("mapaddress")) {
String[] maps = args.split(" ");
// check syntax
for (int i = 0; i < maps.length; i++) {
if (maps[i].indexOf("=") == -1) {
cch.write("512 syntax error in command argument");
return;
}
}
for (int i = 0; i < maps.length; i++) {
if (ControlCommandMapAddress.handleMapAddress(cch, maps[i])) {
cch.write("250 " + maps[i]);
}
}
}
else if (command.equals("saveconf")) {
if(cch.getControlServer().getTorConfig().saveConf()) {
cch.write("250 OK");
cch.getControlServer().getLogger().debug("Control command: saving config");
} else {
cch.write("551 Unable to write configuration to disk");
cch.getControlServer().getLogger().error("Control command: could not save config file");
}
}
else if (command.equals("getinfo")) {
String[] confs = args.split(" ");
HashMap pairs = new HashMap();
for (int i = 0; i < confs.length; i++) {
try {
String value = ControlCommandGetInfo.handleGetInfo(cch, confs[i]);
pairs.put(confs[i], value);
} catch (KeyNotFoundException e) {
cch.write("552 unknown configuration keyword");
cch.getControlServer().getLogger().warning("Control command: key not found: " + confs[i]);
return;
} catch (FeatureNotSupportedException e) {
cch.write("551 feature not supported");
return;
}
}
// reply with key=value pairs
Iterator it = pairs.keySet().iterator();
while (it.hasNext()) {
String key = (String)it.next();
String val = ((String)pairs.get(key));
if (val.indexOf("\n") > 0) {
cch.write("250+" + key + "=");
String[] vals = val.split("\n");
for (int i = 0; i < vals.length; i++) {
cch.write(vals[i]);
}
cch.write(".");
} else {
cch.write("250 " + key + "=" + val);
}
}
}
else if (command.equals("usefeature")) {
ControlCommandUseFeature.handleUseFeature(cch, args);
}
else if (command.equals("setevents")) {
try {
ControlCommandSetEvents.handleSetEvent(cch, args);
cch.write("250 OK");
} catch (KeyNotFoundException e) {
cch.write("552 Unrecognized event");
}
}
}
/** Removes any unescaped quotes from a given string */
public static String removeQuotes(String in) {
int index = in.indexOf("\"");
while (index < in.length() && index > 0) {
if (!in.substring(index-1, index).equals("\\")) {
//remove the quote as it's not escaped
in = in.substring(0, index) + in.substring(index+1);
}
index = in.indexOf("\"", index);
}
return in;
}
}