Index: /applications/editors/josm/plugins/remotecontrol/README
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/README	(revision 9464)
+++ /applications/editors/josm/plugins/remotecontrol/README	(revision 9465)
@@ -1,2 +1,9 @@
-A plugin for JOSM that listens for commands on a TCP port. It can be used to 
-tell JOSM to zoom to an area and download data.
+A plugin for JOSM that listens for commands on a TCP port (8111). 
+It can be used to tell JOSM to zoom to an area and download data. 
+Details are found on the Wiki page, 
+
+http://wiki.openstreetmap.org/index.php/JOSM/Plugins/RemoteControl
+
+Initially written by Frederik Ramm <frederik@remote.org>. License:
+GPL V2 or later. Incorporates code taken from YWMS plugin by frsantos.
+
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/HttpServer.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/HttpServer.java	(revision 9464)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/HttpServer.java	(revision 9465)
@@ -8,10 +8,12 @@
 
 /**
- * Simple HTTP server that spawns a {@link RequestProcessor} for every connection.
- * 
- * @author frsantos
-  */
-public class HttpServer extends Thread 
-{
+ * Simple HTTP server that spawns a {@link RequestProcessor} for every 
+ * connection.
+ *
+ * Taken from YWMS plugin by frsantos.
+ */
+
+public class HttpServer extends Thread {
+
     /** Default port for the HTTP server */
 	public static final int DEFAULT_PORT = 8111;
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 9464)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPreferences.java	(revision 9465)
@@ -3,11 +3,15 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.Color;
+import java.awt.GridBagLayout;
 import java.awt.event.*;
 
 import javax.swing.*;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
 import org.openstreetmap.josm.plugins.remotecontrol.Util.Version;
+import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.I18n;
 
@@ -19,51 +23,44 @@
 public class RemoteControlPreferences implements PreferenceSetting
 {
-
-    
-    public void addGui( final PreferenceDialog gui ) 
+	private JCheckBox permissionLoadData = new JCheckBox(tr("load data from API"));
+	private JCheckBox permissionChangeSelection = new JCheckBox(tr("change the selection"));
+	private JCheckBox permissionChangeViewport = new JCheckBox(tr("change the viewport"));
+	private JCheckBox alwaysAskUserConfirm = new JCheckBox(tr("confirm all Remote Control actions manually"));
+	
+    public void addGui(final PreferenceDialog gui) 
     {
- 
 		Version ver = Util.getVersion();
 		String description = tr("A plugin that allows JOSM to be controlled from other applications.");
-		if( ver != null )
+		if (ver != null)
 			description += "<br><br>" + tr("Version: {0}<br>Last change at {1}", ver.revision, ver.time) + "<br><br>";
-    	JPanel ywms = gui.createPreferenceTab("remotecontrol.gif", I18n.tr("Remote Control"), description + I18n.tr("Settings for the Remote Control plugin."));
-    	ywms.add(new JLabel("no prefs yet."));
+    	JPanel remote = gui.createPreferenceTab("remotecontrol.gif", tr("Remote Control"), tr("Settings for the Remote Control plugin."));    
+    	remote.add(new JLabel("<html>"+tr("The Remote Control plugin will always listen on port 8111 on localhost." + 
+    			"The port is not variable because it is referenced by external applications talking to the plugin.") + "</html>"), GBC.eol().insets(0,5,0,10).fill(GBC.HORIZONTAL));
+
+    	JPanel perms = new JPanel();
+    	perms.setLayout(new GridBagLayout());
+    	perms.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Permitted actions")));
+        perms.add(permissionLoadData, GBC.eol().insets(0,5,0,0).fill(GBC.HORIZONTAL));
+        perms.add(permissionChangeSelection, GBC.eol().insets(0,5,0,0).fill(GBC.HORIZONTAL));
+        perms.add(permissionChangeViewport, GBC.eol().insets(0,5,0,0).fill(GBC.HORIZONTAL));       
+        remote.add(perms, GBC.eol().fill(GBC.HORIZONTAL));
+        
+        remote.add(alwaysAskUserConfirm, GBC.eol().insets(0,5,0,0).fill(GBC.HORIZONTAL));
+        remote.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+
+        
+        permissionLoadData.setSelected(Main.pref.getBoolean("remotecontrol.permission.load-data", true));
+        permissionChangeSelection.setSelected(Main.pref.getBoolean("remotecontrol.permission.change-selection", true));
+        permissionChangeViewport.setSelected(Main.pref.getBoolean("remotecontrol.permission.change-viewport", true));
+        alwaysAskUserConfirm.setSelected(Main.pref.getBoolean("remotecontrol.always-confirm", false));
+        
     }
     
     public void ok() {
-    }
-
-    /**
-     * ActionListener for the configuration of WMS plugin  
-     * @author frsantos
-     */
-    private final class RemoteControlConfigurationActionListener implements ActionListener, FocusListener
-    {
-    	/** If the action is already handled */
-        boolean alreadyHandled = false;
-        public void actionPerformed(ActionEvent e) {
-            if(!alreadyHandled)
-                configureRemoteControlPluginPreferences();
-            alreadyHandled = true;
-        }
-
-        public void focusGained(FocusEvent e) {
-            alreadyHandled = false;
-        }
-
-        public void focusLost(FocusEvent e) {
-            if(!alreadyHandled)
-                configureRemoteControlPluginPreferences();
-            alreadyHandled = true;
-        }
-    }
-    
-    
-    /**
-     * Configures Remote Control 
-     */
-    private void configureRemoteControlPluginPreferences()
-    {
+    	Main.pref.put("remotecontrol.permission.load-data", permissionLoadData.isSelected());
+    	Main.pref.put("remotecontrol.permission.change-selection", permissionChangeSelection.isSelected());
+    	Main.pref.put("remotecontrol.permission.change-viewport", permissionChangeViewport.isSelected());
+    	Main.pref.put("remotecontrol.always-confirm", alwaysAskUserConfirm.isSelected());
+    		
     }
 }
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 9464)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestProcessor.java	(revision 9465)
@@ -1,4 +1,8 @@
 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.*;
 import java.net.Socket;
@@ -8,10 +12,16 @@
 import java.util.StringTokenizer;
 
+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.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
 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.gui.download.DownloadDialog.DownloadTask;
 
@@ -23,4 +33,8 @@
 	/** The socket this processor listens on */
 	private Socket request;
+	
+	private class AlreadyLoadedException extends Exception {};
+	private class DeniedException extends Exception {};
+	private class LoadDeniedException extends Exception {};
   
 	/**
@@ -73,6 +87,5 @@
 	        String url = st.nextToken();
 
-	        if( !method.equals("GET") )
-	        {
+	        if(!method.equals("GET")) {
 	        	sendNotImplemented(out);
 	        	return;
@@ -85,33 +98,61 @@
             {
                 String param = st.nextToken();
-                if (command == null) 
-                {
+                if (command == null) {
                     command = param;
-                } 
-                else
-                {
+                } else {
                     int eq = param.indexOf("=");
-                    if (eq>-1)
-                    	args.put(param.substring(0,eq), param.substring(eq+1));
+                    if (eq>-1) args.put(param.substring(0,eq), param.substring(eq+1));
                 }
             }
             
             if (command.equals("/load_and_zoom")) {
-				DownloadTask osmTask = new DownloadOsmTask();
+            	if (Main.pref.getBoolean("remotecontrol.always-confirm", false)) {
+            		if (JOptionPane.showConfirmDialog(Main.parent,
+            			tr("Remote Control has been asked to load data from the API. Request details: {0}. Do you want to allow this?", url),
+            			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"))) {
-					sendError(out);
+					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 {
-					double minlat = Double.parseDouble(args.get("bottom"));
-					double maxlat = Double.parseDouble(args.get("top"));
-					double minlon = Double.parseDouble(args.get("left"));
-					double maxlon = Double.parseDouble(args.get("right"));
-					osmTask.download(Main.main.menu.download, minlat,minlon,maxlat,maxlon);
-				}
-				catch (Exception ex)
-				{
+					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 = Main.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();
+					}
+					osmTask.download(null, minlat,minlon,maxlat,maxlon);
+				} catch (AlreadyLoadedException ex) {
+					System.out.println("RemoteControl: no download necessary");
+				} catch (LoadDeniedException ex) {
+					System.out.println("RemoteControl: download forbidden by preferences");
+				} catch (Exception ex) {
 					sendError(out);
 					System.out.println("RemoteControl: Error parsing load_and_zoom remote control request:");
@@ -119,5 +160,6 @@
 					return;
 				}
-				if (args.containsKey("select")) {
+				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() {
@@ -142,7 +184,22 @@
 							for (Relation r : Main.ds.relations) if (relations.contains(r.id)) newSel.add(r);	
 							Main.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 LatLon min = new LatLon(minlat, minlon);
+					final LatLon max = new LatLon(maxlat, maxlon);
+					
+					Main.worker.execute(new Runnable() {
+						public void run() {
+							BoundingXYVisitor bbox = new BoundingXYVisitor();
+							bbox.min = Main.proj.latlon2eastNorth(min);
+							bbox.max = Main.proj.latlon2eastNorth(max);
+							Main.map.mapView.recalculateCenterScale(bbox);
+						}
+					});
+				}
             }
 			sendHeader(out, "200 OK", "text/plain", false);
@@ -153,20 +210,13 @@
 		}
 		catch (IOException ioe) { }
-		catch(Exception e)
-		{
+		catch(Exception e) {
 			e.printStackTrace();
-			try 
-			{
+			try {
 				sendError(out);
-			} 
-			catch (IOException e1) { }
-		}
-		finally 
-		{
-	        try 
-	        {
+			} catch (IOException e1) { }
+		} finally {
+	        try {
 	        	request.close();        
-	        }
-	        catch (IOException e) {} 
+	        } catch (IOException e) {} 
 		}
 	}
@@ -205,4 +255,37 @@
 		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();
+	}
 	
 	/**
Index: /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/Util.java
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/Util.java	(revision 9464)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/Util.java	(revision 9465)
@@ -18,9 +18,7 @@
  * Utility class
  * 
- * @author frsantos
  */
 public class Util 
 {
-
     /**
      * Utility method to retrieve the plugin for classes that can't access to the plugin object directly.
@@ -34,10 +32,9 @@
         for (PluginProxy plugin : Main.plugins)
         {
-            if( plugin.info.className.equals(classname) )
+            if(plugin.info.className.equals(classname))
             {
                 return (Plugin)plugin.plugin;
             }
         }
-        
         return null;
     }
@@ -54,23 +51,4 @@
 
 	/**
-	 * Utility method for creating buttons
-	 * @param name The name of the button
-	 * @param icon Icon of the button
-	 * @param tooltip Tooltip
-	 * @param action The action performed when clicking the button
-	 * @return The created button
-	 */
-    public static JButton createButton(String name, String icon, String tooltip, ActionListener action) 
-    {
-		JButton button = new JButton(tr(name), ImageProvider.get(icon));
-		button.setActionCommand(name);
-		button.addActionListener(action);
-		button.setToolTipText(tr(tooltip));
-		button.putClientProperty("help", "Dialog/SelectionList/" + name);
-		return button;
-	}
-    
-    
-	/**
 	 * Returns the version
 	 * @return The version of the application
@@ -79,5 +57,5 @@
     {
         PluginInformation info = PluginInformation.getLoaded("ywms");
-        if( info == null )
+        if (info == null) 
             return null;
 
@@ -108,144 +86,3 @@
 		}
     }
-    
-    
-    /**
-     * Loads a text file in a String
-     * 
-     * @param resource The URL of the file
-     * @return A String with the file contents
-     * @throws IOException when error reading the file
-     */
-    public static String loadFile(URL resource) throws IOException
-    {
-    	BufferedReader in = null;
-		try 
-		{
-			in = new BufferedReader(new InputStreamReader(resource.openStream()));
-			StringBuilder sb = new StringBuilder();
-			for (String line = in.readLine(); line != null; line = in.readLine()) 
-			{
-				sb.append(line);
-				sb.append('\n');
-			}
-			return sb.toString();
-		}
-		finally
-		{
-			if( in != null )
-			{
-				try {
-					in.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-			}
-		}
-    }
-    
-    /**
-     * Mirrors a file to a local file.
-     * <p>
-     * The file mirrored is only downloaded if it has been more than one day since last download
-     * 
-     * @param url The URL of the remote file
-     * @param destDir The destionation dir of the mirrored file
-     * @param maxTime The time interval, in seconds, to check if the file changed. If less than 0, it defaults to 1 week 
-     * @return The local file
-     */
-    public static File mirror(URL url, String destDir, long maxTime)
-    {
-        if( url.getProtocol().equals("file") )
-            return new File(url.toString() ) ;
-        
-        String localPath = Main.pref.get("tests.mirror." + url);
-        File oldFile = null;
-        if( localPath != null && localPath.length() > 0)
-        {
-            StringTokenizer st = new StringTokenizer(localPath, ";");
-            long checkDate = Long.parseLong(st.nextToken());
-            localPath = st.nextToken();
-            oldFile = new File(localPath);
-            maxTime = (maxTime <= 0) ? 7 * 24 * 60 * 60 * 1000 : maxTime * 1000;
-            if( System.currentTimeMillis() - checkDate < maxTime )
-            {
-                if( oldFile.exists() )
-                    return oldFile;
-            }
-        }
-
-        File destDirFile = new File(destDir);
-        if( !destDirFile.exists() )
-            destDirFile.mkdirs();
-
-        localPath = destDir + System.currentTimeMillis() + "-" + new File(url.getPath()).getName();
-        BufferedOutputStream bos = null;
-        BufferedInputStream bis = null;
-        try 
-        {
-            URLConnection conn = url.openConnection();
-            conn.setConnectTimeout(5000);
-            bis = new BufferedInputStream(conn.getInputStream());
-            bos = new BufferedOutputStream( new FileOutputStream(localPath) );
-            byte[] buffer = new byte[4096];
-            int length;
-            while( (length = bis.read( buffer )) > -1 )
-            {
-                bos.write( buffer, 0, length );
-            }
-        }
-        catch(IOException ioe)
-        {
-            if( oldFile != null )
-                return oldFile;
-            else
-                return null;
-        }
-        finally
-        {
-            if( bis != null )
-            {
-                try {
-                    bis.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-            }
-            if( bos != null )
-            {
-                try {
-                    bos.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-            }
-        }
-        
-        Main.pref.put("tests.mirror." + url, System.currentTimeMillis() + ";" + localPath);
-        
-        if( oldFile != null )
-            oldFile.delete();
-
-        return new File(localPath);
-    }
-    
-	/**
-	 * Copies the ressource 'from' to the file in the plugin directory named 'to'.
-	 * @param from The source directory
-	 * @param to The destination directory
-	 * @throws FileNotFoundException 
-	 * @throws IOException 
-	 */
-	public static void copy(String from, String to) throws FileNotFoundException, IOException {
-		File pluginDir = new File(getPluginDir());
-		if (!pluginDir.exists())
-			pluginDir.mkdirs();
-    	FileOutputStream out = new FileOutputStream(getPluginDir()+to);
-    	InputStream in = Util.class.getResourceAsStream(from);
-    	byte[] buffer = new byte[8192];
-    	for(int len = in.read(buffer); len > 0; len = in.read(buffer))
-    		out.write(buffer, 0, len);
-    	in.close();
-    	out.close();
-    }
 }
