// Path: tor/src/main/java/com/msopentech/thali/toronionproxy/OnionProxyManagerEventHandler.java
// public class OnionProxyManagerEventHandler implements EventHandler {
//     private static final Logger LOG = LoggerFactory.getLogger(OnionProxyManagerEventHandler.class);
//     private String lastLog="";
//     public String getLastLog()
//     {
//         return lastLog;
//     }
// /*
//     public void circuitStatus(String status, String id, List<String> path, Map<String, String> info) {
//         String msg = "CircuitStatus: " + id + " " + status;
//         String purpose = info.get("PURPOSE");
//         if(purpose != null) msg += ", purpose: " + purpose;
//         String hsState = info.get("HS_STATE");
//         if(hsState != null) msg += ", state: " + hsState;
//         String rendQuery = info.get("REND_QUERY");
//         if(rendQuery != null) msg += ", service: " + rendQuery;
//         if(!path.isEmpty()) msg += ", path: " + shortenPath(path);
//         LOG.info(msg);
//     }*/
//     public void circuitStatus(String status, String circID, String path) {
//         String msg = "CircuitStatus: " + circID + " " + status;
//         //String purpose = info.get("PURPOSE");
//         //if(purpose != null) msg += ", purpose: " + purpose;
//         //String hsState = info.get("HS_STATE");
//         //if(hsState != null) msg += ", state: " + hsState;
//         //String rendQuery = info.get("REND_QUERY");
//         //if(rendQuery != null) msg += ", service: " + rendQuery;
//         if(!path.isEmpty()) msg += ", path: " + shortenPath(path);
// //        LOG.info(msg);
//         lastLog=msg;
//     }
// 
//     public void streamStatus(String status, String id, String target) {
//         LOG.info("streamStatus: status: " + status + ", id: " + id + ", target: " + target);
//     }
// 
//     public void orConnStatus(String status, String orName) {
//         LOG.info("OR connection: status: " + status + ", orName: " + orName);
//     }
// 
//     public void bandwidthUsed(long read, long written) {
//         LOG.info("bandwidthUsed: read: " + read + ", written: " + written);
//     }
// 
//     public void newDescriptors(List<String> orList) {
//         Iterator<String> iterator = orList.iterator();
//         StringBuilder stringBuilder = new StringBuilder();
//         while(iterator.hasNext()) {
//             stringBuilder.append(iterator.next());
//         }
//         LOG.info("newDescriptors: " + stringBuilder.toString());
//     }
// 
//     public void message(String severity, String msg) {
//         LOG.info("message: severity: " + severity + ", msg: " + msg);
//         lastLog=msg;
//     }
// 
//     public void unrecognized(String type, String msg) {
//         LOG.info("unrecognized: type: " + type + ", msg: " + msg);
//         lastLog=msg;
//     }
// 
//     private String shortenPath(List<String> path) {
//         StringBuilder s = new StringBuilder();
//         for(String id : path) {
//             if(s.length() > 0) s.append(',');
//             s.append(id.substring(1, 7));
//         }
//         return s.toString();
//     }
//     private String shortenPath(String id) {
//         StringBuilder s = new StringBuilder();
//             if(s.length() > 0) s.append(',');
//             s.append(id.substring(1, 7));
//         return s.toString();
//     }
// 
// }


import com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
        StringBuffer sb = new StringBuffer("MAPADDRESS");
        for (Iterator<String> it = kvLines.iterator(); it.hasNext(); ) {
            String kv = it.next();
            int i = kv.indexOf(' ');
            sb.append(" ").append(kv.substring(0,i)).append("=")
                .append(quote(kv.substring(i+1)));
        }
        sb.append("\r\n");
        List<ReplyLine> lst = sendAndWaitForResponse(sb.toString(), null);
        Map<String,String> result = new HashMap<String,String>();
        for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) {
            String kv = (it.next()).msg;
            int idx = kv.indexOf('=');
            result.put(kv.substring(0, idx),
                       kv.substring(idx+1));
        }
        return result;
    }

    public Map<String,String> mapAddresses(Map<String,String> addresses) throws IOException {
        List<String> kvList = new ArrayList<String>();
        for (Iterator<Map.Entry<String, String>> it = addresses.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<String,String> e = it.next();
            kvList.add(e.getKey()+" "+e.getValue());
        }
        return mapAddresses(kvList);
    }

    public String mapAddress(String fromAddr, String toAddr) throws IOException {
        List<String> lst = new ArrayList<String>();
// Path: tor/src/main/java/com/msopentech/thali/toronionproxy/OnionProxyManagerEventHandler.java
// public class OnionProxyManagerEventHandler implements EventHandler {
//     private static final Logger LOG = LoggerFactory.getLogger(OnionProxyManagerEventHandler.class);
//     private String lastLog="";
//     public String getLastLog()
//     {
//         return lastLog;
//     }
// /*
//     public void circuitStatus(String status, String id, List<String> path, Map<String, String> info) {
//         String msg = "CircuitStatus: " + id + " " + status;
//         String purpose = info.get("PURPOSE");
//         if(purpose != null) msg += ", purpose: " + purpose;
//         String hsState = info.get("HS_STATE");
//         if(hsState != null) msg += ", state: " + hsState;
//         String rendQuery = info.get("REND_QUERY");
//         if(rendQuery != null) msg += ", service: " + rendQuery;
//         if(!path.isEmpty()) msg += ", path: " + shortenPath(path);
//         LOG.info(msg);
//     }*/
//     public void circuitStatus(String status, String circID, String path) {
//         String msg = "CircuitStatus: " + circID + " " + status;
//         //String purpose = info.get("PURPOSE");
//         //if(purpose != null) msg += ", purpose: " + purpose;
//         //String hsState = info.get("HS_STATE");
//         //if(hsState != null) msg += ", state: " + hsState;
//         //String rendQuery = info.get("REND_QUERY");
//         //if(rendQuery != null) msg += ", service: " + rendQuery;
//         if(!path.isEmpty()) msg += ", path: " + shortenPath(path);
// //        LOG.info(msg);
//         lastLog=msg;
//     }
// 
//     public void streamStatus(String status, String id, String target) {
//         LOG.info("streamStatus: status: " + status + ", id: " + id + ", target: " + target);
//     }
// 
//     public void orConnStatus(String status, String orName) {
//         LOG.info("OR connection: status: " + status + ", orName: " + orName);
//     }
// 
//     public void bandwidthUsed(long read, long written) {
//         LOG.info("bandwidthUsed: read: " + read + ", written: " + written);
//     }
// 
//     public void newDescriptors(List<String> orList) {
//         Iterator<String> iterator = orList.iterator();
//         StringBuilder stringBuilder = new StringBuilder();
//         while(iterator.hasNext()) {
//             stringBuilder.append(iterator.next());
//         }
//         LOG.info("newDescriptors: " + stringBuilder.toString());
//     }
// 
//     public void message(String severity, String msg) {
//         LOG.info("message: severity: " + severity + ", msg: " + msg);
//         lastLog=msg;
//     }
// 
//     public void unrecognized(String type, String msg) {
//         LOG.info("unrecognized: type: " + type + ", msg: " + msg);
//         lastLog=msg;
//     }
// 
//     private String shortenPath(List<String> path) {
//         StringBuilder s = new StringBuilder();
//         for(String id : path) {
//             if(s.length() > 0) s.append(',');
//             s.append(id.substring(1, 7));
//         }
//         return s.toString();
//     }
//     private String shortenPath(String id) {
//         StringBuilder s = new StringBuilder();
//             if(s.length() > 0) s.append(',');
//             s.append(id.substring(1, 7));
//         return s.toString();
//     }
// 
// }


// Path: tor/src/main/java/net/freehaven/tor/control/TorControlConnection.java
import com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

        StringBuffer sb = new StringBuffer("MAPADDRESS");
        for (Iterator<String> it = kvLines.iterator(); it.hasNext(); ) {
            String kv = it.next();
            int i = kv.indexOf(' ');
            sb.append(" ").append(kv.substring(0,i)).append("=")
                .append(quote(kv.substring(i+1)));
        }
        sb.append("\r\n");
        List<ReplyLine> lst = sendAndWaitForResponse(sb.toString(), null);
        Map<String,String> result = new HashMap<String,String>();
        for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) {
            String kv = (it.next()).msg;
            int idx = kv.indexOf('=');
            result.put(kv.substring(0, idx),
                       kv.substring(idx+1));
        }
        return result;
    }

    public Map<String,String> mapAddresses(Map<String,String> addresses) throws IOException {
        List<String> kvList = new ArrayList<String>();
        for (Iterator<Map.Entry<String, String>> it = addresses.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<String,String> e = it.next();
            kvList.add(e.getKey()+" "+e.getValue());
        }
        return mapAddresses(kvList);
    }

    public String mapAddress(String fromAddr, String toAddr) throws IOException {
        List<String> lst = new ArrayList<String>();
        lst.add(fromAddr+" "+toAddr+"\n");