Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/webdavlib.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00036 class webdav_client {
00037 
00042     private $_debug = false;
00043     private $sock;
00044     private $_server;
00045     private $_protocol = 'HTTP/1.1';
00046     private $_port = 80;
00047     private $_path ='/';
00048     private $_auth = false;
00049     private $_user;
00050     private $_pass;
00051 
00052     private $_socket_timeout = 5;
00053     private $_errno;
00054     private $_errstr;
00055     private $_user_agent = 'Moodle WebDav Client';
00056     private $_crlf = "\r\n";
00057     private $_req;
00058     private $_resp_status;
00059     private $_parser;
00060     private $_xmltree;
00061     private $_tree;
00062     private $_ls = array();
00063     private $_ls_ref;
00064     private $_ls_ref_cdata;
00065     private $_delete = array();
00066     private $_delete_ref;
00067     private $_delete_ref_cdata;
00068     private $_lock = array();
00069     private $_lock_ref;
00070     private $_lock_rec_cdata;
00071     private $_null = NULL;
00072     private $_header='';
00073     private $_body='';
00074     private $_connection_closed = false;
00075     private $_maxheaderlenth = 1000;
00076 
00082     function __construct($server = '', $user = '', $pass = '', $auth = false) {
00083         if (!empty($server)) {
00084             $this->_server = $server;
00085         }
00086         if (!empty($user) && !empty($pass)) {
00087             $this->user = $user;
00088             $this->pass = $pass;
00089         }
00090         $this->_auth = $auth;
00091     }
00092     public function __set($key, $value) {
00093         $property = '_' . $key;
00094         $this->$property = $value;
00095     }
00096 
00103     function set_protocol($version) {
00104         if ($version == 1) {
00105             $this->_protocol = 'HTTP/1.1';
00106         } else {
00107             $this->_protocol = 'HTTP/1.0';
00108         }
00109     }
00110 
00117     function iso8601totime($iso8601) {
00118         /*
00119 
00120          date-time       = full-date "T" full-time
00121 
00122          full-date       = date-fullyear "-" date-month "-" date-mday
00123          full-time       = partial-time time-offset
00124 
00125          date-fullyear   = 4DIGIT
00126          date-month      = 2DIGIT  ; 01-12
00127          date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on
00128          month/year
00129          time-hour       = 2DIGIT  ; 00-23
00130          time-minute     = 2DIGIT  ; 00-59
00131          time-second     = 2DIGIT  ; 00-59, 00-60 based on leap second rules
00132          time-secfrac    = "." 1*DIGIT
00133          time-numoffset  = ("+" / "-") time-hour ":" time-minute
00134          time-offset     = "Z" / time-numoffset
00135 
00136          partial-time    = time-hour ":" time-minute ":" time-second
00137                                             [time-secfrac]
00138          */
00139 
00140         $regs = array();
00141         /*         [1]        [2]        [3]        [4]        [5]        [6]  */
00142         if (preg_match('/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})Z$/', $iso8601, $regs)) {
00143             return mktime($regs[4],$regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
00144         }
00145         // to be done: regex for partial-time...apache webdav mod never returns partial-time
00146 
00147         return false;
00148     }
00149 
00154     function open() {
00155         // let's try to open a socket
00156         $this->_error_log('open a socket connection');
00157         $this->sock = fsockopen($this->_server, $this->_port, $this->_errno, $this->_errstr, $this->_socket_timeout);
00158         set_time_limit(30);
00159         if (is_resource($this->sock)) {
00160             socket_set_blocking($this->sock, true);
00161             $this->_connection_closed = false;
00162             $this->_error_log('socket is open: ' . $this->sock);
00163             return true;
00164         } else {
00165             $this->_error_log("$this->_errstr ($this->_errno)\n");
00166             return false;
00167         }
00168     }
00169 
00173     function close() {
00174         $this->_error_log('closing socket ' . $this->sock);
00175         $this->_connection_closed = true;
00176         fclose($this->sock);
00177     }
00178 
00185     function check_webdav() {
00186         $resp = $this->options();
00187         if (!$resp) {
00188             return false;
00189         }
00190         $this->_error_log($resp['header']['DAV']);
00191         // check schema
00192         if (preg_match('/1,2/', $resp['header']['DAV'])) {
00193             return true;
00194         }
00195         // otherwise return false
00196         return false;
00197     }
00198 
00199 
00204     function options() {
00205         $this->header_unset();
00206         $this->create_basic_request('OPTIONS');
00207         $this->send_request();
00208         $this->get_respond();
00209         $response = $this->process_respond();
00210         // validate the response ...
00211         // check http-version
00212         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00213             $response['status']['http-version'] == 'HTTP/1.0') {
00214                 return $response;
00215             }
00216         $this->_error_log('Response was not even http');
00217         return false;
00218 
00219     }
00220 
00228     function mkcol($path) {
00229         $this->_path = $this->translate_uri($path);
00230         $this->header_unset();
00231         $this->create_basic_request('MKCOL');
00232         $this->send_request();
00233         $this->get_respond();
00234         $response = $this->process_respond();
00235         // validate the response ...
00236         // check http-version
00237         $http_version = $response['status']['http-version'];
00238         if ($http_version == 'HTTP/1.1' || $http_version == 'HTTP/1.0') {
00253             return $response['status']['status-code'];
00254         }
00255 
00256     }
00257 
00265     function get($path, &$buffer) {
00266         $this->_path = $this->translate_uri($path);
00267         $this->header_unset();
00268         $this->create_basic_request('GET');
00269         $this->send_request();
00270         $this->get_respond();
00271         $response = $this->process_respond();
00272 
00273         $http_version = $response['status']['http-version'];
00274         // validate the response
00275         // check http-version
00276         if ($http_version == 'HTTP/1.1' || $http_version == 'HTTP/1.0') {
00277                 // seems to be http ... proceed
00278                 // We expect a 200 code
00279                 if ($response['status']['status-code'] == 200 ) {
00280                     $this->_error_log('returning buffer with ' . strlen($response['body']) . ' bytes.');
00281                     $buffer = $response['body'];
00282                 }
00283                 return $response['status']['status-code'];
00284             }
00285         // ups: no http status was returned ?
00286         return false;
00287     }
00288 
00297     function put($path, $data ) {
00298         $this->_path = $this->translate_uri($path);
00299         $this->header_unset();
00300         $this->create_basic_request('PUT');
00301         // add more needed header information ...
00302         $this->header_add('Content-length: ' . strlen($data));
00303         $this->header_add('Content-type: application/octet-stream');
00304         // send header
00305         $this->send_request();
00306         // send the rest (data)
00307         fputs($this->sock, $data);
00308         $this->get_respond();
00309         $response = $this->process_respond();
00310 
00311         // validate the response
00312         // check http-version
00313         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00314             $response['status']['http-version'] == 'HTTP/1.0') {
00315                 // seems to be http ... proceed
00316                 // We expect a 200 or 204 status code
00317                 // see rfc 2068 - 9.6 PUT...
00318                 // print 'http ok<br>';
00319                 return $response['status']['status-code'];
00320             }
00321         // ups: no http status was returned ?
00322         return false;
00323     }
00324 
00336     function put_file($path, $filename) {
00337         // try to open the file ...
00338 
00339 
00340         $handle = @fopen ($filename, 'r');
00341 
00342         if ($handle) {
00343             // $this->sock = pfsockopen ($this->_server, $this->_port, $this->_errno, $this->_errstr, $this->_socket_timeout);
00344             $this->_path = $this->translate_uri($path);
00345             $this->header_unset();
00346             $this->create_basic_request('PUT');
00347             // add more needed header information ...
00348             $this->header_add('Content-length: ' . filesize($filename));
00349             $this->header_add('Content-type: application/octet-stream');
00350             // send header
00351             $this->send_request();
00352             while (!feof($handle)) {
00353                 fputs($this->sock,fgets($handle,4096));
00354             }
00355             fclose($handle);
00356             $this->get_respond();
00357             $response = $this->process_respond();
00358 
00359             // validate the response
00360             // check http-version
00361             if ($response['status']['http-version'] == 'HTTP/1.1' ||
00362                 $response['status']['http-version'] == 'HTTP/1.0') {
00363                     // seems to be http ... proceed
00364                     // We expect a 200 or 204 status code
00365                     // see rfc 2068 - 9.6 PUT...
00366                     // print 'http ok<br>';
00367                     return $response['status']['status-code'];
00368                 }
00369             // ups: no http status was returned ?
00370             return false;
00371         } else {
00372             $this->_error_log('put_file: could not open ' . $filename);
00373             return false;
00374         }
00375 
00376     }
00377 
00387     function get_file($srcpath, $localpath) {
00388 
00389         if ($this->get($srcpath, $buffer)) {
00390             // convert utf-8 filename to iso-8859-1
00391 
00392             $localpath = $this->utf_decode_path($localpath);
00393 
00394             $handle = fopen ($localpath, 'w');
00395             if ($handle) {
00396                 fwrite($handle, $buffer);
00397                 fclose($handle);
00398                 return true;
00399             } else {
00400                 return false;
00401             }
00402         } else {
00403             return false;
00404         }
00405     }
00406 
00419     function copy_file($src_path, $dst_path, $overwrite) {
00420         $this->_path = $this->translate_uri($src_path);
00421         $this->header_unset();
00422         $this->create_basic_request('COPY');
00423         $this->header_add(sprintf('Destination: http://%s%s', $this->_server, $this->translate_uri($dst_path)));
00424         if ($overwrite) {
00425             $this->header_add('Overwrite: T');
00426         } else {
00427             $this->header_add('Overwrite: F');
00428         }
00429         $this->header_add('');
00430         $this->send_request();
00431         $this->get_respond();
00432         $response = $this->process_respond();
00433         // validate the response ...
00434         // check http-version
00435         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00436             $response['status']['http-version'] == 'HTTP/1.0') {
00437          /* seems to be http ... proceed
00438              just return what server gave us (as defined in rfc 2518) :
00439              201 (Created) - The source resource was successfully copied. The copy operation resulted in the creation of a new resource.
00440              204 (No Content) - The source resource was successfully copied to a pre-existing destination resource.
00441              403 (Forbidden) - The source and destination URIs are the same.
00442              409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created.
00443              412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element
00444                      or the Overwrite header is "F" and the state of the destination resource is non-null.
00445              423 (Locked) - The destination resource was locked.
00446              502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
00447              507 (Insufficient Storage) - The destination resource does not have sufficient space to record the state of the resource after the
00448                      execution of this method.
00449           */
00450                 return $response['status']['status-code'];
00451             }
00452         return false;
00453     }
00454 
00467     function copy_coll($src_path, $dst_path, $overwrite) {
00468         $this->_path = $this->translate_uri($src_path);
00469         $this->header_unset();
00470         $this->create_basic_request('COPY');
00471         $this->header_add(sprintf('Destination: http://%s%s', $this->_server, $this->translate_uri($dst_path)));
00472         $this->header_add('Depth: Infinity');
00473 
00474         $xml  = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n";
00475         $xml .= "<d:propertybehavior xmlns:d=\"DAV:\">\r\n";
00476         $xml .= "  <d:keepalive>*</d:keepalive>\r\n";
00477         $xml .= "</d:propertybehavior>\r\n";
00478 
00479         $this->header_add('Content-length: ' . strlen($xml));
00480         $this->header_add('Content-type: application/xml');
00481         $this->send_request();
00482         // send also xml
00483         fputs($this->sock, $xml);
00484         $this->get_respond();
00485         $response = $this->process_respond();
00486         // validate the response ...
00487         // check http-version
00488         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00489             $response['status']['http-version'] == 'HTTP/1.0') {
00490          /* seems to be http ... proceed
00491              just return what server gave us (as defined in rfc 2518) :
00492              201 (Created) - The source resource was successfully copied. The copy operation resulted in the creation of a new resource.
00493              204 (No Content) - The source resource was successfully copied to a pre-existing destination resource.
00494              403 (Forbidden) - The source and destination URIs are the same.
00495              409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created.
00496              412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element
00497                      or the Overwrite header is "F" and the state of the destination resource is non-null.
00498              423 (Locked) - The destination resource was locked.
00499              502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
00500              507 (Insufficient Storage) - The destination resource does not have sufficient space to record the state of the resource after the
00501                      execution of this method.
00502           */
00503                 return $response['status']['status-code'];
00504             }
00505         return false;
00506     }
00507 
00518     // --------------------------------------------------------------------------
00519     // public method move
00520     // move/rename a file/collection on webdav server
00521     function move($src_path,$dst_path, $overwrite) {
00522 
00523         $this->_path = $this->translate_uri($src_path);
00524         $this->header_unset();
00525 
00526         $this->create_basic_request('MOVE');
00527         $this->header_add(sprintf('Destination: http://%s%s', $this->_server, $this->translate_uri($dst_path)));
00528         if ($overwrite) {
00529             $this->header_add('Overwrite: T');
00530         } else {
00531             $this->header_add('Overwrite: F');
00532         }
00533         $this->header_add('');
00534 
00535         $this->send_request();
00536         $this->get_respond();
00537         $response = $this->process_respond();
00538         // validate the response ...
00539         // check http-version
00540         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00541             $response['status']['http-version'] == 'HTTP/1.0') {
00542             /* seems to be http ... proceed
00543                 just return what server gave us (as defined in rfc 2518) :
00544                 201 (Created) - The source resource was successfully moved, and a new resource was created at the destination.
00545                 204 (No Content) - The source resource was successfully moved to a pre-existing destination resource.
00546                 403 (Forbidden) - The source and destination URIs are the same.
00547                 409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created.
00548                 412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element
00549                          or the Overwrite header is "F" and the state of the destination resource is non-null.
00550                 423 (Locked) - The source or the destination resource was locked.
00551                 502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
00552 
00553                 201 (Created) - The collection or structured resource was created in its entirety.
00554                 403 (Forbidden) - This indicates at least one of two conditions: 1) the server does not allow the creation of collections at the given
00555                                                  location in its namespace, or 2) the parent collection of the Request-URI exists but cannot accept members.
00556                 405 (Method Not Allowed) - MKCOL can only be executed on a deleted/non-existent resource.
00557                 409 (Conflict) - A collection cannot be made at the Request-URI until one or more intermediate collections have been created.
00558                 415 (Unsupported Media Type)- The server does not support the request type of the body.
00559                 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the resource after the execution of this method.
00560              */
00561                 return $response['status']['status-code'];
00562             }
00563         return false;
00564     }
00565 
00576     function lock($path) {
00577         $this->_path = $this->translate_uri($path);
00578         $this->header_unset();
00579         $this->create_basic_request('LOCK');
00580         $this->header_add('Timeout: Infinite');
00581         $this->header_add('Content-type: text/xml');
00582         // create the xml request ...
00583         $xml =  "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n";
00584         $xml .= "<D:lockinfo xmlns:D='DAV:'\r\n>";
00585         $xml .= "  <D:lockscope><D:exclusive/></D:lockscope>\r\n";
00586         $xml .= "  <D:locktype><D:write/></D:locktype>\r\n";
00587         $xml .= "  <D:owner>\r\n";
00588         $xml .= "    <D:href>".($this->_user)."</D:href>\r\n";
00589         $xml .= "  </D:owner>\r\n";
00590         $xml .= "</D:lockinfo>\r\n";
00591         $this->header_add('Content-length: ' . strlen($xml));
00592         $this->send_request();
00593         // send also xml
00594         fputs($this->sock, $xml);
00595         $this->get_respond();
00596         $response = $this->process_respond();
00597         // validate the response ... (only basic validation)
00598         // check http-version
00599         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00600             $response['status']['http-version'] == 'HTTP/1.0') {
00601             /* seems to be http ... proceed
00602             rfc 2518 says:
00603             200 (OK) - The lock request succeeded and the value of the lockdiscovery property is included in the body.
00604             412 (Precondition Failed) - The included lock token was not enforceable on this resource or the server could not satisfy the
00605                      request in the lockinfo XML element.
00606             423 (Locked) - The resource is locked, so the method has been rejected.
00607              */
00608 
00609                 switch($response['status']['status-code']) {
00610                 case 200:
00611                     // collection was successfully locked... see xml response to get lock token...
00612                     if (strcmp($response['header']['Content-Type'], 'text/xml; charset="utf-8"') == 0) {
00613                         // ok let's get the content of the xml stuff
00614                         $this->_parser = xml_parser_create_ns();
00615                         // forget old data...
00616                         unset($this->_lock[$this->_parser]);
00617                         unset($this->_xmltree[$this->_parser]);
00618                         xml_parser_set_option($this->_parser,XML_OPTION_SKIP_WHITE,0);
00619                         xml_parser_set_option($this->_parser,XML_OPTION_CASE_FOLDING,0);
00620                         xml_set_object($this->_parser, $this);
00621                         xml_set_element_handler($this->_parser, "_lock_startElement", "_endElement");
00622                         xml_set_character_data_handler($this->_parser, "_lock_cdata");
00623 
00624                         if (!xml_parse($this->_parser, $response['body'])) {
00625                             die(sprintf("XML error: %s at line %d",
00626                                 xml_error_string(xml_get_error_code($this->_parser)),
00627                                 xml_get_current_line_number($this->_parser)));
00628                         }
00629 
00630                         // Free resources
00631                         xml_parser_free($this->_parser);
00632                         // add status code to array
00633                         $this->_lock[$this->_parser]['status'] = 200;
00634                         return $this->_lock[$this->_parser];
00635 
00636                     } else {
00637                         print 'Missing Content-Type: text/xml header in response.<br>';
00638                     }
00639                     return false;
00640 
00641                 default:
00642                     // hmm. not what we expected. Just return what we got from webdav server
00643                     // someone else has to handle it.
00644                     $this->_lock['status'] = $response['status']['status-code'];
00645                     return $this->_lock;
00646                 }
00647             }
00648 
00649 
00650     }
00651 
00652 
00661     function unlock($path, $locktoken) {
00662         $this->_path = $this->translate_uri($path);
00663         $this->header_unset();
00664         $this->create_basic_request('UNLOCK');
00665         $this->header_add(sprintf('Lock-Token: <%s>', $locktoken));
00666         $this->send_request();
00667         $this->get_respond();
00668         $response = $this->process_respond();
00669         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00670             $response['status']['http-version'] == 'HTTP/1.0') {
00671             /* seems to be http ... proceed
00672             rfc 2518 says:
00673             204 (OK) - The 204 (No Content) status code is used instead of 200 (OK) because there is no response entity body.
00674              */
00675                 return $response['status']['status-code'];
00676             }
00677         return false;
00678     }
00679 
00687     function delete($path) {
00688         $this->_path = $this->translate_uri($path);
00689         $this->header_unset();
00690         $this->create_basic_request('DELETE');
00691         /* $this->header_add('Content-Length: 0'); */
00692         $this->header_add('');
00693         $this->send_request();
00694         $this->get_respond();
00695         $response = $this->process_respond();
00696 
00697         // validate the response ...
00698         // check http-version
00699         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00700             $response['status']['http-version'] == 'HTTP/1.0') {
00701                 // seems to be http ... proceed
00702                 // We expect a 207 Multi-Status status code
00703                 // print 'http ok<br>';
00704 
00705                 switch ($response['status']['status-code']) {
00706                 case 207:
00707                     // collection was NOT deleted... see xml response for reason...
00708                     // next there should be a Content-Type: text/xml; charset="utf-8" header line
00709                     if (strcmp($response['header']['Content-Type'], 'text/xml; charset="utf-8"') == 0) {
00710                         // ok let's get the content of the xml stuff
00711                         $this->_parser = xml_parser_create_ns();
00712                         // forget old data...
00713                         unset($this->_delete[$this->_parser]);
00714                         unset($this->_xmltree[$this->_parser]);
00715                         xml_parser_set_option($this->_parser,XML_OPTION_SKIP_WHITE,0);
00716                         xml_parser_set_option($this->_parser,XML_OPTION_CASE_FOLDING,0);
00717                         xml_set_object($this->_parser, $this);
00718                         xml_set_element_handler($this->_parser, "_delete_startElement", "_endElement");
00719                         xml_set_character_data_handler($this->_parser, "_delete_cdata");
00720 
00721                         if (!xml_parse($this->_parser, $response['body'])) {
00722                             die(sprintf("XML error: %s at line %d",
00723                                 xml_error_string(xml_get_error_code($this->_parser)),
00724                                 xml_get_current_line_number($this->_parser)));
00725                         }
00726 
00727                         print "<br>";
00728 
00729                         // Free resources
00730                         xml_parser_free($this->_parser);
00731                         $this->_delete[$this->_parser]['status'] = $response['status']['status-code'];
00732                         return $this->_delete[$this->_parser];
00733 
00734                     } else {
00735                         print 'Missing Content-Type: text/xml header in response.<br>';
00736                     }
00737                     return false;
00738 
00739                 default:
00740                     // collection or file was successfully deleted
00741                     $this->_delete['status'] = $response['status']['status-code'];
00742                     return $this->_delete;
00743 
00744 
00745                 }
00746             }
00747 
00748     }
00749 
00760     function ls($path) {
00761 
00762         if (trim($path) == '') {
00763             $this->_error_log('Missing a path in method ls');
00764             return false;
00765         }
00766         $this->_path = $this->translate_uri($path);
00767 
00768         $this->header_unset();
00769         $this->create_basic_request('PROPFIND');
00770         $this->header_add('Depth: 1');
00771         $this->header_add('Content-type: application/xml');
00772         // create profind xml request...
00773         $xml  = <<<EOD
00774 <?xml version="1.0" encoding="utf-8"?>
00775 <propfind xmlns="DAV:"><prop>
00776 <getcontentlength xmlns="DAV:"/>
00777 <getlastmodified xmlns="DAV:"/>
00778 <executable xmlns="http://apache.org/dav/props/"/>
00779 <resourcetype xmlns="DAV:"/>
00780 <checked-in xmlns="DAV:"/>
00781 <checked-out xmlns="DAV:"/>
00782 </prop></propfind>
00783 EOD;
00784         $this->header_add('Content-length: ' . strlen($xml));
00785         $this->send_request();
00786         $this->_error_log($xml);
00787         fputs($this->sock, $xml);
00788         $this->get_respond();
00789         $response = $this->process_respond();
00790         // validate the response ... (only basic validation)
00791         // check http-version
00792         if ($response['status']['http-version'] == 'HTTP/1.1' ||
00793             $response['status']['http-version'] == 'HTTP/1.0') {
00794                 // seems to be http ... proceed
00795                 // We expect a 207 Multi-Status status code
00796                 // print 'http ok<br>';
00797                 if (strcmp($response['status']['status-code'],'207') == 0 ) {
00798                     // ok so far
00799                     // next there should be a Content-Type: text/xml; charset="utf-8" header line
00800                     if (preg_match('#(application|text)/xml;\s?charset=[\'\"]?utf-8[\'\"]?#i', $response['header']['Content-Type'])) {
00801                         // ok let's get the content of the xml stuff
00802                         $this->_parser = xml_parser_create_ns('UTF-8');
00803                         // forget old data...
00804                         unset($this->_ls[$this->_parser]);
00805                         unset($this->_xmltree[$this->_parser]);
00806                         xml_parser_set_option($this->_parser,XML_OPTION_SKIP_WHITE,0);
00807                         xml_parser_set_option($this->_parser,XML_OPTION_CASE_FOLDING,0);
00808                         // xml_parser_set_option($this->_parser,XML_OPTION_TARGET_ENCODING,'UTF-8');
00809                         xml_set_object($this->_parser, $this);
00810                         xml_set_element_handler($this->_parser, "_propfind_startElement", "_endElement");
00811                         xml_set_character_data_handler($this->_parser, "_propfind_cdata");
00812 
00813 
00814                         if (!xml_parse($this->_parser, $response['body'])) {
00815                             die(sprintf("XML error: %s at line %d",
00816                                 xml_error_string(xml_get_error_code($this->_parser)),
00817                                 xml_get_current_line_number($this->_parser)));
00818                         }
00819 
00820                         // Free resources
00821                         xml_parser_free($this->_parser);
00822                         $arr = $this->_ls[$this->_parser];
00823                         return $arr;
00824                     } else {
00825                         $this->_error_log('Missing Content-Type: text/xml header in response!!');
00826                         return false;
00827                     }
00828                 } else {
00829                     // return status code ...
00830                     return $response['status']['status-code'];
00831                 }
00832             }
00833 
00834         // response was not http
00835         $this->_error_log('Ups in method ls: error in response from server');
00836         return false;
00837     }
00838 
00839 
00848     function gpi($path) {
00849 
00850         // split path by last "/"
00851         $path = rtrim($path, "/");
00852         $item = basename($path);
00853         $dir  = dirname($path);
00854 
00855         $list = $this->ls($dir);
00856 
00857         // be sure it is an array
00858         if (is_array($list)) {
00859             foreach($list as $e) {
00860 
00861                 $fullpath = urldecode($e['href']);
00862                 $filename = basename($fullpath);
00863 
00864                 if ($filename == $item && $filename != "" and $fullpath != $dir."/") {
00865                     return $e;
00866                 }
00867             }
00868         }
00869         return false;
00870     }
00871 
00880     function is_file($path) {
00881 
00882         $item = $this->gpi($path);
00883 
00884         if ($item === false) {
00885             return false;
00886         } else {
00887             return ($item['resourcetype'] != 'collection');
00888         }
00889     }
00890 
00898     function is_dir($path) {
00899 
00900         // be sure path is utf-8
00901         $item = $this->gpi($path);
00902 
00903         if ($item === false) {
00904             return false;
00905         } else {
00906             return ($item['resourcetype'] == 'collection');
00907         }
00908     }
00909 
00910 
00922     function mput($filelist) {
00923 
00924         $result = true;
00925 
00926         while (list($localpath, $destpath) = each($filelist)) {
00927 
00928             $localpath = rtrim($localpath, "/");
00929             $destpath  = rtrim($destpath, "/");
00930 
00931             // attempt to create target path
00932             if (is_dir($localpath)) {
00933                 $pathparts = explode("/", $destpath."/ "); // add one level, last level will be created as dir
00934             } else {
00935                 $pathparts = explode("/", $destpath);
00936             }
00937             $checkpath = "";
00938             for ($i=1; $i<sizeof($pathparts)-1; $i++) {
00939                 $checkpath .= "/" . $pathparts[$i];
00940                 if (!($this->is_dir($checkpath))) {
00941 
00942                     $result &= ($this->mkcol($checkpath) == 201 );
00943                 }
00944             }
00945 
00946             if ($result) {
00947                 // recurse directories
00948                 if (is_dir($localpath)) {
00949                     $dp = opendir($localpath);
00950                     $fl = array();
00951                     while($filename = readdir($dp)) {
00952                         if ((is_file($localpath."/".$filename) || is_dir($localpath."/".$filename)) && $filename!="." && $filename != "..") {
00953                             $fl[$localpath."/".$filename] = $destpath."/".$filename;
00954                         }
00955                     }
00956                     $result &= $this->mput($fl);
00957                 } else {
00958                     $result &= ($this->put_file($destpath, $localpath) == 201);
00959                 }
00960             }
00961         }
00962         return $result;
00963     }
00964 
00976     function mget($filelist) {
00977 
00978         $result = true;
00979 
00980         while (list($remotepath, $localpath) = each($filelist)) {
00981 
00982             $localpath   = rtrim($localpath, "/");
00983             $remotepath  = rtrim($remotepath, "/");
00984 
00985             // attempt to create local path
00986             if ($this->is_dir($remotepath)) {
00987                 $pathparts = explode("/", $localpath."/ "); // add one level, last level will be created as dir
00988             } else {
00989                 $pathparts = explode("/", $localpath);
00990             }
00991             $checkpath = "";
00992             for ($i=1; $i<sizeof($pathparts)-1; $i++) {
00993                 $checkpath .= "/" . $pathparts[$i];
00994                 if (!is_dir($checkpath)) {
00995 
00996                     $result &= mkdir($checkpath);
00997                 }
00998             }
00999 
01000             if ($result) {
01001                 // recurse directories
01002                 if ($this->is_dir($remotepath)) {
01003                     $list = $this->ls($remotepath);
01004 
01005                     $fl = array();
01006                     foreach($list as $e) {
01007                         $fullpath = urldecode($e['href']);
01008                         $filename = basename($fullpath);
01009                         if ($filename != '' and $fullpath != $remotepath . '/') {
01010                             $fl[$remotepath."/".$filename] = $localpath."/".$filename;
01011                         }
01012                     }
01013                     $result &= $this->mget($fl);
01014                 } else {
01015                     $result &= ($this->get_file($remotepath, $localpath));
01016                 }
01017             }
01018         }
01019         return $result;
01020     }
01021 
01022     // --------------------------------------------------------------------------
01023     // private xml callback and helper functions starting here
01024     // --------------------------------------------------------------------------
01025 
01026 
01036     private function _endElement($parser, $name) {
01037         // end tag was found...
01038         $this->_xmltree[$parser] = substr($this->_xmltree[$parser],0, strlen($this->_xmltree[$parser]) - (strlen($name) + 1));
01039     }
01040 
01051     private function _propfind_startElement($parser, $name, $attrs) {
01052         // lower XML Names... maybe break a RFC, don't know ...
01053 
01054         $propname = strtolower($name);
01055         if (!empty($this->_xmltree[$parser])) {
01056             $this->_xmltree[$parser] .= $propname . '_';
01057         } else {
01058             $this->_xmltree[$parser] = $propname . '_';
01059         }
01060 
01061         // translate xml tree to a flat array ...
01062         switch($this->_xmltree[$parser]) {
01063         case 'dav::multistatus_dav::response_':
01064             // new element in mu
01065             $this->_ls_ref =& $this->_ls[$parser][];
01066             break;
01067         case 'dav::multistatus_dav::response_dav::href_':
01068             $this->_ls_ref_cdata = &$this->_ls_ref['href'];
01069             break;
01070         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::creationdate_':
01071             $this->_ls_ref_cdata = &$this->_ls_ref['creationdate'];
01072             break;
01073         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::getlastmodified_':
01074             $this->_ls_ref_cdata = &$this->_ls_ref['lastmodified'];
01075             break;
01076         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::getcontenttype_':
01077             $this->_ls_ref_cdata = &$this->_ls_ref['getcontenttype'];
01078             break;
01079         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::getcontentlength_':
01080             $this->_ls_ref_cdata = &$this->_ls_ref['getcontentlength'];
01081             break;
01082         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::depth_':
01083             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_depth'];
01084             break;
01085         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_dav::href_':
01086             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_owner'];
01087             break;
01088         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_':
01089             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_owner'];
01090             break;
01091         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::timeout_':
01092             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_timeout'];
01093             break;
01094         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::locktoken_dav::href_':
01095             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_token'];
01096             break;
01097         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::locktype_dav::write_':
01098             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_type'];
01099             $this->_ls_ref_cdata = 'write';
01100             $this->_ls_ref_cdata = &$this->_null;
01101             break;
01102         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::resourcetype_dav::collection_':
01103             $this->_ls_ref_cdata = &$this->_ls_ref['resourcetype'];
01104             $this->_ls_ref_cdata = 'collection';
01105             $this->_ls_ref_cdata = &$this->_null;
01106             break;
01107         case 'dav::multistatus_dav::response_dav::propstat_dav::status_':
01108             $this->_ls_ref_cdata = &$this->_ls_ref['status'];
01109             break;
01110 
01111         default:
01112             // handle unknown xml elements...
01113             $this->_ls_ref_cdata = &$this->_ls_ref[$this->_xmltree[$parser]];
01114         }
01115     }
01116 
01127     private function _propfind_cData($parser, $cdata) {
01128         if (trim($cdata) <> '') {
01129             // cdata must be appended, because sometimes the php xml parser makes multiple calls
01130             // to _propfind_cData before the xml end tag was reached...
01131             $this->_ls_ref_cdata .= $cdata;
01132         } else {
01133             // do nothing
01134         }
01135     }
01136 
01146     private function _delete_startElement($parser, $name, $attrs) {
01147         // lower XML Names... maybe break a RFC, don't know ...
01148         $propname = strtolower($name);
01149         $this->_xmltree[$parser] .= $propname . '_';
01150 
01151         // translate xml tree to a flat array ...
01152         switch($this->_xmltree[$parser]) {
01153         case 'dav::multistatus_dav::response_':
01154             // new element in mu
01155             $this->_delete_ref =& $this->_delete[$parser][];
01156             break;
01157         case 'dav::multistatus_dav::response_dav::href_':
01158             $this->_delete_ref_cdata = &$this->_ls_ref['href'];
01159             break;
01160 
01161         default:
01162             // handle unknown xml elements...
01163             $this->_delete_cdata = &$this->_delete_ref[$this->_xmltree[$parser]];
01164         }
01165     }
01166 
01167 
01178     private function _delete_cData($parser, $cdata) {
01179         if (trim($cdata) <> '') {
01180             $this->_delete_ref_cdata .= $cdata;
01181         } else {
01182             // do nothing
01183         }
01184     }
01185 
01186 
01197     private function _lock_startElement($parser, $name, $attrs) {
01198         // lower XML Names... maybe break a RFC, don't know ...
01199         $propname = strtolower($name);
01200         $this->_xmltree[$parser] .= $propname . '_';
01201 
01202         // translate xml tree to a flat array ...
01203         /*
01204         dav::prop_dav::lockdiscovery_dav::activelock_dav::depth_=
01205         dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_dav::href_=
01206         dav::prop_dav::lockdiscovery_dav::activelock_dav::timeout_=
01207         dav::prop_dav::lockdiscovery_dav::activelock_dav::locktoken_dav::href_=
01208          */
01209         switch($this->_xmltree[$parser]) {
01210         case 'dav::prop_dav::lockdiscovery_dav::activelock_':
01211             // new element
01212             $this->_lock_ref =& $this->_lock[$parser][];
01213             break;
01214         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::locktype_dav::write_':
01215             $this->_lock_ref_cdata = &$this->_lock_ref['locktype'];
01216             $this->_lock_cdata = 'write';
01217             $this->_lock_cdata = &$this->_null;
01218             break;
01219         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::lockscope_dav::exclusive_':
01220             $this->_lock_ref_cdata = &$this->_lock_ref['lockscope'];
01221             $this->_lock_ref_cdata = 'exclusive';
01222             $this->_lock_ref_cdata = &$this->_null;
01223             break;
01224         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::depth_':
01225             $this->_lock_ref_cdata = &$this->_lock_ref['depth'];
01226             break;
01227         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_dav::href_':
01228             $this->_lock_ref_cdata = &$this->_lock_ref['owner'];
01229             break;
01230         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::timeout_':
01231             $this->_lock_ref_cdata = &$this->_lock_ref['timeout'];
01232             break;
01233         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::locktoken_dav::href_':
01234             $this->_lock_ref_cdata = &$this->_lock_ref['locktoken'];
01235             break;
01236         default:
01237             // handle unknown xml elements...
01238             $this->_lock_cdata = &$this->_lock_ref[$this->_xmltree[$parser]];
01239 
01240         }
01241     }
01242 
01253     private function _lock_cData($parser, $cdata) {
01254         if (trim($cdata) <> '') {
01255             // $this->_error_log(($this->_xmltree[$parser]) . '='. htmlentities($cdata));
01256             $this->_lock_ref_cdata .= $cdata;
01257         } else {
01258             // do nothing
01259         }
01260     }
01261 
01262 
01270     private function header_add($string) {
01271         $this->_req[] = $string;
01272     }
01273 
01281     private function header_unset() {
01282         unset($this->_req);
01283     }
01284 
01292     private function create_basic_request($method) {
01293         $request = '';
01294         $this->header_add(sprintf('%s %s %s', $method, $this->_path, $this->_protocol));
01295         $this->header_add(sprintf('Host: %s:%s', $this->_server, $this->_port));
01296         //$request .= sprintf('Connection: Keep-Alive');
01297         $this->header_add(sprintf('User-Agent: %s', $this->_user_agent));
01298         $this->header_add('Connection: TE');
01299         $this->header_add('TE: Trailers');
01300         if ($this->_auth == 'basic') {
01301             $this->header_add(sprintf('Authorization: Basic %s', base64_encode("$this->_user:$this->_pass")));
01302         }
01303     }
01304 
01312     private function send_request() {
01313         // check if stream is declared to be open
01314         // only logical check we are not sure if socket is really still open ...
01315         if ($this->_connection_closed) {
01316             // reopen it
01317             // be sure to close the open socket.
01318             $this->close();
01319             $this->reopen();
01320         }
01321 
01322         // convert array to string
01323         $buffer = implode("\r\n", $this->_req);
01324         $buffer .= "\r\n\r\n";
01325         $this->_error_log($buffer);
01326         fputs($this->sock, $buffer);
01327     }
01328 
01340     private function get_respond() {
01341         $this->_error_log('get_respond()');
01342         // init vars (good coding style ;-)
01343         $buffer = '';
01344         $header = '';
01345         // attention: do not make max_chunk_size to big....
01346         $max_chunk_size = 8192;
01347         // be sure we got a open ressource
01348         if (! $this->sock) {
01349             $this->_error_log('socket is not open. Can not process response');
01350             return false;
01351         }
01352 
01353         // following code maybe helps to improve socket behaviour ... more testing needed
01354         // disabled at the moment ...
01355         // socket_set_timeout($this->sock,1 );
01356         // $socket_state = socket_get_status($this->sock);
01357 
01358         // read stream one byte by another until http header ends
01359         $i = 0;
01360         $matches = array();
01361         do {
01362             $header.=fread($this->sock, 1);
01363             $i++;
01364         } while (!preg_match('/\\r\\n\\r\\n$/',$header, $matches) && $i < $this->_maxheaderlenth);
01365 
01366         $this->_error_log($header);
01367 
01368         if (preg_match('/Connection: close\\r\\n/', $header)) {
01369             // This says that the server will close connection at the end of this stream.
01370             // Therefore we need to reopen the socket, before are sending the next request...
01371             $this->_error_log('Connection: close found');
01372             $this->_connection_closed = true;
01373         }
01374         // check how to get the data on socket stream
01375         // chunked or content-length (HTTP/1.1) or
01376         // one block until feof is received (HTTP/1.0)
01377         switch(true) {
01378         case (preg_match('/Transfer\\-Encoding:\\s+chunked\\r\\n/',$header)):
01379             $this->_error_log('Getting HTTP/1.1 chunked data...');
01380             do {
01381                 $byte = '';
01382                 $chunk_size='';
01383                 do {
01384                     $chunk_size.=$byte;
01385                     $byte=fread($this->sock,1);
01386                     // check what happens while reading, because I do not really understand how php reads the socketstream...
01387                     // but so far - it seems to work here - tested with php v4.3.1 on apache 1.3.27 and Debian Linux 3.0 ...
01388                     if (strlen($byte) == 0) {
01389                         $this->_error_log('get_respond: warning --> read zero bytes');
01390                     }
01391                 } while ($byte!="\r" and strlen($byte)>0);      // till we match the Carriage Return
01392                 fread($this->sock, 1);                           // also drop off the Line Feed
01393                 $chunk_size=hexdec($chunk_size);                // convert to a number in decimal system
01394                 if ($chunk_size > 0) {
01395                     $buffer .= fread($this->sock,$chunk_size);
01396                 }
01397                 fread($this->sock, 2);                            // ditch the CRLF that trails the chunk
01398             } while ($chunk_size);                            // till we reach the 0 length chunk (end marker)
01399             break;
01400 
01401             // check for a specified content-length
01402         case preg_match('/Content\\-Length:\\s+([0-9]*)\\r\\n/',$header,$matches):
01403             $this->_error_log('Getting data using Content-Length '. $matches[1]);
01404 
01405             // check if we the content data size is small enough to get it as one block
01406             if ($matches[1] <= $max_chunk_size ) {
01407                 // only read something if Content-Length is bigger than 0
01408                 if ($matches[1] > 0 ) {
01409                     $buffer = fread($this->sock, $matches[1]);
01410                     $loadsize = strlen($buffer);
01411                     //did we realy get the full length?
01412                     if ($loadsize < $matches[1]) {
01413                         $max_chunk_size = $loadsize;
01414                         do {
01415                             $mod = $max_chunk_size % ($matches[1] - strlen($buffer));
01416                             $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($buffer));
01417                             $buffer .= fread($this->sock, $chunk_size);
01418                             $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($buffer));
01419                         } while ($mod == $max_chunk_size);
01420                         break;
01421                     } else {
01422                         break;
01423                     }
01424                 } else {
01425                     $buffer = '';
01426                     break;
01427                 }
01428             }
01429 
01430             // data is to big to handle it as one. Get it chunk per chunk...
01431             //trying to get the full length of max_chunk_size
01432             $buffer = fread($this->sock, $max_chunk_size);
01433             $loadsize = strlen($buffer);
01434             if ($loadsize < $max_chunk_size) {
01435                 $max_chunk_size = $loadsize;
01436             }
01437             do {
01438                 $mod = $max_chunk_size % ($matches[1] - strlen($buffer));
01439                 $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($buffer));
01440                 $buffer .= fread($this->sock, $chunk_size);
01441                 $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($buffer));
01442             } while ($mod == $max_chunk_size);
01443             $loadsize = strlen($buffer);
01444             if ($loadsize < $matches[1]) {
01445                 $buffer .= fread($this->sock, $matches[1] - $loadsize);
01446             }
01447             break;
01448 
01449             // check for 204 No Content
01450             // 204 responds have no body.
01451             // Therefore we do not need to read any data from socket stream.
01452         case preg_match('/HTTP\/1\.1\ 204/',$header):
01453             // nothing to do, just proceed
01454             $this->_error_log('204 No Content found. No further data to read..');
01455             break;
01456         default:
01457             // just get the data until foef appears...
01458             $this->_error_log('reading until feof...' . $header);
01459             socket_set_timeout($this->sock, 0, 0);
01460             while (!feof($this->sock)) {
01461                 $buffer .= fread($this->sock, 4096);
01462             }
01463             // renew the socket timeout...does it do something ???? Is it needed. More debugging needed...
01464             socket_set_timeout($this->sock, $this->_socket_timeout, 0);
01465         }
01466 
01467         $this->_header = $header;
01468         $this->_body = $buffer;
01469         // $this->_buffer = $header . "\r\n\r\n" . $buffer;
01470         $this->_error_log($this->_header);
01471         $this->_error_log($this->_body);
01472     }
01473 
01474 
01483     private function process_respond() {
01484         $lines = explode("\r\n", $this->_header);
01485         $header_done = false;
01486         // $this->_error_log($this->_buffer);
01487         // First line should be a HTTP status line (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6)
01488         // Format is: HTTP-Version SP Status-Code SP Reason-Phrase CRLF
01489         list($ret_struct['status']['http-version'],
01490             $ret_struct['status']['status-code'],
01491             $ret_struct['status']['reason-phrase']) = explode(' ', $lines[0],3);
01492 
01493         // print "HTTP Version: '$http_version' Status-Code: '$status_code' Reason Phrase: '$reason_phrase'<br>";
01494         // get the response header fields
01495         // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
01496         for($i=1; $i<count($lines); $i++) {
01497             if (rtrim($lines[$i]) == '' && !$header_done) {
01498                 $header_done = true;
01499                 // print "--- response header end ---<br>";
01500 
01501             }
01502             if (!$header_done ) {
01503                 // store all found headers in array ...
01504                 list($fieldname, $fieldvalue) = explode(':', $lines[$i]);
01505                 // check if this header was allready set (apache 2.0 webdav module does this....).
01506                 // If so we add the the value to the end the fieldvalue, separated by comma...
01507                 if (empty($ret_struct['header'])) {
01508                     $ret_struct['header'] = array();
01509                 }
01510                 if (empty($ret_struct['header'][$fieldname])) {
01511                     $ret_struct['header'][$fieldname] = trim($fieldvalue);
01512                 } else {
01513                     $ret_struct['header'][$fieldname] .= ',' . trim($fieldvalue);
01514                 }
01515             }
01516         }
01517         // print 'string len of response_body:'. strlen($response_body);
01518         // print '[' . htmlentities($response_body) . ']';
01519         $ret_struct['body'] = $this->_body;
01520         $this->_error_log('process_respond: ' . var_export($ret_struct,true));
01521         return $ret_struct;
01522 
01523     }
01524 
01533     private function reopen() {
01534         // let's try to reopen a socket
01535         $this->_error_log('reopen a socket connection');
01536         return $this->open();
01537     }
01538 
01539 
01549     private function translate_uri($uri) {
01550         // remove all html entities...
01551         $native_path = html_entity_decode($uri);
01552         $parts = explode('/', $native_path);
01553         for ($i = 0; $i < count($parts); $i++) {
01554             // check if part is allready utf8
01555             if (iconv('UTF-8', 'UTF-8', $parts[$i]) == $parts[$i]) {
01556                 $parts[$i] = rawurlencode($parts[$i]);
01557             } else {
01558                 $parts[$i] = rawurlencode(utf8_encode($parts[$i]));
01559             }
01560         }
01561         return implode('/', $parts);
01562     }
01563 
01571     private function utf_decode_path($path) {
01572         $fullpath = $path;
01573         if (iconv('UTF-8', 'UTF-8', $fullpath) == $fullpath) {
01574             $this->_error_log("filename is utf-8. Needs conversion...");
01575             $fullpath = utf8_decode($fullpath);
01576         }
01577         return $fullpath;
01578     }
01579 
01587     private function _error_log($err_string) {
01588         if ($this->_debug) {
01589             error_log($err_string);
01590         }
01591     }
01592 }
 All Data Structures Namespaces Files Functions Variables Enumerations