diff --git a/src/mapfile.py b/src/mapfile.py
deleted file mode 100644
index 2166ad24a7a61d4bcae135e081b769b0cf6d5eed..0000000000000000000000000000000000000000
--- a/src/mapfile.py
+++ /dev/null
@@ -1,1071 +0,0 @@
-#!/usr/bin/env python2.7
-# -*- coding: utf-8 -*-
-
-# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-#                                                                       #
-#   MapServer REST API is a python wrapper around MapServer which       #
-#   allows to manipulate a mapfile in a RESTFul way. It has been        #
-#   developped to match as close as possible the way the GeoServer      #
-#   REST API acts.                                                      #
-#                                                                       #
-#   Copyright (C) 2011-2013 Neogeo Technologies.                        #
-#                                                                       #
-#   This file is part of MapServer Rest API.                            #
-#                                                                       #
-#   MapServer Rest API is free software: you can redistribute it        #
-#   and/or modify it under the terms of the GNU General Public License  #
-#   as published by the Free Software Foundation, either version 3 of   #
-#   the License, or (at your option) any later version.                 #
-#                                                                       #
-#   MapServer Rest API is distributed in the hope that it will be       #
-#   useful, but WITHOUT ANY WARRANTY; without even the implied warranty #
-#   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the     #
-#   GNU General Public License for more details.                        #
-#                                                                       #
-# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-
-import os
-import re
-import mapscript
-import urlparse
-import stores
-import metadata
-import webapp
-
-import functools
-
-from webapp import KeyExists
-
-import tools
-from extensions import plugins
-
-class MetadataMixin(object):
-
-    def __getattr__(self, attr):
-        if hasattr(self, "ms") and hasattr(metadata, attr):
-            return functools.partial(getattr(metadata, attr), self.ms)
-        raise AttributeError("'%s' object has no attribute '%s'" %
-                             (type(self).__name__, attr))
-
-
-def get_store_connection_string(info):
-    cparam = info["connectionParameters"]
-    if cparam.get("dbtype", "") == "postgis":
-        # First mandatory
-        url = "PG:dbname=%s port=%s host=%s " % (cparam["database"], cparam["port"], cparam["host"])
-        # Then optionals:
-        url += " ".join("%s=%s" % (p, cparam[p]) for p in ["user", "password"] if p in cparam)
-        return url
-    elif "url" in cparam:
-        url = urlparse.urlparse(cparam["url"])
-        if url.scheme != "file" or url.netloc:
-            raise ValueError("Only local files are suported.")
-        return tools.get_resource_path(url.path)
-    else:
-        raise ValueError("Unhandled type '%s'" % cparam.get("dbtype", "<unknown>"))
-
-
-class Class(object):
-    """
-    """
-
-    def __init__(self, backend):
-        self.ms = backend
-
-
-class Layer(MetadataMixin):
-    """
-    """
-
-    def __init__(self, backend):
-        self.ms = backend
-
-    def enable(self, enabled=True):
-        requests = ["GetCapabilities", "GetMap", "GetFeatureInfo", "GetLegendGraphic"]
-        self.ms.status = mapscript.MS_ON if enabled else mapscript.MS_OFF
-        self.set_metadata("wms_enable_request", " ".join(('%s' if enabled else "!%s") % c for c in requests))
-
-    def get_type_name(self):
-        return {
-            0: "POINT",
-            1: "LINESTRING",
-            2: "POLYGON",
-            3: "RASTER",
-            4: "ANNOTATION",
-            }[self.ms.type]
-
-    def get_proj4(self):
-        return self.ms.getProjection()
-
-    def get_extent(self):
-        extent = self.ms.getExtent()
-        return stores.Extent(extent.minx, extent.miny, extent.maxx, extent.maxy)
-
-    def get_latlon_extent(self):
-        rect = mapscript.rectObj(*self.get_extent())
-        res = rect.project(mapscript.projectionObj(self.get_proj4()),
-                           mapscript.projectionObj('+init=epsg:4326'))
-        return stores.Extent(rect.minx, rect.miny, rect.maxx, rect.maxy)
-
-    def get_fields(self):
-        fields = self.get_metadata("gml_include_items", "")
-
-        if fields == "all":
-            # TODO: Get fields from feature type
-            raise NotImplemented()
-        elif not fields:
-            return []
-        else:
-            fields = fields.split(",")
-        return fields
-
-    def iter_fields(self):
-        return iter(self.get_fields())
-
-    def iter_classes(self):
-        for i in xrange(self.ms.numclasses):
-            yield Class(self.ms.getClass(i))
-
-    def get_styles(self):
-        return set(clazz.ms.group for clazz in self.iter_classes())
-
-    def iter_styles(self):
-        return iter(self.get_styles())
-
-    def get_SLD(self):
-        return self.ms.generateSLD().decode("LATIN1").encode("UTF8")
-
-    def add_style_sld(self, mf, s_name, new_sld):
-
-        # Because we do not want to apply the sld to real layers by mistake
-        # we need to rename it to something we are sure is not used.
-        sld_layer_name = "__mra_tmp_template"
-
-        # Most xml parsers will have trouble with the kind of mess we get as sld.
-        # Mostly because we haven't got the proper declarations, we fallback to
-        # an html parser, which luckily is much more forgiving.
-        from xml.dom.minidom import parseString
-        xmlsld = parseString(new_sld)
-
-        try:
-            xmlsld.firstChild.getElementsByTagName("NamedLayer")[0]\
-                .getElementsByTagName("Name")[0].firstChild.data = sld_layer_name
-        except:
-            raise ValueError("Bad sld (No NamedLayer/Name)")
-
-        # Remove encoding ?
-        # @wapiflapi Mapscript ne gère pas les espaces...
-        new_sld = xmlsld.toxml()
-
-        ms_template_layer = self.ms.clone()
-        ms_template_layer.name = sld_layer_name
-        mf.ms.insertLayer(ms_template_layer)
-
-        try:
-            ms_template_layer.applySLD(new_sld, sld_layer_name)
-        except:
-            raise ValueError("Unable to access storage.")
-
-        for i in xrange(ms_template_layer.numclasses):
-            ms_class = ms_template_layer.getClass(i)
-            ms_class.group = s_name
-            self.ms.insertClass(ms_class)
-
-        mf.ms.removeLayer(ms_template_layer.index)
-
-
-    def set_default_style(self, mf):
-
-        if self.ms.type == mapscript.MS_LAYER_POINT:
-            self.ms.tolerance = 8
-            self.ms.toleranceunits = 6
-            s_name = 'default_point'
-        elif self.ms.type == mapscript.MS_LAYER_LINE:
-            self.ms.tolerance = 8
-            self.ms.toleranceunits = 6
-            s_name = 'default_line'
-        elif self.ms.type == mapscript.MS_LAYER_POLYGON:
-            self.ms.tolerance = 0
-            self.ms.toleranceunits = 6
-            s_name = 'default_polygon'
-        else:
-            return
-
-        try:
-            style = open(os.path.join(os.path.dirname(__file__), "%s.sld" % s_name)).read()
-        except IOError, OSError:
-            return
-
-        self.add_style_sld(mf, s_name, style)
-        self.ms.classgroup = s_name
-
-    def remove_style(self, s_name):
-        for c_index in reversed(xrange(self.ms.numclasses)):
-            c = self.ms.getClass(c_index)
-            if c.group == s_name:
-                self.ms.removeClass(c_index)
-                break
-        else:
-            raise KeyError(s_name)
-
-    def update(self, metadata):
-        self.update_metadatas(metadata)
-
-
-class LayerGroup(object):
-    """
-    """
-
-    # TODO: We need to handle the order of the layers in a group.
-
-    def __init__(self, name, mapfile):
-        """
-        """
-
-        self.name = name
-        self.mapfile = mapfile
-
-    def iter_layers(self):
-        return self.mapfile.iter_layers(meta={"wms_group_name":self.name})
-
-    def get_layers(self):
-        return list(self.iter_layers())
-
-    def add_layer(self, layer):
-        layer.ms.group = self.name
-        layer.set_metadata("wms_group_name", self.name)
-        for k, v in self.mapfile.get_mra_metadata("layergroups")[self.name]:
-            layer.set_metadata("wms_group_%s" % k, v)
-        self.mapfile.move_layer_down(layer.ms.name)
-
-    def add(self, *args):
-        for layer in args:
-            if isinstance(layer, basestring):
-                layer = self.mapfile.get_layer(layer)
-            self.add_layer(layer)
-
-    def remove_layer(self, layer):
-        layer.ms.group = None
-        for mkey in layer.get_metadata_keys():
-            # (We really do not want to use iter_metadata_keys())
-            if mkey.startswith("wms_group_"):
-                layer.del_metadata(mkey)
-
-    def remove(self, *args):
-        for layer in args:
-            if isinstance(layer, basestring):
-                layer = mapfile.get_layer(layer)
-            self.remove_layer(layer)
-
-    def clear(self):
-        # Remove all the layers from this group.
-        for layer in self.mapfile.iter_layers(attr={"group": self.name}):
-            self.remove_layer(layer)
-
-    def get_latlon_extent(self):
-        layers = self.get_layers()
-        if not layers:
-            return stores.Extent(0, 0, 0, 0)
-
-        extent = layers[0].get_latlon_extent()
-        for layer in layers[1:]:
-            e = layer.get_latlon_extent()
-            extent.addX(e.minX(), e.maxX())
-            extent.addY(e.minY(), e.maxY())
-
-        return extent
-
-class LayerModel(MetadataMixin):
-    """
-    """
-
-    def __init__(self, backend):
-        self.ms = backend
-        self.name = self.get_mra_metadata("name", None)
-
-    def get_extent(self):
-        extent = self.ms.getExtent()
-        return stores.Extent(extent.minx, extent.miny, extent.maxx, extent.maxy)
-
-    def get_latlon_extent(self):
-        rect = mapscript.rectObj(*self.get_extent())
-        res = rect.project(mapscript.projectionObj(self.ms.getProjection()),
-                           mapscript.projectionObj('+init=epsg:4326'))
-        return stores.Extent(rect.minx, rect.miny, rect.maxx, rect.maxy)
-
-    def get_proj4(self):
-        return self.ms.getProjection()
-
-    def get_wkt(self):
-        return tools.proj4_to_wkt(self.ms.getProjection())
-
-    def get_authority(self):
-        return tools.wkt_to_authority(self.get_wkt())
-
-    def get_authority_name(self):
-        return self.get_authority()[0]
-
-    def get_authority_code(self):
-        return self.get_authority()[1]
-
-
-class FeatureTypeModel(LayerModel):
-    """
-    """
-
-    def update(self, ws, ft_name, ds_name, metadata):
-
-        ds = ws.get_datastore(ds_name)
-        ft = ds[ft_name]
-        self.name = ft_name
-
-        # Set basic attributes.
-        self.ms.name = "ft:%s:%s:%s" % (ws.name, ds_name, ft_name)
-        self.ms.status = mapscript.MS_OFF
-        self.ms.type = ft.get_geomtype_mapscript()
-        self.ms.setProjection(ft.get_proj4())
-        self.ms.setExtent(*ft.get_extent())
-
-        # Configure the connection to the store.
-        # This is a little hacky as we have to translate stuff...
-        info = ws.get_datastore_info(ds_name)
-        cparam = info["connectionParameters"]
-        if cparam.get("dbtype", None) in ["postgis", "postgres", "postgresql"]:
-            self.ms.connectiontype = mapscript.MS_POSTGIS
-            connection = "dbname=%s port=%s host=%s " % (cparam["database"], cparam["port"], cparam["host"])
-            connection += " ".join("%s=%s" % (p, cparam[p]) for p in ["user", "password"] if p in cparam)
-            self.ms.connection = connection
-            self.ms.data = "%s FROM %s" % (ds[ft_name].get_geometry_column(), ft_name)
-            self.set_metadata("ows_extent", "%s %s %s %s" %
-                (ft.get_extent().minX(), ft.get_extent().minY(),
-                ft.get_extent().maxX(), ft.get_extent().maxY()))
-        #elif cpram["dbtype"] in ["shp", "shapefile"]:
-        else:
-            self.ms.connectiontype = mapscript.MS_SHAPEFILE
-            url = urlparse.urlparse(cparam["url"])
-            self.ms.data = tools.get_resource_path(url.path)
-
-            # TODO: strip extention.
-        #else:
-        #    raise ValueError("Unhandled type '%s'" % info["dbtype"])
-
-        # Deactivate wms and wfs requests, because we are a virtual layer.
-        self.set_metadatas({
-            "wms_enable_request": "!GetCapabilities !GetMap !GetFeatureInfo !GetLegendGraphic",
-            "wfs_enable_request": "!GetCapabilities !DescribeFeatureType !GetFeature",
-            })
-
-        # Update mra metadatas, and make sure the mandatory ones are left untouched.
-        self.update_mra_metadatas(metadata)
-        self.update_mra_metadatas({"name": ft_name, "type": "featuretype", "storage": ds_name,
-                                   "workspace": ws.name, "is_model": True})
-
-    def configure_layer(self, ws, layer, enabled=True):
-
-        plugins.extend("pre_configure_vector_layer", self, ws, layer)
-
-        # We must also update all our personal attributes (type, ...)
-        # because we might not have been cloned.
-
-        layer.ms.type = self.ms.type
-        layer.ms.setProjection(self.ms.getProjection())
-        layer.ms.setExtent(self.ms.extent.minx, self.ms.extent.miny,
-                           self.ms.extent.maxx, self.ms.extent.maxy)
-        layer.ms.data = self.ms.data
-        layer.ms.connectiontype = self.ms.connectiontype
-        layer.ms.connection = self.ms.connection
-
-        layer.update_mra_metadatas({
-                "name": self.get_mra_metadata("name"),
-                "type": self.get_mra_metadata("type"),
-                "storage": self.get_mra_metadata("storage"),
-                "workspace": self.get_mra_metadata("workspace"),
-                "is_model": False,
-                })
-
-        layer.update_metadatas({
-                "wfs_name": layer.get_metadata("wms_name"),
-                "wfs_title": layer.get_metadata("wms_title"),
-                "wfs_abstract": layer.get_metadata("wms_abstract"),
-                })
-
-        if enabled:
-            layer.set_metadata("wfs_enable_request",
-                               "GetCapabilities GetFeature DescribeFeatureType")
-
-        # Configure the layer based on information from the store.
-        ds = ws.get_datastore(self.get_mra_metadata("storage"))
-        ft = ds[self.get_mra_metadata("name")]
-
-        # Configure the different fields.
-        field_names = []
-        for field in ft.iterfields():
-            layer.set_metadatas({
-                "gml_%s_alias" % field.get_name(): field.get_name(),
-                "gml_%s_type" % field.get_name(): field.get_type_gml(),
-                # TODO: Add gml_<field name>_precision, gml_<field name>_width
-                })
-            field_names.append(field.get_name())
-
-        geometry_column = ft.get_geometry_column()
-        if geometry_column == None:
-            geometry_column = "geometry"
-        layer.set_metadatas({
-            "ows_include_items": ",".join(field_names),
-            "gml_include_items": ",".join(field_names),
-            "gml_geometries": geometry_column,
-            "gml_%s_type" % geometry_column: ft.get_geomtype_gml(),
-            # TODO: Add gml_<geometry name>_occurances,
-            "wfs_srs": "EPSG:4326",
-            "wfs_getfeature_formatlist": "OGRGML,SHAPEZIP",
-            })
-
-        if ft.get_fid_column() != None:
-            layer.set_metadatas({
-                "wfs_featureid": ft.get_fid_column(),
-                "gml_featureid": ft.get_fid_column(),
-                })
-
-        plugins.extend("post_configure_vector_layer", self, ws, ds, ft, layer)
-
-
-class CoverageModel(LayerModel):
-    """
-    """
-
-    def update(self, ws, c_name, cs_name, metadata):
-
-        cs = ws.get_coveragestore(cs_name)
-        self.name = c_name
-
-        # Set basic attributes.
-        self.ms.name = "c:%s:%s" % (cs_name, c_name)
-        self.ms.status = mapscript.MS_OFF
-        self.ms.type = mapscript.MS_LAYER_RASTER
-        self.ms.setProjection(cs.get_proj4())
-        self.ms.setExtent(*cs.get_extent())
-        self.ms.setProcessingKey("RESAMPLE","AVERAGE")
-
-        # Configure the connection to the store.
-        # This is a little hacky as we have to translate stuff...
-        info = ws.get_coveragestore_info(cs_name)
-        cparam = info["connectionParameters"]
-
-        #if cparam["dbtype"] in ["tif", "tiff"]:
-        self.ms.connectiontype = mapscript.MS_RASTER
-        url = urlparse.urlparse(cparam["url"])
-        self.ms.data = tools.get_resource_path(url.path)
-            # TODO: strip extention.
-        #else:
-        #    raise ValueError("Unhandled type '%s'" % cparam["dbtype"])
-
-        # Deactivate wms and wcs requests, because we are a virtual layer.
-        self.set_metadatas({
-            "wms_enable_request": "!GetCapabilities !GetMap !GetFeatureInfo !GetLegendGraphic",
-            "wcs_enable_request": "!GetCapabilities !DescribeCoverage !GetCoverage",
-            })
-
-        # Update mra metadatas, and make sure the mandatory ones are left untouched.
-        self.update_mra_metadatas(metadata)
-        self.update_mra_metadatas({"name": c_name, "type": "coverage", "storage": cs_name,
-                                   "workspace": ws.name, "is_model": True})
-
-    def configure_layer(self, ws, layer, enabled=True):
-
-        plugins.extend("pre_configure_raster_layer", self, ws, layer)
-
-        # We must also update all our personal attributes (type, ...)
-        # because we might not have been cloned.
-
-        layer.ms.type = self.ms.type
-        layer.ms.setProjection(self.ms.getProjection())
-        layer.ms.setExtent(self.ms.extent.minx, self.ms.extent.miny,
-                           self.ms.extent.maxx, self.ms.extent.maxy)
-        layer.ms.setProcessingKey("RESAMPLE","AVERAGE")
-        layer.ms.data = self.ms.data
-        layer.ms.connectiontype = self.ms.connectiontype
-        layer.ms.connection = self.ms.connection
-
-        layer.update_mra_metadatas({
-                "name": self.get_mra_metadata("name"),
-                "type": self.get_mra_metadata("type"),
-                "storage": self.get_mra_metadata("storage"),
-                "workspace": self.get_mra_metadata("workspace"),
-                "is_model": False,
-                })
-
-        layer.set_metadatas({
-                "wfs_name": layer.get_metadata("wms_name"),
-                "wfs_title": layer.get_metadata("wms_title"),
-                "wfs_abstract": layer.get_metadata("wms_abstract"),
-                # TODO: wfs_keywordlist, wcs_srs, wcs_getfeature_formatlist...
-                })
-
-        if enabled:
-            layer.set_metadata("wcs_enable_request", "GetCapabilities GetCoverage DescribeCoverage")
-
-        plugins.extend("post_configure_raster_layer", self, ws, layer)
-
-
-class Workspace(object):
-    # TODO
-    pass
-
-class MapfileWorkspace(Workspace):
-    """ A workspace representing a whole mapfile.
-    This is currently the only existing type of workspace,
-    but there should be others that can handle subsets of
-    the mapfile.
-    """
-
-    def __init__(self, mapfile):
-        # We are obvliously the default workspace.
-        self.name = mapfile.get_default_workspace_name()
-        self.mapfile = mapfile
-
-    def save(self):
-        """Saves the workspace to disk, same as calling save on the
-        associated mapfile.
-        """
-        self.mapfile.save()
-
-    # Stores:
-    def get_store(self, st_type, name):
-        st_type = st_type if st_type.endswith("s") else st_type + "s"
-        cparam = get_store_connection_string(self.get_store_info(st_type, name))
-        if st_type == "datastores":
-            return stores.Datastore(cparam)
-        elif st_type == "coveragestores":
-            return stores.Coveragestore(cparam)
-
-    def get_store_info(self, st_type, name):
-        st_type = st_type if st_type.endswith("s") else st_type + "s"
-        info = self.mapfile.get_mra_metadata(st_type, {})[name].copy()
-        info["name"] = name
-        return info
-
-    def iter_store_names(self, st_type):
-        st_type = st_type if st_type.endswith("s") else st_type + "s"
-        return self.mapfile.get_mra_metadata(st_type, {}).iterkeys()
-
-    def iter_stores(self, st_type):
-        st_type = st_type if st_type.endswith("s") else st_type + "s"
-        return self.mapfile.get_mra_metadata(st_type, {}).iteritems()
-
-    def create_store(self, st_type, name, configuration):
-        st_type = st_type if st_type.endswith("s") else st_type + "s"
-        with self.mapfile.mra_metadata(st_type, {}) as stores:
-            if name in stores:
-                raise KeyExists(name)
-            stores[name] = configuration
-
-    def update_store(self, st_type, name, configuration):
-        st_type = st_type if st_type.endswith("s") else st_type + "s"
-        with self.mapfile.mra_metadata(st_type, {}) as stores:
-            stores[name].update(configuration)
-
-    def delete_store(self, st_type, name):
-        st_type = st_type if st_type.endswith("s") else st_type + "s"
-        with self.mapfile.mra_metadata(st_type, {}) as stores:
-            del stores[name]
-
-    # Datastores:
-
-    def get_datastore(self, name):
-        """Returns a store.Datastore object from the workspace."""
-        return self.get_store("datastores", name)
-
-    def get_datastore_info(self, name):
-        """Returns info for a datastore from the workspace."""
-        return self.get_store_info("datastores", name)
-
-    def iter_datastore_names(self):
-        """Return an iterator over the datastore names."""
-        return self.iter_store_names("datastores")
-
-    def iter_datastores(self):
-        """Return an iterator over the datastore (names, configuration)."""
-        return self.iter_stores("datastores")
-
-    def create_datastore(self, name, configuration):
-        """Creates a new datastore."""
-        return self.create_store("datastores", name, configuration)
-
-    def update_datastore(self, name, configuration):
-        """Update a datastore."""
-        return self.update_store("datastores", name, configuration)
-
-    def delete_datastore(self, name):
-        """Delete a datastore."""
-        return self.delete_store("datastores", name)
-
-    # Coveragestores (this is c/p from datastores):
-
-    def get_coveragestore(self, name):
-        """Returns a store.Coveragestore object from the workspace."""
-        return self.get_store("coveragestores", name)
-
-    def get_coveragestore_info(self, name):
-        """Returns info for a coveragestore from the workspace."""
-        return self.get_store_info("coveragestores", name)
-
-    def iter_coveragestore_names(self):
-        """Return an iterator over the coveragestore names."""
-        return self.iter_store_names("coveragestores")
-
-    def iter_coveragestores(self):
-        """Return an iterator over the coveragestore (names, configuration)."""
-        return self.iter_stores("coveragestores")
-
-    def create_coveragestore(self, name, configuration):
-        """Creates a new coveragestore."""
-        return self.create_store("coveragestores", name, configuration)
-
-    def update_coveragestore(self, name, configuration):
-        """Update a coveragestore."""
-        return self.update_store("coveragestores", name, configuration)
-
-    def delete_coveragestore(self, name):
-        """Delete a coveragestore."""
-        return self.delete_store("coveragestores", name)
-
-    # Feature types
-
-    def iter_featuretypemodels(self, ds_name=None, **kwargs):
-        kwargs.setdefault("mra", {}).update({"type":"featuretype", "is_model":True})
-        if ds_name != None:
-            kwargs["mra"].update({"storage":ds_name, "workspace":self.name})
-        for ms_layer in self.mapfile.iter_ms_layers(**kwargs):
-            yield FeatureTypeModel(ms_layer)
-
-    def get_featuretypemodel(self, ft_name, ds_name):
-        # Improvement: Use get by name combined with a coverage-specific naming.
-        try:
-            return next(self.iter_featuretypemodels(ds_name, mra={"name":ft_name}))
-        except StopIteration:
-            raise KeyError((ds_name, ft_name))
-
-    def has_featuretypemodel(self, ft_name, ds_name):
-        # Improvement: See get_featuretypemodel
-        try:
-            self.get_featuretypemodel(ft_name, ds_name)
-        except KeyError:
-            return False
-        else:
-            return True
-
-    def create_featuretypemodel(self, ft_name, ds_name, metadata={}):
-        if self.has_featuretypemodel(ft_name, ds_name):
-            raise KeyExists(ft_name)
-
-        ft = FeatureTypeModel(mapscript.layerObj(self.mapfile.ms))
-        ft.update(self, ft_name, ds_name, metadata)
-        return ft
-
-    def update_featuretypemodel(self, ft_name, ds_name, metadata={}):
-        ft = self.get_featuretypemodel(ft_name, ds_name)
-        ft.update(self, ft_name, ds_name, metadata)
-
-    def delete_featuretypemodel(self, ft_name, ds_name):
-        try:
-            next(self.mapfile.iter_layers(mra={"workspace":self.name, "type":"featuretype",
-                                               "storage":ds_name, "name":ft_name}))
-        except StopIteration:
-            pass # No layers use our featuretyp, all OK.
-        else:
-            raise ValueError("The featuretype '%s' can't be delete because it is used." % ft_name)
-
-        ft = self.get_featuretypemodel(ft_name, ds_name)
-        self.mapfile.ms.removeLayer(ft.ms.index)
-
-    # Coverages
-
-    def iter_coveragemodels(self, cs_name=None, **kwargs):
-        kwargs.setdefault("mra", {}).update({"type":"coverage", "is_model":True})
-        if cs_name != None:
-            kwargs["mra"].update({"storage":cs_name, "workspace":self.name})
-        for ms_layer in self.mapfile.iter_ms_layers(**kwargs):
-            yield CoverageModel(ms_layer)
-
-    def get_coveragemodel(self, c_name, cs_name):
-        # Improvement: Use get by name combined with a coverage-specific naming.
-        try:
-            return next(self.iter_coveragemodels(cs_name, mra={"name":c_name}))
-        except StopIteration:
-            raise KeyError((cs_name, c_name))
-
-    def has_coveragemodel(self, c_name, cs_name):
-        # Improvement: See get_coveragemodel
-        try:
-            self.get_coveragemodel(c_name, cs_name)
-        except KeyError:
-            return False
-        else:
-            return True
-
-    def create_coveragemodel(self, c_name, cs_name, metadata={}):
-        if self.has_coveragemodel(c_name, cs_name):
-            raise KeyExists(c_name)
-
-        c = CoverageModel(mapscript.layerObj(self.mapfile.ms))
-        c.update(self, c_name, cs_name, metadata)
-        return c
-
-    def update_coveragemodel(self, c_name, cs_name, metadata={}):
-        c = self.get_coveragemodel(c_name, cs_name)
-        c.update(self, c_name, cs_name, metadata)
-
-    def delete_coveragemodel(self, c_name, cs_name):
-        try:
-            next(self.mapfile.iter_layers(mra={"workspace":self.name, "type":"coverage",
-                                               "storage":cs_name, "name":c_name}))
-        except StopIteration:
-            pass # No layers use our featuretyp, all OK.
-        else:
-            raise ValueError("The coverage '%s' can't be delete because it is used." % c_name)
-
-        c = self.get_coveragemodel(c_name, cs_name)
-        self.mapfile.ms.removeLayer(c.ms.index)
-
-    # All the above :)
-
-    def get_model(self, m_name, s_type, s_name):
-
-        if s_type == "coverage":
-            return self.get_coveragemodel(m_name, s_name)
-        elif s_type == "featuretype":
-            return self.get_featuretypemodel(m_name, s_name)
-        else:
-            raise ValueError("Bad storage type '%s'." % s_type)
-
-
-def create_mapfile(path, map_name, data):
-    if os.path.exists("%s.map" % path):
-        raise KeyExists(map_name)
-
-    mf = mapscript.mapObj()
-    mf.name = map_name
-
-    # The following could be defined in <mapfile.py>:
-
-    mf.web.metadata.set("ows_name", map_name)
-    mf.web.metadata.set("ows_title", data.get("title", map_name))
-    mf.web.metadata.set("ows_abstract", data.get("abstract", ""))
-
-    # Set default values:
-    # It should be configurable to the future.
-
-    # mf.web.metadata.set("ows_keywordlist", "")
-    # mf.web.metadata.set("ows_keywordlist_vocabulary", "")
-    # + ows_keywordlist_[vocabulary’s name]_items
-    # mf.web.metadata.set("wms_onlineresource", "")
-    # mf.web.metadata.set("wfs_onlineresource", "")
-    # mf.web.metadata.set("wms_service_onlineresource", "")
-    # mf.web.metadata.set("wfs_service_onlineresource", "")
-    mf.web.metadata.set("wms_srs", "EPSG:4326")
-    mf.web.metadata.set("wfs_srs", "EPSG:4326")
-    mf.web.metadata.set("wms_bbox_extended", "true")
-    # mf.web.metadata.set("wms_resx", "")
-    # mf.web.metadata.set("wms_resy", "")
-
-    mf.web.metadata.set("ows_schemas_location",
-                        "http://schemas.opengeospatial.net")
-    mf.web.metadata.set("ows_updatesequence", "foo")
-    mf.web.metadata.set("ows_addresstype", "foo")
-    mf.web.metadata.set("ows_address", "foo")
-    mf.web.metadata.set("ows_city", "foo")
-    mf.web.metadata.set("ows_stateorprovince", "foo")
-    mf.web.metadata.set("ows_postcode", "foo")
-    mf.web.metadata.set("ows_contactperson", "foo")
-    mf.web.metadata.set("ows_contactposition", "foo")
-    mf.web.metadata.set("ows_contactorganization", "foo")
-    mf.web.metadata.set("ows_contactelectronicmailaddress", "foo")
-    mf.web.metadata.set("ows_contactfacsimiletelephone", "foo")
-    mf.web.metadata.set("ows_contactvoicetelephone", "foo")
-    mf.web.metadata.set("wms_fees", "none")
-    mf.web.metadata.set("wfs_fees", "none")
-    mf.web.metadata.set("wms_accessconstraints", "none")
-    mf.web.metadata.set("wfs_accessconstraints", "none")
-    # mf.web.metadata.set("ows_attribution_logourl_format", "")
-    # mf.web.metadata.set("ows_attribution_logourl_height", "")
-    # mf.web.metadata.set("ows_attribution_logourl_href", "")
-    # mf.web.metadata.set("ows_attribution_logourl_width", "")
-    # mf.web.metadata.set("ows_attribution_onlineresource", "")
-    # mf.web.metadata.set("ows_attribution_title", "")
-
-    mf.web.metadata.set("wms_enable_request",
-                        "GetCapabilities GetMap GetFeatureInfo GetLegendGraphic")
-    mf.web.metadata.set("wfs_enable_request",
-                        "GetCapabilities DescribeFeatureType GetFeature")
-    mf.web.metadata.set("ows_sld_enabled", "true")
-    mf.web.metadata.set("wms_getcapabilities_version", "1.3.0")
-    mf.web.metadata.set("wfs_getcapabilities_version", "1.0.0")
-    # mf.web.metadata.set("wms_getmap_formatlist", "")
-    # mf.web.metadata.set("wms_getlegendgraphic_formatlist", "")
-    mf.web.metadata.set("wms_feature_info_mime_type",
-                        "application/vnd.ogc.gml,text/plain")
-                        # TODO: text/html
-    mf.web.metadata.set("wms_encoding", "UTF-8")
-    mf.web.metadata.set("wfs_encoding", "UTF-8")
-
-    # mf.web.metadata.set("wms_timeformat", "")
-    # mf.web.metadata.set("wms_languages", "")
-    # mf.web.metadata.set("wms_layerlimit", "")
-    # mf.web.metadata.set("wms_rootlayer_abstract", "")
-    # mf.web.metadata.set("wms_rootlayer_keywordlist", "")
-    # mf.web.metadata.set("wms_rootlayer_title", "")
-    # mf.web.metadata.set("wfs_maxfeatures", "")
-    # mf.web.metadata.set("wfs_feature_collection", "")
-    # mf.web.metadata.set("wfs_namespace_uri", "")
-    # mf.web.metadata.set("wfs_namespace_prefix", "")
-
-    mf.status = mapscript.MS_ON
-    mf.setSize(256,256)
-    mf.maxsize = 4096
-    mf.resolution = 96
-    mf.imagetype = 'png'
-    mf.imagecolor.setRGB(255,255,255)
-    mf.setProjection("init=epsg:4326")
-    mf.setExtent(-180,-90,180,90)
-    mf.units = mapscript.MS_DD
-
-    mf.save("%s.map" % path)
-
-
-class Mapfile(MetadataMixin):
-    """
-    """
-
-    def __init__(self, path, root=None):
-
-        if root != None:
-            full_path = os.path.realpath(os.path.join(root, "%s.map" % path))
-            if not full_path.startswith(root):
-                raise IOError("mapfile '%s' outside root directory." % (path))
-            path = full_path
-        if isinstance(path, mapscript.mapObj):
-            self.path = None
-            self.ms = path
-        else:
-            self.path = path
-            self.ms = mapscript.mapObj(self.path)
-
-        self.filename = os.path.basename(self.path)
-
-        # We have one workspace that represents the file.
-        self.__default_workspace = MapfileWorkspace(self)
-
-    def save(self, path=None):
-        if path is None:
-            path = self.path
-        self.ms.save(path)
-
-    def rawtext(self):
-        open(self.path, "r").read()
-
-    def update(self, configuration):
-        raise NotImplemented()
-
-    # Layers:
-
-    def iter_ms_layers(self, attr={}, meta={}, mra={}):
-        def check(f, v):
-            return f(v) if callable(f) else f == v
-
-        for l in xrange(self.ms.numlayers):
-            ms_layer = self.ms.getLayer(l)
-            if not all(check(checker, getattr(ms_layer, k, None)) for k, checker in attr.iteritems()):
-                continue
-            if not all(check(checker, metadata.get_metadata(ms_layer, k, None)) for k, checker in meta.iteritems()):
-                continue
-            if not all(check(checker, metadata.get_mra_metadata(ms_layer, k, None)) for k, checker in mra.iteritems()):
-                continue
-            yield ms_layer
-
-    def iter_layers(self, **kwargs):
-        kwargs.setdefault("mra", {}).update({"is_model": lambda x: x != True})
-        for ms_layer in self.iter_ms_layers(**kwargs):
-            yield Layer(ms_layer)
-
-    def get_layer(self, l_name):
-        try:
-            return next(self.iter_layers(attr={"name": l_name}))
-        except StopIteration:
-            raise KeyError(l_name)
-
-    def move_layer_down(self, l_name):
-        layer = self.get_layer(l_name)
-        self.ms.moveLayerDown(layer.ms.index)
-
-    def has_layer(self, l_name):
-        try:
-            self.get_layer(l_name)
-        except KeyError:
-            return False
-        else:
-            return True
-
-    def get_layer_wsm(self, l_name):
-        layer = self.get_layer(l_name)
-        ws = self.get_workspace(layer.get_mra_metadata("workspace"))
-        model = ws.get_model(m_name=layer.get_mra_metadata("name"),
-                             s_type=layer.get_mra_metadata("type"),
-                             s_name=layer.get_mra_metadata("storage"))
-        return layer, ws, model
-
-    def create_layer(self, ws, model, l_name, l_enabled, l_metadata={}):
-        # First create the layer, then configure it.
-
-        if self.has_layer(l_name):
-            raise KeyExists(l_name)
-
-        # We still clone, because we have to start from something,
-        # but everything should be configured() anyway.
-        layer = Layer(model.ms.clone())
-        self.ms.insertLayer(layer.ms)
-
-        layer.ms.name = l_name
-        layer.enable(l_enabled)
-
-        # is queryable (by default):
-        layer.ms.template = "foo.html" # TODO: Support html format response
-
-        l_metadata["wms_name"] = l_name
-
-        l_metadata.setdefault("wms_title", l_name)
-        l_metadata.setdefault("wms_abstract", l_name)
-        l_metadata.setdefault("wms_bbox_extended", "true")
-        # TODO: Add other default values as above:
-        # wms_keywordlist, wms_keywordlist_vocabulary
-        # wms_keywordlist_<vocabulary>_items
-        # wms_srs
-        # wms_dataurl_(format|href)
-        # ows_attribution_(title|onlineresource)
-        # ows_attribution_logourl_(href|format|height|width)
-        # ows_identifier_(authority|value)
-        # ows_authorityurl_(name|href)
-        # ows_metadataurl_(type|href|format)
-
-        # TODO: pass correct data (title, abstract, ...) to layer.update()
-        layer.update(l_metadata)
-
-        model.configure_layer(ws, layer, l_enabled)
-
-        layer.set_default_style(self)
-
-    def delete_layer(self, l_name):
-        layer = self.get_layer(l_name)
-
-        self.ms.removeLayer(layer.ms.index)
-
-    # Layergroups
-
-    def create_layergroup(self, lg_name, mra_metadata={}):
-        with self.mra_metadata("layergroups", {}) as layergroups:
-            if lg_name in layergroups:
-                raise KeyExists(lg_name)
-            layergroups[lg_name] = mra_metadata
-        return LayerGroup(lg_name, self)
-
-    def iter_layergroups(self):
-        return (LayerGroup(name, self) for name in self.get_mra_metadata("layergroups", {}).iterkeys())
-
-    def get_layergroup(self, lg_name):
-        if lg_name in self.get_mra_metadata("layergroups", {}):
-            return LayerGroup(lg_name, self)
-        else:
-            raise KeyError(lg_name)
-
-    def add_to_layergroup(self, lg_name, *args):
-        lg = self.get_layergroup(lg_name)
-        lg.add(*args)
-
-    def remove_from_layergroup(self, lg_name, *args):
-        lg = self.get_layergroup(lg_name)
-        lg.remove(*args)
-
-    def delete_layergroup(self, lg_name):
-        layer_group = self.get_layergroup(lg_name)
-        # Remove all the layers from this group.
-        for layer in self.iter_layers(attr={"group": layer_group.name}):
-            layer_group.remove(layer)
-        # Remove the group from mra metadata.
-        with self.mra_metadata("layergroups", {}) as layergroups:
-            del layergroups[lg_name]
-
-    # Styles:
-
-    def iter_styles(self):
-        return iter(self.get_styles())
-
-    def get_styles(self):
-        # The group name is the style's name.
-
-        styles = set()
-        for layer in self.iter_layers():
-            for clazz in layer.iter_classes():
-                styles.add(clazz.ms.group)
-
-        return styles
-
-    def get_style_sld(self, s_name):
-        # Because styles do not really exist here, we first need to find
-        # a layer that has the style we want.
-
-        for layer in self.iter_layers():
-            if s_name in layer.get_styles():
-                break
-        else:
-            raise KeyError(s_name)
-
-        # This is a ugly hack. We clone the layer and remove all the other styles.
-        # Then we ask mapscript to generate the sld, but aprently for that the
-        # cloned style needs to be in the mapfile... so be it.
-        clone = layer.ms.clone()
-        for c_index in reversed(xrange(clone.numclasses)):
-            c = clone.getClass(c_index)
-            if c.group != s_name:
-                clone.removeClass(c_index)
-
-        self.ms.insertLayer(clone)
-        sld = Layer(clone).get_SLD()
-        self.ms.removeLayer(clone.index)
-
-        return sld
-
-    # Workspaces:
-
-    def get_default_workspace_name(self):
-        return self.get_mra_metadata("default_workspace", "default")
-
-    def set_default_workspace_name(self, name):
-        self.set_mra_metadata("default_workspace", name)
-
-    def iter_workspaces(self):
-        """iter_ates over the workspaces managed by a mapfile.
-        For current version, only "default" workspace is available which
-        correspond to the current mapfile. The relationship between default
-        workspace and mapfile is one to one.
-        However there is currently work underway to change this...
-        """
-        yield self.get_default_workspace()
-
-    def get_workspaces(self):
-        """Gets workspaces from mapfile that match all the specified conditions.
-        For the moment this is equivalent to iter_workspaces, because workpspaces
-        are still a "virtual" notion and do not really exist.
-        """
-        return list(self.iter_workspaces())
-
-    def get_workspace(self, name):
-        if name != self.get_default_workspace_name():
-            raise KeyError(name)
-        return self.get_default_workspace()
-
-    def get_default_workspace(self):
-        return self.__default_workspace
-
-    # Let"s delegate other stuff to the default workspace.
-    # def __getattr__(self, name):
-    #     if hasattr(self, "__default_workspace"):
-    #         return getattr(self.__default_workspace, name)
diff --git a/src/server.py b/src/server.py
index 66c6d498e7644b1f6e4dc49b28b2fb6493d9c464..bdac1edd2b2d44dba0de4a755c4c371316b6050d 100755
--- a/src/server.py
+++ b/src/server.py
@@ -35,7 +35,6 @@ import urlparse
 import mralogs
 import logging
 
-import mapfile
 from mra import MRA
 
 
diff --git a/src/tools.py b/src/tools.py
index e1a9c1f2e882c02fae157f2255e95333d1602331..14932d98caaec6380fe06bfd01ea5b02b68ec26a 100644
--- a/src/tools.py
+++ b/src/tools.py
@@ -28,7 +28,6 @@ import os
 import yaml
 import sys
 
-import mapfile
 import pyxml
 
 import webapp