Index: /trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java	(revision 7241)
+++ /trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java	(revision 7242)
@@ -33,8 +33,28 @@
  */
 public class MirroredInputStream extends InputStream {
+    
+    /**
+     * Caching strategy.
+     */
+    public enum CachingStrategy {
+        /**
+         * If cached file on disk is older than a certain time (7 days by default),
+         * consider the cache stale and try to download the file again.
+         */
+        MaxAge, 
+        /**
+         * Similar to MaxAge, considers the cache stale when a certain age is
+         * exceeded. In addition, a If-Modified-Since HTTP header is added.
+         * When the server replies "304 Not Modified", this is considered the same
+         * as a full download.
+         */
+        IfModifiedSince 
+    }
+    
     InputStream fs = null;
     File file = null;
 
     public static final long DEFAULT_MAXTIME = -1L;
+    public static final long DAYS = 24*60*60; // factor to get caching time in days
 
     /**
@@ -136,4 +156,24 @@
      */
     public MirroredInputStream(String name, String destDir, long maxTime, String httpAccept) throws IOException {
+        this(name, destDir, maxTime, httpAccept, CachingStrategy.MaxAge);
+    }
+
+    /**
+     * Constructs an input stream from a given filename, URL or internal resource.
+     *
+     * @param name can be:<ul>
+     *  <li>relative or absolute file name</li>
+     *  <li>{@code file:///SOME/FILE} the same as above</li>
+     *  <li>{@code resource://SOME/FILE} file from the classpath (usually in the current *.jar)</li>
+     *  <li>{@code josmdir://SOME/FILE} file inside josm config directory (since r7058)</li>
+     *  <li>{@code http://...} a URL. It will be cached on disk.</li></ul>
+     * @param destDir the destination directory for the cache file. Only applies for URLs.
+     * @param maxTime the maximum age of the cache file (in seconds)
+     * @param httpAccept The accepted MIME types sent in the HTTP Accept header. Only applies for URLs.
+     * @param caching the caching strategy
+     * @throws IOException when the resource with the given name could not be retrieved
+     * @since 6867
+     */
+    public MirroredInputStream(String name, String destDir, long maxTime, String httpAccept, CachingStrategy caching) throws IOException {
         URL url;
         try {
@@ -145,5 +185,5 @@
                 }
             } else {
-                file = checkLocal(url, destDir, maxTime, httpAccept);
+                file = checkLocal(url, destDir, maxTime, httpAccept, caching);
             }
         } catch (java.net.MalformedURLException e) {
@@ -276,7 +316,8 @@
     }
 
-    private File checkLocal(URL url, String destDir, long maxTime, String httpAccept) throws IOException {
+    private File checkLocal(URL url, String destDir, long maxTime, String httpAccept, CachingStrategy caching) throws IOException {
         String prefKey = getPrefKey(url, destDir);
         long age = 0L;
+        Long ifModifiedSince = null;
         File localFile = null;
         List<String> localPathEntry = new ArrayList<>(Main.pref.getCollection(prefKey));
@@ -295,4 +336,7 @@
                     return localFile;
                 }
+                if (caching == CachingStrategy.IfModifiedSince) {
+                    ifModifiedSince = Long.parseLong(localPathEntry.get(0));
+                }
             }
         }
@@ -305,10 +349,17 @@
             destDirFile.mkdirs();
         }
-
+        
         String a = url.toString().replaceAll("[^A-Za-z0-9_.-]", "_");
         String localPath = "mirror_" + a;
         destDirFile = new File(destDir, localPath + ".tmp");
         try {
-            HttpURLConnection con = connectFollowingRedirect(url, httpAccept);
+            HttpURLConnection con = connectFollowingRedirect(url, httpAccept, ifModifiedSince);
+            if (ifModifiedSince != null && con.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
+                Main.debug("304 Not Modified ("+url+")");
+                if (localFile == null) throw new AssertionError();
+                Main.pref.putCollection(prefKey, 
+                        Arrays.asList(Long.toString(System.currentTimeMillis()), localPathEntry.get(1)));
+                return localFile;
+            } 
             try (
                 InputStream bis = new BufferedInputStream(con.getInputStream());
@@ -324,6 +375,6 @@
             localFile = new File(destDir, localPath);
             if(Main.platform.rename(destDirFile, localFile)) {
-                Main.pref.putCollection(prefKey, Arrays.asList(new String[]
-                {Long.toString(System.currentTimeMillis()), localFile.toString()}));
+                Main.pref.putCollection(prefKey, 
+                        Arrays.asList(Long.toString(System.currentTimeMillis()), localFile.toString()));
             } else {
                 Main.warn(tr("Failed to rename file {0} to {1}.",
@@ -353,4 +404,5 @@
      * @param downloadUrl The resource URL to download
      * @param httpAccept The accepted MIME types sent in the HTTP Accept header. Can be {@code null}
+     * @param ifModifiedSince The download time of the cache file, optional
      * @return The HTTP connection effectively linked to the resource, after all potential redirections
      * @throws MalformedURLException If a redirected URL is wrong
@@ -358,9 +410,12 @@
      * @since 6867
      */
-    public static HttpURLConnection connectFollowingRedirect(URL downloadUrl, String httpAccept) throws MalformedURLException, IOException {
+    public static HttpURLConnection connectFollowingRedirect(URL downloadUrl, String httpAccept, Long ifModifiedSince) throws MalformedURLException, IOException {
         HttpURLConnection con = null;
         int numRedirects = 0;
         while(true) {
             con = Utils.openHttpConnection(downloadUrl);
+            if (ifModifiedSince != null) {
+                con.setIfModifiedSince(ifModifiedSince);
+            }
             con.setInstanceFollowRedirects(false);
             con.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
@@ -380,4 +435,7 @@
             case HttpURLConnection.HTTP_OK:
                 return con;
+            case HttpURLConnection.HTTP_NOT_MODIFIED:
+                if (ifModifiedSince != null)
+                    return con;
             case HttpURLConnection.HTTP_MOVED_PERM:
             case HttpURLConnection.HTTP_MOVED_TEMP:
Index: /trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 7241)
+++ /trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 7242)
@@ -50,5 +50,6 @@
             SAXParserFactory factory = SAXParserFactory.newInstance();
             factory.setNamespaceAware(true);
-            try (InputStream in = new MirroredInputStream(source)) {
+            try (InputStream in = new MirroredInputStream(source, null, 1*MirroredInputStream.DAYS, null, 
+                    MirroredInputStream.CachingStrategy.IfModifiedSince)) {
                 InputSource is = new InputSource(UTFInputStreamReader.create(in));
                 factory.newSAXParser().parse(is, parser);
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginDownloadTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginDownloadTask.java	(revision 7241)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginDownloadTask.java	(revision 7242)
@@ -125,5 +125,5 @@
             URL url = new URL(pi.downloadlink);
             synchronized(this) {
-                downloadConnection = MirroredInputStream.connectFollowingRedirect(url, PLUGIN_MIME_TYPES);
+                downloadConnection = MirroredInputStream.connectFollowingRedirect(url, PLUGIN_MIME_TYPES, null);
             }
             try (
