| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 4 | //
 | 
        
           |  |  | 5 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 6 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 7 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 8 | // (at your option) any later version.
 | 
        
           |  |  | 9 | //
 | 
        
           |  |  | 10 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 13 | // GNU General Public License for more details.
 | 
        
           |  |  | 14 | //
 | 
        
           |  |  | 15 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 16 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 17 |   | 
        
           |  |  | 18 | /**
 | 
        
           |  |  | 19 |  * A Moodle-modified WebDAV client, based on
 | 
        
           |  |  | 20 |  * webdav_client v0.1.5, a php based webdav client class.
 | 
        
           |  |  | 21 |  * class webdav client. a php based nearly RFC 2518 conforming client.
 | 
        
           |  |  | 22 |  *
 | 
        
           |  |  | 23 |  * This class implements methods to get access to an webdav server.
 | 
        
           |  |  | 24 |  * Most of the methods are returning boolean false on error, an integer status (http response status) on success
 | 
        
           |  |  | 25 |  * or an array in case of a multistatus response (207) from the webdav server. Look at the code which keys are used in arrays.
 | 
        
           |  |  | 26 |  * It's your responsibility to handle the webdav server responses in an proper manner.
 | 
        
           |  |  | 27 |  * Please notice that all Filenames coming from or going to the webdav server should be UTF-8 encoded (see RFC 2518).
 | 
        
           |  |  | 28 |  * This class tries to convert all you filenames into utf-8 when it's needed.
 | 
        
           |  |  | 29 |  *
 | 
        
           |  |  | 30 |  * Moodle modifications:
 | 
        
           |  |  | 31 |  * * Moodle 3.4: Add support for OAuth 2 bearer token-based authentication
 | 
        
           |  |  | 32 |  *
 | 
        
           |  |  | 33 |  * @package moodlecore
 | 
        
           |  |  | 34 |  * @author Christian Juerges <christian.juerges@xwave.ch>, Xwave GmbH, Josefstr. 92, 8005 Zuerich - Switzerland
 | 
        
           |  |  | 35 |  * @copyright (C) 2003/2004, Christian Juerges
 | 
        
           |  |  | 36 |  * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
 | 
        
           |  |  | 37 |  */
 | 
        
           |  |  | 38 |   | 
        
           |  |  | 39 | class webdav_client {
 | 
        
           |  |  | 40 |   | 
        
           |  |  | 41 |     /**#@+
 | 
        
           |  |  | 42 |      * @access private
 | 
        
           |  |  | 43 |      * @var string
 | 
        
           |  |  | 44 |      */
 | 
        
           |  |  | 45 |     private $_debug = false;
 | 
        
           |  |  | 46 |     private $sock;
 | 
        
           |  |  | 47 |     private $_server;
 | 
        
           |  |  | 48 |     private $_protocol = 'HTTP/1.1';
 | 
        
           |  |  | 49 |     private $_port = 80;
 | 
        
           |  |  | 50 |     private $_socket = '';
 | 
        
           |  |  | 51 |     private $_path ='/';
 | 
        
           |  |  | 52 |     private $_auth = false;
 | 
        
           |  |  | 53 |   | 
        
           |  |  | 54 |     private $_socket_timeout = 5;
 | 
        
           |  |  | 55 |     private $_errno;
 | 
        
           |  |  | 56 |     private $_errstr;
 | 
        
           |  |  | 57 |     private $_user_agent = 'Moodle WebDav Client';
 | 
        
           |  |  | 58 |     private $_crlf = "\r\n";
 | 
        
           |  |  | 59 |     private $_req;
 | 
        
           |  |  | 60 |     private $_resp_status;
 | 
        
           |  |  | 61 |     private $_parser;
 | 
        
           |  |  | 62 |     private $_parserid;
 | 
        
           |  |  | 63 |     private $_xmltree;
 | 
        
           |  |  | 64 |     private $_tree;
 | 
        
           |  |  | 65 |     private $_ls = array();
 | 
        
           |  |  | 66 |     private $_ls_ref;
 | 
        
           |  |  | 67 |     private $_ls_ref_cdata;
 | 
        
           |  |  | 68 |     private $_delete = array();
 | 
        
           |  |  | 69 |     private $_delete_ref;
 | 
        
           |  |  | 70 |     private $_delete_ref_cdata;
 | 
        
           |  |  | 71 |     private $_lock = array();
 | 
        
           |  |  | 72 |     private $_lock_ref;
 | 
        
           |  |  | 73 |     private $_lock_rec_cdata;
 | 
        
           |  |  | 74 |     private $_null = NULL;
 | 
        
           |  |  | 75 |     private $_header='';
 | 
        
           |  |  | 76 |     private $_body='';
 | 
        
           |  |  | 77 |     private $_connection_closed = false;
 | 
        
           |  |  | 78 |     private $_maxheaderlenth = 65536;
 | 
        
           |  |  | 79 |     private $_digestchallenge = null;
 | 
        
           |  |  | 80 |     private $_cnonce = '';
 | 
        
           |  |  | 81 |     private $_nc = 0;
 | 
        
           |  |  | 82 |   | 
        
           |  |  | 83 |     /**
 | 
        
           |  |  | 84 |      * OAuth token used for bearer auth.
 | 
        
           |  |  | 85 |      * @var string
 | 
        
           |  |  | 86 |      */
 | 
        
           |  |  | 87 |     private $oauthtoken;
 | 
        
           |  |  | 88 |   | 
        
           |  |  | 89 |     /** @var string Username (for basic/digest auth, see $auth). */
 | 
        
           |  |  | 90 |     private $_user;
 | 
        
           |  |  | 91 |   | 
        
           |  |  | 92 |     /** @var string Password (for basic/digest auth, see $auth). */
 | 
        
           |  |  | 93 |     private $_pass;
 | 
        
           |  |  | 94 |   | 
        
           |  |  | 95 |     /** @var mixed to store xml data that need to be handled. */
 | 
        
           |  |  | 96 |     private $_lock_ref_cdata;
 | 
        
           |  |  | 97 |   | 
        
           |  |  | 98 |     /** @var mixed to store the deleted xml data. */
 | 
        
           |  |  | 99 |     private $_delete_cdata;
 | 
        
           |  |  | 100 |   | 
        
           |  |  | 101 |     /** @var string to store the locked xml data. */
 | 
        
           |  |  | 102 |     private $_lock_cdata;
 | 
        
           |  |  | 103 |   | 
        
           |  |  | 104 |     /**#@-*/
 | 
        
           |  |  | 105 |   | 
        
           |  |  | 106 |     /**
 | 
        
           |  |  | 107 |      * Constructor - Initialise class variables
 | 
        
           |  |  | 108 |      * @param string $server Hostname of the server to connect to
 | 
        
           |  |  | 109 |      * @param string $user Username (for basic/digest auth, see $auth)
 | 
        
           |  |  | 110 |      * @param string $pass Password (for basic/digest auth, see $auth)
 | 
        
           |  |  | 111 |      * @param bool $auth Authentication type; one of ['basic', 'digest', 'bearer']
 | 
        
           |  |  | 112 |      * @param string $socket Used protocol for fsockopen, usually: '' (empty) or 'ssl://'
 | 
        
           |  |  | 113 |      * @param string $oauthtoken OAuth 2 bearer token (for bearer auth, see $auth)
 | 
        
           |  |  | 114 |      */
 | 
        
           |  |  | 115 |     public function __construct($server = '', $user = '', $pass = '', $auth = false, $socket = '', $oauthtoken = '') {
 | 
        
           |  |  | 116 |         if (!empty($server)) {
 | 
        
           |  |  | 117 |             $this->_server = $server;
 | 
        
           |  |  | 118 |         }
 | 
        
           |  |  | 119 |         if (!empty($user) && !empty($pass)) {
 | 
        
           |  |  | 120 |             $this->_user = $user;
 | 
        
           |  |  | 121 |             $this->_pass = $pass;
 | 
        
           |  |  | 122 |         }
 | 
        
           |  |  | 123 |         $this->_auth = $auth;
 | 
        
           |  |  | 124 |         $this->_socket = $socket;
 | 
        
           |  |  | 125 |         if ($auth == 'bearer') {
 | 
        
           |  |  | 126 |             $this->oauthtoken = $oauthtoken;
 | 
        
           |  |  | 127 |         }
 | 
        
           |  |  | 128 |     }
 | 
        
           |  |  | 129 |     public function __set($key, $value) {
 | 
        
           |  |  | 130 |         $property = '_' . $key;
 | 
        
           |  |  | 131 |         $this->$property = $value;
 | 
        
           |  |  | 132 |     }
 | 
        
           |  |  | 133 |   | 
        
           |  |  | 134 |     /**
 | 
        
           |  |  | 135 |      * Set which HTTP protocol will be used.
 | 
        
           |  |  | 136 |      * Value 1 defines that HTTP/1.1 should be used (Keeps Connection to webdav server alive).
 | 
        
           |  |  | 137 |      * Otherwise HTTP/1.0 will be used.
 | 
        
           |  |  | 138 |      * @param int version
 | 
        
           |  |  | 139 |      */
 | 
        
           |  |  | 140 |     function set_protocol($version) {
 | 
        
           |  |  | 141 |         if ($version == 1) {
 | 
        
           |  |  | 142 |             $this->_protocol = 'HTTP/1.1';
 | 
        
           |  |  | 143 |         } else {
 | 
        
           |  |  | 144 |             $this->_protocol = 'HTTP/1.0';
 | 
        
           |  |  | 145 |         }
 | 
        
           |  |  | 146 |     }
 | 
        
           |  |  | 147 |   | 
        
           |  |  | 148 |     /**
 | 
        
           |  |  | 149 |      * Convert ISO 8601 Date and Time Profile used in RFC 2518 to an unix timestamp.
 | 
        
           |  |  | 150 |      * @access private
 | 
        
           |  |  | 151 |      * @param string iso8601
 | 
        
           |  |  | 152 |      * @return unixtimestamp on sucess. Otherwise false.
 | 
        
           |  |  | 153 |      */
 | 
        
           |  |  | 154 |     function iso8601totime($iso8601) {
 | 
        
           |  |  | 155 |         /*
 | 
        
           |  |  | 156 |   | 
        
           |  |  | 157 |          date-time       = full-date "T" full-time
 | 
        
           |  |  | 158 |   | 
        
           |  |  | 159 |          full-date       = date-fullyear "-" date-month "-" date-mday
 | 
        
           |  |  | 160 |          full-time       = partial-time time-offset
 | 
        
           |  |  | 161 |   | 
        
           |  |  | 162 |          date-fullyear   = 4DIGIT
 | 
        
           |  |  | 163 |          date-month      = 2DIGIT  ; 01-12
 | 
        
           |  |  | 164 |          date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on
 | 
        
           |  |  | 165 |          month/year
 | 
        
           |  |  | 166 |          time-hour       = 2DIGIT  ; 00-23
 | 
        
           |  |  | 167 |          time-minute     = 2DIGIT  ; 00-59
 | 
        
           |  |  | 168 |          time-second     = 2DIGIT  ; 00-59, 00-60 based on leap second rules
 | 
        
           |  |  | 169 |          time-secfrac    = "." 1*DIGIT
 | 
        
           |  |  | 170 |          time-numoffset  = ("+" / "-") time-hour ":" time-minute
 | 
        
           |  |  | 171 |          time-offset     = "Z" / time-numoffset
 | 
        
           |  |  | 172 |   | 
        
           |  |  | 173 |          partial-time    = time-hour ":" time-minute ":" time-second
 | 
        
           |  |  | 174 |                                             [time-secfrac]
 | 
        
           |  |  | 175 |          */
 | 
        
           |  |  | 176 |   | 
        
           |  |  | 177 |         $regs = array();
 | 
        
           |  |  | 178 |         /*         [1]        [2]        [3]        [4]        [5]        [6]  */
 | 
        
           |  |  | 179 |         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)) {
 | 
        
           |  |  | 180 |             return mktime($regs[4],$regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 | 
        
           |  |  | 181 |         }
 | 
        
           |  |  | 182 |         // to be done: regex for partial-time...apache webdav mod never returns partial-time
 | 
        
           |  |  | 183 |   | 
        
           |  |  | 184 |         return false;
 | 
        
           |  |  | 185 |     }
 | 
        
           |  |  | 186 |   | 
        
           |  |  | 187 |     /**
 | 
        
           |  |  | 188 |      * Open's a socket to a webdav server
 | 
        
           |  |  | 189 |      * @return bool true on success. Otherwise false.
 | 
        
           |  |  | 190 |      */
 | 
        
           |  |  | 191 |     function open() {
 | 
        
           |  |  | 192 |         // let's try to open a socket
 | 
        
           |  |  | 193 |         $this->_error_log('open a socket connection');
 | 
        
           |  |  | 194 |         $this->sock = fsockopen($this->_socket . $this->_server, $this->_port, $this->_errno, $this->_errstr, $this->_socket_timeout);
 | 
        
           |  |  | 195 |         core_php_time_limit::raise(30);
 | 
        
           |  |  | 196 |         if (is_resource($this->sock)) {
 | 
        
           |  |  | 197 |             socket_set_blocking($this->sock, true);
 | 
        
           |  |  | 198 |             $this->_connection_closed = false;
 | 
        
           |  |  | 199 |             $this->_error_log('socket is open: ' . $this->sock);
 | 
        
           |  |  | 200 |             return true;
 | 
        
           |  |  | 201 |         } else {
 | 
        
           |  |  | 202 |             $this->_error_log("$this->_errstr ($this->_errno)\n");
 | 
        
           |  |  | 203 |             return false;
 | 
        
           |  |  | 204 |         }
 | 
        
           |  |  | 205 |     }
 | 
        
           |  |  | 206 |   | 
        
           |  |  | 207 |     /**
 | 
        
           |  |  | 208 |      * Closes an open socket.
 | 
        
           |  |  | 209 |      */
 | 
        
           |  |  | 210 |     function close() {
 | 
        
           |  |  | 211 |         $this->_error_log('closing socket ' . $this->sock);
 | 
        
           |  |  | 212 |         $this->_connection_closed = true;
 | 
        
           |  |  | 213 |         if (is_resource($this->sock)) {
 | 
        
           |  |  | 214 |             // Only close the socket if it is a resource.
 | 
        
           |  |  | 215 |             fclose($this->sock);
 | 
        
           |  |  | 216 |         }
 | 
        
           |  |  | 217 |     }
 | 
        
           |  |  | 218 |   | 
        
           |  |  | 219 |     /**
 | 
        
           |  |  | 220 |      * Check's if server is a webdav compliant server.
 | 
        
           |  |  | 221 |      * True if server returns a DAV Element in Header and when
 | 
        
           |  |  | 222 |      * schema 1,2 is supported.
 | 
        
           |  |  | 223 |      * @return bool true if server is webdav server. Otherwise false.
 | 
        
           |  |  | 224 |      */
 | 
        
           |  |  | 225 |     function check_webdav() {
 | 
        
           |  |  | 226 |         $resp = $this->options();
 | 
        
           |  |  | 227 |         if (!$resp) {
 | 
        
           |  |  | 228 |             return false;
 | 
        
           |  |  | 229 |         }
 | 
        
           |  |  | 230 |         $this->_error_log($resp['header']['DAV']);
 | 
        
           |  |  | 231 |         // check schema
 | 
        
           |  |  | 232 |         if (preg_match('/1,2/', $resp['header']['DAV'])) {
 | 
        
           |  |  | 233 |             return true;
 | 
        
           |  |  | 234 |         }
 | 
        
           |  |  | 235 |         // otherwise return false
 | 
        
           |  |  | 236 |         return false;
 | 
        
           |  |  | 237 |     }
 | 
        
           |  |  | 238 |   | 
        
           |  |  | 239 |   | 
        
           |  |  | 240 |     /**
 | 
        
           |  |  | 241 |      * Get options from webdav server.
 | 
        
           |  |  | 242 |      * @return array with all header fields returned from webdav server. false if server does not speak http.
 | 
        
           |  |  | 243 |      */
 | 
        
           |  |  | 244 |     function options() {
 | 
        
           |  |  | 245 |         $this->header_unset();
 | 
        
           |  |  | 246 |         $this->create_basic_request('OPTIONS');
 | 
        
           |  |  | 247 |         $this->send_request();
 | 
        
           |  |  | 248 |         $this->get_respond();
 | 
        
           |  |  | 249 |         $response = $this->process_respond();
 | 
        
           |  |  | 250 |         // validate the response ...
 | 
        
           |  |  | 251 |         // check http-version
 | 
        
           |  |  | 252 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 253 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 254 |                 return $response;
 | 
        
           |  |  | 255 |             }
 | 
        
           |  |  | 256 |         $this->_error_log('Response was not even http');
 | 
        
           |  |  | 257 |         return false;
 | 
        
           |  |  | 258 |   | 
        
           |  |  | 259 |     }
 | 
        
           |  |  | 260 |   | 
        
           |  |  | 261 |     /**
 | 
        
           |  |  | 262 |      * Public method mkcol
 | 
        
           |  |  | 263 |      *
 | 
        
           |  |  | 264 |      * Creates a new collection/directory on a webdav server
 | 
        
           |  |  | 265 |      * @param string path
 | 
        
           |  |  | 266 |      * @return int status code received as response from webdav server (see rfc 2518)
 | 
        
           |  |  | 267 |      */
 | 
        
           |  |  | 268 |     function mkcol($path) {
 | 
        
           |  |  | 269 |         $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 270 |         $this->header_unset();
 | 
        
           |  |  | 271 |         $this->create_basic_request('MKCOL');
 | 
        
           |  |  | 272 |         $this->send_request();
 | 
        
           |  |  | 273 |         $this->get_respond();
 | 
        
           |  |  | 274 |         $response = $this->process_respond();
 | 
        
           |  |  | 275 |         // validate the response ...
 | 
        
           |  |  | 276 |         // check http-version
 | 
        
           |  |  | 277 |         $http_version = $response['status']['http-version'];
 | 
        
           |  |  | 278 |         if ($http_version == 'HTTP/1.1' || $http_version == 'HTTP/1.0') {
 | 
        
           |  |  | 279 |             /** seems to be http ... proceed
 | 
        
           |  |  | 280 |              * just return what server gave us
 | 
        
           |  |  | 281 |              * rfc 2518 says:
 | 
        
           |  |  | 282 |              * 201 (Created) - The collection or structured resource was created in its entirety.
 | 
        
           |  |  | 283 |              * 403 (Forbidden) - This indicates at least one of two conditions:
 | 
        
           |  |  | 284 |              *    1) the server does not allow the creation of collections at the given location in its namespace, or
 | 
        
           |  |  | 285 |              *    2) the parent collection of the Request-URI exists but cannot accept members.
 | 
        
           |  |  | 286 |              * 405 (Method Not Allowed) - MKCOL can only be executed on a deleted/non-existent resource.
 | 
        
           |  |  | 287 |              * 409 (Conflict) - A collection cannot be made at the Request-URI until one or more intermediate
 | 
        
           |  |  | 288 |              *                  collections have been created.
 | 
        
           |  |  | 289 |              * 415 (Unsupported Media Type)- The server does not support the request type of the body.
 | 
        
           |  |  | 290 |              * 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the
 | 
        
           |  |  | 291 |              *                              resource after the execution of this method.
 | 
        
           |  |  | 292 |              */
 | 
        
           |  |  | 293 |             return $response['status']['status-code'];
 | 
        
           |  |  | 294 |         }
 | 
        
           |  |  | 295 |   | 
        
           |  |  | 296 |     }
 | 
        
           |  |  | 297 |   | 
        
           |  |  | 298 |     /**
 | 
        
           |  |  | 299 |      * Public method get
 | 
        
           |  |  | 300 |      *
 | 
        
           |  |  | 301 |      * Gets a file from a webdav collection.
 | 
        
           |  |  | 302 |      * @param string $path the path to the file on the webdav server
 | 
        
           |  |  | 303 |      * @param string &$buffer the buffer to store the data in
 | 
        
           |  |  | 304 |      * @param resource $fp optional if included, the data is written directly to this resource and not to the buffer
 | 
        
           |  |  | 305 |      * @return string|bool status code and &$buffer (by reference) with response data from server on success. False on error.
 | 
        
           |  |  | 306 |      */
 | 
        
           |  |  | 307 |     function get($path, &$buffer, $fp = null) {
 | 
        
           |  |  | 308 |         $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 309 |         $this->header_unset();
 | 
        
           |  |  | 310 |         $this->create_basic_request('GET');
 | 
        
           |  |  | 311 |         $this->send_request();
 | 
        
           |  |  | 312 |         $this->get_respond($fp);
 | 
        
           |  |  | 313 |         $response = $this->process_respond();
 | 
        
           |  |  | 314 |   | 
        
           |  |  | 315 |         $http_version = $response['status']['http-version'];
 | 
        
           |  |  | 316 |         // validate the response
 | 
        
           |  |  | 317 |         // check http-version
 | 
        
           |  |  | 318 |         if ($http_version == 'HTTP/1.1' || $http_version == 'HTTP/1.0') {
 | 
        
           |  |  | 319 |                 // seems to be http ... proceed
 | 
        
           |  |  | 320 |                 // We expect a 200 code
 | 
        
           |  |  | 321 |                 if ($response['status']['status-code'] == 200 ) {
 | 
        
           |  |  | 322 |                     if (!is_null($fp)) {
 | 
        
           |  |  | 323 |                         $stat = fstat($fp);
 | 
        
           |  |  | 324 |                         $this->_error_log('file created with ' . $stat['size'] . ' bytes.');
 | 
        
           |  |  | 325 |                     } else {
 | 
        
           |  |  | 326 |                         $this->_error_log('returning buffer with ' . strlen($response['body']) . ' bytes.');
 | 
        
           |  |  | 327 |                         $buffer = $response['body'];
 | 
        
           |  |  | 328 |                     }
 | 
        
           |  |  | 329 |                 }
 | 
        
           |  |  | 330 |                 return $response['status']['status-code'];
 | 
        
           |  |  | 331 |             }
 | 
        
           |  |  | 332 |         // ups: no http status was returned ?
 | 
        
           |  |  | 333 |         return false;
 | 
        
           |  |  | 334 |     }
 | 
        
           |  |  | 335 |   | 
        
           |  |  | 336 |     /**
 | 
        
           |  |  | 337 |      * Public method put
 | 
        
           |  |  | 338 |      *
 | 
        
           |  |  | 339 |      * Puts a file into a collection.
 | 
        
           |  |  | 340 |      *	Data is putted as one chunk!
 | 
        
           |  |  | 341 |      * @param string path, string data
 | 
        
           |  |  | 342 |      * @return int status-code read from webdavserver. False on error.
 | 
        
           |  |  | 343 |      */
 | 
        
           |  |  | 344 |     function put($path, $data ) {
 | 
        
           |  |  | 345 |         $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 346 |         $this->header_unset();
 | 
        
           |  |  | 347 |         $this->create_basic_request('PUT');
 | 
        
           |  |  | 348 |         // add more needed header information ...
 | 
        
           |  |  | 349 |         $this->header_add('Content-length: ' . strlen($data));
 | 
        
           |  |  | 350 |         $this->header_add('Content-type: application/octet-stream');
 | 
        
           |  |  | 351 |         // send header
 | 
        
           |  |  | 352 |         $this->send_request();
 | 
        
           |  |  | 353 |         // send the rest (data)
 | 
        
           |  |  | 354 |         fputs($this->sock, $data);
 | 
        
           |  |  | 355 |         $this->get_respond();
 | 
        
           |  |  | 356 |         $response = $this->process_respond();
 | 
        
           |  |  | 357 |   | 
        
           |  |  | 358 |         // validate the response
 | 
        
           |  |  | 359 |         // check http-version
 | 
        
           |  |  | 360 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 361 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 362 |                 // seems to be http ... proceed
 | 
        
           |  |  | 363 |                 // We expect a 200 or 204 status code
 | 
        
           |  |  | 364 |                 // see rfc 2068 - 9.6 PUT...
 | 
        
           |  |  | 365 |                 // print 'http ok<br>';
 | 
        
           |  |  | 366 |                 return $response['status']['status-code'];
 | 
        
           |  |  | 367 |             }
 | 
        
           |  |  | 368 |         // ups: no http status was returned ?
 | 
        
           |  |  | 369 |         return false;
 | 
        
           |  |  | 370 |     }
 | 
        
           |  |  | 371 |   | 
        
           |  |  | 372 |     /**
 | 
        
           |  |  | 373 |      * Public method put_file
 | 
        
           |  |  | 374 |      *
 | 
        
           |  |  | 375 |      * Read a file as stream and puts it chunk by chunk into webdav server collection.
 | 
        
           |  |  | 376 |      *
 | 
        
           |  |  | 377 |      * Look at php documenation for legal filenames with fopen();
 | 
        
           |  |  | 378 |      * The filename will be translated into utf-8 if not allready in utf-8.
 | 
        
           |  |  | 379 |      *
 | 
        
           |  |  | 380 |      * @param string targetpath, string filename
 | 
        
           |  |  | 381 |      * @return int status code. False on error.
 | 
        
           |  |  | 382 |      */
 | 
        
           |  |  | 383 |     function put_file($path, $filename) {
 | 
        
           |  |  | 384 |         // try to open the file ...
 | 
        
           |  |  | 385 |   | 
        
           |  |  | 386 |   | 
        
           |  |  | 387 |         $handle = @fopen ($filename, 'r');
 | 
        
           |  |  | 388 |   | 
        
           |  |  | 389 |         if ($handle) {
 | 
        
           |  |  | 390 |             // $this->sock = pfsockopen ($this->_server, $this->_port, $this->_errno, $this->_errstr, $this->_socket_timeout);
 | 
        
           |  |  | 391 |             $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 392 |             $this->header_unset();
 | 
        
           |  |  | 393 |             $this->create_basic_request('PUT');
 | 
        
           |  |  | 394 |             // add more needed header information ...
 | 
        
           |  |  | 395 |             $this->header_add('Content-length: ' . filesize($filename));
 | 
        
           |  |  | 396 |             $this->header_add('Content-type: application/octet-stream');
 | 
        
           |  |  | 397 |             // send header
 | 
        
           |  |  | 398 |             $this->send_request();
 | 
        
           |  |  | 399 |             while (!feof($handle)) {
 | 
        
           |  |  | 400 |                 fputs($this->sock,fgets($handle,4096));
 | 
        
           |  |  | 401 |             }
 | 
        
           |  |  | 402 |             fclose($handle);
 | 
        
           |  |  | 403 |             $this->get_respond();
 | 
        
           |  |  | 404 |             $response = $this->process_respond();
 | 
        
           |  |  | 405 |   | 
        
           |  |  | 406 |             // validate the response
 | 
        
           |  |  | 407 |             // check http-version
 | 
        
           |  |  | 408 |             if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 409 |                 $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 410 |                     // seems to be http ... proceed
 | 
        
           |  |  | 411 |                     // We expect a 200 or 204 status code
 | 
        
           |  |  | 412 |                     // see rfc 2068 - 9.6 PUT...
 | 
        
           |  |  | 413 |                     // print 'http ok<br>';
 | 
        
           |  |  | 414 |                     return $response['status']['status-code'];
 | 
        
           |  |  | 415 |                 }
 | 
        
           |  |  | 416 |             // ups: no http status was returned ?
 | 
        
           |  |  | 417 |             return false;
 | 
        
           |  |  | 418 |         } else {
 | 
        
           |  |  | 419 |             $this->_error_log('put_file: could not open ' . $filename);
 | 
        
           |  |  | 420 |             return false;
 | 
        
           |  |  | 421 |         }
 | 
        
           |  |  | 422 |   | 
        
           |  |  | 423 |     }
 | 
        
           |  |  | 424 |   | 
        
           |  |  | 425 |     /**
 | 
        
           |  |  | 426 |      * Public method get_file
 | 
        
           |  |  | 427 |      *
 | 
        
           |  |  | 428 |      * Gets a file from a collection into local filesystem.
 | 
        
           |  |  | 429 |      *
 | 
        
           |  |  | 430 |      * fopen() is used.
 | 
        
           |  |  | 431 |      * @param string $srcpath
 | 
        
           |  |  | 432 |      * @param string $localpath
 | 
        
           |  |  | 433 |      * @return bool true on success. false on error.
 | 
        
           |  |  | 434 |      */
 | 
        
           |  |  | 435 |     function get_file($srcpath, $localpath) {
 | 
        
           |  |  | 436 |   | 
        
           |  |  | 437 |         $localpath = $this->utf_decode_path($localpath);
 | 
        
           |  |  | 438 |   | 
        
           |  |  | 439 |         $handle = fopen($localpath, 'wb');
 | 
        
           |  |  | 440 |         if ($handle) {
 | 
        
           |  |  | 441 |             $unused = '';
 | 
        
           |  |  | 442 |             $ret = $this->get($srcpath, $unused, $handle);
 | 
        
           |  |  | 443 |             fclose($handle);
 | 
        
           |  |  | 444 |             if ($ret) {
 | 
        
           |  |  | 445 |                 return true;
 | 
        
           |  |  | 446 |             }
 | 
        
           |  |  | 447 |         }
 | 
        
           |  |  | 448 |         return false;
 | 
        
           |  |  | 449 |     }
 | 
        
           |  |  | 450 |   | 
        
           |  |  | 451 |     /**
 | 
        
           |  |  | 452 |      * Public method copy_file
 | 
        
           |  |  | 453 |      *
 | 
        
           |  |  | 454 |      * Copies a file on a webdav server
 | 
        
           |  |  | 455 |      *
 | 
        
           |  |  | 456 |      * Duplicates a file on the webdav server (serverside).
 | 
        
           |  |  | 457 |      * All work is done on the webdav server. If you set param overwrite as true,
 | 
        
           |  |  | 458 |      * the target will be overwritten.
 | 
        
           |  |  | 459 |      *
 | 
        
           |  |  | 460 |      * @param string src_path, string dest_path, bool overwrite
 | 
        
           |  |  | 461 |      * @return int status code (look at rfc 2518). false on error.
 | 
        
           |  |  | 462 |      */
 | 
        
           |  |  | 463 |     function copy_file($src_path, $dst_path, $overwrite) {
 | 
        
           |  |  | 464 |         $this->_path = $this->translate_uri($src_path);
 | 
        
           |  |  | 465 |         $this->header_unset();
 | 
        
           |  |  | 466 |         $this->create_basic_request('COPY');
 | 
        
           |  |  | 467 |         $this->header_add(sprintf('Destination: http://%s%s', $this->_server, $this->translate_uri($dst_path)));
 | 
        
           |  |  | 468 |         if ($overwrite) {
 | 
        
           |  |  | 469 |             $this->header_add('Overwrite: T');
 | 
        
           |  |  | 470 |         } else {
 | 
        
           |  |  | 471 |             $this->header_add('Overwrite: F');
 | 
        
           |  |  | 472 |         }
 | 
        
           |  |  | 473 |         $this->header_add('');
 | 
        
           |  |  | 474 |         $this->send_request();
 | 
        
           |  |  | 475 |         $this->get_respond();
 | 
        
           |  |  | 476 |         $response = $this->process_respond();
 | 
        
           |  |  | 477 |         // validate the response ...
 | 
        
           |  |  | 478 |         // check http-version
 | 
        
           |  |  | 479 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 480 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 481 |          /* seems to be http ... proceed
 | 
        
           |  |  | 482 |              just return what server gave us (as defined in rfc 2518) :
 | 
        
           |  |  | 483 |              201 (Created) - The source resource was successfully copied. The copy operation resulted in the creation of a new resource.
 | 
        
           |  |  | 484 |              204 (No Content) - The source resource was successfully copied to a pre-existing destination resource.
 | 
        
           |  |  | 485 |              403 (Forbidden) - The source and destination URIs are the same.
 | 
        
           |  |  | 486 |              409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created.
 | 
        
           |  |  | 487 |              412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element
 | 
        
           |  |  | 488 |                      or the Overwrite header is "F" and the state of the destination resource is non-null.
 | 
        
           |  |  | 489 |              423 (Locked) - The destination resource was locked.
 | 
        
           |  |  | 490 |              502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
 | 
        
           |  |  | 491 |              507 (Insufficient Storage) - The destination resource does not have sufficient space to record the state of the resource after the
 | 
        
           |  |  | 492 |                      execution of this method.
 | 
        
           |  |  | 493 |           */
 | 
        
           |  |  | 494 |                 return $response['status']['status-code'];
 | 
        
           |  |  | 495 |             }
 | 
        
           |  |  | 496 |         return false;
 | 
        
           |  |  | 497 |     }
 | 
        
           |  |  | 498 |   | 
        
           |  |  | 499 |     /**
 | 
        
           |  |  | 500 |      * Public method copy_coll
 | 
        
           |  |  | 501 |      *
 | 
        
           |  |  | 502 |      * Copies a collection on a webdav server
 | 
        
           |  |  | 503 |      *
 | 
        
           |  |  | 504 |      * Duplicates a collection on the webdav server (serverside).
 | 
        
           |  |  | 505 |      * All work is done on the webdav server. If you set param overwrite as true,
 | 
        
           |  |  | 506 |      * the target will be overwritten.
 | 
        
           |  |  | 507 |      *
 | 
        
           |  |  | 508 |      * @param string src_path, string dest_path, bool overwrite
 | 
        
           |  |  | 509 |      * @return int status code (look at rfc 2518). false on error.
 | 
        
           |  |  | 510 |      */
 | 
        
           |  |  | 511 |     function copy_coll($src_path, $dst_path, $overwrite) {
 | 
        
           |  |  | 512 |         $this->_path = $this->translate_uri($src_path);
 | 
        
           |  |  | 513 |         $this->header_unset();
 | 
        
           |  |  | 514 |         $this->create_basic_request('COPY');
 | 
        
           |  |  | 515 |         $this->header_add(sprintf('Destination: http://%s%s', $this->_server, $this->translate_uri($dst_path)));
 | 
        
           |  |  | 516 |         $this->header_add('Depth: Infinity');
 | 
        
           |  |  | 517 |   | 
        
           |  |  | 518 |         $xml  = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n";
 | 
        
           |  |  | 519 |         $xml .= "<d:propertybehavior xmlns:d=\"DAV:\">\r\n";
 | 
        
           |  |  | 520 |         $xml .= "  <d:keepalive>*</d:keepalive>\r\n";
 | 
        
           |  |  | 521 |         $xml .= "</d:propertybehavior>\r\n";
 | 
        
           |  |  | 522 |   | 
        
           |  |  | 523 |         $this->header_add('Content-length: ' . strlen($xml));
 | 
        
           |  |  | 524 |         $this->header_add('Content-type: application/xml');
 | 
        
           |  |  | 525 |         $this->send_request();
 | 
        
           |  |  | 526 |         // send also xml
 | 
        
           |  |  | 527 |         fputs($this->sock, $xml);
 | 
        
           |  |  | 528 |         $this->get_respond();
 | 
        
           |  |  | 529 |         $response = $this->process_respond();
 | 
        
           |  |  | 530 |         // validate the response ...
 | 
        
           |  |  | 531 |         // check http-version
 | 
        
           |  |  | 532 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 533 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 534 |          /* seems to be http ... proceed
 | 
        
           |  |  | 535 |              just return what server gave us (as defined in rfc 2518) :
 | 
        
           |  |  | 536 |              201 (Created) - The source resource was successfully copied. The copy operation resulted in the creation of a new resource.
 | 
        
           |  |  | 537 |              204 (No Content) - The source resource was successfully copied to a pre-existing destination resource.
 | 
        
           |  |  | 538 |              403 (Forbidden) - The source and destination URIs are the same.
 | 
        
           |  |  | 539 |              409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created.
 | 
        
           |  |  | 540 |              412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element
 | 
        
           |  |  | 541 |                      or the Overwrite header is "F" and the state of the destination resource is non-null.
 | 
        
           |  |  | 542 |              423 (Locked) - The destination resource was locked.
 | 
        
           |  |  | 543 |              502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
 | 
        
           |  |  | 544 |              507 (Insufficient Storage) - The destination resource does not have sufficient space to record the state of the resource after the
 | 
        
           |  |  | 545 |                      execution of this method.
 | 
        
           |  |  | 546 |           */
 | 
        
           |  |  | 547 |                 return $response['status']['status-code'];
 | 
        
           |  |  | 548 |             }
 | 
        
           |  |  | 549 |         return false;
 | 
        
           |  |  | 550 |     }
 | 
        
           |  |  | 551 |   | 
        
           |  |  | 552 |     /**
 | 
        
           |  |  | 553 |      * Public method move
 | 
        
           |  |  | 554 |      *
 | 
        
           |  |  | 555 |      * Moves a file or collection on webdav server (serverside)
 | 
        
           |  |  | 556 |      *
 | 
        
           |  |  | 557 |      * If you set param overwrite as true, the target will be overwritten.
 | 
        
           |  |  | 558 |      *
 | 
        
           |  |  | 559 |      * @param string src_path, string dest_path, bool overwrite
 | 
        
           |  |  | 560 |      * @return int status code (look at rfc 2518). false on error.
 | 
        
           |  |  | 561 |      */
 | 
        
           |  |  | 562 |     // --------------------------------------------------------------------------
 | 
        
           |  |  | 563 |     // public method move
 | 
        
           |  |  | 564 |     // move/rename a file/collection on webdav server
 | 
        
           |  |  | 565 |     function move($src_path,$dst_path, $overwrite) {
 | 
        
           |  |  | 566 |   | 
        
           |  |  | 567 |         $this->_path = $this->translate_uri($src_path);
 | 
        
           |  |  | 568 |         $this->header_unset();
 | 
        
           |  |  | 569 |   | 
        
           |  |  | 570 |         $this->create_basic_request('MOVE');
 | 
        
           |  |  | 571 |         $this->header_add(sprintf('Destination: http://%s%s', $this->_server, $this->translate_uri($dst_path)));
 | 
        
           |  |  | 572 |         if ($overwrite) {
 | 
        
           |  |  | 573 |             $this->header_add('Overwrite: T');
 | 
        
           |  |  | 574 |         } else {
 | 
        
           |  |  | 575 |             $this->header_add('Overwrite: F');
 | 
        
           |  |  | 576 |         }
 | 
        
           |  |  | 577 |         $this->header_add('');
 | 
        
           |  |  | 578 |   | 
        
           |  |  | 579 |         $this->send_request();
 | 
        
           |  |  | 580 |         $this->get_respond();
 | 
        
           |  |  | 581 |         $response = $this->process_respond();
 | 
        
           |  |  | 582 |         // validate the response ...
 | 
        
           |  |  | 583 |         // check http-version
 | 
        
           |  |  | 584 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 585 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 586 |             /* seems to be http ... proceed
 | 
        
           |  |  | 587 |                 just return what server gave us (as defined in rfc 2518) :
 | 
        
           |  |  | 588 |                 201 (Created) - The source resource was successfully moved, and a new resource was created at the destination.
 | 
        
           |  |  | 589 |                 204 (No Content) - The source resource was successfully moved to a pre-existing destination resource.
 | 
        
           |  |  | 590 |                 403 (Forbidden) - The source and destination URIs are the same.
 | 
        
           |  |  | 591 |                 409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created.
 | 
        
           |  |  | 592 |                 412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element
 | 
        
           |  |  | 593 |                          or the Overwrite header is "F" and the state of the destination resource is non-null.
 | 
        
           |  |  | 594 |                 423 (Locked) - The source or the destination resource was locked.
 | 
        
           |  |  | 595 |                 502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
 | 
        
           |  |  | 596 |   | 
        
           |  |  | 597 |                 201 (Created) - The collection or structured resource was created in its entirety.
 | 
        
           |  |  | 598 |                 403 (Forbidden) - This indicates at least one of two conditions: 1) the server does not allow the creation of collections at the given
 | 
        
           |  |  | 599 |                                                  location in its namespace, or 2) the parent collection of the Request-URI exists but cannot accept members.
 | 
        
           |  |  | 600 |                 405 (Method Not Allowed) - MKCOL can only be executed on a deleted/non-existent resource.
 | 
        
           |  |  | 601 |                 409 (Conflict) - A collection cannot be made at the Request-URI until one or more intermediate collections have been created.
 | 
        
           |  |  | 602 |                 415 (Unsupported Media Type)- The server does not support the request type of the body.
 | 
        
           |  |  | 603 |                 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the resource after the execution of this method.
 | 
        
           |  |  | 604 |              */
 | 
        
           |  |  | 605 |                 return $response['status']['status-code'];
 | 
        
           |  |  | 606 |             }
 | 
        
           |  |  | 607 |         return false;
 | 
        
           |  |  | 608 |     }
 | 
        
           |  |  | 609 |   | 
        
           |  |  | 610 |     /**
 | 
        
           |  |  | 611 |      * Public method lock
 | 
        
           |  |  | 612 |      *
 | 
        
           |  |  | 613 |      * Locks a file or collection.
 | 
        
           |  |  | 614 |      *
 | 
        
           |  |  | 615 |      * Lock uses this->_user as lock owner.
 | 
        
           |  |  | 616 |      *
 | 
        
           |  |  | 617 |      * @param string path
 | 
        
           |  |  | 618 |      * @return int status code (look at rfc 2518). false on error.
 | 
        
           |  |  | 619 |      */
 | 
        
           |  |  | 620 |     function lock($path) {
 | 
        
           |  |  | 621 |         $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 622 |         $this->header_unset();
 | 
        
           |  |  | 623 |         $this->create_basic_request('LOCK');
 | 
        
           |  |  | 624 |         $this->header_add('Timeout: Infinite');
 | 
        
           |  |  | 625 |         $this->header_add('Content-type: text/xml');
 | 
        
           |  |  | 626 |         // create the xml request ...
 | 
        
           |  |  | 627 |         $xml =  "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n";
 | 
        
           |  |  | 628 |         $xml .= "<D:lockinfo xmlns:D='DAV:'\r\n>";
 | 
        
           |  |  | 629 |         $xml .= "  <D:lockscope><D:exclusive/></D:lockscope>\r\n";
 | 
        
           |  |  | 630 |         $xml .= "  <D:locktype><D:write/></D:locktype>\r\n";
 | 
        
           |  |  | 631 |         $xml .= "  <D:owner>\r\n";
 | 
        
           |  |  | 632 |         $xml .= "    <D:href>".($this->_user)."</D:href>\r\n";
 | 
        
           |  |  | 633 |         $xml .= "  </D:owner>\r\n";
 | 
        
           |  |  | 634 |         $xml .= "</D:lockinfo>\r\n";
 | 
        
           |  |  | 635 |         $this->header_add('Content-length: ' . strlen($xml));
 | 
        
           |  |  | 636 |         $this->send_request();
 | 
        
           |  |  | 637 |         // send also xml
 | 
        
           |  |  | 638 |         fputs($this->sock, $xml);
 | 
        
           |  |  | 639 |         $this->get_respond();
 | 
        
           |  |  | 640 |         $response = $this->process_respond();
 | 
        
           |  |  | 641 |         // validate the response ... (only basic validation)
 | 
        
           |  |  | 642 |         // check http-version
 | 
        
           |  |  | 643 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 644 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 645 |             /* seems to be http ... proceed
 | 
        
           |  |  | 646 |             rfc 2518 says:
 | 
        
           |  |  | 647 |             200 (OK) - The lock request succeeded and the value of the lockdiscovery property is included in the body.
 | 
        
           |  |  | 648 |             412 (Precondition Failed) - The included lock token was not enforceable on this resource or the server could not satisfy the
 | 
        
           |  |  | 649 |                      request in the lockinfo XML element.
 | 
        
           |  |  | 650 |             423 (Locked) - The resource is locked, so the method has been rejected.
 | 
        
           |  |  | 651 |              */
 | 
        
           |  |  | 652 |   | 
        
           |  |  | 653 |                 switch($response['status']['status-code']) {
 | 
        
           |  |  | 654 |                 case 200:
 | 
        
           |  |  | 655 |                     // collection was successfully locked... see xml response to get lock token...
 | 
        
           |  |  | 656 |                     if (strcmp($response['header']['Content-Type'], 'text/xml; charset="utf-8"') == 0) {
 | 
        
           |  |  | 657 |                         // ok let's get the content of the xml stuff
 | 
        
           |  |  | 658 |                         $this->_parser = xml_parser_create_ns();
 | 
        
           |  |  | 659 |                         $this->_parserid = $this->get_parser_id($this->_parser);
 | 
        
           |  |  | 660 |                         // forget old data...
 | 
        
           |  |  | 661 |                         unset($this->_lock[$this->_parserid]);
 | 
        
           |  |  | 662 |                         unset($this->_xmltree[$this->_parserid]);
 | 
        
           |  |  | 663 |                         xml_parser_set_option($this->_parser,XML_OPTION_SKIP_WHITE,0);
 | 
        
           |  |  | 664 |                         xml_parser_set_option($this->_parser,XML_OPTION_CASE_FOLDING,0);
 | 
        
           | 1441 | ariadna | 665 |                         xml_set_element_handler($this->_parser, [$this, "_lock_startElement"], [$this, "_endElement"]);
 | 
        
           |  |  | 666 |                         xml_set_character_data_handler($this->_parser, [$this, "_lock_cdata"]);
 | 
        
           | 1 | efrain | 667 |   | 
        
           |  |  | 668 |                         if (!xml_parse($this->_parser, $response['body'])) {
 | 
        
           |  |  | 669 |                             die(sprintf("XML error: %s at line %d",
 | 
        
           |  |  | 670 |                                 xml_error_string(xml_get_error_code($this->_parser)),
 | 
        
           |  |  | 671 |                                 xml_get_current_line_number($this->_parser)));
 | 
        
           |  |  | 672 |                         }
 | 
        
           |  |  | 673 |   | 
        
           |  |  | 674 |                         // Free resources
 | 
        
           |  |  | 675 |                         xml_parser_free($this->_parser);
 | 
        
           |  |  | 676 |                         // add status code to array
 | 
        
           |  |  | 677 |                         $this->_lock[$this->_parserid]['status'] = 200;
 | 
        
           |  |  | 678 |                         return $this->_lock[$this->_parserid];
 | 
        
           |  |  | 679 |   | 
        
           |  |  | 680 |                     } else {
 | 
        
           |  |  | 681 |                         print 'Missing Content-Type: text/xml header in response.<br>';
 | 
        
           |  |  | 682 |                     }
 | 
        
           |  |  | 683 |                     return false;
 | 
        
           |  |  | 684 |   | 
        
           |  |  | 685 |                 default:
 | 
        
           |  |  | 686 |                     // hmm. not what we expected. Just return what we got from webdav server
 | 
        
           |  |  | 687 |                     // someone else has to handle it.
 | 
        
           |  |  | 688 |                     $this->_lock['status'] = $response['status']['status-code'];
 | 
        
           |  |  | 689 |                     return $this->_lock;
 | 
        
           |  |  | 690 |                 }
 | 
        
           |  |  | 691 |             }
 | 
        
           |  |  | 692 |   | 
        
           |  |  | 693 |   | 
        
           |  |  | 694 |     }
 | 
        
           |  |  | 695 |   | 
        
           |  |  | 696 |   | 
        
           |  |  | 697 |     /**
 | 
        
           |  |  | 698 |      * Public method unlock
 | 
        
           |  |  | 699 |      *
 | 
        
           |  |  | 700 |      * Unlocks a file or collection.
 | 
        
           |  |  | 701 |      *
 | 
        
           |  |  | 702 |      * @param string path, string locktoken
 | 
        
           |  |  | 703 |      * @return int status code (look at rfc 2518). false on error.
 | 
        
           |  |  | 704 |      */
 | 
        
           |  |  | 705 |     function unlock($path, $locktoken) {
 | 
        
           |  |  | 706 |         $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 707 |         $this->header_unset();
 | 
        
           |  |  | 708 |         $this->create_basic_request('UNLOCK');
 | 
        
           |  |  | 709 |         $this->header_add(sprintf('Lock-Token: <%s>', $locktoken));
 | 
        
           |  |  | 710 |         $this->send_request();
 | 
        
           |  |  | 711 |         $this->get_respond();
 | 
        
           |  |  | 712 |         $response = $this->process_respond();
 | 
        
           |  |  | 713 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 714 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 715 |             /* seems to be http ... proceed
 | 
        
           |  |  | 716 |             rfc 2518 says:
 | 
        
           |  |  | 717 |             204 (OK) - The 204 (No Content) status code is used instead of 200 (OK) because there is no response entity body.
 | 
        
           |  |  | 718 |              */
 | 
        
           |  |  | 719 |                 return $response['status']['status-code'];
 | 
        
           |  |  | 720 |             }
 | 
        
           |  |  | 721 |         return false;
 | 
        
           |  |  | 722 |     }
 | 
        
           |  |  | 723 |   | 
        
           |  |  | 724 |     /**
 | 
        
           |  |  | 725 |      * Public method delete
 | 
        
           |  |  | 726 |      *
 | 
        
           |  |  | 727 |      * deletes a collection/directory on a webdav server
 | 
        
           |  |  | 728 |      * @param string path
 | 
        
           |  |  | 729 |      * @return int status code (look at rfc 2518). false on error.
 | 
        
           |  |  | 730 |      */
 | 
        
           |  |  | 731 |     function delete($path) {
 | 
        
           |  |  | 732 |         $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 733 |         $this->header_unset();
 | 
        
           |  |  | 734 |         $this->create_basic_request('DELETE');
 | 
        
           |  |  | 735 |         /* $this->header_add('Content-Length: 0'); */
 | 
        
           |  |  | 736 |         $this->header_add('');
 | 
        
           |  |  | 737 |         $this->send_request();
 | 
        
           |  |  | 738 |         $this->get_respond();
 | 
        
           |  |  | 739 |         $response = $this->process_respond();
 | 
        
           |  |  | 740 |   | 
        
           |  |  | 741 |         // validate the response ...
 | 
        
           |  |  | 742 |         // check http-version
 | 
        
           |  |  | 743 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 744 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 745 |                 // seems to be http ... proceed
 | 
        
           |  |  | 746 |                 // We expect a 207 Multi-Status status code
 | 
        
           |  |  | 747 |                 // print 'http ok<br>';
 | 
        
           |  |  | 748 |   | 
        
           |  |  | 749 |                 switch ($response['status']['status-code']) {
 | 
        
           |  |  | 750 |                 case 207:
 | 
        
           |  |  | 751 |                     // collection was NOT deleted... see xml response for reason...
 | 
        
           |  |  | 752 |                     // next there should be a Content-Type: text/xml; charset="utf-8" header line
 | 
        
           |  |  | 753 |                     if (strcmp($response['header']['Content-Type'], 'text/xml; charset="utf-8"') == 0) {
 | 
        
           |  |  | 754 |                         // ok let's get the content of the xml stuff
 | 
        
           |  |  | 755 |                         $this->_parser = xml_parser_create_ns();
 | 
        
           |  |  | 756 |                         $this->_parserid = $this->get_parser_id($this->_parser);
 | 
        
           |  |  | 757 |                         // forget old data...
 | 
        
           |  |  | 758 |                         unset($this->_delete[$this->_parserid]);
 | 
        
           |  |  | 759 |                         unset($this->_xmltree[$this->_parserid]);
 | 
        
           |  |  | 760 |                         xml_parser_set_option($this->_parser,XML_OPTION_SKIP_WHITE,0);
 | 
        
           |  |  | 761 |                         xml_parser_set_option($this->_parser,XML_OPTION_CASE_FOLDING,0);
 | 
        
           | 1441 | ariadna | 762 |                         xml_set_element_handler($this->_parser, [$this, "_delete_startElement"], [$this, "_endElement"]);
 | 
        
           |  |  | 763 |                         xml_set_character_data_handler($this->_parser, [$this, "_delete_cdata"]);
 | 
        
           | 1 | efrain | 764 |   | 
        
           |  |  | 765 |                         if (!xml_parse($this->_parser, $response['body'])) {
 | 
        
           |  |  | 766 |                             die(sprintf("XML error: %s at line %d",
 | 
        
           |  |  | 767 |                                 xml_error_string(xml_get_error_code($this->_parser)),
 | 
        
           |  |  | 768 |                                 xml_get_current_line_number($this->_parser)));
 | 
        
           |  |  | 769 |                         }
 | 
        
           |  |  | 770 |   | 
        
           |  |  | 771 |                         print "<br>";
 | 
        
           |  |  | 772 |   | 
        
           |  |  | 773 |                         // Free resources
 | 
        
           |  |  | 774 |                         xml_parser_free($this->_parser);
 | 
        
           |  |  | 775 |                         $this->_delete[$this->_parserid]['status'] = $response['status']['status-code'];
 | 
        
           |  |  | 776 |                         return $this->_delete[$this->_parserid];
 | 
        
           |  |  | 777 |   | 
        
           |  |  | 778 |                     } else {
 | 
        
           |  |  | 779 |                         print 'Missing Content-Type: text/xml header in response.<br>';
 | 
        
           |  |  | 780 |                     }
 | 
        
           |  |  | 781 |                     return false;
 | 
        
           |  |  | 782 |   | 
        
           |  |  | 783 |                 default:
 | 
        
           |  |  | 784 |                     // collection or file was successfully deleted
 | 
        
           |  |  | 785 |                     $this->_delete['status'] = $response['status']['status-code'];
 | 
        
           |  |  | 786 |                     return $this->_delete;
 | 
        
           |  |  | 787 |   | 
        
           |  |  | 788 |   | 
        
           |  |  | 789 |                 }
 | 
        
           |  |  | 790 |             }
 | 
        
           |  |  | 791 |   | 
        
           |  |  | 792 |     }
 | 
        
           |  |  | 793 |   | 
        
           |  |  | 794 |     /**
 | 
        
           |  |  | 795 |      * Public method ls
 | 
        
           |  |  | 796 |      *
 | 
        
           |  |  | 797 |      * Get's directory information from webdav server into flat a array using PROPFIND
 | 
        
           |  |  | 798 |      *
 | 
        
           |  |  | 799 |      * All filenames are UTF-8 encoded.
 | 
        
           |  |  | 800 |      * Have a look at _propfind_startElement what keys are used in array returned.
 | 
        
           |  |  | 801 |      * @param string path
 | 
        
           |  |  | 802 |      * @return array dirinfo, false on error
 | 
        
           |  |  | 803 |      */
 | 
        
           |  |  | 804 |     function ls($path) {
 | 
        
           |  |  | 805 |   | 
        
           |  |  | 806 |         if (trim($path) == '') {
 | 
        
           |  |  | 807 |             $this->_error_log('Missing a path in method ls');
 | 
        
           |  |  | 808 |             return false;
 | 
        
           |  |  | 809 |         }
 | 
        
           |  |  | 810 |         $this->_path = $this->translate_uri($path);
 | 
        
           |  |  | 811 |   | 
        
           |  |  | 812 |         $this->header_unset();
 | 
        
           |  |  | 813 |         $this->create_basic_request('PROPFIND');
 | 
        
           |  |  | 814 |         $this->header_add('Depth: 1');
 | 
        
           |  |  | 815 |         $this->header_add('Content-type: application/xml');
 | 
        
           |  |  | 816 |         // create profind xml request...
 | 
        
           |  |  | 817 |         $xml  = <<<EOD
 | 
        
           |  |  | 818 | <?xml version="1.0" encoding="utf-8"?>
 | 
        
           |  |  | 819 | <propfind xmlns="DAV:"><prop>
 | 
        
           |  |  | 820 | <getcontentlength xmlns="DAV:"/>
 | 
        
           |  |  | 821 | <getlastmodified xmlns="DAV:"/>
 | 
        
           |  |  | 822 | <executable xmlns="http://apache.org/dav/props/"/>
 | 
        
           |  |  | 823 | <resourcetype xmlns="DAV:"/>
 | 
        
           |  |  | 824 | <checked-in xmlns="DAV:"/>
 | 
        
           |  |  | 825 | <checked-out xmlns="DAV:"/>
 | 
        
           |  |  | 826 | </prop></propfind>
 | 
        
           |  |  | 827 | EOD;
 | 
        
           |  |  | 828 |         $this->header_add('Content-length: ' . strlen($xml));
 | 
        
           |  |  | 829 |         $this->send_request();
 | 
        
           |  |  | 830 |         $this->_error_log($xml);
 | 
        
           |  |  | 831 |         fputs($this->sock, $xml);
 | 
        
           |  |  | 832 |         $this->get_respond();
 | 
        
           |  |  | 833 |         $response = $this->process_respond();
 | 
        
           |  |  | 834 |         // validate the response ... (only basic validation)
 | 
        
           |  |  | 835 |         // check http-version
 | 
        
           |  |  | 836 |         if ($response['status']['http-version'] == 'HTTP/1.1' ||
 | 
        
           |  |  | 837 |             $response['status']['http-version'] == 'HTTP/1.0') {
 | 
        
           |  |  | 838 |                 // seems to be http ... proceed
 | 
        
           |  |  | 839 |                 // We expect a 207 Multi-Status status code
 | 
        
           |  |  | 840 |                 // print 'http ok<br>';
 | 
        
           |  |  | 841 |                 if (strcmp($response['status']['status-code'],'207') == 0 ) {
 | 
        
           |  |  | 842 |                     // ok so far
 | 
        
           |  |  | 843 |                     // next there should be a Content-Type: text/xml; charset="utf-8" header line
 | 
        
           |  |  | 844 |                     if (preg_match('#(application|text)/xml;\s?charset=[\'\"]?utf-8[\'\"]?#i', $response['header']['Content-Type'])) {
 | 
        
           |  |  | 845 |                         // ok let's get the content of the xml stuff
 | 
        
           |  |  | 846 |                         $this->_parser = xml_parser_create_ns('UTF-8');
 | 
        
           |  |  | 847 |                         $this->_parserid = $this->get_parser_id($this->_parser);
 | 
        
           |  |  | 848 |                         // forget old data...
 | 
        
           |  |  | 849 |                         unset($this->_ls[$this->_parserid]);
 | 
        
           |  |  | 850 |                         unset($this->_xmltree[$this->_parserid]);
 | 
        
           |  |  | 851 |                         xml_parser_set_option($this->_parser,XML_OPTION_SKIP_WHITE,0);
 | 
        
           |  |  | 852 |                         xml_parser_set_option($this->_parser,XML_OPTION_CASE_FOLDING,0);
 | 
        
           |  |  | 853 |                         // xml_parser_set_option($this->_parser,XML_OPTION_TARGET_ENCODING,'UTF-8');
 | 
        
           | 1441 | ariadna | 854 |                         xml_set_element_handler($this->_parser, [$this, "_propfind_startElement"], [$this, "_endElement"]);
 | 
        
           |  |  | 855 |                         xml_set_character_data_handler($this->_parser, [$this, "_propfind_cdata"]);
 | 
        
           | 1 | efrain | 856 |   | 
        
           |  |  | 857 |   | 
        
           |  |  | 858 |                         if (!xml_parse($this->_parser, $response['body'])) {
 | 
        
           |  |  | 859 |                             die(sprintf("XML error: %s at line %d",
 | 
        
           |  |  | 860 |                                 xml_error_string(xml_get_error_code($this->_parser)),
 | 
        
           |  |  | 861 |                                 xml_get_current_line_number($this->_parser)));
 | 
        
           |  |  | 862 |                         }
 | 
        
           |  |  | 863 |   | 
        
           |  |  | 864 |                         // Free resources
 | 
        
           |  |  | 865 |                         xml_parser_free($this->_parser);
 | 
        
           |  |  | 866 |                         $arr = $this->_ls[$this->_parserid];
 | 
        
           |  |  | 867 |                         return $arr;
 | 
        
           |  |  | 868 |                     } else {
 | 
        
           |  |  | 869 |                         $this->_error_log('Missing Content-Type: text/xml header in response!!');
 | 
        
           |  |  | 870 |                         return false;
 | 
        
           |  |  | 871 |                     }
 | 
        
           |  |  | 872 |                 } else {
 | 
        
           |  |  | 873 |                     // return status code ...
 | 
        
           |  |  | 874 |                     return $response['status']['status-code'];
 | 
        
           |  |  | 875 |                 }
 | 
        
           |  |  | 876 |             }
 | 
        
           |  |  | 877 |   | 
        
           |  |  | 878 |         // response was not http
 | 
        
           |  |  | 879 |         $this->_error_log('Ups in method ls: error in response from server');
 | 
        
           |  |  | 880 |         return false;
 | 
        
           |  |  | 881 |     }
 | 
        
           |  |  | 882 |   | 
        
           |  |  | 883 |   | 
        
           |  |  | 884 |     /**
 | 
        
           |  |  | 885 |      * Public method gpi
 | 
        
           |  |  | 886 |      *
 | 
        
           |  |  | 887 |      * Get's path information from webdav server for one element.
 | 
        
           |  |  | 888 |      *
 | 
        
           |  |  | 889 |      * @param string path
 | 
        
           |  |  | 890 |      * @return array dirinfo. false on error
 | 
        
           |  |  | 891 |      */
 | 
        
           |  |  | 892 |     function gpi($path) {
 | 
        
           |  |  | 893 |   | 
        
           |  |  | 894 |         // split path by last "/"
 | 
        
           |  |  | 895 |         $path = rtrim($path, "/");
 | 
        
           |  |  | 896 |         $item = basename($path);
 | 
        
           |  |  | 897 |         $dir  = dirname($path);
 | 
        
           |  |  | 898 |   | 
        
           |  |  | 899 |         $list = $this->ls($dir);
 | 
        
           |  |  | 900 |   | 
        
           |  |  | 901 |         // be sure it is an array
 | 
        
           |  |  | 902 |         if (is_array($list)) {
 | 
        
           |  |  | 903 |             foreach($list as $e) {
 | 
        
           |  |  | 904 |   | 
        
           |  |  | 905 |                 $fullpath = urldecode($e['href']);
 | 
        
           |  |  | 906 |                 $filename = basename($fullpath);
 | 
        
           |  |  | 907 |   | 
        
           |  |  | 908 |                 if ($filename == $item && $filename != "" and $fullpath != $dir."/") {
 | 
        
           |  |  | 909 |                     return $e;
 | 
        
           |  |  | 910 |                 }
 | 
        
           |  |  | 911 |             }
 | 
        
           |  |  | 912 |         }
 | 
        
           |  |  | 913 |         return false;
 | 
        
           |  |  | 914 |     }
 | 
        
           |  |  | 915 |   | 
        
           |  |  | 916 |     /**
 | 
        
           |  |  | 917 |      * Public method is_file
 | 
        
           |  |  | 918 |      *
 | 
        
           |  |  | 919 |      * Gathers whether a path points to a file or not.
 | 
        
           |  |  | 920 |      *
 | 
        
           |  |  | 921 |      * @param string path
 | 
        
           |  |  | 922 |      * @return bool true or false
 | 
        
           |  |  | 923 |      */
 | 
        
           |  |  | 924 |     function is_file($path) {
 | 
        
           |  |  | 925 |   | 
        
           |  |  | 926 |         $item = $this->gpi($path);
 | 
        
           |  |  | 927 |   | 
        
           |  |  | 928 |         if ($item === false) {
 | 
        
           |  |  | 929 |             return false;
 | 
        
           |  |  | 930 |         } else {
 | 
        
           |  |  | 931 |             return ($item['resourcetype'] != 'collection');
 | 
        
           |  |  | 932 |         }
 | 
        
           |  |  | 933 |     }
 | 
        
           |  |  | 934 |   | 
        
           |  |  | 935 |     /**
 | 
        
           |  |  | 936 |      * Public method is_dir
 | 
        
           |  |  | 937 |      *
 | 
        
           |  |  | 938 |      * Gather whether a path points to a directory
 | 
        
           |  |  | 939 |      * @param string path
 | 
        
           |  |  | 940 |      * return bool true or false
 | 
        
           |  |  | 941 |      */
 | 
        
           |  |  | 942 |     function is_dir($path) {
 | 
        
           |  |  | 943 |   | 
        
           |  |  | 944 |         // be sure path is utf-8
 | 
        
           |  |  | 945 |         $item = $this->gpi($path);
 | 
        
           |  |  | 946 |   | 
        
           |  |  | 947 |         if ($item === false) {
 | 
        
           |  |  | 948 |             return false;
 | 
        
           |  |  | 949 |         } else {
 | 
        
           |  |  | 950 |             return ($item['resourcetype'] == 'collection');
 | 
        
           |  |  | 951 |         }
 | 
        
           |  |  | 952 |     }
 | 
        
           |  |  | 953 |   | 
        
           |  |  | 954 |   | 
        
           |  |  | 955 |     /**
 | 
        
           |  |  | 956 |      * Public method mput
 | 
        
           |  |  | 957 |      *
 | 
        
           |  |  | 958 |      * Puts multiple files and/or directories onto a webdav server.
 | 
        
           |  |  | 959 |      *
 | 
        
           |  |  | 960 |      * Filenames should be allready UTF-8 encoded.
 | 
        
           |  |  | 961 |      * Param fileList must be in format array("localpath" => "destpath").
 | 
        
           |  |  | 962 |      *
 | 
        
           |  |  | 963 |      * @param array filelist
 | 
        
           |  |  | 964 |      * @return bool true on success. otherwise int status code on error
 | 
        
           |  |  | 965 |      */
 | 
        
           |  |  | 966 |     function mput($filelist) {
 | 
        
           |  |  | 967 |   | 
        
           |  |  | 968 |         $result = true;
 | 
        
           |  |  | 969 |   | 
        
           |  |  | 970 |         foreach ($filelist as $localpath => $destpath) {
 | 
        
           |  |  | 971 |   | 
        
           |  |  | 972 |             $localpath = rtrim($localpath, "/");
 | 
        
           |  |  | 973 |             $destpath  = rtrim($destpath, "/");
 | 
        
           |  |  | 974 |   | 
        
           |  |  | 975 |             // attempt to create target path
 | 
        
           |  |  | 976 |             if (is_dir($localpath)) {
 | 
        
           |  |  | 977 |                 $pathparts = explode("/", $destpath."/ "); // add one level, last level will be created as dir
 | 
        
           |  |  | 978 |             } else {
 | 
        
           |  |  | 979 |                 $pathparts = explode("/", $destpath);
 | 
        
           |  |  | 980 |             }
 | 
        
           |  |  | 981 |             $checkpath = "";
 | 
        
           |  |  | 982 |             for ($i=1; $i<sizeof($pathparts)-1; $i++) {
 | 
        
           |  |  | 983 |                 $checkpath .= "/" . $pathparts[$i];
 | 
        
           |  |  | 984 |                 if (!($this->is_dir($checkpath))) {
 | 
        
           |  |  | 985 |   | 
        
           |  |  | 986 |                     $result &= ($this->mkcol($checkpath) == 201 );
 | 
        
           |  |  | 987 |                 }
 | 
        
           |  |  | 988 |             }
 | 
        
           |  |  | 989 |   | 
        
           |  |  | 990 |             if ($result) {
 | 
        
           |  |  | 991 |                 // recurse directories
 | 
        
           |  |  | 992 |                 if (is_dir($localpath)) {
 | 
        
           |  |  | 993 |                     if (!$dp = opendir($localpath)) {
 | 
        
           |  |  | 994 |                         $this->_error_log("Could not open localpath for reading");
 | 
        
           |  |  | 995 |                         return false;
 | 
        
           |  |  | 996 |                     }
 | 
        
           |  |  | 997 |                     $fl = array();
 | 
        
           |  |  | 998 |                     while($filename = readdir($dp)) {
 | 
        
           |  |  | 999 |                         if ((is_file($localpath."/".$filename) || is_dir($localpath."/".$filename)) && $filename!="." && $filename != "..") {
 | 
        
           |  |  | 1000 |                             $fl[$localpath."/".$filename] = $destpath."/".$filename;
 | 
        
           |  |  | 1001 |                         }
 | 
        
           |  |  | 1002 |                     }
 | 
        
           |  |  | 1003 |                     $result &= $this->mput($fl);
 | 
        
           |  |  | 1004 |                 } else {
 | 
        
           |  |  | 1005 |                     $result &= ($this->put_file($destpath, $localpath) == 201);
 | 
        
           |  |  | 1006 |                 }
 | 
        
           |  |  | 1007 |             }
 | 
        
           |  |  | 1008 |         }
 | 
        
           |  |  | 1009 |         return $result;
 | 
        
           |  |  | 1010 |     }
 | 
        
           |  |  | 1011 |   | 
        
           |  |  | 1012 |     /**
 | 
        
           |  |  | 1013 |      * Public method mget
 | 
        
           |  |  | 1014 |      *
 | 
        
           |  |  | 1015 |      * Gets multiple files and directories.
 | 
        
           |  |  | 1016 |      *
 | 
        
           |  |  | 1017 |      * FileList must be in format array("remotepath" => "localpath").
 | 
        
           |  |  | 1018 |      * Filenames are UTF-8 encoded.
 | 
        
           |  |  | 1019 |      *
 | 
        
           |  |  | 1020 |      * @param array filelist
 | 
        
           |  |  | 1021 |      * @return bool true on succes, other int status code on error
 | 
        
           |  |  | 1022 |      */
 | 
        
           |  |  | 1023 |     function mget($filelist) {
 | 
        
           |  |  | 1024 |   | 
        
           |  |  | 1025 |         $result = true;
 | 
        
           |  |  | 1026 |   | 
        
           |  |  | 1027 |         foreach ($filelist as $remotepath => $localpath) {
 | 
        
           |  |  | 1028 |   | 
        
           |  |  | 1029 |             $localpath   = rtrim($localpath, "/");
 | 
        
           |  |  | 1030 |             $remotepath  = rtrim($remotepath, "/");
 | 
        
           |  |  | 1031 |   | 
        
           |  |  | 1032 |             // attempt to create local path
 | 
        
           |  |  | 1033 |             if ($this->is_dir($remotepath)) {
 | 
        
           |  |  | 1034 |                 $pathparts = explode("/", $localpath."/ "); // add one level, last level will be created as dir
 | 
        
           |  |  | 1035 |             } else {
 | 
        
           |  |  | 1036 |                 $pathparts = explode("/", $localpath);
 | 
        
           |  |  | 1037 |             }
 | 
        
           |  |  | 1038 |             $checkpath = "";
 | 
        
           |  |  | 1039 |             for ($i=1; $i<sizeof($pathparts)-1; $i++) {
 | 
        
           |  |  | 1040 |                 $checkpath .= "/" . $pathparts[$i];
 | 
        
           |  |  | 1041 |                 if (!is_dir($checkpath)) {
 | 
        
           |  |  | 1042 |   | 
        
           |  |  | 1043 |                     $result &= mkdir($checkpath);
 | 
        
           |  |  | 1044 |                 }
 | 
        
           |  |  | 1045 |             }
 | 
        
           |  |  | 1046 |   | 
        
           |  |  | 1047 |             if ($result) {
 | 
        
           |  |  | 1048 |                 // recurse directories
 | 
        
           |  |  | 1049 |                 if ($this->is_dir($remotepath)) {
 | 
        
           |  |  | 1050 |                     $list = $this->ls($remotepath);
 | 
        
           |  |  | 1051 |   | 
        
           |  |  | 1052 |                     $fl = array();
 | 
        
           |  |  | 1053 |                     foreach($list as $e) {
 | 
        
           |  |  | 1054 |                         $fullpath = urldecode($e['href']);
 | 
        
           |  |  | 1055 |                         $filename = basename($fullpath);
 | 
        
           |  |  | 1056 |                         if ($filename != '' and $fullpath != $remotepath . '/') {
 | 
        
           |  |  | 1057 |                             $fl[$remotepath."/".$filename] = $localpath."/".$filename;
 | 
        
           |  |  | 1058 |                         }
 | 
        
           |  |  | 1059 |                     }
 | 
        
           |  |  | 1060 |                     $result &= $this->mget($fl);
 | 
        
           |  |  | 1061 |                 } else {
 | 
        
           |  |  | 1062 |                     $result &= ($this->get_file($remotepath, $localpath));
 | 
        
           |  |  | 1063 |                 }
 | 
        
           |  |  | 1064 |             }
 | 
        
           |  |  | 1065 |         }
 | 
        
           |  |  | 1066 |         return $result;
 | 
        
           |  |  | 1067 |     }
 | 
        
           |  |  | 1068 |   | 
        
           |  |  | 1069 |     // --------------------------------------------------------------------------
 | 
        
           |  |  | 1070 |     // private xml callback and helper functions starting here
 | 
        
           |  |  | 1071 |     // --------------------------------------------------------------------------
 | 
        
           |  |  | 1072 |   | 
        
           |  |  | 1073 |   | 
        
           |  |  | 1074 |     /**
 | 
        
           |  |  | 1075 |      * Private method _endelement
 | 
        
           |  |  | 1076 |      *
 | 
        
           |  |  | 1077 |      * a generic endElement method  (used for all xml callbacks).
 | 
        
           |  |  | 1078 |      *
 | 
        
           |  |  | 1079 |      * @param resource parser, string name
 | 
        
           |  |  | 1080 |      * @access private
 | 
        
           |  |  | 1081 |      */
 | 
        
           |  |  | 1082 |   | 
        
           |  |  | 1083 |     private function _endElement($parser, $name) {
 | 
        
           |  |  | 1084 |         // end tag was found...
 | 
        
           |  |  | 1085 |         $parserid = $this->get_parser_id($parser);
 | 
        
           |  |  | 1086 |         $this->_xmltree[$parserid] = substr($this->_xmltree[$parserid],0, strlen($this->_xmltree[$parserid]) - (strlen($name) + 1));
 | 
        
           |  |  | 1087 |     }
 | 
        
           |  |  | 1088 |   | 
        
           |  |  | 1089 |     /**
 | 
        
           |  |  | 1090 |      * Private method _propfind_startElement
 | 
        
           |  |  | 1091 |      *
 | 
        
           |  |  | 1092 |      * Is needed by public method ls.
 | 
        
           |  |  | 1093 |      *
 | 
        
           |  |  | 1094 |      * Generic method will called by php xml_parse when a xml start element tag has been detected.
 | 
        
           |  |  | 1095 |      * The xml tree will translated into a flat php array for easier access.
 | 
        
           |  |  | 1096 |      * @param resource parser, string name, string attrs
 | 
        
           |  |  | 1097 |      * @access private
 | 
        
           |  |  | 1098 |      */
 | 
        
           |  |  | 1099 |     private function _propfind_startElement($parser, $name, $attrs) {
 | 
        
           |  |  | 1100 |         // lower XML Names... maybe break a RFC, don't know ...
 | 
        
           |  |  | 1101 |         $parserid = $this->get_parser_id($parser);
 | 
        
           |  |  | 1102 |   | 
        
           |  |  | 1103 |         $propname = strtolower($name);
 | 
        
           |  |  | 1104 |         if (!empty($this->_xmltree[$parserid])) {
 | 
        
           |  |  | 1105 |             $this->_xmltree[$parserid] .= $propname . '_';
 | 
        
           |  |  | 1106 |         } else {
 | 
        
           |  |  | 1107 |             $this->_xmltree[$parserid] = $propname . '_';
 | 
        
           |  |  | 1108 |         }
 | 
        
           |  |  | 1109 |   | 
        
           |  |  | 1110 |         // translate xml tree to a flat array ...
 | 
        
           |  |  | 1111 |         switch($this->_xmltree[$parserid]) {
 | 
        
           |  |  | 1112 |         case 'dav::multistatus_dav::response_':
 | 
        
           |  |  | 1113 |             // new element in mu
 | 
        
           |  |  | 1114 |             $this->_ls_ref =& $this->_ls[$parserid][];
 | 
        
           |  |  | 1115 |             break;
 | 
        
           |  |  | 1116 |         case 'dav::multistatus_dav::response_dav::href_':
 | 
        
           |  |  | 1117 |             $this->_ls_ref_cdata = &$this->_ls_ref['href'];
 | 
        
           |  |  | 1118 |             break;
 | 
        
           |  |  | 1119 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::creationdate_':
 | 
        
           |  |  | 1120 |             $this->_ls_ref_cdata = &$this->_ls_ref['creationdate'];
 | 
        
           |  |  | 1121 |             break;
 | 
        
           |  |  | 1122 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::getlastmodified_':
 | 
        
           |  |  | 1123 |             $this->_ls_ref_cdata = &$this->_ls_ref['lastmodified'];
 | 
        
           |  |  | 1124 |             break;
 | 
        
           |  |  | 1125 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::getcontenttype_':
 | 
        
           |  |  | 1126 |             $this->_ls_ref_cdata = &$this->_ls_ref['getcontenttype'];
 | 
        
           |  |  | 1127 |             break;
 | 
        
           |  |  | 1128 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::getcontentlength_':
 | 
        
           |  |  | 1129 |             $this->_ls_ref_cdata = &$this->_ls_ref['getcontentlength'];
 | 
        
           |  |  | 1130 |             break;
 | 
        
           |  |  | 1131 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::depth_':
 | 
        
           |  |  | 1132 |             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_depth'];
 | 
        
           |  |  | 1133 |             break;
 | 
        
           |  |  | 1134 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_dav::href_':
 | 
        
           |  |  | 1135 |             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_owner'];
 | 
        
           |  |  | 1136 |             break;
 | 
        
           |  |  | 1137 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_':
 | 
        
           |  |  | 1138 |             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_owner'];
 | 
        
           |  |  | 1139 |             break;
 | 
        
           |  |  | 1140 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::timeout_':
 | 
        
           |  |  | 1141 |             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_timeout'];
 | 
        
           |  |  | 1142 |             break;
 | 
        
           |  |  | 1143 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::locktoken_dav::href_':
 | 
        
           |  |  | 1144 |             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_token'];
 | 
        
           |  |  | 1145 |             break;
 | 
        
           |  |  | 1146 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::lockdiscovery_dav::activelock_dav::locktype_dav::write_':
 | 
        
           |  |  | 1147 |             $this->_ls_ref_cdata = &$this->_ls_ref['activelock_type'];
 | 
        
           |  |  | 1148 |             $this->_ls_ref_cdata = 'write';
 | 
        
           |  |  | 1149 |             $this->_ls_ref_cdata = &$this->_null;
 | 
        
           |  |  | 1150 |             break;
 | 
        
           |  |  | 1151 |         case 'dav::multistatus_dav::response_dav::propstat_dav::prop_dav::resourcetype_dav::collection_':
 | 
        
           |  |  | 1152 |             $this->_ls_ref_cdata = &$this->_ls_ref['resourcetype'];
 | 
        
           |  |  | 1153 |             $this->_ls_ref_cdata = 'collection';
 | 
        
           |  |  | 1154 |             $this->_ls_ref_cdata = &$this->_null;
 | 
        
           |  |  | 1155 |             break;
 | 
        
           |  |  | 1156 |         case 'dav::multistatus_dav::response_dav::propstat_dav::status_':
 | 
        
           |  |  | 1157 |             $this->_ls_ref_cdata = &$this->_ls_ref['status'];
 | 
        
           |  |  | 1158 |             break;
 | 
        
           |  |  | 1159 |   | 
        
           |  |  | 1160 |         default:
 | 
        
           |  |  | 1161 |             // handle unknown xml elements...
 | 
        
           |  |  | 1162 |             $this->_ls_ref_cdata = &$this->_ls_ref[$this->_xmltree[$parserid]];
 | 
        
           |  |  | 1163 |         }
 | 
        
           |  |  | 1164 |     }
 | 
        
           |  |  | 1165 |   | 
        
           |  |  | 1166 |     /**
 | 
        
           |  |  | 1167 |      * Private method _propfind_cData
 | 
        
           |  |  | 1168 |      *
 | 
        
           |  |  | 1169 |      * Is needed by public method ls.
 | 
        
           |  |  | 1170 |      *
 | 
        
           |  |  | 1171 |      * Will be called by php xml_set_character_data_handler() when xml data has to be handled.
 | 
        
           |  |  | 1172 |      * Stores data found into class var _ls_ref_cdata
 | 
        
           |  |  | 1173 |      * @param resource parser, string cdata
 | 
        
           |  |  | 1174 |      * @access private
 | 
        
           |  |  | 1175 |      */
 | 
        
           |  |  | 1176 |     private function _propfind_cData($parser, $cdata) {
 | 
        
           |  |  | 1177 |         if (trim($cdata) <> '') {
 | 
        
           |  |  | 1178 |             // cdata must be appended, because sometimes the php xml parser makes multiple calls
 | 
        
           |  |  | 1179 |             // to _propfind_cData before the xml end tag was reached...
 | 
        
           |  |  | 1180 |             $this->_ls_ref_cdata .= $cdata;
 | 
        
           |  |  | 1181 |         } else {
 | 
        
           |  |  | 1182 |             // do nothing
 | 
        
           |  |  | 1183 |         }
 | 
        
           |  |  | 1184 |     }
 | 
        
           |  |  | 1185 |   | 
        
           |  |  | 1186 |     /**
 | 
        
           |  |  | 1187 |      * Private method _delete_startElement
 | 
        
           |  |  | 1188 |      *
 | 
        
           |  |  | 1189 |      * Is used by public method delete.
 | 
        
           |  |  | 1190 |      *
 | 
        
           |  |  | 1191 |      * Will be called by php xml_parse.
 | 
        
           |  |  | 1192 |      * @param resource parser, string name, string attrs)
 | 
        
           |  |  | 1193 |      * @access private
 | 
        
           |  |  | 1194 |      */
 | 
        
           |  |  | 1195 |     private function _delete_startElement($parser, $name, $attrs) {
 | 
        
           |  |  | 1196 |         // lower XML Names... maybe break a RFC, don't know ...
 | 
        
           |  |  | 1197 |         $parserid = $this->get_parser_id($parser);
 | 
        
           |  |  | 1198 |         $propname = strtolower($name);
 | 
        
           |  |  | 1199 |         $this->_xmltree[$parserid] .= $propname . '_';
 | 
        
           |  |  | 1200 |   | 
        
           |  |  | 1201 |         // translate xml tree to a flat array ...
 | 
        
           |  |  | 1202 |         switch($this->_xmltree[$parserid]) {
 | 
        
           |  |  | 1203 |         case 'dav::multistatus_dav::response_':
 | 
        
           |  |  | 1204 |             // new element in mu
 | 
        
           |  |  | 1205 |             $this->_delete_ref =& $this->_delete[$parserid][];
 | 
        
           |  |  | 1206 |             break;
 | 
        
           |  |  | 1207 |         case 'dav::multistatus_dav::response_dav::href_':
 | 
        
           |  |  | 1208 |             $this->_delete_ref_cdata = &$this->_ls_ref['href'];
 | 
        
           |  |  | 1209 |             break;
 | 
        
           |  |  | 1210 |   | 
        
           |  |  | 1211 |         default:
 | 
        
           |  |  | 1212 |             // handle unknown xml elements...
 | 
        
           |  |  | 1213 |             $this->_delete_cdata = &$this->_delete_ref[$this->_xmltree[$parserid]];
 | 
        
           |  |  | 1214 |         }
 | 
        
           |  |  | 1215 |     }
 | 
        
           |  |  | 1216 |   | 
        
           |  |  | 1217 |   | 
        
           |  |  | 1218 |     /**
 | 
        
           |  |  | 1219 |      * Private method _delete_cData
 | 
        
           |  |  | 1220 |      *
 | 
        
           |  |  | 1221 |      * Is used by public method delete.
 | 
        
           |  |  | 1222 |      *
 | 
        
           |  |  | 1223 |      * Will be called by php xml_set_character_data_handler() when xml data has to be handled.
 | 
        
           |  |  | 1224 |      * Stores data found into class var _delete_ref_cdata
 | 
        
           |  |  | 1225 |      * @param resource parser, string cdata
 | 
        
           |  |  | 1226 |      * @access private
 | 
        
           |  |  | 1227 |      */
 | 
        
           |  |  | 1228 |     private function _delete_cData($parser, $cdata) {
 | 
        
           |  |  | 1229 |         if (trim($cdata) <> '') {
 | 
        
           |  |  | 1230 |             $this->_delete_ref_cdata .= $cdata;
 | 
        
           |  |  | 1231 |         } else {
 | 
        
           |  |  | 1232 |             // do nothing
 | 
        
           |  |  | 1233 |         }
 | 
        
           |  |  | 1234 |     }
 | 
        
           |  |  | 1235 |   | 
        
           |  |  | 1236 |   | 
        
           |  |  | 1237 |     /**
 | 
        
           |  |  | 1238 |      * Private method _lock_startElement
 | 
        
           |  |  | 1239 |      *
 | 
        
           |  |  | 1240 |      * Is needed by public method lock.
 | 
        
           |  |  | 1241 |      *
 | 
        
           |  |  | 1242 |      * Mmethod will called by php xml_parse when a xml start element tag has been detected.
 | 
        
           |  |  | 1243 |      * The xml tree will translated into a flat php array for easier access.
 | 
        
           |  |  | 1244 |      * @param resource parser, string name, string attrs
 | 
        
           |  |  | 1245 |      * @access private
 | 
        
           |  |  | 1246 |      */
 | 
        
           |  |  | 1247 |     private function _lock_startElement($parser, $name, $attrs) {
 | 
        
           |  |  | 1248 |         // lower XML Names... maybe break a RFC, don't know ...
 | 
        
           |  |  | 1249 |         $parserid = $this->get_parser_id($parser);
 | 
        
           |  |  | 1250 |         $propname = strtolower($name);
 | 
        
           |  |  | 1251 |         $this->_xmltree[$parserid] .= $propname . '_';
 | 
        
           |  |  | 1252 |   | 
        
           |  |  | 1253 |         // translate xml tree to a flat array ...
 | 
        
           |  |  | 1254 |         /*
 | 
        
           |  |  | 1255 |         dav::prop_dav::lockdiscovery_dav::activelock_dav::depth_=
 | 
        
           |  |  | 1256 |         dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_dav::href_=
 | 
        
           |  |  | 1257 |         dav::prop_dav::lockdiscovery_dav::activelock_dav::timeout_=
 | 
        
           |  |  | 1258 |         dav::prop_dav::lockdiscovery_dav::activelock_dav::locktoken_dav::href_=
 | 
        
           |  |  | 1259 |          */
 | 
        
           |  |  | 1260 |         switch($this->_xmltree[$parserid]) {
 | 
        
           |  |  | 1261 |         case 'dav::prop_dav::lockdiscovery_dav::activelock_':
 | 
        
           |  |  | 1262 |             // new element
 | 
        
           |  |  | 1263 |             $this->_lock_ref =& $this->_lock[$parserid][];
 | 
        
           |  |  | 1264 |             break;
 | 
        
           |  |  | 1265 |         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::locktype_dav::write_':
 | 
        
           |  |  | 1266 |             $this->_lock_ref_cdata = &$this->_lock_ref['locktype'];
 | 
        
           |  |  | 1267 |             $this->_lock_cdata = 'write';
 | 
        
           |  |  | 1268 |             $this->_lock_cdata = &$this->_null;
 | 
        
           |  |  | 1269 |             break;
 | 
        
           |  |  | 1270 |         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::lockscope_dav::exclusive_':
 | 
        
           |  |  | 1271 |             $this->_lock_ref_cdata = &$this->_lock_ref['lockscope'];
 | 
        
           |  |  | 1272 |             $this->_lock_ref_cdata = 'exclusive';
 | 
        
           |  |  | 1273 |             $this->_lock_ref_cdata = &$this->_null;
 | 
        
           |  |  | 1274 |             break;
 | 
        
           |  |  | 1275 |         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::depth_':
 | 
        
           |  |  | 1276 |             $this->_lock_ref_cdata = &$this->_lock_ref['depth'];
 | 
        
           |  |  | 1277 |             break;
 | 
        
           |  |  | 1278 |         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::owner_dav::href_':
 | 
        
           |  |  | 1279 |             $this->_lock_ref_cdata = &$this->_lock_ref['owner'];
 | 
        
           |  |  | 1280 |             break;
 | 
        
           |  |  | 1281 |         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::timeout_':
 | 
        
           |  |  | 1282 |             $this->_lock_ref_cdata = &$this->_lock_ref['timeout'];
 | 
        
           |  |  | 1283 |             break;
 | 
        
           |  |  | 1284 |         case 'dav::prop_dav::lockdiscovery_dav::activelock_dav::locktoken_dav::href_':
 | 
        
           |  |  | 1285 |             $this->_lock_ref_cdata = &$this->_lock_ref['locktoken'];
 | 
        
           |  |  | 1286 |             break;
 | 
        
           |  |  | 1287 |         default:
 | 
        
           |  |  | 1288 |             // handle unknown xml elements...
 | 
        
           |  |  | 1289 |             $this->_lock_cdata = &$this->_lock_ref[$this->_xmltree[$parserid]];
 | 
        
           |  |  | 1290 |   | 
        
           |  |  | 1291 |         }
 | 
        
           |  |  | 1292 |     }
 | 
        
           |  |  | 1293 |   | 
        
           |  |  | 1294 |     /**
 | 
        
           |  |  | 1295 |      * Private method _lock_cData
 | 
        
           |  |  | 1296 |      *
 | 
        
           |  |  | 1297 |      * Is used by public method lock.
 | 
        
           |  |  | 1298 |      *
 | 
        
           |  |  | 1299 |      * Will be called by php xml_set_character_data_handler() when xml data has to be handled.
 | 
        
           |  |  | 1300 |      * Stores data found into class var _lock_ref_cdata
 | 
        
           |  |  | 1301 |      * @param resource parser, string cdata
 | 
        
           |  |  | 1302 |      * @access private
 | 
        
           |  |  | 1303 |      */
 | 
        
           |  |  | 1304 |     private function _lock_cData($parser, $cdata) {
 | 
        
           |  |  | 1305 |         $parserid = $this->get_parser_id($parser);
 | 
        
           |  |  | 1306 |         if (trim($cdata) <> '') {
 | 
        
           |  |  | 1307 |             // $this->_error_log(($this->_xmltree[$parserid]) . '='. htmlentities($cdata));
 | 
        
           |  |  | 1308 |             $this->_lock_ref_cdata .= $cdata;
 | 
        
           |  |  | 1309 |         } else {
 | 
        
           |  |  | 1310 |             // do nothing
 | 
        
           |  |  | 1311 |         }
 | 
        
           |  |  | 1312 |     }
 | 
        
           |  |  | 1313 |   | 
        
           |  |  | 1314 |   | 
        
           |  |  | 1315 |     /**
 | 
        
           |  |  | 1316 |      * Private method header_add
 | 
        
           |  |  | 1317 |      *
 | 
        
           |  |  | 1318 |      * extends class var array _req
 | 
        
           |  |  | 1319 |      * @param string string
 | 
        
           |  |  | 1320 |      * @access private
 | 
        
           |  |  | 1321 |      */
 | 
        
           |  |  | 1322 |     private function header_add($string) {
 | 
        
           |  |  | 1323 |         $this->_req[] = $string;
 | 
        
           |  |  | 1324 |     }
 | 
        
           |  |  | 1325 |   | 
        
           |  |  | 1326 |     /**
 | 
        
           |  |  | 1327 |      * Private method header_unset
 | 
        
           |  |  | 1328 |      *
 | 
        
           |  |  | 1329 |      * unsets class var array _req
 | 
        
           |  |  | 1330 |      * @access private
 | 
        
           |  |  | 1331 |      */
 | 
        
           |  |  | 1332 |   | 
        
           |  |  | 1333 |     private function header_unset() {
 | 
        
           |  |  | 1334 |         unset($this->_req);
 | 
        
           |  |  | 1335 |     }
 | 
        
           |  |  | 1336 |   | 
        
           |  |  | 1337 |     /**
 | 
        
           |  |  | 1338 |      * Private method create_basic_request
 | 
        
           |  |  | 1339 |      *
 | 
        
           |  |  | 1340 |      * creates by using private method header_add an general request header.
 | 
        
           |  |  | 1341 |      * @param string method
 | 
        
           |  |  | 1342 |      * @access private
 | 
        
           |  |  | 1343 |      */
 | 
        
           |  |  | 1344 |     private function create_basic_request($method) {
 | 
        
           |  |  | 1345 |         $this->header_add(sprintf('%s %s %s', $method, $this->_path, $this->_protocol));
 | 
        
           |  |  | 1346 |         $this->header_add(sprintf('Host: %s:%s', $this->_server, $this->_port));
 | 
        
           |  |  | 1347 |         //$request .= sprintf('Connection: Keep-Alive');
 | 
        
           |  |  | 1348 |         $this->header_add(sprintf('User-Agent: %s', $this->_user_agent));
 | 
        
           |  |  | 1349 |         $this->header_add('Connection: TE');
 | 
        
           |  |  | 1350 |         $this->header_add('TE: Trailers');
 | 
        
           |  |  | 1351 |         if ($this->_auth == 'basic') {
 | 
        
           |  |  | 1352 |             $this->header_add(sprintf('Authorization: Basic %s', base64_encode("$this->_user:$this->_pass")));
 | 
        
           |  |  | 1353 |         } else if ($this->_auth == 'digest') {
 | 
        
           |  |  | 1354 |             if ($signature = $this->digest_signature($method)){
 | 
        
           |  |  | 1355 |                 $this->header_add($signature);
 | 
        
           |  |  | 1356 |             }
 | 
        
           |  |  | 1357 |         } else if ($this->_auth == 'bearer') {
 | 
        
           |  |  | 1358 |             $this->header_add(sprintf('Authorization: Bearer %s', $this->oauthtoken));
 | 
        
           |  |  | 1359 |         }
 | 
        
           |  |  | 1360 |     }
 | 
        
           |  |  | 1361 |   | 
        
           |  |  | 1362 |     /**
 | 
        
           |  |  | 1363 |      * Reads the header, stores the challenge information
 | 
        
           |  |  | 1364 |      *
 | 
        
           |  |  | 1365 |      * @return void
 | 
        
           |  |  | 1366 |      */
 | 
        
           |  |  | 1367 |     private function digest_auth() {
 | 
        
           |  |  | 1368 |   | 
        
           |  |  | 1369 |         $headers = array();
 | 
        
           |  |  | 1370 |         $headers[] = sprintf('%s %s %s', 'HEAD', $this->_path, $this->_protocol);
 | 
        
           |  |  | 1371 |         $headers[] = sprintf('Host: %s:%s', $this->_server, $this->_port);
 | 
        
           |  |  | 1372 |         $headers[] = sprintf('User-Agent: %s', $this->_user_agent);
 | 
        
           |  |  | 1373 |         $headers = implode("\r\n", $headers);
 | 
        
           |  |  | 1374 |         $headers .= "\r\n\r\n";
 | 
        
           |  |  | 1375 |         fputs($this->sock, $headers);
 | 
        
           |  |  | 1376 |   | 
        
           |  |  | 1377 |         // Reads the headers.
 | 
        
           |  |  | 1378 |         $i = 0;
 | 
        
           |  |  | 1379 |         $header = '';
 | 
        
           |  |  | 1380 |         do {
 | 
        
           |  |  | 1381 |             $header .= fread($this->sock, 1);
 | 
        
           |  |  | 1382 |             $i++;
 | 
        
           |  |  | 1383 |         } while (!preg_match('/\\r\\n\\r\\n$/', $header, $matches) && $i < $this->_maxheaderlenth);
 | 
        
           |  |  | 1384 |   | 
        
           |  |  | 1385 |         // Analyse the headers.
 | 
        
           |  |  | 1386 |         $digest = array();
 | 
        
           |  |  | 1387 |         $splitheaders = explode("\r\n", $header);
 | 
        
           |  |  | 1388 |         foreach ($splitheaders as $line) {
 | 
        
           |  |  | 1389 |             if (!preg_match('/^WWW-Authenticate: Digest/', $line)) {
 | 
        
           |  |  | 1390 |                 continue;
 | 
        
           |  |  | 1391 |             }
 | 
        
           |  |  | 1392 |             $line = substr($line, strlen('WWW-Authenticate: Digest '));
 | 
        
           |  |  | 1393 |             $params = explode(',', $line);
 | 
        
           |  |  | 1394 |             foreach ($params as $param) {
 | 
        
           |  |  | 1395 |                 list($key, $value) = explode('=', trim($param), 2);
 | 
        
           |  |  | 1396 |                 $digest[$key] = trim($value, '"');
 | 
        
           |  |  | 1397 |             }
 | 
        
           |  |  | 1398 |             break;
 | 
        
           |  |  | 1399 |         }
 | 
        
           |  |  | 1400 |   | 
        
           |  |  | 1401 |         $this->_digestchallenge = $digest;
 | 
        
           |  |  | 1402 |     }
 | 
        
           |  |  | 1403 |   | 
        
           |  |  | 1404 |     /**
 | 
        
           |  |  | 1405 |      * Generates the digest signature
 | 
        
           |  |  | 1406 |      *
 | 
        
           |  |  | 1407 |      * @return string signature to add to the headers
 | 
        
           |  |  | 1408 |      * @access private
 | 
        
           |  |  | 1409 |      */
 | 
        
           |  |  | 1410 |     private function digest_signature($method) {
 | 
        
           |  |  | 1411 |         if (!$this->_digestchallenge) {
 | 
        
           |  |  | 1412 |             $this->digest_auth();
 | 
        
           |  |  | 1413 |         }
 | 
        
           |  |  | 1414 |   | 
        
           |  |  | 1415 |         $signature = array();
 | 
        
           |  |  | 1416 |         $signature['username'] = '"' . $this->_user . '"';
 | 
        
           |  |  | 1417 |         $signature['realm'] = '"' . $this->_digestchallenge['realm'] . '"';
 | 
        
           |  |  | 1418 |         $signature['nonce'] = '"' . $this->_digestchallenge['nonce'] . '"';
 | 
        
           |  |  | 1419 |         $signature['uri'] = '"' . $this->_path . '"';
 | 
        
           |  |  | 1420 |   | 
        
           |  |  | 1421 |         if (isset($this->_digestchallenge['algorithm']) && $this->_digestchallenge['algorithm'] != 'MD5') {
 | 
        
           |  |  | 1422 |             $this->_error_log('Algorithm other than MD5 are not supported');
 | 
        
           |  |  | 1423 |             return false;
 | 
        
           |  |  | 1424 |         }
 | 
        
           |  |  | 1425 |   | 
        
           |  |  | 1426 |         $a1 = $this->_user . ':' . $this->_digestchallenge['realm'] . ':' . $this->_pass;
 | 
        
           |  |  | 1427 |         $a2 = $method . ':' . $this->_path;
 | 
        
           |  |  | 1428 |   | 
        
           |  |  | 1429 |         if (!isset($this->_digestchallenge['qop'])) {
 | 
        
           |  |  | 1430 |             $signature['response'] = '"' . md5(md5($a1) . ':' . $this->_digestchallenge['nonce'] . ':' . md5($a2)) . '"';
 | 
        
           |  |  | 1431 |         } else {
 | 
        
           |  |  | 1432 |             // Assume QOP is auth
 | 
        
           |  |  | 1433 |             if (empty($this->_cnonce)) {
 | 
        
           |  |  | 1434 |                 $this->_cnonce = random_string();
 | 
        
           |  |  | 1435 |                 $this->_nc = 0;
 | 
        
           |  |  | 1436 |             }
 | 
        
           |  |  | 1437 |             $this->_nc++;
 | 
        
           |  |  | 1438 |             $nc = sprintf('%08d', $this->_nc);
 | 
        
           |  |  | 1439 |             $signature['cnonce'] = '"' . $this->_cnonce . '"';
 | 
        
           |  |  | 1440 |             $signature['nc'] = '"' . $nc . '"';
 | 
        
           |  |  | 1441 |             $signature['qop'] = '"' . $this->_digestchallenge['qop'] . '"';
 | 
        
           |  |  | 1442 |             $signature['response'] = '"' . md5(md5($a1) . ':' . $this->_digestchallenge['nonce'] . ':' .
 | 
        
           |  |  | 1443 |                     $nc . ':' . $this->_cnonce . ':' . $this->_digestchallenge['qop'] . ':' . md5($a2)) . '"';
 | 
        
           |  |  | 1444 |         }
 | 
        
           |  |  | 1445 |   | 
        
           |  |  | 1446 |         $response = array();
 | 
        
           |  |  | 1447 |         foreach ($signature as $key => $value) {
 | 
        
           |  |  | 1448 |             $response[] = "$key=$value";
 | 
        
           |  |  | 1449 |         }
 | 
        
           |  |  | 1450 |         return 'Authorization: Digest ' . implode(', ', $response);
 | 
        
           |  |  | 1451 |     }
 | 
        
           |  |  | 1452 |   | 
        
           |  |  | 1453 |     /**
 | 
        
           |  |  | 1454 |      * Private method send_request
 | 
        
           |  |  | 1455 |      *
 | 
        
           |  |  | 1456 |      * Sends a ready formed http/webdav request to webdav server.
 | 
        
           |  |  | 1457 |      *
 | 
        
           |  |  | 1458 |      * @access private
 | 
        
           |  |  | 1459 |      */
 | 
        
           |  |  | 1460 |     private function send_request() {
 | 
        
           |  |  | 1461 |         // check if stream is declared to be open
 | 
        
           |  |  | 1462 |         // only logical check we are not sure if socket is really still open ...
 | 
        
           |  |  | 1463 |         if ($this->_connection_closed) {
 | 
        
           |  |  | 1464 |             // reopen it
 | 
        
           |  |  | 1465 |             // be sure to close the open socket.
 | 
        
           |  |  | 1466 |             $this->close();
 | 
        
           |  |  | 1467 |             $this->reopen();
 | 
        
           |  |  | 1468 |         }
 | 
        
           |  |  | 1469 |   | 
        
           |  |  | 1470 |         // convert array to string
 | 
        
           |  |  | 1471 |         $buffer = implode("\r\n", $this->_req);
 | 
        
           |  |  | 1472 |         $buffer .= "\r\n\r\n";
 | 
        
           |  |  | 1473 |         $this->_error_log($buffer);
 | 
        
           |  |  | 1474 |         fputs($this->sock, $buffer);
 | 
        
           |  |  | 1475 |     }
 | 
        
           |  |  | 1476 |   | 
        
           |  |  | 1477 |     /**
 | 
        
           |  |  | 1478 |      * Private method get_respond
 | 
        
           |  |  | 1479 |      *
 | 
        
           |  |  | 1480 |      * Reads the response from the webdav server.
 | 
        
           |  |  | 1481 |      *
 | 
        
           |  |  | 1482 |      * Stores data into class vars _header for the header data and
 | 
        
           |  |  | 1483 |      * _body for the rest of the response.
 | 
        
           |  |  | 1484 |      * This routine is the weakest part of this class, because it very depends how php does handle a socket stream.
 | 
        
           |  |  | 1485 |      * If the stream is blocked for some reason php is blocked as well.
 | 
        
           |  |  | 1486 |      * @access private
 | 
        
           |  |  | 1487 |      * @param resource $fp optional the file handle to write the body content to (stored internally in the '_body' if not set)
 | 
        
           |  |  | 1488 |      */
 | 
        
           |  |  | 1489 |     private function get_respond($fp = null) {
 | 
        
           |  |  | 1490 |         $this->_error_log('get_respond()');
 | 
        
           |  |  | 1491 |         // init vars (good coding style ;-)
 | 
        
           |  |  | 1492 |         $buffer = '';
 | 
        
           |  |  | 1493 |         $header = '';
 | 
        
           |  |  | 1494 |         // attention: do not make max_chunk_size to big....
 | 
        
           |  |  | 1495 |         $max_chunk_size = 8192;
 | 
        
           |  |  | 1496 |         // be sure we got a open ressource
 | 
        
           |  |  | 1497 |         if (! $this->sock) {
 | 
        
           |  |  | 1498 |             $this->_error_log('socket is not open. Can not process response');
 | 
        
           |  |  | 1499 |             return false;
 | 
        
           |  |  | 1500 |         }
 | 
        
           |  |  | 1501 |   | 
        
           |  |  | 1502 |         // following code maybe helps to improve socket behaviour ... more testing needed
 | 
        
           |  |  | 1503 |         // disabled at the moment ...
 | 
        
           |  |  | 1504 |         // socket_set_timeout($this->sock,1 );
 | 
        
           |  |  | 1505 |         // $socket_state = socket_get_status($this->sock);
 | 
        
           |  |  | 1506 |   | 
        
           |  |  | 1507 |         // read stream one byte by another until http header ends
 | 
        
           |  |  | 1508 |         $i = 0;
 | 
        
           |  |  | 1509 |         $matches = array();
 | 
        
           |  |  | 1510 |         do {
 | 
        
           |  |  | 1511 |             $header.=fread($this->sock, 1);
 | 
        
           |  |  | 1512 |             $i++;
 | 
        
           |  |  | 1513 |         } while (!preg_match('/\\r\\n\\r\\n$/',$header, $matches) && $i < $this->_maxheaderlenth);
 | 
        
           |  |  | 1514 |   | 
        
           |  |  | 1515 |         $this->_error_log($header);
 | 
        
           |  |  | 1516 |   | 
        
           |  |  | 1517 |         if (preg_match('/Connection: close\\r\\n/', $header)) {
 | 
        
           |  |  | 1518 |             // This says that the server will close connection at the end of this stream.
 | 
        
           |  |  | 1519 |             // Therefore we need to reopen the socket, before are sending the next request...
 | 
        
           |  |  | 1520 |             $this->_error_log('Connection: close found');
 | 
        
           |  |  | 1521 |             $this->_connection_closed = true;
 | 
        
           |  |  | 1522 |         } else if (preg_match('@^HTTP/1\.(1|0) 401 @', $header)) {
 | 
        
           |  |  | 1523 |             $this->_error_log('The server requires an authentication');
 | 
        
           |  |  | 1524 |         }
 | 
        
           |  |  | 1525 |   | 
        
           |  |  | 1526 |         // check how to get the data on socket stream
 | 
        
           |  |  | 1527 |         // chunked or content-length (HTTP/1.1) or
 | 
        
           |  |  | 1528 |         // one block until feof is received (HTTP/1.0)
 | 
        
           |  |  | 1529 |         switch(true) {
 | 
        
           |  |  | 1530 |         case (preg_match('/Transfer\\-Encoding:\\s+chunked\\r\\n/',$header)):
 | 
        
           |  |  | 1531 |             $this->_error_log('Getting HTTP/1.1 chunked data...');
 | 
        
           |  |  | 1532 |             do {
 | 
        
           |  |  | 1533 |                 $byte = '';
 | 
        
           |  |  | 1534 |                 $chunk_size='';
 | 
        
           |  |  | 1535 |                 do {
 | 
        
           |  |  | 1536 |                     $chunk_size.=$byte;
 | 
        
           |  |  | 1537 |                     $byte=fread($this->sock,1);
 | 
        
           |  |  | 1538 |                     // check what happens while reading, because I do not really understand how php reads the socketstream...
 | 
        
           |  |  | 1539 |                     // but so far - it seems to work here - tested with php v4.3.1 on apache 1.3.27 and Debian Linux 3.0 ...
 | 
        
           |  |  | 1540 |                     if (strlen($byte) == 0) {
 | 
        
           |  |  | 1541 |                         $this->_error_log('get_respond: warning --> read zero bytes');
 | 
        
           |  |  | 1542 |                     }
 | 
        
           |  |  | 1543 |                 } while ($byte!="\r" and strlen($byte)>0);      // till we match the Carriage Return
 | 
        
           |  |  | 1544 |                 fread($this->sock, 1);                           // also drop off the Line Feed
 | 
        
           |  |  | 1545 |                 $chunk_size=hexdec($chunk_size);                // convert to a number in decimal system
 | 
        
           |  |  | 1546 |                 if ($chunk_size > 0) {
 | 
        
           |  |  | 1547 |                     $read = 0;
 | 
        
           |  |  | 1548 |                     // Reading the chunk in one bite is not secure, we read it byte by byte.
 | 
        
           |  |  | 1549 |                     while ($read < $chunk_size) {
 | 
        
           |  |  | 1550 |                         $chunk = fread($this->sock, 1);
 | 
        
           |  |  | 1551 |                         self::update_file_or_buffer($chunk, $fp, $buffer);
 | 
        
           |  |  | 1552 |                         $read++;
 | 
        
           |  |  | 1553 |                     }
 | 
        
           |  |  | 1554 |                 }
 | 
        
           |  |  | 1555 |                 fread($this->sock, 2);                            // ditch the CRLF that trails the chunk
 | 
        
           |  |  | 1556 |             } while ($chunk_size);                            // till we reach the 0 length chunk (end marker)
 | 
        
           |  |  | 1557 |             break;
 | 
        
           |  |  | 1558 |   | 
        
           |  |  | 1559 |             // check for a specified content-length
 | 
        
           |  |  | 1560 |         case preg_match('/Content\\-Length:\\s+([0-9]*)\\r\\n/',$header,$matches):
 | 
        
           |  |  | 1561 |             $this->_error_log('Getting data using Content-Length '. $matches[1]);
 | 
        
           |  |  | 1562 |   | 
        
           |  |  | 1563 |             // check if we the content data size is small enough to get it as one block
 | 
        
           |  |  | 1564 |             if ($matches[1] <= $max_chunk_size ) {
 | 
        
           |  |  | 1565 |                 // only read something if Content-Length is bigger than 0
 | 
        
           |  |  | 1566 |                 if ($matches[1] > 0 ) {
 | 
        
           |  |  | 1567 |                     $chunk = fread($this->sock, $matches[1]);
 | 
        
           |  |  | 1568 |                     $loadsize = strlen($chunk);
 | 
        
           |  |  | 1569 |                     //did we realy get the full length?
 | 
        
           |  |  | 1570 |                     if ($loadsize < $matches[1]) {
 | 
        
           |  |  | 1571 |                         $max_chunk_size = $loadsize;
 | 
        
           |  |  | 1572 |                         do {
 | 
        
           |  |  | 1573 |                             $mod = $max_chunk_size % ($matches[1] - strlen($chunk));
 | 
        
           |  |  | 1574 |                             $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($chunk));
 | 
        
           |  |  | 1575 |                             $chunk .= fread($this->sock, $chunk_size);
 | 
        
           |  |  | 1576 |                             $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($chunk));
 | 
        
           |  |  | 1577 |                         } while (strlen($chunk) < $matches[1]);
 | 
        
           |  |  | 1578 |                     }
 | 
        
           |  |  | 1579 |                     self::update_file_or_buffer($chunk, $fp, $buffer);
 | 
        
           |  |  | 1580 |                     break;
 | 
        
           |  |  | 1581 |                 } else {
 | 
        
           |  |  | 1582 |                     $buffer = '';
 | 
        
           |  |  | 1583 |                     break;
 | 
        
           |  |  | 1584 |                 }
 | 
        
           |  |  | 1585 |             }
 | 
        
           |  |  | 1586 |   | 
        
           |  |  | 1587 |             // data is to big to handle it as one. Get it chunk per chunk...
 | 
        
           |  |  | 1588 |             //trying to get the full length of max_chunk_size
 | 
        
           |  |  | 1589 |             $chunk = fread($this->sock, $max_chunk_size);
 | 
        
           |  |  | 1590 |             $loadsize = strlen($chunk);
 | 
        
           |  |  | 1591 |             self::update_file_or_buffer($chunk, $fp, $buffer);
 | 
        
           |  |  | 1592 |             if ($loadsize < $max_chunk_size) {
 | 
        
           |  |  | 1593 |                 $max_chunk_size = $loadsize;
 | 
        
           |  |  | 1594 |             }
 | 
        
           |  |  | 1595 |             do {
 | 
        
           |  |  | 1596 |                 $mod = $max_chunk_size % ($matches[1] - $loadsize);
 | 
        
           |  |  | 1597 |                 $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - $loadsize);
 | 
        
           |  |  | 1598 |                 $chunk = fread($this->sock, $chunk_size);
 | 
        
           |  |  | 1599 |                 self::update_file_or_buffer($chunk, $fp, $buffer);
 | 
        
           |  |  | 1600 |                 $loadsize += strlen($chunk);
 | 
        
           |  |  | 1601 |                 $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . $loadsize);
 | 
        
           |  |  | 1602 |             } while ($matches[1] > $loadsize);
 | 
        
           |  |  | 1603 |             break;
 | 
        
           |  |  | 1604 |   | 
        
           |  |  | 1605 |             // check for 204 No Content
 | 
        
           |  |  | 1606 |             // 204 responds have no body.
 | 
        
           |  |  | 1607 |             // Therefore we do not need to read any data from socket stream.
 | 
        
           |  |  | 1608 |         case preg_match('/HTTP\/1\.1\ 204/',$header):
 | 
        
           |  |  | 1609 |             // nothing to do, just proceed
 | 
        
           |  |  | 1610 |             $this->_error_log('204 No Content found. No further data to read..');
 | 
        
           |  |  | 1611 |             break;
 | 
        
           |  |  | 1612 |         default:
 | 
        
           |  |  | 1613 |             // just get the data until foef appears...
 | 
        
           |  |  | 1614 |             $this->_error_log('reading until feof...' . $header);
 | 
        
           |  |  | 1615 |             socket_set_timeout($this->sock, 0, 0);
 | 
        
           |  |  | 1616 |             while (!feof($this->sock)) {
 | 
        
           |  |  | 1617 |                 $chunk = fread($this->sock, 4096);
 | 
        
           |  |  | 1618 |                 self::update_file_or_buffer($chunk, $fp, $buffer);
 | 
        
           |  |  | 1619 |             }
 | 
        
           |  |  | 1620 |             // renew the socket timeout...does it do something ???? Is it needed. More debugging needed...
 | 
        
           |  |  | 1621 |             socket_set_timeout($this->sock, $this->_socket_timeout, 0);
 | 
        
           |  |  | 1622 |         }
 | 
        
           |  |  | 1623 |   | 
        
           |  |  | 1624 |         $this->_header = $header;
 | 
        
           |  |  | 1625 |         $this->_body = $buffer;
 | 
        
           |  |  | 1626 |         // $this->_buffer = $header . "\r\n\r\n" . $buffer;
 | 
        
           |  |  | 1627 |         $this->_error_log($this->_header);
 | 
        
           |  |  | 1628 |         $this->_error_log($this->_body);
 | 
        
           |  |  | 1629 |   | 
        
           |  |  | 1630 |     }
 | 
        
           |  |  | 1631 |   | 
        
           |  |  | 1632 |     /**
 | 
        
           |  |  | 1633 |      * Write the chunk to the file if $fp is set, otherwise append the data to the buffer
 | 
        
           |  |  | 1634 |      * @param string $chunk the data to add
 | 
        
           |  |  | 1635 |      * @param resource $fp the file handle to write to (or null)
 | 
        
           |  |  | 1636 |      * @param string &$buffer the buffer to append to (if $fp is null)
 | 
        
           |  |  | 1637 |      */
 | 
        
           |  |  | 1638 |     private static function update_file_or_buffer($chunk, $fp, &$buffer) {
 | 
        
           |  |  | 1639 |         if ($fp) {
 | 
        
           |  |  | 1640 |             fwrite($fp, $chunk);
 | 
        
           |  |  | 1641 |         } else {
 | 
        
           |  |  | 1642 |             $buffer .= $chunk;
 | 
        
           |  |  | 1643 |         }
 | 
        
           |  |  | 1644 |     }
 | 
        
           |  |  | 1645 |   | 
        
           |  |  | 1646 |     /**
 | 
        
           |  |  | 1647 |      * Private method process_respond
 | 
        
           |  |  | 1648 |      *
 | 
        
           |  |  | 1649 |      * Processes the webdav server respond and detects its components (header, body).
 | 
        
           |  |  | 1650 |      * and returns data array structure.
 | 
        
           |  |  | 1651 |      * @return array ret_struct
 | 
        
           |  |  | 1652 |      * @access private
 | 
        
           |  |  | 1653 |      */
 | 
        
           |  |  | 1654 |     private function process_respond() {
 | 
        
           |  |  | 1655 |         $lines = explode("\r\n", $this->_header);
 | 
        
           |  |  | 1656 |         $header_done = false;
 | 
        
           |  |  | 1657 |         // $this->_error_log($this->_buffer);
 | 
        
           |  |  | 1658 |         // First line should be a HTTP status line (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6)
 | 
        
           |  |  | 1659 |         // Format is: HTTP-Version SP Status-Code SP Reason-Phrase CRLF
 | 
        
           |  |  | 1660 |         list($ret_struct['status']['http-version'],
 | 
        
           |  |  | 1661 |             $ret_struct['status']['status-code'],
 | 
        
           |  |  | 1662 |             $ret_struct['status']['reason-phrase']) = explode(' ', $lines[0],3);
 | 
        
           |  |  | 1663 |   | 
        
           |  |  | 1664 |         // print "HTTP Version: '$http_version' Status-Code: '$status_code' Reason Phrase: '$reason_phrase'<br>";
 | 
        
           |  |  | 1665 |         // get the response header fields
 | 
        
           |  |  | 1666 |         // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
 | 
        
           |  |  | 1667 |         for($i=1; $i<count($lines); $i++) {
 | 
        
           |  |  | 1668 |             if (rtrim($lines[$i]) == '' && !$header_done) {
 | 
        
           |  |  | 1669 |                 $header_done = true;
 | 
        
           |  |  | 1670 |                 // print "--- response header end ---<br>";
 | 
        
           |  |  | 1671 |   | 
        
           |  |  | 1672 |             }
 | 
        
           |  |  | 1673 |             if (!$header_done ) {
 | 
        
           |  |  | 1674 |                 // store all found headers in array ...
 | 
        
           |  |  | 1675 |                 list($fieldname, $fieldvalue) = explode(':', $lines[$i]);
 | 
        
           |  |  | 1676 |                 // check if this header was allready set (apache 2.0 webdav module does this....).
 | 
        
           |  |  | 1677 |                 // If so we add the the value to the end the fieldvalue, separated by comma...
 | 
        
           |  |  | 1678 |                 if (empty($ret_struct['header'])) {
 | 
        
           |  |  | 1679 |                     $ret_struct['header'] = array();
 | 
        
           |  |  | 1680 |                 }
 | 
        
           |  |  | 1681 |                 if (empty($ret_struct['header'][$fieldname])) {
 | 
        
           |  |  | 1682 |                     $ret_struct['header'][$fieldname] = trim($fieldvalue);
 | 
        
           |  |  | 1683 |                 } else {
 | 
        
           |  |  | 1684 |                     $ret_struct['header'][$fieldname] .= ',' . trim($fieldvalue);
 | 
        
           |  |  | 1685 |                 }
 | 
        
           |  |  | 1686 |             }
 | 
        
           |  |  | 1687 |         }
 | 
        
           |  |  | 1688 |         // print 'string len of response_body:'. strlen($response_body);
 | 
        
           |  |  | 1689 |         // print '[' . htmlentities($response_body) . ']';
 | 
        
           |  |  | 1690 |         $ret_struct['body'] = $this->_body;
 | 
        
           |  |  | 1691 |         $this->_error_log('process_respond: ' . var_export($ret_struct,true));
 | 
        
           |  |  | 1692 |         return $ret_struct;
 | 
        
           |  |  | 1693 |   | 
        
           |  |  | 1694 |     }
 | 
        
           |  |  | 1695 |   | 
        
           |  |  | 1696 |     /**
 | 
        
           |  |  | 1697 |      * Private method reopen
 | 
        
           |  |  | 1698 |      *
 | 
        
           |  |  | 1699 |      * Reopens a socket, if 'connection: closed'-header was received from server.
 | 
        
           |  |  | 1700 |      *
 | 
        
           |  |  | 1701 |      * Uses public method open.
 | 
        
           |  |  | 1702 |      * @access private
 | 
        
           |  |  | 1703 |      */
 | 
        
           |  |  | 1704 |     private function reopen() {
 | 
        
           |  |  | 1705 |         // let's try to reopen a socket
 | 
        
           |  |  | 1706 |         $this->_error_log('reopen a socket connection');
 | 
        
           |  |  | 1707 |         return $this->open();
 | 
        
           |  |  | 1708 |     }
 | 
        
           |  |  | 1709 |   | 
        
           |  |  | 1710 |   | 
        
           |  |  | 1711 |     /**
 | 
        
           |  |  | 1712 |      * Private method translate_uri
 | 
        
           |  |  | 1713 |      *
 | 
        
           |  |  | 1714 |      * translates an uri to raw url encoded string.
 | 
        
           |  |  | 1715 |      * Removes any html entity in uri
 | 
        
           |  |  | 1716 |      * @param string uri
 | 
        
           |  |  | 1717 |      * @return string translated_uri
 | 
        
           |  |  | 1718 |      * @access private
 | 
        
           |  |  | 1719 |      */
 | 
        
           |  |  | 1720 |     private function translate_uri($uri) {
 | 
        
           |  |  | 1721 |         // remove all html entities...
 | 
        
           |  |  | 1722 |         $native_path = html_entity_decode($uri, ENT_COMPAT);
 | 
        
           |  |  | 1723 |         $parts = explode('/', $native_path);
 | 
        
           |  |  | 1724 |         for ($i = 0; $i < count($parts); $i++) {
 | 
        
           |  |  | 1725 |             // check if part is allready utf8
 | 
        
           |  |  | 1726 |             if (iconv('UTF-8', 'UTF-8', $parts[$i]) == $parts[$i]) {
 | 
        
           |  |  | 1727 |                 $parts[$i] = rawurlencode($parts[$i]);
 | 
        
           |  |  | 1728 |             } else {
 | 
        
           |  |  | 1729 |                 $parts[$i] = rawurlencode(\core_text::convert($parts[$i], 'ISO-8859-1', 'UTF-8'));
 | 
        
           |  |  | 1730 |             }
 | 
        
           |  |  | 1731 |         }
 | 
        
           |  |  | 1732 |         return implode('/', $parts);
 | 
        
           |  |  | 1733 |     }
 | 
        
           |  |  | 1734 |   | 
        
           |  |  | 1735 |     /**
 | 
        
           |  |  | 1736 |      * Private method utf_decode_path
 | 
        
           |  |  | 1737 |      *
 | 
        
           |  |  | 1738 |      * decodes a UTF-8 encoded string
 | 
        
           |  |  | 1739 |      * @return string decodedstring
 | 
        
           |  |  | 1740 |      * @access private
 | 
        
           |  |  | 1741 |      */
 | 
        
           |  |  | 1742 |     private function utf_decode_path($path) {
 | 
        
           |  |  | 1743 |         $fullpath = $path;
 | 
        
           |  |  | 1744 |         if (iconv('UTF-8', 'UTF-8', $fullpath) == $fullpath) {
 | 
        
           |  |  | 1745 |             $this->_error_log("filename is utf-8. Needs conversion...");
 | 
        
           |  |  | 1746 |             $fullpath = \core_text::convert($fullpath, 'UTF-8', 'ISO-8859-1');
 | 
        
           |  |  | 1747 |         }
 | 
        
           |  |  | 1748 |         return $fullpath;
 | 
        
           |  |  | 1749 |     }
 | 
        
           |  |  | 1750 |   | 
        
           |  |  | 1751 |     /**
 | 
        
           |  |  | 1752 |      * Private method _error_log
 | 
        
           |  |  | 1753 |      *
 | 
        
           |  |  | 1754 |      * a simple php error_log wrapper.
 | 
        
           |  |  | 1755 |      * @param string err_string
 | 
        
           |  |  | 1756 |      * @access private
 | 
        
           |  |  | 1757 |      */
 | 
        
           |  |  | 1758 |     private function _error_log($err_string) {
 | 
        
           |  |  | 1759 |         if ($this->_debug) {
 | 
        
           |  |  | 1760 |             error_log($err_string);
 | 
        
           |  |  | 1761 |         }
 | 
        
           |  |  | 1762 |     }
 | 
        
           |  |  | 1763 |   | 
        
           |  |  | 1764 |     /**
 | 
        
           |  |  | 1765 |      * Helper method to get the parser id for both PHP 7 and 8.
 | 
        
           |  |  | 1766 |      *
 | 
        
           |  |  | 1767 |      * @param resource|object $parser
 | 
        
           |  |  | 1768 |      * @return int
 | 
        
           |  |  | 1769 |      */
 | 
        
           |  |  | 1770 |     private function get_parser_id($parser): int {
 | 
        
           |  |  | 1771 |         if (is_object($parser)) {
 | 
        
           |  |  | 1772 |             return spl_object_id($parser);
 | 
        
           |  |  | 1773 |         } else {
 | 
        
           |  |  | 1774 |             return (int) $parser;
 | 
        
           |  |  | 1775 |         }
 | 
        
           |  |  | 1776 |     }
 | 
        
           |  |  | 1777 | }
 |