Kontakt aufnehmen

WebDav-Client mit PHP

Aufgrund der unbeschritten Vorteile erfreut sich die HTTP-WebDav-Erweiterung immer größerer Beliebtheit. Die Anbieter der Betriebssysteme bauen für den Zugriff auf ein WebDav-Laufwerk bereits standardmäßige Erweiterungen ein. Es gibt aber auch die Alternative für einen Administrator oder Programmierer, auf eine betriebssystem-unabhängige Variante zurückzugreifen. WebDav besteht aus einer Sammlung von HTTP-Methoden und so gibt es die Möglichkeit, sich mit einer Sprache wie z.B. PHP, die den Zugriff auf Sockets unterstützt, einen eigenen WebDav-Client zu programmieren.

Interne connect-Funktion der Klasse

Grundsätzlich beruht die Arbeit der PHP-Klasse darauf, dass in den einzelnen Funktionen, die vom Skript aus angesprochen werden, die Inhalte generiert werden und diese danach an die Socket-Funktion der Klasse übergeben werden, die auch die Antwort vom Server erhält. Der Quellcode dieser Socketfunktion kann z. B. so aussehen:

function __connect( $param = array() )
{
 $fp = fsockopen($this->server, 80, $errno, $errstr, 5);
 if(!$fp)
 {
  return "$errno -> $errstr<br>";
 }
 else
 {
  fwrite($fp, $param['content']);
  $output_array = array();
  while(!feof($fp))
  {
   array_push($output_array,fgets($fp));
  }
  fclose($fp);
  return $output_array;
 }
}

Ohne das Schlüsselwort public kann die Funktion nur innerhalb der Klasse aufgerufen werden. Zuerst wird eine Socket-Verbindung über den Port 80 an die übergebene IP-Adresse oder Hostnamen hergestellt. Wenn das nach 5 Sekunden nicht möglich ist, wird eine Fehlernummer zusammen mit einem Fehlertext, der im Rahmen der fsockopen-Funktion zur Verfügung steht, zurückgegeben. Im Erfolgsfall wird über die Funktion fwrite der an die Funktion übergebene Inhalt in das Socket geschrieben. Die Antwort der Gegenstelle wird innerhalb der while-Schleife in ein Array geschrieben, welches über return zurückgegeben wird.

Funktionen der WebDav-Klasse

Man muss sicherlich den Fall mit einkalkulieren, dass der Programmierer der Anwendung nicht unbedingt auch der Betreuer des Webservers ist. Insofern ist es möglich, dass der Programmierer nur die IP-Adresse oder den Hostnamen des Webservers kennt und darüber hinaus keine Details zur Verfügung hat. Deshalb ist es sinnvoll eine Funktion mit in die Klasse einzubauen, die überprüft, ob der Webserver WeDav fähig ist oder nicht. Mit der folgenden Funktion ist dies möglich:

public function check_webdav( $param=array() )
{
 $content = "HEAD / HTTP/1.1 rn";
 $content .= "Host: $this->server rn";
 $content .= "Connection: Closern";
 $content .= "rn";
 $output = $this->__connect( array('content'=>$content) );

 foreach($output as $line)
 {
  if( preg_match("/Server:/",$line) )
  {
   if( !preg_match("/DAV*/",$line) )
   {
    return "1";
   }
  }
 }
}

Zuerst wird der Variablen $content ein HTTP-Header mit der Methode HEAD zugewiesen. Damit werden nur sog. Kopfdaten und keine Nutzdaten per HTTP an den Server geschickt. Danach wird der Header an die interne connect-Funktion geschickt und der Rückgabewert dieser Funktion als Array in die Variable $output geschrieben. Zuletzt wird das Array $output nach der eindeutigen Zeile, die den Ausdruck "Server:" enthält, durchsucht. In der gleichen Zeile wird überprüft, ob diese den Ausdruck "DAV" enthält. Wenn das nicht der Fall ist, wird die "1" zurückgegeben. Das bedeutet, dass der angegebene Webserver nicht WebDav-fähig ist.

Inhalt des WebDav-Laufwerkes / Verzeichnisses

Nachdem man weiß, dass der Webserver WebDav unterstützt, sollte man sich als erstes den Inhalt des Laufwerkes oder eines bestimmten Verzeichnisses anzeigen lassen. Das folgende Beispiel bezieht sich auf einen bestimmten Ordner im WebDav-Laufwerk, der durch einen übergebenen Parameter eindeutig festgelegt wird. Die folgende Funktion zeigt eine Option auf, wie der Inhalt eines bestimmten Ordners angezeigt werden kann:

public function show_content( $param=array() )
{
 $user = $param['user'];

 $content = "PROPFIND /files/$user/ HTTP/1.1 rn";
 $content .= "Host: $this->server rn";
 $content .= "Depth: 1rn";
 $content .= "Content-Type: text/xmlrn";
 $content .= "Connection:closern";
 $content .= "Content-Length: 0rn";
 $content .= "rn";
 $content .= "<?xml version="1.0" encoding="utf-8"?>rn";
 $content .= "<D:propfind xmlns:D="DAV:">rn";
 $content .= "<D:allprop/>rn";
 $content .= "</D:propfind>rn";

 $output = $this->__connect( array('content'=>$content) );
 array_pop($output);
 $key = array_search("rn",$output);
 $output = array_slice($output,($key+1));
}

Die Abfrage des Laufwerkinhaltes beruht auf der WebDav-Methode "PROPFIND". In dem obigen Beispiel werden alle möglichen Eigenschaften des Inhaltes abgefragt. Egal, welche Meta-Informationen des Inhaltes abgefragt werden, das WebDav-Modul des Apache-Webserver gibt immer alle Details aus. Insofern macht die PROPFIND-Variante "allprop" am ehesten Sinn. Wichtig beim "Zusammenbau" des Requestes ist, dass beim Header "Depth" der Wert "1" angegeben wird, damit auch die Informationen des Laufwerkinhaltes und nicht nur des Laufwerkes selbst angezeigt werden. Beim Wert "0" für "Depth" ist das der Fall.

Hinweise: Der Inhalt der Funktion wird nur bis zum Array $output angezeigt, welches als XML-Struktur alle abgefragten Details enthält. Die weitere Verarbeitung dieses Arrays bleibt jeden selbst überlassen. Die folgenden Informationen werden konkret abgerufen: Ordner-/Dateiname, Zeitpunkt der Erstellung, Zeitpunkt der letzten Änderung und - wenn möglich - die Dateigröße.
Für jedes Verzeichnis, welches beim PROPFIND-Request ermittelt wurde, muss ein neuer Request durchgeführt werden, um den kompletten Laufwerksinhalt auf allen Ebenen darstellen zu können.

Upload einer Datei in das WebDav-Verzeichnis

Hier bietet sich die Anwendung der PHP-Funktion copy() an. Dabei muss man beachten, dass man für das Ziel zwar eine URL angeben kann, bei der Ausführung der Funktion mit dieser Variante aber ein Fehler angezeigt wird. Da ein WebDav-Laufwerk nichts anderes als ein Verzeichnis auf dem Server ist, kann jedoch auch der lokale Pfad für das Ziel hergenommen werden. Insofern kann ein Dateiupload in den WebDav-Pfad mit der folgenden Funktion realisiert werden:

public function file_upload( $param = array() )
{
 $user = $param['user'];
 $tmp_filename = $param['tmpfile'];
 $filename = $param['file'];

 if(copy($tmp_filename, "D:/Webdav/" . $user ."/" . $filename) )
 {
  $content = "<script type=text/javascript>
  alert("Datei $filename wurde hochgeladen");
  document.location.href="index.php";
 </script>";
 }
 else
 {
  $content = "<script type=text/javascript>
  alert("Datei $filename konnte nicht hochgeladen werden");
  document.location.href="index.php";
 </script>";
 }
 return $content;
}

Löschen einer Datei

Im Rahmen der Ermittlung des Verzeichnisinhaltes wurde in die Tabelle ein Button mit eingefügt, der es ermöglicht, die dazugehörige Datei zu löschen. Konkret sieht dieses kleine Formular so aus:

<form action="?method=delete&file=$folder" method=post>
<input type=submit value="Datei löschen">
</form>

Zur genauen Unterscheidung wird im Query-String eine Aufgabe mitgegeben (method) und als Wert für $folder wird die zu löschende Datei mitgeliefert. Der Quellcode der Lösch-Funktion in der PHP-Klasse sieht im Beispiel so aus:

public function delete_file( $param = array() )
{
 $query_string = $param['query_string'];
 $query_param = split("&",$query_string);

 $filename = split("=",$query_param[1]);
 $file_tmp = str_replace("%3CD:href%3E","",$filename[1]);
 $file = str_replace("%3C/D:href%3E","",$file_tmp);

 $file_param = pathinfo($file);
 $path = $file_param['dirname'] . "/";
 $file = $file_param['basename'];

 $content = "DELETE $path/$file HTTP/1.1 rn";
 $content .= "Host: $this->server rn";
 $content .= "Connection: Closern";
 $content .= "Content-length:0rn";
 $content .= "Destroy:NoUndeletern";
 $content .= "rn";

 $output = $this->__connect( array('content'=>$content) );

 if(preg_match("/204/",$output[0]))
 {
  $content = "<script type=text/javascript>
  alert('Datei $file wurde gelöscht');
  document.location.href="index.php";
 </script>";
 }
 else
 {
  $content = "<script type=text/javascript>
  alert('Datei $file konnte nicht gelöscht werden');
  document.location.href="index.php";
 </script>";
 }
 return $content;
}

Nach der Analyse des Query-Strings und der Extrahierung des Datei- und Verzeichnisnamens aus der XML-Struktur wird der Header der HTTP-DELETE-Methode "gefüttert". Nach dem Aufruf der internen connect-Funktion wird ihr Ergebnis ausgewertet. Wenn das Löschen der Datei erfolgreich war, wird die HTTP-Statusmeldung "204" zurückgegeben. Es wird überprüft, ob das der Fall ist. Wenn ja, wird ein Javascript-Meldefenster angezeigt und die Webseite aktualisiert, so dass bei der Anzeige des Inhaltes die gelöschte Datei verschwindet. Im Fehlerfall wird eine allgemeine Fehlermeldung im Meldefenster ausgegeben.

Hinweis: Wenn eine Datei gelöscht wird, landet diese bei einem Windows-Server nicht im Papierkorb!

Anlegen eines neuen Ordners

Die WebDav-Methode MKCOL ermöglicht es, ein neues Verzeichnis im WebDav-Laufwerk anzulegen. Auch für diese Operation gibt es eine Funktion in der PHP-Beispielklasse. Der Code dieser Funktion sieht so aus:

public function create_new_folder( $param = array() )
{
 $user = $param['user'];
 $folder = $param['folder'];

 $content = "MKCOL /files/$user/$folder HTTP/1.1 rn";
 $content .= "Host: $this->server rn";
 $content .= "Connection: Closern";
 $content .= "rn";

 $output = $this->__connect( array('content'=>$content) );
 if(preg_match("/201/",$output[0]))
 {
  $content = "<script type=text/javascript>
  alert('Ordner $folder wurde angelegt');
  document.location.href="index.php";
 </script>";
 }
 else
 {
  $content = "<script type=text/javascript>
  alert('Der Ordner $folder konnte nicht angelegt werden.');
  document.location.href="index.php";
 </script>";
 }
 return $content;
}

Durch die übergebenen Parameter $user und $folder wird der Pfad und der Name des neuen Ordners der Funktion bekannt gemacht. Danach wird der HTTP-MKCOL-Request gebaut und an die Connect-Funktion übermitttelt. In der Antwort der Connect-Funktion wird wiederum nach der HTTP-Statusnummer gesucht. Mit einem regulären Ausdruck wird überprüft, ob die Statusnummer "201" ist, was einen Erfolg der Operation bedeutet. Wenn die Nummer nicht "201" ist, wird eine allgemeine Fehlermeldung ausgegeben. In beiden Fällen wird die Webseite zum Schluß aktualisiert.

Zugriff auf die PHP-Klasse in einem Skript

Nach dem Einbinden der Klasse mit include und dem Anlegen der Objekt-Instanz wird zuerst die WebDav-Fähigkeit des Webservers überprüft. In Abhängigkeit des Ergebnisses wird eine Fehlermeldung oder die zu Verfügung stehenden Optionen angezeigt.

$conn_check = $webdav->check_webdav(array("server"=>"localhost"));
if($conn_check == "1")
{
 print("Der angegebene Server ist nicht webdav-kompatibel");
}
else
{
 ... Anzeige des WebDav-Formulares ...
}

Der Inhalt des WebDav-Laufwerkes wird durch den folgenden Funktionsaufruf realisiert:

$content = $webdav->show_content( array("user"=>"test1") );

Aufruf der Funktionen in der PHP-Klasse

Die Funktionen der Klasse für die möglichen WebDav-Operationen werden immer nach dem gleichen Prinzip aufgerufen. Wenn z. B. ein neuer Ordner angelegt werden soll, werden in einem kleinen Formular alle notwendigen Informationen abgefragt. In diesem Fall ist es nur der Name des neuen Ordners. Das Formular sieht im Beispiel so aus:

print("
       <h1>Anlegen eines neuen Ordners</h1>
       <form action="index.php?method=folder" method="post">
       <input type="text" name="ordner">
       <input type="submit" value="Ordner anlegen">
       </form>
");

Mit dem Klick auf den Submit-Button wird das aktuelle Skript wieder aufgerufen, jedoch im Query-String ein Parameter mit übermittelt, der die Definition eines neuen Bereiches im Skript ermöglicht. In diesem Bereich werden alle weiteren Code-Zeilen zum Anlegen des neuen Ordners angegeben. Im Beispielskript wird der neue Ordner mit folgendem Code angelegt:

if(preg_match("/folder/",getenv('QUERY_STRING')))
{
 $folder = $_POST['ordner'];
 $folder_content = $webdav->create_new_folder( 
                   array('user' => $user, 'folder' => $folder));
 print_r($folder_content);
}

Mit den Operationen "Löschen einer Datei" (Filter "delete" im Query-String) und "Dateiupload" (Filter "upload" im Query-String) wird nach dem gleichen Prinzip verfahren. Die Funktionsaufrufe sehen so aus:

if(preg_match("/delete/",getenv('QUERY_STRING')))
{
 $delete_content = $webdav->delete_file( 
                   array('query_string' => getenv('QUERY_STRING')));
 print("$delete_content");
}


if(preg_match("/upload/",getenv('QUERY_STRING')))
{
 $upload = $webdav->file_upload( 
           array("user"=>$user, "tmpfile"=>$_FILES['file']['tmp_name'], 
                 "file" => $_FILES['file']['name']));
 print("$upload");
}

Du arbeitest in einer Agentur oder als Freelancer?
Dann wirf doch mal einen Blick auf unsere Software FeatValue.

Weiterlesen: ⯈ Umfragensystem

Über uns

Stefan Wienströer

Wir entwickeln Webanwendungen mit viel Leidenschaft. Unser Wissen geben wir dabei gerne weiter. Mehr über a coding project

Cookie-Einstellungen

Helfen Sie dabei, uns noch besser zu machen. Wir nutzen Cookies und ähnliche Technologien, um die Website auf Ihre Bedürfnisse anzupassen. Zur Datenschutzerklärung

Auswahl speichern