Index: /applications/editors/josm/plugins/remotecontrol/.classpath
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/.classpath	(revision 9417)
+++ /applications/editors/josm/plugins/remotecontrol/.classpath	(revision 9417)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
Index: /applications/editors/josm/plugins/remotecontrol/.project
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/.project	(revision 9416)
+++ /applications/editors/josm/plugins/remotecontrol/.project	(revision 9417)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>remotecontrol plugin</name>
+	<name>remotecontrol</name>
 	<comment></comment>
 	<projects>
Index: /applications/editors/josm/plugins/remotecontrol/CONTRIBUTION
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/CONTRIBUTION	(revision 9417)
+++ /applications/editors/josm/plugins/remotecontrol/CONTRIBUTION	(revision 9417)
@@ -0,0 +1,2 @@
+Originally developed by Frederik Ramm <frederik@remote.org>. Add yourself
+if you contribute.
Index: /applications/editors/josm/plugins/remotecontrol/README
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/README	(revision 9417)
+++ /applications/editors/josm/plugins/remotecontrol/README	(revision 9417)
@@ -0,0 +1,2 @@
+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.
Index: /applications/editors/josm/plugins/remotecontrol/build.xml
===================================================================
--- /applications/editors/josm/plugins/remotecontrol/build.xml	(revision 9417)
+++ /applications/editors/josm/plugins/remotecontrol/build.xml	(revision 9417)
@@ -0,0 +1,61 @@
+<project name="remotecontrol" default="dist" basedir=".">
+
+  <!-- compilation properties -->
+  <property name="josm.build.dir"	value="../../core"/>
+  <property name="josm.home.dir"	value="${user.home}/.josm"/>
+  <property name="josm"			location="../../core/dist/josm-custom.jar" />
+  <property name="plugin.build.dir"	value="build"/>
+  <property name="plugin.dist.dir"	value="../../dist"/>
+  <property name="plugin.name"		value="${ant.project.name}"/>
+  <property name="plugin.jar"		value="../../dist/${plugin.name}.jar"/>
+
+  <property name="ant.build.javac.target" value="1.5"/>
+  
+  <target name="init">
+    <mkdir dir="${plugin.build.dir}"/>
+  </target>
+
+  <target name="compile" depends="init">
+    <echo message="creating ${plugin.jar}"/>
+    <javac srcdir="src" classpath="${josm}" destdir="build" debug="true">
+      <include name="**/*.java" />
+    </javac>
+  </target>
+
+  <target name="dist" depends="compile">
+    <copy todir="${plugin.build.dir}/images">
+      <fileset dir="images"/>
+    </copy>
+    <exec append="false" output="REVISION" executable="svn" failifexecutionfails="false">
+      <env key="LANG" value="C"/>
+      <arg value="info"/>
+      <arg value="--xml"/>
+      <arg value="."/>
+    </exec>
+    <xmlproperty file="REVISION" prefix="version" keepRoot="false" collapseAttributes="true"/>
+    <delete file="REVISION"/>
+    <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
+      <manifest>
+	<attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.remotecontrol.RemoteControlPlugin"/>
+	<attribute name="Plugin-Description" value="A plugin allowing other applications to send commands to JOSM." />
+	<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
+	<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
+	<attribute name="Author" value="Frederik Ramm &lt;frederik@remote.org&gt;"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="clean">
+    <delete dir="${plugin.build.dir}" />
+    <delete file="${plugin.jar}" />
+  </target>
+  
+  <target name="install" depends="dist">
+    <copy file="${plugin.jar}" todir="${user.home}/.josm/plugins"/>
+  </target>
+
+  <target name="test" depends="install">
+    <java jar="${josm}" fork="true"/>
+  </target>
+
+</project>
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 9417)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/HttpServer.java	(revision 9417)
@@ -0,0 +1,73 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.InetAddress;
+
+/**
+ * Simple HTTP server that spawns a {@link RequestProcessor} for every connection.
+ * 
+ * @author frsantos
+  */
+public class HttpServer extends Thread 
+{
+    /** Default port for the HTTP server */
+	public static final int DEFAULT_PORT = 8111;
+	
+	/** The server socket */
+	private ServerSocket server;
+
+	/**
+	 * Constructor
+	 * @param port The port this server will listen on
+	 * @throws IOException when connection errors
+	 */
+	public HttpServer(int port)
+		throws IOException 
+	{
+		super("RemoteControl HTTP Server");
+		this.setDaemon(true);
+		// Start the server socket with only 1 connection.
+        // Also make sure we only listen
+        // on the local interface so nobody from the outside can connect!
+		this.server = new ServerSocket(port, 1, 
+            InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
+	}
+
+	/**
+	 * The main loop, spawns a {@link RequestProcessor} for each connection
+	 */
+	public void run() 
+	{
+		System.out.println("RemoteControl::Accepting connections on port " + server.getLocalPort());
+		while (true) 
+		{
+			try 
+			{
+				Socket request = server.accept();
+				RequestProcessor.processRequest(request);
+			}
+			catch( SocketException se)
+			{
+				if( !server.isClosed() )
+					se.printStackTrace();
+			}
+			catch (IOException ioe) 
+			{
+				ioe.printStackTrace();
+			}
+		}
+	}
+	
+	/**
+     * Stops the HTTP server
+     *  
+     * @throws IOException
+	 */
+	public void stopServer() throws IOException
+	{
+		server.close();
+	}
+}
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 9417)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPlugin.java	(revision 9417)
@@ -0,0 +1,51 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+import java.io.IOException;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+
+/**
+
+ */
+public class RemoteControlPlugin extends Plugin 
+{
+	/** The HTTP server this plugin launches */
+	static HttpServer server;
+    
+	/**
+	 * Creates the plugin, and starts the HTTP server
+	 */
+	public RemoteControlPlugin()
+	{
+		restartServer();
+	}
+	
+    @Override
+	public PreferenceSetting getPreferenceSetting() 
+	{
+		return new RemoteControlPreferences();
+	}
+	
+	/**
+	 * Starts or restarts the HTTP server
+	 *
+	 */
+	public void restartServer()
+	{
+		try
+		{
+			if (server != null)
+				server.stopServer();
+			
+			int port = HttpServer.DEFAULT_PORT;
+			server = new HttpServer(port);
+			server.start();
+		}
+		catch(IOException ioe)
+		{
+			ioe.printStackTrace();
+		}
+	}
+}
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 9417)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RemoteControlPreferences.java	(revision 9417)
@@ -0,0 +1,69 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.*;
+
+import javax.swing.*;
+
+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.I18n;
+
+/**
+ * Preference settings for the Remote Control plugin
+ * 
+ * @author Frederik Ramm
+ */
+public class RemoteControlPreferences implements PreferenceSetting
+{
+
+    
+    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 )
+			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."));
+    }
+    
+    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()
+    {
+    }
+}
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 9417)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/RequestProcessor.java	(revision 9417)
@@ -0,0 +1,227 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+import java.io.*;
+import java.net.Socket;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.StringTokenizer;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
+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.gui.download.DownloadDialog.DownloadTask;
+
+/**
+ * Processes HTTP "remote control" requests.
+ */
+public class RequestProcessor extends Thread 
+{
+	/** The socket this processor listens on */
+	private Socket request;
+  
+	/**
+	 * Constructor
+	 * 
+	 * @param request The WMS 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 
+		{            
+	        OutputStream raw = new BufferedOutputStream( request.getOutputStream());         
+	        out = new OutputStreamWriter(raw);
+	        Reader in = new InputStreamReader(new BufferedInputStream(request.getInputStream()), "ASCII");
+	        
+	        StringBuffer requestLine = new StringBuffer();
+	        while (true) 
+	        {
+	            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);
+	        String method = st.nextToken();
+	        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")) {
+				DownloadTask osmTask = new DownloadOsmTask();
+				if (!(args.containsKey("bottom") && args.containsKey("top") && 
+					args.containsKey("left") && args.containsKey("right"))) {
+					sendError(out);
+					System.out.println("load_and_zoom remote control request must have bottom,top,left,right parameters");
+					return;	
+				}
+				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)
+				{
+					sendError(out);
+					System.out.println("RemoteControl: Error parsing load_and_zoom remote control request:");
+					ex.printStackTrace();
+					return;
+				}
+				if (args.containsKey("select")) {
+					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 {
+									System.out.println("RemoteControl: invalid selection '"+item+"' ignored");
+								}
+							}
+							for (Way w : Main.ds.ways) if (ways.contains(w.id)) newSel.add(w);
+							for (Node n : Main.ds.nodes) if (nodes.contains(n.id)) newSel.add(n);
+							for (Relation r : Main.ds.relations) if (relations.contains(r.id)) newSel.add(r);	
+							Main.ds.setSelected(newSel);
+						}
+					});
+				};
+            }
+			sendHeader(out, "200 OK", "text/plain", false);
+            out.write("Content-length: 4\r\n");
+            out.write("\r\n");
+            out.write("OK\r\n");
+            out.flush();
+		}
+		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();
+	}
+	
+	/**
+	 * 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");
+        if (endHeaders)
+        	out.write("\r\n");
+	}
+}
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 9417)
+++ /applications/editors/josm/plugins/remotecontrol/src/org/openstreetmap/josm/plugins/remotecontrol/Util.java	(revision 9417)
@@ -0,0 +1,251 @@
+package org.openstreetmap.josm.plugins.remotecontrol;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionListener;
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.StringTokenizer;
+
+import javax.swing.JButton;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.*;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Utility class
+ * 
+ * @author frsantos
+ */
+public class Util 
+{
+
+    /**
+     * Utility method to retrieve the plugin for classes that can't access to the plugin object directly.
+     * 
+     * @param clazz The plugin class
+     * @return The YWMS plugin
+     */
+    public static Plugin getPlugin(Class<? extends Plugin> clazz)
+    {
+    	String classname = clazz.getName();
+        for (PluginProxy plugin : Main.plugins)
+        {
+            if( plugin.info.className.equals(classname) )
+            {
+                return (Plugin)plugin.plugin;
+            }
+        }
+        
+        return null;
+    }
+    
+	/** 
+	 * Returns the plugin's directory of the plugin
+	 * 
+	 * @return The directory of the plugin
+	 */
+	public static String getPluginDir()
+	{
+		return Main.pref.getPreferencesDir() + "plugins/ywms/";
+	}
+
+	/**
+	 * 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
+	 */
+	public static Version getVersion()
+    {
+        PluginInformation info = PluginInformation.getLoaded("ywms");
+        if( info == null )
+            return null;
+
+        return new Version(info.version, info.attr.get("Plugin-Date"));
+    }
+
+    /**
+     * Utility class for displaying versions
+     * 
+     * @author frsantos
+     */
+    public static class Version
+    {
+    	/** The revision */
+    	public String revision;
+    	/** The build time */
+    	public String time;
+    	
+        /**
+         * Constructor
+         * @param revision
+         * @param time
+         */
+        public Version(String revision, String time) 
+        {
+			this.revision = revision;
+			this.time = time;
+		}
+    }
+    
+    
+    /**
+     * 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();
+    }
+}
