package org.limewire.lws.server;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.limewire.lws.server.LWSDispatcherSupport;
import org.limewire.lws.server.LWSServerUtil;
import org.limewire.lws.server.StringCallback;
import org.limewire.net.SocketsManager;
/**
* A simple remote {@link RemoteServer}.
*/
public class RemoteServerImpl extends AbstractServer implements RemoteServer {
/** The port on which we'll connect this server. */
public final static int PORT = 8080;
private final LocalServerDelegate del;
public RemoteServerImpl(SocketsManager socketsManager, int otherPort) {
super(PORT, "Remote Server");
setDispatcher(new DispatcherImpl());
this.del = new LocalServerDelegate(socketsManager, "localhost", otherPort);
}
/**
* We DO want the IP of the incoming request to go to our handlers.
*/
@Override
protected final boolean sendIPToHandlers() {
return true;
}
/**
* Returns the equivalent of
*
* <pre>
* {"public" -> "PCURJKKTXE", "private" -> "BMBTVRVCSX" }
* </pre>
*
* for request
*
* <pre>
* store\app\pages\client\ClientCom\command\StoreKey\public\PCURJKKTXE\private\BMBTVRVCSX/
* </pre>
*
* @param request request of the form
*
* <pre>
* store\app\pages\client\ClientCom\command\StoreKey\public\PCURJKKTXE\private\BMBTVRVCSX/
* </pre>
*
* @return the equivalent of
*
* <pre>
* {"public" -> "PCURJKKTXE", "private" -> "BMBTVRVCSX" }
* </pre>
*
* for request
*
* <pre>
* store\app\pages\client\ClientCom\command\StoreKey\public\PCURJKKTXE\private\BMBTVRVCSX/
* </pre>
*/
protected static Map<String, String> getArgs(String request) {
Map<String,String> args = new HashMap<String,String>();
String target = "ClientCom";
int itarget = request.indexOf(target);
if (itarget == -1) return args;
String rest = request.substring(itarget + target.length());
if (rest.equals("")) return args;
if (!Character.isLetterOrDigit(rest.charAt(0))) rest = rest.substring(1);
if (rest.equals("")) return args;
for (StringTokenizer st = new StringTokenizer(rest,"/\\" + File.separatorChar, false);
st.hasMoreTokens();) {
String name = st.nextToken();
if (!st.hasMoreTokens()) {
args.put(name, null);
break;
}
String value = st.nextToken();
args.put(name,value);
}
return args;
}
/**
* Returns <code>StoreKey</code> for the following String:
* <pre>
* store\app\pages\client\ClientCom\command\StoreKey\public\PCURJKKTXE\private\BMBTVRVCSX/
* </pre>
*
* @param request
* @return
*/
public static String getCommand(String request) {
String target = "command";
int itarget = request.indexOf(target);
if (itarget == -1) return null;
String rest = request.substring(itarget + target.length());
if (rest.equals("")) return null;
if (!Character.isLetterOrDigit(rest.charAt(0))) rest = rest.substring(1);
if (rest.equals("")) return null;
StringBuffer res = new StringBuffer();
for (int i=0; i<rest.length(); i++) {
char c = rest.charAt(i);
if (!Character.isLetterOrDigit(c)) break;
res.append(c);
}
return res.toString();
}
private final class DispatcherImpl extends LWSDispatcherSupport {
@Override
protected final Handler[] createHandlers() {
return new Handler[] { new StoreKey(), new GiveKey(), };
}
// ------------------------------------------------------------
// Handlers
// ------------------------------------------------------------
/**
* Sent from local server with parameters {@link Parameters#PUBLIC} and
* {@link Parameters#PRIVATE} to store the in coming ip and these
* parameters for when the local code asks for it.
*/
class StoreKey extends AbstractHandler {
public void handle(final Map<String, String> args, StringCallback cb) {
String publicKey = args.get(LWSDispatcherSupport.Parameters.PUBLIC);
if (publicKey == null) {
cb.process(report(LWSDispatcherSupport.ErrorCodes.MISSING_PUBLIC_KEY_PARAMETER));
return;
}
String privateKey = args.get(LWSDispatcherSupport.Parameters.PRIVATE);
if (privateKey == null) {
cb.process(report(LWSDispatcherSupport.ErrorCodes.MISSING_PRIVATE_KEY_PARAMETER));
return;
}
String ip = args.get(LWSDispatcherSupport.Parameters.IP);
if (ip == null) {
cb.process(report(LWSDispatcherSupport.ErrorCodes.MISSING_IP_PARAMETER));
return;
}
if (LWSServerUtil.isEmpty(publicKey)) {
cb.process(LWSDispatcherSupport.ErrorCodes.INVALID_PUBLIC_KEY);
return;
}
storeKeys(publicKey, privateKey, ip);
cb.process(LWSDispatcherSupport.Responses.OK);
}
}
/**
* Sent from local code to retrieve the private key with parameter
* {@link Parameters#PUBLIC}.
* {@link Parameters#IP}
*/
class GiveKey extends HandlerWithCallback {
@Override
public void handleRest(final Map<String, String> args, StringCallback cb) {
String publicKey = args.get(LWSDispatcherSupport.Parameters.PUBLIC);
if (publicKey == null) {
cb.process(report(LWSDispatcherSupport.ErrorCodes.MISSING_PUBLIC_KEY_PARAMETER));
return;
}
String ip = args.get(LWSDispatcherSupport.Parameters.IP);
if (ip == null) {
cb.process(report(LWSDispatcherSupport.ErrorCodes.MISSING_IP_PARAMETER));
return;
}
String privateKey = lookupPrivateKey(publicKey, ip);
cb.process(privateKey);
}
}
public void sendMessageToServer(String msg, Map<String,String> args, StringCallback cb) throws IOException {
del.sendMessageToServer(msg, args, cb, LocalServerDelegate.NormalStyleURLConstructor.INSTANCE);
}
@Override
protected Map<String, String> getArgs(String request) {
return RemoteServerImpl.getArgs(request);
}
@Override
protected String getCommand(String request) {
return RemoteServerImpl.getCommand(request);
}
@Override
protected boolean isAuthenticated() {
// This doesn't matter
return false;
}
public void deauthenticate() {
// This isn't used
}
}
// ---------------------------------------------------------------
// RemoteServer
// ---------------------------------------------------------------
/** A pair. */
private static class Pair {
final String key;
final String ip;
Pair(final String key, final String ip) {
this.key = key;
this.ip = ip;
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof Pair)) return false;
Pair that = (Pair) o;
return this.key.equals(that.key) && this.ip.equals(that.ip);
}
@Override
public int hashCode() {
return this.key.hashCode() << 16 + this.ip.hashCode();
}
@Override
public String toString() {
return "<" + key + "," + ip + ">";
}
}
private final Map<Pair, String> pairs2privateKeys = new HashMap<Pair, String>();
public boolean storeKeys(String publicKey, String privateKey, String ip) {
boolean result = pairs2privateKeys.put(new Pair(publicKey, ip), privateKey) != null;
return result;
}
public String lookupPrivateKey(String publicKey, String ip) {
if (publicKey == null) {
return LWSDispatcherSupport.ErrorCodes.INVALID_PUBLIC_KEY_OR_IP;
}
Pair p = new Pair(publicKey, ip);
String privateKey = pairs2privateKeys.get(p);
String res = privateKey == null
? LWSDispatcherSupport.ErrorCodes.INVALID_PUBLIC_KEY_OR_IP
: privateKey;
return res;
}
}