[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-bugs-dist
Subject:    Bug#36348: Preliminary WebDAV support
From:       Hamish Rodda <meddie () yoyo ! cc ! monash ! edu ! au>
Date:       2001-12-30 17:09:23
[Download RAW message or body]

I have attached a very rough patch (apply in the kdelibs/kio/http dir) which 
adds basic webdav functionality to kio_http.  This is mostly for developers 
to look at and provide feedback on, but it successfully lists collections 
(directories), moves, copies, deletes, uploads and downloads files. Lock 
support has only just begun, and error handling is nonexistant.

If you do try it out, don't forget to make -f Makefile.cvs first. Use 
webdav://hostname/ to access the ioslave.

I will probably reorganise this quite a bit before it is ready to be merged.

Hamish
["webdav-v0.1.patch" (text/x-diff)]

Index: Makefile.am
===================================================================
RCS file: /home/kde/kdelibs/kio/http/Makefile.am,v
retrieving revision 1.38
diff -u -3 -p -u -r1.38 Makefile.am
--- Makefile.am	2001/07/14 01:55:08	1.38
+++ Makefile.am	2001/12/30 17:22:33
@@ -27,7 +27,7 @@ kio_http_cache_cleaner_la_LDFLAGS = -mod
 noinst_HEADERS = http.h
 
 kdelnkdir = $(kde_servicesdir)
-kdelnk_DATA = http_cache_cleaner.desktop http.protocol https.protocol
+kdelnk_DATA = http_cache_cleaner.desktop http.protocol https.protocol \
webdav.protocol  
 dummy.cpp:
 	echo > dummy.cpp
Index: http.cc
===================================================================
RCS file: /home/kde/kdelibs/kio/http/http.cc,v
retrieving revision 1.429
diff -u -3 -p -u -r1.429 http.cc
--- http.cc	2001/12/28 22:14:03	1.429
+++ http.cc	2001/12/30 17:22:35
@@ -52,6 +52,9 @@
 
 #include <qregexp.h>
 #include <qfile.h>
+#include <qdom.h>
+#include <qdatetime.h>
+#include <qstringlist.h>
 
 #include <kapplication.h>
 #include <kurl.h>
@@ -158,6 +161,9 @@ HTTPProtocol::HTTPProtocol( const QCStri
 
   setMultipleAuthCaching( true );
   reparseConfiguration();
+  
+  monthList << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun" << "Jul"
+            << "Aug" << "Sep" << "Oct" << "Nov" << "Dec";
 }
 
 HTTPProtocol::~HTTPProtocol()
@@ -329,19 +335,22 @@ bool HTTPProtocol::checkRequestURL( cons
   return true;
 }
 
-void HTTPProtocol::retrieveContent()
+void HTTPProtocol::retrieveContent( bool dataInternal )
 {
-  if ( !retrieveHeader(false) )
+  if ( !retrieveHeader( false ) )
   {
     if ( m_bError ) { return; }
   }
   else
   {
-    if ( !readBody() && m_bError ) { return; }
+    if ( !readBody( dataInternal ) && m_bError ) { return; }
   }
 
   httpClose();
-  finished();
+  // if data is required internally, don't finish,
+  // it is processed before we finish()
+  if ( !dataInternal )
+    finished();
 }
 
 bool HTTPProtocol::retrieveHeader( bool close_connection )
@@ -411,26 +420,414 @@ void HTTPProtocol::stat(const KURL& url)
   if ( !checkRequestURL( url ) )
     return;
 
+  if ( m_protocol != "webdav" ) {
+    UDSEntry entry;
+    UDSAtom atom;
+    atom.m_uds = KIO::UDS_NAME;
+    atom.m_str = url.fileName();
+    entry.append( atom );
+
+    atom.m_uds = KIO::UDS_FILE_TYPE;
+    atom.m_long = S_IFREG; // a file
+    entry.append( atom );
+
+    atom.m_uds = KIO::UDS_ACCESS;
+    atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; // readable by everybody
+    entry.append( atom );
+
+    statEntry( entry );
+    finished();
+    return;
+  }
+
+  davStatList( url );
+}
+
+void HTTPProtocol::listDir( const KURL& url )
+{
+  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::listDir " << url.url()
+                << endl;
+
+  if ( !checkRequestURL( url ) )
+    return;
+
+  davStatList( url, false );
+}
+
+void HTTPProtocol::davStatList( const KURL& url, bool stat )
+{
   UDSEntry entry;
+
+  // WebDAV Stat or List...
+  m_request.method = DAV_PROPFIND;
+  m_request.path = url.path();
+  m_request.query = QString::null;
+  m_request.cache = CC_Reload;
+  m_request.doProxy = m_bUseProxy;
+  m_request.davData.depth = stat ? 0 : 1;
+
+  retrieveContent( true );
+
+  QDomDocument multiResponse;
+  multiResponse.setContent( m_intData, true );
+  QDomNode response = multiResponse.documentElement().firstChild();
+
+  while( !response.isNull() ) {
+    QDomElement thisResponse = response.toElement(); // try to convert the node to \
an element. +    if ( !thisResponse.isNull() ) {
+      QDomElement href = thisResponse.namedItem( "href" ).toElement();
+      if ( !href.isNull() ) {
+        if ( 1 ) {//!stat || href.text() == url.path() ) {
+          entry.clear();
+
+          // remove path from url
+          QString filename = href.text().right( href.text().length() - \
url.path().length() ); +
+          // strip '/' from start of directory/file
+          if ( filename[0] == '/' )
+            filename = filename.right( filename.length() - 1 );
+
+          // strip '/' from end of directory
+          if ( filename[filename.length() - 1] == '/' )
+            filename = filename.left( filename.length() - 1 );
+
+          UDSAtom atom;
+          atom.m_uds = KIO::UDS_NAME;
+          atom.m_str = filename;
+          entry.append( atom );
+
+          QDomNode propstats = thisResponse.firstChild(); // namedItem( "propstat" \
)//.toElement(); +          while ( !propstats.isNull() ) {
+            QDomElement propstat = propstats.toElement();
+            if ( !propstat.isNull() && propstat.tagName() == "propstat" ) {
+              davParsePropstat( propstat, entry );
+            }
+
+            propstats = propstats.nextSibling();
+          }
+
+          if ( stat ) {
+            // return an item
+            kdDebug() << "stat()ing " << url.prettyURL() << endl;
+            statEntry( entry );
+            finished();
+            return;
+          } else {
+            kdDebug() << "List()ing " << url.prettyURL() << endl;
+            listEntry( entry, false );
+          } // stat
+
+        } else  {
+          kdDebug() << "Unexpected incorrect url: " << href.text() << " (expected "
+                    << url.path() << ")" << endl;
+
+        } // not a stat or the location is the one we want (href.text() == \
url.path()) +      } else {
+        kdDebug() << "Unexpected problem stat()ing " << url.prettyURL() << endl;
+      }
+    }
+    response = response.nextSibling();
+  }
+
+  if ( stat )
+    error( ERR_UNSUPPORTED_PROTOCOL, i18n("Your server did not return a WebDAV 1.0 \
compliant response.") ); +  else {
+    listEntry( entry, true );
+    finished();
+  }
+}
+
+void HTTPProtocol::davParsePropstat( const QDomElement& propstat, UDSEntry& entry )
+{
+  QDomElement status = propstat.namedItem( "status" ).toElement();
+  if ( status.isNull() ) {
+    // error, no status code in this propstat
+    kdDebug() << "error, no status code in this propstat" << endl;
+    return;
+  }
+
+  int firstSpace = status.text().find( ' ' );
+  int secondSpace = status.text().find( ' ', firstSpace + 1 );
+  int code = status.text().mid( firstSpace + 1, secondSpace - firstSpace - 1 \
).toInt(); +
+  if ( code != 200 ) {
+    kdDebug() << "Error: status code " << code << endl;
+    return;
+  }
+
+  QDomElement prop = propstat.namedItem( "prop" ).toElement();
+  if ( prop.isNull() ) {
+    kdDebug() << "error, no prop segment in this propstat." << endl;
+    return;
+  }
+
+  QDomNode properties = prop.firstChild();
+
   UDSAtom atom;
-  atom.m_uds = KIO::UDS_NAME;
-  atom.m_str = url.fileName();
-  entry.append( atom );
+  bool foundExecutable = false;
+  bool foundContentType = false;
+  while ( !properties.isNull() ) {
+    QDomElement property = properties.toElement();
+
+    if ( !property.isNull() ) {
+      if ( property.tagName() == "creationdate" ) {
+        kdDebug() << "Found creation date: " << property.text() << endl;
+
+        // No idea why this doesn't work :(
+        /*QDateTime dt;
+        dt = QDateTime::fromString( property.text(), Qt::ISODate );
+        QDateTime jan1970( QDate(1970,1,1), QTime(00,00) );
+        kdDebug() << "Verify creation date: " << dt.toString() << endl;*/
+
+        atom.m_uds = KIO::UDS_CREATION_TIME;
+        atom.m_long = parseDateTime( property.text(), property.attribute("dt") );
+        entry.append( atom );
+
+      } else if ( property.tagName() == "getcontentlength" ) {
+        kdDebug() << "Found content length: " << property.text() << endl;
+        atom.m_uds = KIO::UDS_SIZE;
+        atom.m_long = property.text().toULong();
+        entry.append( atom );
+
+      } else if ( property.tagName() == "getcontenttype" ) {
+        foundContentType = true;
+        if ( property.text() == "httpd/unix-directory" ) {
+          foundExecutable = true;
+          atom.m_uds = KIO::UDS_FILE_TYPE;
+          atom.m_long = S_IFDIR;
+          entry.append( atom );
+
+        } else {
+          atom.m_uds = KIO::UDS_FILE_TYPE;
+          atom.m_long = S_IFREG;
+          entry.append( atom );
+
+          atom.m_uds = KIO::UDS_MIME_TYPE;
+          atom.m_str = property.text();
+          entry.append( atom );
+        }
+      } else if ( property.tagName() == "executable" ) {
+        kdDebug() << "Found executable: " << property.text() << endl;
+        if ( property.text() == "T" )
+          foundExecutable = true;
+
+      } else if ( property.tagName() == "getlastmodified" ) {
+        kdDebug() << "Found last modification date: " << property.text() << endl;
+        atom.m_uds = KIO::UDS_MODIFICATION_TIME;
+        atom.m_long = parseDateTime( property.text(), property.attribute("dt") );
+        entry.append( atom );
+
+      } else if ( property.tagName() == "getetag" ) {
+        kdDebug() << "Found etag: " << property.text() << endl;
+        setMetaData( "etag", property.text() );
 
-  atom.m_uds = KIO::UDS_FILE_TYPE;
-  atom.m_long = S_IFREG; // a file
-  entry.append( atom );
+      } else if ( property.tagName() == "supportedlock" ) {
+        kdDebug() << "Found supportedlock: " << property.text() << endl;
 
-  atom.m_uds = KIO::UDS_ACCESS;
-  atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; // readable by everybody
-  entry.append( atom );
+      } else if ( property.tagName() == "lockdiscovery" ) {
+        kdDebug() << "Found lockdiscovery: " << property.text() << endl;
 
-  statEntry( entry );
+      } else {
+        kdDebug() << "Found unknown property: " << property.tagName() << endl;
+      }
+    } else {
+      kdDebug() << "Found unknown item." << endl;
+    }
+
+    properties = properties.nextSibling();
+  }
+
+  if ( foundExecutable ) {
+    atom.m_uds = KIO::UDS_ACCESS;
+    atom.m_long = 0700;
+    entry.append(atom);
+  } else {
+    atom.m_uds = KIO::UDS_ACCESS;
+    atom.m_long = 0600;
+    entry.append(atom);
+  }
+
+  if ( !foundContentType ) {
+    atom.m_uds = KIO::UDS_FILE_TYPE;
+    atom.m_long = S_IFREG;
+    entry.append( atom );
+  }
+}
+
+long HTTPProtocol::parseDateTime( const QString& input, const QString& type )
+{
+  QDateTime dt;
+  if ( type == "dateTime.tz" ) {
+    int offset;
+    dt = parseDateISO8601( input, offset );
+    dt.addSecs( offset * 60 );
+  } else if ( type == "dateTime.rfc1123" ) {
+    parseDateRFC1123( input, dt );
+  } else {
+    kdDebug(7113) << "Datetime format unrecognised: " << type << endl;
+  }
 
-  finished();
+  static const QDateTime jan1970( QDate(1970,1,1), QTime(00,00) );
+
+  return -dt.secsTo( jan1970 );
 }
 
+/**
+ * Parse an ISO 8601 date, including possible extension suffixes.
+ * Thanks to Rik Hemsley (rikkus) <rik@kde.org> for this one
+ */
+QDateTime HTTPProtocol::parseDateISO8601( const QString& input, int& offset )
+{
+  // These dates look like this:
+  // YYYY-MM-DDTHH:MM:SS
+  // But they may also have 0, 1 or 2 suffixes.
+  // Suffix 1: .secfrac (fraction of second)
+  // Suffix 2: Either 'Z' or +zone or -zone, where zone is HHMM
+
+  unsigned int year     = 0;
+  unsigned int month    = 0;
+  unsigned int mday     = 0;
+  unsigned int hour     = 0;
+  unsigned int min      = 0;
+  unsigned int sec      = 0;
+  unsigned int secfrac  = 0;
+
+  offset = 0;
+
+  // First find the 'T' separator.
+  int tPos = input.find('T');
+
+  if (-1 == tPos)
+    return QDateTime();
+
+  // Now parse the date part.
+
+  QString dateString = input.left(tPos).stripWhiteSpace();
+
+  QString timeString = input.mid(tPos + 1).stripWhiteSpace();
+
+  QStringList l = QStringList::split('-', dateString);
+
+  year   = l[0].toUInt();
+  month  = l[1].toUInt();
+  mday   = l[2].toUInt();
+
+  // Z suffix means UTC.
+  if ('Z' == timeString.at(timeString.length() - 1))
+  {
+    timeString.remove(timeString.length() - 1, 1);
+  }
+
+  // +zone or -zone suffix (offset from UTC).
+
+  int plusPos = timeString.findRev('+');
+
+  if (-1 != plusPos) {
+    QString offsetString = timeString.mid(plusPos + 1);
+
+    offset = offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt();
+
+    timeString = timeString.left(plusPos);
+  } else {
+    int minusPos = timeString.findRev('-');
+
+    if (-1 != minusPos) {
+      QString offsetString = timeString.mid(minusPos + 1);
+
+      offset = offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt();
+
+      timeString = timeString.left(minusPos);
+    }
+  }
+
+  // secfrac suffix.
+  int dotPos = timeString.findRev('.');
+
+  if (-1 != dotPos) {
+    secfrac = timeString.mid(dotPos + 1).toUInt();
+
+    timeString = timeString.left(dotPos);
+  }
+
+  // Now parse the time part.
+
+  l = QStringList::split(':', timeString);
+
+  hour   = l[0].toUInt();
+  min    = l[1].toUInt();
+  sec    = l[2].toUInt();
+
+  return QDateTime
+    (QDate(year, month, mday), QTime(hour, min, sec, secfrac * 10));
+}
+
+// Taken from kdenetwork/kpf/src/Utils.cpp (MIT licence)
+// Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+void HTTPProtocol::parseDateRFC1123(const QString& input, QDateTime& dt)
+{
+  QStringList l(QStringList::split(' ', input));
+
+  if ("GMT" != l[5]) {
+    kdDebug(7113) << "Datetime parsing difficulty: not GMT." << endl;
+    return;
+  }
+
+  uint day(l[1].toUInt());
+
+  bool haveMonth = false;
+  uint month = 0;
+
+  QStringList::ConstIterator it;
+
+  for (it = monthList.begin(); it != monthList.end(); ++it) {
+    if (*it == l[2]) {
+      haveMonth = true;
+      break;
+    }
+    ++month;
+  }
+
+  if (!haveMonth) {
+    kdDebug(7113) << "Datetime parsing difficulty: month unrecognised." << endl;
+    return false;
+  }
+
+  uint year(l[3].toUInt());
+
+  QStringList timeTokenList(QStringList::split(':', l[4]));
+
+  if (3 != timeTokenList.count()) {
+    kdDebug(7113) << "Datetime parsing difficulty: time parsing failed." << endl;
+    return false;
+  }
+
+  uint hours    (timeTokenList[0].toUInt());
+  uint minutes  (timeTokenList[1].toUInt());
+  uint seconds  (timeTokenList[2].toUInt());
+
+  dt.setDate(QDate(year, month + 1, day));
+  dt.setTime(QTime(hours, minutes, seconds));
+
+  return dt.isValid();
+}
+
+void HTTPProtocol::mkdir( const KURL& url, int permissions )
+{
+  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::mkdir " << url.url()
+                << endl;
+
+  if ( !checkRequestURL( url ) )
+    return;
+
+  m_request.method = DAV_MKCOL;
+  m_request.path = url.path();
+  m_request.query = QString::null;
+  m_request.cache = CC_Reload;
+  m_request.doProxy = m_bUseProxy;
 
+  retrieveContent();
+}
+
 void HTTPProtocol::get( const KURL& url )
 {
   kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::get " << url.url()
@@ -455,7 +852,7 @@ void HTTPProtocol::get( const KURL& url 
   retrieveContent();
 }
 
-void HTTPProtocol::put( const KURL &url, int, bool, bool)
+void HTTPProtocol::put( const KURL& url, int, bool, bool )
 {
   kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::put " << url.prettyURL()
                 << endl;
@@ -472,6 +869,80 @@ void HTTPProtocol::put( const KURL &url,
   retrieveContent();
 }
 
+void HTTPProtocol::copy( const KURL& src, const KURL& dest, int permissions, bool \
overwrite ) +{
+  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::copy " << src.path()
+                << " -> " << dest.prettyURL() << endl;
+
+  if ( !checkRequestURL( dest ) || !checkRequestURL( src ) )
+    return;
+
+  // destination has to be "http://..."
+  KURL newDest = dest;
+  newDest.setProtocol( "http" );
+
+  QString filename = src.path().right( src.path().length() - src.path().findRev('/') \
- 1 ); +
+  newDest.setPath( newDest.path() + '/' + filename );
+
+  m_request.method = DAV_COPY;
+  m_request.path = src.path();
+  m_request.davData.desturl = newDest.url();
+  m_request.davData.overwrite = overwrite;
+  m_request.query = QString::null;
+  m_request.cache = CC_Reload;
+  m_request.doProxy = m_bUseProxy;
+
+  retrieveContent();
+}
+
+void HTTPProtocol::rename( const KURL& src, const KURL& dest, bool overwrite )
+{
+  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::rename " << src.path()
+                << " -> " << dest.prettyURL() << endl;
+
+  if ( !checkRequestURL( dest ) || !checkRequestURL( src ) )
+    return;
+
+  // destination has to be "http://..."
+  KURL newDest = dest;
+  newDest.setProtocol( "http" );
+
+  m_request.method = DAV_MOVE;
+  m_request.path = src.path();
+  m_request.davData.desturl = newDest.url();
+  m_request.davData.overwrite = overwrite;
+  m_request.query = QString::null;
+  m_request.cache = CC_Reload;
+  m_request.doProxy = m_bUseProxy;
+
+  retrieveContent();
+}
+
+void HTTPProtocol::del( const KURL& url, bool isfile )
+{
+  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::del " << url.prettyURL()
+                << endl;
+
+  if ( !checkRequestURL( url ) )
+    return;
+
+  m_request.method = HTTP_DELETE;
+  m_request.path = url.path();
+  m_request.query = QString::null;
+  m_request.cache = CC_Reload;
+  m_request.doProxy = m_bUseProxy;
+
+  retrieveHeader( false );
+  /**
+  * The server returns a HTTP/1.1 204 No Content on successful completion
+  */
+  if ( m_responseCode == 204 )
+    finished();
+  else
+    error(ERR_ACCESS_DENIED, "");
+}
+
 void HTTPProtocol::post( const KURL& url)
 {
   kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::post "
@@ -489,6 +960,40 @@ void HTTPProtocol::post( const KURL& url
   retrieveContent();
 }
 
+void HTTPProtocol::davLock( const KURL& url )
+{
+  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davLock "
+                << url.prettyURL() << endl;
+
+  if ( !checkRequestURL( url ) )
+    return;
+
+  m_request.method = DAV_LOCK;
+  m_request.path = url.path();
+  m_request.query = QString::null;
+  m_request.cache = CC_Reload;
+  m_request.doProxy = m_bUseProxy;
+
+  retrieveContent();
+}
+
+void HTTPProtocol::davUnlock( const KURL& url )
+{
+  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davUnlock "
+                << url.prettyURL() << endl;
+
+  if ( !checkRequestURL( url ) )
+    return;
+
+  m_request.method = DAV_UNLOCK;
+  m_request.path = url.path();
+  m_request.query = QString::null;
+  m_request.cache = CC_Reload;
+  m_request.doProxy = m_bUseProxy;
+
+  retrieveContent();
+}
+
 void HTTPProtocol::multiGet(const QByteArray &data)
 {
   QDataStream stream(data, IO_ReadOnly);
@@ -874,8 +1379,10 @@ bool HTTPProtocol::httpOpen()
 
   // Clean up previous POST
   bool moreData = false;
+  bool davData = true;
   // Variable to hold the entire header...
   QString header;
+  QString davHeader;
 
   // Determine if this is a POST or GET method
   switch ( m_request.method) {
@@ -899,6 +1406,46 @@ bool HTTPProtocol::httpOpen()
       header = "DELETE ";
       m_bCachedWrite = false; // Do not put any result in the cache
       break;
+  case DAV_PROPFIND:
+      header = "PROPFIND ";
+      davHeader = "Depth: ";
+      if ( m_request.davData.depth == 2 )
+        davHeader += "infinity";
+      else
+        davHeader += QString("%1").arg( m_request.davData.depth );
+      davHeader += "\r\n";
+      davData = true;
+      m_bCachedWrite = false; // Do not put any result in the cache
+      break;
+  case DAV_PROPPATCH:
+      header = "PROPPATCH ";
+      davData = true;
+      m_bCachedWrite = false; // Do not put any result in the cache
+      break;
+  case DAV_MKCOL:
+      header = "MKCOL ";
+      m_bCachedWrite = false; // Do not put any result in the cache
+      break;
+  case DAV_COPY:
+  case DAV_MOVE:
+      header = ( m_request.method == DAV_COPY ) ? "COPY " : "MOVE ";
+      davHeader = "Destination: " + m_request.davData.desturl;
+      // infinity depth means copy recursively
+      // (optional for copy -> but is the desired action)
+      davHeader += "\r\nDepth: infinity\r\nOverwrite: ";
+      davHeader += m_request.davData.overwrite ? "T" : "F";
+      davHeader += "\r\n";
+      m_bCachedWrite = false; // Do not put any result in the cache
+      break;
+  case DAV_LOCK:
+      header = "LOCK ";
+      m_bCachedWrite = false; // Do not put any result in the cache
+      break;
+  case DAV_UNLOCK:
+      header = "UNLOCK ";
+      davHeader = "Lock-token: " + metaData("locktoken") + "\r\n";
+      m_bCachedWrite = false; // Do not put any result in the cache
+      break;
   }
 
   if ( isSSLTunnelEnabled() )
@@ -1105,6 +1652,10 @@ bool HTTPProtocol::httpOpen()
       header += proxyAuthenticationHeader();
   }
 
+  // add extra header elements for WebDAV
+  if ( davHeader != QString::null )
+    header += davHeader;
+
   if ( !moreData )
     header += "\r\n";  /* end header */
 
@@ -1376,6 +1927,10 @@ bool HTTPProtocol::readHeader()
         m_bCachedWrite = false; // Don't put in cache
         mayCache = false;
       }
+      else if ( m_responseCode == 207 ) // Multi-status (for WebDav)
+      {
+
+      }
       else if ( m_responseCode == 204 ) // No content
       {
         // error(ERR_NO_CONTENT, i18n("Data have been successfully sent."));
@@ -1388,6 +1943,15 @@ bool HTTPProtocol::readHeader()
         if ( m_request.offset )
           m_bCanResume = true;
       }
+      else if (m_responseCode == 102) // Processing (for WebDAV)
+      {
+        /***
+         * This status code is given when the server expects the
+         * command to take significant time to complete. So, inform
+         * the user.
+         */
+        infoMessage( i18n( "Server processing request..." ) );
+      }
       else if (m_responseCode == 100)
       {
         // We got 'Continue' - ignore it
@@ -2109,10 +2671,9 @@ void HTTPProtocol::mimetype( const KURL&

   kdDebug(7113) << "(" << m_pid << ") http: mimetype = " << m_strMimeType
                 << endl;
-
 }
 
-void HTTPProtocol::special( const QByteArray &data)
+void HTTPProtocol::special( const QByteArray &data )
 {
   kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::special" << endl;
 
@@ -2137,6 +2698,19 @@ void HTTPProtocol::special( const QByteA
       cacheUpdate( url, no_cache, expireDate );
       break;
     }
+    case 5: // WebDAV lock
+    {
+      KURL url;
+      stream >> url;
+      davLock( url );
+    }
+    case 6: // WebDAV unlock
+    {
+      KURL url;
+      QString lockXML;
+      stream >> url;
+      davUnlock( url );
+    }
     default:
       // Some command we don't understand.
       // Just ignore it, it may come from some future version of KDE.
@@ -2377,8 +2951,11 @@ int HTTPProtocol::readUnlimited()
  * (meaning that the client is done sending data) or by 'httpOpen()'
  * (if we are in the process of a PUT/POST request).
  */
-bool HTTPProtocol::readBody()
+bool HTTPProtocol::readBody( bool dataInternal )
 {
+  if ( dataInternal )
+    m_intData = QString::null;
+
   // Check if we need to decode the data.
   // If we are in copy mode, then use only transfer decoding.
   bool decode = ( !m_qTransferEncodings.isEmpty() ||
@@ -2416,19 +2993,25 @@ bool HTTPProtocol::readBody()
         if (nbytes > 0)
         {
           m_bufReceive.setRawData( buffer, nbytes);
           data( m_bufReceive );
+          if ( dataInternal )
+            m_intData += m_bufReceive;
           m_bufReceive.resetRawData( buffer, nbytes );
           sz += nbytes;
         }
      }
      processedSize( sz );
      // FINALLY, we compute our final speed and let everybody know that we
      // are done
      t_last = time(0L);
      if (sz && t_last - t_start)
        speed(sz / (t_last - t_start));
      m_bufReceive.resize( 0 );
      data( QByteArray() );
      return true;
   }

@@ -2469,56 +3052,59 @@ bool HTTPProtocol::readBody()
     // won't work with it!
     if (bytesReceived > 0)
     {
-      // If a broken server does not send the mime-type,
-      // we try to id it from the content before dealing
-      // with the content itself.
-      if ( m_strMimeType.isEmpty() && !( m_responseCode >= 300 &&
-                                         m_responseCode <=399) )
-      {
-        kdDebug(7113) << "(" << m_pid << ") Determining mime-type from content..." \
                << endl;
-        int old_size = mimeTypeBuffer.size();
-        mimeTypeBuffer.resize( old_size + bytesReceived );
-        memcpy( mimeTypeBuffer.data() + old_size, m_bufReceive.data(),
-                bytesReceived );
-        if ( m_iBytesLeft > 0 && mimeTypeBuffer.size() < 1024 )
-        {
-          cpMimeBuffer = true;
-          continue;   // Do not send up the data since we do not yet know its \
                mimetype!
-        }
-
-        kdDebug(7113) << "(" << m_pid << ") Mimetype buffer size: " << \
                mimeTypeBuffer.size()
-                      << endl;
-        KMimeMagicResult * result = KMimeMagic::self()->findBufferFileType( \
                mimeTypeBuffer, m_request.url.fileName() );
-        if( result )
-        {
-          m_strMimeType = result->mimeType();
-          kdDebug(7113) << "(" << m_pid << ") Mimetype from content: " << \
                m_strMimeType << endl;
-        }
+      // internal functions don't worry abount mimetypes
+      if ( !dataInternal ) {
+        // If a broken server does not send the mime-type,
+        // we try to id it from the content before dealing
+        // with the content itself.
+        if ( m_strMimeType.isEmpty() && !( m_responseCode >= 300 &&
+                                          m_responseCode <=399) )
+        {
+          kdDebug(7113) << "(" << m_pid << ") Determining mime-type from content..." \
<< endl; +          int old_size = mimeTypeBuffer.size();
+          mimeTypeBuffer.resize( old_size + bytesReceived );
+          memcpy( mimeTypeBuffer.data() + old_size, m_bufReceive.data(),
+                  bytesReceived );
+          if ( m_iBytesLeft > 0 && mimeTypeBuffer.size() < 1024 )
+          {
+            cpMimeBuffer = true;
+            continue;   // Do not send up the data since we do not yet know its \
mimetype! +          }
 
-        if ( m_strMimeType.isEmpty() )
-        {
-          m_strMimeType = QString::fromLatin1( DEFAULT_MIME_TYPE );
-          kdDebug(7113) << "(" << m_pid << ") Using default mimetype: " <<  \
m_strMimeType +          kdDebug(7113) << "(" << m_pid << ") Mimetype buffer size: " \
<< mimeTypeBuffer.size()  << endl;
-        }
+          KMimeMagicResult * result = KMimeMagic::self()->findBufferFileType( \
mimeTypeBuffer, m_request.url.fileName() ); +          if( result )
+          {
+            m_strMimeType = result->mimeType();
+            kdDebug(7113) << "(" << m_pid << ") Mimetype from content: " << \
m_strMimeType << endl; +          }
 
-        if ( m_bCachedWrite )
-        {
-          createCacheEntry( m_strMimeType, m_expireDate );
-          if (!m_fcache)
-            m_bCachedWrite = false;
-        }
+          if ( m_strMimeType.isEmpty() )
+          {
+            m_strMimeType = QString::fromLatin1( DEFAULT_MIME_TYPE );
+            kdDebug(7113) << "(" << m_pid << ") Using default mimetype: " <<  \
m_strMimeType +                          << endl;
+          }
 
-        if ( cpMimeBuffer )
-        {
-          bytesReceived = mimeTypeBuffer.size();
-          m_bufReceive.resize(0);
-          m_bufReceive.resize(bytesReceived);
-          memcpy( m_bufReceive.data(), mimeTypeBuffer.data(),
-                  bytesReceived );
+          if ( m_bCachedWrite )
+          {
+            createCacheEntry( m_strMimeType, m_expireDate );
+            if (!m_fcache)
+              m_bCachedWrite = false;
+          }
+
+          if ( cpMimeBuffer )
+          {
+            bytesReceived = mimeTypeBuffer.size();
+            m_bufReceive.resize(0);
+            m_bufReceive.resize(bytesReceived);
+            memcpy( m_bufReceive.data(), mimeTypeBuffer.data(),
+                    bytesReceived );
+          }
+          mimeType(m_strMimeType);
+          mimeTypeBuffer.resize(0);
         }
-        mimeType(m_strMimeType);
-        mimeTypeBuffer.resize(0);
       }
 
       // check on the encoding.  can we get away with it as is?
@@ -2532,16 +3118,21 @@ bool HTTPProtocol::readBody()
         if (useMD5)
           context.update( m_bufReceive );
         // Let the world know that we have some data
         data( m_bufReceive );
+        if ( dataInternal )
+          m_intData += m_bufReceive;

         if (m_bCachedWrite && m_fcache)
            writeCacheEntry(m_bufReceive.data(), bytesReceived);

         sz += bytesReceived;
         processedSize( sz );
         time_t t = time( 0L );
         if ( t - t_last >= 1 )
         {
           speed( (sz - m_request.offset) / ( t - t_start ) );
           t_last = t;
         }
@@ -2613,9 +3205,13 @@ bool HTTPProtocol::readBody()
       while ( 1 )
       {
         array.setRawData( m_bufEncodedData.data()+sz, bytesToSend);
         data( array );
+        if ( dataInternal )
+          m_intData += array;
         array.resetRawData( m_bufEncodedData.data()+sz, bytesToSend);
         sz += bytesToSend;
         processedSize( sz );
         if ( sz >= bytesReceived )
           break;
@@ -2626,7 +3222,11 @@ bool HTTPProtocol::readBody()
     else
     {
       sz = bytesReceived;
       data( m_bufEncodedData );
+      if ( dataInternal )
+        m_intData += m_bufEncodedData;
       processedSize( bytesReceived );
     }

Index: http.h
===================================================================
RCS file: /home/kde/kdelibs/kio/http/http.h,v
retrieving revision 1.111
diff -u -3 -p -u -r1.111 http.h
--- http.h	2001/12/13 01:21:13	1.111
+++ http.h	2001/12/30 17:22:35
@@ -37,6 +37,7 @@
 #include "kio/tcpslavebase.h"

 class DCOPClient;
+class QDomElement;

 namespace KIO {
     class AuthInfo;
@@ -58,8 +59,13 @@ public:
   /** Current protocol mode used **/
   enum HTTP_PROTO  {PROTO_HTTP, PROTO_HTTPS, PROTO_WEBDAV};

-  /** HTTP method **/
-  enum HTTP_METHOD {HTTP_GET, HTTP_PUT, HTTP_POST, HTTP_HEAD, HTTP_DELETE};
+  /** HTTP / DAV method **/
+  enum HTTP_METHOD {HTTP_GET, HTTP_PUT, HTTP_POST, HTTP_HEAD, HTTP_DELETE,
+                    DAV_PROPFIND, DAV_PROPPATCH, DAV_MKCOL, DAV_COPY,
+                    DAV_MOVE, DAV_LOCK, DAV_UNLOCK };

   /** State of the current Connection **/
   typedef struct
@@ -72,6 +78,14 @@ public:
     QString cef; // Cache Entry File belonging to this URL.
   } HTTPState;

+  /** DAV-specific request elements for the current connection **/
+  typedef struct
+  {
+    QString desturl;
+    bool overwrite;
+    int depth;
+  } DAVRequest;
+
   /** The request for the current connection **/
   typedef struct
   {
@@ -94,6 +108,7 @@ public:
     bool disablePassDlg;
     QString userAgent;
     QString id;
+    DAVRequest davData;
   } HTTPRequest;

   typedef struct
@@ -122,14 +137,28 @@ public:
   virtual void put( const KURL& url, int _mode, bool _overwrite,
                     bool _resume );

+//----------------- Re-implemented methods for WebDAV -----------
+  virtual void listDir( const KURL& url );
+  virtual void mkdir( const KURL& url, int permissions );
+
+  virtual void rename( const KURL& src, const KURL& dest, bool overwrite );
+  virtual void copy ( const KURL& src, const KURL& dest, int permissions, bool \
overwrite ); +  virtual void del( const KURL& url, bool isfile );
+
+  void davLock( const KURL& url );
+  void davUnlock( const KURL& url );
+//---------------------------- End WebDAV -----------------------
+
   /**
    * Special commands supported by this slave :
    * 1 - HTTP POST
    * 2 - Cache has been updated
    * 3 - SSL Certificate Cache has been updated
    * 4 - HTTP multi get
+   * 5 - DAV LOCK
+   * 6 - DAV UNLOCK
    */
-  virtual void special( const QByteArray &data);
+  virtual void special( const QByteArray &data );

   virtual void mimetype( const KURL& url);

@@ -194,7 +223,22 @@ protected:

   bool readHeader();
   bool sendBody();
-  bool readBody();
+  // where dataInternal == true, the content is to be made available
+  // to an internal function.
+  bool readBody( bool dataInternal = false );
+
+  /**
+   * Performs a WebDAV stat or list
+   */
+  void davStatList( const KURL& url, bool stat = true );
+  void davParsePropstat( const QDomElement& propstat, KIO::UDSEntry& entry );
+
+  /**
+   * Parses a date & time string
+   */
+  long parseDateTime( const QString& input, const QString& type );
+  QDateTime parseDateISO8601( const QString& input, int& offset );
+  void parseDateRFC1123(const QString& input, QDateTime& dt);

   /**
    * Send a cookie to the cookiejar
@@ -258,7 +302,9 @@ protected:
   /**
    * Performs a GET HTTP request.
    */
-  void retrieveContent();
+  // where dataInternal == true, the content is to be made available
+  // to an internal function.
+  void retrieveContent( bool dataInternal = false );

   /**
    * Performs a HEAD HTTP request.
@@ -332,6 +378,11 @@ protected:
   char *m_linePtrUnget;
   size_t m_lineCountUnget;

+  // WebDAV
+  // Data structure to hold data which will be passed to an internal func.
+  QString m_intData;
+  QStringList monthList;
+
   // Holds the POST data so it won't get lost on if we
   // happend to get a 401/407 response when submitting,
   // a form.


["webdav.protocol" (text/plain)]

[Protocol]
exec=kio_http
protocol=webdav
input=none
output=filesystem
listing=Name,Type,Size,Access,
reading=true
writing=true
makedir=true
deleting=true
moving=true
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
Icon=www
maxInstances=3


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic