Le protocole Webdav permet d'accéder en lecture/écriture à une arborescence de fichiers au travers du protocole HTTP.
Généralement on utilise ce protocole pour accéder un système de fichier distant.
De la même manière que l'on peut produire une page HTML sans qu'elle existe réellement sur le serveur HTTP, on peut simuler un système de fichier complet en Webdav. Comme pour générer du HTML on peut utiliser PHP pour produire le XML spécifique à Webdav. Pour éviter d'apprendre la norme Webdav, on peut utiliser des APIs. A ma connaissance, il existe 2 en PHP :
Si on regarde de plus près la première, elle semble obsolète (numéro de version, date de version, etc...) Au delà des apparences cette classe est parfaitement utilisable et elle fonctionne très bien. Elle est d'ailleurs utiliser dans de nombreux projets PHP qui proposent une interface Webdav.
Installation
Package PEAR non à jour
Bien que emballée sous forme d'un package PEAR, il est préférable de ne pas utiliser cette version. Le code source de la classe a évolué mais le package PEAR, lui, n'a jamais été régénéré. Il donc préférable d'aller chercher directement le code source dans SVN.
Depuis le SVN
Cette classe est très légère (3 fichiers). Donc pour l'utiliser, on peut récupérer à la main ces 3 fichiers (sans même passer par les commandes subversion) :
% mkdir -p HTTP/WebDAV/Tools/
% cd HTTP/WebDAV/
% wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Server.php?view=co -O Server.php
% cd Tools
% wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Tools/_parse_lockinfo.php?view=co -O _parse_lockinfo.php
% wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Tools/_parse_propfind.php?view=co -O _parse_propfind.php
% wget http://svn.php.net/viewvc/pear/packages/HTTP_WebDAV_Server/trunk/Tools/_parse_proppatch.php?view=co -O _parse_proppatch.php
Environnement de Test
Serveur HTTP
Pour utiliser notre classe, il faut un serveur HTTP. Pour plus de souplesse dans la configuration, on peut utiliser
Pkgi
Pour notre exemple on utilisera le fichier .htaccess, donc dans PKGI on indiquera htaccess dans le champ APACHE_OPTIONS
Fichiers
En plus des fichiers téléchargés, il nous faut 3 autres fichiers :
- .htaccess
- index.php
- Test.php
Ce qui donne
`-----.htaccess
`-----index.php
`-----HTTP
`-----WebDAV
`-----Server.php
`-----Server
| `-----Test.php
`-----Tools
`-----_parse_lockinfo.php
`-----_parse_propfind.php
`-----_parse_proppatch.php
Configuration
Un serveur Webdav s'attend à répondre à des URLs pointant vers des répertoires et des fichiers sans aucun paramètre supplémentaire. Donc pour concentrer les accès (toutes les URLS) vers un point unique on utilisera le module Apache mod_rewrite que l'on configura avec ces instructions dans le fichier .htaccess :
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php
</IfModule>
Le fichier index.php lance le serveur webdav.
<?php
_SERVER['SCRIPT_NAME'] = '';
$server->ServeRequest();
La classe HTTP_WebDAV_Server se base sur les variables systèmes SCRIPT_NAME et REQUEST_URI pour calculer le chemin webdav demandé, il est extrêmement important de modifier ces variables afin d'obtenir pour la suite un chemin correct. Dans le cas contraire, on risque d'obtenir pour toutes requêtes le chemin par défaut à savoir "/". Dans notre cas, il suffit de mettre à blanc la variable SCRIPT_NAME
La classe HTTP_WebDAV_Server_Test
La classe HTTP_WebDAV_Server s'utilise par dérivation, il faut donc la surcharger et coder les méthodes abstraites. A chaque méthode correspond (en gros) une action webdav :
-
- GET() get a resource from the server
- HEAD() get resource headers only from the server
- PUT() create or modify a resource on the server
- COPY() copy a resource on the server
- MOVE() move a resource on the server
- DELETE() delete a resource on the server
- MKCOL() create a new collection
- PROPFIND() get property data for a resource
- PROPPATCH() modify property data for a resource
- LOCK() lock a resource
- UNLOCK() unlock a locked resource
- checklock() check whether a resource is locked
- check_auth() check authentication
Voici un exemple qui permet de décrire une arborescence composée récursivement d'un fichier (MyFile) et d'un répertoire (MyDir). Le ficher contient son nom et son chemin.
require_once "HTTP/WebDAV/Server.php";
class HTTP_WebDAV_Server_Test extends HTTP_WebDAV_Server
{
var $myfile = '%s%s';
function check_auth($type, $user, $pass)
{
return true;
}
function HEAD(&$options)
{
$options['mimetype'] = 'text/xml';
$options['mtime'] = filemtime(__FILE__);
$options['size'] = strlen(sprintf($this->myfile, basename($options['path']), $options['path']));
return true;
}
function GET(&$options)
{
if (!$this->HEAD($options)) {
return false;
}
$options['data'] = sprintf($this->myfile, basename($options['path']), $options['path']);
return true;
}
function PROPFIND(&$options, &$files)
{
$i = 0;
$files["files"] = array();
// On décrit la ressource demandée
$path = $options['path'];
$name = basename($path);
$files["files"][$i] = array();
$files["files"][$i]["path"] = $path;
$files["files"][$i]["props"] = array();
$files["files"][$i]["props"][] = $this->mkprop("displayname", $name);
$files["files"][$i]["props"][] = $this->mkprop("creationdate", filectime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("getlastmodified", filemtime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("lastaccessed", fileatime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("ishidden", false);
// La ressource demandée est-elle un repertoire ou un fichier ?
$testdir = trim($options['path'], '/');
if ($testdir === '' or preg_match(',MyDir$,', $testdir)) {
$files["files"][$i]["props"][] = $this->mkprop("resourcetype", "collection");
$files["files"][$i]["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
}
else {
$files["files"][$i]["props"][] = $this->mkprop("getcontenttype", 'text/xml');
$files["files"][$i]["props"][] = $this->mkprop("resourcetype", "");
$files["files"][$i]["props"][] = $this->mkprop("getcontentlength", strlen(sprintf($this->myfile, $name, $path)));
}
++$i;
// On décrit le contenu de la ressource
if ($options['depth'] == 1) {
$path = rtrim($options['path'], '/').'/MyDir';
$name = basename($path);
$files["files"][$i] = array();
$files["files"][$i]["path"] = $path;
$files["files"][$i]["props"] = array();
$files["files"][$i]["props"][] = $this->mkprop("displayname", $name);
$files["files"][$i]["props"][] = $this->mkprop("creationdate", filectime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("getlastmodified", filemtime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("lastaccessed", fileatime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("resourcetype", "collection");
$files["files"][$i]["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
++$i;
$path = rtrim($options['path'], '/').'/MyFile.xml';
$name = basename($path);
$files["files"][$i] = array();
$files["files"][$i]["path"] = $path;
$files["files"][$i]["props"] = array();
$files["files"][$i]["props"][] = $this->mkprop("displayname", $name);
$files["files"][$i]["props"][] = $this->mkprop("creationdate", filectime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("getlastmodified", filemtime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("lastaccessed", fileatime(__FILE__));
$files["files"][$i]["props"][] = $this->mkprop("ishidden", false);
$files["files"][$i]["props"][] = $this->mkprop("getcontenttype", 'text/xml');
$files["files"][$i]["props"][] = $this->mkprop("resourcetype", "");
$files["files"][$i]["props"][] = $this->mkprop("getcontentlength", strlen(sprintf($this->myfile, $name, $path)));
++$i;
}
return true;
}
}
Pour tester
En ligne de commande
L'utilitaire cadaver permet de tester toutes les parties du protocoles Webdav.
% cadaver
dav:!> open http://localhost:50009/
dav:/> ls
Listing collection `/': succeeded.
Coll: MyDir 0 juil. 15 15:35
MyFile.xml 60 juil. 15 15:35
dav:/> cd MyDir
dav:/MyDir/> ls
Listing collection `/MyDir/': succeeded.
Coll: MyDir 0 juil. 15 15:35
MyFile.xml 66 juil. 15 15:35
dav:/MyDir/> get MyFile.xml
Downloading `/MyDir/MyFile.xml' to MyFile.xml:
Progress: [=============================>] 100,0% of 66 bytes succeeded.
dav:/MyDir/> propget MyFile.xml
Fetching properties for `MyFile.xml':
displayname = MyFile.xml
creationdate = 2009-07-15T13:35:11Z
getlastmodified = Wed, 15 Jul 2009 13:35:11 GMT
lastaccessed = Wed, 15 Jul 2009 13:39:15 GMT
ishidden =
getcontenttype = text/xml
resourcetype =
getcontentlength = 66
dav:/MyDir/> more MyFile.xml
Displaying `/MyDir/MyFile.xml':
MyFile.xml/MyDir/MyFile.xml
dav:/MyDir/> exit
Connection to `localhost' closed.
%
Avec nautilus sous Ubuntu