Index: src/org/openstreetmap/josm/gui/layer/WMSLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(revision 4195)
+++ src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(working copy)
@@ -695,7 +695,7 @@
                     oos.writeInt(imageSize);
                     oos.writeDouble(info.getPixelPerDegree());
                     oos.writeObject(info.getName());
-                    oos.writeObject(info.getFullUrl());
+                    oos.writeObject(info.getExtendedUrl());
                     oos.writeObject(images);
                     oos.close();
                 }
@@ -734,7 +734,7 @@
                 imageSize = ois.readInt();
                 info.setPixelPerDegree(ois.readDouble());
                 doSetName((String)ois.readObject());
-                info.setUrl((String) ois.readObject());
+                info.setExtendedUrl((String) ois.readObject());
                 images = (GeorefImage[][])ois.readObject();
                 ois.close();
                 fis.close();
Index: src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java
===================================================================
--- src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java	(revision 4195)
+++ src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java	(working copy)
@@ -591,7 +591,7 @@
                 case 0:
                     return info.getName();
                 case 1:
-                    return info.getFullUrl();
+                    return info.getExtendedUrl();
                 case 2:
                     return (info.getImageryType() == ImageryType.WMS || info.getImageryType() == ImageryType.HTML) ?
                             (info.getPixelPerDegree() == 0.0 ? "" : info.getPixelPerDegree()) :
@@ -609,7 +609,7 @@
                     info.setName((String) o);
                     break;
                 case 1:
-                    info.setUrl((String)o);
+                    info.setExtendedUrl((String)o);
                     break;
                 case 2:
                     info.setPixelPerDegree(0);
@@ -658,7 +658,7 @@
                 case 0:
                     return info.getName();
                 case 1:
-                    return info.getFullUrl();
+                    return info.getExtendedUrl();
                 }
                 return null;
             }
Index: src/org/openstreetmap/josm/io/imagery/ImageryReader.java
===================================================================
--- src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 0)
+++ src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 0)
@@ -0,0 +1,249 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io.imagery;
+
+import static org.openstreetmap.josm.tools.Utils.equal;
+
+import java.io.IOException;
+import java.io.InputStream;
+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.data.Bounds;
+import org.openstreetmap.josm.data.imagery.ImageryInfo;
+import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
+import org.openstreetmap.josm.io.UTFInputStreamReader;
+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 InputSource inputSource;
+
+    private final static boolean debug = true;
+
+    private enum State { INIT, IMAGERY, ENTRY, ENTRY_ATTRIBUTE }
+
+    public ImageryReader(InputStream source) throws IOException {
+        this.inputSource = new InputSource(UTFInputStreamReader.create(source, "UTF-8"));
+    }
+
+    public List<ImageryInfo> parse() throws SAXException, IOException {
+        Parser parser = new Parser();
+        try {
+            SAXParserFactory factory = SAXParserFactory.newInstance();
+            factory.setNamespaceAware(true);
+            factory.newSAXParser().parse(inputSource, parser);
+            return parser.entries;
+        } catch (SAXException e) {
+            throw e;
+        } catch (ParserConfigurationException e) {
+            e.printStackTrace(); // broken SAXException chaining
+            throw new SAXException(e);
+        }
+    }
+
+
+    private class Parser extends DefaultHandler {
+        private StringBuffer accumulator = new StringBuffer();
+
+        private Stack<State> states;
+
+        List<ImageryInfo> entries;
+
+        /**
+         * When entering an unknown element, don't try to recognize
+         * the inner content. Just keep track of the nesting depth and
+         * go on when the unknown element is finished.
+         */
+        int unknownLevel;
+        /**
+         * Skip the current entry because it has mandatory attributes
+         * that this version of JOSM cannot process.
+         */
+        boolean skipEntry;
+
+        ImageryInfo entry;
+        Bounds bounds;
+
+        @Override public void startDocument() {
+            accumulator = new StringBuffer();
+            unknownLevel = 0;
+            skipEntry = false;
+            states = new Stack<State>();
+            states.push(State.INIT);
+            entries = new ArrayList<ImageryInfo>();
+            entry = null;
+            bounds = null;
+        }
+
+        @Override
+        public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+            if (debug) System.err.println("<"+qName+">"+(unknownLevel > 0 ? "["+unknownLevel+"]" : ""));
+
+            accumulator.setLength(0);
+            State newState = null;
+            known:{
+                if (unknownLevel > 0) {
+                    break known;
+                } else {
+                    switch (states.peek()) {
+                        case INIT:
+                            if (qName.equals("imagery")) {
+                                newState = State.IMAGERY;
+                            } else {
+                                break known;
+                            }
+                            break;
+                        case IMAGERY:
+                            if (qName.equals("entry")) {
+                                entry = new ImageryInfo();
+                                skipEntry = false;
+                                newState = State.ENTRY;
+                            } else {
+                                break known;
+                            }
+                            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 known;
+                                }
+                                newState = State.ENTRY_ATTRIBUTE;
+                            } else {
+                                break known;
+                            }
+                            break;
+                        default:
+                            break known;
+                    }
+                }
+                if (newState == null) throw new AssertionError();
+                states.push(newState);
+                return;
+            }
+            unknownLevel++;
+            if (equal(atts.getValue("mandatory"), "true")) {
+                skipEntry = true;
+            }
+        }
+
+        @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) {
+            if (debug) System.err.println("</"+qName+">"+(unknownLevel > 0 ? "["+unknownLevel+"]" : ""));
+
+            if (unknownLevel > 0) {
+                unknownLevel--;
+            } else {
+                switch (states.pop()) {
+                case INIT:
+                    throw new RuntimeException();
+                case IMAGERY:
+                    break;
+                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")) {
+                        // FIXME
+                    } else if (qName.equals("terms-of-use-text")) {
+                        // FIXME
+                    } else if (qName.equals("terms-of-use-url")) {
+                        entry.setTermsOfUseURL(accumulator.toString());
+                    }
+                    break;
+                }
+            }
+        }
+    }
+}
Index: src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java
===================================================================
--- src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java	(revision 4195)
+++ src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java	(working copy)
@@ -25,7 +25,7 @@
     @Override
     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.getExtendedUrl(), "UTF-8"));
             System.out.println(tr("Querying offset availability: {0}", url));
             final BufferedReader rdr = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), "UTF-8"));
             String response = rdr.readLine();
@@ -41,7 +41,7 @@
     public EastNorth getOffset(ImageryInfo info, EastNorth en) {
         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.getExtendedUrl(), "UTF-8"));
             System.out.println(tr("Querying offset: {0}", url.toString()));
             final BufferedReader rdr = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), "UTF-8"));
             String s = rdr.readLine();
Index: src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
===================================================================
--- src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 4195)
+++ src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(working copy)
@@ -17,7 +17,10 @@
 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 {
 
@@ -26,7 +29,7 @@
     static ArrayList<ImageryInfo> defaultLayers = new ArrayList<ImageryInfo>();
 
     private final static String[] DEFAULT_LAYER_SITES = {
-        "http://josm.openstreetmap.de/maps"
+        "resource://data/imagery.xml" // TODO : create wiki site
     };
 
     private ImageryLayerInfo() {
@@ -50,8 +53,28 @@
         Collection<String> defaults = Main.pref.getCollection(
                 "imagery.layers.default", Collections.<String>emptySet());
         ArrayList<String> defaultsSave = new ArrayList<String>();
-        for(String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES)))
-        {
+        for (String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES))) {
+            if (clearCache) {
+                MirroredInputStream.cleanup(source);
+            }
+            MirroredInputStream stream = null;
+            try {
+                stream = new MirroredInputStream(source, -1);
+                ImageryReader rd = new ImageryReader(stream);
+                Collection<ImageryInfo> result = rd.parse();
+                defaultLayers.addAll(result);
+            } catch (IOException ex) {
+                Utils.close(stream);
+                ex.printStackTrace();
+                continue;
+            } catch (SAXException sex) {
+                Utils.close(stream);
+                sex.printStackTrace();
+                continue;
+            }
+            
+            if (true) continue;
+            
             try
             {
                 if (clearCache) {
@@ -114,7 +137,7 @@
                                 if (!defaults.contains(url)) {
                                     for (ImageryInfo i : layers) {
                                         if ((i.getImageryType() == ImageryType.WMS && url.equals(i.getUrl()))
-                                                || url.equals(i.getFullUrl())) {
+                                                || url.equals(i.getExtendedUrl())) {
                                             force = false;
                                         }
                                     }
Index: src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
===================================================================
--- src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 4195)
+++ src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(working copy)
@@ -41,6 +41,7 @@
 
     private String name;
     private String url = null;
+    private boolean defaultEntry = false;
     private String cookies = null;
     private String eulaAcceptanceRequired= null;
     private ImageryType imageryType = ImageryType.WMS;
@@ -54,31 +55,34 @@
     private String attributionLinkURL;
     private String termsOfUseURL;
 
+    public ImageryInfo() {
+    }
+
     public ImageryInfo(String name) {
         this.name=name;
     }
 
     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;
     }
 
     public ImageryInfo(String name, String url, String eulaAcceptanceRequired, String cookies) {
         this.name=name;
-        setUrl(url);
+        setExtendedUrl(url);
         this.cookies=cookies;
         this.eulaAcceptanceRequired = eulaAcceptanceRequired;
     }
 
     public ImageryInfo(String name, String url, String cookies, double pixelPerDegree) {
         this.name=name;
-        setUrl(url);
+        setExtendedUrl(url);
         this.cookies=cookies;
         this.pixelPerDegree=pixelPerDegree;
     }
@@ -86,7 +90,7 @@
     public ArrayList<String> getInfoArray() {
         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) {
             res.add(pixelPerDegree != 0.0 ? String.valueOf(pixelPerDegree) : null);
@@ -105,7 +109,7 @@
         ArrayList<String> array = new ArrayList<String>(list);
         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()) {
             this.cookies=array.get(2);
@@ -177,6 +181,14 @@
         this.pixelPerDegree = ppd;
     }
 
+    public void setDefaultMaxZoom(int defaultMaxZoom) {
+        this.defaultMaxZoom = defaultMaxZoom;
+    }
+
+    public void setDefaultMinZoom(int defaultMinZoom) {
+        this.defaultMinZoom = defaultMinZoom;
+    }
+    
     public void setMaxZoom(int maxZoom) {
         this.maxZoom = maxZoom;
     }
@@ -201,7 +213,7 @@
         termsOfUseURL = text;
     }
 
-    public void setUrl(String url) {
+    public void setExtendedUrl(String url) {
         CheckParameterUtil.ensureParameterNotNull(url);
         
         defaultMaxZoom = 0;
@@ -239,6 +251,18 @@
         return this.url;
     }
 
+    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;
     }
@@ -259,7 +283,11 @@
         return eulaAcceptanceRequired;
     }
 
-    public String getFullUrl() {
+    public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
+        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
+    }
+
+    public String getExtendedUrl() {
         return imageryType.getUrlString() + (defaultMaxZoom != 0
             ? "["+(defaultMinZoom != 0 ? defaultMinZoom+",":"")+defaultMaxZoom+"]" : "") + ":" + url;
     }
@@ -315,6 +343,10 @@
         return imageryType;
     }
 
+    public void setImageryType(ImageryType imageryType) {
+        this.imageryType = imageryType;
+    }
+
     public static boolean isUrlWithPatterns(String url) {
         return url != null && url.contains("{") && url.contains("}");
     }
Index: data/imagery.xml
===================================================================
--- data/imagery.xml	(revision 0)
+++ data/imagery.xml	(revision 0)
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<imagery>
+    <!--"true;OpenStreetMap (Mapnik);tms[18]:http://tile.openstreetmap.org/;;;osm;osm;osm"-->
+    <entry>
+        <name>Bing</name>
+        <type>bing</type>
+        <default>true</default>
+    </entry>
+    <entry>
+        <name>Landsat</name>
+        <type>wms</type>
+        <default>true</default>
+        <url><![CDATA[http://irs.gis-lab.info/?layers=landsat&]]></url>
+    </entry>
+    <entry>
+        <name>Yahoo Sat</name>
+        <type>html</type>
+        <url><![CDATA[http://josm.openstreetmap.de/wmsplugin/YahooDirect.html?]]></url>
+    </entry>
+    <entry>
+        <name>MapQuest Open Aerial</name>
+        <type>tms</type>
+        <default>true</default>
+        <url><![CDATA[http://oatile1.mqcdn.com/naip/{zoom}/{x}/{y}.png]]></url>
+    </entry>
+    <entry>
+    <!--"false;SPOTMaps (France);http://spotmaps.youmapps.org/cgi-bin/mapserv?map=/home/ortho/ortho.map&service=wms&version=1.1.1&srs=EPSG:4326&request=GetMap&layers=spotmaps4osm&format=image/jpeg&FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=demo&;http://www.youmapps.org/licenses/EULA-OSM-J-{lang}.html"-->
+        <name>SPOTMaps (France)</name>
+        <type>wms</type>
+        <url><![CDATA[http://spotmaps.youmapps.org/cgi-bin/mapserv?map=/home/ortho/ortho.map&service=wms&version=1.1.1&srs=EPSG:4326&request=GetMap&layers=spotmaps4osm&format=image/jpeg&FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=demo&]]></url>
+        <eula mandatory='true'><![CDATA[http://www.youmapps.org/licenses/EULA-OSM-J-{lang}.html]]></eula>
+    </entry>
+    <entry>
+    <!--"false;OpenPT Map;tms[5,16]:http://openptmap.de/tiles;;45.7,5.9,55.0,17.3;osm;osm;osm"-->
+        <name>OpenPT Map</name>
+        <type>tms</type>
+        <url><![CDATA[http://openptmap.de/tiles]]></url>
+        <min-zoom>5</min-zoom>
+        <max-zoom>16</max-zoom>
+        <bounds min-lat='45.7' min-lon='5.9' max-lat='55.0' max-lon='17.3'/>
+        <attribution-text mandatory='true'>© OpenStreetMap contributors, CC-BY-SA</attribution-text>
+        <attribution-url>http://openstreetmap.org/</attribution-url>
+    </entry>
+    <entry>
+    <!--"false;ÖPNV;tms[18]:http://tile.xn-  -pnvkarte-m4a.de/tilegen;;;osm;osm;osm"-->
+        <name>ÖPNV</name>
+        <type>tms</type>
+        <url><![CDATA[http://tile.xn--pnvkarte-m4a.de/tilegen]]></url>
+        <max-zoom>18</max-zoom>
+
+        <attribution-text mandatory='true'>© OpenStreetMap contributors, CC-BY-SA</attribution-text>
+        <attribution-url>http://openstreetmap.org/</attribution-url>
+        <terms-of-use-text>Background terms of use</terms-of-use-text>
+        <terms-of-use-url>http://www.openstreetmap.org/copyright</terms-of-use-url>
+        <!-- just for test - we don't really want a logo in this case -->
+        <logo-image>http://wiki.openstreetmap.org/w/images/c/c8/Public-images-osm_logo.png</logo-image>
+        <logo-url>http://wiki.openstreetmap.org/</logo-url>
+    </entry>
+</imagery>
