Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 4240)
@@ -4,4 +4,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -42,4 +44,5 @@
     private String name;
     private String url = null;
+    private boolean defaultEntry = false;
     private String cookies = null;
     private String eulaAcceptanceRequired= null;
@@ -50,4 +53,5 @@
     private int defaultMinZoom = 0;
     private Bounds bounds = null;
+    private List<String> serverProjections;
     private String attributionText;
     private String attributionImage;
@@ -55,4 +59,7 @@
     private String termsOfUseURL;
 
+    public ImageryInfo() {
+    }
+
     public ImageryInfo(String name) {
         this.name=name;
@@ -61,10 +68,10 @@
     public ImageryInfo(String name, String url) {
         this.name=name;
-        setUrl(url);
+        setExtendedUrl(url);
     }
 
     public ImageryInfo(String name, String url, String eulaAcceptanceRequired) {
         this.name=name;
-        setUrl(url);
+        setExtendedUrl(url);
         this.eulaAcceptanceRequired = eulaAcceptanceRequired;
     }
@@ -72,5 +79,5 @@
     public ImageryInfo(String name, String url, String eulaAcceptanceRequired, String cookies) {
         this.name=name;
-        setUrl(url);
+        setExtendedUrl(url);
         this.cookies=cookies;
         this.eulaAcceptanceRequired = eulaAcceptanceRequired;
@@ -79,5 +86,5 @@
     public ImageryInfo(String name, String url, String cookies, double pixelPerDegree) {
         this.name=name;
-        setUrl(url);
+        setExtendedUrl(url);
         this.cookies=cookies;
         this.pixelPerDegree=pixelPerDegree;
@@ -87,5 +94,5 @@
         ArrayList<String> res = new ArrayList<String>();
         res.add(name);
-        res.add((url != null && !url.isEmpty()) ? getFullUrl() : null);
+        res.add((url != null && !url.isEmpty()) ? getExtendedUrl() : null);
         res.add(cookies);
         if(imageryType == ImageryType.WMS || imageryType == ImageryType.HTML) {
@@ -106,5 +113,5 @@
         this.name=array.get(0);
         if(array.size() >= 2 && !array.get(1).isEmpty()) {
-            setUrl(array.get(1));
+            setExtendedUrl(array.get(1));
         }
         if(array.size() >= 3 && !array.get(2).isEmpty()) {
@@ -178,4 +185,12 @@
     }
 
+    public void setDefaultMaxZoom(int defaultMaxZoom) {
+        this.defaultMaxZoom = defaultMaxZoom;
+    }
+
+    public void setDefaultMinZoom(int defaultMinZoom) {
+        this.defaultMinZoom = defaultMinZoom;
+    }
+    
     public void setMaxZoom(int maxZoom) {
         this.maxZoom = maxZoom;
@@ -206,5 +221,5 @@
     }
 
-    public void setUrl(String url) {
+    public void setExtendedUrl(String url) {
         CheckParameterUtil.ensureParameterNotNull(url);
         
@@ -244,4 +259,16 @@
     }
 
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public boolean isDefaultEntry() {
+        return defaultEntry;
+    }
+
+    public void setDefaultEntry(boolean defaultEntry) {
+        this.defaultEntry = defaultEntry;
+    }
+
     public String getCookies() {
         return this.cookies;
@@ -264,5 +291,24 @@
     }
 
-    public String getFullUrl() {
+    public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
+        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
+    }
+
+    /**
+     * Get the projections supported by the server. Only relevant for
+     * WMS-type ImageryInfo at the moment.
+     * @return null, if no projections have been specified; the list
+     * of supported projections otherwise.
+     */
+    public List<String> getServerProjections() {
+        if (serverProjections == null) return null;
+        return Collections.unmodifiableList(serverProjections);
+    }
+
+    public void setServerProjections(Collection<String> serverProjections) {
+        this.serverProjections = new ArrayList<String>(serverProjections);
+    }
+
+    public String getExtendedUrl() {
         return imageryType.getUrlString() + (defaultMaxZoom != 0
             ? "["+(defaultMinZoom != 0 ? defaultMinZoom+",":"")+defaultMaxZoom+"]" : "") + ":" + url;
@@ -331,4 +377,8 @@
     public ImageryType getImageryType() {
         return imageryType;
+    }
+
+    public void setImageryType(ImageryType imageryType) {
+        this.imageryType = imageryType;
     }
 
Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 4240)
@@ -2,10 +2,5 @@
 package org.openstreetmap.josm.data.imagery;
 
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -16,7 +11,8 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
-import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.io.imagery.ImageryReader;
 import org.openstreetmap.josm.io.MirroredInputStream;
+import org.openstreetmap.josm.tools.Utils;
+import org.xml.sax.SAXException;
 
 public class ImageryLayerInfo {
@@ -65,88 +61,50 @@
     public void loadDefaults(boolean clearCache) {
         defaultLayers.clear();
-        Collection<String> defaults = Main.pref.getCollection(
-                "imagery.layers.default", Collections.<String>emptySet());
+        for (String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES))) {
+            if (clearCache) {
+                MirroredInputStream.cleanup(source);
+            }
+            MirroredInputStream stream = null;
+            try {
+                ImageryReader reader = new ImageryReader(source);
+                Collection<ImageryInfo> result = reader.parse();
+                defaultLayers.addAll(result);
+            } catch (IOException ex) {
+                Utils.close(stream);
+                ex.printStackTrace();
+                continue;
+            } catch (SAXException sex) {
+                Utils.close(stream);
+                sex.printStackTrace();
+                continue;
+            }
+        }
+        while (defaultLayers.remove(null)) {}
+        
+        Collection<String> defaults = Main.pref.getCollection("imagery.layers.default");
         ArrayList<String> defaultsSave = new ArrayList<String>();
-        for(String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES)))
-        {
-            try
-            {
-                if (clearCache) {
-                    MirroredInputStream.cleanup(source);
+        for (ImageryInfo def : defaultLayers) {
+            if (def.isDefaultEntry()) {
+                defaultsSave.add(def.getUrl());
+                
+                boolean isKnownDefault = false;
+                for (String url : defaults) {
+                    if (isSimilar(url, def.getUrl())) {
+                        isKnownDefault = true;
+                        break;
+                    }
                 }
-                MirroredInputStream s = new MirroredInputStream(source, -1);
-                try {
-                    InputStreamReader r;
-                    try
-                    {
-                        r = new InputStreamReader(s, "UTF-8");
-                    }
-                    catch (UnsupportedEncodingException e)
-                    {
-                        r = new InputStreamReader(s);
-                    }
-                    BufferedReader reader = new BufferedReader(r);
-                    String line;
-                    while((line = reader.readLine()) != null)
-                    {
-                        String val[] = line.split(";");
-                        if(!line.startsWith("#") && val.length >= 3) {
-                            boolean force = "true".equals(val[0]);
-                            String name = tr(val[1]);
-                            String url = val[2];
-                            String eulaAcceptanceRequired = null;
-
-                            if (val.length >= 4 && !val[3].isEmpty()) {
-                                // 4th parameter optional for license agreement (EULA)
-                                eulaAcceptanceRequired = val[3];
-                            }
-
-                            ImageryInfo info = new ImageryInfo(name, url, eulaAcceptanceRequired);
-
-                            if (val.length >= 5 && !val[4].isEmpty()) {
-                                // 5th parameter optional for bounds
-                                try {
-                                    info.setBounds(new Bounds(val[4], ","));
-                                } catch (IllegalArgumentException e) {
-                                    Main.warn(e.toString());
-                                }
-                            }
-                            if (val.length >= 6 && !val[5].isEmpty()) {
-                                info.setAttributionText(val[5]);
-                            }
-                            if (val.length >= 7 && !val[6].isEmpty()) {
-                                info.setAttributionLinkURL(val[6]);
-                            }
-                            if (val.length >= 8 && !val[7].isEmpty()) {
-                                info.setTermsOfUseURL(val[7]);
-                            }
-                            if (val.length >= 9 && !val[8].isEmpty()) {
-                                info.setAttributionImage(val[8]);
-                            }
-
-                            defaultLayers.add(info);
-
-                            if (force) {
-                                defaultsSave.add(url);
-                                if (!defaults.contains(url)) {
-                                    for (ImageryInfo i : layers) {
-                                        if ((i.getImageryType() == ImageryType.WMS && url.equals(i.getUrl()))
-                                                || url.equals(i.getFullUrl())) {
-                                            force = false;
-                                        }
-                                    }
-                                    if (force) {
-                                        add(new ImageryInfo(name, url));
-                                    }
-                                }
-                            }
+                boolean isInUserList = false;
+                if (!isKnownDefault) {
+                    for (ImageryInfo i : layers) {
+                        if (isSimilar(def.getUrl(), i.getUrl())) {
+                            isInUserList = true;
+                            break;
                         }
                     }
-                } finally {
-                    s.close();
                 }
-            }
-            catch (IOException e)
-            {
+                if (!isKnownDefault && !isInUserList) {
+                    add(new ImageryInfo(def));
+                }
             }
         }
@@ -155,4 +113,9 @@
         Main.pref.putCollection("imagery.layers.default", defaultsSave.size() > 0
                 ? defaultsSave : defaults);
+    }
+    
+    // some additional checks to respect extended URLs in preferences (legacy workaround)
+    private boolean isSimilar(String a, String b) {
+        return Utils.equal(a, b) || (a != null && b != null && !"".equals(a) && !"".equals(b) && (a.contains(b) || b.contains(a)));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(revision 4240)
@@ -108,5 +108,5 @@
     private int workingThreadCount;
     private boolean canceled;
-    private ArrayList<String> serverProjections = null;
+    private List<String> serverProjections = null;
 
     /** set to true if this layer uses an invalid base url */
@@ -121,4 +121,5 @@
     public WMSLayer(ImageryInfo info) {
         super(info);
+        serverProjections = info.getServerProjections();
         mv = Main.map.mapView;
         setBackgroundLayer(true); /* set global background variable */
@@ -143,5 +144,7 @@
 
         if(info.getUrl() != null) {
-            serverProjections = WMSGrabber.getServerProjections(info.getUrl(), true);
+            if (serverProjections == null) {
+                serverProjections = WMSGrabber.getServerProjections(info.getUrl(), true);
+            }
             startGrabberThreads();
             if(info.getImageryType() == ImageryType.WMS && !ImageryInfo.isUrlWithPatterns(info.getUrl())) {
@@ -310,5 +313,5 @@
      */
     public int getBaseImageWidth() {
-        int overlap = (PROP_OVERLAP.get()?PROP_OVERLAP_EAST.get() * imageSize / 100:0);
+        int overlap = PROP_OVERLAP.get() ? (PROP_OVERLAP_EAST.get() * imageSize / 100) : 0;
         return imageSize + overlap;
     }
@@ -319,5 +322,5 @@
      */
     public int getBaseImageHeight() {
-        int overlap = (PROP_OVERLAP.get()?PROP_OVERLAP_NORTH.get() * imageSize / 100:0);
+        int overlap = PROP_OVERLAP.get() ? (PROP_OVERLAP_NORTH.get() * imageSize / 100) : 0;
         return imageSize + overlap;
     }
@@ -696,5 +699,5 @@
                     oos.writeDouble(info.getPixelPerDegree());
                     oos.writeObject(info.getName());
-                    oos.writeObject(info.getFullUrl());
+                    oos.writeObject(info.getExtendedUrl());
                     oos.writeObject(images);
                     oos.close();
@@ -735,5 +738,5 @@
                 info.setPixelPerDegree(ois.readDouble());
                 doSetName((String)ois.readObject());
-                info.setUrl((String) ois.readObject());
+                info.setExtendedUrl((String) ois.readObject());
                 images = (GeorefImage[][])ois.readObject();
                 ois.close();
@@ -914,4 +917,15 @@
     }
 
+    /**
+     * Get the list of projections supported by the WMS server corresponding to this layer.
+     * @return The list of projections, if known. An empty list otherwise.
+     */
+    public List<String> getServerProjections() {
+        if (serverProjections == null)
+            return Collections.emptyList();
+        else
+            return Collections.unmodifiableList(serverProjections);
+    }
+    
     @Override
     public boolean isProjectionSupported(Projection proj) {
Index: trunk/src/org/openstreetmap/josm/gui/preferences/AddWMSLayerPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/AddWMSLayerPanel.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/AddWMSLayerPanel.java	(revision 4240)
@@ -323,4 +323,5 @@
         String incomingData;
         try {
+            System.out.println("GET "+getCapabilitiesUrl.toString());
             URLConnection openConnection = getCapabilitiesUrl.openConnection();
             InputStream inputStream = openConnection.getInputStream();
@@ -341,4 +342,5 @@
         Document document;
         try {
+            //System.out.println("WMS capabilities:\n"+incomingData+"\n");
             DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
             builderFactory.setValidating(false);
Index: trunk/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java	(revision 4240)
@@ -593,5 +593,5 @@
                     return info.getName();
                 case 1:
-                    return info.getFullUrl();
+                    return info.getExtendedUrl();
                 case 2:
                     return (info.getImageryType() == ImageryType.WMS || info.getImageryType() == ImageryType.HTML) ?
@@ -611,5 +611,5 @@
                     break;
                 case 1:
-                    info.setUrl((String)o);
+                    info.setExtendedUrl((String)o);
                     break;
                 case 2:
@@ -660,5 +660,5 @@
                     return info.getName();
                 case 1:
-                    return info.getFullUrl();
+                    return info.getExtendedUrl();
                 }
                 return null;
Index: trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java	(revision 4240)
@@ -29,7 +29,9 @@
     InputStream fs = null;
     File file = null;
+    
+    public final static long DEFAULT_MAXTIME = -1l;
 
     public MirroredInputStream(String name) throws IOException {
-        this(name, null, -1L);
+        this(name, null, DEFAULT_MAXTIME);
     }
 
@@ -39,5 +41,5 @@
 
     public MirroredInputStream(String name, String destDir) throws IOException {
-        this(name, destDir, -1L);
+        this(name, destDir, DEFAULT_MAXTIME);
     }
 
@@ -193,5 +195,7 @@
                 file = null;
             else {
-                if (maxTime <= 0) {
+                if ( maxTime == DEFAULT_MAXTIME 
+                        || maxTime <= 0 // arbitrary value <= 0 is deprecated
+                ) {
                     maxTime = Main.pref.getInteger("mirror.maxtime", 7*24*60*60);
                 }
Index: trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 4240)
+++ trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 4240)
@@ -0,0 +1,367 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io.imagery;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.Utils.equal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Stack;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.imagery.ImageryInfo;
+import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
+import org.openstreetmap.josm.io.MirroredInputStream;
+import org.openstreetmap.josm.io.UTFInputStreamReader;
+import org.openstreetmap.josm.tools.Utils;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class ImageryReader {
+
+    private String source;
+
+    private enum State { 
+        INIT,               // initial state, should always be at the bottom of the stack
+        IMAGERY,            // inside the imagery element
+        ENTRY,              // inside an entry
+        ENTRY_ATTRIBUTE,    // note we are inside an entry attribute to collect the character data
+        SUPPORTED_PROJECTIONS,
+        PR,
+        UNKNOWN,            // element is not recognized in the current context
+    }
+
+    public ImageryReader(String source) throws IOException {
+        this.source = source;
+    }
+
+    public List<ImageryInfo> parse() throws SAXException, IOException {
+        if (isXml(source)) {
+            Parser parser = new Parser();
+            try {
+                SAXParserFactory factory = SAXParserFactory.newInstance();
+                factory.setNamespaceAware(true);
+                InputStream in = new MirroredInputStream(source);
+                InputSource is = new InputSource(UTFInputStreamReader.create(in, "UTF-8"));
+                factory.newSAXParser().parse(is, parser);
+                return parser.entries;
+            } catch (SAXException e) {
+                throw e;
+            } catch (ParserConfigurationException e) {
+                e.printStackTrace(); // broken SAXException chaining
+                throw new SAXException(e);
+            }
+        } else {
+            return readCSV(source);
+        }
+    }
+
+    /**
+     * Probe the file to see if it is xml or the traditional csv format.
+     * 
+     * If the first non-whitespace character is a '<', decide for
+     * xml, otherwise csv.
+     */
+    private boolean isXml(String source) {
+        MirroredInputStream in = null;
+        try {
+            in = new MirroredInputStream(source);
+            InputStreamReader reader = UTFInputStreamReader.create(in, null);
+            WHILE: while (true) {
+                int c = reader.read();
+                switch (c) {
+                    case -1:
+                        break WHILE;
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                        continue;
+                    case '<':
+                        return true;
+                    default:
+                        return false;
+                }
+            }
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        } finally {
+            Utils.close(in);
+        }
+        System.err.println("Warning: Could not detect type of imagery source '"+source+"'. Using default (xml).");
+        return true;
+    }
+
+    private List<ImageryInfo> readCSV(String source) {
+        List<ImageryInfo> entries = new ArrayList<ImageryInfo>();
+        MirroredInputStream s = null;
+        try {
+            s = new MirroredInputStream(source);
+            try {
+                InputStreamReader r;
+                try
+                {
+                    r = new InputStreamReader(s, "UTF-8");
+                }
+                catch (UnsupportedEncodingException e)
+                {
+                    r = new InputStreamReader(s);
+                }
+                BufferedReader reader = new BufferedReader(r);
+                String line;
+                while((line = reader.readLine()) != null)
+                {
+                    String val[] = line.split(";");
+                    if(!line.startsWith("#") && val.length >= 3) {
+                        boolean defaultEntry = "true".equals(val[0]);
+                        String name = tr(val[1]);
+                        String url = val[2];
+                        String eulaAcceptanceRequired = null;
+
+                        if (val.length >= 4 && !val[3].isEmpty()) {
+                            // 4th parameter optional for license agreement (EULA)
+                            eulaAcceptanceRequired = val[3];
+                        }
+
+                        ImageryInfo info = new ImageryInfo(name, url, eulaAcceptanceRequired);
+                        
+                        info.setDefaultEntry(defaultEntry);
+
+                        if (val.length >= 5 && !val[4].isEmpty()) {
+                            // 5th parameter optional for bounds
+                            try {
+                                info.setBounds(new Bounds(val[4], ","));
+                            } catch (IllegalArgumentException e) {
+                                Main.warn(e.toString());
+                            }
+                        }
+                        if (val.length >= 6 && !val[5].isEmpty()) {
+                            info.setAttributionText(val[5]);
+                        }
+                        if (val.length >= 7 && !val[6].isEmpty()) {
+                            info.setAttributionLinkURL(val[6]);
+                        }
+                        if (val.length >= 8 && !val[7].isEmpty()) {
+                            info.setTermsOfUseURL(val[7]);
+                        }
+                        if (val.length >= 9 && !val[8].isEmpty()) {
+                            info.setAttributionImage(val[8]);
+                        }
+
+                        entries.add(info);
+                    }
+                }
+            } finally {
+                Utils.close(s);
+            }
+            return entries;
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        } finally {
+            Utils.close(s);
+        }
+        return entries;
+    }
+
+    private class Parser extends DefaultHandler {
+        private StringBuffer accumulator = new StringBuffer();
+
+        private Stack<State> states;
+
+        List<ImageryInfo> entries;
+
+        /**
+         * Skip the current entry because it has mandatory attributes
+         * that this version of JOSM cannot process.
+         */
+        boolean skipEntry;
+
+        ImageryInfo entry;
+        Bounds bounds;
+        List<String> supported_srs;
+
+        @Override public void startDocument() {
+            accumulator = new StringBuffer();
+            skipEntry = false;
+            states = new Stack<State>();
+            states.push(State.INIT);
+            entries = new ArrayList<ImageryInfo>();
+            entry = null;
+            bounds = null;
+            supported_srs = null;
+        }
+
+        @Override
+        public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+            accumulator.setLength(0);
+            State newState = null;
+            switch (states.peek()) {
+                case INIT:
+                    if (qName.equals("imagery")) {
+                        newState = State.IMAGERY;
+                    }
+                    break;
+                case IMAGERY:
+                    if (qName.equals("entry")) {
+                        entry = new ImageryInfo();
+                        skipEntry = false;
+                        newState = State.ENTRY;
+                    }
+                    break;
+                case ENTRY:
+                    if (Arrays.asList(new String[] {
+                        "name",
+                        "type",
+                        "default",
+                        "url",
+                        "eula",
+                        "min-zoom",
+                        "max-zoom",
+                        "attribution-text",
+                        "attribution-url",
+                        "logo-image",
+                        "logo-url",
+                        "terms-of-use-text",
+                        "terms-of-use-url",
+                    }).contains(qName)) {
+                        newState = State.ENTRY_ATTRIBUTE;
+                    } else if (qName.equals("bounds")) {
+                        try {
+                            bounds = new Bounds(
+                                    atts.getValue("min-lat") + "," +
+                                    atts.getValue("min-lon") + "," +
+                                    atts.getValue("max-lat") + "," +
+                                    atts.getValue("max-lon"), ",");
+                        } catch (IllegalArgumentException e) {
+                            break;
+                        }
+                        newState = State.ENTRY_ATTRIBUTE;
+                    } else if (qName.equals("supported-projections")) {
+                        supported_srs = new ArrayList<String>();
+                        newState = State.SUPPORTED_PROJECTIONS;
+                    }
+                    break;
+                case SUPPORTED_PROJECTIONS:
+                    if (qName.equals("pr")) {
+                        newState = State.PR;
+                    }
+                    break;
+            }
+            /**
+             * Did not recognize the element, so the new state is UNKOWN.
+             * This includes the case where we are already inside an unknown
+             * element, i.e. we do not try to understand the inner content
+             * of an unknown element, but wait till it's over.
+             */
+            if (newState == null) {
+                newState = State.UNKNOWN;
+            }
+            states.push(newState);
+            if (newState == State.UNKNOWN && equal(atts.getValue("mandatory"), "true")) {
+                skipEntry = true;
+            }
+            return;
+        }
+
+        @Override
+        public void characters(char[] ch, int start, int length) {
+            accumulator.append(ch, start, length);
+        }
+
+        @Override
+        public void endElement(String namespaceURI, String qName, String rqName) {
+            switch (states.pop()) {
+                case INIT:
+                    throw new RuntimeException("parsing error: more closing than opening elements");
+                case ENTRY:
+                    if (qName.equals("entry")) {
+                        if (!skipEntry) {
+                            entries.add(entry);
+                        }
+                        entry = null;
+                    }
+                    break;
+                case ENTRY_ATTRIBUTE:
+                    if (qName.equals("name")) {
+                        entry.setName(accumulator.toString());
+                    } else if (qName.equals("type")) {
+                        boolean found = false;
+                        for (ImageryType type : ImageryType.values()) {
+                            if (equal(accumulator.toString(), type.getUrlString())) {
+                                entry.setImageryType(type);
+                                found = true;
+                                break;
+                            }
+                        }
+                        if (!found) {
+                            skipEntry = true;
+                        }
+                    } else if (qName.equals("default")) {
+                        if (accumulator.toString().equals("true")) {
+                            entry.setDefaultEntry(true);
+                        } else if (accumulator.toString().equals("false")) {
+                            entry.setDefaultEntry(false);
+                        } else {
+                            skipEntry = true;
+                        }
+                    } else if (qName.equals("url")) {
+                        entry.setUrl(accumulator.toString());
+                    } else if (qName.equals("eula")) {
+                        entry.setEulaAcceptanceRequired(accumulator.toString());
+                    } else if (qName.equals("min-zoom") || qName.equals("max-zoom")) {
+                        Integer val = null;
+                        try {
+                            val = Integer.parseInt(accumulator.toString());
+                        } catch(NumberFormatException e) {
+                            val = null;
+                        }
+                        if (val == null) {
+                            skipEntry = true;
+                        } else {
+                            if (qName.equals("min-zoom")) {
+                                entry.setDefaultMinZoom(val);
+                            } else {
+                                entry.setDefaultMaxZoom(val);
+                                entry.setMaxZoom(val);
+                            }
+                        }
+                    } else if (qName.equals("bounds")) {
+                        entry.setBounds(bounds);
+                        bounds = null;
+                    } else if (qName.equals("attribution-text")) {
+                        entry.setAttributionText(accumulator.toString());
+                    } else if (qName.equals("attribution-url")) {
+                        entry.setAttributionLinkURL(accumulator.toString());
+                    } else if (qName.equals("logo-image")) {
+                        entry.setAttributionImage(accumulator.toString());
+                    } else if (qName.equals("logo-url")) {
+                        // TODO: it should be possible to specify the link for the logo
+                    } else if (qName.equals("terms-of-use-text")) {
+                        // TODO: it should be possible to configure the terms of use display text
+                    } else if (qName.equals("terms-of-use-url")) {
+                        entry.setTermsOfUseURL(accumulator.toString());
+                    }
+                    break;
+                case PR:
+                    supported_srs.add(accumulator.toString());
+                    break;
+                case SUPPORTED_PROJECTIONS:
+                    entry.setServerProjections(supported_srs);
+                    supported_srs = null;
+                    break;
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java	(revision 4240)
@@ -26,5 +26,5 @@
     public boolean isLayerSupported(ImageryInfo info) {
         try {
-            URL url = new URL(this.url + "action=CheckAvailability&id=" + URLEncoder.encode(info.getFullUrl(), "UTF-8"));
+            URL url = new URL(this.url + "action=CheckAvailability&id=" + URLEncoder.encode(info.getUrl(), "UTF-8"));
             System.out.println(tr("Querying offset availability: {0}", url));
             final BufferedReader rdr = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), "UTF-8"));
@@ -42,5 +42,5 @@
         LatLon ll = Main.getProjection().eastNorth2latlon(en);
         try {
-            URL url = new URL(this.url + "action=GetOffsetForPoint&lat=" + ll.lat() + "&lon=" + ll.lon() + "&id=" + URLEncoder.encode(info.getFullUrl(), "UTF-8"));
+            URL url = new URL(this.url + "action=GetOffsetForPoint&lat=" + ll.lat() + "&lon=" + ll.lon() + "&id=" + URLEncoder.encode(info.getUrl(), "UTF-8"));
             System.out.println(tr("Querying offset: {0}", url.toString()));
             final BufferedReader rdr = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), "UTF-8"));
Index: trunk/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java	(revision 4238)
+++ trunk/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java	(revision 4240)
@@ -19,4 +19,5 @@
 import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map.Entry;
 import java.util.Locale;
@@ -47,4 +48,5 @@
     protected String baseURL;
     private final boolean urlWithPatterns;
+    private List<String> serverProjections;
     private Map<String, String> props = new HashMap<String, String>();
 
@@ -52,4 +54,5 @@
         super(mv, layer);
         this.baseURL = layer.getInfo().getUrl();
+        this.serverProjections = layer.getServerProjections();
         /* URL containing placeholders? */
         urlWithPatterns = ImageryInfo.isUrlWithPatterns(baseURL);
@@ -139,6 +142,6 @@
         } else {
             str += "bbox=" + bbox
-            + srs
-            + "&width=" + wi + "&height=" + ht;
+                    + srs
+                    + "&width=" + wi + "&height=" + ht;
             if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
                 System.out.println(tr("Warning: The base URL ''{0}'' for a WMS service doesn't have a trailing '&' or a trailing '?'.", baseURL));
