From 57a2f2cd90d8db040c44016cf664f2b4e2c13b7a Mon Sep 17 00:00:00 2001
From: DHONT <rldhont@3liz.com>
Date: Tue, 13 Mar 2012 18:07:27 +0100
Subject: [PATCH] WFS-Server V4

 src/app/qgsprojectproperties.cpp        |   48 ++-
 src/core/qgsgeometry.cpp                |  290 ++++++++++
 src/core/qgsgeometry.h                  |    5 +
 src/mapserver/CMakeLists.txt            |    1 +
 src/mapserver/qgis_map_serv.cpp         |  107 ++++-
 src/mapserver/qgsconfigparser.h         |    4 +
 src/mapserver/qgshttprequesthandler.cpp |   50 ++
 src/mapserver/qgshttprequesthandler.h   |    3 +
 src/mapserver/qgsprojectparser.cpp      |   98 ++++
 src/mapserver/qgsprojectparser.h        |    5 +
 src/mapserver/qgsrequesthandler.h       |    3 +
 src/mapserver/qgssldparser.h            |    2 +
 src/mapserver/qgswfsserver.cpp          |  938 +++++++++++++++++++++++++++++++
 src/mapserver/qgswfsserver.h            |   92 +++
 src/ui/qgsprojectpropertiesbase.ui      |   38 ++-
 15 files changed, 1675 insertions(+), 9 deletions(-)
 create mode 100644 src/mapserver/qgswfsserver.cpp
 create mode 100644 src/mapserver/qgswfsserver.h

diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp
index 8022c05..2515823 100644
--- a/src/app/qgsprojectproperties.cpp
+++ b/src/app/qgsprojectproperties.cpp
@@ -175,7 +175,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas* mapCanvas, QWidget *pa
     twIdentifyLayers->setCellWidget( i, 2, cb );
   }
 
-  grpWMSServiceCapabilities->setChecked( QgsProject::instance()->readBoolEntry( "WMSServiceCapabilities", "/", false ) );
+  grpOWSServiceCapabilities->setChecked( QgsProject::instance()->readBoolEntry( "WMSServiceCapabilities", "/", false ) );
   mWMSTitle->setText( QgsProject::instance()->readEntry( "WMSServiceTitle", "/" ) );
   mWMSContactOrganization->setText( QgsProject::instance()->readEntry( "WMSContactOrganization", "/", "" ) );
   mWMSContactPerson->setText( QgsProject::instance()->readEntry( "WMSContactPerson", "/", "" ) );
@@ -229,6 +229,38 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas* mapCanvas, QWidget *pa
   bool addWktGeometry = QgsProject::instance()->readBoolEntry( "WMSAddWktGeometry", "/" );
   mAddWktGeometryCheckBox->setChecked( addWktGeometry );
 
+  QStringList wfsLayerIdList = QgsProject::instance()->readListEntry( "WFSLayers", "/" );
+
+  twWFSLayers->setColumnCount( 2 );
+  twWFSLayers->horizontalHeader()->setVisible( true );
+  twWFSLayers->setRowCount( mapLayers.size() );
+
+  i = 0;
+  int j = 0;
+  for ( QMap<QString, QgsMapLayer*>::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); it++, i++ )
+  {
+    currentLayer = it.value();
+    if ( currentLayer->type() == QgsMapLayer::VectorLayer )
+    {
+
+    QTableWidgetItem *twi = new QTableWidgetItem( QString::number( j ) );
+    twWFSLayers->setVerticalHeaderItem( j, twi );
+
+    twi = new QTableWidgetItem( currentLayer->name() );
+    twi->setData( Qt::UserRole, it.key() );
+    twi->setFlags( twi->flags() & ~Qt::ItemIsEditable );
+    twWFSLayers->setItem( j, 0, twi );
+
+    QCheckBox *cb = new QCheckBox();
+    cb->setChecked( wfsLayerIdList.contains( currentLayer->id() ) );
+    twWFSLayers->setCellWidget( j, 1, cb );
+    j++;
+
+    }
+  }
+  twWFSLayers->setRowCount( j );
+  twWFSLayers->verticalHeader()->setResizeMode( QHeaderView::ResizeToContents );
+
   restoreState();
 }
 
@@ -380,7 +412,7 @@ void QgsProjectProperties::apply()
 
   QgsProject::instance()->writeEntry( "Identify", "/disabledLayers", noIdentifyLayerList );
 
-  QgsProject::instance()->writeEntry( "WMSServiceCapabilities", "/", grpWMSServiceCapabilities->isChecked() );
+  QgsProject::instance()->writeEntry( "WMSServiceCapabilities", "/", grpOWSServiceCapabilities->isChecked() );
   QgsProject::instance()->writeEntry( "WMSServiceTitle", "/", mWMSTitle->text() );
   QgsProject::instance()->writeEntry( "WMSContactOrganization", "/", mWMSContactOrganization->text() );
   QgsProject::instance()->writeEntry( "WMSContactPerson", "/", mWMSContactPerson->text() );
@@ -428,6 +460,18 @@ void QgsProjectProperties::apply()
 
   QgsProject::instance()->writeEntry( "WMSAddWktGeometry", "/", mAddWktGeometryCheckBox->isChecked() );
 
+  QStringList wfsLayerList;
+  for ( int i = 0; i < twWFSLayers->rowCount(); i++ )
+  {
+    QCheckBox *cb = qobject_cast<QCheckBox *>( twWFSLayers->cellWidget( i, 1 ) );
+    if ( cb && cb->isChecked() )
+    {
+      QString id = twWFSLayers->item( i, 0 )->data( Qt::UserRole ).toString();
+      wfsLayerList << id;
+    }
+  }
+  QgsProject::instance()->writeEntry( "WFSLayers", "/", wfsLayerList );
+
   //todo XXX set canvas color
   emit refresh();
 }
diff --git a/src/core/qgsgeometry.cpp b/src/core/qgsgeometry.cpp
index a83b5dc..9e92cb1 100755
--- a/src/core/qgsgeometry.cpp
+++ b/src/core/qgsgeometry.cpp
@@ -4185,6 +4185,296 @@ QString QgsGeometry::exportToWkt()
   }
 }
 
+QString QgsGeometry::exportToGeoJSON()
+{
+  QgsDebugMsg( "entered." );
+
+  // TODO: implement with GEOS
+  if ( mDirtyWkb )
+  {
+    exportGeosToWkb();
+  }
+
+  if ( !mGeometry )
+  {
+    QgsDebugMsg( "WKB geometry not available!" );
+    return QString::null;
+  }
+
+  QGis::WkbType wkbType;
+  bool hasZValue = false;
+  double *x, *y;
+
+  QString mWkt; // TODO: rename
+
+  // Will this really work when mGeometry[0] == 0 ???? I (gavin) think not.
+  //wkbType = (mGeometry[0] == 1) ? mGeometry[1] : mGeometry[4];
+  memcpy( &wkbType, &( mGeometry[1] ), sizeof( int ) );
+
+  switch ( wkbType )
+  {
+    case QGis::WKBPoint25D:
+    case QGis::WKBPoint:
+    {
+      mWkt += "{ \"type\": \"Point\", \"coordinates\": [";
+      x = ( double * )( mGeometry + 5 );
+      mWkt += QString::number( *x, 'f', 6 );
+      mWkt += ", ";
+      y = ( double * )( mGeometry + 5 + sizeof( double ) );
+      mWkt += QString::number( *y, 'f', 6 );
+      mWkt += "] }";
+      return mWkt;
+    }
+
+    case QGis::WKBLineString25D:
+      hasZValue = true;
+    case QGis::WKBLineString:
+    {
+      QgsDebugMsg( "LINESTRING found" );
+      unsigned char *ptr;
+      int *nPoints;
+      int idx;
+
+      mWkt += "{ \"type\": \"LineString\", \"coordinates\": [ ";
+      // get number of points in the line
+      ptr = mGeometry + 5;
+      nPoints = ( int * ) ptr;
+      ptr = mGeometry + 1 + 2 * sizeof( int );
+      for ( idx = 0; idx < *nPoints; ++idx )
+      {
+        if ( idx != 0 )
+        {
+          mWkt += ", ";
+        }
+        mWkt += "[";
+        x = ( double * ) ptr;
+        mWkt += QString::number( *x, 'f', 6 );
+        mWkt += ", ";
+        ptr += sizeof( double );
+        y = ( double * ) ptr;
+        mWkt += QString::number( *y, 'f', 6 );
+        ptr += sizeof( double );
+        if ( hasZValue )
+        {
+          ptr += sizeof( double );
+        }
+        mWkt += "]";
+      }
+      mWkt += " ] }";
+      return mWkt;
+    }
+
+    case QGis::WKBPolygon25D:
+      hasZValue = true;
+    case QGis::WKBPolygon:
+    {
+      QgsDebugMsg( "POLYGON found" );
+      unsigned char *ptr;
+      int idx, jdx;
+      int *numRings, *nPoints;
+
+      mWkt += "{ \"type\": \"Polygon\", \"coordinates\": [ ";
+      // get number of rings in the polygon
+      numRings = ( int * )( mGeometry + 1 + sizeof( int ) );
+      if ( !( *numRings ) )  // sanity check for zero rings in polygon
+      {
+        return QString();
+      }
+      int *ringStart; // index of first point for each ring
+      int *ringNumPoints; // number of points in each ring
+      ringStart = new int[*numRings];
+      ringNumPoints = new int[*numRings];
+      ptr = mGeometry + 1 + 2 * sizeof( int ); // set pointer to the first ring
+      for ( idx = 0; idx < *numRings; idx++ )
+      {
+        if ( idx != 0 )
+        {
+          mWkt += ", ";
+        }
+        mWkt += "[ ";
+        // get number of points in the ring
+        nPoints = ( int * ) ptr;
+        ringNumPoints[idx] = *nPoints;
+        ptr += 4;
+
+        for ( jdx = 0; jdx < *nPoints; jdx++ )
+        {
+          if ( jdx != 0 )
+          {
+            mWkt += ", ";
+          }
+          mWkt += "[";
+          x = ( double * ) ptr;
+          mWkt += QString::number( *x, 'f', 6 );
+          mWkt += ", ";
+          ptr += sizeof( double );
+          y = ( double * ) ptr;
+          mWkt += QString::number( *y, 'f', 6 );
+          ptr += sizeof( double );
+          if ( hasZValue )
+          {
+            ptr += sizeof( double );
+          }
+          mWkt += "]";
+        }
+        mWkt += " ]";
+      }
+      mWkt += " ] }";
+      delete [] ringStart;
+      delete [] ringNumPoints;
+      return mWkt;
+    }
+
+    case QGis::WKBMultiPoint25D:
+      hasZValue = true;
+    case QGis::WKBMultiPoint:
+    {
+      unsigned char *ptr;
+      int idx;
+      int *nPoints;
+
+      mWkt += "{ \"type\": \"MultiPoint\", \"coordinates\": [ ";
+      nPoints = ( int* )( mGeometry + 5 );
+      ptr = mGeometry + 5 + sizeof( int );
+      for ( idx = 0; idx < *nPoints; ++idx )
+      {
+        ptr += ( 1 + sizeof( int ) );
+        if ( idx != 0 )
+        {
+          mWkt += ", ";
+        }
+        mWkt += "[";
+        x = ( double * )( ptr );
+        mWkt += QString::number( *x, 'f', 6 );
+        mWkt += ", ";
+        ptr += sizeof( double );
+        y = ( double * )( ptr );
+        mWkt += QString::number( *y, 'f', 6 );
+        ptr += sizeof( double );
+        if ( hasZValue )
+        {
+          ptr += sizeof( double );
+        }
+        mWkt += "]";
+      }
+      mWkt += " ] }";
+      return mWkt;
+    }
+
+    case QGis::WKBMultiLineString25D:
+      hasZValue = true;
+    case QGis::WKBMultiLineString:
+    {
+      QgsDebugMsg( "MULTILINESTRING found" );
+      unsigned char *ptr;
+      int idx, jdx, numLineStrings;
+      int *nPoints;
+
+      mWkt += "{ \"type\": \"MultiLineString\", \"coordinates\": [ ";
+      numLineStrings = ( int )( mGeometry[5] );
+      ptr = mGeometry + 9;
+      for ( jdx = 0; jdx < numLineStrings; jdx++ )
+      {
+        if ( jdx != 0 )
+        {
+          mWkt += ", ";
+        }
+        mWkt += "[ ";
+        ptr += 5; // skip type since we know its 2
+        nPoints = ( int * ) ptr;
+        ptr += sizeof( int );
+        for ( idx = 0; idx < *nPoints; idx++ )
+        {
+          if ( idx != 0 )
+          {
+            mWkt += ", ";
+          }
+          mWkt += "[";
+          x = ( double * ) ptr;
+          mWkt += QString::number( *x, 'f', 6 );
+          ptr += sizeof( double );
+          mWkt += ", ";
+          y = ( double * ) ptr;
+          mWkt += QString::number( *y, 'f', 6 );
+          ptr += sizeof( double );
+          if ( hasZValue )
+          {
+            ptr += sizeof( double );
+          }
+          mWkt += "]";
+        }
+        mWkt += " ]";
+      }
+      mWkt += " ] }";
+      return mWkt;
+    }
+
+    case QGis::WKBMultiPolygon25D:
+      hasZValue = true;
+    case QGis::WKBMultiPolygon:
+    {
+      QgsDebugMsg( "MULTIPOLYGON found" );
+      unsigned char *ptr;
+      int idx, jdx, kdx;
+      int *numPolygons, *numRings, *nPoints;
+
+      mWkt += "{ \"type\": \"MultiPolygon\", \"coordinates\": [ ";
+      ptr = mGeometry + 5;
+      numPolygons = ( int * ) ptr;
+      ptr = mGeometry + 9;
+      for ( kdx = 0; kdx < *numPolygons; kdx++ )
+      {
+        if ( kdx != 0 )
+        {
+          mWkt += ", ";
+        }
+        mWkt += "[ ";
+        ptr += 5;
+        numRings = ( int * ) ptr;
+        ptr += 4;
+        for ( idx = 0; idx < *numRings; idx++ )
+        {
+          if ( idx != 0 )
+          {
+            mWkt += ", ";
+          }
+          mWkt += "[ ";
+          nPoints = ( int * ) ptr;
+          ptr += 4;
+          for ( jdx = 0; jdx < *nPoints; jdx++ )
+          {
+            if ( jdx != 0 )
+            {
+              mWkt += ", ";
+            }
+            mWkt += "[";
+            x = ( double * ) ptr;
+            mWkt += QString::number( *x, 'f', 6 );
+            ptr += sizeof( double );
+            mWkt += ", ";
+            y = ( double * ) ptr;
+            mWkt += QString::number( *y, 'f', 6 );
+            ptr += sizeof( double );
+            if ( hasZValue )
+            {
+              ptr += sizeof( double );
+            }
+            mWkt += "]";
+          }
+          mWkt += " ]";
+        }
+        mWkt += " ]";
+      }
+      mWkt += " ] }";
+      return mWkt;
+    }
+
+    default:
+      QgsDebugMsg( "error: mGeometry type not recognized" );
+      return QString::null;
+  }
+}
+
 bool QgsGeometry::exportWkbToGeos()
 {
   QgsDebugMsgLevel( "entered.", 3 );
diff --git a/src/core/qgsgeometry.h b/src/core/qgsgeometry.h
index 4c7ee99..6583c89 100644
--- a/src/core/qgsgeometry.h
+++ b/src/core/qgsgeometry.h
@@ -361,6 +361,11 @@ class CORE_EXPORT QgsGeometry
      */
     QString exportToWkt();
 
+    /** Exports the geometry to mGeoJSON
+        @return true in case of success and false else
+     */
+    QString exportToGeoJSON();
+
     /* Accessor functions for getting geometry data */
 
     /** return contents of the geometry as a point
diff --git a/src/mapserver/CMakeLists.txt b/src/mapserver/CMakeLists.txt
index a78a686..c4e353b 100644
--- a/src/mapserver/CMakeLists.txt
+++ b/src/mapserver/CMakeLists.txt
@@ -28,6 +28,7 @@ SET ( qgis_mapserv_SRCS
   qgssldparser.cpp
   qgssldrenderer.cpp
   qgswmsserver.cpp
+  qgswfsserver.cpp
   qgsmapserviceexception.cpp
   qgsmslayercache.cpp
   qgsfilter.cpp
diff --git a/src/mapserver/qgis_map_serv.cpp b/src/mapserver/qgis_map_serv.cpp
index 6be6e19..6df30ad 100644
--- a/src/mapserver/qgis_map_serv.cpp
+++ b/src/mapserver/qgis_map_serv.cpp
@@ -26,6 +26,7 @@ map service syntax for SOAP/HTTP POST
 #include "qgsproviderregistry.h"
 #include "qgslogger.h"
 #include "qgswmsserver.h"
+#include "qgswfsserver.h"
 #include "qgsmaprenderer.h"
 #include "qgsmapserviceexception.h"
 #include "qgsprojectparser.h"
@@ -264,24 +265,122 @@ int main( int argc, char * argv[] )
 
     //request to WMS?
     QString serviceString;
-#ifndef QGISDEBUG
-    serviceString = parameterMap.value( "SERVICE", "WMS" );
-#else
     paramIt = parameterMap.find( "SERVICE" );
     if ( paramIt == parameterMap.constEnd() )
     {
+#ifndef QGISDEBUG
+      serviceString = parameterMap.value( "SERVICE", "WMS" );
+#else
       QgsDebugMsg( "unable to find 'SERVICE' parameter, exiting..." );
       theRequestHandler->sendServiceException( QgsMapServiceException( "ServiceNotSpecified", "Service not specified. The SERVICE parameter is mandatory" ) );
       delete theRequestHandler;
       continue;
+#endif
     }
     else
     {
       serviceString = paramIt.value();
     }
-#endif
 
     QgsWMSServer* theServer = 0;
+    if ( serviceString == "WFS" )
+    {
+      delete theServer;
+      QgsWFSServer* theServer = 0;
+      try
+      {
+        theServer = new QgsWFSServer( parameterMap );
+      }
+      catch ( QgsMapServiceException e ) //admin.sld may be invalid
+      {
+        theRequestHandler->sendServiceException( e );
+        continue;
+      }
+
+      theServer->setAdminConfigParser( adminConfigParser );
+
+
+      //request type
+      QString request = parameterMap.value( "REQUEST" );
+      if ( request.isEmpty() )
+      {
+        //do some error handling
+        QgsDebugMsg( "unable to find 'REQUEST' parameter, exiting..." );
+        theRequestHandler->sendServiceException( QgsMapServiceException( "OperationNotSupported", "Please check the value of the REQUEST parameter" ) );
+        delete theRequestHandler;
+        delete theServer;
+        continue;
+      }
+
+      if ( request == "GetCapabilities" )
+      {
+        QDomDocument capabilitiesDocument;
+        try
+        {
+          capabilitiesDocument = theServer->getCapabilities();
+        }
+        catch ( QgsMapServiceException& ex )
+        {
+          theRequestHandler->sendServiceException( ex );
+          delete theRequestHandler;
+          delete theServer;
+          continue;
+        }
+        QgsDebugMsg( "sending GetCapabilities response" );
+        theRequestHandler->sendGetCapabilitiesResponse( capabilitiesDocument );
+        delete theRequestHandler;
+        delete theServer;
+        continue;
+      }
+      else if ( request == "DescribeFeatureType" )
+      {
+        QDomDocument describeDocument;
+        try
+        {
+          describeDocument = theServer->describeFeatureType();
+        }
+        catch ( QgsMapServiceException& ex )
+        {
+          theRequestHandler->sendServiceException( ex );
+          delete theRequestHandler;
+          delete theServer;
+          continue;
+        }
+        QgsDebugMsg( "sending GetCapabilities response" );
+        theRequestHandler->sendGetCapabilitiesResponse( describeDocument );
+        delete theRequestHandler;
+        delete theServer;
+        continue;
+      }
+      else if ( request == "GetFeature" )
+      {
+        //output format for GetFeature
+        QString outputFormat = parameterMap.value( "OUTPUTFORMAT" );
+        try
+        {
+          if ( theServer->getFeature( *theRequestHandler, outputFormat ) != 0 )
+          {
+            delete theRequestHandler;
+            delete theServer;
+            continue;
+          } else {
+            delete theRequestHandler;
+            delete theServer;
+            continue;
+          }
+        }
+        catch ( QgsMapServiceException& ex )
+        {
+          theRequestHandler->sendServiceException( ex );
+          delete theRequestHandler;
+          delete theServer;
+          continue;
+        }
+      }
+
+      return 0;
+    }
+
     try
     {
       theServer = new QgsWMSServer( parameterMap, theMapRenderer );
diff --git a/src/mapserver/qgsconfigparser.h b/src/mapserver/qgsconfigparser.h
index bea118d..a165e8b 100644
--- a/src/mapserver/qgsconfigparser.h
+++ b/src/mapserver/qgsconfigparser.h
@@ -42,6 +42,8 @@ class QgsConfigParser
     /**Adds layer and style specific capabilities elements to the parent node. This includes the individual layers and styles, their description, native CRS, bounding boxes, etc.*/
     virtual void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const = 0;
 
+    virtual void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const = 0;
+
     /**Returns one or possibly several maplayers for a given layer name and style. If there are several layers, the layers should be drawn in inverse list order.
        If no layers/style are found, an empty list is returned
       @param allowCache true if layer can be read from / written to cache*/
@@ -87,6 +89,8 @@ class QgsConfigParser
 
     /**Returns an ID-list of layers which are not queryable*/
     virtual QStringList identifyDisabledLayers() const { return QStringList(); }
+    /**Returns an ID-list of layers which queryable in WFS service*/
+    virtual QStringList wfsLayers() const { return QStringList(); }
 
     /**Returns a set of supported epsg codes for the capabilities document. An empty list means
        that all possible CRS should be advertised (which could result in very long capabilities documents)*/
diff --git a/src/mapserver/qgshttprequesthandler.cpp b/src/mapserver/qgshttprequesthandler.cpp
index caa9fe9..41922cf 100644
--- a/src/mapserver/qgshttprequesthandler.cpp
+++ b/src/mapserver/qgshttprequesthandler.cpp
@@ -288,6 +288,56 @@ void QgsHttpRequestHandler::sendGetPrintResponse( QByteArray* ba ) const
   sendHttpResponse( ba, formatToMimeType( mFormat ) );
 }
 
+bool QgsHttpRequestHandler::startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const
+{
+  if ( !ba )
+  {
+    return false;
+  }
+
+  if ( ba->size() < 1 )
+  {
+    return false;
+  }
+
+  QString format;
+  if (infoFormat == "GeoJSON")
+    format = "text/plain";
+  else
+    format = "text/xml";
+
+  printf( "Content-Type: " );
+  printf( format.toLocal8Bit() );
+  printf( "\n" );
+  printf( "\n" );
+  fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
+  return true;
+}
+
+void QgsHttpRequestHandler::sendGetFeatureResponse( QByteArray* ba ) const
+{
+  if ( !ba )
+  {
+    return;
+  }
+
+  if ( ba->size() < 1 )
+  {
+    return;
+  }
+  fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
+}
+
+void QgsHttpRequestHandler::endGetFeatureResponse( QByteArray* ba ) const
+{
+  if ( !ba )
+  {
+    return;
+  }
+
+  fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
+}
+
 void QgsHttpRequestHandler::requestStringToParameterMap( const QString& request, QMap<QString, QString>& parameters )
 {
   parameters.clear();
diff --git a/src/mapserver/qgshttprequesthandler.h b/src/mapserver/qgshttprequesthandler.h
index 4353243..9d4bae4 100644
--- a/src/mapserver/qgshttprequesthandler.h
+++ b/src/mapserver/qgshttprequesthandler.h
@@ -34,6 +34,9 @@ class QgsHttpRequestHandler: public QgsRequestHandler
     virtual void sendServiceException( const QgsMapServiceException& ex ) const;
     virtual void sendGetStyleResponse( const QDomDocument& doc ) const;
     virtual void sendGetPrintResponse( QByteArray* ba ) const;
+    virtual bool startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const;
+    virtual void sendGetFeatureResponse( QByteArray* ba ) const;
+    virtual void endGetFeatureResponse( QByteArray* ba ) const;
 
   protected:
     void sendHttpResponse( QByteArray* ba, const QString& format ) const;
diff --git a/src/mapserver/qgsprojectparser.cpp b/src/mapserver/qgsprojectparser.cpp
index e1f1e2c..ccf2da5 100644
--- a/src/mapserver/qgsprojectparser.cpp
+++ b/src/mapserver/qgsprojectparser.cpp
@@ -138,6 +138,73 @@ void QgsProjectParser::layersAndStylesCapabilities( QDomElement& parentElement,
   combineExtentAndCrsOfGroupChildren( layerParentElem, doc );
 }
 
+void QgsProjectParser::featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const
+{
+  QStringList wfsLayersId = wfsLayers();
+
+  if ( mProjectLayerElements.size() < 1 )
+  {
+    return;
+  }
+
+  QMap<QString, QgsMapLayer *> layerMap;
+
+  foreach( const QDomElement &elem, mProjectLayerElements )
+  {
+    QString type = elem.attribute( "type" );
+    if ( type == "vector" )
+    {
+      //QgsMapLayer *layer = createLayerFromElement( *layerIt );
+      QgsMapLayer *layer = createLayerFromElement( elem );
+      if ( layer && wfsLayersId.contains( layer->id() ) )
+      {
+        QgsDebugMsg( QString( "add layer %1 to map" ).arg( layer->id() ) );
+        layerMap.insert( layer->id(), layer );
+
+        QDomElement layerElem = doc.createElement( "FeatureType" );
+        QDomElement nameElem = doc.createElement( "Name" );
+        //We use the layer name even though it might not be unique.
+        //Because the id sometimes contains user/pw information and the name is more descriptive
+        QDomText nameText = doc.createTextNode( layer->name() );
+        nameElem.appendChild( nameText );
+        layerElem.appendChild( nameElem );
+
+        QDomElement titleElem = doc.createElement( "Title" );
+        QDomText titleText = doc.createTextNode( layer->name() );
+        titleElem.appendChild( titleText );
+        layerElem.appendChild( titleElem );
+
+        //appendExGeographicBoundingBox( layerElem, doc, layer->extent(), layer->crs() );
+
+        QDomElement srsElem = doc.createElement( "SRS" );
+        QDomText srsText = doc.createTextNode( layer->crs().authid() );
+        srsElem.appendChild( srsText );
+        layerElem.appendChild( srsElem );
+
+        QgsRectangle layerExtent = layer->extent();
+        QDomElement bBoxElement = doc.createElement( "LatLongBoundingBox" );
+        bBoxElement.setAttribute( "minx", QString::number( layerExtent.xMinimum() ) );
+        bBoxElement.setAttribute( "miny", QString::number( layerExtent.yMinimum() ) );
+        bBoxElement.setAttribute( "maxx", QString::number( layerExtent.xMaximum() ) );
+        bBoxElement.setAttribute( "maxy", QString::number( layerExtent.yMaximum() ) );
+        layerElem.appendChild( bBoxElement );
+
+        parentElement.appendChild(layerElem);
+      }
+#if QGSMSDEBUG
+      else
+      {
+        QString buf;
+        QTextStream s( &buf );
+        layerIt->save( s, 0 );
+        QgsMSDebugMsg( QString( "layer %1 not found or not in WFS Layers" ).arg( buf ) );
+      }
+#endif
+    }
+  }
+  return;
+}
+
 void QgsProjectParser::addLayers( QDomDocument &doc,
                                   QDomElement &parentElem,
                                   const QDomElement &legendElem,
@@ -584,6 +651,37 @@ QStringList QgsProjectParser::identifyDisabledLayers() const
   return disabledList;
 }
 
+QStringList QgsProjectParser::wfsLayers() const
+{
+  QStringList wfsList;
+  if ( !mXMLDoc )
+  {
+    return wfsList;
+  }
+
+  QDomElement qgisElem = mXMLDoc->documentElement();
+  if ( qgisElem.isNull() )
+  {
+    return wfsList;
+  }
+  QDomElement propertiesElem = qgisElem.firstChildElement( "properties" );
+  if ( propertiesElem.isNull() )
+  {
+    return wfsList;
+  }
+  QDomElement wfsLayersElem = propertiesElem.firstChildElement( "WFSLayers" );
+  if ( wfsLayersElem.isNull() )
+  {
+    return wfsList;
+  }
+  QDomNodeList valueList = wfsLayersElem.elementsByTagName( "value" );
+  for ( int i = 0; i < valueList.size(); ++i )
+  {
+    wfsList << valueList.at( i ).toElement().text();
+  }
+  return wfsList;
+}
+
 QStringList QgsProjectParser::supportedOutputCrsList() const
 {
   QStringList crsList;
diff --git a/src/mapserver/qgsprojectparser.h b/src/mapserver/qgsprojectparser.h
index 6f44a66..6064e28 100644
--- a/src/mapserver/qgsprojectparser.h
+++ b/src/mapserver/qgsprojectparser.h
@@ -38,6 +38,8 @@ class QgsProjectParser: public QgsConfigParser
     /**Adds layer and style specific capabilities elements to the parent node. This includes the individual layers and styles, their description, native CRS, bounding boxes, etc.*/
     virtual void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
 
+    virtual void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const;
+
     int numberOfLayers() const;
 
     /**Returns one or possibly several maplayers for a given layer name and style. If no layers/style are found, an empty list is returned*/
@@ -58,6 +60,9 @@ class QgsProjectParser: public QgsConfigParser
     /**Returns an ID-list of layers which are not queryable (comes from <properties> -> <Identify> -> <disabledLayers in the project file*/
     virtual QStringList identifyDisabledLayers() const;
 
+    /**Returns an ID-list of layers queryable for WFS service (comes from <properties> -> <WFSLayers> in the project file*/
+    virtual QStringList wfsLayers() const;
+
     /**Returns a set of supported epsg codes for the capabilities document. The list comes from the property <WMSEpsgList> in the project file.
        An empty set means that all possible CRS should be advertised (which could result in very long capabilities documents)
        Example:
diff --git a/src/mapserver/qgsrequesthandler.h b/src/mapserver/qgsrequesthandler.h
index a4aee30..b5381be 100644
--- a/src/mapserver/qgsrequesthandler.h
+++ b/src/mapserver/qgsrequesthandler.h
@@ -41,6 +41,9 @@ class QgsRequestHandler
     virtual void sendServiceException( const QgsMapServiceException& ex ) const = 0;
     virtual void sendGetStyleResponse( const QDomDocument& doc ) const = 0;
     virtual void sendGetPrintResponse( QByteArray* ba ) const = 0;
+    virtual bool startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const = 0;
+    virtual void sendGetFeatureResponse( QByteArray* ba ) const = 0;
+    virtual void endGetFeatureResponse( QByteArray* ba ) const = 0;
     QString format() const { return mFormat; }
   protected:
     /**This is set by the parseInput methods of the subclasses (parameter FORMAT, e.g. 'FORMAT=PNG')*/
diff --git a/src/mapserver/qgssldparser.h b/src/mapserver/qgssldparser.h
index 7034efd..fbb9cd5 100644
--- a/src/mapserver/qgssldparser.h
+++ b/src/mapserver/qgssldparser.h
@@ -56,6 +56,8 @@ class QgsSLDParser: public QgsConfigParser
     /**Adds layer and style specific capabilities elements to the parent node. This includes the individual layers and styles, their description, native CRS, bounding boxes, etc.*/
     void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
 
+    void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const {};
+
     /**Returns number of layers in configuration*/
     int numberOfLayers() const;
 
diff --git a/src/mapserver/qgswfsserver.cpp b/src/mapserver/qgswfsserver.cpp
new file mode 100644
index 0000000..8cae69a
--- /dev/null
+++ b/src/mapserver/qgswfsserver.cpp
@@ -0,0 +1,938 @@
+#include "qgswfsserver.h"
+#include "qgsconfigparser.h"
+#include "qgscrscache.h"
+#include "qgsfield.h"
+#include "qgsgeometry.h"
+#include "qgsmaplayer.h"
+#include "qgsmaplayerregistry.h"
+#include "qgsmaprenderer.h"
+#include "qgsmaptopixel.h"
+#include "qgspallabeling.h"
+#include "qgsproject.h"
+#include "qgsrasterlayer.h"
+#include "qgsscalecalculator.h"
+#include "qgscoordinatereferencesystem.h"
+#include "qgsvectordataprovider.h"
+#include "qgsvectorlayer.h"
+#include "qgsfilter.h"
+#include "qgslogger.h"
+#include "qgsmapserviceexception.h"
+#include "qgssldparser.h"
+#include "qgssymbol.h"
+#include "qgssymbolv2.h"
+#include "qgsrenderer.h"
+#include "qgslegendmodel.h"
+#include "qgscomposerlegenditem.h"
+#include "qgslogger.h"
+#include "qgsrequesthandler.h"
+#include <QImage>
+#include <QPainter>
+#include <QStringList>
+#include <QTextStream>
+#include <QDir>
+
+//for printing
+#include "qgscomposition.h"
+#include <QBuffer>
+#include <QPrinter>
+#include <QSvgGenerator>
+#include <QUrl>
+#include <QPaintEngine>
+
+QgsWFSServer::QgsWFSServer( QMap<QString, QString> parameters )
+    : mParameterMap( parameters )
+    , mConfigParser( 0 )
+{
+}
+
+QgsWFSServer::~QgsWFSServer()
+{
+}
+
+QgsWFSServer::QgsWFSServer()
+{
+}
+
+QDomDocument QgsWFSServer::getCapabilities()
+{
+  QgsDebugMsg( "Entering." );
+  QDomDocument doc;
+  //wfs:WFS_Capabilities element
+  QDomElement wfsCapabilitiesElement = doc.createElement( "WFS_Capabilities"/*wms:WFS_Capabilities*/ );
+  wfsCapabilitiesElement.setAttribute( "xmlns", "http://www.opengis.net/wfs" );
+  wfsCapabilitiesElement.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
+  wfsCapabilitiesElement.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
+  wfsCapabilitiesElement.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
+  wfsCapabilitiesElement.setAttribute( "xmlns:gml", "http://www.opengis.net/gml" );
+  wfsCapabilitiesElement.setAttribute( "xmlns:ows", "http://www.opengis.net/ows" );
+  wfsCapabilitiesElement.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
+  wfsCapabilitiesElement.setAttribute( "version", "1.0.0" );
+  wfsCapabilitiesElement.setAttribute( "updateSequence", "0" );
+  doc.appendChild( wfsCapabilitiesElement );
+
+  if ( mConfigParser )
+  {
+    mConfigParser->serviceCapabilities( wfsCapabilitiesElement, doc );
+  }
+
+  //wfs:Capability element
+  QDomElement capabilityElement = doc.createElement( "Capability"/*wfs:Capability*/ );
+  wfsCapabilitiesElement.appendChild( capabilityElement );
+  
+  //wfs:Request element
+  QDomElement requestElement = doc.createElement( "Request"/*wfs:Request*/ );
+  capabilityElement.appendChild( requestElement );
+  //wfs:GetCapabilities
+  QDomElement getCapabilitiesElement = doc.createElement( "GetCapabilities"/*wfs:GetCapabilities*/ );
+  requestElement.appendChild( getCapabilitiesElement );
+  QDomElement capabilitiesFormatElement = doc.createElement( "Format" );/*wfs:Format*/
+  getCapabilitiesElement.appendChild( capabilitiesFormatElement );
+  QDomText capabilitiesFormatText = doc.createTextNode( "text/xml" );
+  capabilitiesFormatElement.appendChild( capabilitiesFormatText );
+
+  QDomElement dcpTypeElement = doc.createElement( "DCPType"/*wfs:DCPType*/ );
+  getCapabilitiesElement.appendChild( dcpTypeElement );
+  QDomElement httpElement = doc.createElement( "HTTP"/*wfs:HTTP*/ );
+  dcpTypeElement.appendChild( httpElement );
+
+  //Prepare url
+  //Some client requests already have http://<SERVER_NAME> in the REQUEST_URI variable
+  QString hrefString;
+  QString requestUrl = getenv( "REQUEST_URI" );
+  QUrl mapUrl( requestUrl );
+  mapUrl.setHost( QString( getenv( "SERVER_NAME" ) ) );
+  mapUrl.removeQueryItem( "REQUEST" );
+  mapUrl.removeQueryItem( "VERSION" );
+  mapUrl.removeQueryItem( "SERVICE" );
+  hrefString = mapUrl.toString();
+
+  //only Get supported for the moment
+  QDomElement getElement = doc.createElement( "Get"/*wfs:Get*/ );
+  httpElement.appendChild( getElement );
+  QDomElement olResourceElement = doc.createElement( "OnlineResource"/*wfs:OnlineResource*/ );
+  olResourceElement.setAttribute( "xlink:type", "simple" );
+  requestUrl.truncate( requestUrl.indexOf( "?" ) + 1 );
+  olResourceElement.setAttribute( "xlink:href", hrefString );
+  getElement.appendChild( olResourceElement );
+
+  //wfs:DescribeFeatureType
+  QDomElement describeFeatureTypeElement = doc.createElement( "DescribeFeatureType"/*wfs:DescribeFeatureType*/ );
+  requestElement.appendChild( describeFeatureTypeElement );
+  QDomElement schemaDescriptionLanguageElement = doc.createElement( "SchemaDescriptionLanguage"/*wfs:SchemaDescriptionLanguage*/ );
+  describeFeatureTypeElement.appendChild( schemaDescriptionLanguageElement );
+  QDomElement xmlSchemaElement = doc.createElement( "XMLSCHEMA"/*wfs:XMLSCHEMA*/ );
+  schemaDescriptionLanguageElement.appendChild( xmlSchemaElement );
+  QDomElement describeFeatureTypeDhcTypeElement = dcpTypeElement.cloneNode().toElement();//this is the same as for 'GetCapabilities'
+  describeFeatureTypeElement.appendChild( describeFeatureTypeDhcTypeElement );
+
+  //wfs:GetFeature
+  QDomElement getFeatureElement = doc.createElement( "GetFeature"/*wfs:GetFeature*/ );
+  requestElement.appendChild( getFeatureElement );
+  QDomElement getFeatureFormatElement = doc.createElement( "ResultFormat" );/*wfs:ResultFormat*/
+  getFeatureElement.appendChild( getFeatureFormatElement );
+  QDomElement gmlFormatElement = doc.createElement( "GML2" );/*wfs:GML2*/
+  getFeatureFormatElement.appendChild( gmlFormatElement );
+  QDomElement geojsonFormatElement = doc.createElement( "GeoJSON" );/*wfs:GeoJSON*/
+  getFeatureFormatElement.appendChild( geojsonFormatElement );
+  QDomElement getFeatureDhcTypeElement = dcpTypeElement.cloneNode().toElement();//this is the same as for 'GetCapabilities'
+  getFeatureElement.appendChild( getFeatureDhcTypeElement );
+
+  //wfs:FeatureTypeList element
+  QDomElement featureTypeListElement = doc.createElement( "FeatureTypeList"/*wfs:FeatureTypeList*/ );
+  capabilityElement.appendChild( featureTypeListElement );
+  //wfs:Operations element
+  QDomElement operationsElement = doc.createElement( "Operations"/*wfs:Operations*/ );
+  featureTypeListElement.appendChild( operationsElement );
+  //wfs:Query element
+  QDomElement queryElement = doc.createElement( "Query"/*wfs:Query*/ );
+  operationsElement.appendChild( queryElement );
+  /*
+   * Adding layer liste in featureTypeListElement
+   */
+  if ( mConfigParser )
+  {
+    mConfigParser->featureTypeList( featureTypeListElement, doc );
+  }
+
+  /*
+   * Adding ogc:Filter_Capabilities in capabilityElement
+   */
+  //ogc:Filter_Capabilities element
+  QDomElement filterCapabilitiesElement = doc.createElement( "ogc:Filter_Capabilities"/*ogc:Filter_Capabilities*/ );
+  capabilityElement.appendChild( filterCapabilitiesElement );
+  QDomElement spatialCapabilitiesElement = doc.createElement( "ogc:Spatial_Capabilities"/*ogc:Spatial_Capabilities*/ );
+  filterCapabilitiesElement.appendChild( spatialCapabilitiesElement );
+  QDomElement spatialOperatorsElement = doc.createElement( "ogc:Spatial_Operators"/*ogc:Spatial_Operators*/ );
+  spatialCapabilitiesElement.appendChild( spatialOperatorsElement );
+  QDomElement ogcBboxElement = doc.createElement( "ogc:BBOX"/*ogc:BBOX*/ );
+  spatialOperatorsElement.appendChild( ogcBboxElement );
+  QDomElement scalarCapabilitiesElement = doc.createElement( "ogc:Scalar_Capabilities"/*ogc:Scalar_Capabilities*/ );
+  filterCapabilitiesElement.appendChild( scalarCapabilitiesElement );
+  QDomElement comparisonOperatorsElement = doc.createElement( "ogc:Comparison_Operators"/*ogc:Comparison_Operators*/ );
+  scalarCapabilitiesElement.appendChild( comparisonOperatorsElement );
+  QDomElement simpleComparisonsElement = doc.createElement( "ogc:Simple_Comparisons"/*ogc:Simple_Comparisons*/ );
+  comparisonOperatorsElement.appendChild( simpleComparisonsElement );
+  return doc;
+}
+
+QDomDocument QgsWFSServer::describeFeatureType()
+{
+  QgsDebugMsg( "Entering." );
+  QDomDocument doc;
+  //xsd:schema
+  QDomElement schemaElement = doc.createElement( "schema"/*xsd:schema*/ );
+  schemaElement.setAttribute( "xmlns", "http://www.w3.org/2001/XMLSchema" );
+  schemaElement.setAttribute( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema" );
+  schemaElement.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
+  schemaElement.setAttribute( "xmlns:gml", "http://www.opengis.net/gml" );
+  schemaElement.setAttribute( "xmlns:qgs", "http://www.qgis.org/gml" );
+  schemaElement.setAttribute( "targetNamespace", "http://www.qgis.org/gml" );
+  doc.appendChild( schemaElement );
+
+  //xsd:import
+  QDomElement importElement = doc.createElement( "import"/*xsd:import*/ );
+  importElement.setAttribute( "namespace", "http://www.opengis.net/gml" );
+  importElement.setAttribute( "schemaLocation", "http://schemas.opengis.net/gml/2.1.2/feature.xsd" );
+  schemaElement.appendChild( importElement );
+
+  //read TYPENAME
+  QString typeName;
+  QMap<QString, QString>::const_iterator type_name_it = mParameterMap.find( "TYPENAME" );
+  if ( type_name_it != mParameterMap.end() )
+  {
+    typeName = type_name_it.value();
+  }
+  else
+  {
+    return doc;
+  }
+
+  QStringList wfsLayersId = mConfigParser->wfsLayers();
+  QMap< QString, QMap< int, QString > > aliasInfo = mConfigParser->layerAliasInfo();
+  QMap< QString, QSet<QString> > hiddenAttributes = mConfigParser->hiddenAttributes();
+  
+  QList<QgsMapLayer*> layerList;
+  QgsMapLayer* currentLayer = 0;
+
+  layerList = mConfigParser->mapLayerFromStyle( typeName, "" );
+  currentLayer = layerList.at( 0 );
+  
+  QgsVectorLayer* layer = dynamic_cast<QgsVectorLayer*>( currentLayer );
+  if ( layer && wfsLayersId.contains( layer->id() ) )
+  {
+    //is there alias info for this vector layer?
+    QMap< int, QString > layerAliasInfo;
+    QMap< QString, QMap< int, QString > >::const_iterator aliasIt = aliasInfo.find( currentLayer->id() );
+    if ( aliasIt != aliasInfo.constEnd() )
+    {
+      layerAliasInfo = aliasIt.value();
+    }
+
+    //hidden attributes for this layer
+    QSet<QString> layerHiddenAttributes;
+    QMap< QString, QSet<QString> >::const_iterator hiddenIt = hiddenAttributes.find( currentLayer->id() );
+    if ( hiddenIt != hiddenAttributes.constEnd() )
+    {
+      layerHiddenAttributes = hiddenIt.value();
+    }
+    
+    //do a select with searchRect and go through all the features
+    QgsVectorDataProvider* provider = layer->dataProvider();
+    if ( !provider )
+    {
+      return doc;
+    }
+
+    typeName = typeName.replace( QString(" "), QString("_") );
+    
+    //xsd:element
+    QDomElement elementElem = doc.createElement( "element"/*xsd:element*/ );
+    elementElem.setAttribute( "name", typeName );
+    elementElem.setAttribute( "type", "qgs:" + typeName + "Type" );
+    elementElem.setAttribute( "substitutionGroup", "gml:_Feature" );
+    schemaElement.appendChild( elementElem );
+
+    //xsd:complexType
+    QDomElement complexTypeElem = doc.createElement( "complexType"/*xsd:complexType*/ );
+    complexTypeElem.setAttribute( "name", typeName + "Type" );
+    schemaElement.appendChild( complexTypeElem );
+
+    //xsd:complexType
+    QDomElement complexContentElem = doc.createElement( "complexContent"/*xsd:complexContent*/ );
+    complexTypeElem.appendChild( complexContentElem );
+
+    //xsd:extension
+    QDomElement extensionElem = doc.createElement( "extension"/*xsd:extension*/ );
+    extensionElem.setAttribute( "base", "gml:AbstractFeatureType" );
+    complexContentElem.appendChild( extensionElem );
+
+    //xsd:sequence
+    QDomElement sequenceElem = doc.createElement( "sequence"/*xsd:sequence*/ );
+    extensionElem.appendChild( sequenceElem );
+
+    //xsd:element
+    QDomElement geomElem = doc.createElement( "element"/*xsd:element*/ );
+    geomElem.setAttribute( "name", "geometry" );
+    geomElem.setAttribute( "type", "gml:GeometryPropertyType" );
+    geomElem.setAttribute( "minOccurs", "0" );
+    geomElem.setAttribute( "maxOccurs", "1" );
+    sequenceElem.appendChild( geomElem );
+
+    const QgsFieldMap& fields = provider->fields();
+    for ( QgsFieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it )
+    {
+
+      QString attributeName = it.value().name();
+      //skip attribute if it has edit type 'hidden'
+      if ( layerHiddenAttributes.contains( attributeName ) )
+      {
+        continue;
+      }
+
+      //xsd:element
+      QDomElement geomElem = doc.createElement( "element"/*xsd:element*/ );
+      geomElem.setAttribute( "name", attributeName );
+      if ( it.value().type() == 2 )
+        geomElem.setAttribute( "type", "integer" );
+      else if ( it.value().type() == 6 )
+        geomElem.setAttribute( "type", "double" );
+      else
+        geomElem.setAttribute( "type", "string" );
+
+      sequenceElem.appendChild( geomElem );
+
+      //check if the attribute name should be replaced with an alias
+      QMap<int, QString>::const_iterator aliasIt = layerAliasInfo.find( it.key() );
+      if ( aliasIt != layerAliasInfo.constEnd() )
+      {
+        geomElem.setAttribute( "alias", aliasIt.value() );
+      }
+
+    }
+  }
+
+  return doc;
+}
+
+int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format )
+{
+  QgsDebugMsg( "Info format is:" + infoFormat );
+
+  //read TYPENAME
+  QMap<QString, QString>::const_iterator type_name_it = mParameterMap.find( "TYPENAME" );
+  if ( type_name_it != mParameterMap.end() )
+  {
+    mTypeName = type_name_it.value();
+  }
+  else
+  {
+    return 1;
+  }
+
+  QStringList wfsLayersId = mConfigParser->wfsLayers();
+  QMap< QString, QMap< int, QString > > aliasInfo = mConfigParser->layerAliasInfo();
+  QMap< QString, QSet<QString> > hiddenAttributes = mConfigParser->hiddenAttributes();
+  
+  QList<QgsMapLayer*> layerList;
+  QgsMapLayer* currentLayer = 0;
+
+  layerList = mConfigParser->mapLayerFromStyle( mTypeName, "" );
+  currentLayer = layerList.at( 0 );
+  
+  QgsVectorLayer* layer = dynamic_cast<QgsVectorLayer*>( currentLayer );
+  if ( layer && wfsLayersId.contains( layer->id() ) )
+  {
+    //is there alias info for this vector layer?
+    QMap< int, QString > layerAliasInfo;
+    QMap< QString, QMap< int, QString > >::const_iterator aliasIt = aliasInfo.find( currentLayer->id() );
+    if ( aliasIt != aliasInfo.constEnd() )
+    {
+      layerAliasInfo = aliasIt.value();
+    }
+
+    //hidden attributes for this layer
+    QSet<QString> layerHiddenAttributes;
+    QMap< QString, QSet<QString> >::const_iterator hiddenIt = hiddenAttributes.find( currentLayer->id() );
+    if ( hiddenIt != hiddenAttributes.constEnd() )
+    {
+      layerHiddenAttributes = hiddenIt.value();
+    }
+    
+    //do a select with searchRect and go through all the features
+    QgsVectorDataProvider* provider = layer->dataProvider();
+    if ( !provider )
+    {
+      return 2;
+    }
+
+    QgsFeature feature;
+    QgsAttributeMap featureAttributes;
+    const QgsFieldMap& fields = provider->fields();
+
+    //map extent
+    QgsRectangle searchRect = layer->extent();
+
+    //read FEATUREDID
+    bool fidOk = false;
+    QString fid;
+    QMap<QString, QString>::const_iterator fidIt = mParameterMap.find( "FEATUREID" );
+    if ( fidIt != mParameterMap.end() ) {
+      fidOk = true;
+      fid = fidIt.value();
+    }
+
+    //read FILTER
+    bool filterOk = false;
+    QDomDocument filter;
+    QMap<QString, QString>::const_iterator filterIt = mParameterMap.find( "FILTER" );
+    if ( filterIt != mParameterMap.end() ) {
+      try {
+      QString errorMsg;
+      if ( !filter.setContent( filterIt.value(), true, &errorMsg ) )
+      {
+        QgsDebugMsg( "soap request parse error" );
+        QgsDebugMsg( "error message: " + errorMsg );
+        QgsDebugMsg( "the xml string was:" );
+        QgsDebugMsg( filterIt.value() );
+      }
+      else 
+      {
+        filterOk = true;
+      }
+      } catch(QgsMapServiceException& e ) {
+        filterOk = false;
+      }
+    }
+
+
+    bool conversionSuccess;
+    double minx, miny, maxx, maxy;
+    bool bboxOk = false;
+    //read BBOX
+    QMap<QString, QString>::const_iterator bbIt = mParameterMap.find( "BBOX" );
+    if ( bbIt == mParameterMap.end() )
+    {
+      minx = 0; miny = 0; maxx = 0; maxy = 0;
+    }
+    else
+    {
+      bboxOk = true;
+      QString bbString = bbIt.value();
+      minx = bbString.section( ",", 0, 0 ).toDouble( &conversionSuccess );
+      if ( !conversionSuccess ) {bboxOk = false;}
+      miny = bbString.section( ",", 1, 1 ).toDouble( &conversionSuccess );
+      if ( !conversionSuccess ) {bboxOk = false;}
+      maxx = bbString.section( ",", 2, 2 ).toDouble( &conversionSuccess );
+      if ( !conversionSuccess ) {bboxOk = false;}
+      maxy = bbString.section( ",", 3, 3 ).toDouble( &conversionSuccess );
+      if ( !conversionSuccess ) {bboxOk = false;}
+    }
+
+    //read MAXFEATURES
+    long maxFeat = layer->featureCount();
+    long featureCounter = 0;
+    QMap<QString, QString>::const_iterator mfIt = mParameterMap.find( "MAXFEATURES" );
+    if ( mfIt != mParameterMap.end() )
+    {
+       QString mfString = mfIt.value();
+       bool mfOk;
+       maxFeat = mfString.toLong(&mfOk, 10);
+       if ( !mfOk ) { maxFeat = layer->featureCount(); }
+    }
+
+    //read PROPERTYNAME
+    mWithGeom = true;
+    QgsAttributeList attrIndexes = provider->attributeIndexes();
+    QMap<QString, QString>::const_iterator pnIt = mParameterMap.find( "PROPERTYNAME" );
+    if ( pnIt != mParameterMap.end() )
+    {
+       QStringList attrList = pnIt.value().split( "," );
+       if ( attrList.size() > 0 )
+       {
+         mWithGeom = false;
+         QStringList::const_iterator alstIt;
+         QList<int> idxList;
+         QMap<QString, int> fieldMap = provider->fieldNameMap();
+         QMap<QString, int>::const_iterator fieldIt;
+         QString fieldName;
+         for ( alstIt = attrList.begin(); alstIt != attrList.end(); ++alstIt )
+         {
+           fieldName = *alstIt;
+           fieldIt = fieldMap.find( fieldName );
+           if ( fieldIt != fieldMap.end() )
+           {
+             idxList.append( fieldIt.value() );
+           }
+           else if ( fieldName == "geometry" )
+           {
+             mWithGeom = true;
+           }
+         }
+         if ( idxList.size() > 0 || mWithGeom)
+         {
+           attrIndexes = idxList;
+         }
+         else
+         {
+           mWithGeom = true;
+         }
+       }
+    }
+    
+    QgsCoordinateReferenceSystem layerCrs = layer->crs();
+    
+    startGetFeature( request, format );
+
+    if ( fidOk )
+    {
+      provider->featureAtId(  fid.toInt(), feature, mWithGeom, attrIndexes );
+      sendGetFeature( request, format, &feature, 0, layerCrs, fields, layerHiddenAttributes);
+    }
+    else if ( filterOk )
+    {
+      provider->select( attrIndexes, searchRect, mWithGeom, true );
+      try {
+        QgsFilter* mFilter = QgsFilter::createFilterFromXml( filter.firstChild().toElement().firstChild().toElement(), layer );
+        while ( provider->nextFeature( feature ) && featureCounter < maxFeat )
+        {
+          if ( mFilter )
+          {
+            if ( mFilter->evaluate( feature ) )
+            {
+              sendGetFeature( request, format, &feature, featureCounter, layerCrs, fields, layerHiddenAttributes);
+              ++featureCounter;
+            }
+          }
+          else
+          {
+            sendGetFeature( request, format, &feature, featureCounter, layerCrs, fields, layerHiddenAttributes);
+            ++featureCounter;
+          }
+        }
+        delete mFilter;
+      } catch(QgsMapServiceException& e ) {
+        while ( provider->nextFeature( feature ) && featureCounter < maxFeat )
+        {
+          sendGetFeature( request, format, &feature, featureCounter, layerCrs, fields, layerHiddenAttributes);
+          ++featureCounter;
+        }
+      }
+    }
+    else
+    {
+      if ( bboxOk )
+        searchRect.set( minx, miny, maxx, maxy );
+      provider->select( attrIndexes, searchRect, mWithGeom, true );
+      while ( provider->nextFeature( feature ) && featureCounter < maxFeat )
+      {
+        sendGetFeature( request, format, &feature, featureCounter, layerCrs, fields, layerHiddenAttributes);
+        ++featureCounter;
+      }
+    }
+   
+    endGetFeature( request, format );
+  }
+  else
+  {
+    return 2;
+  }
+  return 0;
+}
+
+void QgsWFSServer::startGetFeature( QgsRequestHandler& request, const QString& format ) 
+{
+  QByteArray result;
+  QString fcString;
+  if ( format == "GeoJSON" )
+  {
+    fcString = "{\"type\": \"FeatureCollection\",\n";
+    fcString += " \"features\": [\n";
+    result = fcString.toUtf8();
+    request.startGetFeatureResponse(&result, format);
+  }
+  else
+  {
+    //wfs:FeatureCollection
+    fcString = "<wfs:FeatureCollection";
+    fcString += " xmlns=\"http://www.opengis.net/wfs\"";
+    fcString += " xmlns:wfs=\"http://www.opengis.net/wfs\"";
+    fcString += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
+    fcString += " xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd\"";
+    fcString += " xmlns:ogc=\"http://www.opengis.net/ogc\"";
+    fcString += " xmlns:gml=\"http://www.opengis.net/gml\"";
+    fcString += " xmlns:ows=\"http://www.opengis.net/ows\"";
+    fcString += " xmlns:xlink=\"http://www.w3.org/1999/xlink\"";
+    fcString += " xmlns:qgs=\"http://www.qgis.org/gml\"";
+    fcString += ">";
+    result = fcString.toUtf8();
+    request.startGetFeatureResponse(&result, format);
+  }
+  fcString = "";
+}
+
+void QgsWFSServer::sendGetFeature( QgsRequestHandler& request, const QString& format, QgsFeature* feat, int featIdx, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes) /*const*/
+{
+  QByteArray result;
+  if ( format == "GeoJSON" )
+  {
+    QString fcString;
+    if (featIdx == 0)
+      fcString += "  ";
+    else
+      fcString += " ,";
+    fcString += createFeatureGeoJSON(feat, crs, fields, hiddenAttributes);
+    fcString += "\n";
+
+    result = fcString.toUtf8();
+    request.sendGetFeatureResponse( &result );
+    fcString = "";
+  }
+  else
+  {
+    QDomDocument gmlDoc;
+    QDomElement featureElement = createFeatureElem(feat, gmlDoc, crs, fields, hiddenAttributes);
+    gmlDoc.appendChild(featureElement);
+
+    result = gmlDoc.toByteArray();
+    request.sendGetFeatureResponse( &result );
+    gmlDoc.removeChild(featureElement);
+  }
+}
+
+void QgsWFSServer::endGetFeature( QgsRequestHandler& request, const QString& format ) 
+{
+  QByteArray result;
+  QString fcString;
+  if ( format == "GeoJSON" )
+  {
+    fcString += " ]\n";
+    fcString += "}";
+
+    result = fcString.toUtf8();
+    request.endGetFeatureResponse( &result );
+    fcString = "";
+  }
+  else
+  {
+    fcString = "</wfs:FeatureCollection>";
+    result = fcString.toUtf8();
+    request.endGetFeatureResponse( &result );
+    fcString = "";
+  }
+}
+
+QString QgsWFSServer::createFeatureGeoJSON( QgsFeature* feat, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes) /*const*/
+{
+  QString fStr = "{\"type\": \"Feature\",\n";
+
+  fStr += "   \"id\": ";
+  fStr +=  QString::number( feat->id() );
+  fStr += ",\n";
+
+  QgsGeometry* geom = feat->geometry();
+  if ( geom && mWithGeom )
+  {
+    fStr += "  \"geometry\": ";
+    fStr += geom->exportToGeoJSON();
+    fStr += ",\n";
+  }
+
+  //read all attribute values from the feature
+  fStr += "   \"properties\": {\n";
+  QgsAttributeMap featureAttributes = feat->attributeMap();
+  int attributeCounter = 0;
+  for ( QgsAttributeMap::const_iterator it = featureAttributes.begin(); it != featureAttributes.end(); ++it )
+  {
+    QString attributeName = fields[it.key()].name();
+    //skip attribute if it has edit type 'hidden'
+    if ( hiddenAttributes.contains( attributeName ) )
+    {
+      continue;
+    }
+
+    if (attributeCounter == 0)
+      fStr += "    \"";
+    else
+      fStr += "   ,\"";
+    fStr += attributeName;
+    fStr += "\": ";
+    if (it->type() == 6 || it->type() == 2) 
+    {
+      fStr +=  it->toString();
+    }
+    else
+    {
+      fStr += "\"";
+      fStr +=  it->toString().replace( QString( "\"" ), QString( "\\\"" ) );
+      fStr += "\"";
+    }
+    fStr += "\n";
+    ++attributeCounter;
+  }
+
+  fStr += "   }\n";
+
+  fStr += "  }";
+
+  return fStr;
+}
+
+QDomElement QgsWFSServer::createFeatureElem( QgsFeature* feat, QDomDocument& doc, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes) /*const*/
+{
+  //gml:FeatureMember
+  QDomElement featureElement = doc.createElement( "gml:featureMember"/*wfs:FeatureMember*/ );
+
+  //qgs:%TYPENAME%
+  QDomElement typeNameElement = doc.createElement( "qgs:"+mTypeName.replace( QString(" "), QString("_") )/*qgs:%TYPENAME%*/ );
+  typeNameElement.setAttribute( "fid", QString::number( feat->id() ) );
+  featureElement.appendChild(typeNameElement);
+
+  if ( mWithGeom )
+  {
+    //add geometry column (as gml)
+    QDomElement geomElem = doc.createElement( "qgs:geometry" );
+    QDomElement gmlElem = createGeometryElem( feat->geometry(), doc );
+    if ( !gmlElem.isNull() )
+    {
+      if ( crs.isValid() )
+      {
+        gmlElem.setAttribute( "srsName", crs.authid() );
+      }
+      geomElem.appendChild( gmlElem );
+      typeNameElement.appendChild( geomElem );
+    }
+  }
+
+  //read all attribute values from the feature
+  QgsAttributeMap featureAttributes = feat->attributeMap();
+  for ( QgsAttributeMap::const_iterator it = featureAttributes.begin(); it != featureAttributes.end(); ++it )
+  {
+
+    QString attributeName = fields[it.key()].name();
+    //skip attribute if it has edit type 'hidden'
+    if ( hiddenAttributes.contains( attributeName ) )
+    {
+      continue;
+    }
+          
+    QDomElement fieldElem = doc.createElement( "qgs:"+attributeName.replace( QString(" "), QString("_") ) );
+    QDomText fieldText = doc.createTextNode( it->toString() );
+    fieldElem.appendChild( fieldText );
+    typeNameElement.appendChild( fieldElem );
+  }
+
+   return featureElement;
+}
+
+QDomElement QgsWFSServer::createGeometryElem( QgsGeometry* geom, QDomDocument& doc ) /*const*/
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement geomElement;
+
+  QString geomTypeName;
+  QGis::WkbType wkbType = geom->wkbType();
+  switch ( wkbType )
+  {
+    case QGis::WKBPoint:
+    case QGis::WKBPoint25D:
+      geomElement = createPointElem( geom, doc );
+      break;
+    case QGis::WKBMultiPoint:
+    case QGis::WKBMultiPoint25D:
+      geomElement = createMultiPointElem( geom, doc );
+      break;
+    case QGis::WKBLineString:
+    case QGis::WKBLineString25D:
+      geomElement = createLineStringElem( geom, doc );
+      break;
+    case QGis::WKBMultiLineString:
+    case QGis::WKBMultiLineString25D:
+      geomElement = createMultiLineStringElem( geom, doc );
+      break;
+    case QGis::WKBPolygon:
+    case QGis::WKBPolygon25D:
+      geomElement = createPolygonElem( geom, doc );
+      break;
+    case QGis::WKBMultiPolygon:
+    case QGis::WKBMultiPolygon25D:
+      geomElement = createMultiPolygonElem( geom, doc );
+      break;
+    default:
+      return QDomElement();
+  }
+  return geomElement;
+}
+
+QDomElement QgsWFSServer::createLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement lineStringElem = doc.createElement( "gml:LineString" );
+  QDomElement coordElem = createCoordinateElem( geom->asPolyline(), doc );
+  lineStringElem.appendChild( coordElem );
+  return lineStringElem;
+}
+
+QDomElement QgsWFSServer::createMultiLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement multiLineStringElem = doc.createElement( "gml:MultiLineString" );
+  QgsMultiPolyline multiline = geom->asMultiPolyline();
+
+  QgsMultiPolyline::const_iterator multiLineIt = multiline.constBegin();
+  for ( ; multiLineIt != multiline.constEnd(); ++multiLineIt )
+  {
+    QgsGeometry* lineGeom = QgsGeometry::fromPolyline( *multiLineIt );
+    if ( lineGeom )
+    {
+      QDomElement lineStringMemberElem = doc.createElement( "gml:lineStringMember" );
+      QDomElement lineElem = createLineStringElem( lineGeom, doc );
+      lineStringMemberElem.appendChild( lineElem );
+      multiLineStringElem.appendChild( lineStringMemberElem );
+    }
+    delete lineGeom;
+  }
+
+  return multiLineStringElem;
+}
+
+QDomElement QgsWFSServer::createPointElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement pointElem = doc.createElement( "gml:Point" );
+  QgsPoint p = geom->asPoint();
+  QVector<QgsPoint> v;
+  v.append( p );
+  QDomElement coordElem = createCoordinateElem( v, doc );
+  pointElem.appendChild( coordElem );
+  return pointElem;
+}
+
+QDomElement QgsWFSServer::createMultiPointElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement multiPointElem = doc.createElement( "gml:MultiPoint" );
+  QgsMultiPoint multiPoint = geom->asMultiPoint();
+
+  QgsMultiPoint::const_iterator multiPointIt = multiPoint.constBegin();
+  for ( ; multiPointIt != multiPoint.constEnd(); ++multiPointIt )
+  {
+    QgsGeometry* pointGeom = QgsGeometry::fromPoint( *multiPointIt );
+    if ( pointGeom )
+    {
+      QDomElement multiPointMemberElem = doc.createElement( "gml:pointMember" );
+      QDomElement pointElem = createPointElem( pointGeom, doc );
+      multiPointMemberElem.appendChild( pointElem );
+      multiPointElem.appendChild( multiPointMemberElem );
+    }
+  }
+  return multiPointElem;
+}
+
+QDomElement QgsWFSServer::createPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement polygonElem = doc.createElement( "gml:Polygon" );
+  QgsPolygon poly = geom->asPolygon();
+  for ( int i = 0; i < poly.size(); ++i )
+  {
+    QString boundaryName;
+    if ( i == 0 )
+    {
+      boundaryName = "outerBoundaryIs";
+    }
+    else
+    {
+      boundaryName = "innerBoundaryIs";
+    }
+    QDomElement boundaryElem = doc.createElementNS( "http://www.opengis.net/gml", boundaryName );
+    QDomElement ringElem = doc.createElement( "gml:LinearRing" );
+    QDomElement coordElem = createCoordinateElem( poly.at( i ), doc );
+    ringElem.appendChild( coordElem );
+    boundaryElem.appendChild( ringElem );
+    polygonElem.appendChild( boundaryElem );
+  }
+  return polygonElem;
+}
+
+QDomElement QgsWFSServer::createMultiPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+  QDomElement multiPolygonElem = doc.createElement( "gml:MultiPolygon" );
+  QgsMultiPolygon multipoly = geom->asMultiPolygon();
+
+  QgsMultiPolygon::const_iterator polyIt = multipoly.constBegin();
+  for ( ; polyIt != multipoly.constEnd(); ++polyIt )
+  {
+    QgsGeometry* polygonGeom = QgsGeometry::fromPolygon( *polyIt );
+    if ( polygonGeom )
+    {
+      QDomElement polygonMemberElem = doc.createElement( "gml:polygonMember" );
+      QDomElement polygonElem = createPolygonElem( polygonGeom, doc );
+      delete polygonGeom;
+      polygonMemberElem.appendChild( polygonElem );
+      multiPolygonElem.appendChild( polygonMemberElem );
+    }
+  }
+  return multiPolygonElem;
+}
+
+QDomElement QgsWFSServer::createCoordinateElem( const QVector<QgsPoint> points, QDomDocument& doc ) const
+{
+  QDomElement coordElem = doc.createElement( "gml:coordinates" );
+  coordElem.setAttribute( "cs", "," );
+  coordElem.setAttribute( "ts", " " );
+
+  //precision 4 for meters / feet, precision 8 for degrees
+  int precision = 8;
+  /*
+  if ( mSourceCRS.mapUnits() == QGis::Meters
+       || mSourceCRS.mapUnits() == QGis::Feet )
+  {
+    precision = 4;
+  }
+  */
+
+  QString coordString;
+  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
+  for ( ; pointIt != points.constEnd(); ++pointIt )
+  {
+    if ( pointIt != points.constBegin() )
+    {
+      coordString += " ";
+    }
+    coordString += QString::number( pointIt->x(), 'f', precision );
+    coordString += ",";
+    coordString += QString::number( pointIt->y(), 'f', precision );
+  }
+
+  QDomText coordText = doc.createTextNode( coordString );
+  coordElem.appendChild( coordText );
+  return coordElem;
+}
diff --git a/src/mapserver/qgswfsserver.h b/src/mapserver/qgswfsserver.h
new file mode 100644
index 0000000..79a7206
--- /dev/null
+++ b/src/mapserver/qgswfsserver.h
@@ -0,0 +1,92 @@
+
+#ifndef QGSWFSSERVER_H
+#define QGSWFSSERVER_H
+
+#include <QDomDocument>
+#include <QMap>
+#include <QString>
+#include <map>
+
+class QgsCoordinateReferenceSystem;
+class QgsComposerLayerItem;
+class QgsComposerLegendItem;
+class QgsComposition;
+class QgsMapLayer;
+class QgsMapRenderer;
+class QgsPoint;
+class QgsRasterLayer;
+class QgsConfigParser;
+class QgsVectorLayer;
+class QgsCoordinateReferenceSystem;
+class QgsField;
+class QgsFeature;
+class QgsGeometry;
+class QgsSymbol;
+class QgsRequestHandler;
+class QFile;
+class QFont;
+class QImage;
+class QPaintDevice;
+class QPainter;
+
+/**This class handles all the wms server requests. The parameters and values have to be passed in the form of
+a map<QString, QString>. This map is usually generated by a subclass of QgsWMSRequestHandler, which makes QgsWFSServer
+independent from any server side technology*/
+
+class QgsWFSServer
+{
+  public:
+    /**Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
+    QgsWFSServer( QMap<QString, QString> parameters );
+    ~QgsWFSServer();
+    /**Returns an XML file with the capabilities description (as described in the WFS specs)*/
+    QDomDocument getCapabilities();
+
+    /**Returns an XML file with the describe feature type (as described in the WFS specs)*/
+    QDomDocument describeFeatureType();
+
+    /**Creates a document that describes the result of the getFeature request.
+       @return 0 in case of success*/
+    int getFeature( QgsRequestHandler& request, const QString& format );
+    
+    /**Sets configuration parser for administration settings. Does not take ownership*/
+    void setAdminConfigParser( QgsConfigParser* parser ) { mConfigParser = parser; }
+
+  private:
+    /**Don't use the default constructor*/
+    QgsWFSServer();
+    
+    /**Map containing the WMS parameters*/
+    QMap<QString, QString> mParameterMap;
+    QgsConfigParser* mConfigParser;
+    QString mTypeName;
+    bool mWithGeom;
+
+  protected:
+
+    void startGetFeature( QgsRequestHandler& request, const QString& format );
+    void sendGetFeature( QgsRequestHandler& request, const QString& format, QgsFeature* feat, int featIdx, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes );
+    void endGetFeature( QgsRequestHandler& request, const QString& format );
+
+    //methods to write GeoJSON
+    QString createFeatureGeoJSON( QgsFeature* feat, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes ) /*const*/;
+
+    //methods to write GML2
+    QDomElement createFeatureElem( QgsFeature* feat, QDomDocument& doc, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes ) /*const*/;
+
+    QDomElement createGeometryElem( QgsGeometry* g, QDomDocument& doc ) /*const*/;
+    QDomElement createLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createMultiLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createMultiPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createMultiPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;
+
+    /**Create a GML coordinate string from a point list.
+      @param points list of data points
+      @param coordString out: GML coord string
+      @return 0 in case of success*/
+    QDomElement createCoordinateElem( const QVector<QgsPoint> points, QDomDocument& doc ) const;
+};
+
+#endif
diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui
index 55a531d..bd26d49 100644
--- a/src/ui/qgsprojectpropertiesbase.ui
+++ b/src/ui/qgsprojectpropertiesbase.ui
@@ -363,9 +363,9 @@
        </item>
       </layout>
      </widget>
-     <widget class="QWidget" name="tab">
+     <widget class="QWidget" name="tab4">
       <attribute name="title">
-       <string>WMS Server</string>
+       <string>OWS Server</string>
       </attribute>
       <layout class="QGridLayout" name="gridLayout_3">
        <item row="0" column="0" rowspan="2">
@@ -384,7 +384,7 @@
           </property>
           <layout class="QGridLayout" name="gridLayout_7">
            <item row="0" column="0" colspan="2">
-            <widget class="QGroupBox" name="grpWMSServiceCapabilities">
+            <widget class="QGroupBox" name="grpOWSServiceCapabilities">
              <property name="title">
               <string>Service Capabilitities</string>
              </property>
@@ -483,6 +483,12 @@
              </layout>
             </widget>
            </item>
+           <item row="1" column="0" colspan="2">
+            <widget class="QGroupBox" name="grpWMSCapabilities">
+             <property name="title">
+              <string>WMS Capabilitities</string>
+             </property>
+             <layout class="QGridLayout" name="gridLayout_7">
            <item row="1" column="0">
             <widget class="QGroupBox" name="grpWMSExt">
              <property name="title">
@@ -632,6 +638,32 @@
              </property>
             </widget>
            </item>
+             </layout>
+            </widget>
+           </item>
+           <item row="3" column="0" colspan="2">
+            <widget class="QGroupBox" name="grpWFSCapabilities">
+             <property name="title">
+              <string>WFS Capabilitities</string>
+             </property>
+             <layout class="QGridLayout" name="gridLayout_8">
+              <item row="0" column="0">
+               <widget class="QTableWidget" name="twWFSLayers">
+                <column>
+                 <property name="text">
+                  <string>Layer</string>
+                 </property>
+                </column>
+                <column>
+                 <property name="text">
+                  <string>Published</string>
+                 </property>
+                </column>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </item>
           </layout>
          </widget>
         </widget>
