Просмотр исходного кода

added webdav support utilizing qtwebdav-qnetworkmanager

Klaas Freitag 14 лет назад
Родитель
Сommit
4c002a127f

+ 15 - 1
src/CMakeLists.txt

@@ -1,8 +1,19 @@
 include_directories(${CMAKE_SOURCE_DIR}/src)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libqtwebdav)
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 include(${QT_USE_FILE})
 
+# webdav library
+set( qtwebdav_LIB_SRCS
+     libqtwebdav/qwebdav.cpp
+     libqtwebdav/qwebdav_url_info.cpp)
+add_library(qtwebdav_static STATIC ${qtwebdav_LIB_SRCS})
+target_link_libraries(qtwebdav_static ${QT_LIBRARIES})
+qt4_automoc(${qtwebdav_LIB_SRCS})
+
+# -------------------------------------------------------
+
 QT4_ADD_RESOURCES ( MIRALL_RC_SRC ../mirall.qrc)
 
 set(mirall_UI
@@ -39,8 +50,9 @@ mirall/owncloudwizard.cpp
 mirall/owncloudsetup.cpp
 mirall/owncloudinfo.cpp
 mirall/ownclouddircheck.cpp
-
+mirall/mirallwebdav.cpp
 )
+
 if(CSYNC_FOUND)
   set(mirall_SRCS
     ${mirall_SRCS}
@@ -56,6 +68,8 @@ target_link_libraries(mirall_static ${QT_LIBRARIES})
 
 add_executable(mirall main.cpp ${MIRALL_RC_SRC})
 target_link_libraries(mirall mirall_static)
+target_link_libraries(mirall qtwebdav_static)
+
 if(CSYNC_FOUND)
   target_link_libraries(mirall ${CSYNC_LIBRARY})
 endif(CSYNC_FOUND)

+ 1 - 0
src/libqtwebdav/QWebdav

@@ -0,0 +1 @@
+#include "qwebdav.h"

+ 1 - 0
src/libqtwebdav/QWebdavUrlInfo

@@ -0,0 +1 @@
+#include "qwebdav_url_info.h"

+ 9 - 0
src/libqtwebdav/README

@@ -0,0 +1,9 @@
+These files are third party and were taken from a Qt based
+webDAV implementation initially done by 
+Corentin Chary <corentin.chary@gmail.com>. Some changes 
+were done by Klaas Freitag <freitag@kde.org>
+
+The original code is hosted at 
+http://code.google.com/p/qtwebdav-qnetworkaccessmanager/
+
+

+ 328 - 0
src/libqtwebdav/qwebdav.cpp

@@ -0,0 +1,328 @@
+/* This file is part of QWebdav
+ *
+ * Copyright (C) 2009-2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <QDomDocument>
+#include <QDomNode>
+#include <QUrl>
+#include <QDebug>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QNetworkAccessManager>
+#include <QBuffer>
+#include <QStringList>
+#include <QAuthenticator>
+
+#include "qwebdav.h"
+#include "qwebdav_url_info.h"
+
+QWebdav::QWebdav (QObject *parent)
+  : QNetworkAccessManager(parent)
+{
+
+}
+
+QWebdav::~QWebdav ()
+{
+}
+
+void QWebdav::init(const QString & hostName, const QString& user, const QString& passwd )
+{
+  _host = hostName;
+  _user = user;
+  _passwd = passwd;
+
+  emitListInfo = false;
+  connect(this, SIGNAL(finished ( QNetworkReply* )),
+          this, SLOT(slotFinished ( QNetworkReply* )));
+
+  connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
+          SLOT(slotAuthenticate(QNetworkReply*, QAuthenticator*)));
+}
+
+void QWebdav::slotAuthenticate( QNetworkReply*, QAuthenticator *auth )
+{
+  qDebug() << "!!!! Authentication required!";
+  if( auth ) {
+    auth->setUser( _user );
+    auth->setPassword( _passwd );
+  }
+
+}
+
+QNetworkReply* QWebdav::list ( const QString & dir)
+{
+  QWebdav::PropNames query;
+  QStringList props;
+
+  props << "creationdate";
+  props << "getcontentlength";
+  props << "displayname";
+  props << "source";
+  props << "getcontentlanguage";
+  props << "getcontenttype";
+  props << "executable";
+  props << "getlastmodified";
+  props << "getetag";
+  props << "resourcetype";
+
+  query["DAV:"] = props;
+
+  return propfind(dir, query, 1);
+}
+
+QNetworkReply*
+QWebdav::search ( const QString & path, const QString & q )
+{
+  QByteArray query = "<?xml version=\"1.0\"?>\r\n";
+
+  query.append( "<D:searchrequest xmlns:D=\"DAV:\">\r\n" );
+  query.append( q.toUtf8() );
+  query.append( "</D:searchrequest>\r\n" );
+
+  QNetworkRequest req;
+  req.setUrl(path);
+
+  return davRequest("SEARCH", req);
+}
+
+QNetworkReply*
+QWebdav::put ( const QString & path, QIODevice * data )
+{
+  QNetworkRequest req;
+  req.setUrl(QUrl(path));
+
+  return davRequest("PUT", req, data);
+}
+
+QNetworkReply*
+QWebdav::put ( const QString & path, QByteArray & data )
+{
+  QBuffer buffer(&data);
+
+  return put(path, &buffer);
+}
+
+QNetworkReply*
+QWebdav::propfind ( const QString & path, const QWebdav::PropNames & props,
+		    int depth)
+{
+  QByteArray query;
+
+  query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
+  query += "<D:propfind xmlns:D=\"DAV:\" >";
+  query += "<D:prop>";
+  foreach (QString ns, props.keys())
+    {
+      foreach (const QString key, props[ns])
+	if (ns == "DAV:")
+	  query += "<D:" + key + "/>";
+	else
+      	  query += "<" + key + " xmlns=\"" + ns + "\"/>";
+    }
+  query += "</D:prop>";
+  query += "</D:propfind>";
+  return propfind(path, query, depth);
+}
+
+
+QNetworkReply*
+QWebdav::propfind( const QString & path, const QByteArray & query, int depth )
+{
+  QNetworkRequest req;
+
+  req.setUrl(QUrl(path));
+
+  QString value;
+
+  if (depth == 2)
+    value = "infinity";
+  else
+    value = QString("%1").arg(depth);
+  req.setRawHeader(QByteArray("Depth"), value.toUtf8());
+  return davRequest("PROPFIND", req, query);
+}
+
+QNetworkReply*
+QWebdav::proppatch ( const QString & path, const QWebdav::PropValues & props)
+{
+  QByteArray query;
+
+  query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
+  query += "<D:proppatch xmlns:D=\"DAV:\" >";
+  query += "<D:prop>";
+  foreach (QString ns, props.keys())
+    {
+      QMap < QString , QVariant >::const_iterator i;
+
+      for (i = props[ns].constBegin(); i != props[ns].constEnd(); ++i) {
+	if (ns == "DAV:") {
+	  query += "<D:" + i.key() + ">";
+	  query += i.value().toString();
+	  query += "</D:" + i.key() + ">" ;
+	} else {
+	  query += "<" + i.key() + " xmlns=\"" + ns + "\">";
+	  query += i.value().toString();
+	  query += "</" + i.key() + " xmlns=\"" + ns + "\"/>";
+	}
+      }
+    }
+  query += "</D:prop>";
+  query += "</D:propfind>";
+
+  return proppatch(path, query);
+}
+
+QNetworkReply*
+QWebdav::proppatch( const QString & path, const QByteArray & query)
+{
+  QNetworkRequest req;
+  req.setUrl(QUrl(path));
+
+  return davRequest("PROPPATCH", req, query);
+}
+
+void
+QWebdav::emitListInfos()
+{
+  QDomDocument multiResponse;
+  bool hasResponse = false;
+
+  multiResponse.setContent(buffer, true);
+
+  for ( QDomNode n = multiResponse.documentElement().firstChild();
+        !n.isNull(); n = n.nextSibling())
+    {
+      QDomElement thisResponse = n.toElement();
+
+      if (thisResponse.isNull())
+	continue;
+
+      QWebdavUrlInfo info(thisResponse);
+
+      if (!info.isValid())
+	continue;
+
+      hasResponse = true;
+      emit listInfo(info);
+    }
+}
+
+void
+QWebdav::slotFinished ( QNetworkReply * reply )
+{
+  qDebug() << "WebDAV finished: " << reply->error();
+  _lastError = reply->errorString();
+  _lastReplyCode = reply->error();
+
+  if( reply->error() != QNetworkReply::NoError ) {
+    qDebug() << "Network error: " << reply->errorString();
+  }
+  if (emitListInfo && reply->error() == QNetworkReply::NoError)
+    emitListInfos();
+  buffer.clear();
+  emitListInfo = false;
+
+  emit webdavFinished( reply );
+}
+
+
+void
+QWebdav::setupHeaders(QNetworkRequest & req, quint64 size)
+{
+  QUrl url( _host );
+  qDebug() << "Setting up host header: " << url.host().toUtf8();
+  req.setRawHeader(QByteArray("Host"), url.host().toUtf8());
+  req.setRawHeader(QByteArray("Connection"), QByteArray("Keep-Alive"));
+  if (size) {
+      req.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(size));
+      req.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/xml; charset=utf-8"));
+  }
+}
+
+QNetworkReply*
+QWebdav::davRequest(const QString & reqVerb,  QNetworkRequest & req, const QByteArray & data)
+{
+  QByteArray dataClone(data);
+  QBuffer buffer(&dataClone);
+  return davRequest(reqVerb, req, &buffer);
+}
+
+QNetworkReply*
+QWebdav::davRequest(const QString & reqVerb,  QNetworkRequest & req, QIODevice * data)
+{
+    setupHeaders(req, data ? data->size() : 0);
+    return sendCustomRequest(req, reqVerb.toUtf8(), data);
+}
+
+QNetworkReply*
+QWebdav::mkdir ( const QString & dir )
+{
+  QNetworkRequest req;
+  qDebug() << "Making dir " << dir;
+
+  req.setUrl( QUrl( dir ) );
+  return davRequest("MKCOL", req, 0);
+}
+
+QNetworkReply*
+QWebdav::copy ( const QString & oldname, const QString & newname, bool overwrite)
+{
+  QNetworkRequest req;
+
+  req.setUrl(QUrl(oldname));
+  req.setRawHeader(QByteArray("Destination"), newname.toUtf8());
+  req.setRawHeader(QByteArray("Depth"), QByteArray("infinity"));
+  req.setRawHeader(QByteArray("Overwrite"), QByteArray(overwrite ? "T" : "F"));
+  return davRequest("COPY", req);
+}
+
+QNetworkReply*
+QWebdav::rename ( const QString & oldname, const QString & newname, bool overwrite)
+{
+  return move(oldname, newname, overwrite);
+}
+
+QNetworkReply*
+QWebdav::move ( const QString & oldname, const QString & newname, bool overwrite)
+{
+  QNetworkRequest req;
+  req.setUrl(QUrl(oldname));
+
+  req.setRawHeader(QByteArray("Destination"), newname.toUtf8());
+  req.setRawHeader(QByteArray("Depth"), QByteArray("infinity"));
+  req.setRawHeader(QByteArray("Overwrite"), QByteArray(overwrite ? "T" : "F"));
+  return davRequest("MOVE", req);
+}
+
+QNetworkReply*
+QWebdav::rmdir ( const QString & dir )
+{
+  return remove(dir);
+}
+
+QNetworkReply*
+QWebdav::remove ( const QString & path )
+{
+  QNetworkRequest req;
+  req.setUrl(QUrl(path));
+  return davRequest("DELETE", req);
+}
+
+#include "qwebdav.moc"

+ 114 - 0
src/libqtwebdav/qwebdav.h

@@ -0,0 +1,114 @@
+/* This file is part of QWebdav
+ *
+ * Copyright (C) 2009-2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef QWEBDAV_H
+#define QWEBDAV_H
+
+#include <QNetworkAccessManager>
+#include <QUrlInfo>
+#include <QDateTime>
+#include <QDomNodeList>
+#include <QMap>
+#include <QNetworkReply>
+
+#include "qwebdav_export.h"
+
+class QWebdavUrlInfo;
+class QNetworkReply;
+
+/**
+ * @brief Main class used to handle the webdav protocol
+ */
+class QWEBDAV_EXPORT QWebdav : virtual public QNetworkAccessManager
+{
+  Q_OBJECT
+ public:
+  QWebdav (QObject * parent = 0 );
+  ~QWebdav ();
+
+  void init(const QString & hostName, const QString & user, const QString & passwd);
+
+
+
+  typedef QMap < QString, QMap < QString, QVariant > > PropValues;
+  typedef QMap < QString , QStringList > PropNames;
+
+  QNetworkReply* list ( const QString & dir = QString() );
+  QNetworkReply* search ( const QString & path, const QString & query );
+  QNetworkReply* put ( const QString & path, QIODevice *data );
+  QNetworkReply* put ( const QString & path, QByteArray & data );
+
+  QNetworkReply* mkcol ( const QString & dir );
+
+  QNetworkReply* mkdir ( const QString & dir );
+  QNetworkReply* copy ( const QString & oldname, const QString & newname,
+	     bool overwrite = false );
+  QNetworkReply* rename ( const QString & oldname, const QString & newname,
+	       bool overwrite = false );
+  QNetworkReply* move ( const QString & oldname, const QString & newname,
+	     bool overwrite = false );
+  QNetworkReply* rmdir ( const QString & dir );
+  QNetworkReply* remove ( const QString & path );
+
+  QNetworkReply* propfind ( const QString & path, const QByteArray & query, int depth = 0 );
+  QNetworkReply* propfind ( const QString & path, const QWebdav::PropNames & props,
+		 int depth = 0 );
+
+  QNetworkReply* proppatch ( const QString & path, const QWebdav::PropValues & props);
+  QNetworkReply* proppatch ( const QString & path, const QByteArray & query );
+
+  int setHost ( const QString &, quint16 );
+
+  /* TODO lock, unlock */
+ signals:
+  void listInfo ( const QWebdavUrlInfo & i );
+  void webdavFinished( QNetworkReply *reply );
+
+ private slots:
+  void slotFinished ( QNetworkReply * resp );
+  void slotAuthenticate( QNetworkReply*, QAuthenticator* );
+
+ private:
+
+  void emitListInfos();
+  void davParsePropstats( const QDomNodeList & propstat );
+  int codeFromResponse( const QString& response );
+  QDateTime parseDateTime( const QString& input, const QString& type );
+  QNetworkReply* davRequest(const QString & reqVerb,
+                 QNetworkRequest & req,
+                 const QByteArray & data = QByteArray());
+  QNetworkReply* davRequest(const QString & reqVerb,
+                 QNetworkRequest & req,
+		 QIODevice * data);
+  void setupHeaders(QNetworkRequest & req, quint64 size);
+
+ private:
+  Q_DISABLE_COPY(QWebdav);
+  bool emitListInfo;
+  QByteArray buffer;
+  QString _host;
+  QString _user;
+  QString _passwd;
+
+  QString _lastError;
+  QNetworkReply::NetworkError _lastReplyCode;
+};
+
+#endif // QWEBDAV_H

+ 36 - 0
src/libqtwebdav/qwebdav_export.h

@@ -0,0 +1,36 @@
+/* This file is part of QWebdav
+ *
+ * Copyright (C) 2009-2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef QWEBDAV_EXPORT_H
+#define QWEBDAV_EXPORT_H
+
+#include <QtCore/qglobal.h>
+
+#ifndef QWEBDAV_EXPORT
+# if defined(QWEBDAV_MAKEDLL)
+   /* We are building this library */
+#  define QWEBDAV_EXPORT Q_DECL_EXPORT
+# else
+   /* We are using this library */
+#  define QWEBDAV_EXPORT Q_DECL_IMPORT
+# endif
+#endif
+
+#endif

+ 283 - 0
src/libqtwebdav/qwebdav_url_info.cpp

@@ -0,0 +1,283 @@
+/* This file is part of QWebdav
+ *
+ * Copyright (C) 2009-2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <QUrl>
+#include <QDebug>
+
+#include "qwebdav_url_info.h"
+
+QWebdavUrlInfo::QWebdavUrlInfo()
+{
+}
+
+QWebdavUrlInfo::~QWebdavUrlInfo()
+{
+}
+
+QWebdavUrlInfo::QWebdavUrlInfo(const QDomElement & dom)
+{
+  QDomElement href = dom.namedItem( "href" ).toElement();
+
+  node_ = dom.cloneNode();
+
+  if ( !href.isNull() )
+    {
+      QString urlStr = QUrl::fromPercentEncoding(href.text().toUtf8());
+      QDomNodeList propstats = dom.elementsByTagName( "propstat" );
+      davParsePropstats( urlStr, propstats );
+    }
+}
+
+QWebdavUrlInfo::QWebdavUrlInfo (const QWebdavUrlInfo & wui)
+  : QUrlInfo(wui),
+    properties_(wui.properties_),
+    createdAt_(wui.createdAt_),
+    displayName_(wui.displayName_),
+    source_(wui.source_),
+    contentLanguage_(wui.contentLanguage_),
+    entityTag_(wui.entityTag_),
+    mimeType_(wui.mimeType_)
+{
+  node_ = wui.node_.cloneNode();
+}
+
+int
+QWebdavUrlInfo::codeFromResponse( const QString& response )
+{
+  int firstSpace = response.indexOf( ' ' );
+  int secondSpace = response.indexOf( ' ', firstSpace + 1 );
+  return response.mid( firstSpace + 1, secondSpace - firstSpace - 1 ).toInt();
+}
+
+QDateTime
+QWebdavUrlInfo::parseDateTime( const QString& input, const QString& type )
+{
+  QDateTime datetime;
+
+  if ( type == "dateTime.tz" )
+    datetime =  QDateTime::fromString( input, Qt::ISODate );
+  else if ( type == "dateTime.rfc1123" )
+    datetime = QDateTime::fromString( input );
+
+  if (!datetime.isNull())
+    return datetime;
+
+  datetime = QDateTime::fromString(input.left(19), "yyyy-MM-dd'T'hh:mm:ss");
+  if (!datetime.isNull())
+    return datetime;
+  datetime = QDateTime::fromString(input.mid(5, 20) , "d MMM yyyy hh:mm:ss");
+  if (!datetime.isNull())
+    return datetime;
+  QDate date;
+  QTime time;
+
+  date = QDate::fromString(input.mid(5, 11) , "d MMM yyyy");
+  time = QTime::fromString(input.mid(17, 8) , "hh:mm:ss");
+  return QDateTime(date, time);
+}
+
+void
+QWebdavUrlInfo::davParsePropstats( const QString & path,
+				   const QDomNodeList & propstats )
+{
+  QString mimeType;
+  bool foundExecutable = false;
+  bool isDirectory = false;
+
+  setName(path);
+
+  for ( int i = 0; i < propstats.count(); i++) {
+    QDomElement propstat = propstats.item(i).toElement();
+    QDomElement status = propstat.namedItem( "status" ).toElement();
+
+    if ( status.isNull() ) {
+      qDebug() << "Error, no status code in this propstat";
+      return;
+    }
+
+    int code = codeFromResponse( status.text() );
+
+    if (code == 404)
+      continue ;
+
+    QDomElement prop = propstat.namedItem( "prop" ).toElement();
+
+    if ( prop.isNull() ) {
+      qDebug() << "Error: no prop segment in this propstat.";
+      return;
+    }
+
+    for ( QDomNode n = prop.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+      QDomElement property = n.toElement();
+
+      if (property.isNull())
+        continue;
+
+      properties_[property.namespaceURI()][property.tagName()] = property.text();
+
+      if ( property.namespaceURI() != "DAV:" ) {
+	// break out - we're only interested in properties from the DAV namespace
+	continue;
+      }
+
+      if ( property.tagName() == "creationdate" )
+	setCreatedAt(parseDateTime( property.text(), property.attribute("dt") ));
+      else if ( property.tagName() == "getcontentlength" )
+        setSize(property.text().toULong());
+      else if ( property.tagName() == "displayname" )
+	setDisplayName(property.text());
+      else if ( property.tagName() == "source" )
+      {
+        QDomElement source;
+
+	source = property.namedItem( "link" ).toElement()
+	  .namedItem( "dst" ).toElement();
+
+        if ( !source.isNull() )
+          setSource(source.text());
+      }
+      else if ( property.tagName() == "getcontentlanguage" )
+	setContentLanguage(property.text());
+      else if ( property.tagName() == "getcontenttype" )
+	{
+	  if ( property.text() == "httpd/unix-directory" )
+	    isDirectory = true;
+	  else
+	    mimeType = property.text();
+	}
+      else if ( property.tagName() == "executable" )
+	{
+	  if ( property.text() == "T" )
+	    foundExecutable = true;
+	}
+      else if ( property.tagName() == "getlastmodified" )
+	setLastModified(parseDateTime( property.text(), property.attribute("dt") ));
+      else if ( property.tagName() == "getetag" )
+	setEntitytag(property.text());
+      else if ( property.tagName() == "resourcetype" )
+        {
+	  if ( !property.namedItem( "collection" ).toElement().isNull() )
+	    isDirectory = true;
+	}
+      else
+        qDebug() << "Found unknown webdav property: "
+		 << property.tagName() << property.text();
+    }
+  }
+  setDir(isDirectory);
+  setFile(!isDirectory);
+
+  if (isDirectory && !name().endsWith("/"))
+    setName(name() + "/");
+
+  if ( foundExecutable || isDirectory )
+    setPermissions(0700);
+  else
+    setPermissions(0600);
+
+  if ( !isDirectory && !mimeType.isEmpty() )
+    setMimeType(mimeType);
+}
+
+
+void
+QWebdavUrlInfo::setCreatedAt(const QDateTime & date)
+{
+  createdAt_ = date;
+}
+
+void
+QWebdavUrlInfo::setDisplayName(const QString & name)
+{
+  displayName_ = name;
+}
+
+void
+QWebdavUrlInfo::setSource(const QString & source)
+{
+  source_ = source;
+}
+
+void
+QWebdavUrlInfo::setContentLanguage(const QString & lang)
+{
+  contentLanguage_ = lang;
+}
+
+void
+QWebdavUrlInfo::setEntitytag(const QString & etag)
+{
+  entityTag_ = etag;
+}
+
+void
+QWebdavUrlInfo::setMimeType(const QString & mime)
+{
+  mimeType_ = mime;
+}
+
+
+QDateTime
+QWebdavUrlInfo::createdAt() const
+{
+  return createdAt_;
+}
+
+QString
+QWebdavUrlInfo::displayName() const
+{
+  return displayName_;
+}
+
+QString
+QWebdavUrlInfo::source() const
+{
+  return source_;
+}
+
+QString
+QWebdavUrlInfo::contentLanguage() const
+{
+  return contentLanguage_;
+}
+
+QString
+QWebdavUrlInfo::entityTag() const
+{
+  return entityTag_;
+}
+
+QString
+QWebdavUrlInfo::mimeType() const
+{
+  return mimeType_;
+}
+
+QDomElement
+QWebdavUrlInfo::propElement() const
+{
+  return node_.toElement();
+}
+
+const QWebdav::PropValues &
+QWebdavUrlInfo::properties() const
+{
+  return properties_;
+}

+ 76 - 0
src/libqtwebdav/qwebdav_url_info.h

@@ -0,0 +1,76 @@
+/* This file is part of QWebdav
+ *
+ * Copyright (C) 2009-2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef QWEBDAV_URL_INFO_H
+#define QWEBDAV_URL_INFO_H
+
+#include <QDomDocument>
+#include <QDomElement>
+#include <QDomNodeList>
+#include <QDateTime>
+#include <QUrlInfo>
+#include <QVariant>
+
+#include "qwebdav_export.h"
+#include "qwebdav.h"
+
+class QWEBDAV_EXPORT QWebdavUrlInfo : virtual public QUrlInfo
+{
+ public:
+  QWebdavUrlInfo ();
+  QWebdavUrlInfo ( const QDomElement & dom );
+  QWebdavUrlInfo ( const QWebdavUrlInfo & wui );
+
+  virtual ~QWebdavUrlInfo ();
+ private:
+  int codeFromResponse( const QString & response );
+  QDateTime parseDateTime( const QString& input, const QString& type );
+  void davParsePropstats(const QString & path, const QDomNodeList & propstat);
+
+ public:
+  void setCreatedAt(const QDateTime & date);
+  void setDisplayName(const QString & name);
+  void setSource(const QString & source);
+  void setContentLanguage(const QString & lang);
+  void setEntitytag(const QString & etag);
+  void setMimeType(const QString & mime);
+
+  QDateTime createdAt() const;
+  QString displayName() const;
+  QString source() const;
+  QString contentLanguage() const;
+  QString entityTag() const;
+  QString mimeType() const;
+
+  QDomElement propElement() const;
+  const QWebdav::PropValues & properties() const;
+ private:
+
+  QDomNode node_;
+  QWebdav::PropValues properties_;
+  QDateTime createdAt_;
+  QString displayName_;
+  QString source_;
+  QString contentLanguage_;
+  QString entityTag_;
+  QString mimeType_;
+};
+
+#endif /* QWEBDAV_URL_INFO_H */

+ 69 - 11
src/mirall/folderwizard.cpp

@@ -26,6 +26,8 @@
 #include "mirall/folderwizard.h"
 #include "mirall/owncloudinfo.h"
 #include "mirall/ownclouddircheck.h"
+#include "mirall/owncloudsetup.h"
+#include "mirall/mirallwebdav.h"
 
 
 namespace Mirall
@@ -48,6 +50,16 @@ FolderWizardSourcePage::~FolderWizardSourcePage()
 
 }
 
+void FolderWizardSourcePage::initializePage()
+{
+  _ui.warnLabel->hide();
+}
+
+void FolderWizardSourcePage::cleanupPage()
+{
+  _ui.warnLabel->hide();
+}
+
 bool FolderWizardSourcePage::isComplete() const
 {
   QFileInfo selFile( _ui.localFolderLineEdit->text() );
@@ -140,7 +152,7 @@ FolderWizardTargetPage::FolderWizardTargetPage()
   _warnWasVisible(false)
 {
     _ui.setupUi(this);
-    _ui.warnLabel->hide();
+    _ui.warnFrame->hide();
 
     registerField("local?",            _ui.localFolderRadioBtn);
     registerField("remote?",           _ui.urlFolderRadioBtn);
@@ -190,12 +202,49 @@ void FolderWizardTargetPage::slotDirCheckReply(const QString &url, bool exists )
   if( _dirChecked ) {
     _ui.warnLabel->hide();
   } else {
-    showWarn( tr("The folder is not available on your ownCloud. Please create it.") );
+    showWarn( tr("The folder is not available on your ownCloud.<br/>Click to let mirall create it."), true );
   }
 
   emit completeChanged();
 }
 
+void FolderWizardTargetPage::slotCreateRemoteFolder()
+{
+  _ui.OCFolderLineEdit->setEnabled( false );
+
+  const QString folder = _ui.OCFolderLineEdit->text();
+  if( folder.isEmpty() ) return;
+
+  OwncloudSetup ocSetup;
+  QString url = ocSetup.ownCloudUrl();
+  url.append( "files/webdav.php/");
+  url.append( folder );
+  qDebug() << "creating folder on ownCloud: " << url;
+
+  MirallWebDAV *webdav = new MirallWebDAV(this);
+  connect( webdav, SIGNAL(webdavFinished(QNetworkReply*)),
+           SLOT(slotCreateRemoteFolderFinished(QNetworkReply*)));
+
+  webdav->httpConnect( url, ocSetup.ownCloudUser(), ocSetup.ownCloudPasswd() );
+  if( webdav->mkdir(  url  ) ) {
+    qDebug() << "WebDAV mkdir request successfully started";
+  } else {
+    qDebug() << "WebDAV mkdir request failed";
+  }
+}
+
+void FolderWizardTargetPage::slotCreateRemoteFolderFinished( QNetworkReply *reply )
+{
+  qDebug() << "** webdav request finished";
+
+  _ui.OCFolderLineEdit->setEnabled( true );
+  if( reply->error() == QNetworkReply::NoError ) {
+    showWarn( tr("Folder on ownCloud was successfully created."), false );
+    slotTimerFires();
+  } else {
+    showWarn( tr("Failed to create the folder on ownCloud.<br/>Please check manually."), false );
+  }
+}
 
 FolderWizardTargetPage::~FolderWizardTargetPage()
 {
@@ -213,7 +262,7 @@ bool FolderWizardTargetPage::isComplete() const
       /* owncloud selected */
       QString dir = _ui.OCFolderLineEdit->text();
       if( dir.isEmpty() ) {
-        showWarn( tr("Better do not use the remote root directory.<br/>If you do, you can <b>not</b> mirror another local folder."));
+        showWarn( tr("Better do not use the remote root directory.<br/>If you do, you can <b>not</b> mirror another local folder."), false);
         return true;
       } else {
         if( _dirChecked ) {
@@ -225,14 +274,21 @@ bool FolderWizardTargetPage::isComplete() const
     return false;
 }
 
+void FolderWizardTargetPage::cleanupPage()
+{
+  _ui.warnFrame->hide();
+}
+
 void FolderWizardTargetPage::initializePage()
 {
     slotToggleItems();
+    _ui.warnFrame->hide();
 
     ownCloudInfo *ocInfo = new ownCloudInfo( this );
     if( ocInfo->isConfigured() ) {
       connect(ocInfo, SIGNAL(ownCloudInfoFound(QString,QString)),SLOT(slotOwnCloudFound(QString,QString)));
       connect(ocInfo,SIGNAL(noOwncloudFound()),SLOT(slotNoOwnCloudFound()));
+      connect(_ui._buttCreateFolder, SIGNAL(clicked()), SLOT(slotCreateRemoteFolder()));
       ocInfo->checkInstallation();
 
     } else {
@@ -256,12 +312,14 @@ void FolderWizardTargetPage::slotNoOwnCloudFound()
   _ui.OCFolderLineEdit->setEnabled( false );
 }
 
-void FolderWizardTargetPage::showWarn( const QString& msg ) const
+void FolderWizardTargetPage::showWarn( const QString& msg, bool showCreateButton ) const
 {
+  _ui._buttCreateFolder->setVisible( showCreateButton );
+
   if( msg.isEmpty() ) {
-    _ui.warnLabel->hide();
+    _ui.warnFrame->hide();
   } else {
-    _ui.warnLabel->show();
+    _ui.warnFrame->show();
     _ui.warnLabel->setText( msg );
   }
 }
@@ -301,20 +359,20 @@ void FolderWizardTargetPage::slotToggleItems()
   _ui.localFolder2LineEdit->setEnabled(enabled);
   _ui.localFolder2ChooseBtn->setEnabled(enabled);
   if( enabled ) {
-    _warnWasVisible = _ui.warnLabel->isVisible();
-    _ui.warnLabel->hide();
+    _warnWasVisible = _ui.warnFrame->isVisible();
+    _ui.warnFrame->hide();
   }
 
   enabled = _ui.urlFolderRadioBtn->isChecked();
   _ui.urlFolderLineEdit->setEnabled(enabled);
   if( enabled ) {
-    _warnWasVisible = _ui.warnLabel->isVisible();
-    _ui.warnLabel->hide();
+    _warnWasVisible = _ui.warnFrame->isVisible();
+    _ui.warnFrame->hide();
   }
 
   enabled = _ui.OCRadioBtn->isChecked();
   _ui.OCFolderLineEdit->setEnabled(enabled);
-  if( enabled ) _ui.warnLabel->setVisible( _warnWasVisible );
+  if( enabled ) _ui.warnFrame->setVisible( _warnWasVisible );
 }
 
 void FolderWizardTargetPage::on_localFolder2ChooseBtn_clicked()

+ 9 - 1
src/mirall/folderwizard.h

@@ -43,6 +43,9 @@ public:
     ~FolderWizardSourcePage();
 
     virtual bool isComplete() const;
+    void initializePage();
+    void cleanupPage();
+
     void setFolderMap( Folder::Map *fm ) { _folderMap = fm; }
 protected slots:
     void on_localFolderChooseBtn_clicked();
@@ -68,6 +71,8 @@ public:
     virtual bool isComplete() const;
 
     virtual void initializePage();
+    virtual void cleanupPage();
+
 protected slots:
     void slotToggleItems();
     void on_localFolder2ChooseBtn_clicked();
@@ -85,7 +90,10 @@ protected slots:
     void slotFolderTextChanged( const QString& );
     void slotTimerFires();
     void slotDirCheckReply( const QString&, bool );
-    void showWarn( const QString& = QString() ) const;
+    void showWarn( const QString& = QString(), bool showCreateButton = false ) const;
+    void slotCreateRemoteFolder();
+    void slotCreateRemoteFolderFinished( QNetworkReply* );
+
 private:
     Ui_FolderWizardTargetPage _ui;
     QTimer *_timer;

+ 82 - 54
src/mirall/folderwizardtargetpage.ui

@@ -7,13 +7,13 @@
     <x>0</x>
     <y>0</y>
     <width>456</width>
-    <height>336</height>
+    <height>352</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Form</string>
   </property>
-  <layout class="QGridLayout" name="gridLayout_5">
+  <layout class="QGridLayout" name="gridLayout_6">
    <item row="0" column="0">
     <layout class="QHBoxLayout" name="horizontalLayout_12">
      <item>
@@ -50,20 +50,13 @@
      <property name="title">
       <string>Pick a place where the data should go to:</string>
      </property>
+     <property name="flat">
+      <bool>true</bool>
+     </property>
      <property name="checkable">
       <bool>false</bool>
      </property>
      <layout class="QGridLayout" name="gridLayout_4">
-      <item row="0" column="0">
-       <widget class="QRadioButton" name="OCRadioBtn">
-        <property name="text">
-         <string/>
-        </property>
-        <property name="checked">
-         <bool>true</bool>
-        </property>
-       </widget>
-      </item>
       <item row="0" column="1">
        <widget class="QFrame" name="frame">
         <property name="frameShape">
@@ -119,16 +112,6 @@
         </layout>
        </widget>
       </item>
-      <item row="1" column="0">
-       <widget class="QRadioButton" name="localFolderRadioBtn">
-        <property name="text">
-         <string/>
-        </property>
-        <property name="checked">
-         <bool>false</bool>
-        </property>
-       </widget>
-      </item>
       <item row="1" column="1">
        <widget class="QFrame" name="frame_2">
         <property name="frameShape">
@@ -200,13 +183,6 @@
         </layout>
        </widget>
       </item>
-      <item row="2" column="0">
-       <widget class="QRadioButton" name="urlFolderRadioBtn">
-        <property name="text">
-         <string/>
-        </property>
-       </widget>
-      </item>
       <item row="2" column="1">
        <widget class="QFrame" name="frame_3">
         <property name="frameShape">
@@ -274,18 +250,38 @@
         </layout>
        </widget>
       </item>
+      <item row="0" column="0">
+       <widget class="QRadioButton" name="OCRadioBtn">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QRadioButton" name="localFolderRadioBtn">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="checked">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QRadioButton" name="urlFolderRadioBtn">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
      </layout>
-     <zorder>frame</zorder>
-     <zorder>frame_2</zorder>
-     <zorder>frame_3</zorder>
-     <zorder>OCRadioBtn</zorder>
-     <zorder>label_5</zorder>
-     <zorder>localFolderRadioBtn</zorder>
-     <zorder>urlFolderRadioBtn</zorder>
     </widget>
    </item>
    <item row="2" column="0">
-    <widget class="QLabel" name="warnLabel">
+    <widget class="QFrame" name="warnFrame">
      <property name="palette">
       <palette>
        <active>
@@ -303,7 +299,7 @@
           <color alpha="255">
            <red>255</red>
            <green>255</green>
-           <blue>192</blue>
+           <blue>153</blue>
           </color>
          </brush>
         </colorrole>
@@ -323,7 +319,7 @@
           <color alpha="255">
            <red>255</red>
            <green>255</green>
-           <blue>192</blue>
+           <blue>153</blue>
           </color>
          </brush>
         </colorrole>
@@ -334,7 +330,7 @@
           <color alpha="255">
            <red>255</red>
            <green>255</green>
-           <blue>192</blue>
+           <blue>153</blue>
           </color>
          </brush>
         </colorrole>
@@ -343,7 +339,7 @@
           <color alpha="255">
            <red>255</red>
            <green>255</green>
-           <blue>192</blue>
+           <blue>153</blue>
           </color>
          </brush>
         </colorrole>
@@ -357,20 +353,52 @@
       <enum>QFrame::NoFrame</enum>
      </property>
      <property name="frameShadow">
-      <enum>QFrame::Plain</enum>
-     </property>
-     <property name="text">
-      <string>TextLabel</string>
-     </property>
-     <property name="textFormat">
-      <enum>Qt::AutoText</enum>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignCenter</set>
-     </property>
-     <property name="margin">
-      <number>3</number>
+      <enum>QFrame::Raised</enum>
      </property>
+     <layout class="QGridLayout" name="gridLayout_5">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="warnLabel">
+          <property name="autoFillBackground">
+           <bool>true</bool>
+          </property>
+          <property name="frameShape">
+           <enum>QFrame::NoFrame</enum>
+          </property>
+          <property name="frameShadow">
+           <enum>QFrame::Plain</enum>
+          </property>
+          <property name="text">
+           <string>TextLabel</string>
+          </property>
+          <property name="textFormat">
+           <enum>Qt::AutoText</enum>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignCenter</set>
+          </property>
+          <property name="margin">
+           <number>3</number>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="_buttCreateFolder">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>create</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
     </widget>
    </item>
    <item row="3" column="0">

+ 51 - 0
src/mirall/mirallwebdav.cpp

@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) by Klaas Freitag <freitag@kde.org>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ */
+
+#include "mirallwebdav.h"
+
+#include <QtCore>
+#include <QtNetwork>
+#include <QObject>
+
+MirallWebDAV::MirallWebDAV(QObject *parent) :
+    QObject(parent)
+{
+  _webdav = new QWebdav;
+  connect( _webdav, SIGNAL(webdavFinished(QNetworkReply*)), this,
+           SIGNAL(webdavFinished(QNetworkReply*)) );
+
+}
+
+bool MirallWebDAV::httpConnect( const QString& str, const QString& user, const QString& passwd)
+{
+  _host = str;
+  if( !_host.endsWith( "webdav.php")) {
+    _host.append( "/files/webdav.php");
+  }
+  _webdav->init( _host, user, passwd );
+}
+
+bool MirallWebDAV::mkdir( const QString& dir )
+{
+  bool re = true;
+
+  QNetworkReply *reply = _webdav->mkdir( dir );
+  if( reply->error() != QNetworkReply::NoError ) {
+    qDebug() << "WebDAV Mkdir failed.";
+    re = false;
+  }
+  return re;
+}
+
+#include "mirallwebdav.moc"

+ 47 - 0
src/mirall/mirallwebdav.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) by Klaas Freitag <freitag@kde.org>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ */
+#ifndef MIRALLWEBDAV_H
+#define MIRALLWEBDAV_H
+
+#include <QObject>
+
+#include <qwebdav.h>
+
+class MirallWebDAV : public QObject
+{
+    Q_OBJECT
+public:
+  explicit MirallWebDAV(QObject *parent = 0);
+
+  bool httpConnect( const QString& url, const QString&, const QString& );
+
+  bool mkdir( const QString& dir );
+
+protected:
+
+signals:
+  void webdavFinished( QNetworkReply* );
+
+public slots:
+
+private:
+  QString _host;
+  QString _user;
+  QString _passwd;
+  QString _error;
+
+  QWebdav *_webdav;
+};
+
+#endif // MIRALLWEBDAV_H

+ 17 - 12
src/mirall/owncloudsetup.cpp

@@ -20,6 +20,7 @@
 #include "owncloudsetup.h"
 #include "mirall/sitecopyconfig.h"
 #include "mirall/sitecopyfolder.h"
+#include "mirall/mirallwebdav.h"
 
 namespace Mirall {
 
@@ -289,23 +290,27 @@ void OwncloudSetup::setupLocalSyncFolder()
 
     if( fi.mkpath( syncFolder ) ) {
       QString targetPath = "mirall"; // Do NOT sync to root dir on ownCloud!
-      qDebug() << "Successfully created " << fi.path();
 
-      // Create a sitecopy config file
-      SitecopyConfig scConfig;
+      MirallWebDAV *webdav = new MirallWebDAV;
+      webdav->httpConnect( ownCloudUrl(), ownCloudUser(), ownCloudPasswd() );
+      if( webdav->mkdir( targetPath ) ) {
 
-      scConfig.writeSiteConfig( syncFolder, /* local path */
-                                "ownCloud", /* _folderWizard->field("OCSiteAlias").toString(),  site alias */
-                                ownCloudUrl(),
-                                ownCloudUser(),
-                                ownCloudPasswd(),
-                                targetPath );
+        qDebug() << "Successfully created " << fi.path();
 
-      // now there is the sitecopy config. A fetch in to the newly created folder mirrors
-      // the files from the ownCloud to local
-      startFetchFromOC( syncFolder );
+        // Create a sitecopy config file
+        SitecopyConfig scConfig;
 
+        scConfig.writeSiteConfig( syncFolder, /* local path */
+                                  "ownCloud", /* _folderWizard->field("OCSiteAlias").toString(),  site alias */
+                                  ownCloudUrl(),
+                                  ownCloudUser(),
+                                  ownCloudPasswd(),
+                                  targetPath );
 
+        // now there is the sitecopy config. A fetch in to the newly created folder mirrors
+        // the files from the ownCloud to local
+        startFetchFromOC( syncFolder );
+      }
     } else {
       qDebug() << "Failed to create " << fi.path();
     }

+ 0 - 0
src/mirallwebdav.cpp


+ 0 - 0
src/mirallwebdav.h