Index: /applications/editors/josm/plugins/remotecontrol/build.xml
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/build.xml	(revision 22674)
+++ /applications/editors/josm/plugins/remotecontrol/build.xml	(revision 22675)
@@ -52,5 +52,5 @@
         <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
             <manifest>
-                <attribute name="Author" value="Frederik Ramm"/>
+                <attribute name="Author" value="Frederik Ramm, Bodo Meissner"/>
                 <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.remotecontrol.RemoteControlPlugin"/>
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
@@ -60,4 +60,7 @@
                 <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
+            	<!-- This plugin must be loaded before any plugin that may want to register 
+            		 handlers for additional remote commands.-->
+            	<attribute name="Plugin-Stage" value="20"/>
             </manifest>
         </jar>
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/PermissionPref.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/PermissionPref.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/PermissionPref.java	(revision 22675)
@@ -0,0 +1,16 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+/**
+ * Contains a preference name to control permission for the operation
+ * implemented by the RequestHandler, and an error message to be displayed
+ * if not permitted.
+ */
+public class PermissionPref {
+	String pref;
+	String message;
+	public PermissionPref(String pref, String message)
+	{
+		this.pref = pref;
+		this.message = message;
+	}
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPlugin.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPlugin.java	(revision 22674)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPlugin.java	(revision 22675)
@@ -21,4 +21,8 @@
     {
     	super(info);
+    	/*
+		System.out.println("constructor " + this.getClass().getName() + " (" + info.name +
+				" v " + info.version + " stage " + info.stage + ")");
+		*/
         restartServer();
     }
@@ -50,3 +54,15 @@
         }
     }
+
+    /**
+     * Add external external request handler.
+     * Can be used by other plug-ins that want to use remote control.
+     *
+     * @param handler The additional request handler.
+     */
+    public void addRequestHandler(String command, Class<? extends RequestHandler> handlerClass)
+    {
+        RequestProcessor.addRequestHandlerClass(command, handlerClass);
+    }
+    
 }
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPreferences.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPreferences.java	(revision 22674)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPreferences.java	(revision 22675)
@@ -13,5 +13,4 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandler.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandler.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandler.java	(revision 22675)
@@ -0,0 +1,188 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+
+/**
+ * This is the parent of all classes that handle a specific command 
+ * in remote control plug-in.
+ *   
+ * @author Bodo Meissner
+ */
+public abstract class RequestHandler
+{
+	/** The GET request arguments */
+	protected HashMap<String,String> args;
+	
+	/** The request URL without "GET". */
+    protected String request;
+
+    /** default response */
+    protected String content = "OK\r\n";
+    /** default content type */
+    protected String contentType = "text/plain";
+
+    /** will be filled with the command assigned to the subclass */
+    private String myCommand;
+    
+    /**
+     * Check permission and parameters and handle request.
+     * 
+     * @throws RequestHandlerForbiddenException
+     * @throws RequestHandlerBadRequestException
+     * @throws RequestHandlerErrorException
+     */
+    final void handle() throws RequestHandlerForbiddenException, RequestHandlerBadRequestException, RequestHandlerErrorException
+    {
+    	checkPermission();
+    	checkMandatoryParams();
+    	handleRequest();
+    }
+    
+    /**
+     * Handle a specific command sent as remote control.
+     * 
+     * This method of the subclass will do the real work.  
+     * 
+     * @throws RequestHandlerErrorException
+     * @throws RequestHandlerBadRequestException 
+     */
+    protected abstract void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException;
+
+    /**
+     * Get a specific message to ask the user for permission for the operation
+     * requested via remote control.
+     * 
+     * This message will be displayed to the user if the preference
+     * remotecontrol.always-confirm is true.
+     * 
+     * @return the message
+     */
+    abstract public String getPermissionMessage();
+
+    /**
+     * Get a PermissionPref object containing the name of a special permission
+     * preference to individually allow the requested operation and an error
+     * message to be displayed when a disabled operation is requested.
+     * 
+     * Default is not to check any special preference. Override this in a 
+     * subclass to define permission preference and error message.
+     * 
+     * @return the preference name and error message or null
+     */
+    public PermissionPref getPermissionPref()
+    {
+        /* Example:
+        return new PermissionPref("fooobar.remotecontrol",
+        "RemoteControl: foobar forbidden by preferences");
+        */
+        return null;
+    }
+
+    protected String[] getMandatoryParams()
+    {
+    	return null;
+    }
+    
+    /**
+     * Check permissions in preferences and display error message 
+     * or ask for permission.
+     * 
+     * @throws RequestHandlerForbiddenException
+     */
+    final public void checkPermission() throws RequestHandlerForbiddenException
+    {
+        /* 
+         * If the subclass defines a specific preference and if this is set
+         * to false, abort with an error message.
+         */
+        PermissionPref permissionPref = getPermissionPref();
+        if((permissionPref != null) && (permissionPref.pref != null))
+        {
+            if (!Main.pref.getBoolean(permissionPref.pref, true)) {
+                System.out.println(permissionPref.message);
+                throw new RequestHandlerForbiddenException();
+            }
+        }
+
+        /* Does the user want to confirm everything?
+         * If yes, display specific confirmation message.
+         */
+        if (Main.pref.getBoolean("remotecontrol.always-confirm", false)) {
+            if (JOptionPane.showConfirmDialog(Main.parent,
+                "<html>" + getPermissionMessage() +
+                "<br>" + tr("Do you want to allow this?"),
+                tr("Confirm Remote Control action"),
+                JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
+                    throw new RequestHandlerForbiddenException();
+            }
+        }
+    }
+
+    /**
+     * Set request URL and parse args.
+     * 
+     * @param url The request URL.
+     */
+	public void setUrl(String url) {
+		this.request = url;
+		parseArgs();
+	}
+
+	/**
+	 * Parse the request parameters as key=value pairs.
+	 * The result will be stored in this.args.
+	 * 
+	 * Can be overridden by subclass.
+	 */
+	protected void parseArgs() {
+		StringTokenizer st = new StringTokenizer(this.request, "&?");
+		HashMap<String, String> args = new HashMap<String, String>();
+		// ignore first token which is the command
+		if(st.hasMoreTokens()) st.nextToken();
+		while (st.hasMoreTokens()) {
+			String param = st.nextToken();
+			int eq = param.indexOf("=");
+			if (eq > -1)
+				args.put(param.substring(0, eq),
+                         param.substring(eq + 1));
+		}
+		this.args = args;
+	}
+	
+	void checkMandatoryParams() throws RequestHandlerBadRequestException
+	{
+		String[] mandatory = getMandatoryParams();
+		if(mandatory == null) return;
+		
+		boolean error = false;
+		for(int i = 0; i < mandatory.length; ++i)
+		{
+			String key = mandatory[i];
+			String value = args.get(key);
+			if((value == null) || (value.length() == 0))
+			{
+				error = true;
+				System.out.println("'" + myCommand + "' remote control request must have '" + key + "' parameter");
+			}
+		}
+		if(error) throw new RequestHandlerBadRequestException();
+	}
+	
+	/**
+	 * Save command associated with this handler.
+	 * 
+	 * @param command The command.
+	 */
+	public void setCommand(String command)
+	{
+		if(command.charAt(0) == '/') command = command.substring(1);
+		myCommand = command;
+	}
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerBadRequestException.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerBadRequestException.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerBadRequestException.java	(revision 22675)
@@ -0,0 +1,4 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+public class RequestHandlerBadRequestException extends RequestHandlerException {
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerErrorException.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerErrorException.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerErrorException.java	(revision 22675)
@@ -0,0 +1,4 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+public class RequestHandlerErrorException extends RequestHandlerException {
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerException.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerException.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerException.java	(revision 22675)
@@ -0,0 +1,4 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+public class RequestHandlerException extends Exception {
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerForbiddenException.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerForbiddenException.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestHandlerForbiddenException.java	(revision 22675)
@@ -0,0 +1,5 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+public class RequestHandlerForbiddenException extends RequestHandlerException {
+	private static final long serialVersionUID = 2263904699747115423L;
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestProcessor.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestProcessor.java	(revision 22674)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestProcessor.java	(revision 22675)
@@ -1,8 +1,4 @@
 package org.openstreetmap.josm.plugins.remotecontrol;
 
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.geom.Area;
-import java.awt.geom.Rectangle2D;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -14,434 +10,289 @@
 import java.io.Writer;
 import java.net.Socket;
-import java.net.URLDecoder;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.StringTokenizer;
-import java.util.concurrent.Future;
-
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.AutoScaleAction;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
-import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
-import org.openstreetmap.josm.command.AddCommand;
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+
+import org.openstreetmap.josm.plugins.remotecontrol.handler.AddNodeHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.handler.ImportHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.handler.LoadAndZoomHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.handler.VersionHandler;
 
 /**
  * Processes HTTP "remote control" requests.
  */
-public class RequestProcessor extends Thread
-{
-	/**
-	 * RemoteControl protocol version.
-	 * Change minor number for compatible interface extensions. Change major number in case of incompatible changes.
+public class RequestProcessor extends Thread {
+	/**
+	 * RemoteControl protocol version. Change minor number for compatible
+	 * interface extensions. Change major number in case of incompatible
+	 * changes.
 	 */
 	public static final String PROTOCOLVERSION = "{\"protocolversion\": {\"major\": 1, \"minor\": 0}, \"application\": \"JOSM RemoteControl\"}";
 
-	
-    /** The socket this processor listens on */
-    private Socket request;
-
-    private class AlreadyLoadedException extends Exception {};
-    private class LoadDeniedException extends Exception {};
-
-    /**
-     * Constructor
-     *
-     * @param request
-     */
-    public RequestProcessor(Socket request)
-    {
-        super("RemoteControl request processor");
-        this.setDaemon(true);
-        this.request = request;
-    }
-
-    /**
-     * Spawns a new thread for the request
-     *
-     * @param request The WMS request
-     */
-    public static void processRequest(Socket request)
-    {
-        RequestProcessor processor = new RequestProcessor(request);
-        processor.start();
-    }
-
-    /**
-     * The work is done here.
-     */
-    public void run()
-    {
-        Writer out = null;
-        try
-        {
-			String content = "OK\r\n";
-			String contentType = "text/plain";
-			
-            OutputStream raw = new BufferedOutputStream( request.getOutputStream());
-            out = new OutputStreamWriter(raw);
-            Reader in = new InputStreamReader(new BufferedInputStream(request.getInputStream()), "ASCII");
-
-            StringBuffer requestLine = new StringBuffer();
-            while (requestLine.length() < 1024)
-            {
-                int c = in.read();
-                if (c == '\r' || c == '\n') break;
-                requestLine.append((char) c);
-            }
-
-            System.out.println("RemoteControl received: " + requestLine);
-            String get = requestLine.toString();
-            StringTokenizer st = new StringTokenizer(get);
-            if (!st.hasMoreTokens()) { sendError(out); return; }
-            String method = st.nextToken();
-            if (!st.hasMoreTokens()) { sendError(out); return; }
-            String url = st.nextToken();
-
-            if (!method.equals("GET")) {
-                sendNotImplemented(out);
-                return;
-            }
-
-            st = new StringTokenizer(url, "&?");
-            String command = null;
-            HashMap<String,String> args = new HashMap<String,String>();
-            while (st.hasMoreTokens())
-            {
-                String param = st.nextToken();
-                if (command == null) {
-                    command = param;
-                } else {
-                    int eq = param.indexOf("=");
-                    if (eq>-1) args.put(param.substring(0,eq), param.substring(eq+1));
-                }
-            }
-
-            if (command.equals("/load_and_zoom")) {
-                if (Main.pref.getBoolean("remotecontrol.always-confirm", false)) {
-                    if (JOptionPane.showConfirmDialog(Main.parent,
-                        "<html>" + tr("Remote Control has been asked to load data from the API.") +
-                        "<br>" + tr("Request details: {0}", url) + "<br>" + tr("Do you want to allow this?"),
-                        tr("Confirm Remote Control action"),
-                        JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
-                            sendForbidden(out);
-                            return;
-                    }
-                }
-                DownloadTask osmTask = new DownloadOsmTask();
-                if (!(args.containsKey("bottom") && args.containsKey("top") &&
-                    args.containsKey("left") && args.containsKey("right"))) {
-                    sendBadRequest(out);
-                    System.out.println("load_and_zoom remote control request must have bottom,top,left,right parameters");
-                    return;
-                }
-                double minlat = 0;
-                double maxlat = 0;
-                double minlon = 0;
-                double maxlon = 0;
-                try {
-                    minlat = Double.parseDouble(args.get("bottom"));
-                    maxlat = Double.parseDouble(args.get("top"));
-                    minlon = Double.parseDouble(args.get("left"));
-                    maxlon = Double.parseDouble(args.get("right"));
-
-                    if (!Main.pref.getBoolean("remotecontrol.permission.load-data", true))
-                        throw new LoadDeniedException();
-
-                    // find out whether some data has already been downloaded
-                    Area present = null;
-                    DataSet ds = Main.main.getCurrentDataSet();
-                    if (ds != null)
-                        present = ds.getDataSourceArea();
-                    if (present != null && !present.isEmpty()) {
-                        Area toDownload = new Area(new Rectangle2D.Double(minlon,minlat,maxlon-minlon,maxlat-minlat));
-                        toDownload.subtract(present);
-                        if (toDownload.isEmpty()) throw new AlreadyLoadedException();
-                        // the result might not be a rectangle (L shaped etc)
-                        Rectangle2D downloadBounds = toDownload.getBounds2D();
-                        minlat = downloadBounds.getMinY();
-                        minlon = downloadBounds.getMinX();
-                        maxlat = downloadBounds.getMaxY();
-                        maxlon = downloadBounds.getMaxX();
-                    }
-                    Future<?> future = osmTask.download(false /*no new layer*/, new Bounds(minlat,minlon,maxlat,maxlon), null /* let the task manage the progress monitor */);
-                    Main.worker.submit(new PostDownloadHandler(osmTask, future));
-                } catch (AlreadyLoadedException ex) {
-                    System.out.println("RemoteControl: no download necessary");
-                } catch (LoadDeniedException ex) {
-                    System.out.println("RemoteControl: download forbidden by preferences");
-                    sendForbidden(out);
-                    return;
-                } catch (Exception ex) {
-                    sendError(out);
-                    System.out.println("RemoteControl: Error parsing load_and_zoom remote control request:");
-                    ex.printStackTrace();
-                    return;
-                }
-                if (args.containsKey("select") && Main.pref.getBoolean("remotecontrol.permission.change-selection", true)) {
-                    // select objects after downloading, zoom to selection.
-                    final String selection = args.get("select");
-                    Main.worker.execute(new Runnable() {
-                        public void run() {
-                            HashSet<Long> ways = new HashSet<Long>();
-                            HashSet<Long> nodes = new HashSet<Long>();
-                            HashSet<Long> relations = new HashSet<Long>();
-                            HashSet<OsmPrimitive> newSel = new HashSet<OsmPrimitive>();
-                            for (String item : selection.split(",")) {
-                                if (item.startsWith("way")) {
-                                    ways.add(Long.parseLong(item.substring(3)));
-                                } else if (item.startsWith("node")) {
-                                    nodes.add(Long.parseLong(item.substring(4)));
-                                } else if (item.startsWith("relation")) {
-                                    relations.add(Long.parseLong(item.substring(8)));
-                                } else if (item.startsWith("rel")) {
-                                    relations.add(Long.parseLong(item.substring(3)));
-                                } else {
-                                    System.out.println("RemoteControl: invalid selection '"+item+"' ignored");
-                                }
-                            }
-                            DataSet ds = Main.main.getCurrentDataSet();
-                            if(ds == null) // e.g. download failed
-                                return;
-                            for (Way w : ds.getWays()) if (ways.contains(w.getId())) newSel.add(w);
-                            for (Node n : ds.getNodes()) if (nodes.contains(n.getId())) newSel.add(n);
-                            for (Relation r : ds.getRelations()) if (relations.contains(r.getId())) newSel.add(r);
-                            ds.setSelected(newSel);
-                            if (Main.pref.getBoolean("remotecontrol.permission.change-viewport", true))
-                                new AutoScaleAction("selection").actionPerformed(null);
-                        }
-                    });
-                } else if (Main.pref.getBoolean("remotecontrol.permission.change-viewport", true)) {
-                    // after downloading, zoom to downloaded area.
-                    final Bounds bounds = new Bounds(new LatLon(minlat, minlon),
-                        new LatLon(maxlat, maxlon));
-
-                    // make sure this isn't called unless there *is* a MapView
-                    //
-                    if (Main.map != null && Main.map.mapView != null) {
-	                    Main.worker.execute(new Runnable() {
-	                        public void run() {
-	                            BoundingXYVisitor bbox = new BoundingXYVisitor();
-	                            bbox.visit(bounds);
-	                            Main.map.mapView.recalculateCenterScale(bbox);
-	                        }
-	                    });
-                    }
-                }
-            } else if (command.equals("/add_node")) {
-                if (!Main.pref.getBoolean("remotecontrol.permission.create-objects", true)) {
-                    sendForbidden(out);
-                    return;
-                }
-                if (Main.pref.getBoolean("remotecontrol.always-confirm", false)) {
-                    if (JOptionPane.showConfirmDialog(Main.parent,
-                        "<html>" + tr("Remote Control has been asked to create a new node.") +
-                        "<br>" + tr("Do you want to allow this?"),
-                        tr("Confirm Remote Control action"),
-                        JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
-                            sendForbidden(out);
-                            return;
-                    }
-                }
-                addNode(args, out);
-            } else if (command.equals("/import")) {
-                if (Main.pref.getBoolean("remotecontrol.always-confirm", false)) {
-                    if (JOptionPane.showConfirmDialog(Main.parent,
-                        "<html>" + tr("Remote Control has been asked to import data from the following URL:") +
-                        "<br>" + url +
-                        "<br>" + tr("Do you want to allow this?"),
-                        tr("Confirm Remote Control action"),
-                        JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
-                            sendForbidden(out);
-                            return;
-                    }
-                }
-                if (!(args.containsKey("url"))) {
-                    sendBadRequest(out);
-                    System.out.println("'import' remote control request must have url parameter");
-                    return;
-                }
-                try {
-                    if (!Main.pref.getBoolean("remotecontrol.permission.import", true))
-                        throw new LoadDeniedException();
-
-                    DownloadTask osmTask = new DownloadOsmTask();
-                    osmTask.loadUrl(false, URLDecoder.decode(args.get("url"), "UTF-8"), null);
-                } catch (LoadDeniedException ex) {
-                    System.out.println("RemoteControl: import forbidden by preferences");
-                } catch (Exception ex) {
-                    sendError(out);
-                    System.out.println("RemoteControl: Error parsing import remote control request:");
-                    ex.printStackTrace();
-                    return;
-                }
-                // TODO: select/zoom to downloaded
-            } else if (command.equals("/version")) {
-                if (!Main.pref.getBoolean("remotecontrol.permission.read-protocolversion", true)) {
-                    sendForbidden(out);
-                    System.out.println("RemoteControl: /version forbidden by preferences");
-                    return;
-                }
-                if (Main.pref.getBoolean("remotecontrol.always-confirm", false)) {
-                    if (JOptionPane.showConfirmDialog(Main.parent,
-                        "<html>" + tr("Remote Control has been asked to report its protocol version. This enables web sites to detect a running JOSM.") +
-                        "<br>" + tr("Do you want to allow this?"),
-                        tr("Confirm Remote Control action"),
-                        JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
-                            sendForbidden(out);
-                            return;
-                    }
-                }
-
-				content = RequestProcessor.PROTOCOLVERSION;
-				contentType = "application/json";
-				if (args.containsKey("jsonp")) {
-					content = args.get("jsonp")+ " && " + args.get("jsonp") + "(" + content + ")";
+	/** The socket this processor listens on */
+	private Socket request;
+
+	/**
+	 * Collection of request handlers.
+	 * Will be initialized with default handlers here. Other plug-ins
+	 * can extend this list by using @see addRequestHandler 
+	 */
+	private static HashMap<String, Class<? extends RequestHandler>> handlers = new HashMap<String, Class<? extends RequestHandler>>();
+
+	/**
+	 * Constructor
+	 * 
+	 * @param request A socket to read the request.
+	 */
+	public RequestProcessor(Socket request) {
+		super("RemoteControl request processor");
+		this.setDaemon(true);
+		this.request = request;
+	}
+
+	/**
+	 * Spawns a new thread for the request
+	 * 
+	 * @param request
+	 *            The WMS request
+	 */
+	public static void processRequest(Socket request) {
+		RequestProcessor processor = new RequestProcessor(request);
+		processor.start();
+	}
+
+	/**
+	 * Add external request handler. Can be used by other plug-ins that
+	 * want to use remote control.
+	 * @param command The command to handle.
+	 * @param handler The additional request handler.
+	 */
+	static void addRequestHandlerClass(String command,
+			Class<? extends RequestHandler> handler) {
+		addRequestHandlerClass(command, handler, false);
+	}
+
+	/**
+	 * Add external request handler. Message can be suppressed.
+	 * (for internal use)
+	 * @param command The command to handle.
+	 * @param handler The additional request handler.
+	 * @param silent Don't show message if true.
+	 */
+	private static void addRequestHandlerClass(String command,
+				Class<? extends RequestHandler> handler, boolean silent) {
+		if (handlers.get(command) != null) {
+			System.out.println("RemoteControl: duplicate command " + command
+					+ " with handler " + handler.getName());
+		} else {
+			if(!silent) System.out.println("RemoteControl: adding command command " + 
+					command + " (handled by " + handler.getName() + ")");
+			handlers.put(command, handler);
+		}
+	}
+
+	/** Add default request handlers */
+	static {
+		addRequestHandlerClass(LoadAndZoomHandler.command,
+				LoadAndZoomHandler.class, true);
+		addRequestHandlerClass(AddNodeHandler.command, AddNodeHandler.class, true);
+		addRequestHandlerClass(ImportHandler.command, ImportHandler.class, true);
+		addRequestHandlerClass(VersionHandler.command, VersionHandler.class, true);
+	}
+
+	/**
+	 * The work is done here.
+	 */
+	public void run() {
+		Writer out = null;
+		try {
+			OutputStream raw = new BufferedOutputStream(
+					request.getOutputStream());
+			out = new OutputStreamWriter(raw);
+			Reader in = new InputStreamReader(new BufferedInputStream(
+					request.getInputStream()), "ASCII");
+
+			StringBuffer requestLine = new StringBuffer();
+			while (requestLine.length() < 1024) {
+				int c = in.read();
+				if (c == '\r' || c == '\n')
+					break;
+				requestLine.append((char) c);
+			}
+
+			System.out.println("RemoteControl received: " + requestLine);
+			String get = requestLine.toString();
+			StringTokenizer st = new StringTokenizer(get);
+			if (!st.hasMoreTokens()) {
+				sendError(out);
+				return;
+			}
+			String method = st.nextToken();
+			if (!st.hasMoreTokens()) {
+				sendError(out);
+				return;
+			}
+			String url = st.nextToken();
+
+			if (!method.equals("GET")) {
+				sendNotImplemented(out);
+				return;
+			}
+
+			String command = null;
+			int questionPos = url.indexOf('?');
+			if(questionPos < 0)
+			{
+				command = url;
+			}
+			else
+			{
+				command = url.substring(0, questionPos);
+			}
+
+			// find a handler for this command
+			Class<? extends RequestHandler> handlerClass = handlers
+					.get(command);
+			if (handlerClass == null) {
+				// no handler found
+				sendBadRequest(out);
+			} else {
+				// create handler object
+				RequestHandler handler = handlerClass.newInstance();
+				try {
+					handler.setCommand(command);
+					handler.setUrl(url);
+					handler.checkPermission();
+					handler.handle();
+					sendHeader(out, "200 OK", handler.contentType, false);
+					out.write("Content-length: " + handler.content.length()
+							+ "\r\n");
+					out.write("\r\n");
+					out.write(handler.content);
+					out.flush();
+				} catch (RequestHandlerErrorException ex) {
+					sendError(out);
+				} catch (RequestHandlerBadRequestException ex) {
+					sendBadRequest(out);
+				} catch (RequestHandlerForbiddenException ex) {
+					sendForbidden(out);
 				}
 			}
-            sendHeader(out, "200 OK", contentType, false);
-            out.write("Content-length: "+content.length()+"\r\n");
-            out.write("\r\n");
-            out.write(content);
-            out.flush();
-        }
-        catch (IOException ioe) { }
-        catch(Exception e) {
-            e.printStackTrace();
-            try {
-                sendError(out);
-            } catch (IOException e1) { }
-        } finally {
-            try {
-                request.close();
-            } catch (IOException e) {}
-        }
-    }
-
-    /**
-     * Adds a node, reacts to the GET /add_node?lon=...&amp;lat=... request.
-     * @param args
-     * @param out
-     * @throws IOException
-     */
-    private void addNode(HashMap<String, String> args, Writer out) throws IOException {
-        if(!args.containsKey("lat") || !args.containsKey("lon")) {
-            sendBadRequest(out);
-            return;
-        }
-
-        // Parse the arguments
-        double lat = Double.parseDouble(args.get("lat"));
-        double lon = Double.parseDouble(args.get("lon"));
-        System.out.println("Adding node at (" + lat + ", " + lon + ")");
-
-        // Create a new node
-        LatLon ll = new LatLon(lat, lon);
-        Node nnew = new Node(ll);
-
-        // Now execute the commands to add this node.
-        Main.main.undoRedo.add(new AddCommand(nnew));
-        Main.main.getCurrentDataSet().setSelected(nnew);
-        Main.map.mapView.repaint();
-
-    }
-
-    /**
-     * Sends a 500 error: server error
-     * @param out The writer where the error is written
-     * @throws IOException If the error can not be written
-     */
-    private void sendError(Writer out) throws IOException
-    {
-        sendHeader(out, "500 Internal Server Error", "text/html", true);
-        out.write("<HTML>\r\n");
-        out.write("<HEAD><TITLE>Internal Error</TITLE>\r\n");
-        out.write("</HEAD>\r\n");
-        out.write("<BODY>");
-        out.write("<H1>HTTP Error 500: Internal Server Error</h2>\r\n");
-        out.write("</BODY></HTML>\r\n");
-        out.flush();
-    }
-
-    /**
-     * Sends a 501 error: not implemented
-     * @param out The writer where the error is written
-     * @throws IOException If the error can not be written
-     */
-    private void sendNotImplemented(Writer out) throws IOException
-    {
-        sendHeader(out, "501 Not Implemented", "text/html", true);
-        out.write("<HTML>\r\n");
-        out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n");
-        out.write("</HEAD>\r\n");
-        out.write("<BODY>");
-        out.write("<H1>HTTP Error 501: Not Implemented</h2>\r\n");
-        out.write("</BODY></HTML>\r\n");
-        out.flush();
-    }
-
-    /**
-     * Sends a 403 error: forbidden
-     * @param out The writer where the error is written
-     * @throws IOException If the error can not be written
-     */
-    private void sendForbidden(Writer out) throws IOException
-    {
-        sendHeader(out, "403 Forbidden", "text/html", true);
-        out.write("<HTML>\r\n");
-        out.write("<HEAD><TITLE>Forbidden</TITLE>\r\n");
-        out.write("</HEAD>\r\n");
-        out.write("<BODY>");
-        out.write("<H1>HTTP Error 403: Forbidden</h2>\r\n");
-        out.write("</BODY></HTML>\r\n");
-        out.flush();
-    }
-    /**
-     * Sends a 403 error: forbidden
-     * @param out The writer where the error is written
-     * @throws IOException If the error can not be written
-     */
-    private void sendBadRequest(Writer out) throws IOException
-    {
-        sendHeader(out, "400 Bad Request", "text/html", true);
-        out.write("<HTML>\r\n");
-        out.write("<HEAD><TITLE>Bad Request</TITLE>\r\n");
-        out.write("</HEAD>\r\n");
-        out.write("<BODY>");
-        out.write("<H1>HTTP Error 400: Bad Request</h2>\r\n");
-        out.write("</BODY></HTML>\r\n");
-        out.flush();
-    }
-
-    /**
-     * Send common HTTP headers to the client.
-     *
-     * @param out The Writer
-     * @param status The status string ("200 OK", "500", etc)
-     * @param contentType The content type of the data sent
-     * @param endHeaders If true, adds a new line, ending the headers.
-     * @throws IOException When error
-     */
-    private void sendHeader(Writer out, String status, String contentType, boolean endHeaders) throws IOException
-    {
-        out.write("HTTP/1.1 " + status + "\r\n");
-        Date now = new Date();
-        out.write("Date: " + now + "\r\n");
-        out.write("Server: JOSM RemoteControl\r\n");
-        out.write("Content-type: " + contentType + "\r\n");
+
+		} catch (IOException ioe) {
+		} catch (Exception e) {
+			e.printStackTrace();
+			try {
+				sendError(out);
+			} catch (IOException e1) {
+			}
+		} finally {
+			try {
+				request.close();
+			} catch (IOException e) {
+			}
+		}
+	}
+
+	/**
+	 * Sends a 500 error: server error
+	 * 
+	 * @param out
+	 *            The writer where the error is written
+	 * @throws IOException
+	 *             If the error can not be written
+	 */
+	private void sendError(Writer out) throws IOException {
+		sendHeader(out, "500 Internal Server Error", "text/html", true);
+		out.write("<HTML>\r\n");
+		out.write("<HEAD><TITLE>Internal Error</TITLE>\r\n");
+		out.write("</HEAD>\r\n");
+		out.write("<BODY>");
+		out.write("<H1>HTTP Error 500: Internal Server Error</h2>\r\n");
+		out.write("</BODY></HTML>\r\n");
+		out.flush();
+	}
+
+	/**
+	 * Sends a 501 error: not implemented
+	 * 
+	 * @param out
+	 *            The writer where the error is written
+	 * @throws IOException
+	 *             If the error can not be written
+	 */
+	private void sendNotImplemented(Writer out) throws IOException {
+		sendHeader(out, "501 Not Implemented", "text/html", true);
+		out.write("<HTML>\r\n");
+		out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n");
+		out.write("</HEAD>\r\n");
+		out.write("<BODY>");
+		out.write("<H1>HTTP Error 501: Not Implemented</h2>\r\n");
+		out.write("</BODY></HTML>\r\n");
+		out.flush();
+	}
+
+	/**
+	 * Sends a 403 error: forbidden
+	 * 
+	 * @param out
+	 *            The writer where the error is written
+	 * @throws IOException
+	 *             If the error can not be written
+	 */
+	private void sendForbidden(Writer out) throws IOException {
+		sendHeader(out, "403 Forbidden", "text/html", true);
+		out.write("<HTML>\r\n");
+		out.write("<HEAD><TITLE>Forbidden</TITLE>\r\n");
+		out.write("</HEAD>\r\n");
+		out.write("<BODY>");
+		out.write("<H1>HTTP Error 403: Forbidden</h2>\r\n");
+		out.write("</BODY></HTML>\r\n");
+		out.flush();
+	}
+
+	/**
+	 * Sends a 403 error: forbidden
+	 * 
+	 * @param out
+	 *            The writer where the error is written
+	 * @throws IOException
+	 *             If the error can not be written
+	 */
+	private void sendBadRequest(Writer out) throws IOException {
+		sendHeader(out, "400 Bad Request", "text/html", true);
+		out.write("<HTML>\r\n");
+		out.write("<HEAD><TITLE>Bad Request</TITLE>\r\n");
+		out.write("</HEAD>\r\n");
+		out.write("<BODY>");
+		out.write("<H1>HTTP Error 400: Bad Request</h2>\r\n");
+		out.write("</BODY></HTML>\r\n");
+		out.flush();
+	}
+
+	/**
+	 * Send common HTTP headers to the client.
+	 * 
+	 * @param out
+	 *            The Writer
+	 * @param status
+	 *            The status string ("200 OK", "500", etc)
+	 * @param contentType
+	 *            The content type of the data sent
+	 * @param endHeaders
+	 *            If true, adds a new line, ending the headers.
+	 * @throws IOException
+	 *             When error
+	 */
+	private void sendHeader(Writer out, String status, String contentType,
+			boolean endHeaders) throws IOException {
+		out.write("HTTP/1.1 " + status + "\r\n");
+		Date now = new Date();
+		out.write("Date: " + now + "\r\n");
+		out.write("Server: JOSM RemoteControl\r\n");
+		out.write("Content-type: " + contentType + "\r\n");
 		out.write("Access-Control-Allow-Origin: *\r\n");
-        if (endHeaders)
-            out.write("\r\n");
-    }
+		if (endHeaders)
+			out.write("\r\n");
+	}
 }
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/AddNodeHandler.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/AddNodeHandler.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/AddNodeHandler.java	(revision 22675)
@@ -0,0 +1,67 @@
+package org.openstreetmap.josm.plugins.remotecontrol.handler;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.HashMap;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.plugins.remotecontrol.PermissionPref;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerBadRequestException;
+
+/**
+ * Handler for add_node request.
+ */
+public class AddNodeHandler extends RequestHandler {
+
+	public static final String command = "/add_node";
+
+	@Override
+	protected void handleRequest() {
+        addNode(args);
+	}
+
+	@Override
+	protected String[] getMandatoryParams()
+	{
+		return new String[] { "lat", "lon" };
+	}
+	
+	@Override
+	public String getPermissionMessage() {
+		return tr("Remote Control has been asked to create a new node.");
+	}
+
+	@Override
+	public PermissionPref getPermissionPref()
+	{
+		return new PermissionPref("remotecontrol.permission.create-objects",
+				"RemoteControl: creating objects forbidden by preferences");
+	}
+	
+    /**
+     * Adds a node, implements the GET /add_node?lon=...&amp;lat=... request.
+     * @param args
+     */
+    private void addNode(HashMap<String, String> args){
+
+        // Parse the arguments
+        double lat = Double.parseDouble(args.get("lat"));
+        double lon = Double.parseDouble(args.get("lon"));
+        System.out.println("Adding node at (" + lat + ", " + lon + ")");
+
+        // Create a new node
+        LatLon ll = new LatLon(lat, lon);
+        Node nnew = new Node(ll);
+
+        // Now execute the commands to add this node.
+        Main.main.undoRedo.add(new AddCommand(nnew));
+        Main.main.getCurrentDataSet().setSelected(nnew);
+        Main.map.mapView.repaint();
+
+    }
+
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/ImportHandler.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/ImportHandler.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/ImportHandler.java	(revision 22675)
@@ -0,0 +1,51 @@
+package org.openstreetmap.josm.plugins.remotecontrol.handler;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.net.URLDecoder;
+
+import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
+import org.openstreetmap.josm.plugins.remotecontrol.PermissionPref;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerBadRequestException;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerErrorException;
+
+/**
+ * Handler for import request
+ */
+public class ImportHandler extends RequestHandler {
+
+	public static final String command = "/import";
+
+	@Override
+	protected void handleRequest() throws RequestHandlerErrorException {
+        try {
+            DownloadTask osmTask = new DownloadOsmTask();
+            osmTask.loadUrl(false, URLDecoder.decode(args.get("url"), "UTF-8"), null);
+        } catch (Exception ex) {
+            System.out.println("RemoteControl: Error parsing import remote control request:");
+            ex.printStackTrace();
+            throw new RequestHandlerErrorException();
+        }
+	}
+
+	@Override
+	protected String[] getMandatoryParams()
+	{
+		return new String[] { "url" };
+	}
+	
+	@Override
+	public String getPermissionMessage() {
+		return tr("Remote Control has been asked to import data from the following URL:") +
+        "<br>" + request;
+	}
+
+	@Override
+	public PermissionPref getPermissionPref()
+	{
+		return new PermissionPref("remotecontrol.permission.import",
+				"RemoteControl: import forbidden by preferences");
+	}
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/LoadAndZoomHandler.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/LoadAndZoomHandler.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/LoadAndZoomHandler.java	(revision 22675)
@@ -0,0 +1,153 @@
+package org.openstreetmap.josm.plugins.remotecontrol.handler;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.geom.Area;
+import java.awt.geom.Rectangle2D;
+import java.util.HashSet;
+import java.util.concurrent.Future;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.AutoScaleAction;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
+import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerBadRequestException;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerErrorException;
+
+/**
+ * Handler for load_and_zoom request.
+ */
+public class LoadAndZoomHandler extends RequestHandler
+{
+	public static final String command = "/load_and_zoom";
+
+    @Override
+    public String getPermissionMessage()
+    {
+    	return tr("Remote Control has been asked to load data from the API.") +
+        "<br>" + tr("Request details: {0}", request);
+    }
+
+	@Override
+	protected String[] getMandatoryParams()
+	{
+		return new String[] { "bottom", "top", "left", "right" };
+	}
+	
+	@Override
+	protected void handleRequest() throws RequestHandlerErrorException
+	{
+		DownloadTask osmTask = new DownloadOsmTask();
+		double minlat = 0;
+		double maxlat = 0;
+		double minlon = 0;
+		double maxlon = 0;
+		try {
+			minlat = Double.parseDouble(args.get("bottom"));
+			maxlat = Double.parseDouble(args.get("top"));
+			minlon = Double.parseDouble(args.get("left"));
+			maxlon = Double.parseDouble(args.get("right"));
+
+			if (!Main.pref.getBoolean("remotecontrol.permission.load-data", true))
+			{
+				System.out.println("RemoteControl: download forbidden by preferences");
+			}
+			else
+			{
+
+				// find out whether some data has already been downloaded
+				Area present = null;
+				Area toDownload = null;
+				DataSet ds = Main.main.getCurrentDataSet();
+				if (ds != null)
+					present = ds.getDataSourceArea();
+				if (present != null && !present.isEmpty()) {
+					toDownload = new Area(new Rectangle2D.Double(minlon,minlat,maxlon-minlon,maxlat-minlat));
+					toDownload.subtract(present);
+					if (!toDownload.isEmpty())
+					{
+						// the result might not be a rectangle (L shaped etc)
+						Rectangle2D downloadBounds = toDownload.getBounds2D();
+						minlat = downloadBounds.getMinY();
+						minlon = downloadBounds.getMinX();
+						maxlat = downloadBounds.getMaxY();
+						maxlon = downloadBounds.getMaxX();
+					}
+				}
+				if((toDownload != null) && toDownload.isEmpty())
+				{
+					System.out.println("RemoteControl: no download necessary");
+				}
+				else
+				{
+                    Future<?> future = osmTask.download(false /*no new layer*/, new Bounds(minlat,minlon,maxlat,maxlon), null /* let the task manage the progress monitor */);
+                    Main.worker.submit(new PostDownloadHandler(osmTask, future));
+				}
+			}
+		} catch (Exception ex) {
+			System.out.println("RemoteControl: Error parsing load_and_zoom remote control request:");
+			ex.printStackTrace();
+			throw new RequestHandlerErrorException();
+		}
+		if (args.containsKey("select") && Main.pref.getBoolean("remotecontrol.permission.change-selection", true)) {
+			// select objects after downloading, zoom to selection.
+			final String selection = args.get("select");
+			Main.worker.execute(new Runnable() {
+				public void run() {
+					HashSet<Long> ways = new HashSet<Long>();
+					HashSet<Long> nodes = new HashSet<Long>();
+					HashSet<Long> relations = new HashSet<Long>();
+					HashSet<OsmPrimitive> newSel = new HashSet<OsmPrimitive>();
+					for (String item : selection.split(",")) {
+						if (item.startsWith("way")) {
+							ways.add(Long.parseLong(item.substring(3)));
+						} else if (item.startsWith("node")) {
+							nodes.add(Long.parseLong(item.substring(4)));
+						} else if (item.startsWith("relation")) {
+							relations.add(Long.parseLong(item.substring(8)));
+						} else if (item.startsWith("rel")) {
+							relations.add(Long.parseLong(item.substring(3)));
+						} else {
+							System.out.println("RemoteControl: invalid selection '"+item+"' ignored");
+						}
+					}
+					DataSet ds = Main.main.getCurrentDataSet();
+					if(ds == null) // e.g. download failed
+						return;
+					for (Way w : ds.getWays()) if (ways.contains(w.getId())) newSel.add(w);
+					for (Node n : ds.getNodes()) if (nodes.contains(n.getId())) newSel.add(n);
+					for (Relation r : ds.getRelations()) if (relations.contains(r.getId())) newSel.add(r);
+					ds.setSelected(newSel);
+					if (Main.pref.getBoolean("remotecontrol.permission.change-viewport", true))
+						new AutoScaleAction("selection").actionPerformed(null);
+				}
+			});
+		} else if (Main.pref.getBoolean("remotecontrol.permission.change-viewport", true)) {
+			// after downloading, zoom to downloaded area.
+			final Bounds bounds = new Bounds(new LatLon(minlat, minlon),
+					new LatLon(maxlat, maxlon));
+
+			// make sure this isn't called unless there *is* a MapView
+			//
+			if (Main.map != null && Main.map.mapView != null) {
+				Main.worker.execute(new Runnable() {
+					public void run() {
+						BoundingXYVisitor bbox = new BoundingXYVisitor();
+						bbox.visit(bounds);
+						Main.map.mapView.recalculateCenterScale(bbox);
+					}
+				});
+			}
+		}
+	}
+}
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/VersionHandler.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/VersionHandler.java	(revision 22675)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/handler/VersionHandler.java	(revision 22675)
@@ -0,0 +1,38 @@
+package org.openstreetmap.josm.plugins.remotecontrol.handler;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.plugins.remotecontrol.PermissionPref;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerBadRequestException;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerErrorException;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestProcessor;
+
+/**
+ * Handler for version request.
+ */
+public class VersionHandler extends RequestHandler {
+
+	public static final String command = "/version";
+
+	@Override
+	protected void handleRequest() throws RequestHandlerErrorException,
+			RequestHandlerBadRequestException {
+		content = RequestProcessor.PROTOCOLVERSION;
+		contentType = "application/json";
+		if (args.containsKey("jsonp")) {
+			content = args.get("jsonp")+ " && " + args.get("jsonp") + "(" + content + ")";
+		}
+	}
+
+	@Override
+	public String getPermissionMessage() {
+		return tr("Remote Control has been asked to report its protocol version. This enables web sites to detect a running JOSM.");
+	}
+
+	public PermissionPref getPermissionPref()
+	{
+		return new PermissionPref("remotecontrol.permission.read-protocolversion",
+				"RemoteControl: /version forbidden by preferences");
+	}
+}
